diff a/.jcheck/conf b/.jcheck/conf
--- a/.jcheck/conf
+++ b/.jcheck/conf
@@ -1,35 +1,18 @@
 [general]
-project=jdk
+project=valhalla
 jbs=JDK
 version=23
 
 [checks]
-error=author,committer,reviewers,merge,issues,executable,symlink,message,hg-tag,whitespace,problemlists
-
-[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=
+error=author,committer,executable,symlink,whitespace
 
 [census]
 version=0
 domain=openjdk.org
 
 [checks "whitespace"]
 files=.*\.cpp|.*\.hpp|.*\.c|.*\.h|.*\.java|.*\.cc|.*\.hh|.*\.m|.*\.mm|.*\.md|.*\.gmk|.*\.m4|.*\.ac|Makefile
 ignore-tabs=.*\.gmk|Makefile
 
-[checks "merge"]
-message=Merge
-
-[checks "reviewers"]
-reviewers=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/make/CompileJavaModules.gmk b/make/CompileJavaModules.gmk
--- a/make/CompileJavaModules.gmk
+++ b/make/CompileJavaModules.gmk
@@ -88,11 +88,10 @@
 
 -include Java.gmk
 
 ################################################################################
 # Setup the main compilation
-
 $(eval $(call SetupJavaCompilation, $(MODULE), \
     SMALL_JAVA := false, \
     MODULE := $(MODULE), \
     SRC := $(wildcard $(MODULE_SRC_DIRS)), \
     INCLUDES := $(JDK_USER_DEFINED_FILTER), \
@@ -115,10 +114,55 @@
         --system none, \
 ))
 
 TARGETS += $($(MODULE))
 
+################################################################################
+# Setup compilation for value classes in the module
+# TBD: When $(DOCLINT) was included there was an NPE in JavacTypes.getOverriddenMethods
+
+# Directory and file name suffix for jar file containing value classes
+VALUECLASSES_STR := valueclasses
+
+ifneq ($(COMPILER), bootjdk)
+  MODULE_VALUECLASS_SRC_DIRS := $(call FindModuleValueClassSrcDirs, $(MODULE))
+  MODULE_VALUECLASS_SOURCEPATH := $(call GetModuleValueClassSrcPath)
+
+  ifneq ($(MODULE_VALUECLASS_SRC_DIRS),)
+    $(eval $(call SetupJavaCompilation, $(MODULE)-$(VALUECLASSES_STR), \
+        SMALL_JAVA := false, \
+        MODULE := $(MODULE), \
+        SRC := $(wildcard $(MODULE_VALUECLASS_SRC_DIRS)), \
+        INCLUDES := $(JDK_USER_DEFINED_FILTER), \
+        FAIL_NO_SRC := $(FAIL_NO_SRC), \
+        BIN := $(SUPPORT_OUTPUTDIR)/$(VALUECLASSES_STR)/, \
+        JAR := $(JDK_OUTPUTDIR)/lib/$(VALUECLASSES_STR)/$(MODULE)-$(VALUECLASSES_STR).jar, \
+        HEADERS := $(SUPPORT_OUTPUTDIR)/headers, \
+        DISABLED_WARNINGS := $(DISABLED_WARNINGS_java), \
+        EXCLUDES := $(EXCLUDES), \
+        EXCLUDE_FILES := $(EXCLUDE_FILES) \
+        KEEP_ALL_TRANSLATIONS := $(KEEP_ALL_TRANSLATIONS), \
+        DEPENDS := $($(MODULE)), \
+        JAVAC_FLAGS := \
+            $(JAVAC_FLAGS) \
+            --module-source-path $(MODULE_VALUECLASS_SOURCEPATH) \
+            --module-path $(JDK_OUTPUTDIR)/modules \
+            --system none \
+            --enable-preview -source $(JDK_SOURCE_TARGET_VERSION), \
+    ))
+
+    TARGETS += $($(MODULE)-$(VALUECLASSES_STR))
+
+    $(eval $(call SetupCopyFiles, $(MODULE)-copy-valueclass-jar, \
+        FILES := $(JDK_OUTPUTDIR)/lib/$(VALUECLASSES_STR)/$(MODULE)-$(VALUECLASSES_STR).jar, \
+        DEST := $(SUPPORT_OUTPUTDIR)/modules_libs/$(MODULE)/$(VALUECLASSES_STR), \
+    ))
+
+    TARGETS += $($(MODULE)-copy-valueclass-jar)
+  endif
+endif
+
 # Declare dependencies between java compilations of different modules.
 # Since the other modules are declared in different invocations of this file,
 # use the macro to find the correct target file to depend on.
 # Only the javac compilation actually depends on other modules so limit
 # dependency declaration to that by using the *_MODFILELIST variable.
diff a/make/Docs.gmk b/make/Docs.gmk
--- a/make/Docs.gmk
+++ b/make/Docs.gmk
@@ -100,13 +100,15 @@
 # Allow overriding on the command line
 # (intentionally sharing name with the javac option)
 JAVA_WARNINGS_ARE_ERRORS ?= -Werror
 
 # The initial set of options for javadoc
-JAVADOC_OPTIONS := -use -keywords -notimestamp \
+JAVADOC_OPTIONS := -XDignore.symbol.file=true -use -keywords -notimestamp \
     -encoding ISO-8859-1 -docencoding UTF-8 -breakiterator \
     -splitIndex --system none -javafx --expand-requires transitive \
+    -XDenableValueTypes \
+    --enable-preview -source $(JDK_SOURCE_TARGET_VERSION) \
     --override-methods=summary \
     --no-external-specs-page
 
 # The reference options must stay stable to allow for comparisons across the
 # development cycle.
diff a/make/Images.gmk b/make/Images.gmk
--- a/make/Images.gmk
+++ b/make/Images.gmk
@@ -1,7 +1,7 @@
 #
-# Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 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.  Oracle designates this
@@ -129,58 +129,63 @@
 
 # Helper function for creating the CDS archives for the JDK and JRE
 #
 # Param1 - VM variant (e.g., server, client, zero, ...)
 # Param2 - _nocoops, or empty
+# Param3 - _valhalla, or empty
 define CreateCDSArchive
-  $1_$2_DUMP_EXTRA_ARG := $(if $(filter _nocoops, $2),-XX:-UseCompressedOops,)
-  $1_$2_DUMP_TYPE      := $(if $(filter _nocoops, $2),-NOCOOPS,)
+  $1_$2_$3_DUMP_EXTRA_ARG := $(if $(filter _nocoops, $2),-XX:-UseCompressedOops,) $(if $(filter _valhalla, $3),--enable-preview,)
+  $1_$2_$3_DUMP_TYPE      := $(if $(filter _nocoops, $2),-NOCOOPS,)$(if $(filter _valhalla, $3),-VALHALLA,)
 
   # Only G1 supports dumping the shared heap, so explicitly use G1 if the JVM supports it.
-  $1_$2_CDS_DUMP_FLAGS := $(CDS_DUMP_FLAGS) $(if $(filter g1gc, $(JVM_FEATURES_$1)),-XX:+UseG1GC)
+  $1_$2_$3_CDS_DUMP_FLAGS := $(CDS_DUMP_FLAGS) $(if $(filter g1gc, $(JVM_FEATURES_$1)),-XX:+UseG1GC)
 
   ifeq ($(OPENJDK_TARGET_OS), windows)
-    $1_$2_CDS_ARCHIVE := bin/$1/classes$2.jsa
+    $1_$2_$3_CDS_ARCHIVE := bin/$1/classes$2$3.jsa
   else
-    $1_$2_CDS_ARCHIVE := lib/$1/classes$2.jsa
+    $1_$2_$3_CDS_ARCHIVE := lib/$1/classes$2$3.jsa
   endif
 
-  $$(eval $$(call SetupExecute, $1_$2_gen_cds_archive_jdk, \
-      WARN := Creating CDS$$($1_$2_DUMP_TYPE) archive for jdk image for $1, \
-      INFO := Using CDS flags for $1: $$($1_$2_CDS_DUMP_FLAGS), \
+  $$(eval $$(call SetupExecute, $1_$2_$3_gen_cds_archive_jdk, \
+      WARN := Creating CDS$$($1_$2_$3_DUMP_TYPE) archive for jdk image for $1, \
+      INFO := Using CDS flags for $1: $$($1_$2_$3_CDS_DUMP_FLAGS), \
       DEPS := $$(jlink_jdk), \
-      OUTPUT_FILE := $$(JDK_IMAGE_DIR)/$$($1_$2_CDS_ARCHIVE), \
+      OUTPUT_FILE := $$(JDK_IMAGE_DIR)/$$($1_$2_$3_CDS_ARCHIVE), \
       SUPPORT_DIR := $$(JDK_IMAGE_SUPPORT_DIR), \
       COMMAND := $$(FIXPATH) $$(JDK_IMAGE_DIR)/bin/java -Xshare:dump \
-          -XX:SharedArchiveFile=$$(JDK_IMAGE_DIR)/$$($1_$2_CDS_ARCHIVE) \
-          -$1 $$($1_$2_DUMP_EXTRA_ARG) $$($1_$2_CDS_DUMP_FLAGS) $$(LOG_INFO), \
+          -XX:SharedArchiveFile=$$(JDK_IMAGE_DIR)/$$($1_$2_$3_CDS_ARCHIVE) \
+          -$1 $$($1_$2_$3_DUMP_EXTRA_ARG) $$($1_$2_$3_CDS_DUMP_FLAGS) $$(LOG_INFO), \
   ))
 
-  JDK_TARGETS += $$($1_$2_gen_cds_archive_jdk)
+  JDK_TARGETS += $$($1_$2_$3_gen_cds_archive_jdk)
 
-  $$(eval $$(call SetupExecute, $1_$2_gen_cds_archive_jre, \
-      WARN := Creating CDS$$($1_$2_DUMP_TYPE) archive for jre image for $1, \
-      INFO := Using CDS flags for $1: $$($1_$2_CDS_DUMP_FLAGS), \
+  $$(eval $$(call SetupExecute, $1_$2_$3_gen_cds_archive_jre, \
+      WARN := Creating CDS$$($1_$2_$3_DUMP_TYPE) archive for jre image for $1, \
+      INFO := Using CDS flags for $1: $$($1_$2_$3_CDS_DUMP_FLAGS), \
       DEPS := $$(jlink_jre), \
-      OUTPUT_FILE := $$(JRE_IMAGE_DIR)/$$($1_$2_CDS_ARCHIVE), \
+      OUTPUT_FILE := $$(JRE_IMAGE_DIR)/$$($1_$2_$3_CDS_ARCHIVE), \
       SUPPORT_DIR := $$(JRE_IMAGE_SUPPORT_DIR), \
       COMMAND := $$(FIXPATH) $$(JRE_IMAGE_DIR)/bin/java -Xshare:dump \
-          -XX:SharedArchiveFile=$$(JRE_IMAGE_DIR)/$$($1_$2_CDS_ARCHIVE) \
-          -$1 $$($1_$2_DUMP_EXTRA_ARG) $$($1_$2_CDS_DUMP_FLAGS) $$(LOG_INFO), \
+          -XX:SharedArchiveFile=$$(JRE_IMAGE_DIR)/$$($1_$2_$3_CDS_ARCHIVE) \
+          -$1 $$($1_$2_$3_DUMP_EXTRA_ARG) $$($1_$2_$3_CDS_DUMP_FLAGS) $$(LOG_INFO), \
   ))
 
-  JRE_TARGETS += $$($1_$2_gen_cds_archive_jre)
+  JRE_TARGETS += $$($1_$2_$3_gen_cds_archive_jre)
 endef
 
+# TODO: add these to below when CDS & --enable-preview is well tested.
+#     $(eval $(call CreateCDSArchive,$v,,_valhalla))
+#     $(eval $(call CreateCDSArchive,$v,_nocoops,_valhalla))
+
 ifeq ($(BUILD_CDS_ARCHIVE), true)
   $(foreach v, $(JVM_VARIANTS), \
-    $(eval $(call CreateCDSArchive,$v,)) \
+    $(eval $(call CreateCDSArchive,$v,,)) \
   )
 
   ifeq ($(call isTargetCpuBits, 64), true)
     $(foreach v, $(JVM_VARIANTS), \
-      $(eval $(call CreateCDSArchive,$v,_nocoops)) \
+      $(eval $(call CreateCDSArchive,$v,_nocoops,)) \
     )
   endif
 endif
 
 ################################################################################
diff a/make/Main.gmk b/make/Main.gmk
--- a/make/Main.gmk
+++ b/make/Main.gmk
@@ -1,7 +1,7 @@
 #
-# Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 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.  Oracle designates this
diff a/make/autoconf/hotspot.m4 b/make/autoconf/hotspot.m4
--- a/make/autoconf/hotspot.m4
+++ b/make/autoconf/hotspot.m4
@@ -24,10 +24,13 @@
 #
 
 # All valid JVM variants
 VALID_JVM_VARIANTS="server client minimal core zero custom"
 
+# Valhalla temporarily disabled
+VALHALLA_TEMP=false
+
 ###############################################################################
 # Check if the specified JVM variant should be built. To be used in shell if
 # constructs, like this:
 # if HOTSPOT_CHECK_JVM_VARIANT(server); then
 #
diff a/make/common/Modules.gmk b/make/common/Modules.gmk
--- a/make/common/Modules.gmk
+++ b/make/common/Modules.gmk
@@ -78,10 +78,14 @@
 
 GENERATED_SRC_DIRS += \
     $(SUPPORT_OUTPUTDIR)/gensrc \
     #
 
+GENERATED_VALUE_CLASS_SUBDIRS += \
+    $(SUPPORT_OUTPUTDIR)/gensrc-valueclasses \
+    #
+
 TOP_SRC_DIRS += \
     $(TOPDIR)/src \
     #
 
 SRC_SUBDIRS += $(OPENJDK_TARGET_OS)/classes
@@ -144,10 +148,16 @@
 FindModuleSrcDirs = \
     $(strip $(wildcard \
         $(addsuffix /$(strip $1), $(GENERATED_SRC_DIRS) $(IMPORT_MODULES_SRC)) \
         $(foreach sub, $(SRC_SUBDIRS), $(addsuffix /$(strip $1)/$(sub), $(TOP_SRC_DIRS)))))
 
+# Find value class source dirs for a particular module  (only generated)
+# $1 - Module to find source dirs for
+FindModuleValueClassSrcDirs = \
+    $(strip $(wildcard \
+        $(addsuffix /$(strip $1), $(GENERATED_VALUE_CLASS_SUBDIRS))))
+
 # Find all specs dirs for a particular module
 # $1 - Module to find specs dirs for
 FindModuleSpecsDirs = \
     $(strip $(wildcard \
         $(foreach sub, $(SPEC_SUBDIRS), $(addsuffix /$(strip $1)/$(sub), $(TOP_SRC_DIRS)))))
@@ -161,10 +171,16 @@
 # Construct the complete module source path
 GetModuleSrcPath = \
     $(call PathList, \
         $(addsuffix /*, $(GENERATED_SRC_DIRS) $(IMPORT_MODULES_SRC)) \
         $(foreach sub, $(SRC_SUBDIRS), $(addsuffix /*/$(sub), $(TOP_SRC_DIRS))))
+
+# Construct the complete module source path for value classes
+GetModuleValueClassSrcPath = \
+    $(call PathList, \
+        $(addsuffix /*, $(GENERATED_VALUE_CLASS_SUBDIRS) $(GENERATED_SRC_DIRS) $(IMPORT_MODULES_SRC)) \
+        $(foreach sub, $(SRC_SUBDIRS), $(addsuffix /*/$(sub), $(TOP_SRC_DIRS))))
 
 ################################################################################
 # Extract module dependencies from module-info.java files, both normal
 # dependencies ("requires"), and indirect exports ("requires transitive").
 
diff a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js
--- a/make/conf/jib-profiles.js
+++ b/make/conf/jib-profiles.js
@@ -1480,10 +1480,11 @@
         if (input.target_os == "macosx") {
             args = concat(args, "--with-macosx-bundle-build-version="
                           + common.build_number + "." + ciBuildNumber);
         }
     } else {
+        args = concat(args, "--with-version-pre=" + version_numbers.get("DEFAULT_PROMOTED_VERSION_PRE"));
         args = concat(args, "--with-version-opt=" + common.build_id);
     }
     var sourceDate
     if (input.build_id_data && input.build_id_data.creationTime) {
         sourceDate = Math.floor(Date.parse(input.build_id_data.creationTime)/1000);
diff a/make/conf/version-numbers.conf b/make/conf/version-numbers.conf
--- a/make/conf/version-numbers.conf
+++ b/make/conf/version-numbers.conf
@@ -37,6 +37,6 @@
 DEFAULT_VERSION_CLASSFILE_MAJOR=67  # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`"
 DEFAULT_VERSION_CLASSFILE_MINOR=0
 DEFAULT_VERSION_DOCS_API_SINCE=11
 DEFAULT_ACCEPTABLE_BOOT_VERSIONS="21 22 23"
 DEFAULT_JDK_SOURCE_TARGET_VERSION=23
-DEFAULT_PROMOTED_VERSION_PRE=ea
+DEFAULT_PROMOTED_VERSION_PRE=lworld5ea
diff a/make/data/hotspot-symbols/symbols-unix b/make/data/hotspot-symbols/symbols-unix
--- a/make/data/hotspot-symbols/symbols-unix
+++ b/make/data/hotspot-symbols/symbols-unix
@@ -153,12 +153,16 @@
 JVM_IsCDSDumpingEnabled
 JVM_IsConstructorIx
 JVM_IsDumpingClassList
 JVM_IsFinalizationEnabled
 JVM_IsHiddenClass
+JVM_IsIdentityClass
+JVM_IsImplicitlyConstructibleClass
 JVM_IsInterface
+JVM_IsNullRestrictedArray
 JVM_IsPreviewEnabled
+JVM_IsValhallaEnabled
 JVM_IsContinuationsSupported
 JVM_IsPrimitiveClass
 JVM_IsRecord
 JVM_IsSameClassPackage
 JVM_IsSharingEnabled
@@ -179,10 +183,11 @@
 JVM_NanoTime
 JVM_NativePath
 JVM_NewArray
 JVM_NewInstanceFromConstructor
 JVM_NewMultiArray
+JVM_NewNullRestrictedArray
 JVM_PhantomReferenceRefersTo
 JVM_PrintWarningAtDynamicAgentLoad
 JVM_RaiseSignal
 JVM_RawMonitorCreate
 JVM_RawMonitorDestroy
diff a/make/hotspot/lib/JvmFeatures.gmk b/make/hotspot/lib/JvmFeatures.gmk
--- a/make/hotspot/lib/JvmFeatures.gmk
+++ b/make/hotspot/lib/JvmFeatures.gmk
@@ -46,11 +46,11 @@
 
 ifeq ($(call check-jvm-feature, zero), true)
   JVM_EXCLUDES += opto libadt
   JVM_EXCLUDE_PATTERNS += c1_ c1/ c2_ runtime_ /c2/
   JVM_EXCLUDE_FILES += templateInterpreter.cpp templateInterpreterGenerator.cpp \
-                       bcEscapeAnalyzer.cpp ciTypeFlow.cpp
+                       bcEscapeAnalyzer.cpp ciTypeFlow.cpp macroAssembler_common.cpp
   JVM_CFLAGS_FEATURES += -DZERO -DZERO_LIBARCH='"$(OPENJDK_TARGET_CPU_LEGACY_LIB)"' $(LIBFFI_CFLAGS)
   JVM_LIBS_FEATURES += $(LIBFFI_LIBS)
   ifeq ($(ENABLE_LIBFFI_BUNDLING), true)
     JVM_LDFLAGS_FEATURES += $(call SET_EXECUTABLE_ORIGIN,/..)
   endif
diff a/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java b/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java
--- a/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java
+++ b/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java
@@ -2572,10 +2572,11 @@
                 if (((FieldDescription) feature).descriptor.equals("C")) {
                     value = (char) (int) value;
                 }
                 ((FieldDescription) feature).constantValue = value;
                 break;
+            case "Preload":
             case "SourceFile":
                 //ignore, not needed
                 break;
             case "BootstrapMethods":
                 //ignore, not needed
diff a/make/modules/java.base/Gensrc.gmk b/make/modules/java.base/Gensrc.gmk
--- a/make/modules/java.base/Gensrc.gmk
+++ b/make/modules/java.base/Gensrc.gmk
@@ -34,10 +34,11 @@
 include gensrc/GensrcExceptions.gmk
 include gensrc/GensrcVarHandles.gmk
 include gensrc/GensrcModuleLoaderMap.gmk
 include gensrc/GensrcScopedMemoryAccess.gmk
 include gensrc/GensrcRegex.gmk
+include gensrc/GensrcValueClasses.gmk
 
 # GensrcLocaleData.gmk does not set TARGETS, so we must choose which targets
 # to include.
 TARGETS += $(GENSRC_BASELOCALEDATA)
 
diff a/make/modules/java.base/Java.gmk b/make/modules/java.base/Java.gmk
--- a/make/modules/java.base/Java.gmk
+++ b/make/modules/java.base/Java.gmk
@@ -21,11 +21,11 @@
 # 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.
 #
 
-DISABLED_WARNINGS_java += this-escape restricted
+DISABLED_WARNINGS_java += this-escape restricted preview
 
 DOCLINT += -Xdoclint:all/protected \
     '-Xdoclint/package:java.*,javax.*'
 JAVAC_FLAGS += -XDstringConcat=inline
 COPY += .icu .dat .spp .nrm content-types.properties \
diff a/make/modules/java.base/gensrc/GensrcValueClasses.gmk b/make/modules/java.base/gensrc/GensrcValueClasses.gmk
--- /dev/null
+++ b/make/modules/java.base/gensrc/GensrcValueClasses.gmk
@@ -0,0 +1,70 @@
+#
+# 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.  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.
+#
+
+################################################################################
+# Generate the value class replacements for selected java.base source files
+
+java.base-VALUE_CLASS-REPLACEMENTS := \
+    java/lang/Byte.java \
+    java/lang/Short.java \
+    java/lang/Integer.java \
+    java/lang/Long.java \
+    java/lang/Float.java \
+    java/lang/Double.java \
+    java/lang/Boolean.java \
+    java/lang/Character.java \
+    java/lang/Number.java \
+    java/lang/Record.java \
+    java/util/Optional.java \
+    java/util/OptionalInt.java \
+    java/util/OptionalLong.java \
+    java/util/OptionalDouble.java \
+    java/time/LocalDate.java \
+    java/time/LocalDateTime.java \
+    java/time/LocalTime.java \
+    java/time/Duration.java \
+    java/time/Instant.java \
+    java/time/MonthDay.java \
+    java/time/ZonedDateTime.java \
+    java/time/OffsetDateTime.java \
+    java/time/OffsetTime.java \
+    java/time/YearMonth.java \
+    java/time/Year.java \
+    java/time/Period.java \
+    #
+
+java.base-VALUE-CLASS-FILES := \
+    $(foreach f, $(java.base-VALUE_CLASS-REPLACEMENTS), $(addprefix $(TOPDIR)/src/java.base/share/classes/, $(f)))
+
+$(eval $(call SetupTextFileProcessing, JAVA_BASE_VALUECLASS_REPLACEMENTS, \
+    SOURCE_FILES := $(java.base-VALUE-CLASS-FILES), \
+    SOURCE_BASE_DIR := $(TOPDIR)/src/java.base/share/classes, \
+    OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/gensrc-valueclasses/java.base/, \
+    REPLACEMENTS := \
+        public final class => public final value class ; \
+        public abstract class => public abstract value class, \
+))
+
+TARGETS += $(JAVA_BASE_VALUECLASS_REPLACEMENTS)
diff a/make/modules/java.base/gensrc/GensrcVarHandles.gmk b/make/modules/java.base/gensrc/GensrcVarHandles.gmk
--- a/make/modules/java.base/gensrc/GensrcVarHandles.gmk
+++ b/make/modules/java.base/gensrc/GensrcVarHandles.gmk
@@ -50,13 +50,23 @@
 
   ifneq ($$(findstring $$($1_Type), Byte Short Char), )
     $1_ARGS += -KShorterThanInt
   endif
 
+  ifeq ($$($1_Type), Reference)
+    $1_ARGS += -KReference
+  endif
+
+  ifeq ($$($1_Type), Value)
+    $1_ARGS += -KValue
+  endif
+
   $$($1_FILENAME): $(VARHANDLES_SRC_DIR)/X-VarHandle.java.template $(BUILD_TOOLS_JDK)
         ifeq ($$($1_Type), Reference)
 	  $$(eval $1_type := Object)
+        else ifeq ($$($1_Type), Value)
+	  $$(eval $1_type := Object)
         else
 	  $$(eval $1_type := $$$$(shell $(TR) '[:upper:]' '[:lower:]' <<< $$$$($1_Type)))
         endif
 	$$(call MakeDir, $$(@D))
 	$(RM) $$@
@@ -260,11 +270,11 @@
 endef
 
 ################################################################################
 
 # List the types to generate source for, with capitalized first letter
-VARHANDLES_TYPES := Boolean Byte Short Char Int Long Float Double Reference
+VARHANDLES_TYPES := Boolean Byte Short Char Int Long Float Double Reference Value
 $(foreach t, $(VARHANDLES_TYPES), \
   $(eval $(call GenerateVarHandle,VAR_HANDLE_$t,$t)))
 
 # List the types to generate source for, with capitalized first letter
 VARHANDLES_BYTE_ARRAY_TYPES := Short Char Int Long Float Double
diff a/make/test/BuildMicrobenchmark.gmk b/make/test/BuildMicrobenchmark.gmk
--- a/make/test/BuildMicrobenchmark.gmk
+++ b/make/test/BuildMicrobenchmark.gmk
@@ -91,11 +91,11 @@
 $(eval $(call SetupJavaCompilation, BUILD_JDK_MICROBENCHMARK, \
     TARGET_RELEASE := $(TARGET_RELEASE_NEWJDK_UPGRADED), \
     SMALL_JAVA := false, \
     CLASSPATH := $(JMH_COMPILE_JARS), \
     DISABLED_WARNINGS := restricted this-escape processing rawtypes cast \
-        serial preview, \
+        serial preview unchecked deprecation, \
     SRC := $(MICROBENCHMARK_SRC), \
     BIN := $(MICROBENCHMARK_CLASSES), \
     JAVAC_FLAGS := \
         --add-exports java.base/jdk.internal.classfile.impl=ALL-UNNAMED \
         --add-exports java.base/jdk.internal.event=ALL-UNNAMED \
diff a/make/test/BuildTestLib.gmk b/make/test/BuildTestLib.gmk
--- a/make/test/BuildTestLib.gmk
+++ b/make/test/BuildTestLib.gmk
@@ -56,20 +56,26 @@
 TARGETS += $(BUILD_WB_JAR)
 
 $(eval $(call SetupJavaCompilation, BUILD_TEST_LIB_JAR, \
     TARGET_RELEASE := $(TARGET_RELEASE_NEWJDK_UPGRADED), \
     SRC := $(TEST_LIB_SOURCE_DIR), \
-    EXCLUDES := jdk/test/lib/containers jdk/test/lib/security, \
+    EXCLUDES := jdk/test/lib/containers jdk/test/lib/security org, \
     BIN := $(TEST_LIB_SUPPORT)/test-lib_classes, \
     HEADERS := $(TEST_LIB_SUPPORT)/test-lib_headers, \
     JAR := $(TEST_LIB_SUPPORT)/test-lib.jar, \
-    DISABLED_WARNINGS := try deprecation rawtypes unchecked serial cast removal preview, \
+    DISABLED_WARNINGS := try deprecation rawtypes unchecked serial cast removal preview varargs, \
     JAVAC_FLAGS := --add-exports java.base/sun.security.util=ALL-UNNAMED \
         --add-exports java.base/jdk.internal.classfile=ALL-UNNAMED \
         --add-exports java.base/jdk.internal.classfile.attribute=ALL-UNNAMED \
         --add-exports java.base/jdk.internal.classfile.constantpool=ALL-UNNAMED \
         --add-exports java.base/jdk.internal.module=ALL-UNNAMED \
+        --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
+        --add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED \
+        --add-exports jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \
+        --add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED \
+        --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
+        --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \
         --enable-preview, \
 ))
 
 TARGETS += $(BUILD_TEST_LIB_JAR)
 
diff a/make/test/JtregNativeHotspot.gmk b/make/test/JtregNativeHotspot.gmk
--- a/make/test/JtregNativeHotspot.gmk
+++ b/make/test/JtregNativeHotspot.gmk
@@ -746,10 +746,11 @@
 BUILD_HOTSPOT_JTREG_LIBRARIES_CFLAGS_libobjmonusage003 := $(NSK_JVMTI_AGENT_INCLUDES)
 BUILD_HOTSPOT_JTREG_LIBRARIES_CFLAGS_libobjmonusage002 := $(NSK_JVMTI_AGENT_INCLUDES)
 BUILD_HOTSPOT_JTREG_LIBRARIES_CFLAGS_libobjmonusage005 := $(NSK_JVMTI_AGENT_INCLUDES)
 BUILD_HOTSPOT_JTREG_LIBRARIES_CFLAGS_libobjmonusage006 := $(NSK_JVMTI_AGENT_INCLUDES)
 BUILD_HOTSPOT_JTREG_LIBRARIES_CFLAGS_libobjmonusage001 := $(NSK_JVMTI_AGENT_INCLUDES)
+BUILD_HOTSPOT_JTREG_LIBRARIES_CFLAGS_libobjmonusage007 := $(NSK_JVMTI_AGENT_INCLUDES)
 BUILD_HOTSPOT_JTREG_LIBRARIES_CFLAGS_libagentonload001 := $(NSK_JVMTI_AGENT_INCLUDES)
 BUILD_HOTSPOT_JTREG_LIBRARIES_CFLAGS_libagentonload002 := $(NSK_JVMTI_AGENT_INCLUDES)
 BUILD_HOTSPOT_JTREG_LIBRARIES_CFLAGS_libagentonload003 := $(NSK_JVMTI_AGENT_INCLUDES)
 BUILD_HOTSPOT_JTREG_LIBRARIES_CFLAGS_libthrcputime002 := $(NSK_JVMTI_AGENT_INCLUDES)
 BUILD_HOTSPOT_JTREG_LIBRARIES_CFLAGS_libthrcputime001 := $(NSK_JVMTI_AGENT_INCLUDES)
@@ -1461,10 +1462,11 @@
   BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libobjmonusage003 += -lpthread
   BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libobjmonusage002 += -lpthread
   BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libobjmonusage005 += -lpthread
   BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libobjmonusage006 += -lpthread
   BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libobjmonusage001 += -lpthread
+  BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libobjmonusage007 += -lpthread
   BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libagentonload001 += -lpthread
   BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libagentonload002 += -lpthread
   BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libagentonload003 += -lpthread
   BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libthrcputime002 += -lpthread
   BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libthrcputime001 += -lpthread
diff a/src/README b/src/README
--- /dev/null
+++ b/src/README
@@ -0,0 +1,51 @@
+
+*** Project Valhalla "L-World Value Types" Early Access ***
+
+Prototype Version: LW2
+
+This is an "early access" build, from "Project Valhalla" [1], aimed at testing a
+"Value Types" implementation within the JVM, where-by value types may
+interoperate with existing type descriptors. Otherwise known as "L-World Value
+Types" (L-World) [2].
+
+Intended for a number of expert users with active interest in the
+Valhalla project, and is provided as a convenience to users needing to build
+from source code (branch "lworld") [3].
+
+Warning, not only is this build based on an incomplete version of "JDK 12",
+there has been less than comprehensive testing carried out.
+
+* System Limitations
+
+- platforms: x64 Linux, x64 Mac OS X, Windows
+- no support for atomic fields containing value types (work-in-progress for an
+  update)
+- no AOT, CDS, redefineclasses
+- -Xint and C2 only, no C1, no tiered-compilation
+- interpreter is not optimized, focus is on JIT optimization
+
+* EA disclaimer
+
+- EA release functionality might not be released in a GA binary.  Any EA
+  functionality might be changed or removed
+- EA builds do not imply that the feature is being considered to any scheduled
+  OpenJDK release
+- The list of supported platforms at GA might be different than the platforms
+  supported during EA
+- These binaries have not been tested to the same level to which Oracle would
+  test a GA binary.  It has been produced for gathering feedback.  Use for any
+  other purpose is at your own risk
+- These binaries might be missing security vulnerabilities fixes available in GA
+  binaries or in other OpenJDK projects
+- Oracle does not provide support for these binaries
+
+* Filing Bugs
+
+Send email to valhalla-dev@openjdk.java.net
+
+Current known issues [4]
+
+[1] https://wiki.openjdk.java.net/display/valhalla/Main
+[2] https://wiki.openjdk.java.net/display/valhalla/L-World+Value+Types
+[3] http://hg.openjdk.java.net/valhalla/valhalla/
+[4] https://bugs.openjdk.java.net/issues/?filter=35615
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
@@ -1235,11 +1235,11 @@
     _NO_SPECIAL_PTR_REG_mask = _ALL_REG_mask;
     _NO_SPECIAL_PTR_REG_mask.SUBTRACT(_NON_ALLOCATABLE_REG_mask);
 
     // r27 is not allocatable when compressed oops is on and heapbase is not
     // zero, compressed klass pointers doesn't use r27 after JDK-8234794
-    if (UseCompressedOops && (CompressedOops::ptrs_base() != NULL)) {
+    if (UseCompressedOops && (CompressedOops::ptrs_base() != nullptr)) {
       _NO_SPECIAL_REG32_mask.Remove(OptoReg::as_OptoReg(r27->as_VMReg()));
       _NO_SPECIAL_REG_mask.Remove(OptoReg::as_OptoReg(r27->as_VMReg()));
       _NO_SPECIAL_PTR_REG_mask.Remove(OptoReg::as_OptoReg(r27->as_VMReg()));
     }
 
@@ -1579,11 +1579,11 @@
 
 bool needs_releasing_store(const Node *n)
 {
   // assert n->is_Store();
   StoreNode *st = n->as_Store();
-  return st->trailing_membar() != NULL;
+  return st->trailing_membar() != nullptr;
 }
 
 // predicate controlling translation of CAS
 //
 // returns true if CAS needs to use an acquiring load otherwise false
@@ -1591,13 +1591,13 @@
 bool needs_acquiring_load_exclusive(const Node *n)
 {
   assert(is_CAS(n->Opcode(), true), "expecting a compare and swap");
   LoadStoreNode* ldst = n->as_LoadStore();
   if (is_CAS(n->Opcode(), false)) {
-    assert(ldst->trailing_membar() != NULL, "expected trailing membar");
+    assert(ldst->trailing_membar() != nullptr, "expected trailing membar");
   } else {
-    return ldst->trailing_membar() != NULL;
+    return ldst->trailing_membar() != nullptr;
   }
 
   // so we can just return true here
   return true;
 }
@@ -1642,10 +1642,13 @@
   //   stp(zr, rscratch2, Address(__ pre(sp, -2 * wordSize)))
   //   blr(rscratch1)
   CodeBlob *cb = CodeCache::find_blob(_entry_point);
   if (cb) {
     return 1 * NativeInstruction::instruction_size;
+  } else if (_entry_point == nullptr) {
+    // See CallLeafNoFPIndirect
+    return 1 * NativeInstruction::instruction_size;
   } else {
     return 6 * NativeInstruction::instruction_size;
   }
 }
 
@@ -1732,11 +1735,11 @@
     st->print("stp  lr, rfp, [sp, #%d]!\n\t", -(2 * wordSize));
     if (PreserveFramePointer) st->print("mov  rfp, sp\n\t");
     st->print("mov  rscratch1, #%d\n\t", framesize - 2 * wordSize);
     st->print("sub  sp, sp, rscratch1");
   }
-  if (C->stub_function() == NULL && BarrierSet::barrier_set()->barrier_set_nmethod() != NULL) {
+  if (C->stub_function() == nullptr && BarrierSet::barrier_set()->barrier_set_nmethod() != nullptr) {
     st->print("\n\t");
     st->print("ldr  rscratch1, [guard]\n\t");
     st->print("dmb ishld\n\t");
     st->print("ldr  rscratch2, [rthread, #thread_disarmed_guard_value_offset]\n\t");
     st->print("cmp  rscratch1, rscratch2\n\t");
@@ -1753,59 +1756,22 @@
 
 void MachPrologNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
   Compile* C = ra_->C;
   C2_MacroAssembler _masm(&cbuf);
 
-  // n.b. frame size includes space for return pc and rfp
-  const int framesize = C->output()->frame_size_in_bytes();
-
   // insert a nop at the start of the prolog so we can patch in a
   // branch if we need to invalidate the method later
   __ nop();
 
-  if (C->clinit_barrier_on_entry()) {
-    assert(!C->method()->holder()->is_not_initialized(), "initialization should have been started");
-
-    Label L_skip_barrier;
+  __ verified_entry(C, 0);
 
-    __ mov_metadata(rscratch2, C->method()->holder()->constant_encoding());
-    __ clinit_barrier(rscratch2, rscratch1, &L_skip_barrier);
-    __ far_jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub()));
-    __ bind(L_skip_barrier);
+  if (C->stub_function() == nullptr) {
+    __ entry_barrier();
   }
 
-  if (C->max_vector_size() > 0) {
-    __ reinitialize_ptrue();
-  }
-
-  int bangsize = C->output()->bang_size_in_bytes();
-  if (C->output()->need_stack_bang(bangsize))
-    __ generate_stack_overflow_check(bangsize);
-
-  __ build_frame(framesize);
-
-  if (C->stub_function() == NULL) {
-    BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
-    if (BarrierSet::barrier_set()->barrier_set_nmethod() != NULL) {
-      // Dummy labels for just measuring the code size
-      Label dummy_slow_path;
-      Label dummy_continuation;
-      Label dummy_guard;
-      Label* slow_path = &dummy_slow_path;
-      Label* continuation = &dummy_continuation;
-      Label* guard = &dummy_guard;
-      if (!Compile::current()->output()->in_scratch_emit_size()) {
-        // Use real labels from actual stub when not emitting code for the purpose of measuring its size
-        C2EntryBarrierStub* stub = new (Compile::current()->comp_arena()) C2EntryBarrierStub();
-        Compile::current()->output()->add_stub(stub);
-        slow_path = &stub->entry();
-        continuation = &stub->continuation();
-        guard = &stub->guard();
-      }
-      // In the C2 code, we move the non-hot part of nmethod entry barriers out-of-line to a stub.
-      bs->nmethod_entry_barrier(&_masm, slow_path, continuation, guard);
-    }
+  if (!Compile::current()->output()->in_scratch_emit_size()) {
+    __ bind(*_verified_entry);
   }
 
   if (VerifyStackAtCalls) {
     Unimplemented();
   }
@@ -1818,16 +1784,10 @@
     ConstantTable& constant_table = C->output()->constant_table();
     constant_table.set_table_base_offset(constant_table.calculate_table_base_offset());
   }
 }
 
-uint MachPrologNode::size(PhaseRegAlloc* ra_) const
-{
-  return MachNode::size(ra_); // too many variables; just compute it
-                              // the hard way
-}
-
 int MachPrologNode::reloc() const
 {
   return 0;
 }
 
@@ -1867,11 +1827,11 @@
 void MachEpilogNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
   Compile* C = ra_->C;
   C2_MacroAssembler _masm(&cbuf);
   int framesize = C->output()->frame_slots() << LogBytesPerInt;
 
-  __ remove_frame(framesize);
+  __ remove_frame(framesize, C->needs_stack_repair());
 
   if (StackReservedPages > 0 && C->has_reserved_stack_access()) {
     __ reserved_stack_check();
   }
 
@@ -1886,15 +1846,10 @@
     __ relocate(relocInfo::poll_return_type);
     __ safepoint_poll(*code_stub, true /* at_return */, false /* acquire */, true /* in_nmethod */);
   }
 }
 
-uint MachEpilogNode::size(PhaseRegAlloc *ra_) const {
-  // Variable size. Determine dynamically.
-  return MachNode::size(ra_);
-}
-
 int MachEpilogNode::reloc() const {
   // Return number of relocatable values contained in this instruction.
   return 1; // 1 for polling page.
 }
 
@@ -2151,16 +2106,16 @@
 #ifndef PRODUCT
 void MachSpillCopyNode::format(PhaseRegAlloc *ra_, outputStream *st) const {
   if (!ra_)
     st->print("N%d = SpillCopy(N%d)", _idx, in(1)->_idx);
   else
-    implementation(NULL, ra_, false, st);
+    implementation(nullptr, ra_, false, st);
 }
 #endif
 
 void MachSpillCopyNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
-  implementation(&cbuf, ra_, false, NULL);
+  implementation(&cbuf, ra_, false, nullptr);
 }
 
 uint MachSpillCopyNode::size(PhaseRegAlloc *ra_) const {
   return MachNode::size(ra_);
 }
@@ -2196,12 +2151,61 @@
   } else {
     return 2 * NativeInstruction::instruction_size;
   }
 }
 
-//=============================================================================
+///=============================================================================
+#ifndef PRODUCT
+void MachVEPNode::format(PhaseRegAlloc* ra_, outputStream* st) const
+{
+  st->print_cr("# MachVEPNode");
+  if (!_verified) {
+    st->print_cr("\t load_class");
+  } else {
+    st->print_cr("\t unpack_inline_arg");
+  }
+}
+#endif
 
+void MachVEPNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const
+{
+  C2_MacroAssembler _masm(&cbuf);
+
+  if (!_verified) {
+    Label skip;
+    __ cmp_klass(j_rarg0, rscratch2, rscratch1);
+    __ br(Assembler::EQ, skip);
+      __ far_jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub()));
+    __ bind(skip);
+
+  } else {
+    // insert a nop at the start of the prolog so we can patch in a
+    // branch if we need to invalidate the method later
+    __ nop();
+
+    // TODO 8284443 Avoid creation of temporary frame
+    if (ra_->C->stub_function() == nullptr) {
+      __ verified_entry(ra_->C, 0);
+      __ entry_barrier();
+      int framesize = ra_->C->output()->frame_slots() << LogBytesPerInt;
+      __ remove_frame(framesize, false);
+    }
+    // Unpack inline type args passed as oop and then jump to
+    // the verified entry point (skipping the unverified entry).
+    int sp_inc = __ unpack_inline_args(ra_->C, _receiver_only);
+    // Emit code for verified entry and save increment for stack repair on return
+    __ verified_entry(ra_->C, sp_inc);
+    if (Compile::current()->output()->in_scratch_emit_size()) {
+      Label dummy_verified_entry;
+      __ b(dummy_verified_entry);
+    } else {
+      __ b(*_verified_entry);
+    }
+  }
+}
+
+//=============================================================================
 #ifndef PRODUCT
 void MachUEPNode::format(PhaseRegAlloc* ra_, outputStream* st) const
 {
   st->print_cr("# MachUEPNode");
   if (UseCompressedClassPointers) {
@@ -2219,25 +2223,22 @@
 
 void MachUEPNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const
 {
   // This is the unverified entry point.
   C2_MacroAssembler _masm(&cbuf);
+  Label skip;
 
+  // UseCompressedClassPointers logic are inside cmp_klass
   __ cmp_klass(j_rarg0, rscratch2, rscratch1);
-  Label skip;
+
   // TODO
   // can we avoid this skip and still use a reloc?
   __ br(Assembler::EQ, skip);
   __ far_jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub()));
   __ bind(skip);
 }
 
-uint MachUEPNode::size(PhaseRegAlloc* ra_) const
-{
-  return MachNode::size(ra_);
-}
-
 // REQUIRED EMIT CODE
 
 //=============================================================================
 
 // Emit exception handler code.
@@ -2247,11 +2248,11 @@
   // br rscratch1
   // Note that the code buffer's insts_mark is always relative to insts.
   // That's why we must use the macroassembler to generate a handler.
   C2_MacroAssembler _masm(&cbuf);
   address base = __ start_a_stub(size_exception_handler());
-  if (base == NULL) {
+  if (base == nullptr) {
     ciEnv::current()->record_failure("CodeCache is full");
     return 0;  // CodeBuffer::expand failed
   }
   int offset = __ offset();
   __ far_jump(RuntimeAddress(OptoRuntime::exception_blob()->entry_point()));
@@ -2265,11 +2266,11 @@
 {
   // Note that the code buffer's insts_mark is always relative to insts.
   // That's why we must use the macroassembler to generate a handler.
   C2_MacroAssembler _masm(&cbuf);
   address base = __ start_a_stub(size_deopt_handler());
-  if (base == NULL) {
+  if (base == nullptr) {
     ciEnv::current()->record_failure("CodeCache is full");
     return 0;  // CodeBuffer::expand failed
   }
   int offset = __ offset();
 
@@ -2408,11 +2409,11 @@
     case Op_VecA: return new vecAOper();
     case Op_VecD: return new vecDOper();
     case Op_VecX: return new vecXOper();
   }
   ShouldNotReachHere();
-  return NULL;
+  return nullptr;
 }
 
 bool Matcher::is_reg2reg_move(MachNode* m) {
   return false;
 }
@@ -2581,11 +2582,11 @@
   return result;
 }
 
 // Binary src (Replicate con)
 bool is_valid_sve_arith_imm_pattern(Node* n, Node* m) {
-  if (n == NULL || m == NULL) {
+  if (n == nullptr || m == nullptr) {
     return false;
   }
 
   if (UseSVE == 0 || m->Opcode() != Op_Replicate) {
     return false;
@@ -2622,11 +2623,11 @@
 }
 
 // (XorV src (Replicate m1))
 // (XorVMask src (MaskAll m1))
 bool is_vector_bitwise_not_pattern(Node* n, Node* m) {
-  if (n != NULL && m != NULL) {
+  if (n != nullptr && m != nullptr) {
     return (n->Opcode() == Op_XorV || n->Opcode() == Op_XorVMask) &&
            VectorNode::is_all_ones_vector(m);
   }
   return false;
 }
@@ -3428,11 +3429,11 @@
 
   enc_class aarch64_enc_mov_p(iRegP dst, immP src) %{
     C2_MacroAssembler _masm(&cbuf);
     Register dst_reg = as_Register($dst$$reg);
     address con = (address)$src$$constant;
-    if (con == NULL || con == (address)1) {
+    if (con == nullptr || con == (address)1) {
       ShouldNotReachHere();
     } else {
       relocInfo::relocType rtype = $src->constant_reloc();
       if (rtype == relocInfo::oop_type) {
         __ movoop(dst_reg, (jobject)con);
@@ -3471,11 +3472,11 @@
 
   enc_class aarch64_enc_mov_n(iRegN dst, immN src) %{
     C2_MacroAssembler _masm(&cbuf);
     Register dst_reg = as_Register($dst$$reg);
     address con = (address)$src$$constant;
-    if (con == NULL) {
+    if (con == nullptr) {
       ShouldNotReachHere();
     } else {
       relocInfo::relocType rtype = $src->constant_reloc();
       assert(rtype == relocInfo::oop_type, "unexpected reloc type");
       __ set_narrow_oop(dst_reg, (jobject)con);
@@ -3490,11 +3491,11 @@
 
   enc_class aarch64_enc_mov_nk(iRegN dst, immNKlass src) %{
     C2_MacroAssembler _masm(&cbuf);
     Register dst_reg = as_Register($dst$$reg);
     address con = (address)$src$$constant;
-    if (con == NULL) {
+    if (con == nullptr) {
       ShouldNotReachHere();
     } else {
       relocInfo::relocType rtype = $src->constant_reloc();
       assert(rtype == relocInfo::metadata_type, "unexpected reloc type");
       __ set_narrow_klass(dst_reg, (Klass *)con);
@@ -3673,11 +3674,11 @@
      Register result_reg = as_Register($result$$reg);
 
      Label miss;
      C2_MacroAssembler _masm(&cbuf);
      __ check_klass_subtype_slow_path(sub_reg, super_reg, temp_reg, result_reg,
-                                     NULL, &miss,
+                                     nullptr, &miss,
                                      /*set_cond_codes:*/ true);
      if ($primary) {
        __ mov(result_reg, zr);
      }
      __ bind(miss);
@@ -3689,11 +3690,11 @@
     address addr = (address)$meth$$method;
     address call;
     if (!_method) {
       // A call to a runtime wrapper, e.g. new, new_typeArray_Java, uncommon_trap.
       call = __ trampoline_call(Address(addr, relocInfo::runtime_call_type));
-      if (call == NULL) {
+      if (call == nullptr) {
         ciEnv::current()->record_failure("CodeCache is full");
         return;
       }
     } else if (_method->intrinsic_id() == vmIntrinsicID::_ensureMaterializedForStackWalk) {
       // The NOP here is purely to ensure that eliding a call to
@@ -3703,22 +3704,22 @@
     } else {
       int method_index = resolved_method_index(cbuf);
       RelocationHolder rspec = _optimized_virtual ? opt_virtual_call_Relocation::spec(method_index)
                                                   : static_call_Relocation::spec(method_index);
       call = __ trampoline_call(Address(addr, rspec));
-      if (call == NULL) {
+      if (call == nullptr) {
         ciEnv::current()->record_failure("CodeCache is full");
         return;
       }
       if (CodeBuffer::supports_shared_stubs() && _method->can_be_statically_bound()) {
         // Calls of the same statically bound method can share
         // a stub to the interpreter.
         cbuf.shared_stub_to_interp_for(_method, call - cbuf.insts_begin());
       } else {
         // Emit stub for static call
         address stub = CompiledStaticCall::emit_to_interp_stub(cbuf, call);
-        if (stub == NULL) {
+        if (stub == nullptr) {
           ciEnv::current()->record_failure("CodeCache is full");
           return;
         }
       }
     }
@@ -3733,11 +3734,11 @@
 
   enc_class aarch64_enc_java_dynamic_call(method meth) %{
     C2_MacroAssembler _masm(&cbuf);
     int method_index = resolved_method_index(cbuf);
     address call = __ ic_call((address)$meth$$method, method_index);
-    if (call == NULL) {
+    if (call == nullptr) {
       ciEnv::current()->record_failure("CodeCache is full");
       return;
     }
     __ post_call_nop();
     if (Compile::current()->max_vector_size() > 0) {
@@ -3749,10 +3750,41 @@
     C2_MacroAssembler _masm(&cbuf);
     if (VerifyStackAtCalls) {
       // Check that stack depth is unchanged: find majik cookie on stack
       __ call_Unimplemented();
     }
+    if (tf()->returns_inline_type_as_fields() && !_method->is_method_handle_intrinsic()) {
+      // The last return value is not set by the callee but used to pass IsInit information to compiled code.
+      // Search for the corresponding projection, get the register and emit code that initialized it.
+      uint con = (tf()->range_cc()->cnt() - 1);
+      for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) {
+        ProjNode* proj = fast_out(i)->as_Proj();
+        if (proj->_con == con) {
+          // Set IsInit if r0 is non-null (a non-null value is returned buffered or scalarized)
+          OptoReg::Name optoReg = ra_->get_reg_first(proj);
+          VMReg reg = OptoReg::as_VMReg(optoReg, ra_->_framesize, OptoReg::reg2stack(ra_->_matcher._new_SP));
+          Register toReg = reg->is_reg() ? reg->as_Register() : rscratch1;
+          __ cmp(r0, zr);
+          __ cset(toReg, Assembler::NE);
+          if (reg->is_stack()) {
+            int st_off = reg->reg2stack() * VMRegImpl::stack_slot_size;
+            __ str(toReg, Address(sp, st_off));
+          }
+          break;
+        }
+      }
+      if (return_value_is_used()) {
+        // An inline type is returned as fields in multiple registers.
+        // R0 either contains an oop if the inline type is buffered or a pointer
+        // to the corresponding InlineKlass with the lowest bit set to 1. Zero r0
+        // if the lowest bit is set to allow C2 to use the oop after null checking.
+        // r0 &= (r0 & 1) - 1
+        __ andr(rscratch1, r0, 0x1);
+        __ sub(rscratch1, rscratch1, 0x1);
+        __ andr(r0, r0, rscratch1);
+      }
+    }
   %}
 
   enc_class aarch64_enc_java_to_runtime(method meth) %{
     C2_MacroAssembler _masm(&cbuf);
 
@@ -3762,11 +3794,11 @@
     // which loads the absolute address into a register.
     address entry = (address)$meth$$method;
     CodeBlob *cb = CodeCache::find_blob(entry);
     if (cb) {
       address call = __ trampoline_call(Address(entry, relocInfo::runtime_call_type));
-      if (call == NULL) {
+      if (call == nullptr) {
         ciEnv::current()->record_failure("CodeCache is full");
         return;
       }
       __ post_call_nop();
     } else {
@@ -4661,11 +4693,11 @@
   op_cost(0);
   format %{ %}
   interface(CONST_INTER);
 %}
 
-// NULL Pointer Immediate
+// nullptr Pointer Immediate
 operand immP0()
 %{
   predicate(n->get_ptr() == 0);
   match(ConP);
 
@@ -4793,11 +4825,11 @@
   op_cost(0);
   format %{ %}
   interface(CONST_INTER);
 %}
 
-// Narrow NULL Pointer Immediate
+// Narrow nullptr Pointer Immediate
 operand immN0()
 %{
   predicate(n->get_narrowcon() == 0);
   match(ConN);
 
@@ -7202,11 +7234,11 @@
 %{
   match(Set dst con);
 
   ins_cost(INSN_COST * 4);
   format %{
-    "mov  $dst, $con\t# ptr\n\t"
+    "mov  $dst, $con\t# ptr"
   %}
 
   ins_encode(aarch64_enc_mov_p(dst, con));
 
   ins_pipe(ialu_imm);
@@ -7217,11 +7249,11 @@
 instruct loadConP0(iRegPNoSp dst, immP0 con)
 %{
   match(Set dst con);
 
   ins_cost(INSN_COST);
-  format %{ "mov  $dst, $con\t# NULL ptr" %}
+  format %{ "mov  $dst, $con\t# nullptr ptr" %}
 
   ins_encode(aarch64_enc_mov_p0(dst, con));
 
   ins_pipe(ialu_imm);
 %}
@@ -7231,11 +7263,11 @@
 instruct loadConP1(iRegPNoSp dst, immP_1 con)
 %{
   match(Set dst con);
 
   ins_cost(INSN_COST);
-  format %{ "mov  $dst, $con\t# NULL ptr" %}
+  format %{ "mov  $dst, $con\t# nullptr ptr" %}
 
   ins_encode(aarch64_enc_mov_p1(dst, con));
 
   ins_pipe(ialu_imm);
 %}
@@ -7273,11 +7305,11 @@
 instruct loadConN0(iRegNNoSp dst, immN0 con)
 %{
   match(Set dst con);
 
   ins_cost(INSN_COST);
-  format %{ "mov  $dst, $con\t# compressed NULL ptr" %}
+  format %{ "mov  $dst, $con\t# compressed nullptr ptr" %}
 
   ins_encode(aarch64_enc_mov_n0(dst, con));
 
   ins_pipe(ialu_imm);
 %}
@@ -8423,10 +8455,25 @@
   %}
 
   ins_pipe(ialu_reg);
 %}
 
+instruct castN2X(iRegLNoSp dst, iRegN src) %{
+  match(Set dst (CastP2X src));
+
+  ins_cost(INSN_COST);
+  format %{ "mov $dst, $src\t# ptr -> long" %}
+
+  ins_encode %{
+    if ($dst$$reg != $src$$reg) {
+      __ mov(as_Register($dst$$reg), as_Register($src$$reg));
+    }
+  %}
+
+  ins_pipe(ialu_reg);
+%}
+
 instruct castP2X(iRegLNoSp dst, iRegP src) %{
   match(Set dst (CastP2X src));
 
   ins_cost(INSN_COST);
   format %{ "mov $dst, $src\t# ptr -> long" %}
@@ -15244,42 +15291,59 @@
 %}
 
 // ============================================================================
 // clearing of an array
 
-instruct clearArray_reg_reg(iRegL_R11 cnt, iRegP_R10 base, Universe dummy, rFlagsReg cr)
+instruct clearArray_reg_reg_immL0(iRegL_R11 cnt, iRegP_R10 base, immL0 zero, Universe dummy, rFlagsReg cr)
 %{
-  match(Set dummy (ClearArray cnt base));
+  match(Set dummy (ClearArray (Binary cnt base) zero));
   effect(USE_KILL cnt, USE_KILL base, KILL cr);
 
   ins_cost(4 * INSN_COST);
   format %{ "ClearArray $cnt, $base" %}
 
   ins_encode %{
     address tpc = __ zero_words($base$$Register, $cnt$$Register);
-    if (tpc == NULL) {
+    if (tpc == nullptr) {
       ciEnv::current()->record_failure("CodeCache is full");
       return;
     }
   %}
 
   ins_pipe(pipe_class_memory);
 %}
 
+instruct clearArray_reg_reg(iRegL_R11 cnt, iRegP_R10 base, iRegL val, Universe dummy, rFlagsReg cr)
+%{
+  predicate(((ClearArrayNode*)n)->word_copy_only());
+  match(Set dummy (ClearArray (Binary cnt base) val));
+  effect(USE_KILL cnt, USE_KILL base, KILL cr);
+
+  ins_cost(4 * INSN_COST);
+  format %{ "ClearArray $cnt, $base, $val" %}
+
+  ins_encode %{
+    __ fill_words($base$$Register, $cnt$$Register, $val$$Register);
+  %}
+
+  ins_pipe(pipe_class_memory);
+%}
+
 instruct clearArray_imm_reg(immL cnt, iRegP_R10 base, iRegL_R11 temp, Universe dummy, rFlagsReg cr)
 %{
   predicate((uint64_t)n->in(2)->get_long()
-            < (uint64_t)(BlockZeroingLowLimit >> LogBytesPerWord));
+            < (uint64_t)(BlockZeroingLowLimit >> LogBytesPerWord)
+            && !((ClearArrayNode*)n)->word_copy_only());
   match(Set dummy (ClearArray cnt base));
   effect(TEMP temp, USE_KILL base, KILL cr);
 
   ins_cost(4 * INSN_COST);
   format %{ "ClearArray $cnt, $base" %}
 
   ins_encode %{
     address tpc = __ zero_words($base$$Register, (uint64_t)$cnt$$constant);
-    if (tpc == NULL) {
+    if (tpc == nullptr) {
       ciEnv::current()->record_failure("CodeCache is full");
       return;
     }
   %}
 
@@ -16566,12 +16630,32 @@
   ins_pipe(pipe_class_call);
 %}
 
 // Call Runtime Instruction
 
+// entry point is null, target holds the address to call
+instruct CallLeafNoFPIndirect(iRegP target)
+%{
+  predicate(n->as_Call()->entry_point() == nullptr);
+
+  match(CallLeafNoFP target);
+
+  ins_cost(CALL_COST);
+
+  format %{ "CALL, runtime leaf nofp indirect $target" %}
+
+  ins_encode %{
+    __ blr($target$$Register);
+  %}
+
+  ins_pipe(pipe_class_call);
+%}
+
 instruct CallLeafNoFPDirect(method meth)
 %{
+  predicate(n->as_Call()->entry_point() != nullptr);
+
   match(CallLeafNoFP);
 
   effect(USE meth);
 
   ins_cost(CALL_COST);
@@ -17140,11 +17224,11 @@
   format %{ "Array Equals $ary1,ary2 -> $result # KILL $ary1 $ary2 $tmp $tmp1 $tmp2 $tmp3 V0-V7 cr" %}
   ins_encode %{
     address tpc = __ arrays_equals($ary1$$Register, $ary2$$Register,
                                    $tmp1$$Register, $tmp2$$Register, $tmp3$$Register,
                                    $result$$Register, $tmp$$Register, 1);
-    if (tpc == NULL) {
+    if (tpc == nullptr) {
       ciEnv::current()->record_failure("CodeCache is full");
       return;
     }
   %}
   ins_pipe(pipe_class_memory);
@@ -17165,11 +17249,11 @@
   format %{ "Array Equals $ary1,ary2 -> $result # KILL $ary1 $ary2 $tmp $tmp1 $tmp2 $tmp3 V0-V7 cr" %}
   ins_encode %{
     address tpc = __ arrays_equals($ary1$$Register, $ary2$$Register,
                                    $tmp1$$Register, $tmp2$$Register, $tmp3$$Register,
                                    $result$$Register, $tmp$$Register, 2);
-    if (tpc == NULL) {
+    if (tpc == nullptr) {
       ciEnv::current()->record_failure("CodeCache is full");
       return;
     }
   %}
   ins_pipe(pipe_class_memory);
@@ -17180,11 +17264,11 @@
   match(Set result (CountPositives ary1 len));
   effect(USE_KILL ary1, USE_KILL len, KILL cr);
   format %{ "count positives byte[] $ary1,$len -> $result" %}
   ins_encode %{
     address tpc = __ count_positives($ary1$$Register, $len$$Register, $result$$Register);
-    if (tpc == NULL) {
+    if (tpc == nullptr) {
       ciEnv::current()->record_failure("CodeCache is full");
       return;
     }
   %}
   ins_pipe( pipe_slow );
@@ -17223,11 +17307,11 @@
   format %{ "String Inflate $src,$dst # KILL $tmp $src $dst $len V0-V6 cr" %}
   ins_encode %{
     address tpc = __ byte_array_inflate($src$$Register, $dst$$Register, $len$$Register,
                                         $vtmp0$$FloatRegister, $vtmp1$$FloatRegister,
                                         $vtmp2$$FloatRegister, $tmp$$Register);
-    if (tpc == NULL) {
+    if (tpc == nullptr) {
       ciEnv::current()->record_failure("CodeCache is full");
       return;
     }
   %}
   ins_pipe(pipe_class_memory);
diff a/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp
@@ -118,10 +118,76 @@
 #ifdef ASSERT
   __ should_not_reach_here();
 #endif
 }
 
+// Implementation of LoadFlattenedArrayStub
+
+LoadFlattenedArrayStub::LoadFlattenedArrayStub(LIR_Opr array, LIR_Opr index, LIR_Opr result, CodeEmitInfo* info) {
+  _array = array;
+  _index = index;
+  _result = result;
+  _scratch_reg = FrameMap::r0_oop_opr;
+  _info = new CodeEmitInfo(info);
+}
+
+void LoadFlattenedArrayStub::emit_code(LIR_Assembler* ce) {
+  assert(__ rsp_offset() == 0, "frame size should be fixed");
+  __ bind(_entry);
+  ce->store_parameter(_array->as_register(), 1);
+  ce->store_parameter(_index->as_register(), 0);
+  __ far_call(RuntimeAddress(Runtime1::entry_for(Runtime1::load_flat_array_id)));
+  ce->add_call_info_here(_info);
+  ce->verify_oop_map(_info);
+  if (_result->as_register() != r0) {
+    __ mov(_result->as_register(), r0);
+  }
+  __ b(_continuation);
+}
+
+
+// Implementation of StoreFlattenedArrayStub
+
+StoreFlattenedArrayStub::StoreFlattenedArrayStub(LIR_Opr array, LIR_Opr index, LIR_Opr value, CodeEmitInfo* info) {
+  _array = array;
+  _index = index;
+  _value = value;
+  _scratch_reg = FrameMap::r0_oop_opr;
+  _info = new CodeEmitInfo(info);
+}
+
+
+void StoreFlattenedArrayStub::emit_code(LIR_Assembler* ce) {
+  assert(__ rsp_offset() == 0, "frame size should be fixed");
+  __ bind(_entry);
+  ce->store_parameter(_array->as_register(), 2);
+  ce->store_parameter(_index->as_register(), 1);
+  ce->store_parameter(_value->as_register(), 0);
+  __ far_call(RuntimeAddress(Runtime1::entry_for(Runtime1::store_flat_array_id)));
+  ce->add_call_info_here(_info);
+  ce->verify_oop_map(_info);
+  __ b(_continuation);
+}
+
+// Implementation of SubstitutabilityCheckStub
+SubstitutabilityCheckStub::SubstitutabilityCheckStub(LIR_Opr left, LIR_Opr right, CodeEmitInfo* info) {
+  _left = left;
+  _right = right;
+  _scratch_reg = FrameMap::r0_oop_opr;
+  _info = new CodeEmitInfo(info);
+}
+
+void SubstitutabilityCheckStub::emit_code(LIR_Assembler* ce) {
+  assert(__ rsp_offset() == 0, "frame size should be fixed");
+  __ bind(_entry);
+  ce->store_parameter(_left->as_register(), 1);
+  ce->store_parameter(_right->as_register(), 0);
+  __ far_call(RuntimeAddress(Runtime1::entry_for(Runtime1::substitutability_check_id)));
+  ce->add_call_info_here(_info);
+  ce->verify_oop_map(_info);
+  __ b(_continuation);
+}
 
 
 // Implementation of NewInstanceStub
 
 NewInstanceStub::NewInstanceStub(LIR_Opr klass_reg, LIR_Opr result, ciInstanceKlass* klass, CodeEmitInfo* info, Runtime1::StubID stub_id) {
@@ -134,12 +200,10 @@
          stub_id == Runtime1::fast_new_instance_init_check_id,
          "need new_instance id");
   _stub_id   = stub_id;
 }
 
-
-
 void NewInstanceStub::emit_code(LIR_Assembler* ce) {
   assert(__ rsp_offset() == 0, "frame size should be fixed");
   __ bind(_entry);
   __ mov(r3, _klass_reg->as_register());
   __ far_call(RuntimeAddress(Runtime1::entry_for(_stub_id)));
@@ -175,33 +239,51 @@
 }
 
 
 // Implementation of NewObjectArrayStub
 
-NewObjectArrayStub::NewObjectArrayStub(LIR_Opr klass_reg, LIR_Opr length, LIR_Opr result, CodeEmitInfo* info) {
+NewObjectArrayStub::NewObjectArrayStub(LIR_Opr klass_reg, LIR_Opr length, LIR_Opr result,
+                                       CodeEmitInfo* info, bool is_null_free) {
   _klass_reg = klass_reg;
   _result = result;
   _length = length;
   _info = new CodeEmitInfo(info);
+  _is_null_free = is_null_free;
 }
 
 
 void NewObjectArrayStub::emit_code(LIR_Assembler* ce) {
   assert(__ rsp_offset() == 0, "frame size should be fixed");
   __ bind(_entry);
   assert(_length->as_register() == r19, "length must in r19,");
   assert(_klass_reg->as_register() == r3, "klass_reg must in r3");
-  __ far_call(RuntimeAddress(Runtime1::entry_for(Runtime1::new_object_array_id)));
+
+  if (_is_null_free) {
+    __ far_call(RuntimeAddress(Runtime1::entry_for(Runtime1::new_flat_array_id)));
+  } else {
+    __ far_call(RuntimeAddress(Runtime1::entry_for(Runtime1::new_object_array_id)));
+  }
+
   ce->add_call_info_here(_info);
   ce->verify_oop_map(_info);
   assert(_result->as_register() == r0, "result must in r0");
   __ b(_continuation);
 }
 
 void MonitorEnterStub::emit_code(LIR_Assembler* ce) {
   assert(__ rsp_offset() == 0, "frame size should be fixed");
   __ bind(_entry);
+  if (_throw_imse_stub != nullptr) {
+    // When we come here, _obj_reg has already been checked to be non-null.
+    __ ldr(rscratch1, Address(_obj_reg->as_register(), oopDesc::mark_offset_in_bytes()));
+    __ mov(rscratch2, markWord::inline_type_pattern);
+    __ andr(rscratch1, rscratch1, rscratch2);
+
+    __ cmp(rscratch1, rscratch2);
+    __ br(Assembler::EQ, *_throw_imse_stub->entry());
+  }
+
   ce->store_parameter(_obj_reg->as_register(),  1);
   ce->store_parameter(_lock_reg->as_register(), 0);
   Runtime1::StubID enter_id;
   if (ce->compilation()->has_fpu_code()) {
     enter_id = Runtime1::monitorenter_id;
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
@@ -31,16 +31,18 @@
 #include "c1/c1_LIRAssembler.hpp"
 #include "c1/c1_MacroAssembler.hpp"
 #include "c1/c1_Runtime1.hpp"
 #include "c1/c1_ValueStack.hpp"
 #include "ci/ciArrayKlass.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "ci/ciInstance.hpp"
 #include "code/compiledIC.hpp"
 #include "gc/shared/collectedHeap.hpp"
 #include "gc/shared/gc_globals.hpp"
 #include "nativeInst_aarch64.hpp"
 #include "oops/objArrayKlass.hpp"
+#include "oops/oop.inline.hpp"
 #include "runtime/frame.inline.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/stubRoutines.hpp"
 #include "utilities/powerOfTwo.hpp"
 #include "vmreg_aarch64.inline.hpp"
@@ -450,11 +452,11 @@
     __ mov(r0, r19);  // Restore the exception
   }
 
   // remove the activation and dispatch to the unwind handler
   __ block_comment("remove_frame and dispatch to the unwind handler");
-  __ remove_frame(initial_frame_size_in_bytes());
+  __ remove_frame(initial_frame_size_in_bytes(), needs_stack_repair());
   __ far_jump(RuntimeAddress(Runtime1::entry_for(Runtime1::unwind_exception_id)));
 
   // Emit the slow path assembly
   if (stub != nullptr) {
     stub->emit_code(this);
@@ -494,12 +496,40 @@
 }
 
 void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) {
   assert(result->is_illegal() || !result->is_single_cpu() || result->as_register() == r0, "word returns are in r0,");
 
+  if (InlineTypeReturnedAsFields) {
+    // Check if we are returning an non-null inline type and load its fields into registers
+    ciType* return_type = compilation()->method()->return_type();
+    if (return_type->is_inlinetype()) {
+      ciInlineKlass* vk = return_type->as_inline_klass();
+      if (vk->can_be_returned_as_fields()) {
+        address unpack_handler = vk->unpack_handler();
+        assert(unpack_handler != nullptr, "must be");
+        __ far_call(RuntimeAddress(unpack_handler));
+      }
+    } else if (return_type->is_instance_klass() && (!return_type->is_loaded() || StressCallingConvention)) {
+      Label skip;
+      __ test_oop_is_not_inline_type(r0, rscratch2, skip);
+
+      // Load fields from a buffered value with an inline class specific handler
+      __ load_klass(rscratch1 /*dst*/, r0 /*src*/);
+      __ ldr(rscratch1, Address(rscratch1, InstanceKlass::adr_inlineklass_fixed_block_offset()));
+      __ ldr(rscratch1, Address(rscratch1, InlineKlass::unpack_handler_offset()));
+      // Unpack handler can be null if inline type is not scalarizable in returns
+      __ cbz(rscratch1, skip);
+      __ blr(rscratch1);
+
+      __ bind(skip);
+    }
+    // At this point, r0 points to the value object (for interpreter or C1 caller).
+    // The fields of the object are copied into registers (for C2 caller).
+  }
+
   // Pop the stack before the safepoint code
-  __ remove_frame(initial_frame_size_in_bytes());
+  __ remove_frame(initial_frame_size_in_bytes(), needs_stack_repair());
 
   if (StackReservedPages > 0 && compilation()->has_reserved_stack_access()) {
     __ reserved_stack_check();
   }
 
@@ -507,10 +537,14 @@
   __ relocate(relocInfo::poll_return_type);
   __ safepoint_poll(*code_stub->entry(), true /* at_return */, false /* acquire */, true /* in_nmethod */);
   __ ret(lr);
 }
 
+int LIR_Assembler::store_inline_type_fields_to_buf(ciInlineKlass* vk) {
+  return (__ store_inline_type_fields_to_buf(vk, false));
+}
+
 int LIR_Assembler::safepoint_poll(LIR_Opr tmp, CodeEmitInfo* info) {
   guarantee(info != nullptr, "Shouldn't be null");
   __ get_polling_page(rscratch1, relocInfo::poll_type);
   add_debug_info_for_branch(info);  // This isn't just debug info:
                                     // it's the oop map
@@ -553,14 +587,14 @@
       __ mov(dest->as_register_lo(), (intptr_t)c->as_jlong());
       break;
     }
 
     case T_OBJECT: {
-        if (patch_code == lir_patch_none) {
-          jobject2reg(c->as_jobject(), dest->as_register());
-        } else {
+        if (patch_code != lir_patch_none) {
           jobject2reg_with_patching(dest->as_register(), info);
+        } else {
+          jobject2reg(c->as_jobject(), dest->as_register());
         }
       break;
     }
 
     case T_METADATA: {
@@ -666,10 +700,12 @@
     assert(c->as_jint() == 0, "should be");
     insn = &Assembler::strw;
     break;
   case T_OBJECT:
   case T_ARRAY:
+    // Non-null case is not handled on aarch64 but handled on x86
+    // FIXME: do we need to add it here?
     assert(c->as_jobject() == 0, "should be");
     if (UseCompressedOops && !wide) {
       insn = &Assembler::strw;
     } else {
       insn = &Assembler::str;
@@ -1016,10 +1052,24 @@
       __ verify_oop(dest->as_register());
     }
   }
 }
 
+void LIR_Assembler::move(LIR_Opr src, LIR_Opr dst) {
+  assert(dst->is_cpu_register(), "must be");
+  assert(dst->type() == src->type(), "must be");
+
+  if (src->is_cpu_register()) {
+    reg2reg(src, dst);
+  } else if (src->is_stack()) {
+    stack2reg(src, dst, dst->type());
+  } else if (src->is_constant()) {
+    const2reg(src, dst, lir_patch_none, nullptr);
+  } else {
+    ShouldNotReachHere();
+  }
+}
 
 int LIR_Assembler::array_element_size(BasicType type) const {
   int elem_size = type2aelembytes(type);
   return exact_log2(elem_size);
 }
@@ -1207,11 +1257,11 @@
 
 void LIR_Assembler::emit_alloc_array(LIR_OpAllocArray* op) {
   Register len =  op->len()->as_register();
   __ uxtw(len, len);
 
-  if (UseSlowPath ||
+  if (UseSlowPath || op->is_null_free() ||
       (!UseFastNewObjectArray && is_reference_type(op->type())) ||
       (!UseFastNewTypeArray   && !is_reference_type(op->type()))) {
     __ b(*op->stub()->entry());
   } else {
     Register tmp1 = op->tmp1()->as_register();
@@ -1312,36 +1362,38 @@
     select_different_registers(obj, dst, k_RInfo, klass_RInfo, Rtmp1);
   }
 
   assert_different_registers(obj, k_RInfo, klass_RInfo);
 
-  if (should_profile) {
-    Register mdo  = klass_RInfo;
-    __ mov_metadata(mdo, md->constant_encoding());
-    Label not_null;
-    __ cbnz(obj, not_null);
-    // Object is null; update MDO and exit
-    Address data_addr
-      = __ form_address(rscratch2, mdo,
-                        md->byte_offset_of_slot(data, DataLayout::flags_offset()),
-                        0);
-    __ ldrb(rscratch1, data_addr);
-    __ orr(rscratch1, rscratch1, BitData::null_seen_byte_constant());
-    __ strb(rscratch1, data_addr);
-    __ b(*obj_is_null);
-    __ bind(not_null);
-
-    Label update_done;
-    Register recv = k_RInfo;
-    __ load_klass(recv, obj);
-    type_profile_helper(mdo, md, data, recv, &update_done);
-    Address counter_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset()));
-    __ addptr(counter_addr, DataLayout::counter_increment);
+  if (op->need_null_check()) {
+    if (should_profile) {
+      Register mdo  = klass_RInfo;
+      __ mov_metadata(mdo, md->constant_encoding());
+      Label not_null;
+      __ cbnz(obj, not_null);
+      // Object is null; update MDO and exit
+      Address data_addr
+        = __ form_address(rscratch2, mdo,
+                          md->byte_offset_of_slot(data, DataLayout::flags_offset()),
+                          0);
+      __ ldrb(rscratch1, data_addr);
+      __ orr(rscratch1, rscratch1, BitData::null_seen_byte_constant());
+      __ strb(rscratch1, data_addr);
+      __ b(*obj_is_null);
+      __ bind(not_null);
 
-    __ bind(update_done);
-  } else {
-    __ cbz(obj, *obj_is_null);
+      Label update_done;
+      Register recv = k_RInfo;
+      __ load_klass(recv, obj);
+      type_profile_helper(mdo, md, data, recv, &update_done);
+      Address counter_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset()));
+      __ addptr(counter_addr, DataLayout::counter_increment);
+
+      __ bind(update_done);
+    } else {
+      __ cbz(obj, *obj_is_null);
+    }
   }
 
   if (!k->is_loaded()) {
     klass2reg_with_patching(k_RInfo, op->info_for_patch());
   } else {
@@ -1496,10 +1548,137 @@
   } else {
     ShouldNotReachHere();
   }
 }
 
+void LIR_Assembler::emit_opFlattenedArrayCheck(LIR_OpFlattenedArrayCheck* op) {
+  // We are loading/storing from/to an array that *may* be a flat array (the
+  // declared type is Object[], abstract[], interface[] or VT.ref[]).
+  // If this array is a flat array, take the slow path.
+
+  Register klass = op->tmp()->as_register();
+  if (UseArrayMarkWordCheck) {
+    __ test_flat_array_oop(op->array()->as_register(), op->tmp()->as_register(), *op->stub()->entry());
+  } else {
+    __ load_klass(klass, op->array()->as_register());
+    __ ldrw(klass, Address(klass, Klass::layout_helper_offset()));
+    __ tst(klass, Klass::_lh_array_tag_flat_value_bit_inplace);
+    __ br(Assembler::NE, *op->stub()->entry());
+  }
+  if (!op->value()->is_illegal()) {
+    // The array is not a flat array, but it might be null-free. If we are storing
+    // a null into a null-free array, take the slow path (which will throw NPE).
+    Label skip;
+    __ cbnz(op->value()->as_register(), skip);
+    if (UseArrayMarkWordCheck) {
+      __ test_null_free_array_oop(op->array()->as_register(), op->tmp()->as_register(), *op->stub()->entry());
+    } else {
+      __ tst(klass, Klass::_lh_null_free_array_bit_inplace);
+      __ br(Assembler::NE, *op->stub()->entry());
+    }
+    __ bind(skip);
+  }
+}
+
+void LIR_Assembler::emit_opNullFreeArrayCheck(LIR_OpNullFreeArrayCheck* op) {
+  // We are storing into an array that *may* be null-free (the declared type is
+  // Object[], abstract[], interface[] or VT.ref[]).
+  if (UseArrayMarkWordCheck) {
+    Label test_mark_word;
+    Register tmp = op->tmp()->as_register();
+    __ ldr(tmp, Address(op->array()->as_register(), oopDesc::mark_offset_in_bytes()));
+    __ tst(tmp, markWord::unlocked_value);
+    __ br(Assembler::NE, test_mark_word);
+    __ load_prototype_header(tmp, op->array()->as_register());
+    __ bind(test_mark_word);
+    __ tst(tmp, markWord::null_free_array_bit_in_place);
+  } else {
+    Register klass = op->tmp()->as_register();
+    __ load_klass(klass, op->array()->as_register());
+    __ ldr(klass, Address(klass, Klass::layout_helper_offset()));
+    __ tst(klass, Klass::_lh_null_free_array_bit_inplace);
+  }
+}
+
+void LIR_Assembler::emit_opSubstitutabilityCheck(LIR_OpSubstitutabilityCheck* op) {
+  Label L_oops_equal;
+  Label L_oops_not_equal;
+  Label L_end;
+
+  Register left  = op->left()->as_register();
+  Register right = op->right()->as_register();
+
+  __ cmp(left, right);
+  __ br(Assembler::EQ, L_oops_equal);
+
+  // (1) Null check -- if one of the operands is null, the other must not be null (because
+  //     the two references are not equal), so they are not substitutable,
+  //     FIXME: do null check only if the operand is nullable
+  {
+    __ cbz(left, L_oops_not_equal);
+    __ cbz(right, L_oops_not_equal);
+  }
+
+  ciKlass* left_klass = op->left_klass();
+  ciKlass* right_klass = op->right_klass();
+
+  // (2) Inline type check -- if either of the operands is not a inline type,
+  //     they are not substitutable. We do this only if we are not sure that the
+  //     operands are inline type
+  if ((left_klass == nullptr || right_klass == nullptr) ||// The klass is still unloaded, or came from a Phi node.
+      !left_klass->is_inlinetype() || !right_klass->is_inlinetype()) {
+    Register tmp1  = op->tmp1()->as_register();
+    __ mov(tmp1, markWord::inline_type_pattern);
+    __ ldr(rscratch1, Address(left, oopDesc::mark_offset_in_bytes()));
+    __ andr(tmp1, tmp1, rscratch1);
+    __ ldr(rscratch1, Address(right, oopDesc::mark_offset_in_bytes()));
+    __ andr(tmp1, tmp1, rscratch1);
+    __ cmp(tmp1, (u1)markWord::inline_type_pattern);
+    __ br(Assembler::NE, L_oops_not_equal);
+  }
+
+  // (3) Same klass check: if the operands are of different klasses, they are not substitutable.
+  if (left_klass != nullptr && left_klass->is_inlinetype() && left_klass == right_klass) {
+    // No need to load klass -- the operands are statically known to be the same inline klass.
+    __ b(*op->stub()->entry());
+  } else {
+    Register left_klass_op = op->left_klass_op()->as_register();
+    Register right_klass_op = op->right_klass_op()->as_register();
+
+    if (UseCompressedClassPointers) {
+      __ ldrw(left_klass_op,  Address(left,  oopDesc::klass_offset_in_bytes()));
+      __ ldrw(right_klass_op, Address(right, oopDesc::klass_offset_in_bytes()));
+      __ cmpw(left_klass_op, right_klass_op);
+    } else {
+      __ ldr(left_klass_op,  Address(left,  oopDesc::klass_offset_in_bytes()));
+      __ ldr(right_klass_op, Address(right, oopDesc::klass_offset_in_bytes()));
+      __ cmp(left_klass_op, right_klass_op);
+    }
+
+    __ br(Assembler::EQ, *op->stub()->entry()); // same klass -> do slow check
+    // fall through to L_oops_not_equal
+  }
+
+  __ bind(L_oops_not_equal);
+  move(op->not_equal_result(), op->result_opr());
+  __ b(L_end);
+
+  __ bind(L_oops_equal);
+  move(op->equal_result(), op->result_opr());
+  __ b(L_end);
+
+  // We've returned from the stub. R0 contains 0x0 IFF the two
+  // operands are not substitutable. (Don't compare against 0x1 in case the
+  // C compiler is naughty)
+  __ bind(*op->stub()->continuation());
+  __ cbz(r0, L_oops_not_equal); // (call_stub() == 0x0) -> not_equal
+  move(op->equal_result(), op->result_opr()); // (call_stub() != 0x0) -> equal
+  // fall-through
+  __ bind(L_end);
+}
+
+
 void LIR_Assembler::casw(Register addr, Register newval, Register cmpval) {
   __ cmpxchg(addr, cmpval, newval, Assembler::word, /* acquire*/ true, /* release*/ true, /* weak*/ false, rscratch1);
   __ cset(rscratch1, Assembler::NE);
   __ membar(__ AnyAny);
 }
@@ -2012,22 +2191,22 @@
   address call = __ trampoline_call(Address(op->addr(), rtype));
   if (call == nullptr) {
     bailout("trampoline stub overflow");
     return;
   }
-  add_call_info(code_offset(), op->info());
+  add_call_info(code_offset(), op->info(), op->maybe_return_as_fields());
   __ post_call_nop();
 }
 
 
 void LIR_Assembler::ic_call(LIR_OpJavaCall* op) {
   address call = __ ic_call(op->addr());
   if (call == nullptr) {
     bailout("trampoline stub overflow");
     return;
   }
-  add_call_info(code_offset(), op->info());
+  add_call_info(code_offset(), op->info(), op->maybe_return_as_fields());
   __ post_call_nop();
 }
 
 void LIR_Assembler::emit_static_call_stub() {
   address call_pc = __ pc();
@@ -2186,10 +2365,32 @@
   assert(offset_from_rsp_in_bytes < frame_map()->reserved_argument_area_size(), "invalid offset");
   __ lea(rscratch1, __ constant_oop_address(o));
   __ str(rscratch1, Address(sp, offset_from_rsp_in_bytes));
 }
 
+void LIR_Assembler::arraycopy_inlinetype_check(Register obj, Register tmp, CodeStub* slow_path, bool is_dest, bool null_check) {
+  if (null_check) {
+    __ cbz(obj, *slow_path->entry());
+  }
+  if (UseArrayMarkWordCheck) {
+    if (is_dest) {
+      __ test_null_free_array_oop(obj, tmp, *slow_path->entry());
+    } else {
+      __ test_flat_array_oop(obj, tmp, *slow_path->entry());
+    }
+  } else {
+    __ load_klass(tmp, obj);
+    __ ldr(tmp, Address(tmp, Klass::layout_helper_offset()));
+    if (is_dest) {
+      // Take the slow path if it's a null_free destination array, in case the source array contains nulls.
+      __ tst(tmp, Klass::_lh_null_free_array_bit_inplace);
+    } else {
+      __ tst(tmp, Klass::_lh_array_tag_flat_value_bit_inplace);
+    }
+    __ br(Assembler::NE, *slow_path->entry());
+  }
+}
 
 // This code replaces a call to arraycopy; no exception may
 // be thrown in this code, they must be thrown in the System.arraycopy
 // activation frame; we could save some checks if this would not be the case
 void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) {
@@ -2204,10 +2405,16 @@
   CodeStub* stub = op->stub();
   int flags = op->flags();
   BasicType basic_type = default_type != nullptr ? default_type->element_type()->basic_type() : T_ILLEGAL;
   if (is_reference_type(basic_type)) basic_type = T_OBJECT;
 
+  if (flags & LIR_OpArrayCopy::always_slow_path) {
+    __ b(*stub->entry());
+    __ bind(*stub->continuation());
+    return;
+  }
+
   // if we don't know anything, just go through the generic arraycopy
   if (default_type == nullptr // || basic_type == T_OBJECT
       ) {
     Label done;
     assert(src == r1 && src_pos == r2, "mismatch in calling convention");
@@ -2257,10 +2464,19 @@
 
     __ bind(*stub->continuation());
     return;
   }
 
+  // Handle inline type arrays
+  if (flags & LIR_OpArrayCopy::src_inlinetype_check) {
+    arraycopy_inlinetype_check(src, tmp, stub, false, (flags & LIR_OpArrayCopy::src_null_check));
+  }
+
+  if (flags & LIR_OpArrayCopy::dst_inlinetype_check) {
+    arraycopy_inlinetype_check(dst, tmp, stub, true, (flags & LIR_OpArrayCopy::dst_null_check));
+  }
+
   assert(default_type != nullptr && default_type->is_array_klass() && default_type->is_loaded(), "must be true at this point");
 
   int elem_size = type2aelembytes(basic_type);
   int scale = exact_log2(elem_size);
 
@@ -2844,10 +3060,30 @@
     __ bind(next);
   }
   COMMENT("} emit_profile_type");
 }
 
+void LIR_Assembler::emit_profile_inline_type(LIR_OpProfileInlineType* op) {
+  Register obj = op->obj()->as_register();
+  Register tmp = op->tmp()->as_pointer_register();
+  bool not_null = op->not_null();
+  int flag = op->flag();
+
+  Label not_inline_type;
+  if (!not_null) {
+    __ cbz(obj, not_inline_type);
+  }
+
+  __ test_oop_is_not_inline_type(obj, tmp, not_inline_type);
+
+  Address mdo_addr = as_Address(op->mdp()->as_address_ptr(), rscratch2);
+  __ ldrb(rscratch1, mdo_addr);
+  __ orr(rscratch1, rscratch1, flag);
+  __ strb(rscratch1, mdo_addr);
+
+  __ bind(not_inline_type);
+}
 
 void LIR_Assembler::align_backward_branch_target() {
 }
 
 
@@ -2984,10 +3220,14 @@
 
 void LIR_Assembler::get_thread(LIR_Opr result_reg) {
   __ mov(result_reg->as_register(), rthread);
 }
 
+void LIR_Assembler::check_orig_pc() {
+  __ ldr(rscratch2, frame_map()->address_for_orig_pc_addr());
+  __ cmp(rscratch2, (u1)NULL_WORD);
+}
 
 void LIR_Assembler::peephole(LIR_List *lir) {
 #if 0
   if (tableswitch_count >= max_tableswitches)
     return;
diff a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp
--- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp
@@ -76,10 +76,13 @@
     _call_stub_size = 13 * NativeInstruction::instruction_size,
     _exception_handler_size = DEBUG_ONLY(1*K) NOT_DEBUG(175),
     _deopt_handler_size = 7 * NativeInstruction::instruction_size
   };
 
+  void arraycopy_inlinetype_check(Register obj, Register tmp, CodeStub* slow_path, bool is_dest, bool null_check);
+  void move(LIR_Opr src, LIR_Opr dst);
+
 public:
 
   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);
diff a/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp
@@ -31,10 +31,11 @@
 #include "c1/c1_LIRAssembler.hpp"
 #include "c1/c1_LIRGenerator.hpp"
 #include "c1/c1_Runtime1.hpp"
 #include "c1/c1_ValueStack.hpp"
 #include "ci/ciArray.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "ci/ciObjArrayKlass.hpp"
 #include "ci/ciTypeArrayKlass.hpp"
 #include "compiler/compilerDefinitions.inline.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/stubRoutines.hpp"
@@ -101,10 +102,16 @@
   set_vreg_flag(reg, LIRGenerator::byte_reg);
   return reg;
 }
 
 
+void LIRGenerator::init_temps_for_substitutability_check(LIR_Opr& tmp1, LIR_Opr& tmp2) {
+  tmp1 = new_register(T_INT);
+  tmp2 = LIR_OprFact::illegalOpr;
+}
+
+
 //--------- loading items into registers --------------------------------
 
 
 bool LIRGenerator::can_store_as_constant(Value v, BasicType type) const {
   if (v->type()->as_IntConstant() != nullptr) {
@@ -318,15 +325,21 @@
 
   CodeEmitInfo* info_for_exception = nullptr;
   if (x->needs_null_check()) {
     info_for_exception = state_for(x);
   }
+
+  CodeStub* throw_imse_stub =
+      x->maybe_inlinetype() ?
+      new SimpleExceptionStub(Runtime1::throw_illegal_monitor_state_exception_id, LIR_OprFact::illegalOpr, state_for(x)) :
+      nullptr;
+
   // this CodeEmitInfo must not have the xhandlers because here the
   // object is already locked (xhandlers expect object to be unlocked)
   CodeEmitInfo* info = state_for(x, x->state(), true);
   monitor_enter(obj.result(), lock, syncTempOpr(), scratch,
-                        x->monitor_no(), info_for_exception, info);
+                x->monitor_no(), info_for_exception, info, throw_imse_stub);
 }
 
 
 void LIRGenerator::do_MonitorExit(MonitorExit* x) {
   assert(x->is_pinned(),"");
@@ -1121,18 +1134,19 @@
 #ifndef PRODUCT
   if (PrintNotLoaded && !x->klass()->is_loaded()) {
     tty->print_cr("   ###class not loaded at new bci %d", x->printable_bci());
   }
 #endif
-  CodeEmitInfo* info = state_for(x, x->state());
+  CodeEmitInfo* info = state_for(x, x->needs_state_before() ? x->state_before() : x->state());
   LIR_Opr reg = result_register_for(x->type());
   new_instance(reg, x->klass(), x->is_unresolved(),
-                       FrameMap::r10_oop_opr,
-                       FrameMap::r11_oop_opr,
-                       FrameMap::r4_oop_opr,
-                       LIR_OprFact::illegalOpr,
-                       FrameMap::r3_metadata_opr, info);
+               /* allow_inline */ false,
+               FrameMap::r10_oop_opr,
+               FrameMap::r11_oop_opr,
+               FrameMap::r4_oop_opr,
+               LIR_OprFact::illegalOpr,
+               FrameMap::r3_metadata_opr, info);
   LIR_Opr result = rlock_result(x);
   __ move(reg, result);
 }
 
 void LIRGenerator::do_NewTypeArray(NewTypeArray* x) {
@@ -1151,11 +1165,11 @@
   BasicType elem_type = x->elt_type();
 
   __ metadata2reg(ciTypeArrayKlass::make(elem_type)->constant_encoding(), klass_reg);
 
   CodeStub* slow_path = new NewTypeArrayStub(klass_reg, len, reg, info);
-  __ allocate_array(reg, len, tmp1, tmp2, tmp3, tmp4, elem_type, klass_reg, slow_path);
+  __ allocate_array(reg, len, tmp1, tmp2, tmp3, tmp4, elem_type, klass_reg, slow_path, false);
 
   LIR_Opr result = rlock_result(x);
   __ move(reg, result);
 }
 
@@ -1178,17 +1192,18 @@
   LIR_Opr klass_reg = FrameMap::r3_metadata_opr;
 
   length.load_item_force(FrameMap::r19_opr);
   LIR_Opr len = length.result();
 
-  CodeStub* slow_path = new NewObjectArrayStub(klass_reg, len, reg, info);
-  ciKlass* obj = (ciKlass*) ciObjArrayKlass::make(x->klass());
+  ciKlass* obj = (ciKlass*) x->exact_type();
+  CodeStub* slow_path = new NewObjectArrayStub(klass_reg, len, reg, info, x->is_null_free());
   if (obj == ciEnv::unloaded_ciobjarrayklass()) {
     BAILOUT("encountered unloaded_ciobjarrayklass due to out of memory error");
   }
+
   klass2reg_with_patching(klass_reg, obj, patching_info);
-  __ allocate_array(reg, len, tmp1, tmp2, tmp3, tmp4, T_OBJECT, klass_reg, slow_path);
+  __ allocate_array(reg, len, tmp1, tmp2, tmp3, tmp4, T_OBJECT, klass_reg, slow_path, x->is_null_free());
 
   LIR_Opr result = rlock_result(x);
   __ move(reg, result);
 }
 
@@ -1260,10 +1275,13 @@
 
   // info for exceptions
   CodeEmitInfo* info_for_exception =
       (x->needs_exception_state() ? state_for(x) :
                                     state_for(x, x->state_before(), true /*ignore_xhandler*/));
+  if (x->is_null_free()) {
+    __ null_check(obj.result(), new CodeEmitInfo(info_for_exception));
+  }
 
   CodeStub* stub;
   if (x->is_incompatible_class_change_check()) {
     assert(patching_info == nullptr, "can't patch this");
     stub = new SimpleExceptionStub(Runtime1::throw_incompatible_class_change_error_id, LIR_OprFact::illegalOpr, info_for_exception);
@@ -1278,14 +1296,17 @@
   LIR_Opr reg = rlock_result(x);
   LIR_Opr tmp3 = LIR_OprFact::illegalOpr;
   if (!x->klass()->is_loaded() || UseCompressedClassPointers) {
     tmp3 = new_register(objectType);
   }
+
+
   __ checkcast(reg, obj.result(), x->klass(),
                new_register(objectType), new_register(objectType), tmp3,
                x->direct_compare(), info_for_exception, patching_info, stub,
-               x->profiled_method(), x->profiled_bci());
+               x->profiled_method(), x->profiled_bci(), x->is_null_free());
+
 }
 
 void LIRGenerator::do_InstanceOf(InstanceOf* x) {
   LIRItem obj(x->obj(), this);
 
@@ -1359,11 +1380,16 @@
     increment_backedge_counter_conditionally(lir_cond(cond), left, right, state_for(x, x->state_before()),
         x->tsux()->bci(), x->fsux()->bci(), x->profiled_bci());
     __ safepoint(LIR_OprFact::illegalOpr, state_for(x, x->state_before()));
   }
 
-  __ cmp(lir_cond(cond), left, right);
+  if (x->substitutability_check()) {
+    substitutability_check(x, *xin, *yin);
+  } else {
+    __ cmp(lir_cond(cond), left, right);
+  }
+
   // Generate branch profiling. Profiling code doesn't kill flags.
   profile_branch(x, cond);
   move_to_phi(x->state());
   if (x->x()->type()->is_float_kind()) {
     __ branch(lir_cond(cond), x->tsux(), x->usux());
diff a/src/hotspot/cpu/aarch64/c1_LIR_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIR_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/c1_LIR_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/c1_LIR_aarch64.cpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021, 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.
@@ -48,7 +48,8 @@
 void LIR_Address::verify() const {
   assert(base()->is_cpu_register(), "wrong base operand");
   assert(index()->is_illegal() || index()->is_double_cpu() || index()->is_single_cpu(), "wrong index operand");
   assert(base()->type() == T_ADDRESS || base()->type() == T_OBJECT || base()->type() == T_LONG || base()->type() == T_METADATA,
          "wrong type for addresses");
+  assert(index()->is_illegal() || disp() == 0, "cannot set both index and displacement");
 }
 #endif // PRODUCT
diff a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp
@@ -26,10 +26,12 @@
 #include "precompiled.hpp"
 #include "c1/c1_MacroAssembler.hpp"
 #include "c1/c1_Runtime1.hpp"
 #include "gc/shared/barrierSetAssembler.hpp"
 #include "gc/shared/collectedHeap.hpp"
+#include "gc/shared/barrierSet.hpp"
+#include "gc/shared/barrierSetAssembler.hpp"
 #include "gc/shared/tlab_globals.hpp"
 #include "interpreter/interpreter.hpp"
 #include "oops/arrayOop.hpp"
 #include "oops/markWord.hpp"
 #include "runtime/basicLock.hpp"
@@ -86,10 +88,16 @@
     lightweight_lock(obj, hdr, temp, rscratch2, slow_case);
   } else if (LockingMode == LM_LEGACY) {
     Label done;
     // and mark it as unlocked
     orr(hdr, hdr, markWord::unlocked_value);
+
+    if (EnableValhalla) {
+      // Mask always_locked bit such that we go to the slow path if object is an inline type
+      andr(hdr, hdr, ~markWord::inline_type_bit_in_place);
+    }
+
     // save unlocked object header into the displaced header location on the stack
     str(hdr, Address(disp_hdr, 0));
     // test if object header is still the same (i.e. unlocked), and if so, store the
     // displaced header address in the object header - if it is not the same, get the
     // object header instead
@@ -178,12 +186,18 @@
   }
 }
 
 void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register len, Register t1, Register t2) {
   assert_different_registers(obj, klass, len);
-  // This assumes that all prototype bits fit in an int32_t
-  mov(t1, (int32_t)(intptr_t)markWord::prototype().value());
+  if (EnableValhalla) {
+    // Need to copy markWord::prototype header for klass
+    assert_different_registers(obj, klass, len, t1, t2);
+    ldr(t1, Address(klass, Klass::prototype_header_offset()));
+  } else {
+    // This assumes that all prototype bits fit in an int32_t
+    mov(t1, (int32_t)(intptr_t)markWord::prototype().value());
+  }
   str(t1, Address(obj, oopDesc::mark_offset_in_bytes()));
 
   if (UseCompressedClassPointers) { // Take care not to kill klass
     encode_klass_not_null(t1, klass);
     strw(t1, Address(obj, oopDesc::klass_offset_in_bytes()));
@@ -269,10 +283,11 @@
     far_call(RuntimeAddress(Runtime1::entry_for(Runtime1::dtrace_object_alloc_id)));
   }
 
   verify_oop(obj);
 }
+
 void C1_MacroAssembler::allocate_array(Register obj, Register len, Register t1, Register t2, int header_size, int f, Register klass, Label& slow_case) {
   assert_different_registers(obj, len, t1, t2, klass);
 
   // determine alignment mask
   assert(!(BytesPerWord & 1), "must be a multiple of 2 for masking code to work");
@@ -316,36 +331,111 @@
   assert(!MacroAssembler::needs_explicit_null_check(oopDesc::klass_offset_in_bytes()), "must add explicit null check");
 
   cmp_klass(receiver, iCache, rscratch1);
 }
 
+void C1_MacroAssembler::build_frame_helper(int frame_size_in_bytes, int sp_offset_for_orig_pc, int sp_inc, bool reset_orig_pc, bool needs_stack_repair) {
+  MacroAssembler::build_frame(frame_size_in_bytes);
+
+  if (needs_stack_repair) {
+    save_stack_increment(sp_inc, frame_size_in_bytes);
+  }
+  if (reset_orig_pc) {
+    // Zero orig_pc to detect deoptimization during buffering in the entry points
+    str(zr, Address(sp, sp_offset_for_orig_pc));
+  }
+}
 
-void C1_MacroAssembler::build_frame(int framesize, int bang_size_in_bytes) {
-  assert(bang_size_in_bytes >= framesize, "stack bang size incorrect");
+void C1_MacroAssembler::build_frame(int frame_size_in_bytes, int bang_size_in_bytes, int sp_offset_for_orig_pc, bool needs_stack_repair, bool has_scalarized_args, Label* verified_inline_entry_label) {
   // Make sure there is enough stack space for this method's activation.
   // Note that we do this before creating a frame.
+  assert(bang_size_in_bytes >= frame_size_in_bytes, "stack bang size incorrect");
   generate_stack_overflow_check(bang_size_in_bytes);
-  MacroAssembler::build_frame(framesize);
+
+  build_frame_helper(frame_size_in_bytes, sp_offset_for_orig_pc, 0, has_scalarized_args, needs_stack_repair);
 
   // Insert nmethod entry barrier into frame.
   BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
   bs->nmethod_entry_barrier(this, nullptr /* slow_path */, nullptr /* continuation */, nullptr /* guard */);
-}
 
-void C1_MacroAssembler::remove_frame(int framesize) {
-  MacroAssembler::remove_frame(framesize);
+  if (verified_inline_entry_label != nullptr) {
+    // Jump here from the scalarized entry points that already created the frame.
+    bind(*verified_inline_entry_label);
+  }
 }
 
-
 void C1_MacroAssembler::verified_entry(bool breakAtEntry) {
   // If we have to make this method not-entrant we'll overwrite its
   // first instruction with a jump.  For this action to be legal we
   // must ensure that this first instruction is a B, BL, NOP, BKPT,
   // SVC, HVC, or SMC.  Make it a NOP.
   nop();
+  if (C1Breakpoint) brk(1);
+}
+
+int C1_MacroAssembler::scalarized_entry(const CompiledEntrySignature* ces, int frame_size_in_bytes, int bang_size_in_bytes, int sp_offset_for_orig_pc, Label& verified_inline_entry_label, bool is_inline_ro_entry) {
+  assert(InlineTypePassFieldsAsArgs, "sanity");
+  // Make sure there is enough stack space for this method's activation.
+  assert(bang_size_in_bytes >= frame_size_in_bytes, "stack bang size incorrect");
+  generate_stack_overflow_check(bang_size_in_bytes);
+
+  GrowableArray<SigEntry>* sig    = ces->sig();
+  GrowableArray<SigEntry>* sig_cc = is_inline_ro_entry ? ces->sig_cc_ro() : ces->sig_cc();
+  VMRegPair* regs      = ces->regs();
+  VMRegPair* regs_cc   = is_inline_ro_entry ? ces->regs_cc_ro() : ces->regs_cc();
+  int args_on_stack    = ces->args_on_stack();
+  int args_on_stack_cc = is_inline_ro_entry ? ces->args_on_stack_cc_ro() : ces->args_on_stack_cc();
+
+  assert(sig->length() <= sig_cc->length(), "Zero-sized inline class not allowed!");
+  BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, sig_cc->length());
+  int args_passed = sig->length();
+  int args_passed_cc = SigEntry::fill_sig_bt(sig_cc, sig_bt);
+
+  // Create a temp frame so we can call into the runtime. It must be properly set up to accommodate GC.
+  build_frame_helper(frame_size_in_bytes, sp_offset_for_orig_pc, 0, true, ces->c1_needs_stack_repair());
+
+  // The runtime call might safepoint, make sure nmethod entry barrier is executed
+  BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
+  // C1 code is not hot enough to micro optimize the nmethod entry barrier with an out-of-line stub
+  bs->nmethod_entry_barrier(this, nullptr /* slow_path */, nullptr /* continuation */, nullptr /* guard */);
+
+  // FIXME -- call runtime only if we cannot in-line allocate all the incoming inline type args.
+  mov(r19, (intptr_t) ces->method());
+  if (is_inline_ro_entry) {
+    far_call(RuntimeAddress(Runtime1::entry_for(Runtime1::buffer_inline_args_no_receiver_id)));
+  } else {
+    far_call(RuntimeAddress(Runtime1::entry_for(Runtime1::buffer_inline_args_id)));
+  }
+  int rt_call_offset = offset();
+
+  // The runtime call returns the new array in r20 instead of the usual r0
+  // because r0 is also j_rarg7 which may be holding a live argument here.
+  Register val_array = r20;
+
+  // Remove the temp frame
+  MacroAssembler::remove_frame(frame_size_in_bytes);
+
+  // Check if we need to extend the stack for packing
+  int sp_inc = 0;
+  if (args_on_stack > args_on_stack_cc) {
+    sp_inc = extend_stack_for_inline_args(args_on_stack);
+  }
+
+  shuffle_inline_args(true, is_inline_ro_entry, sig_cc,
+                      args_passed_cc, args_on_stack_cc, regs_cc, // from
+                      args_passed, args_on_stack, regs,          // to
+                      sp_inc, val_array);
+
+  // Create the real frame. Below jump will then skip over the stack banging and frame
+  // setup code in the verified_inline_entry (which has a different real_frame_size).
+  build_frame_helper(frame_size_in_bytes, sp_offset_for_orig_pc, sp_inc, false, ces->c1_needs_stack_repair());
+
+  b(verified_inline_entry_label);
+  return rt_call_offset;
 }
 
+
 void C1_MacroAssembler::load_parameter(int offset_in_words, Register reg) {
   // rfp, + 0: link
   //     + 1: return address
   //     + 2: argument with offset 0
   //     + 3: argument with offset 1
diff a/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp
@@ -665,11 +665,12 @@
           __ set_info("fast new_instance init check", dont_gc_arguments);
         }
 
         __ enter();
         OopMap* map = save_live_registers(sasm);
-        int call_offset = __ call_RT(obj, noreg, CAST_FROM_FN_PTR(address, new_instance), klass);
+        int call_offset;
+        call_offset = __ call_RT(obj, noreg, CAST_FROM_FN_PTR(address, new_instance), klass);
         oop_maps = new OopMapSet();
         oop_maps->add_gc_map(call_offset, map);
         restore_live_registers_except_r0(sasm);
         __ verify_oop(obj);
         __ leave();
@@ -698,47 +699,70 @@
       }
       break;
 
     case new_type_array_id:
     case new_object_array_id:
+    case new_flat_array_id:
       {
         Register length   = r19; // Incoming
         Register klass    = r3; // Incoming
         Register obj      = r0; // Result
 
         if (id == new_type_array_id) {
           __ set_info("new_type_array", dont_gc_arguments);
-        } else {
+        } else if (id == new_object_array_id) {
           __ set_info("new_object_array", dont_gc_arguments);
+        } else {
+          __ set_info("new_flat_array", dont_gc_arguments);
         }
 
 #ifdef ASSERT
         // assert object type is really an array of the proper kind
         {
           Label ok;
           Register t0 = obj;
           __ ldrw(t0, Address(klass, Klass::layout_helper_offset()));
           __ asrw(t0, t0, Klass::_lh_array_tag_shift);
-          int tag = ((id == new_type_array_id)
-                     ? Klass::_lh_array_tag_type_value
-                     : Klass::_lh_array_tag_obj_value);
-          __ mov(rscratch1, tag);
-          __ cmpw(t0, rscratch1);
-          __ br(Assembler::EQ, ok);
-          __ stop("assert(is an array klass)");
+          switch (id) {
+          case new_type_array_id:
+            __ cmpw(t0, Klass::_lh_array_tag_type_value);
+            __ br(Assembler::EQ, ok);
+            __ stop("assert(is a type array klass)");
+            break;
+          case new_object_array_id:
+            __ cmpw(t0, Klass::_lh_array_tag_obj_value); // new "[Ljava/lang/Object;"
+            __ br(Assembler::EQ, ok);
+            __ cmpw(t0, Klass::_lh_array_tag_vt_value);  // new "[LVT;"
+            __ br(Assembler::EQ, ok);
+            __ stop("assert(is an object or inline type array klass)");
+            break;
+          case new_flat_array_id:
+            // TODO 8325106 Fix comment
+            // new "[QVT;"
+            __ cmpw(t0, Klass::_lh_array_tag_vt_value);  // the array can be a flat array.
+            __ br(Assembler::EQ, ok);
+            __ cmpw(t0, Klass::_lh_array_tag_obj_value); // the array cannot be a flat array (due to InlineArrayElementMaxFlatSize, etc)
+            __ br(Assembler::EQ, ok);
+            __ stop("assert(is an object or inline type array klass)");
+            break;
+          default:  ShouldNotReachHere();
+          }
           __ should_not_reach_here();
           __ bind(ok);
         }
 #endif // ASSERT
 
         __ enter();
         OopMap* map = save_live_registers(sasm);
         int call_offset;
         if (id == new_type_array_id) {
           call_offset = __ call_RT(obj, noreg, CAST_FROM_FN_PTR(address, new_type_array), klass, length);
-        } else {
+        } else if (id == new_object_array_id) {
           call_offset = __ call_RT(obj, noreg, CAST_FROM_FN_PTR(address, new_object_array), klass, length);
+        } else {
+          assert(id == new_flat_array_id, "must be");
+          call_offset = __ call_RT(obj, noreg, CAST_FROM_FN_PTR(address, new_flat_array), klass, length);
         }
 
         oop_maps = new OopMapSet();
         oop_maps->add_gc_map(call_offset, map);
         restore_live_registers_except_r0(sasm);
@@ -769,10 +793,96 @@
         // r0,: new multi array
         __ verify_oop(r0);
       }
       break;
 
+    case buffer_inline_args_id:
+    case buffer_inline_args_no_receiver_id:
+      {
+        const char* name = (id == buffer_inline_args_id) ?
+          "buffer_inline_args" : "buffer_inline_args_no_receiver";
+        StubFrame f(sasm, name, dont_gc_arguments);
+        OopMap* map = save_live_registers(sasm);
+        Register method = r19;   // Incoming
+        address entry = (id == buffer_inline_args_id) ?
+          CAST_FROM_FN_PTR(address, buffer_inline_args) :
+          CAST_FROM_FN_PTR(address, buffer_inline_args_no_receiver);
+        // This is called from a C1 method's scalarized entry point
+        // where r0-r7 may be holding live argument values so we can't
+        // return the result in r0 as the other stubs do. LR is used as
+        // a temporay below to avoid the result being clobbered by
+        // restore_live_registers.
+        int call_offset = __ call_RT(lr, noreg, entry, method);
+        oop_maps = new OopMapSet();
+        oop_maps->add_gc_map(call_offset, map);
+        restore_live_registers(sasm);
+        __ mov(r20, lr);
+        __ verify_oop(r20);  // r20: an array of buffered value objects
+     }
+     break;
+
+    case load_flat_array_id:
+      {
+        StubFrame f(sasm, "load_flat_array", dont_gc_arguments);
+        OopMap* map = save_live_registers(sasm);
+
+        // Called with store_parameter and not C abi
+
+        f.load_argument(1, r0); // r0,: array
+        f.load_argument(0, r1); // r1,: index
+        int call_offset = __ call_RT(r0, noreg, CAST_FROM_FN_PTR(address, load_flat_array), r0, r1);
+
+        // Ensure the stores that initialize the buffer are visible
+        // before any subsequent store that publishes this reference.
+        __ membar(Assembler::StoreStore);
+
+        oop_maps = new OopMapSet();
+        oop_maps->add_gc_map(call_offset, map);
+        restore_live_registers_except_r0(sasm);
+
+        // r0: loaded element at array[index]
+        __ verify_oop(r0);
+      }
+      break;
+
+    case store_flat_array_id:
+      {
+        StubFrame f(sasm, "store_flat_array", dont_gc_arguments);
+        OopMap* map = save_live_registers(sasm, 4);
+
+        // Called with store_parameter and not C abi
+
+        f.load_argument(2, r0); // r0: array
+        f.load_argument(1, r1); // r1: index
+        f.load_argument(0, r2); // r2: value
+        int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, store_flat_array), r0, r1, r2);
+
+        oop_maps = new OopMapSet();
+        oop_maps->add_gc_map(call_offset, map);
+        restore_live_registers_except_r0(sasm);
+      }
+      break;
+
+    case substitutability_check_id:
+      {
+        StubFrame f(sasm, "substitutability_check", dont_gc_arguments);
+        OopMap* map = save_live_registers(sasm);
+
+        // Called with store_parameter and not C abi
+
+        f.load_argument(1, r1); // r1,: left
+        f.load_argument(0, r2); // r2,: right
+        int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, substitutability_check), r1, r2);
+
+        oop_maps = new OopMapSet();
+        oop_maps->add_gc_map(call_offset, map);
+        restore_live_registers_except_r0(sasm);
+
+        // r0,: are the two operands substitutable
+      }
+      break;
+
     case register_finalizer_id:
       {
         __ set_info("register_finalizer", dont_gc_arguments);
 
         // This is called via call_runtime so the arguments
@@ -808,15 +918,21 @@
         oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_class_cast_exception), true);
       }
       break;
 
     case throw_incompatible_class_change_error_id:
-      { StubFrame f(sasm, "throw_incompatible_class_cast_exception", dont_gc_arguments, does_not_return);
+      { StubFrame f(sasm, "throw_incompatible_class_change_error", dont_gc_arguments, does_not_return);
         oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_incompatible_class_change_error), false);
       }
       break;
 
+    case throw_illegal_monitor_state_exception_id:
+      { StubFrame f(sasm, "throw_illegal_monitor_state_exception", dont_gc_arguments);
+        oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_illegal_monitor_state_exception), false);
+      }
+      break;
+
     case slow_subtype_check_id:
       {
         // Typical calling sequence:
         // __ push(klass_RInfo);  // object klass or other subclass
         // __ push(sup_k_RInfo);  // array element klass or other superclass
@@ -1016,17 +1132,21 @@
         restore_live_registers(sasm);
       }
       break;
 
     default:
+      // FIXME: For unhandled trap_id this code fails with assert during vm intialization
+      // rather than insert a call to unimplemented_entry
       { StubFrame f(sasm, "unimplemented entry", dont_gc_arguments, does_not_return);
         __ mov(r0, (int)id);
         __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, unimplemented_entry), r0);
       }
       break;
     }
   }
+
+
   return oop_maps;
 }
 
 #undef __
 
diff a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp
@@ -43,10 +43,33 @@
 
 #define BIND(label) bind(label); BLOCK_COMMENT(#label ":")
 
 typedef void (MacroAssembler::* chr_insn)(Register Rt, const Address &adr);
 
+void C2_MacroAssembler::entry_barrier() {
+  BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
+  if (BarrierSet::barrier_set()->barrier_set_nmethod() != nullptr) {
+    // Dummy labels for just measuring the code size
+    Label dummy_slow_path;
+    Label dummy_continuation;
+    Label dummy_guard;
+    Label* slow_path = &dummy_slow_path;
+    Label* continuation = &dummy_continuation;
+    Label* guard = &dummy_guard;
+    if (!Compile::current()->output()->in_scratch_emit_size()) {
+      // Use real labels from actual stub when not emitting code for the purpose of measuring its size
+      C2EntryBarrierStub* stub = new (Compile::current()->comp_arena()) C2EntryBarrierStub();
+      Compile::current()->output()->add_stub(stub);
+      slow_path = &stub->entry();
+      continuation = &stub->continuation();
+      guard = &stub->guard();
+    }
+    // In the C2 code, we move the non-hot part of nmethod entry barriers out-of-line to a stub.
+    bs->nmethod_entry_barrier(this, slow_path, continuation, guard);
+  }
+}
+
 void C2_MacroAssembler::fast_lock(Register objectReg, Register boxReg, Register tmpReg,
                                   Register tmp2Reg, Register tmp3Reg) {
   Register oop = objectReg;
   Register box = boxReg;
   Register disp_hdr = tmpReg;
@@ -75,10 +98,15 @@
     b(cont);
   } else if (LockingMode == LM_LEGACY) {
     // Set tmp to be (markWord of object | UNLOCK_VALUE).
     orr(tmp, disp_hdr, markWord::unlocked_value);
 
+    if (EnableValhalla) {
+      // Mask inline_type bit such that we go to the slow path if object is an inline type
+      andr(tmp, tmp, ~((int) markWord::inline_type_bit_in_place));
+    }
+
     // Initialize the box. (Must happen before we update the object mark!)
     str(tmp, Address(box, BasicLock::displaced_header_offset_in_bytes()));
 
     // Compare object markWord with an unlocked value (tmp) and if
     // equal exchange the stack address of our box with object markWord.
diff a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp
--- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp
@@ -33,10 +33,12 @@
 
   void neon_reduce_logical_helper(int opc, bool sf, Register Rd, Register Rn, Register Rm,
                                   enum shift_kind kind = Assembler::LSL, unsigned shift = 0);
 
  public:
+  void entry_barrier();
+
   // Code used by cmpFastLock and cmpFastUnlock mach instructions in .ad file.
   // See full description in macroAssembler_aarch64.cpp.
   void fast_lock(Register object, Register box, Register tmp, Register tmp2, Register tmp3);
   void fast_unlock(Register object, Register box, Register tmp, Register tmp2);
 
diff a/src/hotspot/cpu/aarch64/frame_aarch64.cpp b/src/hotspot/cpu/aarch64/frame_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/frame_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/frame_aarch64.cpp
@@ -152,18 +152,21 @@
       sender_sp = _unextended_sp + _cb->frame_size();
       // Is sender_sp safe?
       if (!thread->is_in_full_stack_checked((address)sender_sp)) {
         return false;
       }
-      sender_unextended_sp = sender_sp;
       // Note: frame::sender_sp_offset is only valid for compiled frame
-      saved_fp = (intptr_t*) *(sender_sp - frame::sender_sp_offset);
+      intptr_t **saved_fp_addr = (intptr_t**) (sender_sp - frame::sender_sp_offset);
+      saved_fp = *saved_fp_addr;
       // Note: PAC authentication may fail in case broken frame is passed in.
       // Just strip it for now.
       sender_pc = pauth_strip_pointer((address) *(sender_sp - 1));
-    }
 
+      // Repair the sender sp if this is a method with scalarized inline type args
+      sender_sp = repair_sender_sp(sender_sp, saved_fp_addr);
+      sender_unextended_sp = sender_sp;
+    }
     if (Continuation::is_return_barrier_entry(sender_pc)) {
       // If our sender_pc is the return barrier, then our "real" sender is the continuation entry
       frame s = Continuation::continuation_bottom_sender(thread, *this, sender_sp);
       sender_sp = s.sp();
       sender_pc = s.pc();
@@ -786,10 +789,26 @@
   init((intptr_t*)sp, (intptr_t*)fp, (address)pc);
 }
 
 #endif
 
+// Check for a method with scalarized inline type arguments that needs
+// a stack repair and return the repaired sender stack pointer.
+intptr_t* frame::repair_sender_sp(intptr_t* sender_sp, intptr_t** saved_fp_addr) const {
+  CompiledMethod* cm = _cb->as_compiled_method_or_null();
+  if (cm != nullptr && cm->needs_stack_repair()) {
+    // The stack increment resides just below the saved FP on the stack and
+    // records the total frame size excluding the two words for saving FP and LR.
+    intptr_t* sp_inc_addr = (intptr_t*) (saved_fp_addr - 1);
+    assert(*sp_inc_addr % StackAlignmentInBytes == 0, "sp_inc not aligned");
+    int real_frame_size = (*sp_inc_addr / wordSize) + 2;
+    assert(real_frame_size >= _cb->frame_size() && real_frame_size <= 1000000, "invalid frame size");
+    sender_sp = unextended_sp() + real_frame_size;
+  }
+  return sender_sp;
+}
+
 void JavaFrameAnchor::make_walkable() {
   // last frame set?
   if (last_Java_sp() == nullptr) return;
   // already walkable?
   if (walkable()) return;
diff a/src/hotspot/cpu/aarch64/frame_aarch64.hpp b/src/hotspot/cpu/aarch64/frame_aarch64.hpp
--- a/src/hotspot/cpu/aarch64/frame_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/frame_aarch64.hpp
@@ -149,10 +149,13 @@
 
   intptr_t* ptr_at_addr(int offset) const {
     return (intptr_t*) addr_at(offset);
   }
 
+  // Support for scalarized inline type calling convention
+  intptr_t* repair_sender_sp(intptr_t* sender_sp, intptr_t** saved_fp_addr) const;
+
 #ifdef ASSERT
   // Used in frame::sender_for_{interpreter,compiled}_frame
   static void verify_deopt_original_pc(   CompiledMethod* nm, intptr_t* unextended_sp);
 #endif
 
diff a/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp b/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp
--- a/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp
+++ b/src/hotspot/cpu/aarch64/frame_aarch64.inline.hpp
@@ -30,10 +30,13 @@
 #include "code/codeCache.inline.hpp"
 #include "code/vmreg.inline.hpp"
 #include "interpreter/interpreter.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "pauth_aarch64.hpp"
+#ifdef COMPILER1
+#include "c1/c1_Runtime1.hpp"
+#endif
 
 // Inline functions for AArch64 frames:
 
 // Constructors:
 
@@ -402,25 +405,54 @@
   // have to find it relative to the unextended sp
 
   assert(_cb->frame_size() > 0, "must have non-zero frame size");
   intptr_t* l_sender_sp = (!PreserveFramePointer || _sp_is_trusted) ? unextended_sp() + _cb->frame_size()
                                                                     : sender_sp();
+#ifdef ASSERT
+   address sender_pc_copy = pauth_strip_verifiable((address) *(l_sender_sp-1));
+#endif
+
   assert(!_sp_is_trusted || l_sender_sp == real_fp(), "");
 
+  intptr_t** saved_fp_addr = (intptr_t**) (l_sender_sp - frame::sender_sp_offset);
+
+  // Repair the sender sp if the frame has been extended
+  l_sender_sp = repair_sender_sp(l_sender_sp, saved_fp_addr);
+
   // The return_address is always the word on the stack.
   // For ROP protection, C1/C2 will have signed the sender_pc,
   // but there is no requirement to authenticate it here.
   address sender_pc = pauth_strip_verifiable((address) *(l_sender_sp - 1));
 
-  intptr_t** saved_fp_addr = (intptr_t**) (l_sender_sp - frame::sender_sp_offset);
+#ifdef ASSERT
+  if (sender_pc != sender_pc_copy) {
+    // When extending the stack in the callee method entry to make room for unpacking of value
+    // type args, we keep a copy of the sender pc at the expected location in the callee frame.
+    // If the sender pc is patched due to deoptimization, the copy is not consistent anymore.
+    nmethod* nm = CodeCache::find_blob(sender_pc)->as_nmethod();
+    assert(sender_pc == nm->deopt_mh_handler_begin() || sender_pc == nm->deopt_handler_begin(), "unexpected sender pc");
+  }
+#endif
 
   if (map->update_map()) {
     // Tell GC to use argument oopmaps for some runtime stubs that need it.
     // For C1, the runtime stub might not have oop maps, so set this flag
     // outside of update_register_map.
-    if (!_cb->is_compiled()) { // compiled frames do not use callee-saved registers
-      map->set_include_argument_oops(_cb->caller_must_gc_arguments(map->thread()));
+    bool c1_buffering = false;
+#ifdef COMPILER1
+    nmethod* nm = _cb->as_nmethod_or_null();
+    if (nm != nullptr && nm->is_compiled_by_c1() && nm->method()->has_scalarized_args() &&
+        pc() < nm->verified_inline_entry_point()) {
+      // The VEP and VIEP(RO) of C1-compiled methods call buffer_inline_args_xxx
+      // before doing any argument shuffling, so we need to scan the oops
+      // as the caller passes them.
+      c1_buffering = true;
+    }
+#endif
+    if (!_cb->is_compiled() || c1_buffering) { // compiled frames do not use callee-saved registers
+      bool caller_args = _cb->caller_must_gc_arguments(map->thread()) || c1_buffering;
+      map->set_include_argument_oops(caller_args);
       if (oop_map() != nullptr) {
         _oop_map->update_register_map(this, map);
       }
     } else {
       assert(!_cb->caller_must_gc_arguments(map->thread()), "");
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
@@ -153,11 +153,31 @@
   __ str(pre_val, Address(tmp1, 0));
   __ b(done);
 
   __ bind(runtime);
 
-  __ push_call_clobbered_registers();
+  // save the live input values
+  RegSet saved = RegSet::of(pre_val);
+  FloatRegSet fsaved;
+
+  // Barriers might be emitted when converting between (scalarized) calling
+  // conventions for inline types. Save all argument registers before calling
+  // into the runtime.
+  if (EnableValhalla && InlineTypePassFieldsAsArgs) {
+    if (tosca_live) saved += RegSet::of(r0);
+    if (obj != noreg) saved += RegSet::of(obj);
+    saved += RegSet::of(j_rarg0, j_rarg1, j_rarg2, j_rarg3);
+    saved += RegSet::of(j_rarg4, j_rarg5, j_rarg6, j_rarg7);
+
+    fsaved += FloatRegSet::of(j_farg0, j_farg1, j_farg2, j_farg3);
+    fsaved += FloatRegSet::of(j_farg4, j_farg5, j_farg6, j_farg7);
+
+    __ push(saved, sp);
+    __ push_fp(fsaved, sp);
+  } else {
+    __ push_call_clobbered_registers();
+  }
 
   // Calling the runtime using the regular call_VM_leaf mechanism generates
   // code (generated by InterpreterMacroAssember::call_VM_leaf_base)
   // that checks that the *(rfp+frame::interpreter_frame_last_sp) == nullptr.
   //
@@ -174,11 +194,16 @@
     __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_pre_entry), pre_val, thread);
   } else {
     __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_pre_entry), pre_val, thread);
   }
 
-  __ pop_call_clobbered_registers();
+  if (EnableValhalla && InlineTypePassFieldsAsArgs) {
+  __ pop_fp(fsaved, sp);
+  __ pop(saved, sp);
+  } else {
+    __ pop_call_clobbered_registers();
+  }
 
   __ bind(done);
 
 }
 
@@ -214,10 +239,12 @@
 
   __ cbz(new_val, done);
 
   // storing region crossing non-null, is card already dirty?
 
+  assert_different_registers(store_addr, thread, tmp1, tmp2, rscratch1);
+
   const Register card_addr = tmp1;
 
   __ lsr(card_addr, store_addr, CardTable::card_shift());
 
   // get the address of the card
@@ -247,14 +274,30 @@
   __ ldr(tmp2, buffer);
   __ str(card_addr, Address(tmp2, rscratch1));
   __ b(done);
 
   __ bind(runtime);
+
   // save the live input values
   RegSet saved = RegSet::of(store_addr);
+  FloatRegSet fsaved;
+
+  // Barriers might be emitted when converting between (scalarized) calling
+  // conventions for inline types. Save all argument registers before calling
+  // into the runtime.
+  if (EnableValhalla && InlineTypePassFieldsAsArgs) {
+    saved += RegSet::of(j_rarg0, j_rarg1, j_rarg2, j_rarg3);
+    saved += RegSet::of(j_rarg4, j_rarg5, j_rarg6, j_rarg7);
+
+    fsaved += FloatRegSet::of(j_farg0, j_farg1, j_farg2, j_farg3);
+    fsaved += FloatRegSet::of(j_farg4, j_farg5, j_farg6, j_farg7);
+  }
+
   __ push(saved, sp);
+  __ push_fp(fsaved, sp);
   __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry), card_addr, thread);
+  __ pop_fp(fsaved, sp);
   __ pop(saved, sp);
 
   __ bind(done);
 }
 
@@ -282,44 +325,61 @@
   }
 }
 
 void G1BarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
                                          Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) {
+
+  bool in_heap = (decorators & IN_HEAP) != 0;
+  bool as_normal = (decorators & AS_NORMAL) != 0;
+  bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0;
+
+  bool needs_pre_barrier = as_normal && !dest_uninitialized;
+  bool needs_post_barrier = (val != noreg && in_heap);
+
+  assert_different_registers(val, tmp1, tmp2, tmp3);
+
   // flatten object address if needed
   if (dst.index() == noreg && dst.offset() == 0) {
     if (dst.base() != tmp3) {
       __ mov(tmp3, dst.base());
     }
   } else {
     __ lea(tmp3, dst);
   }
 
-  g1_write_barrier_pre(masm,
-                       tmp3 /* obj */,
-                       tmp2 /* pre_val */,
-                       rthread /* thread */,
-                       tmp1  /* tmp1 */,
-                       rscratch2  /* tmp2 */,
-                       val != noreg /* tosca_live */,
-                       false /* expand_call */);
+  if (needs_pre_barrier) {
+    g1_write_barrier_pre(masm,
+                         tmp3 /* obj */,
+                         tmp2 /* pre_val */,
+                         rthread /* thread */,
+                         tmp1  /* tmp1 */,
+                         rscratch2  /* tmp2 */,
+                         val != noreg /* tosca_live */,
+                         false /* expand_call */);
+  }
 
   if (val == noreg) {
     BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), noreg, noreg, noreg, noreg);
   } else {
     // G1 barrier needs uncompressed oop for region cross check.
     Register new_val = val;
-    if (UseCompressedOops) {
-      new_val = rscratch2;
-      __ mov(new_val, val);
+    if (needs_post_barrier) {
+      if (UseCompressedOops) {
+        new_val = rscratch2;
+        __ mov(new_val, val);
+      }
     }
+
     BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), val, noreg, noreg, noreg);
-    g1_write_barrier_post(masm,
-                          tmp3 /* store_adr */,
-                          new_val /* new_val */,
-                          rthread /* thread */,
-                          tmp1 /* tmp1 */,
-                          tmp2 /* tmp2 */);
+    if (needs_post_barrier) {
+      g1_write_barrier_post(masm,
+                            tmp3 /* store_adr */,
+                            new_val /* new_val */,
+                            rthread /* thread */,
+                            tmp1 /* tmp1 */,
+                            tmp2 /* tmp2 */);
+    }
   }
 
 }
 
 #ifdef COMPILER1
diff a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp
@@ -25,10 +25,11 @@
 #include "precompiled.hpp"
 #include "classfile/classLoaderData.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/barrierSetAssembler.hpp"
 #include "gc/shared/barrierSetNMethod.hpp"
+#include "gc/shared/barrierSetRuntime.hpp"
 #include "gc/shared/collectedHeap.hpp"
 #include "interpreter/interp_masm.hpp"
 #include "memory/universe.hpp"
 #include "runtime/javaThread.hpp"
 #include "runtime/jniHandles.hpp"
@@ -44,10 +45,11 @@
   // LR is live.  It must be saved around calls.
 
   bool in_heap = (decorators & IN_HEAP) != 0;
   bool in_native = (decorators & IN_NATIVE) != 0;
   bool is_not_null = (decorators & IS_NOT_NULL) != 0;
+
   switch (type) {
   case T_OBJECT:
   case T_ARRAY: {
     if (in_heap) {
       if (UseCompressedOops) {
@@ -81,26 +83,39 @@
 
 void BarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
                                    Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) {
   bool in_heap = (decorators & IN_HEAP) != 0;
   bool in_native = (decorators & IN_NATIVE) != 0;
+  bool is_not_null = (decorators & IS_NOT_NULL) != 0;
+
   switch (type) {
   case T_OBJECT:
   case T_ARRAY: {
-    val = val == noreg ? zr : val;
     if (in_heap) {
-      if (UseCompressedOops) {
-        assert(!dst.uses(val), "not enough registers");
-        if (val != zr) {
-          __ encode_heap_oop(val);
+      if (val == noreg) {
+        assert(!is_not_null, "inconsistent access");
+        if (UseCompressedOops) {
+          __ strw(zr, dst);
+        } else {
+          __ str(zr, dst);
         }
-        __ strw(val, dst);
       } else {
-        __ str(val, dst);
+        if (UseCompressedOops) {
+          assert(!dst.uses(val), "not enough registers");
+          if (is_not_null) {
+            __ encode_heap_oop_not_null(val);
+          } else {
+            __ encode_heap_oop(val);
+          }
+          __ strw(val, dst);
+        } else {
+          __ str(val, dst);
+        }
       }
     } else {
       assert(in_native, "why else?");
+      assert(val != noreg, "not supported");
       __ str(val, dst);
     }
     break;
   }
   case T_BOOLEAN:
@@ -117,10 +132,23 @@
   case T_DOUBLE:  __ strd(v0,  dst); break;
   default: Unimplemented();
   }
 }
 
+void BarrierSetAssembler::value_copy(MacroAssembler* masm, DecoratorSet decorators,
+                                     Register src, Register dst, Register value_klass) {
+  // value_copy implementation is fairly complex, and there are not any
+  // "short-cuts" to be made from asm. What there is, appears to have the same
+  // cost in C++, so just "call_VM_leaf" for now rather than maintain hundreds
+  // of hand-rolled instructions...
+  if (decorators & IS_DEST_UNINITIALIZED) {
+    __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy_is_dest_uninitialized), src, dst, value_klass);
+  } else {
+    __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy), src, dst, value_klass);
+  }
+}
+
 void BarrierSetAssembler::copy_load_at(MacroAssembler* masm,
                                        DecoratorSet decorators,
                                        BasicType type,
                                        size_t bytes,
                                        Register dst1,
diff a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp
--- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp
@@ -97,10 +97,13 @@
   virtual void load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
                        Register dst, Address src, Register tmp1, Register tmp2);
   virtual void store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
                         Address dst, Register val, Register tmp1, Register tmp2, Register tmp3);
 
+  virtual void value_copy(MacroAssembler* masm, DecoratorSet decorators,
+                          Register src, Register dst, Register value_klass);
+
   virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env,
                                              Register obj, Register tmp, Label& slowpath);
 
   virtual void tlab_allocate(MacroAssembler* masm,
     Register obj,                      // result: pointer to object after successful allocation
diff a/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp
@@ -69,10 +69,11 @@
 class NativeNMethodBarrier {
   address  _instruction_address;
   int*     _guard_addr;
   nmethod* _nm;
 
+public:
   address instruction_address() const { return _instruction_address; }
 
   int *guard_addr() {
     return _guard_addr;
   }
@@ -80,25 +81,26 @@
   int local_guard_offset(nmethod* nm) {
     // It's the last instruction
     return (-entry_barrier_offset(nm)) - 4;
   }
 
-public:
-  NativeNMethodBarrier(nmethod* nm): _nm(nm) {
+  NativeNMethodBarrier(nmethod* nm, address alt_entry_instruction_address = 0): _nm(nm) {
 #if INCLUDE_JVMCI
     if (nm->is_compiled_by_jvmci()) {
+      assert(alt_entry_instruction_address == 0, "invariant");
       address pc = nm->code_begin() + nm->jvmci_nmethod_data()->nmethod_entry_patch_offset();
       RelocIterator iter(nm, pc, pc + 4);
       guarantee(iter.next(), "missing relocs");
       guarantee(iter.type() == relocInfo::section_word_type, "unexpected reloc");
 
       _guard_addr = (int*) iter.section_word_reloc()->target();
       _instruction_address = pc;
     } else
 #endif
       {
-        _instruction_address = nm->code_begin() + nm->frame_complete_offset() + entry_barrier_offset(nm);
+        _instruction_address = (alt_entry_instruction_address != 0) ? alt_entry_instruction_address :
+          nm->code_begin() + nm->frame_complete_offset() + entry_barrier_offset(nm);
         if (nm->is_compiled_by_c2()) {
           // With c2 compiled code, the guard is out-of-line in a stub
           // We find it using the RelocIterator.
           RelocIterator iter(nm);
           while (iter.next()) {
@@ -180,10 +182,35 @@
   new_frame->fp = frame.fp();
   new_frame->lr = frame.pc();
   new_frame->pc = SharedRuntime::get_handle_wrong_method_stub();
 }
 
+static void set_value(nmethod* nm, jint val) {
+  NativeNMethodBarrier cmp1 = NativeNMethodBarrier(nm);
+  cmp1.set_value(val);
+
+  if (!nm->is_osr_method() && nm->method()->has_scalarized_args()) {
+    // nmethods with scalarized arguments have multiple entry points that each have an own nmethod entry barrier
+    assert(nm->verified_entry_point() != nm->verified_inline_entry_point(), "scalarized entry point not found");
+    address method_body = nm->is_compiled_by_c1() ? nm->verified_inline_entry_point() : nm->verified_entry_point();
+    address entry_point2 = nm->is_compiled_by_c1() ? nm->verified_entry_point() : nm->verified_inline_entry_point();
+
+    int barrier_offset = cmp1.instruction_address() - method_body;
+    NativeNMethodBarrier cmp2 = NativeNMethodBarrier(nm, entry_point2 + barrier_offset);
+    assert(cmp1.instruction_address() != cmp2.instruction_address(), "sanity");
+    debug_only(cmp2.verify());
+    cmp2.set_value(val);
+
+    if (method_body != nm->verified_inline_ro_entry_point() && entry_point2 != nm->verified_inline_ro_entry_point()) {
+      NativeNMethodBarrier cmp3 = NativeNMethodBarrier(nm, nm->verified_inline_ro_entry_point() + barrier_offset);
+      assert(cmp1.instruction_address() != cmp3.instruction_address() && cmp2.instruction_address() != cmp3.instruction_address(), "sanity");
+      debug_only(cmp3.verify());
+      cmp3.set_value(val);
+    }
+  }
+}
+
 void BarrierSetNMethod::set_guard_value(nmethod* nm, int value) {
   if (!supports_entry_barrier(nm)) {
     return;
   }
 
@@ -196,12 +223,11 @@
     // further fencing by mutators, before they are allowed to enter.
     BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
     bs_asm->increment_patching_epoch();
   }
 
-  NativeNMethodBarrier barrier(nm);
-  barrier.set_value(value);
+  set_value(nm, value);
 }
 
 int BarrierSetNMethod::guard_value(nmethod* nm) {
   if (!supports_entry_barrier(nm)) {
     return disarmed_guard_value();
diff a/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.cpp
@@ -87,12 +87,21 @@
   bool needs_post_barrier = val != noreg && in_heap;
   BarrierSetAssembler::store_at(masm, decorators, type, dst, val, noreg, noreg, noreg);
   if (needs_post_barrier) {
     // flatten object address if needed
     if (!precise || (dst.index() == noreg && dst.offset() == 0)) {
-      store_check(masm, dst.base(), dst);
+      if (tmp3 != noreg) {
+        // Called by MacroAssembler::pack_inline_helper. We cannot corrupt the dst.base() register
+        __ mov(tmp3, dst.base());
+        store_check(masm, tmp3, dst);
+      } else {
+        // It's OK to corrupt the dst.base() register.
+        store_check(masm, dst.base(), dst);
+      }
+
     } else {
       __ lea(tmp3, dst);
       store_check(masm, tmp3, dst);
     }
   }
+
 }
diff a/src/hotspot/cpu/aarch64/globals_aarch64.hpp b/src/hotspot/cpu/aarch64/globals_aarch64.hpp
--- a/src/hotspot/cpu/aarch64/globals_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/globals_aarch64.hpp
@@ -65,10 +65,13 @@
 define_pd_global(bool, RewriteBytecodes,     true);
 define_pd_global(bool, RewriteFrequentPairs, true);
 
 define_pd_global(bool, PreserveFramePointer, false);
 
+define_pd_global(bool, InlineTypePassFieldsAsArgs, true);
+define_pd_global(bool, InlineTypeReturnedAsFields, true);
+
 define_pd_global(uintx, TypeProfileLevel, 111);
 
 define_pd_global(bool, CompactStrings, true);
 
 // Clear short arrays bigger than one word in an arch-specific way
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
@@ -31,13 +31,15 @@
 #include "interp_masm_aarch64.hpp"
 #include "interpreter/interpreter.hpp"
 #include "interpreter/interpreterRuntime.hpp"
 #include "logging/log.hpp"
 #include "oops/arrayOop.hpp"
+#include "oops/constMethodFlags.hpp"
 #include "oops/markWord.hpp"
 #include "oops/method.hpp"
 #include "oops/methodData.hpp"
+#include "oops/inlineKlass.hpp"
 #include "oops/resolvedFieldEntry.hpp"
 #include "oops/resolvedIndyEntry.hpp"
 #include "oops/resolvedMethodEntry.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "prims/jvmtiThreadState.hpp"
@@ -212,10 +214,73 @@
   ldr(mcs, Address(method, Method::method_counters_offset()));
   cbz(mcs, skip); // No MethodCounters allocated, OutOfMemory
   bind(has_counters);
 }
 
+void InterpreterMacroAssembler::allocate_instance(Register klass, Register new_obj,
+                                                  Register t1, Register t2,
+                                                  bool clear_fields, Label& alloc_failed) {
+  MacroAssembler::allocate_instance(klass, new_obj, t1, t2, clear_fields, alloc_failed);
+  {
+    SkipIfEqual skip_if(this, &DTraceAllocProbes, 0);
+    // Trigger dtrace event for fastpath
+    push(atos);
+    call_VM_leaf(CAST_FROM_FN_PTR(address, static_cast<int (*)(oopDesc*)>(SharedRuntime::dtrace_object_alloc)), new_obj);
+    pop(atos);
+  }
+}
+
+void InterpreterMacroAssembler::read_flat_field(Register holder_klass,
+                                                Register field_index, Register field_offset,
+                                                Register temp, Register obj) {
+  Label alloc_failed, empty_value, done;
+  const Register src = field_offset;
+  const Register alloc_temp = rscratch1;
+  const Register dst_temp   = temp;
+  assert_different_registers(obj, holder_klass, field_index, field_offset, dst_temp);
+
+  // Grab the inline field klass
+  push(holder_klass);
+  const Register field_klass = holder_klass;
+  get_inline_type_field_klass(holder_klass, field_index, field_klass);
+
+  //check for empty value klass
+  test_klass_is_empty_inline_type(field_klass, dst_temp, empty_value);
+
+  // allocate buffer
+  push(obj); // save holder
+  allocate_instance(field_klass, obj, alloc_temp, dst_temp, false, alloc_failed);
+
+  // Have an oop instance buffer, copy into it
+  data_for_oop(obj, dst_temp, field_klass);
+  pop(alloc_temp);             // restore holder
+  lea(src, Address(alloc_temp, field_offset));
+  // call_VM_leaf, clobbers a few regs, save restore new obj
+  push(obj);
+  access_value_copy(IS_DEST_UNINITIALIZED, src, dst_temp, field_klass);
+  pop(obj);
+  pop(holder_klass);
+  b(done);
+
+  bind(empty_value);
+  get_empty_inline_type_oop(field_klass, dst_temp, obj);
+  pop(holder_klass);
+  b(done);
+
+  bind(alloc_failed);
+  pop(obj);
+  pop(holder_klass);
+  call_VM(obj, CAST_FROM_FN_PTR(address, InterpreterRuntime::read_flat_field),
+          obj, field_index, holder_klass);
+
+  bind(done);
+
+  // Ensure the stores to copy the inline field contents are visible
+  // before any subsequent store that publishes this reference.
+  membar(Assembler::StoreStore);
+}
+
 // Load object from cpool->resolved_references(index)
 void InterpreterMacroAssembler::load_resolved_reference_at_index(
                                            Register result, Register index, Register tmp) {
   assert_different_registers(result, index);
 
@@ -246,17 +311,20 @@
 //      Rsub_klass: subklass
 //
 // Kills:
 //      r2, r5
 void InterpreterMacroAssembler::gen_subtype_check(Register Rsub_klass,
-                                                  Label& ok_is_subtype) {
+                                                  Label& ok_is_subtype,
+                                                  bool profile) {
   assert(Rsub_klass != r0, "r0 holds superklass");
   assert(Rsub_klass != r2, "r2 holds 2ndary super array length");
   assert(Rsub_klass != r5, "r5 holds 2ndary super array scan ptr");
 
   // Profile the not-null value's klass.
-  profile_typecheck(r2, Rsub_klass, r5); // blows r2, reloads r5
+  if (profile) {
+    profile_typecheck(r2, Rsub_klass, r5); // blows r2, reloads r5
+  }
 
   // Do the check.
   check_klass_subtype(Rsub_klass, r0, r2, ok_is_subtype); // blows r2
 }
 
@@ -620,10 +688,11 @@
 
   // remove activation
   // get sender esp
   ldr(rscratch2,
       Address(rfp, frame::interpreter_frame_sender_sp_offset * wordSize));
+
   if (StackReservedPages > 0) {
     // testing if reserved zone needs to be re-enabled
     Label no_reserved_zone_enabling;
 
     // check if already enabled - if so no re-enabling needed
@@ -645,10 +714,41 @@
     should_not_reach_here();
 
     bind(no_reserved_zone_enabling);
   }
 
+  if (state == atos && InlineTypeReturnedAsFields) {
+    // Check if we are returning an non-null inline type and load its fields into registers
+    Label skip;
+    test_oop_is_not_inline_type(r0, rscratch2, skip);
+
+    // Load fields from a buffered value with an inline class specific handler
+    load_klass(rscratch1 /*dst*/, r0 /*src*/);
+    ldr(rscratch1, Address(rscratch1, InstanceKlass::adr_inlineklass_fixed_block_offset()));
+    ldr(rscratch1, Address(rscratch1, InlineKlass::unpack_handler_offset()));
+    // Unpack handler can be null if inline type is not scalarizable in returns
+    cbz(rscratch1, skip);
+
+    blr(rscratch1);
+#ifdef ASSERT
+    // TODO 8284443 Enable
+    if (StressCallingConvention && false) {
+      Label skip_stress;
+      ldr(rscratch1, Address(rfp, frame::interpreter_frame_method_offset * wordSize));
+      ldrw(rscratch1, Address(rscratch1, Method::flags_offset()));
+      tstw(rscratch1, ConstMethodFlags::has_scalarized_return_flag());
+      br(Assembler::EQ, skip_stress);
+      load_klass(r0, r0);
+      orr(r0, r0, 1);
+      bind(skip_stress);
+    }
+#endif
+    bind(skip);
+    // Check above kills sender esp in rscratch2. Reload it.
+    ldr(rscratch2, Address(rfp, frame::interpreter_frame_sender_sp_offset * wordSize));
+  }
+
   // restore sender esp
   mov(esp, rscratch2);
   // remove frame anchor
   leave();
   // If we're returning to interpreted code we will shortly be
@@ -706,10 +806,14 @@
       b(count);
     } else if (LockingMode == LM_LEGACY) {
       // Load (object->mark() | 1) into swap_reg
       ldr(rscratch1, Address(obj_reg, oopDesc::mark_offset_in_bytes()));
       orr(swap_reg, rscratch1, 1);
+      if (EnableValhalla) {
+        // Mask inline_type bit such that we go to the slow path if object is an inline type
+        andr(swap_reg, swap_reg, ~((int) markWord::inline_type_bit_in_place));
+      }
 
       // Save (object->mark() | 1) into BasicLock's displaced header
       str(swap_reg, Address(lock_reg, mark_offset));
 
       assert(lock_offset == 0,
@@ -1081,11 +1185,11 @@
     bind(profile_continue);
   }
 }
 
 
-void InterpreterMacroAssembler::profile_not_taken_branch(Register mdp) {
+void InterpreterMacroAssembler::profile_not_taken_branch(Register mdp, bool acmp) {
   if (ProfileInterpreter) {
     Label profile_continue;
 
     // If no method data exists, go to profile_continue.
     test_method_data_pointer(mdp, profile_continue);
@@ -1093,11 +1197,11 @@
     // We are taking a branch.  Increment the not taken count.
     increment_mdp_data_at(mdp, in_bytes(BranchData::not_taken_offset()));
 
     // The method data pointer needs to be updated to correspond to
     // the next bytecode
-    update_mdp_by_constant(mdp, in_bytes(BranchData::branch_data_size()));
+    update_mdp_by_constant(mdp, acmp ? in_bytes(ACmpData::acmp_data_size()) : in_bytes(BranchData::branch_data_size()));
     bind(profile_continue);
   }
 }
 
 
@@ -1416,10 +1520,124 @@
 
     bind(profile_continue);
   }
 }
 
+template <class ArrayData> void InterpreterMacroAssembler::profile_array_type(Register mdp,
+                                                                              Register array,
+                                                                              Register tmp) {
+  if (ProfileInterpreter) {
+    Label profile_continue;
+
+    // If no method data exists, go to profile_continue.
+    test_method_data_pointer(mdp, profile_continue);
+
+    mov(tmp, array);
+    profile_obj_type(tmp, Address(mdp, in_bytes(ArrayData::array_offset())));
+
+    Label not_flat;
+    test_non_flat_array_oop(array, tmp, not_flat);
+
+    set_mdp_flag_at(mdp, ArrayData::flat_array_byte_constant());
+
+    bind(not_flat);
+
+    Label not_null_free;
+    test_non_null_free_array_oop(array, tmp, not_null_free);
+
+    set_mdp_flag_at(mdp, ArrayData::null_free_array_byte_constant());
+
+    bind(not_null_free);
+
+    bind(profile_continue);
+  }
+}
+
+template void InterpreterMacroAssembler::profile_array_type<ArrayLoadData>(Register mdp,
+                                                                           Register array,
+                                                                           Register tmp);
+template void InterpreterMacroAssembler::profile_array_type<ArrayStoreData>(Register mdp,
+                                                                            Register array,
+                                                                            Register tmp);
+
+void InterpreterMacroAssembler::profile_multiple_element_types(Register mdp, Register element, Register tmp, const Register tmp2) {
+  if (ProfileInterpreter) {
+    Label profile_continue;
+
+    // If no method data exists, go to profile_continue.
+    test_method_data_pointer(mdp, profile_continue);
+
+    Label done, update;
+    cbnz(element, update);
+    set_mdp_flag_at(mdp, BitData::null_seen_byte_constant());
+    b(done);
+
+    bind(update);
+    load_klass(tmp, element);
+
+    // Record the object type.
+    record_klass_in_profile(tmp, mdp, tmp2);
+
+    bind(done);
+
+    // The method data pointer needs to be updated.
+    update_mdp_by_constant(mdp, in_bytes(ArrayStoreData::array_store_data_size()));
+
+    bind(profile_continue);
+  }
+}
+
+
+void InterpreterMacroAssembler::profile_element_type(Register mdp,
+                                                     Register element,
+                                                     Register tmp) {
+  if (ProfileInterpreter) {
+    Label profile_continue;
+
+    // If no method data exists, go to profile_continue.
+    test_method_data_pointer(mdp, profile_continue);
+
+    mov(tmp, element);
+    profile_obj_type(tmp, Address(mdp, in_bytes(ArrayLoadData::element_offset())));
+
+    // The method data pointer needs to be updated.
+    update_mdp_by_constant(mdp, in_bytes(ArrayLoadData::array_load_data_size()));
+
+    bind(profile_continue);
+  }
+}
+
+void InterpreterMacroAssembler::profile_acmp(Register mdp,
+                                             Register left,
+                                             Register right,
+                                             Register tmp) {
+  if (ProfileInterpreter) {
+    Label profile_continue;
+
+    // If no method data exists, go to profile_continue.
+    test_method_data_pointer(mdp, profile_continue);
+
+    mov(tmp, left);
+    profile_obj_type(tmp, Address(mdp, in_bytes(ACmpData::left_offset())));
+
+    Label left_not_inline_type;
+    test_oop_is_not_inline_type(left, tmp, left_not_inline_type);
+    set_mdp_flag_at(mdp, ACmpData::left_inline_type_byte_constant());
+    bind(left_not_inline_type);
+
+    mov(tmp, right);
+    profile_obj_type(tmp, Address(mdp, in_bytes(ACmpData::right_offset())));
+
+    Label right_not_inline_type;
+    test_oop_is_not_inline_type(right, tmp, right_not_inline_type);
+    set_mdp_flag_at(mdp, ACmpData::right_inline_type_byte_constant());
+    bind(right_not_inline_type);
+
+    bind(profile_continue);
+  }
+}
+
 void InterpreterMacroAssembler::_interp_verify_oop(Register reg, TosState state, const char* file, int line) {
   if (state == atos) {
     MacroAssembler::_verify_oop_checked(reg, "broken oop", file, line);
   }
 }
@@ -1670,11 +1888,11 @@
       if (MethodData::profile_return()) {
         // We're right after the type profile for the last
         // argument. tmp is the number of cells left in the
         // CallTypeData/VirtualCallTypeData to reach its end. Non null
         // if there's a return to profile.
-        assert(ReturnTypeEntry::static_cell_count() < TypeStackSlotEntries::per_arg_count(), "can't move past ret type");
+        assert(SingleTypeEntry::static_cell_count() < TypeStackSlotEntries::per_arg_count(), "can't move past ret type");
         add(mdp, mdp, tmp, LSL, exact_log2(DataLayout::cell_size));
       }
       str(mdp, Address(rfp, frame::interpreter_frame_mdp_offset * wordSize));
     } else {
       assert(MethodData::profile_return(), "either profile call args or call ret");
@@ -1716,11 +1934,11 @@
       br(Assembler::NE, profile_continue);
 
       bind(do_profile);
     }
 
-    Address mdo_ret_addr(mdp, -in_bytes(ReturnTypeEntry::size()));
+    Address mdo_ret_addr(mdp, -in_bytes(SingleTypeEntry::size()));
     mov(tmp, ret);
     profile_obj_type(tmp, mdo_ret_addr);
 
     bind(profile_continue);
   }
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
@@ -141,10 +141,32 @@
 
   void get_unsigned_2_byte_index_at_bcp(Register reg, int bcp_offset);
   void get_cache_index_at_bcp(Register index, int bcp_offset, size_t index_size = sizeof(u2));
   void get_method_counters(Register method, Register mcs, Label& skip);
 
+  // Kills t1 and t2, perserves klass, return allocation in new_obj
+  void allocate_instance(Register klass, Register new_obj,
+                         Register t1, Register t2,
+                         bool clear_fields, Label& alloc_failed);
+
+  // Allocate instance in "obj" and read in the content of the inline field
+  // NOTES:
+  //   - input holder object via "obj", which must be r0,
+  //     will return new instance via the same reg
+  //   - assumes holder_klass and valueKlass field klass have both been resolved
+  void read_flat_field(Register holder_klass,
+                       Register field_index, Register field_offset,
+                       Register temp,  Register obj = r0);
+
+  // Allocate value buffer in "obj" and read in flat element at the given index
+  // NOTES:
+  //   - Return via "obj" must be r0
+  //   - kills all given regs
+  void read_flat_element(Register array, Register index,
+                         Register t1, Register t2,
+                         Register obj = r0);
+
   // load cpool->resolved_references(index);
   void load_resolved_reference_at_index(Register result, Register index, Register tmp = r5);
 
   // load cpool->resolved_klass_at(index);
   void load_resolved_klass_at_offset(Register cpool, Register index, Register klass, Register temp);
@@ -185,11 +207,11 @@
   void load_float(Address src);
   void load_double(Address src);
 
   // Generate a subtype check: branch to ok_is_subtype if sub_klass is
   // a subtype of super_klass.
-  void gen_subtype_check( Register sub_klass, Label &ok_is_subtype );
+  void gen_subtype_check( Register sub_klass, Label &ok_is_subtype, bool profile = true);
 
   // Dispatching
   void dispatch_prolog(TosState state, int step = 0);
   void dispatch_epilog(TosState state, int step = 0);
   // dispatch via rscratch1
@@ -277,11 +299,11 @@
 
   // narrow int return value
   void narrow(Register result);
 
   void profile_taken_branch(Register mdp, Register bumped_count);
-  void profile_not_taken_branch(Register mdp);
+  void profile_not_taken_branch(Register mdp, bool acmp = false);
   void profile_call(Register mdp);
   void profile_final_call(Register mdp);
   void profile_virtual_call(Register receiver, Register mdp,
                             Register scratch2,
                             bool receiver_can_be_null = false);
@@ -290,10 +312,14 @@
   void profile_typecheck(Register mdp, Register klass, Register scratch);
   void profile_typecheck_failed(Register mdp);
   void profile_switch_default(Register mdp);
   void profile_switch_case(Register index_in_scratch, Register mdp,
                            Register scratch2);
+  template <class ArrayData> void profile_array_type(Register mdp, Register array, Register tmp);
+  void profile_multiple_element_types(Register mdp, Register element, Register tmp, Register tmp2);
+  void profile_element_type(Register mdp, Register element, Register tmp);
+  void profile_acmp(Register mdp, Register left, Register right, Register tmp);
 
   void profile_obj_type(Register obj, const Address& mdo_addr);
   void profile_arguments_type(Register mdp, Register callee, Register tmp, bool is_virtual);
   void profile_return_type(Register mdp, Register ret, Register tmp);
   void profile_parameters_type(Register mdp, Register tmp1, Register tmp2);
diff a/src/hotspot/cpu/aarch64/interpreterRT_aarch64.cpp b/src/hotspot/cpu/aarch64/interpreterRT_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/interpreterRT_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/interpreterRT_aarch64.cpp
@@ -172,10 +172,14 @@
     static_assert(sizeof(jobject) == wordSize, "");
     __ str(r0, Address(to(), next_stack_offset(sizeof(jobject))));
   }
 }
 
+void InterpreterRuntime::SignatureHandlerGenerator::pass_valuetype() {
+   pass_object();
+}
+
 void InterpreterRuntime::SignatureHandlerGenerator::generate(uint64_t fingerprint) {
   // generate code to handle arguments
   iterate(fingerprint);
 
   // return result handler
@@ -256,10 +260,15 @@
     if (pass_gpr(value) < 0) {
       pass_stack<>(value);
     }
   }
 
+  virtual void pass_valuetype() {
+    // values are handled with oops, like objects
+    pass_object();
+  }
+
   virtual void pass_long() {
     intptr_t value = *double_slot_addr();
     if (pass_gpr(value) < 0) {
       pass_stack<>(value);
     }
diff a/src/hotspot/cpu/aarch64/interpreterRT_aarch64.hpp b/src/hotspot/cpu/aarch64/interpreterRT_aarch64.hpp
--- a/src/hotspot/cpu/aarch64/interpreterRT_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/interpreterRT_aarch64.hpp
@@ -44,10 +44,11 @@
   void pass_int();
   void pass_long();
   void pass_float();
   void pass_double();
   void pass_object();
+  void pass_valuetype();
 
   Register next_gpr();
   FloatRegister next_fpr();
   int next_stack_offset(unsigned elem_size);
 
diff a/src/hotspot/cpu/aarch64/jniFastGetField_aarch64.cpp b/src/hotspot/cpu/aarch64/jniFastGetField_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/jniFastGetField_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/jniFastGetField_aarch64.cpp
@@ -29,10 +29,11 @@
 #include "gc/shared/barrierSetAssembler.hpp"
 #include "memory/resourceArea.hpp"
 #include "prims/jniFastGetField.hpp"
 #include "prims/jvm_misc.hpp"
 #include "prims/jvmtiExport.hpp"
+#include "runtime/jfieldIDWorkaround.hpp"
 #include "runtime/javaThread.inline.hpp"
 #include "runtime/safepoint.hpp"
 #include "runtime/threadWXSetters.inline.hpp"
 
 #define __ masm->
@@ -151,11 +152,11 @@
 
   // Both robj and rscratch1 are clobbered by try_resolve_jobject_in_native.
   BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
   bs->try_resolve_jobject_in_native(masm, c_rarg0, robj, rscratch1, slow);
 
-  __ lsr(roffset, c_rarg2, 2);                // offset
+  __ lsr(roffset, c_rarg2, jfieldIDWorkaround::offset_shift);       // offset
   __ add(result, robj, roffset);
 
   assert(count < LIST_CAPACITY, "LIST_CAPACITY too small");
   speculative_load_pclist[count] = __ pc();   // Used by the segfault handler
   // Using acquire: Order JVMTI check and load of result wrt. succeeding check
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
@@ -27,10 +27,11 @@
 
 #include "precompiled.hpp"
 #include "asm/assembler.hpp"
 #include "asm/assembler.inline.hpp"
 #include "ci/ciEnv.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "compiler/compileTask.hpp"
 #include "compiler/disassembler.hpp"
 #include "compiler/oopMap.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/barrierSetAssembler.hpp"
@@ -46,18 +47,21 @@
 #include "nativeInst_aarch64.hpp"
 #include "oops/accessDecorators.hpp"
 #include "oops/compressedKlass.inline.hpp"
 #include "oops/compressedOops.inline.hpp"
 #include "oops/klass.inline.hpp"
+#include "oops/resolvedFieldEntry.hpp"
 #include "runtime/continuation.hpp"
 #include "runtime/icache.hpp"
 #include "runtime/interfaceSupport.inline.hpp"
 #include "runtime/javaThread.hpp"
 #include "runtime/jniHandles.inline.hpp"
 #include "runtime/sharedRuntime.hpp"
+#include "runtime/signature_cc.hpp"
 #include "runtime/stubRoutines.hpp"
 #include "utilities/powerOfTwo.hpp"
+#include "vmreg_aarch64.inline.hpp"
 #ifdef COMPILER1
 #include "c1/c1_LIRAssembler.hpp"
 #endif
 #ifdef COMPILER2
 #include "oops/oop.hpp"
@@ -1119,10 +1123,45 @@
 
 void MacroAssembler::check_and_handle_earlyret(Register java_thread) { }
 
 void MacroAssembler::check_and_handle_popframe(Register java_thread) { }
 
+void MacroAssembler::get_default_value_oop(Register inline_klass, Register temp_reg, Register obj) {
+#ifdef ASSERT
+  {
+    Label done_check;
+    test_klass_is_inline_type(inline_klass, temp_reg, done_check);
+    stop("get_default_value_oop from non inline type klass");
+    bind(done_check);
+  }
+#endif
+  Register offset = temp_reg;
+  // Getting the offset of the pre-allocated default value
+  ldr(offset, Address(inline_klass, in_bytes(InstanceKlass::adr_inlineklass_fixed_block_offset())));
+  ldr(offset, Address(offset, in_bytes(InlineKlass::default_value_offset_offset())));
+
+  // Getting the mirror
+  ldr(obj, Address(inline_klass, in_bytes(Klass::java_mirror_offset())));
+  resolve_oop_handle(obj, inline_klass, temp_reg);
+
+  // Getting the pre-allocated default value from the mirror
+  Address field(obj, offset);
+  load_heap_oop(obj, field, inline_klass, rscratch2);
+}
+
+void MacroAssembler::get_empty_inline_type_oop(Register inline_klass, Register temp_reg, Register obj) {
+#ifdef ASSERT
+  {
+    Label done_check;
+    test_klass_is_empty_inline_type(inline_klass, temp_reg, done_check);
+    stop("get_empty_value from non-empty inline klass");
+    bind(done_check);
+  }
+#endif
+  get_default_value_oop(inline_klass, temp_reg, obj);
+}
+
 // Look up the method for a megamorphic invokeinterface call.
 // The target method is determined by <intf_klass, itable_index>.
 // The receiver klass is in recv_klass.
 // On success, the result will be in method_result, and execution falls through.
 // On failure, execution transfers to the given label.
@@ -1572,11 +1611,15 @@
     Unimplemented();
   }
 }
 
 void MacroAssembler::_verify_oop(Register reg, const char* s, const char* file, int line) {
-  if (!VerifyOops) return;
+  if (!VerifyOops || VerifyAdapterSharing) {
+    // Below address of the code string confuses VerifyAdapterSharing
+    // because it may differ between otherwise equivalent adapters.
+    return;
+  }
 
   // Pass register number to verify_oop_subroutine
   const char* b = nullptr;
   {
     ResourceMark rm;
@@ -1605,11 +1648,15 @@
 
   BLOCK_COMMENT("} verify_oop");
 }
 
 void MacroAssembler::_verify_oop_addr(Address addr, const char* s, const char* file, int line) {
-  if (!VerifyOops) return;
+  if (!VerifyOops || VerifyAdapterSharing) {
+    // Below address of the code string confuses VerifyAdapterSharing
+    // because it may differ between otherwise equivalent adapters.
+    return;
+  }
 
   const char* b = nullptr;
   {
     ResourceMark rm;
     stringStream ss;
@@ -1703,10 +1750,14 @@
   pass_arg1(this, arg_1);
   pass_arg2(this, arg_2);
   call_VM_leaf_base(entry_point, 3);
 }
 
+void MacroAssembler::super_call_VM_leaf(address entry_point) {
+  MacroAssembler::call_VM_leaf_base(entry_point, 1);
+}
+
 void MacroAssembler::super_call_VM_leaf(address entry_point, Register arg_0) {
   pass_arg0(this, arg_0);
   MacroAssembler::call_VM_leaf_base(entry_point, 1);
 }
 
@@ -1748,10 +1799,125 @@
     // nothing to do, (later) access of M[reg + offset]
     // will provoke OS null exception if reg is null
   }
 }
 
+void MacroAssembler::test_markword_is_inline_type(Register markword, Label& is_inline_type) {
+  assert_different_registers(markword, rscratch2);
+  andr(markword, markword, markWord::inline_type_mask_in_place);
+  mov(rscratch2, markWord::inline_type_pattern);
+  cmp(markword, rscratch2);
+  br(Assembler::EQ, is_inline_type);
+}
+
+void MacroAssembler::test_klass_is_inline_type(Register klass, Register temp_reg, Label& is_inline_type) {
+  ldrw(temp_reg, Address(klass, Klass::access_flags_offset()));
+  andr(temp_reg, temp_reg, JVM_ACC_IDENTITY);
+  cbz(temp_reg, is_inline_type);
+}
+
+void MacroAssembler::test_oop_is_not_inline_type(Register object, Register tmp, Label& not_inline_type) {
+  assert_different_registers(tmp, rscratch1);
+  cbz(object, not_inline_type);
+  const int is_inline_type_mask = markWord::inline_type_pattern;
+  ldr(tmp, Address(object, oopDesc::mark_offset_in_bytes()));
+  mov(rscratch1, is_inline_type_mask);
+  andr(tmp, tmp, rscratch1);
+  cmp(tmp, rscratch1);
+  br(Assembler::NE, not_inline_type);
+}
+
+void MacroAssembler::test_klass_is_empty_inline_type(Register klass, Register temp_reg, Label& is_empty_inline_type) {
+#ifdef ASSERT
+  {
+    Label done_check;
+    test_klass_is_inline_type(klass, temp_reg, done_check);
+    stop("test_klass_is_empty_inline_type with non inline type klass");
+    bind(done_check);
+  }
+#endif
+  ldrw(temp_reg, Address(klass, InstanceKlass::misc_flags_offset()));
+  andr(temp_reg, temp_reg, InstanceKlassFlags::is_empty_inline_type_value());
+  cbnz(temp_reg, is_empty_inline_type);
+}
+
+void MacroAssembler::test_field_is_null_free_inline_type(Register flags, Register temp_reg, Label& is_null_free_inline_type) {
+  assert(temp_reg == noreg, "not needed"); // keep signature uniform with x86
+  tbnz(flags, ResolvedFieldEntry::is_null_free_inline_type_shift, is_null_free_inline_type);
+}
+
+void MacroAssembler::test_field_is_not_null_free_inline_type(Register flags, Register temp_reg, Label& not_null_free_inline_type) {
+  assert(temp_reg == noreg, "not needed"); // keep signature uniform with x86
+  tbz(flags, ResolvedFieldEntry::is_null_free_inline_type_shift, not_null_free_inline_type);
+}
+
+void MacroAssembler::test_field_is_flat(Register flags, Register temp_reg, Label& is_flat) {
+  assert(temp_reg == noreg, "not needed"); // keep signature uniform with x86
+  tbnz(flags, ResolvedFieldEntry::is_flat_shift, is_flat);
+}
+
+void MacroAssembler::test_field_has_null_marker(Register flags, Register temp_reg, Label& has_null_marker) {
+  assert(temp_reg == noreg, "not needed"); // keep signature uniform with x86
+  tbnz(flags, ResolvedFieldEntry::has_null_marker_shift, has_null_marker);
+}
+
+void MacroAssembler::test_oop_prototype_bit(Register oop, Register temp_reg, int32_t test_bit, bool jmp_set, Label& jmp_label) {
+  Label test_mark_word;
+  // load mark word
+  ldr(temp_reg, Address(oop, oopDesc::mark_offset_in_bytes()));
+  // check displaced
+  tst(temp_reg, markWord::unlocked_value);
+  br(Assembler::NE, test_mark_word);
+  // slow path use klass prototype
+  load_prototype_header(temp_reg, oop);
+
+  bind(test_mark_word);
+  andr(temp_reg, temp_reg, test_bit);
+  if (jmp_set) {
+    cbnz(temp_reg, jmp_label);
+  } else {
+    cbz(temp_reg, jmp_label);
+  }
+}
+
+void MacroAssembler::test_flat_array_oop(Register oop, Register temp_reg, Label& is_flat_array) {
+  test_oop_prototype_bit(oop, temp_reg, markWord::flat_array_bit_in_place, true, is_flat_array);
+}
+
+void MacroAssembler::test_non_flat_array_oop(Register oop, Register temp_reg,
+                                                  Label&is_non_flat_array) {
+  test_oop_prototype_bit(oop, temp_reg, markWord::flat_array_bit_in_place, false, is_non_flat_array);
+}
+
+void MacroAssembler::test_null_free_array_oop(Register oop, Register temp_reg, Label& is_null_free_array) {
+  test_oop_prototype_bit(oop, temp_reg, markWord::null_free_array_bit_in_place, true, is_null_free_array);
+}
+
+void MacroAssembler::test_non_null_free_array_oop(Register oop, Register temp_reg, Label&is_non_null_free_array) {
+  test_oop_prototype_bit(oop, temp_reg, markWord::null_free_array_bit_in_place, false, is_non_null_free_array);
+}
+
+void MacroAssembler::test_flat_array_layout(Register lh, Label& is_flat_array) {
+  tst(lh, Klass::_lh_array_tag_flat_value_bit_inplace);
+  br(Assembler::NE, is_flat_array);
+}
+
+void MacroAssembler::test_non_flat_array_layout(Register lh, Label& is_non_flat_array) {
+  tst(lh, Klass::_lh_array_tag_flat_value_bit_inplace);
+  br(Assembler::EQ, is_non_flat_array);
+}
+
+void MacroAssembler::test_null_free_array_layout(Register lh, Label& is_null_free_array) {
+  tst(lh, Klass::_lh_null_free_array_bit_inplace);
+  br(Assembler::NE, is_null_free_array);
+}
+
+void MacroAssembler::test_non_null_free_array_layout(Register lh, Label& is_non_null_free_array) {
+  tst(lh, Klass::_lh_null_free_array_bit_inplace);
+  br(Assembler::EQ, is_non_null_free_array);
+}
+
 // MacroAssembler protected routines needed to implement
 // public methods
 
 void MacroAssembler::mov(Register r, Address dest) {
   code_section()->relocate(pc(), dest.rspec());
@@ -4417,10 +4583,18 @@
   ldr(holder, Address(method, Method::const_offset()));                      // ConstMethod*
   ldr(holder, Address(holder, ConstMethod::constants_offset()));             // ConstantPool*
   ldr(holder, Address(holder, ConstantPool::pool_holder_offset()));          // InstanceKlass*
 }
 
+void MacroAssembler::load_metadata(Register dst, Register src) {
+  if (UseCompressedClassPointers) {
+    ldrw(dst, Address(src, oopDesc::klass_offset_in_bytes()));
+  } else {
+    ldr(dst, Address(src, oopDesc::klass_offset_in_bytes()));
+  }
+}
+
 void MacroAssembler::load_klass(Register dst, Register src) {
   if (UseCompressedClassPointers) {
     ldrw(dst, Address(src, oopDesc::klass_offset_in_bytes()));
     decode_klass_not_null(dst);
   } else {
@@ -4492,10 +4666,15 @@
     ldr(tmp, Address(oop, oopDesc::klass_offset_in_bytes()));
   }
   cmp(trial_klass, tmp);
 }
 
+void MacroAssembler::load_prototype_header(Register dst, Register src) {
+  load_klass(dst, src);
+  ldr(dst, Address(dst, Klass::prototype_header_offset()));
+}
+
 void MacroAssembler::store_klass(Register dst, Register src) {
   // FIXME: Should this be a store release?  concurrent gcs assumes
   // klass length is valid if klass field is not null.
   if (UseCompressedClassPointers) {
     encode_klass_not_null(src);
@@ -4816,10 +4995,50 @@
   } else {
     bs->store_at(this, decorators, type, dst, val, tmp1, tmp2, tmp3);
   }
 }
 
+void MacroAssembler::access_value_copy(DecoratorSet decorators, Register src, Register dst,
+                                       Register inline_klass) {
+  BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
+  bs->value_copy(this, decorators, src, dst, inline_klass);
+}
+
+void MacroAssembler::first_field_offset(Register inline_klass, Register offset) {
+  ldr(offset, Address(inline_klass, InstanceKlass::adr_inlineklass_fixed_block_offset()));
+  ldrw(offset, Address(offset, InlineKlass::first_field_offset_offset()));
+}
+
+void MacroAssembler::data_for_oop(Register oop, Register data, Register inline_klass) {
+  // ((address) (void*) o) + vk->first_field_offset();
+  Register offset = (data == oop) ? rscratch1 : data;
+  first_field_offset(inline_klass, offset);
+  if (data == oop) {
+    add(data, data, offset);
+  } else {
+    lea(data, Address(oop, offset));
+  }
+}
+
+void MacroAssembler::data_for_value_array_index(Register array, Register array_klass,
+                                                Register index, Register data) {
+  assert_different_registers(array, array_klass, index);
+  assert_different_registers(rscratch1, array, index);
+
+  // array->base() + (index << Klass::layout_helper_log2_element_size(lh));
+  ldrw(rscratch1, Address(array_klass, Klass::layout_helper_offset()));
+
+  // Klass::layout_helper_log2_element_size(lh)
+  // (lh >> _lh_log2_element_size_shift) & _lh_log2_element_size_mask;
+  lsr(rscratch1, rscratch1, Klass::_lh_log2_element_size_shift);
+  andr(rscratch1, rscratch1, Klass::_lh_log2_element_size_mask);
+  lslv(index, index, rscratch1);
+
+  add(data, array, index);
+  add(data, data, arrayOopDesc::base_offset_in_bytes(T_PRIMITIVE_OBJECT));
+}
+
 void MacroAssembler::load_heap_oop(Register dst, Address src, Register tmp1,
                                    Register tmp2, DecoratorSet decorators) {
   access_load_at(T_OBJECT, IN_HEAP | decorators, dst, src, tmp1, tmp2);
 }
 
@@ -4892,10 +5111,106 @@
 #endif
   int oop_index = oop_recorder()->find_index(obj);
   return Address((address)obj, oop_Relocation::spec(oop_index));
 }
 
+// Object / value buffer allocation...
+void MacroAssembler::allocate_instance(Register klass, Register new_obj,
+                                       Register t1, Register t2,
+                                       bool clear_fields, Label& alloc_failed)
+{
+  Label done, initialize_header, initialize_object, slow_case, slow_case_no_pop;
+  Register layout_size = t1;
+  assert(new_obj == r0, "needs to be r0");
+  assert_different_registers(klass, new_obj, t1, t2);
+
+  // get instance_size in InstanceKlass (scaled to a count of bytes)
+  ldrw(layout_size, Address(klass, Klass::layout_helper_offset()));
+  // test to see if it has a finalizer or is malformed in some way
+  tst(layout_size, Klass::_lh_instance_slow_path_bit);
+  br(Assembler::NE, slow_case_no_pop);
+
+  // Allocate the instance:
+  //  If TLAB is enabled:
+  //    Try to allocate in the TLAB.
+  //    If fails, go to the slow path.
+  //    Initialize the allocation.
+  //    Exit.
+  //
+  //  Go to slow path.
+
+  if (UseTLAB) {
+    push(klass);
+    tlab_allocate(new_obj, layout_size, 0, klass, t2, slow_case);
+    if (ZeroTLAB || (!clear_fields)) {
+      // the fields have been already cleared
+      b(initialize_header);
+    } else {
+      // initialize both the header and fields
+      b(initialize_object);
+    }
+
+    if (clear_fields) {
+      // The object is initialized before the header.  If the object size is
+      // zero, go directly to the header initialization.
+      bind(initialize_object);
+      subs(layout_size, layout_size, sizeof(oopDesc));
+      br(Assembler::EQ, initialize_header);
+
+      // Initialize topmost object field, divide size by 8, check if odd and
+      // test if zero.
+
+  #ifdef ASSERT
+      // make sure instance_size was multiple of 8
+      Label L;
+      tst(layout_size, 7);
+      br(Assembler::EQ, L);
+      stop("object size is not multiple of 8 - adjust this code");
+      bind(L);
+      // must be > 0, no extra check needed here
+  #endif
+
+      lsr(layout_size, layout_size, LogBytesPerLong);
+
+      // initialize remaining object fields: instance_size was a multiple of 8
+      {
+        Label loop;
+        Register base = t2;
+
+        bind(loop);
+        add(rscratch1, new_obj, layout_size, Assembler::LSL, LogBytesPerLong);
+        str(zr, Address(rscratch1, sizeof(oopDesc) - 1*oopSize));
+        subs(layout_size, layout_size, 1);
+        br(Assembler::NE, loop);
+      }
+    } // clear_fields
+
+    // initialize object header only.
+    bind(initialize_header);
+    pop(klass);
+    Register mark_word = t2;
+    ldr(mark_word, Address(klass, Klass::prototype_header_offset()));
+    str(mark_word, Address(new_obj, oopDesc::mark_offset_in_bytes ()));
+    store_klass_gap(new_obj, zr);  // zero klass gap for compressed oops
+    mov(t2, klass);         // preserve klass
+    store_klass(new_obj, t2);  // src klass reg is potentially compressed
+
+    // TODO: Valhalla removed SharedRuntime::dtrace_object_alloc from here ?
+
+    b(done);
+  }
+
+  if (UseTLAB) {
+    bind(slow_case);
+    pop(klass);
+  }
+  bind(slow_case_no_pop);
+  b(alloc_failed);
+
+  bind(done);
+}
+
 // Defines obj, preserves var_size_in_bytes, okay for t2 == var_size_in_bytes.
 void MacroAssembler::tlab_allocate(Register obj,
                                    Register var_size_in_bytes,
                                    int con_size_in_bytes,
                                    Register t1,
@@ -4931,10 +5246,24 @@
     ldp(rscratch2, rscratch1, Address(post(sp, 16)));
   }
 #endif
 }
 
+void MacroAssembler::get_inline_type_field_klass(Register klass, Register index, Register inline_klass) {
+  ldr(inline_klass, Address(klass, InstanceKlass::inline_type_field_klasses_offset()));
+#ifdef ASSERT
+  {
+    Label done;
+    cbnz(inline_klass, done);
+    stop("get_inline_type_field_klass contains no inline klass");
+    bind(done);
+  }
+#endif
+  lea(inline_klass, Address(inline_klass, Array<InlineKlass*>::base_offset_in_bytes()));
+  ldr(inline_klass, Address(inline_klass, index, Address::lsl(3)));
+}
+
 // Writes to stack successive pages until offset reached to check for
 // stack overflow + shadow pages.  This clobbers tmp.
 void MacroAssembler::bang_stack_size(Register size, Register tmp) {
   assert_different_registers(tmp, size, rscratch1);
   mov(tmp, sp);
@@ -5056,10 +5385,61 @@
     ldp(rfp, lr, Address(post(sp, 2 * wordSize)));
   }
   authenticate_return_address();
 }
 
+void MacroAssembler::remove_frame(int initial_framesize, bool needs_stack_repair) {
+  if (needs_stack_repair) {
+    // Remove the extension of the caller's frame used for inline type unpacking
+    //
+    // Right now the stack looks like this:
+    //
+    // | Arguments from caller     |
+    // |---------------------------|  <-- caller's SP
+    // | Saved LR #1               |
+    // | Saved FP #1               |
+    // |---------------------------|
+    // | Extension space for       |
+    // |   inline arg (un)packing  |
+    // |---------------------------|  <-- start of this method's frame
+    // | Saved LR #2               |
+    // | Saved FP #2               |
+    // |---------------------------|  <-- FP
+    // | sp_inc                    |
+    // | method locals             |
+    // |---------------------------|  <-- SP
+    //
+    // There are two copies of FP and LR on the stack. They will be identical
+    // unless the caller has been deoptimized, in which case LR #1 will be patched
+    // to point at the deopt blob, and LR #2 will still point into the old method.
+    //
+    // The sp_inc stack slot holds the total size of the frame including the
+    // extension space minus two words for the saved FP and LR.
+
+    int sp_inc_offset = initial_framesize - 3 * wordSize;  // Immediately below saved LR and FP
+
+    ldr(rscratch1, Address(sp, sp_inc_offset));
+    add(sp, sp, rscratch1);
+    ldp(rfp, lr, Address(post(sp, 2 * wordSize)));
+  } else {
+    remove_frame(initial_framesize);
+  }
+}
+
+void MacroAssembler::save_stack_increment(int sp_inc, int frame_size) {
+  int real_frame_size = frame_size + sp_inc;
+  assert(sp_inc == 0 || sp_inc > 2*wordSize, "invalid sp_inc value");
+  assert(real_frame_size >= 2*wordSize, "frame size must include FP/LR space");
+  assert((real_frame_size & (StackAlignmentInBytes-1)) == 0, "frame size not aligned");
+
+  int sp_inc_offset = frame_size - 3 * wordSize;  // Immediately below saved LR and FP
+
+  // Subtract two words for the saved FP and LR as these will be popped
+  // separately. See remove_frame above.
+  mov(rscratch1, real_frame_size - 2*wordSize);
+  str(rscratch1, Address(sp, sp_inc_offset));
+}
 
 // This method counts leading positive bytes (highest bit not set) in provided byte array
 address MacroAssembler::count_positives(Register ary1, Register len, Register result) {
     // Simple and most common case of aligned small array which is not at the
     // end of memory page is placed here. All other cases are in stub.
@@ -5970,10 +6350,451 @@
 
   pop(saved_regs, sp);
   authenticate_return_address();
 }
 
+#ifdef COMPILER2
+// C2 compiled method's prolog code
+// Moved here from aarch64.ad to support Valhalla code belows
+void MacroAssembler::verified_entry(Compile* C, int sp_inc) {
+  if (C->clinit_barrier_on_entry()) {
+    assert(!C->method()->holder()->is_not_initialized(), "initialization should have been started");
+
+    Label L_skip_barrier;
+
+    mov_metadata(rscratch2, C->method()->holder()->constant_encoding());
+    clinit_barrier(rscratch2, rscratch1, &L_skip_barrier);
+    far_jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub()));
+    bind(L_skip_barrier);
+  }
+
+  if (C->max_vector_size() > 0) {
+    reinitialize_ptrue();
+  }
+
+  int bangsize = C->output()->bang_size_in_bytes();
+  if (C->output()->need_stack_bang(bangsize))
+    generate_stack_overflow_check(bangsize);
+
+  // n.b. frame size includes space for return pc and rfp
+  const long framesize = C->output()->frame_size_in_bytes();
+  build_frame(framesize);
+
+  if (C->needs_stack_repair()) {
+    save_stack_increment(sp_inc, framesize);
+  }
+
+  if (VerifyStackAtCalls) {
+    Unimplemented();
+  }
+}
+#endif // COMPILER2
+
+int MacroAssembler::store_inline_type_fields_to_buf(ciInlineKlass* vk, bool from_interpreter) {
+  assert(InlineTypeReturnedAsFields, "Inline types should never be returned as fields");
+  // An inline type might be returned. If fields are in registers we
+  // need to allocate an inline type instance and initialize it with
+  // the value of the fields.
+  Label skip;
+  // We only need a new buffered inline type if a new one is not returned
+  tbz(r0, 0, skip);
+  int call_offset = -1;
+
+  // Be careful not to clobber r1-7 which hold returned fields
+  // Also do not use callee-saved registers as these may be live in the interpreter
+  Register tmp1 = r13, tmp2 = r14, klass = r15, r0_preserved = r12;
+
+  // The following code is similar to allocate_instance but has some slight differences,
+  // e.g. object size is always not zero, sometimes it's constant; storing klass ptr after
+  // allocating is not necessary if vk != nullptr, etc. allocate_instance is not aware of these.
+  Label slow_case;
+  // 1. Try to allocate a new buffered inline instance either from TLAB or eden space
+  mov(r0_preserved, r0); // save r0 for slow_case since *_allocate may corrupt it when allocation failed
+
+  if (vk != nullptr) {
+    // Called from C1, where the return type is statically known.
+    movptr(klass, (intptr_t)vk->get_InlineKlass());
+    jint obj_size = vk->layout_helper();
+    assert(obj_size != Klass::_lh_neutral_value, "inline class in return type must have been resolved");
+    if (UseTLAB) {
+      tlab_allocate(r0, noreg, obj_size, tmp1, tmp2, slow_case);
+    } else {
+      b(slow_case);
+    }
+  } else {
+    // Call from interpreter. R0 contains ((the InlineKlass* of the return type) | 0x01)
+    andr(klass, r0, -2);
+    ldrw(tmp2, Address(klass, Klass::layout_helper_offset()));
+    if (UseTLAB) {
+      tlab_allocate(r0, tmp2, 0, tmp1, tmp2, slow_case);
+    } else {
+      b(slow_case);
+    }
+  }
+  if (UseTLAB) {
+    // 2. Initialize buffered inline instance header
+    Register buffer_obj = r0;
+    mov(rscratch1, (intptr_t)markWord::inline_type_prototype().value());
+    str(rscratch1, Address(buffer_obj, oopDesc::mark_offset_in_bytes()));
+    store_klass_gap(buffer_obj, zr);
+    if (vk == nullptr) {
+      // store_klass corrupts klass, so save it for later use (interpreter case only).
+      mov(tmp1, klass);
+    }
+    store_klass(buffer_obj, klass);
+    // 3. Initialize its fields with an inline class specific handler
+    if (vk != nullptr) {
+      far_call(RuntimeAddress(vk->pack_handler())); // no need for call info as this will not safepoint.
+    } else {
+      // tmp1 holds klass preserved above
+      ldr(tmp1, Address(tmp1, InstanceKlass::adr_inlineklass_fixed_block_offset()));
+      ldr(tmp1, Address(tmp1, InlineKlass::pack_handler_offset()));
+      blr(tmp1);
+    }
+
+    membar(Assembler::StoreStore);
+    b(skip);
+  } else {
+    // Must have already branched to slow_case above.
+    DEBUG_ONLY(should_not_reach_here());
+  }
+  bind(slow_case);
+  // We failed to allocate a new inline type, fall back to a runtime
+  // call. Some oop field may be live in some registers but we can't
+  // tell. That runtime call will take care of preserving them
+  // across a GC if there's one.
+  mov(r0, r0_preserved);
+
+  if (from_interpreter) {
+    super_call_VM_leaf(StubRoutines::store_inline_type_fields_to_buf());
+  } else {
+    far_call(RuntimeAddress(StubRoutines::store_inline_type_fields_to_buf()));
+    call_offset = offset();
+  }
+  membar(Assembler::StoreStore);
+
+  bind(skip);
+  return call_offset;
+}
+
+// Move a value between registers/stack slots and update the reg_state
+bool MacroAssembler::move_helper(VMReg from, VMReg to, BasicType bt, RegState reg_state[]) {
+  assert(from->is_valid() && to->is_valid(), "source and destination must be valid");
+  if (reg_state[to->value()] == reg_written) {
+    return true; // Already written
+  }
+
+  if (from != to && bt != T_VOID) {
+    if (reg_state[to->value()] == reg_readonly) {
+      return false; // Not yet writable
+    }
+    if (from->is_reg()) {
+      if (to->is_reg()) {
+        if (from->is_Register() && to->is_Register()) {
+          mov(to->as_Register(), from->as_Register());
+        } else if (from->is_FloatRegister() && to->is_FloatRegister()) {
+          fmovd(to->as_FloatRegister(), from->as_FloatRegister());
+        } else {
+          ShouldNotReachHere();
+        }
+      } else {
+        int st_off = to->reg2stack() * VMRegImpl::stack_slot_size;
+        Address to_addr = Address(sp, st_off);
+        if (from->is_FloatRegister()) {
+          if (bt == T_DOUBLE) {
+             strd(from->as_FloatRegister(), to_addr);
+          } else {
+             assert(bt == T_FLOAT, "must be float");
+             strs(from->as_FloatRegister(), to_addr);
+          }
+        } else {
+          str(from->as_Register(), to_addr);
+        }
+      }
+    } else {
+      Address from_addr = Address(sp, from->reg2stack() * VMRegImpl::stack_slot_size);
+      if (to->is_reg()) {
+        if (to->is_FloatRegister()) {
+          if (bt == T_DOUBLE) {
+            ldrd(to->as_FloatRegister(), from_addr);
+          } else {
+            assert(bt == T_FLOAT, "must be float");
+            ldrs(to->as_FloatRegister(), from_addr);
+          }
+        } else {
+          ldr(to->as_Register(), from_addr);
+        }
+      } else {
+        int st_off = to->reg2stack() * VMRegImpl::stack_slot_size;
+        ldr(rscratch1, from_addr);
+        str(rscratch1, Address(sp, st_off));
+      }
+    }
+  }
+
+  // Update register states
+  reg_state[from->value()] = reg_writable;
+  reg_state[to->value()] = reg_written;
+  return true;
+}
+
+// Calculate the extra stack space required for packing or unpacking inline
+// args and adjust the stack pointer
+int MacroAssembler::extend_stack_for_inline_args(int args_on_stack) {
+  int sp_inc = args_on_stack * VMRegImpl::stack_slot_size;
+  sp_inc = align_up(sp_inc, StackAlignmentInBytes);
+  assert(sp_inc > 0, "sanity");
+
+  // Save a copy of the FP and LR here for deoptimization patching and frame walking
+  stp(rfp, lr, Address(pre(sp, -2 * wordSize)));
+
+  // Adjust the stack pointer. This will be repaired on return by MacroAssembler::remove_frame
+  if (sp_inc < (1 << 9)) {
+    sub(sp, sp, sp_inc);   // Fits in an immediate
+  } else {
+    mov(rscratch1, sp_inc);
+    sub(sp, sp, rscratch1);
+  }
+
+  return sp_inc + 2 * wordSize;  // Account for the FP/LR space
+}
+
+// Read all fields from an inline type oop and store the values in registers/stack slots
+bool MacroAssembler::unpack_inline_helper(const GrowableArray<SigEntry>* sig, int& sig_index,
+                                          VMReg from, int& from_index, VMRegPair* to, int to_count, int& to_index,
+                                          RegState reg_state[]) {
+  assert(sig->at(sig_index)._bt == T_VOID, "should be at end delimiter");
+  assert(from->is_valid(), "source must be valid");
+  bool progress = false;
+#ifdef ASSERT
+  const int start_offset = offset();
+#endif
+
+  Label L_null, L_notNull;
+  // Don't use r14 as tmp because it's used for spilling (see MacroAssembler::spill_reg_for)
+  Register tmp1 = r10;
+  Register tmp2 = r11;
+  Register fromReg = noreg;
+  ScalarizedInlineArgsStream stream(sig, sig_index, to, to_count, to_index, -1);
+  bool done = true;
+  bool mark_done = true;
+  VMReg toReg;
+  BasicType bt;
+  // Check if argument requires a null check
+  bool null_check = false;
+  VMReg nullCheckReg;
+  while (stream.next(nullCheckReg, bt)) {
+    if (sig->at(stream.sig_index())._offset == -1) {
+      null_check = true;
+      break;
+    }
+  }
+  stream.reset(sig_index, to_index);
+  while (stream.next(toReg, bt)) {
+    assert(toReg->is_valid(), "destination must be valid");
+    int idx = (int)toReg->value();
+    if (reg_state[idx] == reg_readonly) {
+      if (idx != from->value()) {
+        mark_done = false;
+      }
+      done = false;
+      continue;
+    } else if (reg_state[idx] == reg_written) {
+      continue;
+    }
+    assert(reg_state[idx] == reg_writable, "must be writable");
+    reg_state[idx] = reg_written;
+    progress = true;
+
+    if (fromReg == noreg) {
+      if (from->is_reg()) {
+        fromReg = from->as_Register();
+      } else {
+        int st_off = from->reg2stack() * VMRegImpl::stack_slot_size;
+        ldr(tmp1, Address(sp, st_off));
+        fromReg = tmp1;
+      }
+      if (null_check) {
+        // Nullable inline type argument, emit null check
+        cbz(fromReg, L_null);
+      }
+    }
+    int off = sig->at(stream.sig_index())._offset;
+    if (off == -1) {
+      assert(null_check, "Missing null check at");
+      if (toReg->is_stack()) {
+        int st_off = toReg->reg2stack() * VMRegImpl::stack_slot_size;
+        mov(tmp2, 1);
+        str(tmp2, Address(sp, st_off));
+      } else {
+        mov(toReg->as_Register(), 1);
+      }
+      continue;
+    }
+    assert(off > 0, "offset in object should be positive");
+    Address fromAddr = Address(fromReg, off);
+    if (!toReg->is_FloatRegister()) {
+      Register dst = toReg->is_stack() ? tmp2 : toReg->as_Register();
+      if (is_reference_type(bt)) {
+        load_heap_oop(dst, fromAddr, rscratch1, rscratch2);
+      } else {
+        bool is_signed = (bt != T_CHAR) && (bt != T_BOOLEAN);
+        load_sized_value(dst, fromAddr, type2aelembytes(bt), is_signed);
+      }
+      if (toReg->is_stack()) {
+        int st_off = toReg->reg2stack() * VMRegImpl::stack_slot_size;
+        str(dst, Address(sp, st_off));
+      }
+    } else if (bt == T_DOUBLE) {
+      ldrd(toReg->as_FloatRegister(), fromAddr);
+    } else {
+      assert(bt == T_FLOAT, "must be float");
+      ldrs(toReg->as_FloatRegister(), fromAddr);
+    }
+  }
+  if (progress && null_check) {
+    if (done) {
+      b(L_notNull);
+      bind(L_null);
+      // Set IsInit field to zero to signal that the argument is null.
+      // Also set all oop fields to zero to make the GC happy.
+      stream.reset(sig_index, to_index);
+      while (stream.next(toReg, bt)) {
+        if (sig->at(stream.sig_index())._offset == -1 ||
+            bt == T_OBJECT || bt == T_ARRAY) {
+          if (toReg->is_stack()) {
+            int st_off = toReg->reg2stack() * VMRegImpl::stack_slot_size;
+            str(zr, Address(sp, st_off));
+          } else {
+            mov(toReg->as_Register(), zr);
+          }
+        }
+      }
+      bind(L_notNull);
+    } else {
+      bind(L_null);
+    }
+  }
+
+  sig_index = stream.sig_index();
+  to_index = stream.regs_index();
+
+  if (mark_done && reg_state[from->value()] != reg_written) {
+    // This is okay because no one else will write to that slot
+    reg_state[from->value()] = reg_writable;
+  }
+  from_index--;
+  assert(progress || (start_offset == offset()), "should not emit code");
+  return done;
+}
+
+// Pack fields back into an inline type oop
+bool MacroAssembler::pack_inline_helper(const GrowableArray<SigEntry>* sig, int& sig_index, int vtarg_index,
+                                        VMRegPair* from, int from_count, int& from_index, VMReg to,
+                                        RegState reg_state[], Register val_array) {
+  assert(sig->at(sig_index)._bt == T_METADATA, "should be at delimiter");
+  assert(to->is_valid(), "destination must be valid");
+
+  if (reg_state[to->value()] == reg_written) {
+    skip_unpacked_fields(sig, sig_index, from, from_count, from_index);
+    return true; // Already written
+  }
+
+  // The GC barrier expanded by store_heap_oop below may call into the
+  // runtime so use callee-saved registers for any values that need to be
+  // preserved. The GC barrier assembler should take care of saving the
+  // Java argument registers.
+  // TODO 8284443 Isn't it an issue if below code uses r14 as tmp when it contains a spilled value?
+  // Be careful with r14 because it's used for spilling (see MacroAssembler::spill_reg_for).
+  Register val_obj_tmp = r21;
+  Register from_reg_tmp = r22;
+  Register tmp1 = r14;
+  Register tmp2 = r13;
+  Register tmp3 = r12;
+  Register val_obj = to->is_stack() ? val_obj_tmp : to->as_Register();
+
+  assert_different_registers(val_obj_tmp, from_reg_tmp, tmp1, tmp2, tmp3, val_array);
+
+  if (reg_state[to->value()] == reg_readonly) {
+    if (!is_reg_in_unpacked_fields(sig, sig_index, to, from, from_count, from_index)) {
+      skip_unpacked_fields(sig, sig_index, from, from_count, from_index);
+      return false; // Not yet writable
+    }
+    val_obj = val_obj_tmp;
+  }
+
+  int index = arrayOopDesc::base_offset_in_bytes(T_OBJECT) + vtarg_index * type2aelembytes(T_OBJECT);
+  load_heap_oop(val_obj, Address(val_array, index), tmp1, tmp2);
+
+  ScalarizedInlineArgsStream stream(sig, sig_index, from, from_count, from_index);
+  VMReg fromReg;
+  BasicType bt;
+  Label L_null;
+  while (stream.next(fromReg, bt)) {
+    assert(fromReg->is_valid(), "source must be valid");
+    reg_state[fromReg->value()] = reg_writable;
+
+    int off = sig->at(stream.sig_index())._offset;
+    if (off == -1) {
+      // Nullable inline type argument, emit null check
+      Label L_notNull;
+      if (fromReg->is_stack()) {
+        int ld_off = fromReg->reg2stack() * VMRegImpl::stack_slot_size;
+        ldrb(tmp2, Address(sp, ld_off));
+        cbnz(tmp2, L_notNull);
+      } else {
+        cbnz(fromReg->as_Register(), L_notNull);
+      }
+      mov(val_obj, 0);
+      b(L_null);
+      bind(L_notNull);
+      continue;
+    }
+
+    assert(off > 0, "offset in object should be positive");
+    size_t size_in_bytes = is_java_primitive(bt) ? type2aelembytes(bt) : wordSize;
+
+    // Pack the scalarized field into the value object.
+    Address dst(val_obj, off);
+
+    if (!fromReg->is_FloatRegister()) {
+      Register src;
+      if (fromReg->is_stack()) {
+        src = from_reg_tmp;
+        int ld_off = fromReg->reg2stack() * VMRegImpl::stack_slot_size;
+        load_sized_value(src, Address(sp, ld_off), size_in_bytes, /* is_signed */ false);
+      } else {
+        src = fromReg->as_Register();
+      }
+      assert_different_registers(dst.base(), src, tmp1, tmp2, tmp3, val_array);
+      if (is_reference_type(bt)) {
+        store_heap_oop(dst, src, tmp1, tmp2, tmp3, IN_HEAP | ACCESS_WRITE | IS_DEST_UNINITIALIZED);
+      } else {
+        store_sized_value(dst, src, size_in_bytes);
+      }
+    } else if (bt == T_DOUBLE) {
+      strd(fromReg->as_FloatRegister(), dst);
+    } else {
+      assert(bt == T_FLOAT, "must be float");
+      strs(fromReg->as_FloatRegister(), dst);
+    }
+  }
+  bind(L_null);
+  sig_index = stream.sig_index();
+  from_index = stream.regs_index();
+
+  assert(reg_state[to->value()] == reg_writable, "must have already been read");
+  bool success = move_helper(val_obj->as_VMReg(), to, T_OBJECT, reg_state);
+  assert(success, "to register must be writeable");
+
+  return true;
+}
+
+VMReg MacroAssembler::spill_reg_for(VMReg reg) {
+  return (reg->is_FloatRegister()) ? v8->as_VMReg() : r14->as_VMReg();
+}
+
 void MacroAssembler::cache_wb(Address line) {
   assert(line.getMode() == Address::base_plus_offset, "mode should be base_plus_offset");
   assert(line.index() == noreg, "index should be noreg");
   assert(line.offset() == 0, "offset should be 0");
   // would like to assert this
@@ -6343,10 +7164,15 @@
   cmpw(t1, (unsigned)LockStack::end_offset() - 1);
   br(Assembler::GT, slow);
 
   // Load (object->mark() | 1) into hdr
   orr(hdr, hdr, markWord::unlocked_value);
+  if (EnableValhalla) {
+    // Mask inline_type bit such that we go to the slow path if object is an inline type
+    andr(hdr, hdr, ~((int) markWord::inline_type_bit_in_place));
+  }
+
   // Clear lock-bits, into t2
   eor(t2, hdr, markWord::unlocked_value);
   // Try to swing header from unlocked to locked
   // Clobbers rscratch1 when UseLSE is false
   cmpxchg(/*addr*/ obj, /*expected*/ hdr, /*new*/ t2, Assembler::xword,
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
@@ -30,11 +30,16 @@
 #include "code/vmreg.hpp"
 #include "metaprogramming/enableIf.hpp"
 #include "oops/compressedOops.hpp"
 #include "oops/compressedKlass.hpp"
 #include "runtime/vm_version.hpp"
+#include "utilities/macros.hpp"
 #include "utilities/powerOfTwo.hpp"
+#include "runtime/signature.hpp"
+
+
+class ciInlineKlass;
 
 class OopMap;
 
 // MacroAssembler extends Assembler by frequently used macros.
 //
@@ -625,10 +630,42 @@
 
   virtual void null_check(Register reg, int offset = -1);
   static bool needs_explicit_null_check(intptr_t offset);
   static bool uses_implicit_null_check(void* address);
 
+  // markWord tests, kills markWord reg
+  void test_markword_is_inline_type(Register markword, Label& is_inline_type);
+
+  // inlineKlass queries, kills temp_reg
+  void test_klass_is_inline_type(Register klass, Register temp_reg, Label& is_inline_type);
+  void test_klass_is_empty_inline_type(Register klass, Register temp_reg, Label& is_empty_inline_type);
+  void test_oop_is_not_inline_type(Register object, Register tmp, Label& not_inline_type);
+
+  // Get the default value oop for the given InlineKlass
+  void get_default_value_oop(Register inline_klass, Register temp_reg, Register obj);
+  // The empty value oop, for the given InlineKlass ("empty" as in no instance fields)
+  // get_default_value_oop with extra assertion for empty inline klass
+  void get_empty_inline_type_oop(Register inline_klass, Register temp_reg, Register obj);
+
+  void test_field_is_null_free_inline_type(Register flags, Register temp_reg, Label& is_null_free);
+  void test_field_is_not_null_free_inline_type(Register flags, Register temp_reg, Label& not_null_free);
+  void test_field_is_flat(Register flags, Register temp_reg, Label& is_flat);
+  void test_field_has_null_marker(Register flags, Register temp_reg, Label& has_null_marker);
+
+  // Check oops for special arrays, i.e. flat arrays and/or null-free arrays
+  void test_oop_prototype_bit(Register oop, Register temp_reg, int32_t test_bit, bool jmp_set, Label& jmp_label);
+  void test_flat_array_oop(Register klass, Register temp_reg, Label& is_flat_array);
+  void test_non_flat_array_oop(Register oop, Register temp_reg, Label&is_non_flat_array);
+  void test_null_free_array_oop(Register oop, Register temp_reg, Label& is_null_free_array);
+  void test_non_null_free_array_oop(Register oop, Register temp_reg, Label&is_non_null_free_array);
+
+  // Check array klass layout helper for flat or null-free arrays...
+  void test_flat_array_layout(Register lh, Label& is_flat_array);
+  void test_non_flat_array_layout(Register lh, Label& is_non_flat_array);
+  void test_null_free_array_layout(Register lh, Label& is_null_free_array);
+  void test_non_null_free_array_layout(Register lh, Label& is_non_null_free_array);
+
   static address target_addr_for_insn(address insn_addr, unsigned insn);
   static address target_addr_for_insn_or_null(address insn_addr, unsigned insn);
   static address target_addr_for_insn(address insn_addr) {
     unsigned insn = *(unsigned*)insn_addr;
     return target_addr_for_insn(insn_addr, insn);
@@ -861,10 +898,12 @@
 
   void load_method_holder_cld(Register rresult, Register rmethod);
   void load_method_holder(Register holder, Register method);
 
   // oop manipulations
+  void load_metadata(Register dst, Register src);
+
   void load_klass(Register dst, Register src);
   void store_klass(Register dst, Register src);
   void cmp_klass(Register oop, Register trial_klass, Register tmp);
 
   void resolve_weak_handle(Register result, Register tmp1, Register tmp2);
@@ -875,10 +914,19 @@
                       Register tmp1, Register tmp2);
 
   void access_store_at(BasicType type, DecoratorSet decorators, Address dst, Register val,
                        Register tmp1, Register tmp2, Register tmp3);
 
+  void access_value_copy(DecoratorSet decorators, Register src, Register dst, Register inline_klass);
+
+  // inline type data payload offsets...
+  void first_field_offset(Register inline_klass, Register offset);
+  void data_for_oop(Register oop, Register data, Register inline_klass);
+  // get data payload ptr a flat value array at index, kills rcx and index
+  void data_for_value_array_index(Register array, Register array_klass,
+                                  Register index, Register data);
+
   void load_heap_oop(Register dst, Address src, Register tmp1,
                      Register tmp2, DecoratorSet decorators = 0);
 
   void load_heap_oop_not_null(Register dst, Address src, Register tmp1,
                               Register tmp2, DecoratorSet decorators = 0);
@@ -888,10 +936,12 @@
   // currently unimplemented
   // Used for storing null. All other oop constants should be
   // stored using routines that take a jobject.
   void store_heap_oop_null(Address dst);
 
+  void load_prototype_header(Register dst, Register src);
+
   void store_klass_gap(Register dst, Register src);
 
   // This dummy is to prevent a call to store_heap_oop from
   // converting a zero (like null) into a Register by giving
   // the compiler two choices it can't resolve
@@ -935,20 +985,32 @@
   // java.lang.Math::round intrinsics
   void java_round_double(Register dst, FloatRegister src, FloatRegister ftmp);
   void java_round_float(Register dst, FloatRegister src, FloatRegister ftmp);
 
   // allocation
+
+  // Object / value buffer allocation...
+  // Allocate instance of klass, assumes klass initialized by caller
+  // new_obj prefers to be rax
+  // Kills t1 and t2, perserves klass, return allocation in new_obj (rsi on LP64)
+  void allocate_instance(Register klass, Register new_obj,
+                         Register t1, Register t2,
+                         bool clear_fields, Label& alloc_failed);
+
   void tlab_allocate(
     Register obj,                      // result: pointer to object after successful allocation
     Register var_size_in_bytes,        // object size in bytes if unknown at compile time; invalid otherwise
     int      con_size_in_bytes,        // object size in bytes if   known at compile time
     Register t1,                       // temp register
     Register t2,                       // temp register
     Label&   slow_case                 // continuation point if fast allocation fails
   );
   void verify_tlab();
 
+  // For field "index" within "klass", return inline_klass ...
+  void get_inline_type_field_klass(Register klass, Register index, Register inline_klass);
+
   // interface method calling
   void lookup_interface_method(Register recv_klass,
                                Register intf_klass,
                                RegisterOrConstant itable_index,
                                Register method_result,
@@ -1337,10 +1399,28 @@
   void sub(Register Rd, Register Rn, RegisterOrConstant decrement);
   void subw(Register Rd, Register Rn, RegisterOrConstant decrement);
 
   void adrp(Register reg1, const Address &dest, uint64_t &byte_offset);
 
+  void verified_entry(Compile* C, int sp_inc);
+
+  // Inline type specific methods
+  #include "asm/macroAssembler_common.hpp"
+
+  int store_inline_type_fields_to_buf(ciInlineKlass* vk, bool from_interpreter = true);
+  bool move_helper(VMReg from, VMReg to, BasicType bt, RegState reg_state[]);
+  bool unpack_inline_helper(const GrowableArray<SigEntry>* sig, int& sig_index,
+                            VMReg from, int& from_index, VMRegPair* to, int to_count, int& to_index,
+                            RegState reg_state[]);
+  bool pack_inline_helper(const GrowableArray<SigEntry>* sig, int& sig_index, int vtarg_index,
+                          VMRegPair* from, int from_count, int& from_index, VMReg to,
+                          RegState reg_state[], Register val_array);
+  int extend_stack_for_inline_args(int args_on_stack);
+  void remove_frame(int initial_framesize, bool needs_stack_repair);
+  VMReg spill_reg_for(VMReg reg);
+  void save_stack_increment(int sp_inc, int frame_size);
+
   void tableswitch(Register index, jint lowbound, jint highbound,
                    Label &jumptable, Label &jumptable_end, int stride = 1) {
     adr(rscratch1, jumptable);
     subsw(rscratch2, index, lowbound);
     subsw(zr, rscratch2, highbound - lowbound);
@@ -1401,10 +1481,12 @@
 
   void string_equals(Register a1, Register a2, Register result, Register cnt1,
                      int elem_size);
 
   void fill_words(Register base, Register cnt, Register value);
+  void fill_words(Register base, uint64_t cnt, Register value);
+
   address zero_words(Register base, uint64_t cnt);
   address zero_words(Register ptr, Register cnt);
   void zero_dcache_blocks(Register base, Register cnt);
 
   static const int zero_words_block_size;
diff a/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp b/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp
@@ -113,11 +113,15 @@
     __ ldr(rscratch1, Address(method, Method::interpreter_entry_offset()));
     __ br(rscratch1);
     __ BIND(run_compiled_code);
   }
 
-  const ByteSize entry_offset = for_compiler_entry ? Method::from_compiled_offset() :
+  // The following jump might pass an inline type argument that was erased to Object as oop to a
+  // callee that expects inline type arguments to be passed as fields. We need to call the compiled
+  // value entry (_code->inline_entry_point() or _adapter->c2i_inline_entry()) which will take care
+  // of translating between the calling conventions.
+  const ByteSize entry_offset = for_compiler_entry ? Method::from_compiled_inline_offset() :
                                                      Method::from_interpreted_offset();
   __ ldr(rscratch1,Address(method, entry_offset));
   __ br(rscratch1);
   __ bind(L_no_such_method);
   __ far_jump(RuntimeAddress(StubRoutines::throw_AbstractMethodError_entry()));
diff a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp
@@ -25,10 +25,11 @@
  */
 
 #include "precompiled.hpp"
 #include "asm/macroAssembler.hpp"
 #include "asm/macroAssembler.inline.hpp"
+#include "classfile/symbolTable.hpp"
 #include "code/codeCache.hpp"
 #include "code/compiledIC.hpp"
 #include "code/debugInfoRec.hpp"
 #include "code/icBuffer.hpp"
 #include "code/vtableStubs.hpp"
@@ -372,10 +373,89 @@
   }
 
   return stk_args;
 }
 
+
+const uint SharedRuntime::java_return_convention_max_int = Argument::n_int_register_parameters_j;
+const uint SharedRuntime::java_return_convention_max_float = Argument::n_float_register_parameters_j;
+
+int SharedRuntime::java_return_convention(const BasicType *sig_bt, VMRegPair *regs, int total_args_passed) {
+
+  // Create the mapping between argument positions and registers.
+
+  static const Register INT_ArgReg[java_return_convention_max_int] = {
+    r0 /* j_rarg7 */, j_rarg6, j_rarg5, j_rarg4, j_rarg3, j_rarg2, j_rarg1, j_rarg0
+  };
+
+  static const FloatRegister FP_ArgReg[java_return_convention_max_float] = {
+    j_farg0, j_farg1, j_farg2, j_farg3, j_farg4, j_farg5, j_farg6, j_farg7
+  };
+
+  uint int_args = 0;
+  uint fp_args = 0;
+
+  for (int i = 0; i < total_args_passed; i++) {
+    switch (sig_bt[i]) {
+    case T_BOOLEAN:
+    case T_CHAR:
+    case T_BYTE:
+    case T_SHORT:
+    case T_INT:
+      if (int_args < SharedRuntime::java_return_convention_max_int) {
+        regs[i].set1(INT_ArgReg[int_args]->as_VMReg());
+        int_args ++;
+      } else {
+        return -1;
+      }
+      break;
+    case T_VOID:
+      // halves of T_LONG or T_DOUBLE
+      assert(i != 0 && (sig_bt[i - 1] == T_LONG || sig_bt[i - 1] == T_DOUBLE), "expecting half");
+      regs[i].set_bad();
+      break;
+    case T_LONG:
+      assert((i + 1) < total_args_passed && sig_bt[i + 1] == T_VOID, "expecting half");
+      // fall through
+    case T_OBJECT:
+    case T_ARRAY:
+    case T_ADDRESS:
+      // Should T_METADATA be added to java_calling_convention as well ?
+    case T_METADATA:
+      if (int_args < SharedRuntime::java_return_convention_max_int) {
+        regs[i].set2(INT_ArgReg[int_args]->as_VMReg());
+        int_args ++;
+      } else {
+        return -1;
+      }
+      break;
+    case T_FLOAT:
+      if (fp_args < SharedRuntime::java_return_convention_max_float) {
+        regs[i].set1(FP_ArgReg[fp_args]->as_VMReg());
+        fp_args ++;
+      } else {
+        return -1;
+      }
+      break;
+    case T_DOUBLE:
+      assert((i + 1) < total_args_passed && sig_bt[i + 1] == T_VOID, "expecting half");
+      if (fp_args < SharedRuntime::java_return_convention_max_float) {
+        regs[i].set2(FP_ArgReg[fp_args]->as_VMReg());
+        fp_args ++;
+      } else {
+        return -1;
+      }
+      break;
+    default:
+      ShouldNotReachHere();
+      break;
+    }
+  }
+
+  return int_args + fp_args;
+}
+
 // Patch the callers callsite with entry to compiled code if it exists.
 static void patch_callers_callsite(MacroAssembler *masm) {
   Label L;
   __ ldr(rscratch1, Address(rmethod, in_bytes(Method::code_offset())));
   __ cbz(rscratch1, L);
@@ -406,148 +486,326 @@
   // restore sp
   __ leave();
   __ bind(L);
 }
 
+// For each inline type argument, sig includes the list of fields of
+// the inline type. This utility function computes the number of
+// arguments for the call if inline types are passed by reference (the
+// calling convention the interpreter expects).
+static int compute_total_args_passed_int(const GrowableArray<SigEntry>* sig_extended) {
+  int total_args_passed = 0;
+  if (InlineTypePassFieldsAsArgs) {
+     for (int i = 0; i < sig_extended->length(); i++) {
+       BasicType bt = sig_extended->at(i)._bt;
+       if (bt == T_METADATA) {
+         // In sig_extended, an inline type argument starts with:
+         // T_METADATA, followed by the types of the fields of the
+         // inline type and T_VOID to mark the end of the value
+         // type. Inline types are flattened so, for instance, in the
+         // case of an inline type with an int field and an inline type
+         // field that itself has 2 fields, an int and a long:
+         // T_METADATA T_INT T_METADATA T_INT T_LONG T_VOID (second
+         // slot for the T_LONG) T_VOID (inner inline type) T_VOID
+         // (outer inline type)
+         total_args_passed++;
+         int vt = 1;
+         do {
+           i++;
+           BasicType bt = sig_extended->at(i)._bt;
+           BasicType prev_bt = sig_extended->at(i-1)._bt;
+           if (bt == T_METADATA) {
+             vt++;
+           } else if (bt == T_VOID &&
+                      prev_bt != T_LONG &&
+                      prev_bt != T_DOUBLE) {
+             vt--;
+           }
+         } while (vt != 0);
+       } else {
+         total_args_passed++;
+       }
+     }
+  } else {
+    total_args_passed = sig_extended->length();
+  }
+
+  return total_args_passed;
+}
+
+
+static void gen_c2i_adapter_helper(MacroAssembler* masm,
+                                   BasicType bt,
+                                   BasicType prev_bt,
+                                   size_t size_in_bytes,
+                                   const VMRegPair& reg_pair,
+                                   const Address& to,
+                                   Register tmp1,
+                                   Register tmp2,
+                                   Register tmp3,
+                                   int extraspace,
+                                   bool is_oop) {
+  if (bt == T_VOID) {
+    assert(prev_bt == T_LONG || prev_bt == T_DOUBLE, "missing half");
+    return;
+  }
+
+  // Say 4 args:
+  // i   st_off
+  // 0   32 T_LONG
+  // 1   24 T_VOID
+  // 2   16 T_OBJECT
+  // 3    8 T_BOOL
+  // -    0 return address
+  //
+  // However to make thing extra confusing. Because we can fit a Java long/double in
+  // a single slot on a 64 bt vm and it would be silly to break them up, the interpreter
+  // leaves one slot empty and only stores to a single slot. In this case the
+  // slot that is occupied is the T_VOID slot. See I said it was confusing.
+
+  bool wide = (size_in_bytes == wordSize);
+  VMReg r_1 = reg_pair.first();
+  VMReg r_2 = reg_pair.second();
+  assert(r_2->is_valid() == wide, "invalid size");
+  if (!r_1->is_valid()) {
+    assert(!r_2->is_valid(), "");
+    return;
+  }
+
+  if (!r_1->is_FloatRegister()) {
+    Register val = r25;
+    if (r_1->is_stack()) {
+      // memory to memory use r25 (scratch registers is used by store_heap_oop)
+      int ld_off = r_1->reg2stack() * VMRegImpl::stack_slot_size + extraspace;
+      __ load_sized_value(val, Address(sp, ld_off), size_in_bytes, /* is_signed */ false);
+    } else {
+      val = r_1->as_Register();
+    }
+    assert_different_registers(to.base(), val, tmp1, tmp2, tmp3);
+    if (is_oop) {
+      __ store_heap_oop(to, val, tmp1, tmp2, tmp3, IN_HEAP | ACCESS_WRITE | IS_DEST_UNINITIALIZED);
+    } else {
+      __ store_sized_value(to, val, size_in_bytes);
+    }
+  } else {
+    if (wide) {
+      __ strd(r_1->as_FloatRegister(), to);
+    } else {
+      // only a float use just part of the slot
+      __ strs(r_1->as_FloatRegister(), to);
+    }
+  }
+}
+
 static void gen_c2i_adapter(MacroAssembler *masm,
-                            int total_args_passed,
-                            int comp_args_on_stack,
-                            const BasicType *sig_bt,
+                            const GrowableArray<SigEntry>* sig_extended,
                             const VMRegPair *regs,
-                            Label& skip_fixup) {
+                            bool requires_clinit_barrier,
+                            address& c2i_no_clinit_check_entry,
+                            Label& skip_fixup,
+                            address start,
+                            OopMapSet* oop_maps,
+                            int& frame_complete,
+                            int& frame_size_in_words,
+                            bool alloc_inline_receiver) {
+  if (requires_clinit_barrier && VM_Version::supports_fast_class_init_checks()) {
+    Label L_skip_barrier;
+
+    { // Bypass the barrier for non-static methods
+      __ ldrw(rscratch1, Address(rmethod, Method::access_flags_offset()));
+      __ andsw(zr, rscratch1, JVM_ACC_STATIC);
+      __ br(Assembler::EQ, L_skip_barrier); // non-static
+    }
+
+    __ load_method_holder(rscratch2, rmethod);
+    __ clinit_barrier(rscratch2, rscratch1, &L_skip_barrier);
+    __ far_jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub()));
+
+    __ bind(L_skip_barrier);
+    c2i_no_clinit_check_entry = __ pc();
+  }
+
+  BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
+  bs->c2i_entry_barrier(masm);
+
   // Before we get into the guts of the C2I adapter, see if we should be here
   // at all.  We've come from compiled code and are attempting to jump to the
   // interpreter, which means the caller made a static call to get here
   // (vcalls always get a compiled target if there is one).  Check for a
   // compiled target.  If there is one, we need to patch the caller's call.
   patch_callers_callsite(masm);
 
   __ bind(skip_fixup);
 
-  int words_pushed = 0;
+  // Name some registers to be used in the following code. We can use
+  // anything except r0-r7 which are arguments in the Java calling
+  // convention, rmethod (r12), and r13 which holds the outgoing sender
+  // SP for the interpreter.
+  Register buf_array = r10;   // Array of buffered inline types
+  Register buf_oop = r11;     // Buffered inline type oop
+  Register tmp1 = r15;
+  Register tmp2 = r16;
+  Register tmp3 = r17;
+
+  if (InlineTypePassFieldsAsArgs) {
+    // Is there an inline type argument?
+    bool has_inline_argument = false;
+    for (int i = 0; i < sig_extended->length() && !has_inline_argument; i++) {
+      has_inline_argument = (sig_extended->at(i)._bt == T_METADATA);
+    }
+    if (has_inline_argument) {
+      // There is at least an inline type argument: we're coming from
+      // compiled code so we have no buffers to back the inline types
+      // Allocate the buffers here with a runtime call.
+      RegisterSaver reg_save(false /* save_vectors */);
+      OopMap* map = reg_save.save_live_registers(masm, 0, &frame_size_in_words);
 
-  // Since all args are passed on the stack, total_args_passed *
-  // Interpreter::stackElementSize is the space we need.
+      frame_complete = __ offset();
+      address the_pc = __ pc();
 
-  int extraspace = total_args_passed * Interpreter::stackElementSize;
+      Label retaddr;
+      __ set_last_Java_frame(sp, noreg, retaddr, rscratch1);
 
-  __ mov(r19_sender_sp, sp);
+      __ mov(c_rarg0, rthread);
+      __ mov(c_rarg1, rmethod);
+      __ mov(c_rarg2, (int64_t)alloc_inline_receiver);
 
-  // stack is aligned, keep it that way
-  extraspace = align_up(extraspace, 2*wordSize);
+      __ lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::allocate_inline_types)));
+      __ blr(rscratch1);
+      __ bind(retaddr);
 
-  if (extraspace)
-    __ sub(sp, sp, extraspace);
+      oop_maps->add_gc_map(__ pc() - start, map);
+      __ reset_last_Java_frame(false);
 
-  // Now write the args into the outgoing interpreter space
-  for (int i = 0; i < total_args_passed; i++) {
-    if (sig_bt[i] == T_VOID) {
-      assert(i > 0 && (sig_bt[i-1] == T_LONG || sig_bt[i-1] == T_DOUBLE), "missing half");
-      continue;
-    }
+      reg_save.restore_live_registers(masm);
 
-    // offset to start parameters
-    int st_off   = (total_args_passed - i - 1) * Interpreter::stackElementSize;
-    int next_off = st_off - Interpreter::stackElementSize;
-
-    // Say 4 args:
-    // i   st_off
-    // 0   32 T_LONG
-    // 1   24 T_VOID
-    // 2   16 T_OBJECT
-    // 3    8 T_BOOL
-    // -    0 return address
-    //
-    // However to make thing extra confusing. Because we can fit a Java long/double in
-    // a single slot on a 64 bt vm and it would be silly to break them up, the interpreter
-    // leaves one slot empty and only stores to a single slot. In this case the
-    // slot that is occupied is the T_VOID slot. See I said it was confusing.
+      Label no_exception;
+      __ ldr(rscratch1, Address(rthread, Thread::pending_exception_offset()));
+      __ cbz(rscratch1, no_exception);
 
-    VMReg r_1 = regs[i].first();
-    VMReg r_2 = regs[i].second();
-    if (!r_1->is_valid()) {
-      assert(!r_2->is_valid(), "");
-      continue;
+      __ str(zr, Address(rthread, JavaThread::vm_result_offset()));
+      __ ldr(r0, Address(rthread, Thread::pending_exception_offset()));
+      __ b(RuntimeAddress(StubRoutines::forward_exception_entry()));
+
+      __ bind(no_exception);
+
+      // We get an array of objects from the runtime call
+      __ get_vm_result(buf_array, rthread);
+      __ get_vm_result_2(rmethod, rthread); // TODO: required to keep the callee Method live?
     }
-    if (r_1->is_stack()) {
-      // memory to memory use rscratch1
-      int ld_off = (r_1->reg2stack() * VMRegImpl::stack_slot_size
-                    + extraspace
-                    + words_pushed * wordSize);
-      if (!r_2->is_valid()) {
-        // sign extend??
-        __ ldrw(rscratch1, Address(sp, ld_off));
-        __ str(rscratch1, Address(sp, st_off));
+  }
 
-      } else {
+  // Since all args are passed on the stack, total_args_passed *
+  // Interpreter::stackElementSize is the space we need.
 
-        __ ldr(rscratch1, Address(sp, ld_off));
+  int total_args_passed = compute_total_args_passed_int(sig_extended);
+  int extraspace = total_args_passed * Interpreter::stackElementSize;
+
+  // stack is aligned, keep it that way
+  extraspace = align_up(extraspace, StackAlignmentInBytes);
 
-        // Two VMREgs|OptoRegs can be T_OBJECT, T_ADDRESS, T_DOUBLE, T_LONG
-        // T_DOUBLE and T_LONG use two slots in the interpreter
-        if ( sig_bt[i] == T_LONG || sig_bt[i] == T_DOUBLE) {
-          // ld_off == LSW, ld_off+wordSize == MSW
-          // st_off == MSW, next_off == LSW
-          __ str(rscratch1, Address(sp, next_off));
+  // set senderSP value
+  __ mov(r19_sender_sp, sp);
+
+  __ sub(sp, sp, extraspace);
+
+  // Now write the args into the outgoing interpreter space
+
+  // next_arg_comp is the next argument from the compiler point of
+  // view (inline type fields are passed in registers/on the stack). In
+  // sig_extended, an inline type argument starts with: T_METADATA,
+  // followed by the types of the fields of the inline type and T_VOID
+  // to mark the end of the inline type. ignored counts the number of
+  // T_METADATA/T_VOID. next_vt_arg is the next inline type argument:
+  // used to get the buffer for that argument from the pool of buffers
+  // we allocated above and want to pass to the
+  // interpreter. next_arg_int is the next argument from the
+  // interpreter point of view (inline types are passed by reference).
+  for (int next_arg_comp = 0, ignored = 0, next_vt_arg = 0, next_arg_int = 0;
+       next_arg_comp < sig_extended->length(); next_arg_comp++) {
+    assert(ignored <= next_arg_comp, "shouldn't skip over more slots than there are arguments");
+    assert(next_arg_int <= total_args_passed, "more arguments for the interpreter than expected?");
+    BasicType bt = sig_extended->at(next_arg_comp)._bt;
+    int st_off = (total_args_passed - next_arg_int - 1) * Interpreter::stackElementSize;
+    if (!InlineTypePassFieldsAsArgs || bt != T_METADATA) {
+      int next_off = st_off - Interpreter::stackElementSize;
+      const int offset = (bt == T_LONG || bt == T_DOUBLE) ? next_off : st_off;
+      const VMRegPair reg_pair = regs[next_arg_comp-ignored];
+      size_t size_in_bytes = reg_pair.second()->is_valid() ? 8 : 4;
+      gen_c2i_adapter_helper(masm, bt, next_arg_comp > 0 ? sig_extended->at(next_arg_comp-1)._bt : T_ILLEGAL,
+                             size_in_bytes, reg_pair, Address(sp, offset), tmp1, tmp2, tmp3, extraspace, false);
+      next_arg_int++;
 #ifdef ASSERT
-          // Overwrite the unused slot with known junk
-          __ mov(rscratch1, (uint64_t)0xdeadffffdeadaaaaull);
-          __ str(rscratch1, Address(sp, st_off));
-#endif /* ASSERT */
-        } else {
-          __ str(rscratch1, Address(sp, st_off));
-        }
+      if (bt == T_LONG || bt == T_DOUBLE) {
+        // Overwrite the unused slot with known junk
+        __ mov(rscratch1, CONST64(0xdeadffffdeadaaaa));
+        __ str(rscratch1, Address(sp, st_off));
       }
-    } else if (r_1->is_Register()) {
-      Register r = r_1->as_Register();
-      if (!r_2->is_valid()) {
-        // must be only an int (or less ) so move only 32bits to slot
-        // why not sign extend??
-        __ str(r, Address(sp, st_off));
-      } else {
-        // Two VMREgs|OptoRegs can be T_OBJECT, T_ADDRESS, T_DOUBLE, T_LONG
-        // T_DOUBLE and T_LONG use two slots in the interpreter
-        if ( sig_bt[i] == T_LONG || sig_bt[i] == T_DOUBLE) {
-          // jlong/double in gpr
-#ifdef ASSERT
-          // Overwrite the unused slot with known junk
-          __ mov(rscratch1, (uint64_t)0xdeadffffdeadaaabull);
-          __ str(rscratch1, Address(sp, st_off));
 #endif /* ASSERT */
-          __ str(r, Address(sp, next_off));
+    } else {
+      ignored++;
+      // get the buffer from the just allocated pool of buffers
+      int index = arrayOopDesc::base_offset_in_bytes(T_OBJECT) + next_vt_arg * type2aelembytes(T_OBJECT);
+      __ load_heap_oop(buf_oop, Address(buf_array, index), tmp1, tmp2);
+      next_vt_arg++; next_arg_int++;
+      int vt = 1;
+      // write fields we get from compiled code in registers/stack
+      // slots to the buffer: we know we are done with that inline type
+      // argument when we hit the T_VOID that acts as an end of inline
+      // type delimiter for this inline type. Inline types are flattened
+      // so we might encounter embedded inline types. Each entry in
+      // sig_extended contains a field offset in the buffer.
+      Label L_null;
+      do {
+        next_arg_comp++;
+        BasicType bt = sig_extended->at(next_arg_comp)._bt;
+        BasicType prev_bt = sig_extended->at(next_arg_comp - 1)._bt;
+        if (bt == T_METADATA) {
+          vt++;
+          ignored++;
+        } else if (bt == T_VOID && prev_bt != T_LONG && prev_bt != T_DOUBLE) {
+          vt--;
+          ignored++;
         } else {
-          __ str(r, Address(sp, st_off));
+          int off = sig_extended->at(next_arg_comp)._offset;
+          if (off == -1) {
+            // Nullable inline type argument, emit null check
+            VMReg reg = regs[next_arg_comp-ignored].first();
+            Label L_notNull;
+            if (reg->is_stack()) {
+              int ld_off = reg->reg2stack() * VMRegImpl::stack_slot_size + extraspace;
+              __ ldrb(tmp1, Address(sp, ld_off));
+              __ cbnz(tmp1, L_notNull);
+            } else {
+              __ cbnz(reg->as_Register(), L_notNull);
+            }
+            __ str(zr, Address(sp, st_off));
+            __ b(L_null);
+            __ bind(L_notNull);
+            continue;
+          }
+          assert(off > 0, "offset in object should be positive");
+          size_t size_in_bytes = is_java_primitive(bt) ? type2aelembytes(bt) : wordSize;
+          bool is_oop = is_reference_type(bt);
+          gen_c2i_adapter_helper(masm, bt, next_arg_comp > 0 ? sig_extended->at(next_arg_comp-1)._bt : T_ILLEGAL,
+                                 size_in_bytes, regs[next_arg_comp-ignored], Address(buf_oop, off), tmp1, tmp2, tmp3, extraspace, is_oop);
         }
-      }
-    } else {
-      assert(r_1->is_FloatRegister(), "");
-      if (!r_2->is_valid()) {
-        // only a float use just part of the slot
-        __ strs(r_1->as_FloatRegister(), Address(sp, st_off));
-      } else {
-#ifdef ASSERT
-        // Overwrite the unused slot with known junk
-        __ mov(rscratch1, (uint64_t)0xdeadffffdeadaaacull);
-        __ str(rscratch1, Address(sp, st_off));
-#endif /* ASSERT */
-        __ strd(r_1->as_FloatRegister(), Address(sp, next_off));
-      }
+      } while (vt != 0);
+      // pass the buffer to the interpreter
+      __ str(buf_oop, Address(sp, st_off));
+      __ bind(L_null);
     }
   }
 
   __ mov(esp, sp); // Interp expects args on caller's expression stack
 
   __ ldr(rscratch1, Address(rmethod, in_bytes(Method::interpreter_entry_offset())));
   __ br(rscratch1);
 }
 
+void SharedRuntime::gen_i2c_adapter(MacroAssembler *masm, int comp_args_on_stack, const GrowableArray<SigEntry>* sig, const VMRegPair *regs) {
 
-void SharedRuntime::gen_i2c_adapter(MacroAssembler *masm,
-                                    int total_args_passed,
-                                    int comp_args_on_stack,
-                                    const BasicType *sig_bt,
-                                    const VMRegPair *regs) {
 
   // Note: r19_sender_sp contains the senderSP on entry. We must
   // preserve it since we may do a i2c -> c2i transition if we lose a
   // race where compiled code goes non-entrant while we get args
   // ready.
@@ -606,19 +864,20 @@
     __ block_comment("} verify_i2ce ");
 #endif
   }
 
   // Cut-out for having no stack args.
-  int comp_words_on_stack = align_up(comp_args_on_stack*VMRegImpl::stack_slot_size, wordSize)>>LogBytesPerWord;
+  int comp_words_on_stack = 0;
   if (comp_args_on_stack) {
-    __ sub(rscratch1, sp, comp_words_on_stack * wordSize);
-    __ andr(sp, rscratch1, -16);
+     comp_words_on_stack = align_up(comp_args_on_stack * VMRegImpl::stack_slot_size, wordSize) >> LogBytesPerWord;
+     __ sub(rscratch1, sp, comp_words_on_stack * wordSize);
+     __ andr(sp, rscratch1, -16);
   }
 
   // Will jump to the compiled code just as if compiled code was doing it.
   // Pre-load the register-jump target early, to schedule it better.
-  __ ldr(rscratch1, Address(rmethod, in_bytes(Method::from_compiled_offset())));
+  __ ldr(rscratch1, Address(rmethod, in_bytes(Method::from_compiled_inline_offset())));
 
 #if INCLUDE_JVMCI
   if (EnableJVMCI) {
     // check if this call should be routed towards a specific entry point
     __ ldr(rscratch2, Address(rthread, in_bytes(JavaThread::jvmci_alternate_call_target_offset())));
@@ -628,23 +887,25 @@
     __ str(zr, Address(rthread, in_bytes(JavaThread::jvmci_alternate_call_target_offset())));
     __ bind(no_alternative_target);
   }
 #endif // INCLUDE_JVMCI
 
+  int total_args_passed = sig->length();
+
   // Now generate the shuffle code.
   for (int i = 0; i < total_args_passed; i++) {
-    if (sig_bt[i] == T_VOID) {
-      assert(i > 0 && (sig_bt[i-1] == T_LONG || sig_bt[i-1] == T_DOUBLE), "missing half");
+    BasicType bt = sig->at(i)._bt;
+    if (bt == T_VOID) {
+      assert(i > 0 && (sig->at(i - 1)._bt == T_LONG || sig->at(i - 1)._bt == T_DOUBLE), "missing half");
       continue;
     }
 
     // Pick up 0, 1 or 2 words from SP+offset.
+    assert(!regs[i].second()->is_valid() || regs[i].first()->next() == regs[i].second(), "scrambled load targets?");
 
-    assert(!regs[i].second()->is_valid() || regs[i].first()->next() == regs[i].second(),
-            "scrambled load targets?");
     // Load in argument order going down.
-    int ld_off = (total_args_passed - i - 1)*Interpreter::stackElementSize;
+    int ld_off = (total_args_passed - i - 1) * Interpreter::stackElementSize;
     // Point to interpreter value (vs. tag)
     int next_off = ld_off - Interpreter::stackElementSize;
     //
     //
     //
@@ -654,11 +915,11 @@
       assert(!r_2->is_valid(), "");
       continue;
     }
     if (r_1->is_stack()) {
       // Convert stack slot to an SP offset (+ wordSize to account for return address )
-      int st_off = regs[i].first()->reg2stack()*VMRegImpl::stack_slot_size;
+      int st_off = regs[i].first()->reg2stack() * VMRegImpl::stack_slot_size;
       if (!r_2->is_valid()) {
         // sign extend???
         __ ldrsw(rscratch2, Address(esp, ld_off));
         __ str(rscratch2, Address(sp, st_off));
       } else {
@@ -671,43 +932,42 @@
         //
         // Interpreter local[n] == MSW, local[n+1] == LSW however locals
         // are accessed as negative so LSW is at LOW address
 
         // ld_off is MSW so get LSW
-        const int offset = (sig_bt[i]==T_LONG||sig_bt[i]==T_DOUBLE)?
-                           next_off : ld_off;
+        const int offset = (bt == T_LONG || bt == T_DOUBLE) ? next_off : ld_off;
         __ ldr(rscratch2, Address(esp, offset));
         // st_off is LSW (i.e. reg.first())
-        __ str(rscratch2, Address(sp, st_off));
-      }
-    } else if (r_1->is_Register()) {  // Register argument
-      Register r = r_1->as_Register();
-      if (r_2->is_valid()) {
-        //
-        // We are using two VMRegs. This can be either T_OBJECT,
-        // T_ADDRESS, T_LONG, or T_DOUBLE the interpreter allocates
-        // two slots but only uses one for thr T_LONG or T_DOUBLE case
-        // So we must adjust where to pick up the data to match the
-        // interpreter.
+         __ str(rscratch2, Address(sp, st_off));
+       }
+     } else if (r_1->is_Register()) {  // Register argument
+       Register r = r_1->as_Register();
+       if (r_2->is_valid()) {
+         //
+         // We are using two VMRegs. This can be either T_OBJECT,
+         // T_ADDRESS, T_LONG, or T_DOUBLE the interpreter allocates
+         // two slots but only uses one for thr T_LONG or T_DOUBLE case
+         // So we must adjust where to pick up the data to match the
+         // interpreter.
+
+        const int offset = (bt == T_LONG || bt == T_DOUBLE) ? next_off : ld_off;
+
+         // this can be a misaligned move
+         __ ldr(r, Address(esp, offset));
+       } else {
+         // sign extend and use a full word?
+         __ ldrw(r, Address(esp, ld_off));
+       }
+     } else {
+       if (!r_2->is_valid()) {
+         __ ldrs(r_1->as_FloatRegister(), Address(esp, ld_off));
+       } else {
+         __ ldrd(r_1->as_FloatRegister(), Address(esp, next_off));
+       }
+     }
+   }
 
-        const int offset = (sig_bt[i]==T_LONG||sig_bt[i]==T_DOUBLE)?
-                           next_off : ld_off;
-
-        // this can be a misaligned move
-        __ ldr(r, Address(esp, offset));
-      } else {
-        // sign extend and use a full word?
-        __ ldrw(r, Address(esp, ld_off));
-      }
-    } else {
-      if (!r_2->is_valid()) {
-        __ ldrs(r_1->as_FloatRegister(), Address(esp, ld_off));
-      } else {
-        __ ldrd(r_1->as_FloatRegister(), Address(esp, next_off));
-      }
-    }
-  }
 
   __ mov(rscratch2, rscratch1);
   __ push_cont_fastpath(rthread); // Set JavaThread::_cont_fastpath to the sp of the oldest interpreted frame we know about; kills rscratch1
   __ mov(rscratch1, rscratch2);
 
@@ -720,27 +980,14 @@
   // we try and find the callee by normal means a safepoint
   // is possible. So we stash the desired callee in the thread
   // and the vm will find there should this case occur.
 
   __ str(rmethod, Address(rthread, JavaThread::callee_target_offset()));
-
   __ br(rscratch1);
 }
 
-// ---------------------------------------------------------------
-AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm,
-                                                            int total_args_passed,
-                                                            int comp_args_on_stack,
-                                                            const BasicType *sig_bt,
-                                                            const VMRegPair *regs,
-                                                            AdapterFingerPrint* fingerprint) {
-  address i2c_entry = __ pc();
-
-  gen_i2c_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs);
-
-  address c2i_unverified_entry = __ pc();
-  Label skip_fixup;
+static void gen_inline_cache_check(MacroAssembler *masm, Label& skip_fixup) {
 
   Label ok;
 
   Register holder = rscratch2;
   Register receiver = j_rarg0;
@@ -771,38 +1018,75 @@
     __ ldr(rscratch1, Address(rmethod, in_bytes(Method::code_offset())));
     __ cbz(rscratch1, skip_fixup);
     __ far_jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub()));
     __ block_comment("} c2i_unverified_entry");
   }
+}
 
-  address c2i_entry = __ pc();
 
-  // Class initialization barrier for static methods
-  address c2i_no_clinit_check_entry = nullptr;
-  if (VM_Version::supports_fast_class_init_checks()) {
-    Label L_skip_barrier;
+// ---------------------------------------------------------------
+AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler* masm,
+                                                            int comp_args_on_stack,
+                                                            const GrowableArray<SigEntry>* sig,
+                                                            const VMRegPair* regs,
+                                                            const GrowableArray<SigEntry>* sig_cc,
+                                                            const VMRegPair* regs_cc,
+                                                            const GrowableArray<SigEntry>* sig_cc_ro,
+                                                            const VMRegPair* regs_cc_ro,
+                                                            AdapterFingerPrint* fingerprint,
+                                                            AdapterBlob*& new_adapter,
+                                                            bool allocate_code_blob) {
 
-    { // Bypass the barrier for non-static methods
-      __ ldrw(rscratch1, Address(rmethod, Method::access_flags_offset()));
-      __ andsw(zr, rscratch1, JVM_ACC_STATIC);
-      __ br(Assembler::EQ, L_skip_barrier); // non-static
-    }
+  address i2c_entry = __ pc();
+  gen_i2c_adapter(masm, comp_args_on_stack, sig, regs);
 
-    __ load_method_holder(rscratch2, rmethod);
-    __ clinit_barrier(rscratch2, rscratch1, &L_skip_barrier);
-    __ far_jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub()));
+  address c2i_unverified_entry        = __ pc();
+  address c2i_unverified_inline_entry = __ pc();
+  Label skip_fixup;
 
-    __ bind(L_skip_barrier);
-    c2i_no_clinit_check_entry = __ pc();
+  gen_inline_cache_check(masm, skip_fixup);
+
+  OopMapSet* oop_maps = new OopMapSet();
+  int frame_complete = CodeOffsets::frame_never_safe;
+  int frame_size_in_words = 0;
+
+  // Scalarized c2i adapter with non-scalarized receiver (i.e., don't pack receiver)
+  address c2i_no_clinit_check_entry = nullptr;
+  address c2i_inline_ro_entry = __ pc();
+  if (regs_cc != regs_cc_ro) {
+    // No class init barrier needed because method is guaranteed to be non-static
+    gen_c2i_adapter(masm, sig_cc_ro, regs_cc_ro, /* requires_clinit_barrier = */ false, c2i_no_clinit_check_entry,
+                    skip_fixup, i2c_entry, oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ false);
+    skip_fixup.reset();
   }
 
-  BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
-  bs->c2i_entry_barrier(masm);
+  // Scalarized c2i adapter
+  address c2i_entry        = __ pc();
+  address c2i_inline_entry = __ pc();
+  gen_c2i_adapter(masm, sig_cc, regs_cc, /* requires_clinit_barrier = */ true, c2i_no_clinit_check_entry,
+                  skip_fixup, i2c_entry, oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ true);
+
+  // Non-scalarized c2i adapter
+  if (regs != regs_cc) {
+    c2i_unverified_inline_entry = __ pc();
+    Label inline_entry_skip_fixup;
+    gen_inline_cache_check(masm, inline_entry_skip_fixup);
+
+    c2i_inline_entry = __ pc();
+    gen_c2i_adapter(masm, sig, regs, /* requires_clinit_barrier = */ true, c2i_no_clinit_check_entry,
+                    inline_entry_skip_fixup, i2c_entry, oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ false);
+  }
 
-  gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup);
 
-  return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
+  // The c2i adapter might safepoint and trigger a GC. The caller must make sure that
+  // the GC knows about the location of oop argument locations passed to the c2i adapter.
+  if (allocate_code_blob) {
+    bool caller_must_gc_arguments = (regs != regs_cc);
+    new_adapter = AdapterBlob::create(masm->code(), frame_complete, frame_size_in_words, oop_maps, caller_must_gc_arguments);
+  }
+
+  return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_inline_entry, c2i_inline_ro_entry, c2i_unverified_entry, c2i_unverified_inline_entry, c2i_no_clinit_check_entry);
 }
 
 static int c_calling_convention_priv(const BasicType *sig_bt,
                                          VMRegPair *regs,
                                          int total_args_passed) {
@@ -1785,10 +2069,14 @@
       __ b(slow_path_lock);
     } else if (LockingMode == LM_LEGACY) {
       // Load (object->mark() | 1) into swap_reg %r0
       __ ldr(rscratch1, Address(obj_reg, oopDesc::mark_offset_in_bytes()));
       __ orr(swap_reg, rscratch1, 1);
+      if (EnableValhalla) {
+        // Mask inline_type bit such that we go to the slow path if object is an inline type
+        __ andr(swap_reg, swap_reg, ~((int) markWord::inline_type_bit_in_place));
+      }
 
       // Save (object->mark() | 1) into BasicLock's displaced header
       __ str(swap_reg, Address(lock_reg, mark_word_offset));
 
       // src -> dest iff dest == r0 else r0 <- dest
@@ -3107,5 +3395,118 @@
   // Set exception blob
   _exception_blob =  ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1);
 }
 
 #endif // COMPILER2
+
+BufferedInlineTypeBlob* SharedRuntime::generate_buffered_inline_type_adapter(const InlineKlass* vk) {
+  BufferBlob* buf = BufferBlob::create("inline types pack/unpack", 16 * K);
+  CodeBuffer buffer(buf);
+  short buffer_locs[20];
+  buffer.insts()->initialize_shared_locs((relocInfo*)buffer_locs,
+                                         sizeof(buffer_locs)/sizeof(relocInfo));
+
+  MacroAssembler _masm(&buffer);
+  MacroAssembler* masm = &_masm;
+
+  const Array<SigEntry>* sig_vk = vk->extended_sig();
+  const Array<VMRegPair>* regs = vk->return_regs();
+
+  int pack_fields_jobject_off = __ offset();
+  // Resolve pre-allocated buffer from JNI handle.
+  // We cannot do this in generate_call_stub() because it requires GC code to be initialized.
+  Register Rresult = r14;  // See StubGenerator::generate_call_stub().
+  __ ldr(r0, Address(Rresult));
+  __ resolve_jobject(r0 /* value */,
+                     rthread /* thread */,
+                     r12 /* tmp */);
+  __ str(r0, Address(Rresult));
+
+  int pack_fields_off = __ offset();
+
+  int j = 1;
+  for (int i = 0; i < sig_vk->length(); i++) {
+    BasicType bt = sig_vk->at(i)._bt;
+    if (bt == T_METADATA) {
+      continue;
+    }
+    if (bt == T_VOID) {
+      if (sig_vk->at(i-1)._bt == T_LONG ||
+          sig_vk->at(i-1)._bt == T_DOUBLE) {
+        j++;
+      }
+      continue;
+    }
+    int off = sig_vk->at(i)._offset;
+    VMRegPair pair = regs->at(j);
+    VMReg r_1 = pair.first();
+    VMReg r_2 = pair.second();
+    Address to(r0, off);
+    if (bt == T_FLOAT) {
+      __ strs(r_1->as_FloatRegister(), to);
+    } else if (bt == T_DOUBLE) {
+      __ strd(r_1->as_FloatRegister(), to);
+    } else {
+      Register val = r_1->as_Register();
+      assert_different_registers(to.base(), val, r15, r16, r17);
+      if (is_reference_type(bt)) {
+        __ store_heap_oop(to, val, r15, r16, r17, IN_HEAP | ACCESS_WRITE | IS_DEST_UNINITIALIZED);
+      } else {
+        __ store_sized_value(to, r_1->as_Register(), type2aelembytes(bt));
+      }
+    }
+    j++;
+  }
+  assert(j == regs->length(), "missed a field?");
+
+  __ ret(lr);
+
+  int unpack_fields_off = __ offset();
+
+  Label skip;
+  __ cbz(r0, skip);
+
+  j = 1;
+  for (int i = 0; i < sig_vk->length(); i++) {
+    BasicType bt = sig_vk->at(i)._bt;
+    if (bt == T_METADATA) {
+      continue;
+    }
+    if (bt == T_VOID) {
+      if (sig_vk->at(i-1)._bt == T_LONG ||
+          sig_vk->at(i-1)._bt == T_DOUBLE) {
+        j++;
+      }
+      continue;
+    }
+    int off = sig_vk->at(i)._offset;
+    assert(off > 0, "offset in object should be positive");
+    VMRegPair pair = regs->at(j);
+    VMReg r_1 = pair.first();
+    VMReg r_2 = pair.second();
+    Address from(r0, off);
+    if (bt == T_FLOAT) {
+      __ ldrs(r_1->as_FloatRegister(), from);
+    } else if (bt == T_DOUBLE) {
+      __ ldrd(r_1->as_FloatRegister(), from);
+    } else if (bt == T_OBJECT || bt == T_ARRAY) {
+      assert_different_registers(r0, r_1->as_Register());
+      __ load_heap_oop(r_1->as_Register(), from, rscratch1, rscratch2);
+    } else {
+      assert(is_java_primitive(bt), "unexpected basic type");
+      assert_different_registers(r0, r_1->as_Register());
+
+      size_t size_in_bytes = type2aelembytes(bt);
+      __ load_sized_value(r_1->as_Register(), from, size_in_bytes, bt != T_CHAR && bt != T_BOOLEAN);
+    }
+    j++;
+  }
+  assert(j == regs->length(), "missed a field?");
+
+  __ bind(skip);
+
+  __ ret(lr);
+
+  __ flush();
+
+  return BufferedInlineTypeBlob::create(&buffer, pack_fields_off, pack_fields_jobject_off, unpack_fields_off);
+}
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
@@ -326,24 +326,29 @@
 
     // store result depending on type (everything that is not
     // T_OBJECT, T_LONG, T_FLOAT or T_DOUBLE is treated as T_INT)
     // n.b. this assumes Java returns an integral result in r0
     // and a floating result in j_farg0
-    __ ldr(j_rarg2, result);
-    Label is_long, is_float, is_double, exit;
-    __ ldr(j_rarg1, result_type);
-    __ cmp(j_rarg1, (u1)T_OBJECT);
+    // All of j_rargN may be used to return inline type fields so be careful
+    // not to clobber those.
+    // SharedRuntime::generate_buffered_inline_type_adapter() knows the register
+    // assignment of Rresult below.
+    Register Rresult = r14, Rresult_type = r15;
+    __ ldr(Rresult, result);
+    Label is_long, is_float, is_double, check_prim, exit;
+    __ ldr(Rresult_type, result_type);
+    __ cmp(Rresult_type, (u1)T_OBJECT);
+    __ br(Assembler::EQ, check_prim);
+    __ cmp(Rresult_type, (u1)T_LONG);
     __ br(Assembler::EQ, is_long);
-    __ cmp(j_rarg1, (u1)T_LONG);
-    __ br(Assembler::EQ, is_long);
-    __ cmp(j_rarg1, (u1)T_FLOAT);
+    __ cmp(Rresult_type, (u1)T_FLOAT);
     __ br(Assembler::EQ, is_float);
-    __ cmp(j_rarg1, (u1)T_DOUBLE);
+    __ cmp(Rresult_type, (u1)T_DOUBLE);
     __ br(Assembler::EQ, is_double);
 
     // handle T_INT case
-    __ strw(r0, Address(j_rarg2));
+    __ strw(r0, Address(Rresult));
 
     __ BIND(exit);
 
     // pop parameters
     __ sub(esp, rfp, -sp_after_call_off * wordSize);
@@ -391,21 +396,32 @@
     // leave frame and return to caller
     __ leave();
     __ ret(lr);
 
     // handle return types different from T_INT
+    __ BIND(check_prim);
+    if (InlineTypeReturnedAsFields) {
+      // Check for scalarized return value
+      __ tbz(r0, 0, is_long);
+      // Load pack handler address
+      __ andr(rscratch1, r0, -2);
+      __ ldr(rscratch1, Address(rscratch1, InstanceKlass::adr_inlineklass_fixed_block_offset()));
+      __ ldr(rscratch1, Address(rscratch1, InlineKlass::pack_handler_jobject_offset()));
+      __ blr(rscratch1);
+      __ b(exit);
+    }
 
     __ BIND(is_long);
-    __ str(r0, Address(j_rarg2, 0));
+    __ str(r0, Address(Rresult, 0));
     __ br(Assembler::AL, exit);
 
     __ BIND(is_float);
-    __ strs(j_farg0, Address(j_rarg2, 0));
+    __ strs(j_farg0, Address(Rresult, 0));
     __ br(Assembler::AL, exit);
 
     __ BIND(is_double);
-    __ strd(j_farg0, Address(j_rarg2, 0));
+    __ strd(j_farg0, Address(Rresult, 0));
     __ br(Assembler::AL, exit);
 
     return start;
   }
 
@@ -2214,10 +2230,18 @@
     //  if (src->klass() != dst->klass()) return -1;
     __ load_klass(rscratch2, dst);
     __ eor(rscratch2, rscratch2, scratch_src_klass);
     __ cbnz(rscratch2, L_failed);
 
+    // Check for flat inline type array -> return -1
+    __ tst(lh, Klass::_lh_array_tag_flat_value_bit_inplace);
+    __ br(Assembler::NE, L_failed);
+
+    // Check for null-free (non-flat) inline type array -> handle as object array
+    __ tst(lh, Klass::_lh_null_free_array_bit_inplace);
+    __ br(Assembler::NE, L_failed);
+
     //  if (!src->is_Array()) return -1;
     __ tbz(lh, 31, L_failed);  // i.e. (lh >= 0)
 
     // At this point, it is known to be a typeArray (array_tag 0x3).
 #ifdef ASSERT
@@ -8288,10 +8312,138 @@
     //     t0 = sub(Pm_base, Pn_base, t0, len);
     // }
   };
 
 
+  // Call here from the interpreter or compiled code to either load
+  // multiple returned values from the inline type instance being
+  // returned to registers or to store returned values to a newly
+  // allocated inline type instance.
+  address generate_return_value_stub(address destination, const char* name, bool has_res) {
+    // We need to save all registers the calling convention may use so
+    // the runtime calls read or update those registers. This needs to
+    // be in sync with SharedRuntime::java_return_convention().
+    // n.b. aarch64 asserts that frame::arg_reg_save_area_bytes == 0
+    enum layout {
+      j_rarg7_off = 0, j_rarg7_2,    // j_rarg7 is r0
+      j_rarg6_off, j_rarg6_2,
+      j_rarg5_off, j_rarg5_2,
+      j_rarg4_off, j_rarg4_2,
+      j_rarg3_off, j_rarg3_2,
+      j_rarg2_off, j_rarg2_2,
+      j_rarg1_off, j_rarg1_2,
+      j_rarg0_off, j_rarg0_2,
+
+      j_farg7_off, j_farg7_2,
+      j_farg6_off, j_farg6_2,
+      j_farg5_off, j_farg5_2,
+      j_farg4_off, j_farg4_2,
+      j_farg3_off, j_farg3_2,
+      j_farg2_off, j_farg2_2,
+      j_farg1_off, j_farg1_2,
+      j_farg0_off, j_farg0_2,
+
+      rfp_off, rfp_off2,
+      return_off, return_off2,
+
+      framesize // inclusive of return address
+    };
+
+    CodeBuffer code(name, 512, 64);
+    MacroAssembler* masm = new MacroAssembler(&code);
+
+    int frame_size_in_bytes = align_up(framesize*BytesPerInt, 16);
+    assert(frame_size_in_bytes == framesize*BytesPerInt, "misaligned");
+    int frame_size_in_slots = frame_size_in_bytes / BytesPerInt;
+    int frame_size_in_words = frame_size_in_bytes / wordSize;
+
+    OopMapSet* oop_maps = new OopMapSet();
+    OopMap* map = new OopMap(frame_size_in_slots, 0);
+
+    map->set_callee_saved(VMRegImpl::stack2reg(j_rarg7_off), j_rarg7->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_rarg6_off), j_rarg6->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_rarg5_off), j_rarg5->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_rarg4_off), j_rarg4->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_rarg3_off), j_rarg3->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_rarg2_off), j_rarg2->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_rarg1_off), j_rarg1->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_rarg0_off), j_rarg0->as_VMReg());
+
+    map->set_callee_saved(VMRegImpl::stack2reg(j_farg0_off), j_farg0->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_farg1_off), j_farg1->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_farg2_off), j_farg2->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_farg3_off), j_farg3->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_farg4_off), j_farg4->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_farg5_off), j_farg5->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_farg6_off), j_farg6->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_farg7_off), j_farg7->as_VMReg());
+
+    address start = __ pc();
+
+    __ enter(); // Save FP and LR before call
+
+    __ stpd(j_farg1, j_farg0, Address(__ pre(sp, -2 * wordSize)));
+    __ stpd(j_farg3, j_farg2, Address(__ pre(sp, -2 * wordSize)));
+    __ stpd(j_farg5, j_farg4, Address(__ pre(sp, -2 * wordSize)));
+    __ stpd(j_farg7, j_farg6, Address(__ pre(sp, -2 * wordSize)));
+
+    __ stp(j_rarg1, j_rarg0, Address(__ pre(sp, -2 * wordSize)));
+    __ stp(j_rarg3, j_rarg2, Address(__ pre(sp, -2 * wordSize)));
+    __ stp(j_rarg5, j_rarg4, Address(__ pre(sp, -2 * wordSize)));
+    __ stp(j_rarg7, j_rarg6, Address(__ pre(sp, -2 * wordSize)));
+
+    int frame_complete = __ offset();
+
+    // Set up last_Java_sp and last_Java_fp
+    address the_pc = __ pc();
+    __ set_last_Java_frame(sp, noreg, the_pc, rscratch1);
+
+    // Call runtime
+    __ mov(c_rarg1, r0);
+    __ mov(c_rarg0, rthread);
+
+    __ mov(rscratch1, destination);
+    __ blr(rscratch1);
+
+    oop_maps->add_gc_map(the_pc - start, map);
+
+    __ reset_last_Java_frame(false);
+
+    __ ldp(j_rarg7, j_rarg6, Address(__ post(sp, 2 * wordSize)));
+    __ ldp(j_rarg5, j_rarg4, Address(__ post(sp, 2 * wordSize)));
+    __ ldp(j_rarg3, j_rarg2, Address(__ post(sp, 2 * wordSize)));
+    __ ldp(j_rarg1, j_rarg0, Address(__ post(sp, 2 * wordSize)));
+
+    __ ldpd(j_farg7, j_farg6, Address(__ post(sp, 2 * wordSize)));
+    __ ldpd(j_farg5, j_farg4, Address(__ post(sp, 2 * wordSize)));
+    __ ldpd(j_farg3, j_farg2, Address(__ post(sp, 2 * wordSize)));
+    __ ldpd(j_farg1, j_farg0, Address(__ post(sp, 2 * wordSize)));
+
+    __ leave();
+
+    // check for pending exceptions
+    Label pending;
+    __ ldr(rscratch1, Address(rthread, in_bytes(Thread::pending_exception_offset())));
+    __ cbnz(rscratch1, pending);
+
+    if (has_res) {
+      __ get_vm_result(r0, rthread);
+    }
+
+    __ ret(lr);
+
+    __ bind(pending);
+    __ far_jump(RuntimeAddress(StubRoutines::forward_exception_entry()));
+
+    // -------------
+    // make sure all code is generated
+    masm->flush();
+
+    RuntimeStub* stub = RuntimeStub::new_runtime_stub(name, &code, frame_complete, frame_size_in_words, oop_maps, false);
+    return stub->entry_point();
+  }
+
   // Initialization
   void generate_initial_stubs() {
     // Generate initial stubs and initializes the entry points
 
     // entry points that exist in all platforms Note: This is code
@@ -8343,10 +8495,17 @@
     }
 
     if (vmIntrinsics::is_intrinsic_available(vmIntrinsics::_dcos)) {
       StubRoutines::_dcos = generate_dsin_dcos(/* isCos = */ true);
     }
+
+    if (InlineTypeReturnedAsFields) {
+      StubRoutines::_load_inline_type_fields_in_regs =
+         generate_return_value_stub(CAST_FROM_FN_PTR(address, SharedRuntime::load_inline_type_fields_in_regs), "load_inline_type_fields_in_regs", false);
+      StubRoutines::_store_inline_type_fields_to_buf =
+         generate_return_value_stub(CAST_FROM_FN_PTR(address, SharedRuntime::store_inline_type_fields_to_buf), "store_inline_type_fields_to_buf", true);
+    }
   }
 
   void generate_continuation_stubs() {
     // Continuation stubs:
     StubRoutines::_cont_thaw          = generate_cont_thaw();
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
@@ -39,10 +39,11 @@
 #include "oops/arrayOop.hpp"
 #include "oops/method.hpp"
 #include "oops/methodCounters.hpp"
 #include "oops/methodData.hpp"
 #include "oops/oop.inline.hpp"
+#include "oops/inlineKlass.hpp"
 #include "oops/resolvedIndyEntry.hpp"
 #include "oops/resolvedMethodEntry.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "prims/jvmtiThreadState.hpp"
 #include "runtime/arguments.hpp"
@@ -471,10 +472,15 @@
   // Restore stack bottom in case i2c adjusted stack
   __ ldr(rscratch1, Address(rfp, frame::interpreter_frame_last_sp_offset * wordSize));
   __ lea(esp, Address(rfp, rscratch1, Address::lsl(Interpreter::logStackElementSize)));
   // and null it as marker that esp is now tos until next java call
   __ str(zr, Address(rfp, frame::interpreter_frame_last_sp_offset * wordSize));
+
+  if (state == atos && InlineTypeReturnedAsFields) {
+    __ store_inline_type_fields_to_buf(nullptr, true);
+  }
+
   __ restore_bcp();
   __ restore_locals();
   __ restore_constant_pool_cache();
   __ get_method(rmethod);
 
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
@@ -170,10 +170,11 @@
 {
   if (!RewriteBytecodes)  return;
   Label L_patch_done;
 
   switch (bc) {
+  case Bytecodes::_fast_vputfield:
   case Bytecodes::_fast_aputfield:
   case Bytecodes::_fast_bputfield:
   case Bytecodes::_fast_zputfield:
   case Bytecodes::_fast_cputfield:
   case Bytecodes::_fast_dputfield:
@@ -751,14 +752,14 @@
     assert(r1 != array, "different registers");
     __ mov(r1, index);
   }
   Label ok;
   __ br(Assembler::LO, ok);
-    // ??? convention: move array into r3 for exception message
-  __ mov(r3, array);
-  __ mov(rscratch1, Interpreter::_throw_ArrayIndexOutOfBoundsException_entry);
-  __ br(rscratch1);
+  // ??? convention: move array into r3 for exception message
+   __ mov(r3, array);
+   __ mov(rscratch1, Interpreter::_throw_ArrayIndexOutOfBoundsException_entry);
+   __ br(rscratch1);
   __ bind(ok);
 }
 
 void TemplateTable::iaload()
 {
@@ -814,15 +815,30 @@
   __ mov(r1, r0);
   __ pop_ptr(r0);
   // r0: array
   // r1: index
   index_check(r0, r1); // leaves index in r1, kills rscratch1
-  __ add(r1, r1, arrayOopDesc::base_offset_in_bytes(T_OBJECT) >> LogBytesPerHeapOop);
-  do_oop_load(_masm,
-              Address(r0, r1, Address::uxtw(LogBytesPerHeapOop)),
-              r0,
-              IS_ARRAY);
+  __ profile_array_type<ArrayLoadData>(r2, r0, r4);
+  if (UseFlatArray) {
+    Label is_flat_array, done;
+
+    __ test_flat_array_oop(r0, r8 /*temp*/, is_flat_array);
+    __ add(r1, r1, arrayOopDesc::base_offset_in_bytes(T_OBJECT) >> LogBytesPerHeapOop);
+    do_oop_load(_masm, Address(r0, r1, Address::uxtw(LogBytesPerHeapOop)), r0, IS_ARRAY);
+
+    __ b(done);
+    __ bind(is_flat_array);
+    __ call_VM(r0, CAST_FROM_FN_PTR(address, InterpreterRuntime::value_array_load), r0, r1);
+    // Ensure the stores to copy the inline field contents are visible
+    // before any subsequent store that publishes this reference.
+    __ membar(Assembler::StoreStore);
+    __ bind(done);
+  } else {
+    __ add(r1, r1, arrayOopDesc::base_offset_in_bytes(T_OBJECT) >> LogBytesPerHeapOop);
+    do_oop_load(_masm, Address(r0, r1, Address::uxtw(LogBytesPerHeapOop)), r0, IS_ARRAY);
+  }
+  __ profile_element_type(r2, r0, r4);
 }
 
 void TemplateTable::baload()
 {
   transition(itos, itos);
@@ -1105,36 +1121,49 @@
   __ add(r1, r1, arrayOopDesc::base_offset_in_bytes(T_DOUBLE) >> 3);
   __ access_store_at(T_DOUBLE, IN_HEAP | IS_ARRAY, Address(r3, r1, Address::uxtw(3)), noreg /* dtos */, noreg, noreg, noreg);
 }
 
 void TemplateTable::aastore() {
-  Label is_null, ok_is_subtype, done;
+  Label is_null, is_flat_array, ok_is_subtype, done;
   transition(vtos, vtos);
   // stack: ..., array, index, value
   __ ldr(r0, at_tos());    // value
   __ ldr(r2, at_tos_p1()); // index
   __ ldr(r3, at_tos_p2()); // array
 
-  Address element_address(r3, r4, Address::uxtw(LogBytesPerHeapOop));
-
   index_check(r3, r2);     // kills r1
+
+  __ profile_array_type<ArrayStoreData>(r4, r3, r5);
+  __ profile_multiple_element_types(r4, r0, r5, r6);
+
   __ add(r4, r2, arrayOopDesc::base_offset_in_bytes(T_OBJECT) >> LogBytesPerHeapOop);
+  Address element_address(r3, r4, Address::uxtw(LogBytesPerHeapOop));
+  // Be careful not to clobber r4 below
 
   // do array store check - check for null value first
   __ cbz(r0, is_null);
 
+  // Move array class to r5
+  __ load_klass(r5, r3);
+
+  if (UseFlatArray) {
+    __ ldrw(r6, Address(r5, Klass::layout_helper_offset()));
+    __ test_flat_array_layout(r6, is_flat_array);
+  }
+
   // Move subklass into r1
   __ load_klass(r1, r0);
-  // Move superklass into r0
-  __ load_klass(r0, r3);
-  __ ldr(r0, Address(r0,
-                     ObjArrayKlass::element_klass_offset()));
+
+  // Move array element superklass into r0
+  __ ldr(r0, Address(r5, ObjArrayKlass::element_klass_offset()));
   // Compress array + index*oopSize + 12 into a single register.  Frees r2.
 
   // Generate subtype check.  Blows r2, r5
   // Superklass in r0.  Subklass in r1.
-  __ gen_subtype_check(r1, ok_is_subtype);
+
+  // is "r1 <: r0" ? (value subclass <: array element superclass)
+  __ gen_subtype_check(r1, ok_is_subtype, false);
 
   // Come here on failure
   // object is at TOS
   __ b(Interpreter::_throw_ArrayStoreException_entry);
 
@@ -1147,14 +1176,66 @@
   do_oop_store(_masm, element_address, r0, IS_ARRAY);
   __ b(done);
 
   // Have a null in r0, r3=array, r2=index.  Store null at ary[idx]
   __ bind(is_null);
-  __ profile_null_seen(r2);
+  if (EnableValhalla) {
+    Label is_null_into_value_array_npe, store_null;
+
+    // No way to store null in flat null-free array
+    __ test_null_free_array_oop(r3, r8, is_null_into_value_array_npe);
+    __ b(store_null);
+
+    __ bind(is_null_into_value_array_npe);
+    __ b(ExternalAddress(Interpreter::_throw_NullPointerException_entry));
+
+    __ bind(store_null);
+  }
 
   // Store a null
   do_oop_store(_masm, element_address, noreg, IS_ARRAY);
+  __ b(done);
+
+  if (UseFlatArray) {
+     Label is_type_ok;
+    __ bind(is_flat_array); // Store non-null value to flat
+
+    // Simplistic type check...
+    // r0 - value, r2 - index, r3 - array.
+
+    // Profile the not-null value's klass.
+    // Load value class
+     __ load_klass(r1, r0);
+
+    // Move element klass into r7
+     __ ldr(r7, Address(r5, ArrayKlass::element_klass_offset()));
+
+    // flat value array needs exact type match
+    // is "r1 == r7" (value subclass == array element superclass)
+
+     __ cmp(r7, r1);
+     __ br(Assembler::EQ, is_type_ok);
+
+     __ b(ExternalAddress(Interpreter::_throw_ArrayStoreException_entry));
+
+     __ bind(is_type_ok);
+    // r1: value's klass
+    // r3: array
+    // r5: array klass
+    __ test_klass_is_empty_inline_type(r1, r7, done);
+
+    // calc dst for copy
+    __ ldrw(r7, at_tos_p1()); // index
+    __ data_for_value_array_index(r3, r5, r7, r7);
+
+    // ...and src for copy
+    __ ldr(r6, at_tos());  // value
+    __ data_for_oop(r6, r6, r1);
+
+    __ mov(r4, r1);  // Shuffle arguments to avoid conflict with c_rarg1
+    __ access_value_copy(IN_HEAP, r6, r7, r4);
+  }
 
   // Pop stack arguments
   __ bind(done);
   __ add(esp, esp, 3 * Interpreter::stackElementSize);
 }
@@ -1955,23 +2036,71 @@
   branch(false, false);
   __ bind(not_taken);
   __ profile_not_taken_branch(r0);
 }
 
-void TemplateTable::if_acmp(Condition cc)
-{
+void TemplateTable::if_acmp(Condition cc) {
   transition(atos, vtos);
   // assume branch is more often taken than not (loops use backward branches)
-  Label not_taken;
+  Label taken, not_taken;
   __ pop_ptr(r1);
+
+  __ profile_acmp(r2, r1, r0, r4);
+
+  Register is_inline_type_mask = rscratch1;
+  __ mov(is_inline_type_mask, markWord::inline_type_pattern);
+
+  if (EnableValhalla) {
+    __ cmp(r1, r0);
+    __ br(Assembler::EQ, (cc == equal) ? taken : not_taken);
+
+    // might be substitutable, test if either r0 or r1 is null
+    __ andr(r2, r0, r1);
+    __ cbz(r2, (cc == equal) ? not_taken : taken);
+
+    // and both are values ?
+    __ ldr(r2, Address(r1, oopDesc::mark_offset_in_bytes()));
+    __ andr(r2, r2, is_inline_type_mask);
+    __ ldr(r4, Address(r0, oopDesc::mark_offset_in_bytes()));
+    __ andr(r4, r4, is_inline_type_mask);
+    __ andr(r2, r2, r4);
+    __ cmp(r2,  is_inline_type_mask);
+    __ br(Assembler::NE, (cc == equal) ? not_taken : taken);
+
+    // same value klass ?
+    __ load_metadata(r2, r1);
+    __ load_metadata(r4, r0);
+    __ cmp(r2, r4);
+    __ br(Assembler::NE, (cc == equal) ? not_taken : taken);
+
+    // Know both are the same type, let's test for substitutability...
+    if (cc == equal) {
+      invoke_is_substitutable(r0, r1, taken, not_taken);
+    } else {
+      invoke_is_substitutable(r0, r1, not_taken, taken);
+    }
+    __ stop("Not reachable");
+  }
+
   __ cmpoop(r1, r0);
   __ br(j_not(cc), not_taken);
+  __ bind(taken);
   branch(false, false);
   __ bind(not_taken);
-  __ profile_not_taken_branch(r0);
+  __ profile_not_taken_branch(r0, true);
+}
+
+void TemplateTable::invoke_is_substitutable(Register aobj, Register bobj,
+                                            Label& is_subst, Label& not_subst) {
+
+  __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::is_substitutable), aobj, bobj);
+  // Restored... r0 answer, jmp to outcome...
+  __ cbz(r0, not_subst);
+  __ b(is_subst);
 }
 
+
 void TemplateTable::ret() {
   transition(vtos, vtos);
   locals_index(r1);
   __ ldr(r1, aaddress(r1)); // get return bci, compute return bcp
   __ profile_ret(r1, r2);
@@ -2575,20 +2704,28 @@
   __ verify_oop(r);
 }
 
 void TemplateTable::getfield_or_static(int byte_no, bool is_static, RewriteControl rc)
 {
-  const Register cache     = r4;
+  const Register cache     = r2;
   const Register obj       = r4;
+  const Register klass     = r5;
+  const Register inline_klass = r7;
+  const Register field_index = r23;
   const Register index     = r3;
   const Register tos_state = r3;
   const Register off       = r19;
   const Register flags     = r6;
   const Register bc        = r4; // uses same reg as obj, so don't mix them
 
   resolve_cache_and_index_for_field(byte_no, cache, index);
   jvmti_post_field_access(cache, index, is_static, false);
+
+  // Valhalla extras
+  __ load_unsigned_short(field_index, Address(cache, in_bytes(ResolvedFieldEntry::field_index_offset())));
+  __ ldr(klass, Address(cache, ResolvedFieldEntry::field_holder_offset()));
+
   load_resolved_field_entry(obj, cache, tos_state, off, flags, is_static);
 
   if (!is_static) {
     // obj is on the stack
     pop_and_check_object(obj);
@@ -2643,16 +2780,84 @@
 
   __ bind(notBool);
   __ cmp(tos_state, (u1)atos);
   __ br(Assembler::NE, notObj);
   // atos
-  do_oop_load(_masm, field, r0, IN_HEAP);
-  __ push(atos);
-  if (rc == may_rewrite) {
-    patch_bytecode(Bytecodes::_fast_agetfield, bc, r1);
+  if (!EnableValhalla) {
+    do_oop_load(_masm, field, r0, IN_HEAP);
+    __ push(atos);
+    if (rc == may_rewrite) {
+      patch_bytecode(Bytecodes::_fast_agetfield, bc, r1);
+    }
+    __ b(Done);
+  } else { // Valhalla
+    if (is_static) {
+      __ load_heap_oop(r0, field, rscratch1, rscratch2);
+      Label is_null_free_inline_type, uninitialized;
+      // Issue below if the static field has not been initialized yet
+      __ test_field_is_null_free_inline_type(flags, noreg /*temp*/, is_null_free_inline_type);
+        // field is not a null free inline type
+        __ push(atos);
+        __ b(Done);
+      // field is a null free inline type, must not return null even if uninitialized
+      __ bind(is_null_free_inline_type);
+        __ cbz(r0, uninitialized);
+          __ push(atos);
+          __ b(Done);
+        __ bind(uninitialized);
+          Label slow_case, finish;
+          __ ldrb(rscratch1, Address(klass, InstanceKlass::init_state_offset()));
+          __ cmp(rscratch1, (u1)InstanceKlass::fully_initialized);
+          __ br(Assembler::NE, slow_case);
+          __ get_default_value_oop(klass, off /* temp */, r0);
+        __ b(finish);
+        __ bind(slow_case);
+          __ call_VM(r0, CAST_FROM_FN_PTR(address, InterpreterRuntime::uninitialized_static_inline_type_field), obj, cache);
+          __ bind(finish);
+          __ verify_oop(r0);
+          __ push(atos);
+          __ b(Done);
+    } else {
+      Label is_flat, nonnull, is_inline_type, has_null_marker, rewrite_inline;
+      __ test_field_is_null_free_inline_type(flags, noreg /*temp*/, is_inline_type);
+      __ test_field_has_null_marker(flags, noreg /*temp*/, has_null_marker);
+        // Non-inline field case
+        __ load_heap_oop(r0, field, rscratch1, rscratch2);
+        __ push(atos);
+        if (rc == may_rewrite) {
+          patch_bytecode(Bytecodes::_fast_agetfield, bc, r1);
+        }
+        __ b(Done);
+      __ bind(is_inline_type);
+        __ test_field_is_flat(flags, noreg /* temp */, is_flat);
+         // field is not flat
+          __ load_heap_oop(r0, field, rscratch1, rscratch2);
+          __ cbnz(r0, nonnull);
+            __ get_inline_type_field_klass(klass, field_index, inline_klass);
+            __ get_default_value_oop(inline_klass, klass /* temp */, r0);
+          __ bind(nonnull);
+          __ verify_oop(r0);
+          __ push(atos);
+          __ b(rewrite_inline);
+        __ bind(is_flat);
+        // field is flat
+          __ mov(r0, obj);
+          __ read_flat_field(klass, field_index, off, inline_klass /* temp */, r0);
+          __ verify_oop(r0);
+          __ push(atos);
+          __ b(rewrite_inline);
+        __ bind(has_null_marker);
+          call_VM(r0, CAST_FROM_FN_PTR(address, InterpreterRuntime::read_nullable_flat_field), obj, cache);
+          __ verify_oop(r0);
+          __ push(atos);
+      __ bind(rewrite_inline);
+      if (rc == may_rewrite) {
+        patch_bytecode(Bytecodes::_fast_vgetfield, bc, r1);
+      }
+      __ b(Done);
+    }
   }
-  __ b(Done);
 
   __ bind(notObj);
   __ cmp(tos_state, (u1)itos);
   __ br(Assembler::NE, notInt);
   // itos
@@ -2809,23 +3014,22 @@
   const Register cache     = r2;
   const Register index     = r3;
   const Register tos_state = r3;
   const Register obj       = r2;
   const Register off       = r19;
-  const Register flags     = r0;
+  const Register flags     = r6;
   const Register bc        = r4;
+  const Register inline_klass = r5;
 
   resolve_cache_and_index_for_field(byte_no, cache, index);
   jvmti_post_field_mod(cache, index, is_static);
   load_resolved_field_entry(obj, cache, tos_state, off, flags, is_static);
 
   Label Done;
-  __ mov(r5, flags);
-
   {
     Label notVolatile;
-    __ tbz(r5, ResolvedFieldEntry::is_volatile_shift, notVolatile);
+    __ tbz(flags, ResolvedFieldEntry::is_volatile_shift, notVolatile);
     __ membar(MacroAssembler::StoreStore | MacroAssembler::LoadStore);
     __ bind(notVolatile);
   }
 
   // field address
@@ -2870,18 +3074,70 @@
   __ cmp(tos_state, (u1)atos);
   __ br(Assembler::NE, notObj);
 
   // atos
   {
-    __ pop(atos);
-    if (!is_static) pop_and_check_object(obj);
-    // Store into the field
-    do_oop_store(_masm, field, r0, IN_HEAP);
-    if (rc == may_rewrite) {
-      patch_bytecode(Bytecodes::_fast_aputfield, bc, r1, true, byte_no);
-    }
-    __ b(Done);
+     if (!EnableValhalla) {
+      __ pop(atos);
+      if (!is_static) pop_and_check_object(obj);
+      // Store into the field
+      do_oop_store(_masm, field, r0, IN_HEAP);
+      if (rc == may_rewrite) {
+        patch_bytecode(Bytecodes::_fast_aputfield, bc, r1, true, byte_no);
+      }
+      __ b(Done);
+     } else { // Valhalla
+      __ pop(atos);
+      if (is_static) {
+        Label is_inline_type;
+         __ test_field_is_not_null_free_inline_type(flags, noreg /* temp */, is_inline_type);
+         __ null_check(r0);
+         __ bind(is_inline_type);
+         do_oop_store(_masm, field, r0, IN_HEAP);
+         __ b(Done);
+      } else {
+        Label is_inline_type, is_flat, has_null_marker, rewrite_not_inline, rewrite_inline;
+        __ test_field_is_null_free_inline_type(flags, noreg /*temp*/, is_inline_type);
+        __ test_field_has_null_marker(flags, noreg /*temp*/, has_null_marker);
+        // Not an inline type
+        pop_and_check_object(obj);
+        // Store into the field
+        do_oop_store(_masm, field, r0, IN_HEAP);
+        __ bind(rewrite_not_inline);
+        if (rc == may_rewrite) {
+          patch_bytecode(Bytecodes::_fast_aputfield, bc, r19, true, byte_no);
+        }
+        __ b(Done);
+        // Implementation of the inline type semantic
+        __ bind(is_inline_type);
+        __ null_check(r0);
+        __ test_field_is_flat(flags, noreg /*temp*/, is_flat);
+        // field is not flat
+        pop_and_check_object(obj);
+        // Store into the field
+        do_oop_store(_masm, field, r0, IN_HEAP);
+        __ b(rewrite_inline);
+        __ bind(is_flat);
+        // field is flat
+        pop_and_check_object(obj);
+        assert_different_registers(r0, inline_klass, obj, off);
+        __ load_klass(inline_klass, r0);
+        __ data_for_oop(r0, r0, inline_klass);
+        __ add(obj, obj, off);
+        __ access_value_copy(IN_HEAP, r0, obj, inline_klass);
+        __ b(rewrite_inline);
+        __ bind(has_null_marker);
+        assert_different_registers(r0, cache, r19);
+        pop_and_check_object(r19);
+        __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::write_nullable_flat_field), r19, r0, cache);
+        __ bind(rewrite_inline);
+        if (rc == may_rewrite) {
+          patch_bytecode(Bytecodes::_fast_vputfield, bc, r19, true, byte_no);
+        }
+        __ b(Done);
+      }
+     }  // Valhalla
   }
 
   __ bind(notObj);
   __ cmp(tos_state, (u1)itos);
   __ br(Assembler::NE, notInt);
@@ -2982,11 +3238,11 @@
 
   __ bind(Done);
 
   {
     Label notVolatile;
-    __ tbz(r5, ResolvedFieldEntry::is_volatile_shift, notVolatile);
+    __ tbz(flags, ResolvedFieldEntry::is_volatile_shift, notVolatile);
     __ membar(MacroAssembler::StoreLoad | MacroAssembler::StoreStore);
     __ bind(notVolatile);
   }
 }
 
@@ -3016,10 +3272,11 @@
     __ push_ptr(r19);                 // put the object pointer back on tos
     // Save tos values before call_VM() clobbers them. Since we have
     // to do it for every data type, we use the saved values as the
     // jvalue object.
     switch (bytecode()) {          // load values into the jvalue object
+    case Bytecodes::_fast_vputfield: //fall through
     case Bytecodes::_fast_aputfield: __ push_ptr(r0); break;
     case Bytecodes::_fast_bputfield: // fall through
     case Bytecodes::_fast_zputfield: // fall through
     case Bytecodes::_fast_sputfield: // fall through
     case Bytecodes::_fast_cputfield: // fall through
@@ -3042,10 +3299,11 @@
                CAST_FROM_FN_PTR(address,
                                 InterpreterRuntime::post_field_modification),
                r19, c_rarg2, c_rarg3);
 
     switch (bytecode()) {             // restore tos values
+    case Bytecodes::_fast_vputfield: //fall through
     case Bytecodes::_fast_aputfield: __ pop_ptr(r0); break;
     case Bytecodes::_fast_bputfield: // fall through
     case Bytecodes::_fast_zputfield: // fall through
     case Bytecodes::_fast_sputfield: // fall through
     case Bytecodes::_fast_cputfield: // fall through
@@ -3092,10 +3350,33 @@
   // field address
   const Address field(r2, r1);
 
   // access field
   switch (bytecode()) {
+  case Bytecodes::_fast_vputfield:
+   {
+      Label is_flat, has_null_marker, done;
+      __ test_field_has_null_marker(r3, noreg /* temp */, has_null_marker);
+      __ null_check(r0);
+      __ test_field_is_flat(r3, noreg /* temp */, is_flat);
+      // field is not flat
+      do_oop_store(_masm, field, r0, IN_HEAP);
+      __ b(done);
+      __ bind(is_flat);
+      // field is flat
+      __ load_klass(r4, r0);
+      __ data_for_oop(r0, r0, r4);
+      __ lea(rscratch1, field);
+      __ access_value_copy(IN_HEAP, r0, rscratch1, r4);
+      __ b(done);
+      __ bind(has_null_marker);
+      __ load_field_entry(r4, r1);
+      __ mov(r1, r2);
+      __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::write_nullable_flat_field), r1, r0, r4);
+      __ bind(done);
+    }
+    break;
   case Bytecodes::_fast_aputfield:
     do_oop_store(_masm, field, r0, IN_HEAP);
     break;
   case Bytecodes::_fast_lputfield:
     __ access_store_at(T_LONG, IN_HEAP, field, r0, noreg, noreg, noreg);
@@ -3187,10 +3468,39 @@
     __ bind(notVolatile);
   }
 
   // access field
   switch (bytecode()) {
+  case Bytecodes::_fast_vgetfield:
+    {
+      Register index = r4, klass = r5, inline_klass = r6, tmp = r7;
+      Label is_flat, has_null_marker, nonnull, Done;
+      __ test_field_has_null_marker(r3, noreg /*temp*/, has_null_marker);
+      __ test_field_is_flat(r3, noreg /* temp */, is_flat);
+        // field is not flat
+        __ load_heap_oop(r0, field, rscratch1, rscratch2);
+        __ cbnz(r0, nonnull);
+          __ load_unsigned_short(index, Address(r2, in_bytes(ResolvedFieldEntry::field_index_offset())));
+          __ ldr(klass, Address(r2, in_bytes(ResolvedFieldEntry::field_holder_offset())));
+          __ get_inline_type_field_klass(klass, index, inline_klass);
+          __ get_default_value_oop(inline_klass, tmp /* temp */, r0);
+        __ bind(nonnull);
+        __ verify_oop(r0);
+        __ b(Done);
+      __ bind(is_flat);
+      // field is flat
+        __ load_unsigned_short(index, Address(r2, in_bytes(ResolvedFieldEntry::field_index_offset())));
+        __ ldr(klass, Address(r2, in_bytes(ResolvedFieldEntry::field_holder_offset())));
+        __ read_flat_field(klass, index, r1, tmp /* temp */, r0);
+        __ verify_oop(r0);
+        __ b(Done);
+      __ bind(has_null_marker);
+        call_VM(r0, CAST_FROM_FN_PTR(address, InterpreterRuntime::read_nullable_flat_field), r0, r2);
+        __ verify_oop(r0);
+      __ bind(Done);
+    }
+    break;
   case Bytecodes::_fast_agetfield:
     do_oop_load(_masm, field, r0, IN_HEAP);
     __ verify_oop(r0);
     break;
   case Bytecodes::_fast_lgetfield:
@@ -3605,67 +3915,12 @@
 
   // make sure klass is initialized
   assert(VM_Version::supports_fast_class_init_checks(), "Optimization requires support for fast class initialization checks");
   __ clinit_barrier(r4, rscratch1, nullptr /*L_fast_path*/, &slow_case);
 
-  // get instance_size in InstanceKlass (scaled to a count of bytes)
-  __ ldrw(r3,
-          Address(r4,
-                  Klass::layout_helper_offset()));
-  // test to see if it has a finalizer or is malformed in some way
-  __ tbnz(r3, exact_log2(Klass::_lh_instance_slow_path_bit), slow_case);
-
-  // Allocate the instance:
-  //  If TLAB is enabled:
-  //    Try to allocate in the TLAB.
-  //    If fails, go to the slow path.
-  //    Initialize the allocation.
-  //    Exit.
-  //
-  //  Go to slow path.
-
-  if (UseTLAB) {
-    __ tlab_allocate(r0, r3, 0, noreg, r1, slow_case);
-
-    if (ZeroTLAB) {
-      // the fields have been already cleared
-      __ b(initialize_header);
-    }
-
-    // The object is initialized before the header.  If the object size is
-    // zero, go directly to the header initialization.
-    __ sub(r3, r3, sizeof(oopDesc));
-    __ cbz(r3, initialize_header);
-
-    // Initialize object fields
-    {
-      __ add(r2, r0, sizeof(oopDesc));
-      Label loop;
-      __ bind(loop);
-      __ str(zr, Address(__ post(r2, BytesPerLong)));
-      __ sub(r3, r3, BytesPerLong);
-      __ cbnz(r3, loop);
-    }
-
-    // initialize object header only.
-    __ bind(initialize_header);
-    __ mov(rscratch1, (intptr_t)markWord::prototype().value());
-    __ str(rscratch1, Address(r0, oopDesc::mark_offset_in_bytes()));
-    __ store_klass_gap(r0, zr);  // zero klass gap for compressed oops
-    __ store_klass(r0, r4);      // store klass last
-
-    {
-      SkipIfEqual skip(_masm, &DTraceAllocProbes, false);
-      // Trigger dtrace event for fastpath
-      __ push(atos); // save the return value
-      __ call_VM_leaf(
-           CAST_FROM_FN_PTR(address, static_cast<int (*)(oopDesc*)>(SharedRuntime::dtrace_object_alloc)), r0);
-      __ pop(atos); // restore the return value
-
-    }
-    __ b(done);
-  }
+  __ allocate_instance(r4, r0, r3, r1, true, slow_case);
+  __ b(done);
 
   // slow case
   __ bind(slow_case);
   __ get_constant_pool(c_rarg1);
   __ get_unsigned_2_byte_index_at_bcp(c_rarg2, 1);
@@ -3746,18 +4001,18 @@
 
   // Come here on success
   __ bind(ok_is_subtype);
   __ mov(r0, r3); // Restore object in r3
 
+  __ b(done);
+  __ bind(is_null);
+
   // Collect counts on whether this test sees nulls a lot or not.
   if (ProfileInterpreter) {
-    __ b(done);
-    __ bind(is_null);
     __ profile_null_seen(r2);
-  } else {
-    __ bind(is_null);   // same as 'done'
   }
+
   __ bind(done);
 }
 
 void TemplateTable::instanceof() {
   transition(atos, itos);
@@ -3872,10 +4127,14 @@
   transition(atos, vtos);
 
   // check for null object
   __ null_check(r0);
 
+  Label is_inline_type;
+  __ ldr(rscratch1, Address(r0, oopDesc::mark_offset_in_bytes()));
+  __ test_markword_is_inline_type(rscratch1, is_inline_type);
+
   const Address monitor_block_top(
         rfp, frame::interpreter_frame_monitor_block_top_offset * wordSize);
   const Address monitor_block_bot(
         rfp, frame::interpreter_frame_initial_sp_offset * wordSize);
   const int entry_size = frame::interpreter_frame_monitor_size_in_bytes();
@@ -3973,20 +4232,37 @@
   __ generate_stack_overflow_check(0);
 
   // The bcp has already been incremented. Just need to dispatch to
   // next instruction.
   __ dispatch_next(vtos);
+
+  __ bind(is_inline_type);
+  __ call_VM(noreg, CAST_FROM_FN_PTR(address,
+                    InterpreterRuntime::throw_illegal_monitor_state_exception));
+  __ should_not_reach_here();
 }
 
 
 void TemplateTable::monitorexit()
 {
   transition(atos, vtos);
 
   // check for null object
   __ null_check(r0);
 
+  const int is_inline_type_mask = markWord::inline_type_pattern;
+  Label has_identity;
+  __ ldr(rscratch1, Address(r0, oopDesc::mark_offset_in_bytes()));
+  __ mov(rscratch2, is_inline_type_mask);
+  __ andr(rscratch1, rscratch1, rscratch2);
+  __ cmp(rscratch1, rscratch2);
+  __ br(Assembler::NE, has_identity);
+  __ call_VM(noreg, CAST_FROM_FN_PTR(address,
+                     InterpreterRuntime::throw_illegal_monitor_state_exception));
+  __ should_not_reach_here();
+  __ bind(has_identity);
+
   const Address monitor_block_top(
         rfp, frame::interpreter_frame_monitor_block_top_offset * wordSize);
   const Address monitor_block_bot(
         rfp, frame::interpreter_frame_initial_sp_offset * wordSize);
   const int entry_size = frame::interpreter_frame_monitor_size_in_bytes();
diff a/src/hotspot/cpu/aarch64/templateTable_aarch64.hpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.hpp
--- a/src/hotspot/cpu/aarch64/templateTable_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.hpp
@@ -32,6 +32,8 @@
 
   // Helpers
   static void index_check(Register array, Register index);
   static void index_check_without_pop(Register array, Register index);
 
+  static void invoke_is_substitutable(Register aobj, Register bobj, Label& is_subst, Label& not_subst);
+
 #endif // CPU_AARCH64_TEMPLATETABLE_AARCH64_HPP
diff a/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp b/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp
@@ -45,14 +45,14 @@
 
 #ifndef PRODUCT
 extern "C" void bad_compiled_vtable_index(JavaThread* thread, oop receiver, int index);
 #endif
 
-VtableStub* VtableStubs::create_vtable_stub(int vtable_index) {
+VtableStub* VtableStubs::create_vtable_stub(int vtable_index, bool caller_is_c1) {
   // Read "A word on VtableStub sizing" in share/code/vtableStubs.hpp for details on stub sizing.
   const int stub_code_length = code_size_limit(true);
-  VtableStub* s = new(stub_code_length) VtableStub(true, vtable_index);
+  VtableStub* s = new(stub_code_length) VtableStub(true, vtable_index, caller_is_c1);
   // Can be null if there is no free space in the code cache.
   if (s == nullptr) {
     return nullptr;
   }
 
@@ -61,10 +61,14 @@
   // overflow in subsequently generated stubs.
   address   start_pc;
   int       slop_bytes = 0;
   int       slop_delta = 0;
 
+// No variance was detected in vtable stub sizes. Setting index_dependent_slop == 0 will unveil any deviation from this observation.
+  const int index_dependent_slop     = 0;
+  ByteSize  entry_offset = caller_is_c1 ? Method::from_compiled_inline_offset() :  Method::from_compiled_inline_ro_offset();
+
   ResourceMark    rm;
   CodeBuffer      cb(s->entry_point(), stub_code_length);
   MacroAssembler* masm = new MacroAssembler(&cb);
 
 #if (!defined(PRODUCT) && defined(COMPILER2))
@@ -114,35 +118,36 @@
 
 #ifndef PRODUCT
   if (DebugVtables) {
     Label L;
     __ cbz(rmethod, L);
-    __ ldr(rscratch1, Address(rmethod, Method::from_compiled_offset()));
+    __ ldr(rscratch1, Address(rmethod, entry_offset));
     __ cbnz(rscratch1, L);
     __ stop("Vtable entry is null");
     __ bind(L);
   }
 #endif // PRODUCT
 
   // r0: receiver klass
   // rmethod: Method*
   // r2: receiver
   address ame_addr = __ pc();
-  __ ldr(rscratch1, Address(rmethod, Method::from_compiled_offset()));
+  __ ldr(rscratch1, Address(rmethod, entry_offset));
   __ br(rscratch1);
 
   masm->flush();
-  bookkeeping(masm, tty, s, npe_addr, ame_addr, true, vtable_index, slop_bytes, 0);
+  slop_bytes += index_dependent_slop; // add'l slop for size variance due to large itable offsets
+  bookkeeping(masm, tty, s, npe_addr, ame_addr, true, vtable_index, slop_bytes, index_dependent_slop);
 
   return s;
 }
 
 
-VtableStub* VtableStubs::create_itable_stub(int itable_index) {
+VtableStub* VtableStubs::create_itable_stub(int itable_index, bool caller_is_c1) {
   // Read "A word on VtableStub sizing" in share/code/vtableStubs.hpp for details on stub sizing.
   const int stub_code_length = code_size_limit(false);
-  VtableStub* s = new(stub_code_length) VtableStub(false, itable_index);
+  VtableStub* s = new(stub_code_length) VtableStub(false, itable_index, caller_is_c1);
   // Can be null if there is no free space in the code cache.
   if (s == nullptr) {
     return nullptr;
   }
 
@@ -151,10 +156,14 @@
   // overflow in subsequently generated stubs.
   address   start_pc;
   int       slop_bytes = 0;
   int       slop_delta = 0;
 
+  const int index_dependent_slop = (itable_index == 0) ? 4 :     // code size change with transition from 8-bit to 32-bit constant (@index == 16).
+                                   (itable_index < 16) ? 3 : 0;  // index == 0 generates even shorter code.
+  ByteSize  entry_offset = caller_is_c1 ? Method::from_compiled_inline_offset() :  Method::from_compiled_inline_ro_offset();
+
   ResourceMark    rm;
   CodeBuffer      cb(s->entry_point(), stub_code_length);
   MacroAssembler* masm = new MacroAssembler(&cb);
 
 #if (!defined(PRODUCT) && defined(COMPILER2))
@@ -205,21 +214,21 @@
 
 #ifdef ASSERT
   if (DebugVtables) {
     Label L2;
     __ cbz(rmethod, L2);
-    __ ldr(rscratch1, Address(rmethod, Method::from_compiled_offset()));
+    __ ldr(rscratch1, Address(rmethod, entry_offset));
     __ cbnz(rscratch1, L2);
     __ stop("compiler entrypoint is null");
     __ bind(L2);
   }
 #endif // ASSERT
 
   // rmethod: Method*
   // j_rarg0: receiver
   address ame_addr = __ pc();
-  __ ldr(rscratch1, Address(rmethod, Method::from_compiled_offset()));
+  __ ldr(rscratch1, Address(rmethod, entry_offset));
   __ br(rscratch1);
 
   __ bind(L_no_such_interface);
   // Handle IncompatibleClassChangeError in itable stubs.
   // More detailed error message.
@@ -228,11 +237,12 @@
   // dirty work.
   assert(SharedRuntime::get_handle_wrong_method_stub() != nullptr, "check initialization order");
   __ far_jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub()));
 
   masm->flush();
-  bookkeeping(masm, tty, s, npe_addr, ame_addr, false, itable_index, slop_bytes, 0);
+  slop_bytes += index_dependent_slop; // add'l slop for size variance due to large itable offsets
+  bookkeeping(masm, tty, s, npe_addr, ame_addr, false, itable_index, slop_bytes, index_dependent_slop);
 
   return s;
 }
 
 int VtableStub::pd_code_alignment() {
diff a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp
--- a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp
+++ b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp
@@ -2565,10 +2565,14 @@
 
 void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) {
   fatal("Type profiling not implemented on this platform");
 }
 
+void LIR_Assembler::emit_profile_inline_type(LIR_OpProfileInlineType* op) {
+  Unimplemented();
+}
+
 void LIR_Assembler::emit_delay(LIR_OpDelay*) {
   Unimplemented();
 }
 
 
diff a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp
--- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp
+++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp
@@ -3198,10 +3198,13 @@
     if (klass_reg_used) { __ load_const_optimized(R29_TOC, MacroAssembler::global_toc(), R0); } // reinit
   }
   __ bind(Ldone);
 }
 
+void LIR_Assembler::emit_profile_inline_type(LIR_OpProfileInlineType* op) {
+  Unimplemented();
+}
 
 void LIR_Assembler::emit_updatecrc32(LIR_OpUpdateCRC32* op) {
   assert(op->crc()->is_single_cpu(), "crc must be register");
   assert(op->val()->is_single_cpu(), "byte value must be register");
   assert(op->result_opr()->is_single_cpu(), "result must be register");
diff a/src/hotspot/cpu/ppc/globals_ppc.hpp b/src/hotspot/cpu/ppc/globals_ppc.hpp
--- a/src/hotspot/cpu/ppc/globals_ppc.hpp
+++ b/src/hotspot/cpu/ppc/globals_ppc.hpp
@@ -68,10 +68,13 @@
 define_pd_global(bool, RewriteBytecodes,      true);
 define_pd_global(bool, RewriteFrequentPairs,  true);
 
 define_pd_global(bool, PreserveFramePointer,  false);
 
+define_pd_global(bool, InlineTypePassFieldsAsArgs, false);
+define_pd_global(bool, InlineTypeReturnedAsFields, false);
+
 define_pd_global(uintx, TypeProfileLevel, 111);
 
 define_pd_global(bool, CompactStrings, true);
 
 // 2x unrolled loop is shorter with more than 9 HeapWords.
diff a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp
--- a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp
+++ b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp
@@ -1858,11 +1858,11 @@
       if (MethodData::profile_return()) {
         // We're right after the type profile for the last
         // argument. tmp1 is the number of cells left in the
         // CallTypeData/VirtualCallTypeData to reach its end. Non null
         // if there's a return to profile.
-        assert(ReturnTypeEntry::static_cell_count() < TypeStackSlotEntries::per_arg_count(),
+        assert(SingleTypeEntry::static_cell_count() < TypeStackSlotEntries::per_arg_count(),
                "can't move past ret type");
         sldi(tmp1, tmp1, exact_log2(DataLayout::cell_size));
         add(R28_mdx, tmp1, R28_mdx);
       }
     } else {
@@ -1899,11 +1899,11 @@
       cmpwi(CCR1, tmp2, static_cast<int>(vmIntrinsics::_compiledLambdaForm));
       cror(CCR0, Assembler::equal, CCR1, Assembler::equal);
       bne(CCR0, profile_continue);
     }
 
-    profile_obj_type(ret, R28_mdx, -in_bytes(ReturnTypeEntry::size()), tmp1, tmp2);
+    profile_obj_type(ret, R28_mdx, -in_bytes(SingleTypeEntry::size()), tmp1, tmp2);
 
     align(32, 12);
     bind(profile_continue);
   }
 }
diff a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp
--- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp
+++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp
@@ -3079,10 +3079,14 @@
 
     __ bind(next);
   }
 }
 
+void LIR_Assembler::emit_profile_inline_type(LIR_OpProfileInlineType* op) {
+  Unimplemented();
+}
+
 void LIR_Assembler::emit_updatecrc32(LIR_OpUpdateCRC32* op) {
   assert(op->crc()->is_single_cpu(), "crc must be register");
   assert(op->val()->is_single_cpu(), "byte value must be register");
   assert(op->result_opr()->is_single_cpu(), "result must be register");
   Register crc = op->crc()->as_register();
diff a/src/hotspot/cpu/s390/interp_masm_s390.cpp b/src/hotspot/cpu/s390/interp_masm_s390.cpp
--- a/src/hotspot/cpu/s390/interp_masm_s390.cpp
+++ b/src/hotspot/cpu/s390/interp_masm_s390.cpp
@@ -1820,11 +1820,11 @@
       if (MethodData::profile_return()) {
         // We're right after the type profile for the last
         // argument. Tmp is the number of cells left in the
         // CallTypeData/VirtualCallTypeData to reach its end. Non null
         // if there's a return to profile.
-        assert(ReturnTypeEntry::static_cell_count() < TypeStackSlotEntries::per_arg_count(), "can't move past ret type");
+        assert(SingleTypeEntry::static_cell_count() < TypeStackSlotEntries::per_arg_count(), "can't move past ret type");
         z_sllg(tmp, tmp, exact_log2(DataLayout::cell_size));
         z_agr(mdp, tmp);
       }
       z_stg(mdp, _z_ijava_state_neg(mdx), Z_fp);
     } else {
@@ -1869,11 +1869,11 @@
       z_brne(profile_continue);
 
       bind(do_profile);
     }
 
-    Address mdo_ret_addr(mdp, -in_bytes(ReturnTypeEntry::size()));
+    Address mdo_ret_addr(mdp, -in_bytes(SingleTypeEntry::size()));
     profile_obj_type(ret, mdo_ret_addr, tmp);
 
     bind(profile_continue);
   }
 }
diff a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp
--- a/src/hotspot/cpu/x86/assembler_x86.hpp
+++ b/src/hotspot/cpu/x86/assembler_x86.hpp
@@ -1733,11 +1733,10 @@
   void notq(Register dst);
 
   void btsq(Address dst, int imm8);
   void btrq(Address dst, int imm8);
 #endif
-
   void orw(Register dst, Register src);
 
   void orl(Address dst, int32_t imm32);
   void orl(Register dst, int32_t imm32);
   void orl(Register dst, Address src);
diff a/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp b/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp
--- a/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp
+++ b/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp
@@ -28,10 +28,11 @@
 #include "c1/c1_LIRAssembler.hpp"
 #include "c1/c1_MacroAssembler.hpp"
 #include "c1/c1_Runtime1.hpp"
 #include "classfile/javaClasses.hpp"
 #include "nativeInst_x86.hpp"
+#include "oops/objArrayKlass.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "utilities/align.hpp"
 #include "utilities/macros.hpp"
 #include "vmreg_x86.inline.hpp"
 
@@ -168,10 +169,83 @@
   ce->add_call_info_here(_info);
   debug_only(__ should_not_reach_here());
 }
 
 
+// Implementation of LoadFlattenedArrayStub
+
+LoadFlattenedArrayStub::LoadFlattenedArrayStub(LIR_Opr array, LIR_Opr index, LIR_Opr result, CodeEmitInfo* info) {
+  _array = array;
+  _index = index;
+  _result = result;
+  // Tell the register allocator that the runtime call will scratch rax.
+  _scratch_reg = FrameMap::rax_oop_opr;
+  _info = new CodeEmitInfo(info);
+}
+
+void LoadFlattenedArrayStub::emit_code(LIR_Assembler* ce) {
+  assert(__ rsp_offset() == 0, "frame size should be fixed");
+  __ bind(_entry);
+  ce->store_parameter(_array->as_register(), 1);
+  ce->store_parameter(_index->as_register(), 0);
+  __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::load_flat_array_id)));
+  ce->add_call_info_here(_info);
+  ce->verify_oop_map(_info);
+  if (_result->as_register() != rax) {
+    __ movptr(_result->as_register(), rax);
+  }
+  __ jmp(_continuation);
+}
+
+
+// Implementation of StoreFlattenedArrayStub
+
+StoreFlattenedArrayStub::StoreFlattenedArrayStub(LIR_Opr array, LIR_Opr index, LIR_Opr value, CodeEmitInfo* info) {
+  _array = array;
+  _index = index;
+  _value = value;
+  // Tell the register allocator that the runtime call will scratch rax.
+  _scratch_reg = FrameMap::rax_oop_opr;
+  _info = new CodeEmitInfo(info);
+}
+
+
+void StoreFlattenedArrayStub::emit_code(LIR_Assembler* ce) {
+  assert(__ rsp_offset() == 0, "frame size should be fixed");
+  __ bind(_entry);
+  ce->store_parameter(_array->as_register(), 2);
+  ce->store_parameter(_index->as_register(), 1);
+  ce->store_parameter(_value->as_register(), 0);
+  __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::store_flat_array_id)));
+  ce->add_call_info_here(_info);
+  ce->verify_oop_map(_info);
+  __ jmp(_continuation);
+}
+
+
+// Implementation of SubstitutabilityCheckStub
+
+SubstitutabilityCheckStub::SubstitutabilityCheckStub(LIR_Opr left, LIR_Opr right, CodeEmitInfo* info) {
+  _left = left;
+  _right = right;
+  // Tell the register allocator that the runtime call will scratch rax.
+  _scratch_reg = FrameMap::rax_oop_opr;
+  _info = new CodeEmitInfo(info);
+}
+
+void SubstitutabilityCheckStub::emit_code(LIR_Assembler* ce) {
+  assert(__ rsp_offset() == 0, "frame size should be fixed");
+  __ bind(_entry);
+  ce->store_parameter(_left->as_register(), 1);
+  ce->store_parameter(_right->as_register(), 0);
+  __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::substitutability_check_id)));
+  ce->add_call_info_here(_info);
+  ce->verify_oop_map(_info);
+  __ jmp(_continuation);
+}
+
+
 // Implementation of NewInstanceStub
 
 NewInstanceStub::NewInstanceStub(LIR_Opr klass_reg, LIR_Opr result, ciInstanceKlass* klass, CodeEmitInfo* info, Runtime1::StubID stub_id) {
   _result = result;
   _klass = klass;
@@ -220,33 +294,48 @@
 }
 
 
 // Implementation of NewObjectArrayStub
 
-NewObjectArrayStub::NewObjectArrayStub(LIR_Opr klass_reg, LIR_Opr length, LIR_Opr result, CodeEmitInfo* info) {
+NewObjectArrayStub::NewObjectArrayStub(LIR_Opr klass_reg, LIR_Opr length, LIR_Opr result,
+                                       CodeEmitInfo* info, bool is_null_free) {
   _klass_reg = klass_reg;
   _result = result;
   _length = length;
   _info = new CodeEmitInfo(info);
+  _is_null_free = is_null_free;
 }
 
 
 void NewObjectArrayStub::emit_code(LIR_Assembler* ce) {
   assert(__ rsp_offset() == 0, "frame size should be fixed");
   __ bind(_entry);
   assert(_length->as_register() == rbx, "length must in rbx,");
   assert(_klass_reg->as_register() == rdx, "klass_reg must in rdx");
-  __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::new_object_array_id)));
+  if (_is_null_free) {
+    __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::new_flat_array_id)));
+  } else {
+    __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::new_object_array_id)));
+  }
   ce->add_call_info_here(_info);
   ce->verify_oop_map(_info);
   assert(_result->as_register() == rax, "result must in rax,");
   __ jmp(_continuation);
 }
 
 void MonitorEnterStub::emit_code(LIR_Assembler* ce) {
   assert(__ rsp_offset() == 0, "frame size should be fixed");
   __ bind(_entry);
+  if (_throw_imse_stub != nullptr) {
+    // When we come here, _obj_reg has already been checked to be non-null.
+    const int is_value_mask = markWord::inline_type_pattern;
+    Register mark = _scratch_reg->as_register();
+    __ movptr(mark, Address(_obj_reg->as_register(), oopDesc::mark_offset_in_bytes()));
+    __ andptr(mark, is_value_mask);
+    __ cmpl(mark, is_value_mask);
+    __ jcc(Assembler::equal, *_throw_imse_stub->entry());
+  }
   ce->store_parameter(_obj_reg->as_register(),  1);
   ce->store_parameter(_lock_reg->as_register(), 0);
   Runtime1::StubID enter_id;
   if (ce->compilation()->has_fpu_code()) {
     enter_id = Runtime1::monitorenter_id;
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
@@ -30,15 +30,17 @@
 #include "c1/c1_LIRAssembler.hpp"
 #include "c1/c1_MacroAssembler.hpp"
 #include "c1/c1_Runtime1.hpp"
 #include "c1/c1_ValueStack.hpp"
 #include "ci/ciArrayKlass.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "ci/ciInstance.hpp"
 #include "compiler/oopMap.hpp"
 #include "gc/shared/collectedHeap.hpp"
 #include "gc/shared/gc_globals.hpp"
 #include "nativeInst_x86.hpp"
+#include "oops/oop.inline.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "runtime/frame.inline.hpp"
 #include "runtime/safepointMechanism.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/stubRoutines.hpp"
@@ -477,11 +479,11 @@
   if (method()->is_synchronized() || compilation()->env()->dtrace_method_probes()) {
     __ mov(rax, rbx);  // Restore the exception
   }
 
   // remove the activation and dispatch to the unwind handler
-  __ remove_frame(initial_frame_size_in_bytes());
+  __ remove_frame(initial_frame_size_in_bytes(), needs_stack_repair());
   __ jump(RuntimeAddress(Runtime1::entry_for(Runtime1::unwind_exception_id)));
 
   // Emit the slow path assembly
   if (stub != nullptr) {
     stub->emit_code(this);
@@ -514,13 +516,44 @@
 void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) {
   assert(result->is_illegal() || !result->is_single_cpu() || result->as_register() == rax, "word returns are in rax,");
   if (!result->is_illegal() && result->is_float_kind() && !result->is_xmm_register()) {
     assert(result->fpu() == 0, "result must already be on TOS");
   }
+  if (InlineTypeReturnedAsFields) {
+  #ifndef _LP64
+     Unimplemented();
+  #endif
+    // Check if we are returning an non-null inline type and load its fields into registers
+    ciType* return_type = compilation()->method()->return_type();
+    if (return_type->is_inlinetype()) {
+      ciInlineKlass* vk = return_type->as_inline_klass();
+      if (vk->can_be_returned_as_fields()) {
+        address unpack_handler = vk->unpack_handler();
+        assert(unpack_handler != nullptr, "must be");
+        __ call(RuntimeAddress(unpack_handler));
+      }
+    } else if (return_type->is_instance_klass() && (!return_type->is_loaded() || StressCallingConvention)) {
+      Label skip;
+      __ test_oop_is_not_inline_type(rax, rscratch1, skip);
+
+      // Load fields from a buffered value with an inline class specific handler
+      __ load_klass(rdi, rax, rscratch1);
+      __ movptr(rdi, Address(rdi, InstanceKlass::adr_inlineklass_fixed_block_offset()));
+      __ movptr(rdi, Address(rdi, InlineKlass::unpack_handler_offset()));
+      // Unpack handler can be null if inline type is not scalarizable in returns
+      __ testptr(rdi, rdi);
+      __ jcc(Assembler::zero, skip);
+      __ call(rdi);
+
+      __ bind(skip);
+    }
+    // At this point, rax points to the value object (for interpreter or C1 caller).
+    // The fields of the object are copied into registers (for C2 caller).
+  }
 
   // Pop the stack before the safepoint code
-  __ remove_frame(initial_frame_size_in_bytes());
+  __ remove_frame(initial_frame_size_in_bytes(), needs_stack_repair());
 
   if (StackReservedPages > 0 && compilation()->has_reserved_stack_access()) {
     __ reserved_stack_check();
   }
 
@@ -538,10 +571,14 @@
   __ safepoint_poll(*code_stub->entry(), thread, true /* at_return */, true /* in_nmethod */);
   __ ret(0);
 }
 
 
+int LIR_Assembler::store_inline_type_fields_to_buf(ciInlineKlass* vk) {
+  return (__ store_inline_type_fields_to_buf(vk, false));
+}
+
 int LIR_Assembler::safepoint_poll(LIR_Opr tmp, CodeEmitInfo* info) {
   guarantee(info != nullptr, "Shouldn't be null");
   int offset = __ offset();
 #ifdef _LP64
   const Register poll_addr = rscratch1;
@@ -1612,11 +1649,11 @@
 
 void LIR_Assembler::emit_alloc_array(LIR_OpAllocArray* op) {
   Register len =  op->len()->as_register();
   LP64_ONLY( __ movslq(len, len); )
 
-  if (UseSlowPath ||
+  if (UseSlowPath || op->is_null_free() ||
       (!UseFastNewObjectArray && is_reference_type(op->type())) ||
       (!UseFastNewTypeArray   && !is_reference_type(op->type()))) {
     __ jmp(*op->stub()->entry());
   } else {
     Register tmp1 = op->tmp1()->as_register();
@@ -1710,34 +1747,36 @@
     select_different_registers(obj, dst, k_RInfo, klass_RInfo, Rtmp1);
   }
 
   assert_different_registers(obj, k_RInfo, klass_RInfo);
 
-  __ testptr(obj, obj);
-  if (op->should_profile()) {
-    Label not_null;
-    Register mdo  = klass_RInfo;
-    __ mov_metadata(mdo, md->constant_encoding());
-    __ jccb(Assembler::notEqual, not_null);
-    // Object is null; update MDO and exit
-    Address data_addr(mdo, md->byte_offset_of_slot(data, DataLayout::flags_offset()));
-    int header_bits = BitData::null_seen_byte_constant();
-    __ orb(data_addr, header_bits);
-    __ jmp(*obj_is_null);
-    __ bind(not_null);
-
-    Label update_done;
-    Register recv = k_RInfo;
-    __ load_klass(recv, obj, tmp_load_klass);
-    type_profile_helper(mdo, md, data, recv, &update_done);
-
-    Address nonprofiled_receiver_count_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset()));
-    __ addptr(nonprofiled_receiver_count_addr, DataLayout::counter_increment);
-
-    __ bind(update_done);
-  } else {
-    __ jcc(Assembler::equal, *obj_is_null);
+  if (op->need_null_check()) {
+    __ testptr(obj, obj);
+    if (op->should_profile()) {
+      Label not_null;
+      Register mdo  = klass_RInfo;
+      __ mov_metadata(mdo, md->constant_encoding());
+      __ jccb(Assembler::notEqual, not_null);
+      // Object is null; update MDO and exit
+      Address data_addr(mdo, md->byte_offset_of_slot(data, DataLayout::flags_offset()));
+      int header_bits = BitData::null_seen_byte_constant();
+      __ orb(data_addr, header_bits);
+      __ jmp(*obj_is_null);
+      __ bind(not_null);
+
+      Label update_done;
+      Register recv = k_RInfo;
+      __ load_klass(recv, obj, tmp_load_klass);
+      type_profile_helper(mdo, md, data, recv, &update_done);
+
+      Address nonprofiled_receiver_count_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset()));
+      __ addptr(nonprofiled_receiver_count_addr, DataLayout::counter_increment);
+
+      __ bind(update_done);
+    } else {
+      __ jcc(Assembler::equal, *obj_is_null);
+    }
   }
 
   if (!k->is_loaded()) {
     klass2reg_with_patching(k_RInfo, op->info_for_patch());
   } else {
@@ -1925,10 +1964,134 @@
         ShouldNotReachHere();
       }
 
 }
 
+void LIR_Assembler::emit_opFlattenedArrayCheck(LIR_OpFlattenedArrayCheck* op) {
+  // We are loading/storing from/to an array that *may* be a flat array (the
+  // declared type is Object[], abstract[], interface[] or VT.ref[]).
+  // If this array is a flat array, take the slow path.
+  Register klass = op->tmp()->as_register();
+  if (UseArrayMarkWordCheck) {
+    __ test_flat_array_oop(op->array()->as_register(), op->tmp()->as_register(), *op->stub()->entry());
+  } else {
+    Register tmp_load_klass = LP64_ONLY(rscratch1) NOT_LP64(noreg);
+    __ load_klass(klass, op->array()->as_register(), tmp_load_klass);
+    __ movl(klass, Address(klass, Klass::layout_helper_offset()));
+    __ testl(klass, Klass::_lh_array_tag_flat_value_bit_inplace);
+    __ jcc(Assembler::notZero, *op->stub()->entry());
+  }
+  if (!op->value()->is_illegal()) {
+    // The array is not a flat array, but it might be null-free. If we are storing
+    // a null into a null-free array, take the slow path (which will throw NPE).
+    Label skip;
+    __ cmpptr(op->value()->as_register(), NULL_WORD);
+    __ jcc(Assembler::notEqual, skip);
+    if (UseArrayMarkWordCheck) {
+      __ test_null_free_array_oop(op->array()->as_register(), op->tmp()->as_register(), *op->stub()->entry());
+    } else {
+      __ testl(klass, Klass::_lh_null_free_array_bit_inplace);
+      __ jcc(Assembler::notZero, *op->stub()->entry());
+    }
+    __ bind(skip);
+  }
+}
+
+void LIR_Assembler::emit_opNullFreeArrayCheck(LIR_OpNullFreeArrayCheck* op) {
+  // We are storing into an array that *may* be null-free (the declared type is
+  // Object[], abstract[], interface[] or VT.ref[]).
+  if (UseArrayMarkWordCheck) {
+    Label test_mark_word;
+    Register tmp = op->tmp()->as_register();
+    __ movptr(tmp, Address(op->array()->as_register(), oopDesc::mark_offset_in_bytes()));
+    __ testl(tmp, markWord::unlocked_value);
+    __ jccb(Assembler::notZero, test_mark_word);
+    __ load_prototype_header(tmp, op->array()->as_register(), rscratch1);
+    __ bind(test_mark_word);
+    __ testl(tmp, markWord::null_free_array_bit_in_place);
+  } else {
+    Register tmp_load_klass = LP64_ONLY(rscratch1) NOT_LP64(noreg);
+    Register klass = op->tmp()->as_register();
+    __ load_klass(klass, op->array()->as_register(), tmp_load_klass);
+    __ movl(klass, Address(klass, Klass::layout_helper_offset()));
+    __ testl(klass, Klass::_lh_null_free_array_bit_inplace);
+  }
+}
+
+void LIR_Assembler::emit_opSubstitutabilityCheck(LIR_OpSubstitutabilityCheck* op) {
+  Label L_oops_equal;
+  Label L_oops_not_equal;
+  Label L_end;
+
+  Register left  = op->left()->as_register();
+  Register right = op->right()->as_register();
+
+  __ cmpptr(left, right);
+  __ jcc(Assembler::equal, L_oops_equal);
+
+  // (1) Null check -- if one of the operands is null, the other must not be null (because
+  //     the two references are not equal), so they are not substitutable,
+  //     FIXME: do null check only if the operand is nullable
+  __ testptr(left, right);
+  __ jcc(Assembler::zero, L_oops_not_equal);
+
+  ciKlass* left_klass = op->left_klass();
+  ciKlass* right_klass = op->right_klass();
+
+  // (2) Inline type check -- if either of the operands is not a inline type,
+  //     they are not substitutable. We do this only if we are not sure that the
+  //     operands are inline type
+  if ((left_klass == nullptr || right_klass == nullptr) ||// The klass is still unloaded, or came from a Phi node.
+      !left_klass->is_inlinetype() || !right_klass->is_inlinetype()) {
+    Register tmp1  = op->tmp1()->as_register();
+    __ movptr(tmp1, (intptr_t)markWord::inline_type_pattern);
+    __ andptr(tmp1, Address(left, oopDesc::mark_offset_in_bytes()));
+    __ andptr(tmp1, Address(right, oopDesc::mark_offset_in_bytes()));
+    __ cmpptr(tmp1, (intptr_t)markWord::inline_type_pattern);
+    __ jcc(Assembler::notEqual, L_oops_not_equal);
+  }
+
+  // (3) Same klass check: if the operands are of different klasses, they are not substitutable.
+  if (left_klass != nullptr && left_klass->is_inlinetype() && left_klass == right_klass) {
+    // No need to load klass -- the operands are statically known to be the same inline klass.
+    __ jmp(*op->stub()->entry());
+  } else {
+    Register left_klass_op = op->left_klass_op()->as_register();
+    Register right_klass_op = op->right_klass_op()->as_register();
+
+    if (UseCompressedClassPointers) {
+      __ movl(left_klass_op,  Address(left,  oopDesc::klass_offset_in_bytes()));
+      __ movl(right_klass_op, Address(right, oopDesc::klass_offset_in_bytes()));
+      __ cmpl(left_klass_op, right_klass_op);
+    } else {
+      __ movptr(left_klass_op,  Address(left,  oopDesc::klass_offset_in_bytes()));
+      __ movptr(right_klass_op, Address(right, oopDesc::klass_offset_in_bytes()));
+      __ cmpptr(left_klass_op, right_klass_op);
+    }
+
+    __ jcc(Assembler::equal, *op->stub()->entry()); // same klass -> do slow check
+    // fall through to L_oops_not_equal
+  }
+
+  __ bind(L_oops_not_equal);
+  move(op->not_equal_result(), op->result_opr());
+  __ jmp(L_end);
+
+  __ bind(L_oops_equal);
+  move(op->equal_result(), op->result_opr());
+  __ jmp(L_end);
+
+  // We've returned from the stub. RAX contains 0x0 IFF the two
+  // operands are not substitutable. (Don't compare against 0x1 in case the
+  // C compiler is naughty)
+  __ bind(*op->stub()->continuation());
+  __ cmpl(rax, 0);
+  __ jcc(Assembler::equal, L_oops_not_equal); // (call_stub() == 0x0) -> not_equal
+  move(op->equal_result(), op->result_opr()); // (call_stub() != 0x0) -> equal
+  // fall-through
+  __ bind(L_end);
+}
 
 void LIR_Assembler::emit_compare_and_swap(LIR_OpCompareAndSwap* op) {
   if (LP64_ONLY(false &&) op->code() == lir_cas_long) {
     assert(op->cmp_value()->as_register_lo() == rax, "wrong register");
     assert(op->cmp_value()->as_register_hi() == rdx, "wrong register");
@@ -1985,10 +2148,25 @@
   } else {
     Unimplemented();
   }
 }
 
+void LIR_Assembler::move(LIR_Opr src, LIR_Opr dst) {
+  assert(dst->is_cpu_register(), "must be");
+  assert(dst->type() == src->type(), "must be");
+
+  if (src->is_cpu_register()) {
+    reg2reg(src, dst);
+  } else if (src->is_stack()) {
+    stack2reg(src, dst, dst->type());
+  } else if (src->is_constant()) {
+    const2reg(src, dst, lir_patch_none, nullptr);
+  } else {
+    ShouldNotReachHere();
+  }
+}
+
 void LIR_Assembler::cmove(LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, LIR_Opr result, BasicType type,
                           LIR_Opr cmp_opr1, LIR_Opr cmp_opr2) {
   assert(cmp_opr1 == LIR_OprFact::illegalOpr && cmp_opr2 == LIR_OprFact::illegalOpr, "unnecessary cmp oprs on x86");
 
   Assembler::Condition acond, ncond;
@@ -2862,18 +3040,18 @@
 
 void LIR_Assembler::call(LIR_OpJavaCall* op, relocInfo::relocType rtype) {
   assert((__ offset() + NativeCall::displacement_offset) % BytesPerWord == 0,
          "must be aligned");
   __ call(AddressLiteral(op->addr(), rtype));
-  add_call_info(code_offset(), op->info());
+  add_call_info(code_offset(), op->info(), op->maybe_return_as_fields());
   __ post_call_nop();
 }
 
 
 void LIR_Assembler::ic_call(LIR_OpJavaCall* op) {
   __ ic_call(op->addr());
-  add_call_info(code_offset(), op->info());
+  add_call_info(code_offset(), op->info(), op->maybe_return_as_fields());
   assert((__ offset() - NativeCall::instruction_size + NativeCall::displacement_offset) % BytesPerWord == 0,
          "must be aligned");
   __ post_call_nop();
 }
 
@@ -3050,10 +3228,36 @@
   assert(offset_from_rsp_in_bytes < frame_map()->reserved_argument_area_size(), "invalid offset");
   __ mov_metadata(Address(rsp, offset_from_rsp_in_bytes), m, rscratch1);
 }
 
 
+void LIR_Assembler::arraycopy_inlinetype_check(Register obj, Register tmp, CodeStub* slow_path, bool is_dest, bool null_check) {
+  if (null_check) {
+    __ testptr(obj, obj);
+    __ jcc(Assembler::zero, *slow_path->entry());
+  }
+  if (UseArrayMarkWordCheck) {
+    if (is_dest) {
+      __ test_null_free_array_oop(obj, tmp, *slow_path->entry());
+    } else {
+      __ test_flat_array_oop(obj, tmp, *slow_path->entry());
+    }
+  } else {
+    Register tmp_load_klass = LP64_ONLY(rscratch1) NOT_LP64(noreg);
+    __ load_klass(tmp, obj, tmp_load_klass);
+    __ movl(tmp, Address(tmp, Klass::layout_helper_offset()));
+    if (is_dest) {
+      // Take the slow path if it's a null_free destination array, in case the source array contains nullptrs.
+      __ testl(tmp, Klass::_lh_null_free_array_bit_inplace);
+    } else {
+      __ testl(tmp, Klass::_lh_array_tag_flat_value_bit_inplace);
+    }
+    __ jcc(Assembler::notZero, *slow_path->entry());
+  }
+}
+
+
 // This code replaces a call to arraycopy; no exception may
 // be thrown in this code, they must be thrown in the System.arraycopy
 // activation frame; we could save some checks if this would not be the case
 void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) {
   ciArrayKlass* default_type = op->expected_type();
@@ -3068,10 +3272,16 @@
   CodeStub* stub = op->stub();
   int flags = op->flags();
   BasicType basic_type = default_type != nullptr ? default_type->element_type()->basic_type() : T_ILLEGAL;
   if (is_reference_type(basic_type)) basic_type = T_OBJECT;
 
+  if (flags & LIR_OpArrayCopy::always_slow_path) {
+    __ jmp(*stub->entry());
+    __ bind(*stub->continuation());
+    return;
+  }
+
   // if we don't know anything, just go through the generic arraycopy
   if (default_type == nullptr) {
     // save outgoing arguments on stack in case call to System.arraycopy is needed
     // HACK ALERT. This code used to push the parameters in a hardwired fashion
     // for interpreter calling conventions. Now we have to do it in new style conventions.
@@ -3161,10 +3371,18 @@
 
     __ bind(*stub->continuation());
     return;
   }
 
+  // Handle inline type arrays
+  if (flags & LIR_OpArrayCopy::src_inlinetype_check) {
+    arraycopy_inlinetype_check(src, tmp, stub, false, (flags & LIR_OpArrayCopy::src_null_check));
+  }
+  if (flags & LIR_OpArrayCopy::dst_inlinetype_check) {
+    arraycopy_inlinetype_check(dst, tmp, stub, true, (flags & LIR_OpArrayCopy::dst_null_check));
+  }
+
   assert(default_type != nullptr && default_type->is_array_klass() && default_type->is_loaded(), "must be true at this point");
 
   int elem_size = type2aelembytes(basic_type);
   Address::ScaleFactor scale;
 
@@ -3788,10 +4006,30 @@
     }
   }
   __ bind(next);
 }
 
+void LIR_Assembler::emit_profile_inline_type(LIR_OpProfileInlineType* op) {
+  Register obj = op->obj()->as_register();
+  Register tmp = op->tmp()->as_pointer_register();
+  Address mdo_addr = as_Address(op->mdp()->as_address_ptr());
+  bool not_null = op->not_null();
+  int flag = op->flag();
+
+  Label not_inline_type;
+  if (!not_null) {
+    __ testptr(obj, obj);
+    __ jccb(Assembler::zero, not_inline_type);
+  }
+
+  __ test_oop_is_not_inline_type(obj, tmp, not_inline_type);
+
+  __ orb(mdo_addr, flag);
+
+  __ bind(not_inline_type);
+}
+
 void LIR_Assembler::emit_delay(LIR_OpDelay*) {
   Unimplemented();
 }
 
 
@@ -4051,10 +4289,13 @@
 #else
   __ get_thread(result_reg->as_register());
 #endif // _LP64
 }
 
+void LIR_Assembler::check_orig_pc() {
+  __ cmpptr(frame_map()->address_for_orig_pc_addr(), NULL_WORD);
+}
 
 void LIR_Assembler::peephole(LIR_List*) {
   // do nothing for now
 }
 
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
@@ -49,10 +49,13 @@
     _call_stub_size = NOT_LP64(15) LP64_ONLY(28),
     _exception_handler_size = DEBUG_ONLY(1*K) NOT_DEBUG(175),
     _deopt_handler_size = NOT_LP64(10) LP64_ONLY(17)
   };
 
+  void arraycopy_inlinetype_check(Register obj, Register tmp, CodeStub* slow_path, bool is_dest, bool null_check);
+  void move(LIR_Opr src, LIR_Opr dst);
+
 public:
 
   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);
diff a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp
--- a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp
+++ b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp
@@ -29,10 +29,11 @@
 #include "c1/c1_LIRAssembler.hpp"
 #include "c1/c1_LIRGenerator.hpp"
 #include "c1/c1_Runtime1.hpp"
 #include "c1/c1_ValueStack.hpp"
 #include "ci/ciArray.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "ci/ciObjArrayKlass.hpp"
 #include "ci/ciTypeArrayKlass.hpp"
 #include "gc/shared/c1/barrierSetC1.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/stubRoutines.hpp"
@@ -114,10 +115,23 @@
   set_vreg_flag(reg, LIRGenerator::byte_reg);
   return reg;
 }
 
 
+void LIRGenerator::init_temps_for_substitutability_check(LIR_Opr& tmp1, LIR_Opr& tmp2) {
+  // We just need one 32-bit temp register for x86/x64, to check whether both
+  // oops have markWord::always_locked_pattern. See LIR_Assembler::emit_opSubstitutabilityCheck().
+  // @temp = %r10d
+  // mov $0x405, %r10d
+  // and (%left), %r10d   /* if need to check left */
+  // and (%right), %r10d  /* if need to check right */
+  // cmp $0x405, $r10d
+  // jne L_oops_not_equal
+  tmp1 = new_register(T_INT);
+  tmp2 = LIR_OprFact::illegalOpr;
+}
+
 //--------- loading items into registers --------------------------------
 
 
 // i486 instructions can inline constants
 bool LIRGenerator::can_store_as_constant(Value v, BasicType type) const {
@@ -309,21 +323,32 @@
 
   set_no_result(x);
 
   // "lock" stores the address of the monitor stack slot, so this is not an oop
   LIR_Opr lock = new_register(T_INT);
+  // Need a scratch register for inline types on x86
+  LIR_Opr scratch = LIR_OprFact::illegalOpr;
+  if ((LockingMode == LM_LIGHTWEIGHT) ||
+      (EnableValhalla && x->maybe_inlinetype())) {
+    scratch = new_register(T_ADDRESS);
+  }
 
   CodeEmitInfo* info_for_exception = nullptr;
   if (x->needs_null_check()) {
     info_for_exception = state_for(x);
   }
+
+  CodeStub* throw_imse_stub = x->maybe_inlinetype() ?
+      new SimpleExceptionStub(Runtime1::throw_illegal_monitor_state_exception_id,
+                              LIR_OprFact::illegalOpr, state_for(x))
+    : nullptr;
+
   // this CodeEmitInfo must not have the xhandlers because here the
   // object is already locked (xhandlers expect object to be unlocked)
   CodeEmitInfo* info = state_for(x, x->state(), true);
-  LIR_Opr tmp = LockingMode == LM_LIGHTWEIGHT ? new_register(T_ADDRESS) : LIR_OprFact::illegalOpr;
-  monitor_enter(obj.result(), lock, syncTempOpr(), tmp,
-                        x->monitor_no(), info_for_exception, info);
+  monitor_enter(obj.result(), lock, syncTempOpr(), scratch,
+                x->monitor_no(), info_for_exception, info, throw_imse_stub);
 }
 
 
 void LIRGenerator::do_MonitorExit(MonitorExit* x) {
   assert(x->is_pinned(),"");
@@ -1292,23 +1317,23 @@
 
 
 void LIRGenerator::do_NewInstance(NewInstance* x) {
   print_if_not_loaded(x);
 
-  CodeEmitInfo* info = state_for(x, x->state());
+  CodeEmitInfo* info = state_for(x, x->needs_state_before() ? x->state_before() : x->state());
   LIR_Opr reg = result_register_for(x->type());
   new_instance(reg, x->klass(), x->is_unresolved(),
-                       FrameMap::rcx_oop_opr,
-                       FrameMap::rdi_oop_opr,
-                       FrameMap::rsi_oop_opr,
-                       LIR_OprFact::illegalOpr,
-                       FrameMap::rdx_metadata_opr, info);
+               !x->is_unresolved() && x->klass()->is_inlinetype(),
+               FrameMap::rcx_oop_opr,
+               FrameMap::rdi_oop_opr,
+               FrameMap::rsi_oop_opr,
+               LIR_OprFact::illegalOpr,
+               FrameMap::rdx_metadata_opr, info);
   LIR_Opr result = rlock_result(x);
   __ move(reg, result);
 }
 
-
 void LIRGenerator::do_NewTypeArray(NewTypeArray* x) {
   CodeEmitInfo* info = state_for(x, x->state());
 
   LIRItem length(x->length(), this);
   length.load_item_force(FrameMap::rbx_opr);
@@ -1323,11 +1348,11 @@
   BasicType elem_type = x->elt_type();
 
   __ metadata2reg(ciTypeArrayKlass::make(elem_type)->constant_encoding(), klass_reg);
 
   CodeStub* slow_path = new NewTypeArrayStub(klass_reg, len, reg, info);
-  __ allocate_array(reg, len, tmp1, tmp2, tmp3, tmp4, elem_type, klass_reg, slow_path);
+  __ allocate_array(reg, len, tmp1, tmp2, tmp3, tmp4, elem_type, klass_reg, slow_path, false);
 
   LIR_Opr result = rlock_result(x);
   __ move(reg, result);
 }
 
@@ -1351,17 +1376,17 @@
   LIR_Opr klass_reg = FrameMap::rdx_metadata_opr;
 
   length.load_item_force(FrameMap::rbx_opr);
   LIR_Opr len = length.result();
 
-  CodeStub* slow_path = new NewObjectArrayStub(klass_reg, len, reg, info);
-  ciKlass* obj = (ciKlass*) ciObjArrayKlass::make(x->klass());
+  ciKlass* obj = (ciKlass*) x->exact_type();
+  CodeStub* slow_path = new NewObjectArrayStub(klass_reg, len, reg, info, x->is_null_free());
   if (obj == ciEnv::unloaded_ciobjarrayklass()) {
     BAILOUT("encountered unloaded_ciobjarrayklass due to out of memory error");
   }
   klass2reg_with_patching(klass_reg, obj, patching_info);
-  __ allocate_array(reg, len, tmp1, tmp2, tmp3, tmp4, T_OBJECT, klass_reg, slow_path);
+  __ allocate_array(reg, len, tmp1, tmp2, tmp3, tmp4, T_OBJECT, klass_reg, slow_path, x->is_null_free());
 
   LIR_Opr result = rlock_result(x);
   __ move(reg, result);
 }
 
@@ -1436,10 +1461,14 @@
   // info for exceptions
   CodeEmitInfo* info_for_exception =
       (x->needs_exception_state() ? state_for(x) :
                                     state_for(x, x->state_before(), true /*ignore_xhandler*/));
 
+  if (x->is_null_free()) {
+    __ null_check(obj.result(), new CodeEmitInfo(info_for_exception));
+  }
+
   CodeStub* stub;
   if (x->is_incompatible_class_change_check()) {
     assert(patching_info == nullptr, "can't patch this");
     stub = new SimpleExceptionStub(Runtime1::throw_incompatible_class_change_error_id, LIR_OprFact::illegalOpr, info_for_exception);
   } else if (x->is_invokespecial_receiver_check()) {
@@ -1454,11 +1483,11 @@
     tmp3 = new_register(objectType);
   }
   __ checkcast(reg, obj.result(), x->klass(),
                new_register(objectType), new_register(objectType), tmp3,
                x->direct_compare(), info_for_exception, patching_info, stub,
-               x->profiled_method(), x->profiled_bci());
+               x->profiled_method(), x->profiled_bci(), x->is_null_free());
 }
 
 
 void LIRGenerator::do_InstanceOf(InstanceOf* x) {
   LIRItem obj(x->obj(), this);
@@ -1505,11 +1534,11 @@
   }
   xin->load_item();
   if (tag == longTag && yin->is_constant() && yin->get_jlong_constant() == 0 && (cond == If::eql || cond == If::neq)) {
     // inline long zero
     yin->dont_load_item();
-  } else if (tag == longTag || tag == floatTag || tag == doubleTag) {
+  } else if (tag == longTag || tag == floatTag || tag == doubleTag || x->substitutability_check()) {
     // longs cannot handle constants at right side
     yin->load_item();
   } else {
     yin->dont_load_item();
   }
@@ -1525,11 +1554,15 @@
     increment_backedge_counter_conditionally(lir_cond(cond), left, right, state_for(x, x->state_before()),
         x->tsux()->bci(), x->fsux()->bci(), x->profiled_bci());
     __ safepoint(safepoint_poll_register(), state_for(x, x->state_before()));
   }
 
-  __ cmp(lir_cond(cond), left, right);
+  if (x->substitutability_check()) {
+    substitutability_check(x, *xin, *yin);
+  } else {
+    __ cmp(lir_cond(cond), left, right);
+  }
   // Generate branch profiling. Profiling code doesn't kill flags.
   profile_branch(x, cond);
   move_to_phi(x->state());
   if (x->x()->type()->is_float_kind()) {
     __ branch(lir_cond(cond), x->tsux(), x->usux());
diff a/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp
--- a/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp
@@ -32,10 +32,11 @@
 #include "gc/shared/tlab_globals.hpp"
 #include "interpreter/interpreter.hpp"
 #include "oops/arrayOop.hpp"
 #include "oops/markWord.hpp"
 #include "runtime/basicLock.hpp"
+#include "runtime/frame.inline.hpp"
 #include "runtime/os.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/stubRoutines.hpp"
 #include "utilities/checkedCast.hpp"
 
@@ -73,10 +74,14 @@
     lightweight_lock(obj, hdr, thread, tmp, slow_case);
   } else  if (LockingMode == LM_LEGACY) {
     Label done;
     // and mark it as unlocked
     orptr(hdr, markWord::unlocked_value);
+    if (EnableValhalla) {
+      // Mask inline_type bit such that we go to the slow path if object is an inline type
+      andptr(hdr, ~((int) markWord::inline_type_bit_in_place));
+    }
     // save unlocked object header into the displaced header location on the stack
     movptr(Address(disp_hdr, 0), hdr);
     // test if object header is still the same (i.e. unlocked), and if so, store the
     // displaced header address in the object header - if it is not the same, get the
     // object header instead
@@ -163,11 +168,19 @@
 }
 
 
 void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register len, Register t1, Register t2) {
   assert_different_registers(obj, klass, len);
-  movptr(Address(obj, oopDesc::mark_offset_in_bytes()), checked_cast<int32_t>(markWord::prototype().value()));
+  if (EnableValhalla) {
+    // Need to copy markWord::prototype header for klass
+    assert_different_registers(obj, klass, len, t1, t2);
+    movptr(t1, Address(klass, Klass::prototype_header_offset()));
+    movptr(Address(obj, oopDesc::mark_offset_in_bytes()), t1);
+  } else {
+    // This assumes that all prototype bits fit in an int32_t
+    movptr(Address(obj, oopDesc::mark_offset_in_bytes()), checked_cast<int32_t>(markWord::prototype().value()));
+  }
 #ifdef _LP64
   if (UseCompressedClassPointers) { // Take care not to kill klass
     movptr(t1, klass);
     encode_klass_not_null(t1, rscratch1);
     movl(Address(obj, oopDesc::klass_offset_in_bytes()), t1);
@@ -316,44 +329,56 @@
           RuntimeAddress(SharedRuntime::get_ic_miss_stub()));
   const int ic_cmp_size = LP64_ONLY(10) NOT_LP64(9);
   assert(UseCompressedClassPointers || offset() - start_offset == ic_cmp_size, "check alignment in emit_method_entry");
 }
 
+void C1_MacroAssembler::build_frame_helper(int frame_size_in_bytes, int sp_offset_for_orig_pc, int sp_inc, bool reset_orig_pc, bool needs_stack_repair) {
+  push(rbp);
+  if (PreserveFramePointer) {
+    mov(rbp, rsp);
+  }
+#if !defined(_LP64) && defined(COMPILER2)
+  if (UseSSE < 2 && !CompilerConfig::is_c1_only_no_jvmci()) {
+      // c2 leaves fpu stack dirty. Clean it on entry
+      empty_FPU_stack();
+    }
+#endif // !_LP64 && COMPILER2
+  decrement(rsp, frame_size_in_bytes);
 
-void C1_MacroAssembler::build_frame(int frame_size_in_bytes, int bang_size_in_bytes) {
-  assert(bang_size_in_bytes >= frame_size_in_bytes, "stack bang size incorrect");
+  if (needs_stack_repair) {
+    // Save stack increment (also account for fixed framesize and rbp)
+    assert((sp_inc & (StackAlignmentInBytes-1)) == 0, "stack increment not aligned");
+    int real_frame_size = sp_inc + frame_size_in_bytes + wordSize;
+    movptr(Address(rsp, frame_size_in_bytes - wordSize), real_frame_size);
+  }
+  if (reset_orig_pc) {
+    // Zero orig_pc to detect deoptimization during buffering in the entry points
+    movptr(Address(rsp, sp_offset_for_orig_pc), 0);
+  }
+}
+
+void C1_MacroAssembler::build_frame(int frame_size_in_bytes, int bang_size_in_bytes, int sp_offset_for_orig_pc, bool needs_stack_repair, bool has_scalarized_args, Label* verified_inline_entry_label) {
   // Make sure there is enough stack space for this method's activation.
   // Note that we do this before doing an enter(). This matches the
   // ordering of C2's stack overflow check / rsp decrement and allows
   // the SharedRuntime stack overflow handling to be consistent
   // between the two compilers.
+  assert(bang_size_in_bytes >= frame_size_in_bytes, "stack bang size incorrect");
   generate_stack_overflow_check(bang_size_in_bytes);
 
-  push(rbp);
-  if (PreserveFramePointer) {
-    mov(rbp, rsp);
-  }
-#if !defined(_LP64) && defined(COMPILER2)
-  if (UseSSE < 2 && !CompilerConfig::is_c1_only_no_jvmci()) {
-    // c2 leaves fpu stack dirty. Clean it on entry
-    empty_FPU_stack();
-  }
-#endif // !_LP64 && COMPILER2
-  decrement(rsp, frame_size_in_bytes); // does not emit code for frame_size == 0
+  build_frame_helper(frame_size_in_bytes, sp_offset_for_orig_pc, 0, has_scalarized_args, needs_stack_repair);
 
   BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
   // C1 code is not hot enough to micro optimize the nmethod entry barrier with an out-of-line stub
   bs->nmethod_entry_barrier(this, nullptr /* slow_path */, nullptr /* continuation */);
-}
 
-
-void C1_MacroAssembler::remove_frame(int frame_size_in_bytes) {
-  increment(rsp, frame_size_in_bytes);  // Does not emit code for frame_size == 0
-  pop(rbp);
+  if (verified_inline_entry_label != nullptr) {
+    // Jump here from the scalarized entry points that already created the frame.
+    bind(*verified_inline_entry_label);
+  }
 }
 
-
 void C1_MacroAssembler::verified_entry(bool breakAtEntry) {
   if (breakAtEntry || VerifyFPU) {
     // Verified Entry first instruction should be 5 bytes long for correct
     // patching by patch_verified_entry().
     //
@@ -366,10 +391,68 @@
   if (breakAtEntry) int3();
   // build frame
   IA32_ONLY( verify_FPU(0, "method_entry"); )
 }
 
+int C1_MacroAssembler::scalarized_entry(const CompiledEntrySignature* ces, int frame_size_in_bytes, int bang_size_in_bytes, int sp_offset_for_orig_pc, Label& verified_inline_entry_label, bool is_inline_ro_entry) {
+  assert(InlineTypePassFieldsAsArgs, "sanity");
+  // Make sure there is enough stack space for this method's activation.
+  assert(bang_size_in_bytes >= frame_size_in_bytes, "stack bang size incorrect");
+  generate_stack_overflow_check(bang_size_in_bytes);
+
+  GrowableArray<SigEntry>* sig    = ces->sig();
+  GrowableArray<SigEntry>* sig_cc = is_inline_ro_entry ? ces->sig_cc_ro() : ces->sig_cc();
+  VMRegPair* regs      = ces->regs();
+  VMRegPair* regs_cc   = is_inline_ro_entry ? ces->regs_cc_ro() : ces->regs_cc();
+  int args_on_stack    = ces->args_on_stack();
+  int args_on_stack_cc = is_inline_ro_entry ? ces->args_on_stack_cc_ro() : ces->args_on_stack_cc();
+
+  assert(sig->length() <= sig_cc->length(), "Zero-sized inline class not allowed!");
+  BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, sig_cc->length());
+  int args_passed = sig->length();
+  int args_passed_cc = SigEntry::fill_sig_bt(sig_cc, sig_bt);
+
+  // Create a temp frame so we can call into the runtime. It must be properly set up to accommodate GC.
+  build_frame_helper(frame_size_in_bytes, sp_offset_for_orig_pc, 0, true, ces->c1_needs_stack_repair());
+
+  // The runtime call might safepoint, make sure nmethod entry barrier is executed
+  BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
+  // C1 code is not hot enough to micro optimize the nmethod entry barrier with an out-of-line stub
+  bs->nmethod_entry_barrier(this, nullptr /* slow_path */, nullptr /* continuation */);
+
+  // FIXME -- call runtime only if we cannot in-line allocate all the incoming inline type args.
+  movptr(rbx, (intptr_t)(ces->method()));
+  if (is_inline_ro_entry) {
+    call(RuntimeAddress(Runtime1::entry_for(Runtime1::buffer_inline_args_no_receiver_id)));
+  } else {
+    call(RuntimeAddress(Runtime1::entry_for(Runtime1::buffer_inline_args_id)));
+  }
+  int rt_call_offset = offset();
+
+  // Remove the temp frame
+  addptr(rsp, frame_size_in_bytes);
+  pop(rbp);
+
+  // Check if we need to extend the stack for packing
+  int sp_inc = 0;
+  if (args_on_stack > args_on_stack_cc) {
+    sp_inc = extend_stack_for_inline_args(args_on_stack);
+  }
+
+  shuffle_inline_args(true, is_inline_ro_entry, sig_cc,
+                      args_passed_cc, args_on_stack_cc, regs_cc, // from
+                      args_passed, args_on_stack, regs,          // to
+                      sp_inc, rax);
+
+  // Create the real frame. Below jump will then skip over the stack banging and frame
+  // setup code in the verified_inline_entry (which has a different real_frame_size).
+  build_frame_helper(frame_size_in_bytes, sp_offset_for_orig_pc, sp_inc, false, ces->c1_needs_stack_repair());
+
+  jmp(verified_inline_entry_label);
+  return rt_call_offset;
+}
+
 void C1_MacroAssembler::load_parameter(int offset_in_words, Register reg) {
   // rbp, + 0: link
   //     + 1: return address
   //     + 2: argument with offset 0
   //     + 3: argument with offset 1
diff a/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp b/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp
--- a/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp
+++ b/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp
@@ -1034,11 +1034,12 @@
           __ set_info("fast new_instance init check", dont_gc_arguments);
         }
 
         __ enter();
         OopMap* map = save_live_registers(sasm, 2);
-        int call_offset = __ call_RT(obj, noreg, CAST_FROM_FN_PTR(address, new_instance), klass);
+        int call_offset;
+        call_offset = __ call_RT(obj, noreg, CAST_FROM_FN_PTR(address, new_instance), klass);
         oop_maps = new OopMapSet();
         oop_maps->add_gc_map(call_offset, map);
         restore_live_registers_except_rax(sasm);
         __ verify_oop(obj);
         __ leave();
@@ -1067,46 +1068,70 @@
       }
       break;
 
     case new_type_array_id:
     case new_object_array_id:
+    case new_flat_array_id:
       {
         Register length   = rbx; // Incoming
         Register klass    = rdx; // Incoming
         Register obj      = rax; // Result
 
         if (id == new_type_array_id) {
           __ set_info("new_type_array", dont_gc_arguments);
-        } else {
+        } else if (id == new_object_array_id) {
           __ set_info("new_object_array", dont_gc_arguments);
+        } else {
+          __ set_info("new_flat_array", dont_gc_arguments);
         }
 
 #ifdef ASSERT
         // assert object type is really an array of the proper kind
         {
           Label ok;
           Register t0 = obj;
           __ movl(t0, Address(klass, Klass::layout_helper_offset()));
           __ sarl(t0, Klass::_lh_array_tag_shift);
-          int tag = ((id == new_type_array_id)
-                     ? Klass::_lh_array_tag_type_value
-                     : Klass::_lh_array_tag_obj_value);
-          __ cmpl(t0, tag);
-          __ jcc(Assembler::equal, ok);
-          __ stop("assert(is an array klass)");
+          switch (id) {
+          case new_type_array_id:
+            __ cmpl(t0, Klass::_lh_array_tag_type_value);
+            __ jcc(Assembler::equal, ok);
+            __ stop("assert(is a type array klass)");
+            break;
+          case new_object_array_id:
+            __ cmpl(t0, Klass::_lh_array_tag_obj_value); // new "[Ljava/lang/Object;"
+            __ jcc(Assembler::equal, ok);
+            __ cmpl(t0, Klass::_lh_array_tag_vt_value);  // new "[LVT;"
+            __ jcc(Assembler::equal, ok);
+            __ stop("assert(is an object or inline type array klass)");
+            break;
+          case new_flat_array_id:
+            // TODO 8325106 Fix comment
+            // new "[QVT;"
+            __ cmpl(t0, Klass::_lh_array_tag_vt_value);  // the array can be a flat array.
+            __ jcc(Assembler::equal, ok);
+            __ cmpl(t0, Klass::_lh_array_tag_obj_value); // the array cannot be a flat array (due to InlineArrayElementMaxFlatSize, etc)
+            __ jcc(Assembler::equal, ok);
+            __ stop("assert(is an object or inline type array klass)");
+            break;
+          default:  ShouldNotReachHere();
+          }
           __ should_not_reach_here();
           __ bind(ok);
         }
 #endif // ASSERT
 
         __ enter();
         OopMap* map = save_live_registers(sasm, 3);
         int call_offset;
         if (id == new_type_array_id) {
           call_offset = __ call_RT(obj, noreg, CAST_FROM_FN_PTR(address, new_type_array), klass, length);
-        } else {
+        } else if (id == new_object_array_id) {
           call_offset = __ call_RT(obj, noreg, CAST_FROM_FN_PTR(address, new_object_array), klass, length);
+        } else {
+          assert(id == new_flat_array_id, "must be");
+          call_offset = __ call_RT(obj, noreg, CAST_FROM_FN_PTR(address, new_flat_array), klass, length);
         }
 
         oop_maps = new OopMapSet();
         oop_maps->add_gc_map(call_offset, map);
         restore_live_registers_except_rax(sasm);
@@ -1134,10 +1159,87 @@
         // rax,: new multi array
         __ verify_oop(rax);
       }
       break;
 
+    case load_flat_array_id:
+      {
+        StubFrame f(sasm, "load_flat_array", dont_gc_arguments);
+        OopMap* map = save_live_registers(sasm, 3);
+
+        // Called with store_parameter and not C abi
+
+        f.load_argument(1, rax); // rax,: array
+        f.load_argument(0, rbx); // rbx,: index
+        int call_offset = __ call_RT(rax, noreg, CAST_FROM_FN_PTR(address, load_flat_array), rax, rbx);
+
+        oop_maps = new OopMapSet();
+        oop_maps->add_gc_map(call_offset, map);
+        restore_live_registers_except_rax(sasm);
+
+        // rax,: loaded element at array[index]
+        __ verify_oop(rax);
+      }
+      break;
+
+    case store_flat_array_id:
+      {
+        StubFrame f(sasm, "store_flat_array", dont_gc_arguments);
+        OopMap* map = save_live_registers(sasm, 4);
+
+        // Called with store_parameter and not C abi
+
+        f.load_argument(2, rax); // rax,: array
+        f.load_argument(1, rbx); // rbx,: index
+        f.load_argument(0, rcx); // rcx,: value
+        int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, store_flat_array), rax, rbx, rcx);
+
+        oop_maps = new OopMapSet();
+        oop_maps->add_gc_map(call_offset, map);
+        restore_live_registers_except_rax(sasm);
+      }
+      break;
+
+    case substitutability_check_id:
+      {
+        StubFrame f(sasm, "substitutability_check", dont_gc_arguments);
+        OopMap* map = save_live_registers(sasm, 3);
+
+        // Called with store_parameter and not C abi
+
+        f.load_argument(1, rax); // rax,: left
+        f.load_argument(0, rbx); // rbx,: right
+        int call_offset = __ call_RT(noreg, noreg, CAST_FROM_FN_PTR(address, substitutability_check), rax, rbx);
+
+        oop_maps = new OopMapSet();
+        oop_maps->add_gc_map(call_offset, map);
+        restore_live_registers_except_rax(sasm);
+
+        // rax,: are the two operands substitutable
+      }
+      break;
+
+
+    case buffer_inline_args_id:
+    case buffer_inline_args_no_receiver_id:
+      {
+        const char* name = (id == buffer_inline_args_id) ?
+          "buffer_inline_args" : "buffer_inline_args_no_receiver";
+        StubFrame f(sasm, name, dont_gc_arguments);
+        OopMap* map = save_live_registers(sasm, 2);
+        Register method = rbx;
+        address entry = (id == buffer_inline_args_id) ?
+          CAST_FROM_FN_PTR(address, buffer_inline_args) :
+          CAST_FROM_FN_PTR(address, buffer_inline_args_no_receiver);
+        int call_offset = __ call_RT(rax, noreg, entry, method);
+        oop_maps = new OopMapSet();
+        oop_maps->add_gc_map(call_offset, map);
+        restore_live_registers_except_rax(sasm);
+        __ verify_oop(rax);  // rax: an array of buffered value objects
+      }
+      break;
+
     case register_finalizer_id:
       {
         __ set_info("register_finalizer", dont_gc_arguments);
 
         // This is called via call_runtime so the arguments
@@ -1235,15 +1337,21 @@
         oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_class_cast_exception), true);
       }
       break;
 
     case throw_incompatible_class_change_error_id:
-      { StubFrame f(sasm, "throw_incompatible_class_cast_exception", dont_gc_arguments);
+      { StubFrame f(sasm, "throw_incompatible_class_change_error", dont_gc_arguments);
         oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_incompatible_class_change_error), false);
       }
       break;
 
+    case throw_illegal_monitor_state_exception_id:
+      { StubFrame f(sasm, "throw_illegal_monitor_state_exception", dont_gc_arguments);
+        oop_maps = generate_exception_throw(sasm, CAST_FROM_FN_PTR(address, throw_illegal_monitor_state_exception), false);
+      }
+      break;
+
     case slow_subtype_check_id:
       {
         // Typical calling sequence:
         // __ push(klass_RInfo);  // object klass or other subclass
         // __ push(sup_k_RInfo);  // array element klass or other superclass
diff a/src/hotspot/cpu/x86/c1_globals_x86.hpp b/src/hotspot/cpu/x86/c1_globals_x86.hpp
--- a/src/hotspot/cpu/x86/c1_globals_x86.hpp
+++ b/src/hotspot/cpu/x86/c1_globals_x86.hpp
@@ -35,11 +35,11 @@
 define_pd_global(bool, BackgroundCompilation,          true );
 define_pd_global(bool, InlineIntrinsics,               true );
 define_pd_global(bool, PreferInterpreterNativeStubs,   false);
 define_pd_global(bool, ProfileTraps,                   false);
 define_pd_global(bool, UseOnStackReplacement,          true );
-define_pd_global(bool, TieredCompilation,              false);
+define_pd_global(bool, TieredCompilation,              true);
 define_pd_global(intx, CompileThreshold,               1500 );
 
 define_pd_global(intx,   OnStackReplacePercentage,     933  );
 define_pd_global(size_t, NewSizeThreadIncrease,        4*K  );
 define_pd_global(uintx,  InitialCodeCacheSize,         160*K);
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
@@ -44,11 +44,30 @@
 #define BLOCK_COMMENT(str) block_comment(str)
 #define STOP(error) block_comment(error); stop(error)
 #endif
 
 // C2 compiled method's prolog code.
-void C2_MacroAssembler::verified_entry(int framesize, int stack_bang_size, bool fp_mode_24b, bool is_stub) {
+void C2_MacroAssembler::verified_entry(Compile* C, int sp_inc) {
+  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");
+
+    Label L_skip_barrier;
+    Register klass = rscratch1;
+
+    mov_metadata(klass, C->method()->holder()->constant_encoding());
+    clinit_barrier(klass, r15_thread, &L_skip_barrier /*L_fast_path*/);
+
+    jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); // slow path
+
+    bind(L_skip_barrier);
+  }
+
+  int framesize = C->output()->frame_size_in_bytes();
+  int bangsize = C->output()->bang_size_in_bytes();
+  bool fp_mode_24b = false;
+  int stack_bang_size = C->output()->need_stack_bang(bangsize) ? bangsize : 0;
 
   // WARNING: Initial instruction MUST be 5 bytes or longer so that
   // NativeJump::patch_verified_entry will be able to patch out the entry
   // code safely. The push to verify stack depth is ok at 5 bytes,
   // the frame allocation can be either 3 or 6 bytes. So if we don't do
@@ -97,10 +116,16 @@
         addptr(rbp, framesize);
       }
     }
   }
 
+  if (C->needs_stack_repair()) {
+    // Save stack increment just below the saved rbp (also account for fixed framesize and rbp)
+    assert((sp_inc & (StackAlignmentInBytes-1)) == 0, "stack increment not aligned");
+    movptr(Address(rsp, framesize - wordSize), sp_inc + framesize + wordSize);
+  }
+
   if (VerifyStackAtCalls) { // Majik cookie to verify stack depth
     framesize -= wordSize;
     movptr(Address(rsp, framesize), (int32_t)0xbadb100d);
   }
 
@@ -125,34 +150,34 @@
     jcc(Assembler::equal, L);
     STOP("Stack is not properly aligned!");
     bind(L);
   }
 #endif
+}
 
-  if (!is_stub) {
-    BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
- #ifdef _LP64
-    if (BarrierSet::barrier_set()->barrier_set_nmethod() != nullptr) {
-      // We put the non-hot code of the nmethod entry barrier out-of-line in a stub.
-      Label dummy_slow_path;
-      Label dummy_continuation;
-      Label* slow_path = &dummy_slow_path;
-      Label* continuation = &dummy_continuation;
-      if (!Compile::current()->output()->in_scratch_emit_size()) {
-        // Use real labels from actual stub when not emitting code for the purpose of measuring its size
-        C2EntryBarrierStub* stub = new (Compile::current()->comp_arena()) C2EntryBarrierStub();
-        Compile::current()->output()->add_stub(stub);
-        slow_path = &stub->entry();
-        continuation = &stub->continuation();
-      }
-      bs->nmethod_entry_barrier(this, slow_path, continuation);
+void C2_MacroAssembler::entry_barrier() {
+  BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
+#ifdef _LP64
+  if (BarrierSet::barrier_set()->barrier_set_nmethod() != nullptr) {
+    // We put the non-hot code of the nmethod entry barrier out-of-line in a stub.
+    Label dummy_slow_path;
+    Label dummy_continuation;
+    Label* slow_path = &dummy_slow_path;
+    Label* continuation = &dummy_continuation;
+    if (!Compile::current()->output()->in_scratch_emit_size()) {
+      // Use real labels from actual stub when not emitting code for the purpose of measuring its size
+      C2EntryBarrierStub* stub = new (Compile::current()->comp_arena()) C2EntryBarrierStub();
+      Compile::current()->output()->add_stub(stub);
+      slow_path = &stub->entry();
+      continuation = &stub->continuation();
     }
+    bs->nmethod_entry_barrier(this, slow_path, continuation);
+  }
 #else
-    // Don't bother with out-of-line nmethod entry barrier stub for x86_32.
-    bs->nmethod_entry_barrier(this, nullptr /* slow_path */, nullptr /* continuation */);
+  // Don't bother with out-of-line nmethod entry barrier stub for x86_32.
+  bs->nmethod_entry_barrier(this, nullptr /* slow_path */, nullptr /* continuation */);
 #endif
-  }
 }
 
 inline Assembler::AvxVectorLen C2_MacroAssembler::vector_length_encoding(int vlen_in_bytes) {
   switch (vlen_in_bytes) {
     case  4: // fall-through
@@ -606,10 +631,14 @@
     // Clear ZF so that we take the slow path at the DONE label. objReg is known to be not 0.
     testptr(objReg, objReg);
   } else if (LockingMode == LM_LEGACY) {
     // Attempt stack-locking ...
     orptr (tmpReg, markWord::unlocked_value);
+    if (EnableValhalla) {
+      // Mask inline_type bit such that we go to the slow path if object is an inline type
+      andptr(tmpReg, ~((int) markWord::inline_type_bit_in_place));
+    }
     movptr(Address(boxReg, 0), tmpReg);          // Anticipate successful CAS
     lock();
     cmpxchgptr(boxReg, Address(objReg, oopDesc::mark_offset_in_bytes()));      // Updates tmpReg
     jcc(Assembler::equal, COUNT);           // Success
 
diff a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp
--- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp
+++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp
@@ -27,12 +27,13 @@
 
 // C2_MacroAssembler contains high-level macros for C2
 
 public:
   // C2 compiled method's prolog code.
-  void verified_entry(int framesize, int stack_bang_size, bool fp_mode_24b, bool is_stub);
+  void verified_entry(Compile* C, int sp_inc = 0);
 
+  void entry_barrier();
   Assembler::AvxVectorLen vector_length_encoding(int vlen_in_bytes);
 
   // Code used by cmpFastLock and cmpFastUnlock mach instructions in .ad file.
   // See full description in macroAssembler_x86.cpp.
   void fast_lock(Register obj, Register box, Register tmp,
diff a/src/hotspot/cpu/x86/frame_x86.cpp b/src/hotspot/cpu/x86/frame_x86.cpp
--- a/src/hotspot/cpu/x86/frame_x86.cpp
+++ b/src/hotspot/cpu/x86/frame_x86.cpp
@@ -144,17 +144,20 @@
       sender_sp = _unextended_sp + _cb->frame_size();
       // Is sender_sp safe?
       if (!thread->is_in_full_stack_checked((address)sender_sp)) {
         return false;
       }
-      sender_unextended_sp = sender_sp;
       // On Intel the return_address is always the word on the stack
       sender_pc = (address) *(sender_sp-1);
       // Note: frame::sender_sp_offset is only valid for compiled frame
-      saved_fp = (intptr_t*) *(sender_sp - frame::sender_sp_offset);
-    }
+      intptr_t** saved_fp_addr = (intptr_t**) (sender_sp - frame::sender_sp_offset);
+      saved_fp = *saved_fp_addr;
 
+      // Repair the sender sp if this is a method with scalarized inline type args
+      sender_sp = repair_sender_sp(sender_sp, saved_fp_addr);
+      sender_unextended_sp = sender_sp;
+    }
     if (Continuation::is_return_barrier_entry(sender_pc)) {
       // If our sender_pc is the return barrier, then our "real" sender is the continuation entry
       frame s = Continuation::continuation_bottom_sender(thread, *this, sender_sp);
       sender_sp = s.sp();
       sender_pc = s.pc();
@@ -660,10 +663,25 @@
   init((intptr_t*)sp, (intptr_t*)fp, (address)pc);
 }
 
 #endif
 
+// Check for a method with scalarized inline type arguments that needs
+// a stack repair and return the repaired sender stack pointer.
+intptr_t* frame::repair_sender_sp(intptr_t* sender_sp, intptr_t** saved_fp_addr) const {
+  CompiledMethod* cm = _cb->as_compiled_method_or_null();
+  if (cm != nullptr && cm->needs_stack_repair()) {
+    // The stack increment resides just below the saved rbp on the stack
+    // and does not account for the return address.
+    intptr_t* real_frame_size_addr = (intptr_t*) (saved_fp_addr - 1);
+    int real_frame_size = ((*real_frame_size_addr) + wordSize) / wordSize;
+    assert(real_frame_size >= _cb->frame_size() && real_frame_size <= 1000000, "invalid frame size");
+    sender_sp = unextended_sp() + real_frame_size;
+  }
+  return sender_sp;
+}
+
 void JavaFrameAnchor::make_walkable() {
   // last frame set?
   if (last_Java_sp() == nullptr) return;
   // already walkable?
   if (walkable()) return;
diff a/src/hotspot/cpu/x86/frame_x86.hpp b/src/hotspot/cpu/x86/frame_x86.hpp
--- a/src/hotspot/cpu/x86/frame_x86.hpp
+++ b/src/hotspot/cpu/x86/frame_x86.hpp
@@ -142,10 +142,13 @@
 
   intptr_t* ptr_at_addr(int offset) const {
     return (intptr_t*) addr_at(offset);
   }
 
+  // Support for scalarized inline type calling convention
+  intptr_t* repair_sender_sp(intptr_t* sender_sp, intptr_t** saved_fp_addr) const;
+
 #ifdef ASSERT
   // Used in frame::sender_for_{interpreter,compiled}_frame
   static void verify_deopt_original_pc(CompiledMethod* nm, intptr_t* unextended_sp);
 #endif
 
diff a/src/hotspot/cpu/x86/frame_x86.inline.hpp b/src/hotspot/cpu/x86/frame_x86.inline.hpp
--- a/src/hotspot/cpu/x86/frame_x86.inline.hpp
+++ b/src/hotspot/cpu/x86/frame_x86.inline.hpp
@@ -30,10 +30,13 @@
 #include "code/vmreg.inline.hpp"
 #include "compiler/oopMap.inline.hpp"
 #include "interpreter/interpreter.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/registerMap.hpp"
+#ifdef COMPILER1
+#include "c1/c1_Runtime1.hpp"
+#endif
 
 // Inline functions for Intel frames:
 
 // Constructors:
 
@@ -383,24 +386,59 @@
   // frame owned by optimizing compiler
   assert(_cb->frame_size() > 0, "must have non-zero frame size");
   intptr_t* sender_sp = unextended_sp() + _cb->frame_size();
   assert(sender_sp == real_fp(), "");
 
-  // On Intel the return_address is always the word on the stack
-  address sender_pc = (address) *(sender_sp-1);
+#ifdef ASSERT
+  address sender_pc_copy = (address) *(sender_sp-1);
+#endif
 
   // This is the saved value of EBP which may or may not really be an FP.
   // It is only an FP if the sender is an interpreter frame (or C1?).
   // saved_fp_addr should be correct even for a bottom thawed frame (with a return barrier)
   intptr_t** saved_fp_addr = (intptr_t**) (sender_sp - frame::sender_sp_offset);
 
+  // Repair the sender sp if the frame has been extended
+  sender_sp = repair_sender_sp(sender_sp, saved_fp_addr);
+
+  // On Intel the return_address is always the word on the stack
+  address sender_pc = (address) *(sender_sp-1);
+
+#ifdef ASSERT
+  if (sender_pc != sender_pc_copy) {
+    // When extending the stack in the callee method entry to make room for unpacking of value
+    // type args, we keep a copy of the sender pc at the expected location in the callee frame.
+    // If the sender pc is patched due to deoptimization, the copy is not consistent anymore.
+    nmethod* nm = CodeCache::find_blob(sender_pc)->as_nmethod();
+    assert(sender_pc == nm->deopt_mh_handler_begin() || sender_pc == nm->deopt_handler_begin(), "unexpected sender pc");
+  }
+#endif
+
   if (map->update_map()) {
     // Tell GC to use argument oopmaps for some runtime stubs that need it.
     // For C1, the runtime stub might not have oop maps, so set this flag
     // outside of update_register_map.
-    if (!_cb->is_compiled()) { // compiled frames do not use callee-saved registers
-      map->set_include_argument_oops(_cb->caller_must_gc_arguments(map->thread()));
+    bool c1_buffering = false;
+#ifdef COMPILER1
+    nmethod* nm = _cb->as_nmethod_or_null();
+    if (nm != nullptr && nm->is_compiled_by_c1() && nm->method()->has_scalarized_args() &&
+        pc() < nm->verified_inline_entry_point()) {
+      // The VEP and VIEP(RO) of C1-compiled methods call buffer_inline_args_xxx
+      // before doing any argument shuffling, so we need to scan the oops
+      // as the caller passes them.
+      c1_buffering = true;
+#ifdef ASSERT
+      NativeCall* call = nativeCall_before(pc());
+      address dest = call->destination();
+      assert(dest == Runtime1::entry_for(Runtime1::buffer_inline_args_no_receiver_id) ||
+             dest == Runtime1::entry_for(Runtime1::buffer_inline_args_id), "unexpected safepoint in entry point");
+#endif
+    }
+#endif
+    if (!_cb->is_compiled() || c1_buffering) { // compiled frames do not use callee-saved registers
+      bool caller_args = _cb->caller_must_gc_arguments(map->thread()) || c1_buffering;
+      map->set_include_argument_oops(caller_args);
       if (oop_map() != nullptr) {
         _oop_map->update_register_map(this, map);
       }
     } else {
       assert(!_cb->caller_must_gc_arguments(map->thread()), "");
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
@@ -208,11 +208,10 @@
   __ jcc(Assembler::equal, done);
 
   // Can we store original value in the thread's buffer?
   // Is index == 0?
   // (The index field is typed as size_t.)
-
   __ movptr(tmp, index);                   // tmp := *index_adr
   __ cmpptr(tmp, 0);                       // tmp == 0?
   __ jcc(Assembler::equal, runtime);       // If yes, goto runtime
 
   __ subptr(tmp, wordSize);                // tmp := tmp - wordSize
@@ -223,12 +222,28 @@
   __ movptr(Address(tmp, 0), pre_val);
   __ jmp(done);
 
   __ bind(runtime);
 
-  // Determine and save the live input values
-  __ push_call_clobbered_registers();
+  if (EnableValhalla && InlineTypePassFieldsAsArgs) {
+    // Barriers might be emitted when converting between (scalarized) calling conventions for inline
+    // types. Save all argument registers before calling into the runtime.
+    // TODO: use push_set() (see JDK-8283327 push/pop_call_clobbered_registers & aarch64 )
+    __ pusha();
+    __ subptr(rsp, 64);
+    __ movdbl(Address(rsp, 0),  j_farg0);
+    __ movdbl(Address(rsp, 8),  j_farg1);
+    __ movdbl(Address(rsp, 16), j_farg2);
+    __ movdbl(Address(rsp, 24), j_farg3);
+    __ movdbl(Address(rsp, 32), j_farg4);
+    __ movdbl(Address(rsp, 40), j_farg5);
+    __ movdbl(Address(rsp, 48), j_farg6);
+    __ movdbl(Address(rsp, 56), j_farg7);
+  } else {
+    // Determine and save the live input values
+    __ push_call_clobbered_registers();
+  }
 
   // Calling the runtime using the regular call_VM_leaf mechanism generates
   // code (generated by InterpreterMacroAssember::call_VM_leaf_base)
   // that checks that the *(ebp+frame::interpreter_frame_last_sp) == nullptr.
   //
@@ -256,11 +271,25 @@
     __ MacroAssembler::call_VM_leaf_base(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_pre_entry), 2);
   } else {
     __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_pre_entry), pre_val, thread);
   }
 
-  __ pop_call_clobbered_registers();
+  if (EnableValhalla && InlineTypePassFieldsAsArgs) {
+    // Restore registers
+    __ movdbl(j_farg0, Address(rsp, 0));
+    __ movdbl(j_farg1, Address(rsp, 8));
+    __ movdbl(j_farg2, Address(rsp, 16));
+    __ movdbl(j_farg3, Address(rsp, 24));
+    __ movdbl(j_farg4, Address(rsp, 32));
+    __ movdbl(j_farg5, Address(rsp, 40));
+    __ movdbl(j_farg6, Address(rsp, 48));
+    __ movdbl(j_farg7, Address(rsp, 56));
+    __ addptr(rsp, 64);
+    __ popa();
+  } else {
+    __ pop_call_clobbered_registers();
+  }
 
   __ bind(done);
 }
 
 void G1BarrierSetAssembler::g1_write_barrier_post(MacroAssembler* masm,
@@ -329,25 +358,54 @@
   __ addptr(tmp2, buffer);
   __ movptr(Address(tmp2, 0), card_addr);
   __ jmp(done);
 
   __ bind(runtime);
-  // save the live input values
-  RegSet saved = RegSet::of(store_addr NOT_LP64(COMMA thread));
-  __ push_set(saved);
+  // Barriers might be emitted when converting between (scalarized) calling conventions for inline
+  // types. Save all argument registers before calling into the runtime.
+  // TODO: use push_set() (see JDK-8283327 push/pop_call_clobbered_registers & aarch64)
+  __ pusha();
+  __ subptr(rsp, 64);
+  __ movdbl(Address(rsp, 0),  j_farg0);
+  __ movdbl(Address(rsp, 8),  j_farg1);
+  __ movdbl(Address(rsp, 16), j_farg2);
+  __ movdbl(Address(rsp, 24), j_farg3);
+  __ movdbl(Address(rsp, 32), j_farg4);
+  __ movdbl(Address(rsp, 40), j_farg5);
+  __ movdbl(Address(rsp, 48), j_farg6);
+  __ movdbl(Address(rsp, 56), j_farg7);
+
+#ifdef _LP64
+  __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry), card_addr, r15_thread);
+#else
+  __ push(thread);
   __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry), card_addr, thread);
-  __ pop_set(saved);
+  __ pop(thread);
+#endif
+
+  // Restore registers
+  __ movdbl(j_farg0, Address(rsp, 0));
+  __ movdbl(j_farg1, Address(rsp, 8));
+  __ movdbl(j_farg2, Address(rsp, 16));
+  __ movdbl(j_farg3, Address(rsp, 24));
+  __ movdbl(j_farg4, Address(rsp, 32));
+  __ movdbl(j_farg5, Address(rsp, 40));
+  __ movdbl(j_farg6, Address(rsp, 48));
+  __ movdbl(j_farg7, Address(rsp, 56));
+  __ addptr(rsp, 64);
+  __ popa();
 
   __ bind(done);
 }
 
 void G1BarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
                                          Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) {
   bool in_heap = (decorators & IN_HEAP) != 0;
   bool as_normal = (decorators & AS_NORMAL) != 0;
+  bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0;
 
-  bool needs_pre_barrier = as_normal;
+  bool needs_pre_barrier = as_normal && !dest_uninitialized;
   bool needs_post_barrier = val != noreg && in_heap;
 
   Register rthread = LP64_ONLY(r15_thread) NOT_LP64(rcx);
   // flatten object address if needed
   // We do it regardless of precise because we need the registers
diff a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.hpp
--- a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.hpp
+++ b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.hpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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.
diff a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp
--- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp
@@ -21,14 +21,16 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "asm/macroAssembler.inline.hpp"
 #include "classfile/classLoaderData.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/barrierSetAssembler.hpp"
 #include "gc/shared/barrierSetNMethod.hpp"
+#include "gc/shared/barrierSetRuntime.hpp"
 #include "gc/shared/collectedHeap.hpp"
 #include "interpreter/interp_masm.hpp"
 #include "memory/universe.hpp"
 #include "runtime/javaThread.hpp"
 #include "runtime/jniHandles.hpp"
@@ -193,10 +195,23 @@
     break;
   default: Unimplemented();
   }
 }
 
+void BarrierSetAssembler::value_copy(MacroAssembler* masm, DecoratorSet decorators,
+                                     Register src, Register dst, Register value_klass) {
+  // value_copy implementation is fairly complex, and there are not any
+  // "short-cuts" to be made from asm. What there is, appears to have the same
+  // cost in C++, so just "call_VM_leaf" for now rather than maintain hundreds
+  // of hand-rolled instructions...
+  if (decorators & IS_DEST_UNINITIALIZED) {
+    __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy_is_dest_uninitialized), src, dst, value_klass);
+  } else {
+    __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy), src, dst, value_klass);
+  }
+}
+
 void BarrierSetAssembler::copy_load_at(MacroAssembler* masm,
                                        DecoratorSet decorators,
                                        BasicType type,
                                        size_t bytes,
                                        Register dst,
diff a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp
--- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp
+++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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.
@@ -47,10 +47,13 @@
   virtual void load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
                        Register dst, Address src, Register tmp1, Register tmp_thread);
   virtual void store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
                         Address dst, Register val, Register tmp1, Register tmp2, Register tmp3);
 
+  virtual void value_copy(MacroAssembler* masm, DecoratorSet decorators,
+                          Register src, Register dst, Register value_klass);
+
   // The copy_[load/store]_at functions are used by arraycopy stubs. Be careful to only use
   // r10 (aka rscratch1) in a context where restore_arg_regs_using_thread has been used instead
   // of the looser setup_arg_regs. Currently this is done when using type T_OBJECT.
   virtual void copy_load_at(MacroAssembler* masm,
                             DecoratorSet decorators,
diff a/src/hotspot/cpu/x86/gc/shared/barrierSetNMethod_x86.cpp b/src/hotspot/cpu/x86/gc/shared/barrierSetNMethod_x86.cpp
--- a/src/hotspot/cpu/x86/gc/shared/barrierSetNMethod_x86.cpp
+++ b/src/hotspot/cpu/x86/gc/shared/barrierSetNMethod_x86.cpp
@@ -195,17 +195,41 @@
   NativeNMethodCmpBarrier* barrier = reinterpret_cast<NativeNMethodCmpBarrier*>(barrier_address);
   barrier->verify();
   return barrier;
 }
 
+static void set_immediate(nmethod* nm, jint val) {
+  NativeNMethodCmpBarrier* cmp1 = native_nmethod_barrier(nm);
+  cmp1->set_immediate(val);
+
+  if (!nm->is_osr_method() && nm->method()->has_scalarized_args()) {
+    // nmethods with scalarized arguments have multiple entry points that each have an own nmethod entry barrier
+    assert(nm->verified_entry_point() != nm->verified_inline_entry_point(), "scalarized entry point not found");
+    address method_body = nm->is_compiled_by_c1() ? nm->verified_inline_entry_point() : nm->verified_entry_point();
+    address entry_point2 = nm->is_compiled_by_c1() ? nm->verified_entry_point() : nm->verified_inline_entry_point();
+
+    int barrier_offset = reinterpret_cast<address>(cmp1) - method_body;
+    NativeNMethodCmpBarrier* cmp2 = reinterpret_cast<NativeNMethodCmpBarrier*>(entry_point2 + barrier_offset);
+    assert(cmp1 != cmp2, "sanity");
+    debug_only(cmp2->verify());
+    cmp2->set_immediate(val);
+
+    if (method_body != nm->verified_inline_ro_entry_point() && entry_point2 != nm->verified_inline_ro_entry_point()) {
+      NativeNMethodCmpBarrier* cmp3 = reinterpret_cast<NativeNMethodCmpBarrier*>(nm->verified_inline_ro_entry_point() + barrier_offset);
+      assert(cmp1 != cmp3 && cmp2 != cmp3, "sanity");
+      debug_only(cmp3->verify());
+      cmp3->set_immediate(val);
+    }
+  }
+}
+
 void BarrierSetNMethod::set_guard_value(nmethod* nm, int value) {
   if (!supports_entry_barrier(nm)) {
     return;
   }
 
-  NativeNMethodCmpBarrier* cmp = native_nmethod_barrier(nm);
-  cmp->set_immediate(value);
+  set_immediate(nm, value);
 }
 
 int BarrierSetNMethod::guard_value(nmethod* nm) {
   if (!supports_entry_barrier(nm)) {
     return disarmed_guard_value();
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
@@ -139,11 +139,18 @@
 
   BarrierSetAssembler::store_at(masm, decorators, type, dst, val, noreg, noreg, noreg);
   if (needs_post_barrier) {
     // flatten object address if needed
     if (!precise || (dst.index() == noreg && dst.disp() == 0)) {
-      store_check(masm, dst.base(), dst);
+      if (tmp3 != noreg) {
+        // Called by MacroAssembler::pack_inline_helper. We cannot corrupt the dst.base() register
+        __ movptr(tmp3, dst.base());
+        store_check(masm, tmp3, dst);
+      } else {
+        // It's OK to corrupt the dst.base() register.
+        store_check(masm, dst.base(), dst);
+      }
     } else {
       __ lea(tmp1, dst);
       store_check(masm, tmp1, dst);
     }
   }
diff a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp
--- a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp
+++ b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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.
diff a/src/hotspot/cpu/x86/gc/shared/modRefBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shared/modRefBarrierSetAssembler_x86.hpp
--- a/src/hotspot/cpu/x86/gc/shared/modRefBarrierSetAssembler_x86.hpp
+++ b/src/hotspot/cpu/x86/gc/shared/modRefBarrierSetAssembler_x86.hpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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.
diff a/src/hotspot/cpu/x86/globals_x86.hpp b/src/hotspot/cpu/x86/globals_x86.hpp
--- a/src/hotspot/cpu/x86/globals_x86.hpp
+++ b/src/hotspot/cpu/x86/globals_x86.hpp
@@ -94,10 +94,13 @@
 
 define_pd_global(bool, PreserveFramePointer, false);
 
 define_pd_global(intx, InitArrayShortSize, 8*BytesPerLong);
 
+define_pd_global(bool, InlineTypePassFieldsAsArgs, LP64_ONLY(true) NOT_LP64(false));
+define_pd_global(bool, InlineTypeReturnedAsFields, LP64_ONLY(true) NOT_LP64(false));
+
 #define ARCH_FLAGS(develop,                                                 \
                    product,                                                 \
                    notproduct,                                              \
                    range,                                                   \
                    constraint)                                              \
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
@@ -27,13 +27,15 @@
 #include "interp_masm_x86.hpp"
 #include "interpreter/interpreter.hpp"
 #include "interpreter/interpreterRuntime.hpp"
 #include "logging/log.hpp"
 #include "oops/arrayOop.hpp"
+#include "oops/constMethodFlags.hpp"
 #include "oops/markWord.hpp"
 #include "oops/methodData.hpp"
 #include "oops/method.hpp"
+#include "oops/inlineKlass.hpp"
 #include "oops/resolvedFieldEntry.hpp"
 #include "oops/resolvedIndyEntry.hpp"
 #include "oops/resolvedMethodEntry.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "prims/jvmtiThreadState.hpp"
@@ -171,11 +173,11 @@
       if (MethodData::profile_return()) {
         // We're right after the type profile for the last
         // argument. tmp is the number of cells left in the
         // CallTypeData/VirtualCallTypeData to reach its end. Non null
         // if there's a return to profile.
-        assert(ReturnTypeEntry::static_cell_count() < TypeStackSlotEntries::per_arg_count(), "can't move past ret type");
+        assert(SingleTypeEntry::static_cell_count() < TypeStackSlotEntries::per_arg_count(), "can't move past ret type");
         shll(tmp, log2i_exact((int)DataLayout::cell_size));
         addptr(mdp, tmp);
       }
       movptr(Address(rbp, frame::interpreter_frame_mdp_offset * wordSize), mdp);
     } else {
@@ -216,11 +218,11 @@
       jcc(Assembler::notEqual, profile_continue);
 
       bind(do_profile);
     }
 
-    Address mdo_ret_addr(mdp, -in_bytes(ReturnTypeEntry::size()));
+    Address mdo_ret_addr(mdp, -in_bytes(SingleTypeEntry::size()));
     mov(tmp, ret);
     profile_obj_type(tmp, mdo_ret_addr);
 
     bind(profile_continue);
   }
@@ -511,19 +513,22 @@
 //      Rsub_klass: subklass
 //
 // Kills:
 //      rcx, rdi
 void InterpreterMacroAssembler::gen_subtype_check(Register Rsub_klass,
-                                                  Label& ok_is_subtype) {
+                                                  Label& ok_is_subtype,
+                                                  bool profile) {
   assert(Rsub_klass != rax, "rax holds superklass");
   LP64_ONLY(assert(Rsub_klass != r14, "r14 holds locals");)
   LP64_ONLY(assert(Rsub_klass != r13, "r13 holds bcp");)
   assert(Rsub_klass != rcx, "rcx holds 2ndary super array length");
   assert(Rsub_klass != rdi, "rdi holds 2ndary super array scan ptr");
 
   // Profile the not-null value's klass.
-  profile_typecheck(rcx, Rsub_klass, rdi); // blows rcx, reloads rdi
+  if (profile) {
+    profile_typecheck(rcx, Rsub_klass, rdi); // blows rcx, reloads rdi
+  }
 
   // Do the check.
   check_klass_subtype(Rsub_klass, rax, rcx, ok_is_subtype); // blows rcx
 }
 
@@ -971,11 +976,11 @@
   const Address do_not_unlock_if_synchronized(rthread,
     in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()));
   movbool(rbx, do_not_unlock_if_synchronized);
   movbool(do_not_unlock_if_synchronized, false); // reset the flag
 
- // get method access flags
+  // get method access flags
   movptr(rcx, Address(rbp, frame::interpreter_frame_method_offset * wordSize));
   movl(rcx, Address(rcx, Method::access_flags_offset()));
   testl(rcx, JVM_ACC_SYNCHRONIZED);
   jcc(Assembler::zero, unlocked);
 
@@ -1097,15 +1102,13 @@
     notify_method_exit(state, NotifyJVMTI);    // preserve TOSCA
   } else {
     notify_method_exit(state, SkipNotifyJVMTI); // preserve TOSCA
   }
 
-  // remove activation
-  // get sender sp
-  movptr(rbx,
-         Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize));
   if (StackReservedPages > 0) {
+    movptr(rbx,
+               Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize));
     // testing if reserved zone needs to be re-enabled
     Register rthread = LP64_ONLY(r15_thread) NOT_LP64(rcx);
     Label no_reserved_zone_enabling;
 
     NOT_LP64(get_thread(rthread);)
@@ -1124,10 +1127,50 @@
                    InterpreterRuntime::throw_delayed_StackOverflowError));
     should_not_reach_here();
 
     bind(no_reserved_zone_enabling);
   }
+
+  // remove activation
+  // get sender sp
+  movptr(rbx,
+         Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize));
+
+  if (state == atos && InlineTypeReturnedAsFields) {
+    // Check if we are returning an non-null inline type and load its fields into registers
+    Label skip;
+    test_oop_is_not_inline_type(rax, rscratch1, skip);
+
+#ifndef _LP64
+    super_call_VM_leaf(StubRoutines::load_inline_type_fields_in_regs());
+#else
+    // Load fields from a buffered value with an inline class specific handler
+    load_klass(rdi, rax, rscratch1);
+    movptr(rdi, Address(rdi, InstanceKlass::adr_inlineklass_fixed_block_offset()));
+    movptr(rdi, Address(rdi, InlineKlass::unpack_handler_offset()));
+    // Unpack handler can be null if inline type is not scalarizable in returns
+    testptr(rdi, rdi);
+    jcc(Assembler::zero, skip);
+    call(rdi);
+#endif
+#ifdef ASSERT
+    // TODO 8284443 Enable
+    if (StressCallingConvention && false) {
+      Label skip_stress;
+      movptr(rscratch1, Address(rbp, frame::interpreter_frame_method_offset * wordSize));
+      movl(rscratch1, Address(rscratch1, Method::flags_offset()));
+      testl(rcx, ConstMethodFlags::has_scalarized_return_flag());
+      jcc(Assembler::zero, skip_stress);
+      load_klass(rax, rax, rscratch1);
+      orptr(rax, 1);
+      bind(skip_stress);
+    }
+#endif
+    // call above kills the value in rbx. Reload it.
+    movptr(rbx, Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize));
+    bind(skip);
+  }
   leave();                           // remove frame anchor
   pop(ret_addr);                     // get return address
   mov(rsp, rbx);                     // set sp to sender sp
   pop_cont_fastpath();
 }
@@ -1144,10 +1187,116 @@
   testptr(mcs, mcs);
   jcc(Assembler::zero, skip); // No MethodCounters allocated, OutOfMemory
   bind(has_counters);
 }
 
+void InterpreterMacroAssembler::allocate_instance(Register klass, Register new_obj,
+                                                  Register t1, Register t2,
+                                                  bool clear_fields, Label& alloc_failed) {
+  MacroAssembler::allocate_instance(klass, new_obj, t1, t2, clear_fields, alloc_failed);
+  {
+    SkipIfEqual skip_if(this, &DTraceAllocProbes, 0, rscratch1);
+    // Trigger dtrace event for fastpath
+    push(atos);
+    call_VM_leaf(CAST_FROM_FN_PTR(address, static_cast<int (*)(oopDesc*)>(SharedRuntime::dtrace_object_alloc)), new_obj);
+    pop(atos);
+  }
+}
+
+
+void InterpreterMacroAssembler::read_flat_field(Register holder_klass,
+                                                Register field_index, Register field_offset,
+                                                Register obj) {
+  Label alloc_failed, empty_value, done;
+  const Register src = field_offset;
+  const Register alloc_temp = LP64_ONLY(rscratch1) NOT_LP64(rsi);
+  const Register dst_temp   = LP64_ONLY(rscratch2) NOT_LP64(rdi);
+  assert_different_registers(obj, holder_klass, field_index, field_offset, dst_temp);
+
+  // Grap the inline field klass
+  push(holder_klass);
+  const Register field_klass = holder_klass;
+  get_inline_type_field_klass(holder_klass, field_index, field_klass);
+
+  //check for empty value klass
+  test_klass_is_empty_inline_type(field_klass, dst_temp, empty_value);
+
+  // allocate buffer
+  push(obj); // save holder
+  allocate_instance(field_klass, obj, alloc_temp, dst_temp, false, alloc_failed);
+
+  // Have an oop instance buffer, copy into it
+  data_for_oop(obj, dst_temp, field_klass);
+  pop(alloc_temp);             // restore holder
+  lea(src, Address(alloc_temp, field_offset));
+  // call_VM_leaf, clobbers a few regs, save restore new obj
+  push(obj);
+  access_value_copy(IS_DEST_UNINITIALIZED, src, dst_temp, field_klass);
+  pop(obj);
+  pop(holder_klass);
+  jmp(done);
+
+  bind(empty_value);
+  get_empty_inline_type_oop(field_klass, dst_temp, obj);
+  pop(holder_klass);
+  jmp(done);
+
+  bind(alloc_failed);
+  pop(obj);
+  pop(holder_klass);
+  call_VM(obj, CAST_FROM_FN_PTR(address, InterpreterRuntime::read_flat_field),
+          obj, field_index, holder_klass);
+
+  bind(done);
+}
+
+void InterpreterMacroAssembler::read_flat_element(Register array, Register index,
+                                                  Register t1, Register t2,
+                                                  Register obj) {
+  assert_different_registers(array, index, t1, t2);
+  Label alloc_failed, empty_value, done;
+  const Register array_klass = t2;
+  const Register elem_klass = t1;
+  const Register alloc_temp = LP64_ONLY(rscratch1) NOT_LP64(rsi);
+  const Register dst_temp   = LP64_ONLY(rscratch2) NOT_LP64(rdi);
+
+  // load in array->klass()->element_klass()
+  Register tmp_load_klass = LP64_ONLY(rscratch1) NOT_LP64(noreg);
+  load_klass(array_klass, array, tmp_load_klass);
+  movptr(elem_klass, Address(array_klass, ArrayKlass::element_klass_offset()));
+
+  //check for empty value klass
+  test_klass_is_empty_inline_type(elem_klass, dst_temp, empty_value);
+
+  // calc source into "array_klass" and free up some regs
+  const Register src = array_klass;
+  push(index); // preserve index reg in case alloc_failed
+  data_for_value_array_index(array, array_klass, index, src);
+
+  allocate_instance(elem_klass, obj, alloc_temp, dst_temp, false, alloc_failed);
+  // Have an oop instance buffer, copy into it
+  store_ptr(0, obj); // preserve obj (overwrite index, no longer needed)
+  data_for_oop(obj, dst_temp, elem_klass);
+  access_value_copy(IS_DEST_UNINITIALIZED, src, dst_temp, elem_klass);
+  pop(obj);
+  jmp(done);
+
+  bind(empty_value);
+  get_empty_inline_type_oop(elem_klass, dst_temp, obj);
+  jmp(done);
+
+  bind(alloc_failed);
+  pop(index);
+  if (array == c_rarg2) {
+    mov(elem_klass, array);
+    array = elem_klass;
+  }
+  call_VM(obj, CAST_FROM_FN_PTR(address, InterpreterRuntime::value_array_load), array, index);
+
+  bind(done);
+}
+
 
 // Lock object
 //
 // Args:
 //      rdx, c_rarg1: BasicObjectLock to be used for locking
@@ -1199,10 +1348,14 @@
       // Load immediate 1 into swap_reg %rax
       movl(swap_reg, 1);
 
       // Load (object->mark() | 1) into swap_reg %rax
       orptr(swap_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes()));
+      if (EnableValhalla) {
+        // Mask inline_type bit such that we go to the slow path if object is an inline type
+        andptr(swap_reg, ~((int) markWord::inline_type_bit_in_place));
+      }
 
       // Save (object->mark() | 1) into BasicLock's displaced header
       movptr(Address(lock_reg, mark_offset), swap_reg);
 
       assert(lock_offset == 0,
@@ -1571,11 +1724,11 @@
     bind(profile_continue);
   }
 }
 
 
-void InterpreterMacroAssembler::profile_not_taken_branch(Register mdp) {
+void InterpreterMacroAssembler::profile_not_taken_branch(Register mdp, bool acmp) {
   if (ProfileInterpreter) {
     Label profile_continue;
 
     // If no method data exists, go to profile_continue.
     test_method_data_pointer(mdp, profile_continue);
@@ -1583,11 +1736,11 @@
     // We are taking a branch.  Increment the not taken count.
     increment_mdp_data_at(mdp, in_bytes(BranchData::not_taken_offset()));
 
     // The method data pointer needs to be updated to correspond to
     // the next bytecode
-    update_mdp_by_constant(mdp, in_bytes(BranchData::branch_data_size()));
+    update_mdp_by_constant(mdp, acmp ? in_bytes(ACmpData::acmp_data_size()): in_bytes(BranchData::branch_data_size()));
     bind(profile_continue);
   }
 }
 
 void InterpreterMacroAssembler::profile_call(Register mdp) {
@@ -1646,11 +1799,11 @@
       jmp(skip_receiver_profile);
       bind(not_null);
     }
 
     // Record the receiver type.
-    record_klass_in_profile(receiver, mdp, reg2, true);
+    record_klass_in_profile(receiver, mdp, reg2);
     bind(skip_receiver_profile);
 
     // The method data pointer needs to be updated to reflect the new target.
     update_mdp_by_constant(mdp, in_bytes(VirtualCallData::virtual_call_data_size()));
     bind(profile_continue);
@@ -1666,14 +1819,13 @@
 // Because there are two degrees of freedom in the state, a simple linear
 // search will not work; it must be a decision tree.  Hence this helper
 // function is recursive, to generate the required tree structured code.
 // It's the interpreter, so we are trading off code space for speed.
 // See below for example code.
-void InterpreterMacroAssembler::record_klass_in_profile_helper(
-                                        Register receiver, Register mdp,
-                                        Register reg2, int start_row,
-                                        Label& done, bool is_virtual_call) {
+void InterpreterMacroAssembler::record_klass_in_profile_helper(Register receiver, Register mdp,
+                                                               Register reg2, int start_row,
+                                                               Label& done) {
   if (TypeProfileWidth == 0) {
     increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset()));
   } else {
     record_item_in_profile_helper(receiver, mdp, reg2, 0, done, TypeProfileWidth,
                                   &VirtualCallData::receiver_offset, &VirtualCallData::receiver_count_offset);
@@ -1773,17 +1925,15 @@
 //     if (row[2].rec == rec) { row[2].incr(); goto done; }
 //     row[0].init(rec); goto done;
 //   }
 //   done:
 
-void InterpreterMacroAssembler::record_klass_in_profile(Register receiver,
-                                                        Register mdp, Register reg2,
-                                                        bool is_virtual_call) {
+void InterpreterMacroAssembler::record_klass_in_profile(Register receiver, Register mdp, Register reg2) {
   assert(ProfileInterpreter, "must be profiling");
   Label done;
 
-  record_klass_in_profile_helper(receiver, mdp, reg2, 0, done, is_virtual_call);
+  record_klass_in_profile_helper(receiver, mdp, reg2, 0, done);
 
   bind (done);
 }
 
 void InterpreterMacroAssembler::profile_ret(Register return_bci,
@@ -1856,11 +2006,11 @@
     int mdp_delta = in_bytes(BitData::bit_data_size());
     if (TypeProfileCasts) {
       mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size());
 
       // Record the object type.
-      record_klass_in_profile(klass, mdp, reg2, false);
+      record_klass_in_profile(klass, mdp, reg2);
       NOT_LP64(assert(reg2 == rdi, "we know how to fix this blown reg");)
       NOT_LP64(restore_locals();)         // Restore EDI
     }
     update_mdp_by_constant(mdp, mdp_delta);
 
@@ -1918,10 +2068,124 @@
 
     bind(profile_continue);
   }
 }
 
+template <class ArrayData> void InterpreterMacroAssembler::profile_array_type(Register mdp,
+                                                                              Register array,
+                                                                              Register tmp) {
+  if (ProfileInterpreter) {
+    Label profile_continue;
+
+    // If no method data exists, go to profile_continue.
+    test_method_data_pointer(mdp, profile_continue);
+
+    mov(tmp, array);
+    profile_obj_type(tmp, Address(mdp, in_bytes(ArrayData::array_offset())));
+
+    Label not_flat;
+    test_non_flat_array_oop(array, tmp, not_flat);
+
+    set_mdp_flag_at(mdp, ArrayData::flat_array_byte_constant());
+
+    bind(not_flat);
+
+    Label not_null_free;
+    test_non_null_free_array_oop(array, tmp, not_null_free);
+
+    set_mdp_flag_at(mdp, ArrayData::null_free_array_byte_constant());
+
+    bind(not_null_free);
+
+    bind(profile_continue);
+  }
+}
+
+template void InterpreterMacroAssembler::profile_array_type<ArrayLoadData>(Register mdp,
+                                                                           Register array,
+                                                                           Register tmp);
+template void InterpreterMacroAssembler::profile_array_type<ArrayStoreData>(Register mdp,
+                                                                            Register array,
+                                                                            Register tmp);
+
+
+void InterpreterMacroAssembler::profile_multiple_element_types(Register mdp, Register element, Register tmp, const Register tmp2) {
+  if (ProfileInterpreter) {
+    Label profile_continue;
+
+    // If no method data exists, go to profile_continue.
+    test_method_data_pointer(mdp, profile_continue);
+
+    Label done, update;
+    testptr(element, element);
+    jccb(Assembler::notZero, update);
+    set_mdp_flag_at(mdp, BitData::null_seen_byte_constant());
+    jmp(done);
+
+    bind(update);
+    load_klass(tmp, element, rscratch1);
+
+    // Record the object type.
+    record_klass_in_profile(tmp, mdp, tmp2);
+
+    bind(done);
+
+    // The method data pointer needs to be updated.
+    update_mdp_by_constant(mdp, in_bytes(ArrayStoreData::array_store_data_size()));
+
+    bind(profile_continue);
+  }
+}
+
+void InterpreterMacroAssembler::profile_element_type(Register mdp,
+                                                     Register element,
+                                                     Register tmp) {
+  if (ProfileInterpreter) {
+    Label profile_continue;
+
+    // If no method data exists, go to profile_continue.
+    test_method_data_pointer(mdp, profile_continue);
+
+    mov(tmp, element);
+    profile_obj_type(tmp, Address(mdp, in_bytes(ArrayLoadData::element_offset())));
+
+    // The method data pointer needs to be updated.
+    update_mdp_by_constant(mdp, in_bytes(ArrayLoadData::array_load_data_size()));
+
+    bind(profile_continue);
+  }
+}
+
+void InterpreterMacroAssembler::profile_acmp(Register mdp,
+                                             Register left,
+                                             Register right,
+                                             Register tmp) {
+  if (ProfileInterpreter) {
+    Label profile_continue;
+
+    // If no method data exists, go to profile_continue.
+    test_method_data_pointer(mdp, profile_continue);
+
+    mov(tmp, left);
+    profile_obj_type(tmp, Address(mdp, in_bytes(ACmpData::left_offset())));
+
+    Label left_not_inline_type;
+    test_oop_is_not_inline_type(left, tmp, left_not_inline_type);
+    set_mdp_flag_at(mdp, ACmpData::left_inline_type_byte_constant());
+    bind(left_not_inline_type);
+
+    mov(tmp, right);
+    profile_obj_type(tmp, Address(mdp, in_bytes(ACmpData::right_offset())));
+
+    Label right_not_inline_type;
+    test_oop_is_not_inline_type(right, tmp, right_not_inline_type);
+    set_mdp_flag_at(mdp, ACmpData::right_inline_type_byte_constant());
+    bind(right_not_inline_type);
+
+    bind(profile_continue);
+  }
+}
 
 
 void InterpreterMacroAssembler::_interp_verify_oop(Register reg, TosState state, const char* file, int line) {
   if (state == atos) {
     MacroAssembler::_verify_oop_checked(reg, "broken oop", file, line);
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
@@ -170,11 +170,11 @@
   void load_ptr(int n, Register val);
   void store_ptr(int n, Register val);
 
   // Generate a subtype check: branch to ok_is_subtype if sub_klass is
   // a subtype of super_klass.
-  void gen_subtype_check( Register sub_klass, Label &ok_is_subtype );
+  void gen_subtype_check(Register sub_klass, Label &ok_is_subtype, bool profile = true);
 
   // Dispatching
   void dispatch_prolog(TosState state, int step = 0);
   void dispatch_epilog(TosState state, int step = 0);
   // dispatch via rbx (assume rbx is loaded already)
@@ -210,10 +210,33 @@
                          bool throw_monitor_exception = true,
                          bool install_monitor_exception = true,
                          bool notify_jvmdi = true);
   void get_method_counters(Register method, Register mcs, Label& skip);
 
+  // Kills t1 and t2, perserves klass, return allocation in new_obj
+  void allocate_instance(Register klass, Register new_obj,
+                         Register t1, Register t2,
+                         bool clear_fields, Label& alloc_failed);
+  // Allocate instance in "obj" and read in the content of the inline field
+  // NOTES:
+  //   - input holder object via "obj", which must be rax,
+  //     will return new instance via the same reg
+  //   - assumes holder_klass and valueKlass field klass have both been resolved
+  //   - 32 bits: kills rdi and rsi
+  void read_flat_field(Register holder_klass,
+                       Register field_index, Register field_offset,
+                       Register obj = rax);
+
+  // Allocate value buffer in "obj" and read in flat element at the given index
+  // NOTES:
+  //   - Return via "obj" must be rax
+  //   - kills all given regs
+  //   - 32 bits: kills rdi and rsi
+  void read_flat_element(Register array, Register index,
+                         Register t1, Register t2,
+                         Register obj = rax);
+
   // Object locking
   void lock_object  (Register lock_reg);
   void unlock_object(Register lock_reg);
 
   // Interpreter profiling operations
@@ -232,15 +255,12 @@
   void set_mdp_flag_at(Register mdp_in, int flag_constant);
   void test_mdp_data_at(Register mdp_in, int offset, Register value,
                         Register test_value_out,
                         Label& not_equal_continue);
 
-  void record_klass_in_profile(Register receiver, Register mdp,
-                               Register reg2, bool is_virtual_call);
-  void record_klass_in_profile_helper(Register receiver, Register mdp,
-                                      Register reg2, int start_row,
-                                      Label& done, bool is_virtual_call);
+  void record_klass_in_profile(Register receiver, Register mdp, Register reg2);
+  void record_klass_in_profile_helper(Register receiver, Register mdp, Register reg2, int start_row, Label &done);
   void record_item_in_profile_helper(Register item, Register mdp, Register reg2, int start_row,
                                      Label& done, int total_rows,
                                      OffsetFunction item_offset_fn,
                                      OffsetFunction item_count_offset_fn);
 
@@ -248,11 +268,11 @@
   void update_mdp_by_offset(Register mdp_in, Register reg, int offset_of_disp);
   void update_mdp_by_constant(Register mdp_in, int constant);
   void update_mdp_for_ret(Register return_bci);
 
   void profile_taken_branch(Register mdp, Register bumped_count);
-  void profile_not_taken_branch(Register mdp);
+  void profile_not_taken_branch(Register mdp, bool acmp = false);
   void profile_call(Register mdp);
   void profile_final_call(Register mdp);
   void profile_virtual_call(Register receiver, Register mdp,
                             Register scratch2,
                             bool receiver_can_be_null = false);
@@ -261,10 +281,15 @@
   void profile_typecheck(Register mdp, Register klass, Register scratch);
 
   void profile_switch_default(Register mdp);
   void profile_switch_case(Register index_in_scratch, Register mdp,
                            Register scratch2);
+  template <class ArrayData> void profile_array_type(Register mdp, Register array, Register tmp);
+
+  void profile_multiple_element_types(Register mdp, Register element, Register tmp, const Register tmp2);
+  void profile_element_type(Register mdp, Register element, Register tmp);
+  void profile_acmp(Register mdp, Register left, Register right, Register tmp);
 
   // Debugging
   // only if +VerifyOops && state == atos
 #define interp_verify_oop(reg, state) _interp_verify_oop(reg, state, __FILE__, __LINE__);
   void _interp_verify_oop(Register reg, TosState state, const char* file, int line);
diff a/src/hotspot/cpu/x86/interpreterRT_x86_32.cpp b/src/hotspot/cpu/x86/interpreterRT_x86_32.cpp
--- a/src/hotspot/cpu/x86/interpreterRT_x86_32.cpp
+++ b/src/hotspot/cpu/x86/interpreterRT_x86_32.cpp
@@ -69,10 +69,14 @@
 
 void InterpreterRuntime::SignatureHandlerGenerator::pass_object() {
   box (offset(), jni_offset() + 1);
 }
 
+void InterpreterRuntime::SignatureHandlerGenerator::pass_valuetype() {
+  box (offset(), jni_offset() + 1);
+}
+
 void InterpreterRuntime::SignatureHandlerGenerator::move(int from_offset, int to_offset) {
   __ movl(temp(), Address(from(), Interpreter::local_offset_in_bytes(from_offset)));
   __ movl(Address(to(), to_offset * wordSize), temp());
 }
 
@@ -136,10 +140,17 @@
     intptr_t from_addr = (intptr_t)(_from + Interpreter::local_offset_in_bytes(0));
     *_to++ = (*(intptr_t*)from_addr == 0) ? NULL_WORD : from_addr;
     _from -= Interpreter::stackElementSize;
    }
 
+  virtual void pass_valuetype() {
+    // pass address of from
+    intptr_t from_addr = (intptr_t)(_from + Interpreter::local_offset_in_bytes(0));
+    *_to++ = (*(intptr_t*)from_addr == 0) ? NULL_WORD : from_addr;
+    _from -= Interpreter::stackElementSize;
+   }
+
  public:
   SlowSignatureHandler(const methodHandle& method, address from, intptr_t* to) :
     NativeSignatureIterator(method) {
     _from = from;
     _to   = to + (is_static() ? 2 : 1);
diff a/src/hotspot/cpu/x86/jniFastGetField_x86_64.cpp b/src/hotspot/cpu/x86/jniFastGetField_x86_64.cpp
--- a/src/hotspot/cpu/x86/jniFastGetField_x86_64.cpp
+++ b/src/hotspot/cpu/x86/jniFastGetField_x86_64.cpp
@@ -28,10 +28,11 @@
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/barrierSetAssembler.hpp"
 #include "memory/resourceArea.hpp"
 #include "prims/jniFastGetField.hpp"
 #include "prims/jvm_misc.hpp"
+#include "runtime/jfieldIDWorkaround.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "runtime/safepoint.hpp"
 
 #define __ masm->
 
@@ -79,11 +80,11 @@
     __ cmp32(ExternalAddress(JvmtiExport::get_field_access_count_addr()), 0, rscratch1);
     __ jcc(Assembler::notZero, slow);
   }
 
   __ mov   (roffset, c_rarg2);
-  __ shrptr(roffset, 2);                         // offset
+  __ shrptr(roffset, jfieldIDWorkaround::offset_shift);                         // offset
 
   // Both robj and rtmp are clobbered by try_resolve_jobject_in_native.
   BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
   bs->try_resolve_jobject_in_native(masm, /* jni_env */ c_rarg0, robj, rtmp, slow);
   DEBUG_ONLY(__ movl(rtmp, 0xDEADC0DE);)
@@ -181,11 +182,11 @@
   BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
   bs->try_resolve_jobject_in_native(masm, /* jni_env */ c_rarg0, robj, rtmp, slow);
   DEBUG_ONLY(__ movl(rtmp, 0xDEADC0DE);)
 
   __ mov   (roffset, c_rarg2);
-  __ shrptr(roffset, 2);                         // offset
+  __ shrptr(roffset, jfieldIDWorkaround::offset_shift);                         // offset
 
   assert(count < LIST_CAPACITY, "LIST_CAPACITY too small");
   speculative_load_pclist[count] = __ pc();
   switch (type) {
     case T_FLOAT:  __ movflt (xmm0, Address(robj, roffset, Address::times_1)); break;
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
@@ -25,10 +25,11 @@
 #include "precompiled.hpp"
 #include "asm/assembler.hpp"
 #include "asm/assembler.inline.hpp"
 #include "compiler/compiler_globals.hpp"
 #include "compiler/disassembler.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "crc32c.h"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/barrierSetAssembler.hpp"
 #include "gc/shared/collectedHeap.inline.hpp"
 #include "gc/shared/tlab_globals.hpp"
@@ -39,23 +40,29 @@
 #include "memory/universe.hpp"
 #include "oops/accessDecorators.hpp"
 #include "oops/compressedKlass.inline.hpp"
 #include "oops/compressedOops.inline.hpp"
 #include "oops/klass.inline.hpp"
+#include "oops/resolvedFieldEntry.hpp"
 #include "prims/methodHandles.hpp"
 #include "runtime/continuation.hpp"
 #include "runtime/interfaceSupport.inline.hpp"
 #include "runtime/javaThread.hpp"
 #include "runtime/jniHandles.hpp"
 #include "runtime/objectMonitor.hpp"
 #include "runtime/os.hpp"
 #include "runtime/safepoint.hpp"
 #include "runtime/safepointMechanism.hpp"
 #include "runtime/sharedRuntime.hpp"
+#include "runtime/signature_cc.hpp"
 #include "runtime/stubRoutines.hpp"
 #include "utilities/checkedCast.hpp"
 #include "utilities/macros.hpp"
+#include "vmreg_x86.inline.hpp"
+#ifdef COMPILER2
+#include "opto/output.hpp"
+#endif
 
 #ifdef PRODUCT
 #define BLOCK_COMMENT(str) /* nothing */
 #define STOP(error) stop(error)
 #else
@@ -1683,10 +1690,14 @@
   pass_arg1(this, arg_1);
   pass_arg0(this, arg_0);
   call_VM_leaf(entry_point, 3);
 }
 
+void MacroAssembler::super_call_VM_leaf(address entry_point) {
+  MacroAssembler::call_VM_leaf_base(entry_point, 1);
+}
+
 void MacroAssembler::super_call_VM_leaf(address entry_point, Register arg_0) {
   pass_arg0(this, arg_0);
   MacroAssembler::call_VM_leaf_base(entry_point, 1);
 }
 
@@ -2865,10 +2876,150 @@
     // nothing to do, (later) access of M[reg + offset]
     // will provoke OS null exception if reg is null
   }
 }
 
+void MacroAssembler::test_markword_is_inline_type(Register markword, Label& is_inline_type) {
+  andptr(markword, markWord::inline_type_mask_in_place);
+  cmpptr(markword, markWord::inline_type_pattern);
+  jcc(Assembler::equal, is_inline_type);
+}
+
+void MacroAssembler::test_klass_is_inline_type(Register klass, Register temp_reg, Label& is_inline_type) {
+  movl(temp_reg, Address(klass, Klass::access_flags_offset()));
+  testl(temp_reg, JVM_ACC_IDENTITY);
+  jcc(Assembler::zero, is_inline_type);
+}
+
+void MacroAssembler::test_oop_is_not_inline_type(Register object, Register tmp, Label& not_inline_type) {
+  testptr(object, object);
+  jcc(Assembler::zero, not_inline_type);
+  const int is_inline_type_mask = markWord::inline_type_pattern;
+  movptr(tmp, Address(object, oopDesc::mark_offset_in_bytes()));
+  andptr(tmp, is_inline_type_mask);
+  cmpptr(tmp, is_inline_type_mask);
+  jcc(Assembler::notEqual, not_inline_type);
+}
+
+void MacroAssembler::test_klass_is_empty_inline_type(Register klass, Register temp_reg, Label& is_empty_inline_type) {
+#ifdef ASSERT
+  {
+    Label done_check;
+    test_klass_is_inline_type(klass, temp_reg, done_check);
+    stop("test_klass_is_empty_inline_type with non inline type klass");
+    bind(done_check);
+  }
+#endif
+  movl(temp_reg, Address(klass, InstanceKlass::misc_flags_offset()));
+  testl(temp_reg, InstanceKlassFlags::is_empty_inline_type_value());
+  jcc(Assembler::notZero, is_empty_inline_type);
+}
+
+void MacroAssembler::test_field_is_null_free_inline_type(Register flags, Register temp_reg, Label& is_null_free_inline_type) {
+  movl(temp_reg, flags);
+  testl(temp_reg, 1 << ResolvedFieldEntry::is_null_free_inline_type_shift);
+  jcc(Assembler::notEqual, is_null_free_inline_type);
+}
+
+void MacroAssembler::test_field_is_not_null_free_inline_type(Register flags, Register temp_reg, Label& not_null_free_inline_type) {
+  movl(temp_reg, flags);
+  testl(temp_reg, 1 << ResolvedFieldEntry::is_null_free_inline_type_shift);
+  jcc(Assembler::equal, not_null_free_inline_type);
+}
+
+void MacroAssembler::test_field_is_flat(Register flags, Register temp_reg, Label& is_flat) {
+  movl(temp_reg, flags);
+  testl(temp_reg, 1 << ResolvedFieldEntry::is_flat_shift);
+  jcc(Assembler::notEqual, is_flat);
+}
+
+void MacroAssembler::test_field_has_null_marker(Register flags, Register temp_reg, Label& has_null_marker) {
+  movl(temp_reg, flags);
+  testl(temp_reg, 1 << ResolvedFieldEntry::has_null_marker_shift);
+  jcc(Assembler::notEqual, has_null_marker);
+}
+
+void MacroAssembler::test_oop_prototype_bit(Register oop, Register temp_reg, int32_t test_bit, bool jmp_set, Label& jmp_label) {
+  Label test_mark_word;
+  // load mark word
+  movptr(temp_reg, Address(oop, oopDesc::mark_offset_in_bytes()));
+  // check displaced
+  testl(temp_reg, markWord::unlocked_value);
+  jccb(Assembler::notZero, test_mark_word);
+  // slow path use klass prototype
+  push(rscratch1);
+  load_prototype_header(temp_reg, oop, rscratch1);
+  pop(rscratch1);
+
+  bind(test_mark_word);
+  testl(temp_reg, test_bit);
+  jcc((jmp_set) ? Assembler::notZero : Assembler::zero, jmp_label);
+}
+
+void MacroAssembler::test_flat_array_oop(Register oop, Register temp_reg,
+                                         Label& is_flat_array) {
+#ifdef _LP64
+  test_oop_prototype_bit(oop, temp_reg, markWord::flat_array_bit_in_place, true, is_flat_array);
+#else
+  load_klass(temp_reg, oop, noreg);
+  movl(temp_reg, Address(temp_reg, Klass::layout_helper_offset()));
+  test_flat_array_layout(temp_reg, is_flat_array);
+#endif
+}
+
+void MacroAssembler::test_non_flat_array_oop(Register oop, Register temp_reg,
+                                             Label& is_non_flat_array) {
+#ifdef _LP64
+  test_oop_prototype_bit(oop, temp_reg, markWord::flat_array_bit_in_place, false, is_non_flat_array);
+#else
+  load_klass(temp_reg, oop, noreg);
+  movl(temp_reg, Address(temp_reg, Klass::layout_helper_offset()));
+  test_non_flat_array_layout(temp_reg, is_non_flat_array);
+#endif
+}
+
+void MacroAssembler::test_null_free_array_oop(Register oop, Register temp_reg, Label&is_null_free_array) {
+#ifdef _LP64
+  test_oop_prototype_bit(oop, temp_reg, markWord::null_free_array_bit_in_place, true, is_null_free_array);
+#else
+  load_klass(temp_reg, oop, noreg);
+  movl(temp_reg, Address(temp_reg, Klass::layout_helper_offset()));
+  test_null_free_array_layout(temp_reg, is_null_free_array);
+#endif
+}
+
+void MacroAssembler::test_non_null_free_array_oop(Register oop, Register temp_reg, Label&is_non_null_free_array) {
+#ifdef _LP64
+  test_oop_prototype_bit(oop, temp_reg, markWord::null_free_array_bit_in_place, false, is_non_null_free_array);
+#else
+  load_klass(temp_reg, oop, noreg);
+  movl(temp_reg, Address(temp_reg, Klass::layout_helper_offset()));
+  test_non_null_free_array_layout(temp_reg, is_non_null_free_array);
+#endif
+}
+
+void MacroAssembler::test_flat_array_layout(Register lh, Label& is_flat_array) {
+  testl(lh, Klass::_lh_array_tag_flat_value_bit_inplace);
+  jcc(Assembler::notZero, is_flat_array);
+}
+
+void MacroAssembler::test_non_flat_array_layout(Register lh, Label& is_non_flat_array) {
+  testl(lh, Klass::_lh_array_tag_flat_value_bit_inplace);
+  jcc(Assembler::zero, is_non_flat_array);
+}
+
+void MacroAssembler::test_null_free_array_layout(Register lh, Label& is_null_free_array) {
+  testl(lh, Klass::_lh_null_free_array_bit_inplace);
+  jcc(Assembler::notZero, is_null_free_array);
+}
+
+void MacroAssembler::test_non_null_free_array_layout(Register lh, Label& is_non_null_free_array) {
+  testl(lh, Klass::_lh_null_free_array_bit_inplace);
+  jcc(Assembler::zero, is_non_null_free_array);
+}
+
+
 void MacroAssembler::os_breakpoint() {
   // instead of directly emitting a breakpoint, call os:breakpoint for better debugability
   // (e.g., MSVC can't call ps() otherwise)
   call(RuntimeAddress(CAST_FROM_FN_PTR(address, os::breakpoint)));
 }
@@ -4019,10 +4170,124 @@
 
 void MacroAssembler::testptr(Register dst, Register src) {
   LP64_ONLY(testq(dst, src)) NOT_LP64(testl(dst, src));
 }
 
+// Object / value buffer allocation...
+//
+// Kills klass and rsi on LP64
+void MacroAssembler::allocate_instance(Register klass, Register new_obj,
+                                       Register t1, Register t2,
+                                       bool clear_fields, Label& alloc_failed)
+{
+  Label done, initialize_header, initialize_object, slow_case, slow_case_no_pop;
+  Register layout_size = t1;
+  assert(new_obj == rax, "needs to be rax");
+  assert_different_registers(klass, new_obj, t1, t2);
+
+  // get instance_size in InstanceKlass (scaled to a count of bytes)
+  movl(layout_size, Address(klass, Klass::layout_helper_offset()));
+  // test to see if it has a finalizer or is malformed in some way
+  testl(layout_size, Klass::_lh_instance_slow_path_bit);
+  jcc(Assembler::notZero, slow_case_no_pop);
+
+  // Allocate the instance:
+  //  If TLAB is enabled:
+  //    Try to allocate in the TLAB.
+  //    If fails, go to the slow path.
+  //  Else If inline contiguous allocations are enabled:
+  //    Try to allocate in eden.
+  //    If fails due to heap end, go to slow path.
+  //
+  //  If TLAB is enabled OR inline contiguous is enabled:
+  //    Initialize the allocation.
+  //    Exit.
+  //
+  //  Go to slow path.
+
+  push(klass);
+  const Register thread = LP64_ONLY(r15_thread) NOT_LP64(klass);
+#ifndef _LP64
+  if (UseTLAB) {
+    get_thread(thread);
+  }
+#endif // _LP64
+
+  if (UseTLAB) {
+    tlab_allocate(thread, new_obj, layout_size, 0, klass, t2, slow_case);
+    if (ZeroTLAB || (!clear_fields)) {
+      // the fields have been already cleared
+      jmp(initialize_header);
+    } else {
+      // initialize both the header and fields
+      jmp(initialize_object);
+    }
+  } else {
+    jmp(slow_case);
+  }
+
+  // If UseTLAB is true, the object is created above and there is an initialize need.
+  // Otherwise, skip and go to the slow path.
+  if (UseTLAB) {
+    if (clear_fields) {
+      // The object is initialized before the header.  If the object size is
+      // zero, go directly to the header initialization.
+      bind(initialize_object);
+      decrement(layout_size, sizeof(oopDesc));
+      jcc(Assembler::zero, initialize_header);
+
+      // Initialize topmost object field, divide size by 8, check if odd and
+      // test if zero.
+      Register zero = klass;
+      xorl(zero, zero);    // use zero reg to clear memory (shorter code)
+      shrl(layout_size, LogBytesPerLong); // divide by 2*oopSize and set carry flag if odd
+
+  #ifdef ASSERT
+      // make sure instance_size was multiple of 8
+      Label L;
+      // Ignore partial flag stall after shrl() since it is debug VM
+      jcc(Assembler::carryClear, L);
+      stop("object size is not multiple of 2 - adjust this code");
+      bind(L);
+      // must be > 0, no extra check needed here
+  #endif
+
+      // initialize remaining object fields: instance_size was a multiple of 8
+      {
+        Label loop;
+        bind(loop);
+        movptr(Address(new_obj, layout_size, Address::times_8, sizeof(oopDesc) - 1*oopSize), zero);
+        NOT_LP64(movptr(Address(new_obj, layout_size, Address::times_8, sizeof(oopDesc) - 2*oopSize), zero));
+        decrement(layout_size);
+        jcc(Assembler::notZero, loop);
+      }
+    } // clear_fields
+
+    // initialize object header only.
+    bind(initialize_header);
+    pop(klass);
+    Register mark_word = t2;
+    movptr(mark_word, Address(klass, Klass::prototype_header_offset()));
+    movptr(Address(new_obj, oopDesc::mark_offset_in_bytes ()), mark_word);
+#ifdef _LP64
+    xorl(rsi, rsi);                 // use zero reg to clear memory (shorter code)
+    store_klass_gap(new_obj, rsi);  // zero klass gap for compressed oops
+#endif
+    movptr(t2, klass);         // preserve klass
+    store_klass(new_obj, t2, rscratch1);  // src klass reg is potentially compressed
+
+    jmp(done);
+  }
+
+  bind(slow_case);
+  pop(klass);
+  bind(slow_case_no_pop);
+  jmp(alloc_failed);
+
+  bind(done);
+}
+
 // Defines obj, preserves var_size_in_bytes, okay for t2 == var_size_in_bytes.
 void MacroAssembler::tlab_allocate(Register thread, Register obj,
                                    Register var_size_in_bytes,
                                    int con_size_in_bytes,
                                    Register t1,
@@ -4271,10 +4536,60 @@
   }
 
   bind(done);
 }
 
+void MacroAssembler::get_inline_type_field_klass(Register klass, Register index, Register inline_klass) {
+  movptr(inline_klass, Address(klass, InstanceKlass::inline_type_field_klasses_offset()));
+#ifdef ASSERT
+  {
+    Label done;
+    cmpptr(inline_klass, 0);
+    jcc(Assembler::notEqual, done);
+    stop("get_inline_type_field_klass contains no inline klass");
+    bind(done);
+  }
+#endif
+  movptr(inline_klass, Address(inline_klass, index, Address::times_ptr, Array<InlineKlass*>::base_offset_in_bytes()));
+}
+
+void MacroAssembler::get_default_value_oop(Register inline_klass, Register temp_reg, Register obj) {
+#ifdef ASSERT
+  {
+    Label done_check;
+    test_klass_is_inline_type(inline_klass, temp_reg, done_check);
+    stop("get_default_value_oop from non inline type klass");
+    bind(done_check);
+  }
+#endif
+  Register offset = temp_reg;
+  // Getting the offset of the pre-allocated default value
+  movptr(offset, Address(inline_klass, in_bytes(InstanceKlass::adr_inlineklass_fixed_block_offset())));
+  movl(offset, Address(offset, in_bytes(InlineKlass::default_value_offset_offset())));
+
+  // Getting the mirror
+  movptr(obj, Address(inline_klass, in_bytes(Klass::java_mirror_offset())));
+  resolve_oop_handle(obj, inline_klass);
+
+  // Getting the pre-allocated default value from the mirror
+  Address field(obj, offset, Address::times_1);
+  load_heap_oop(obj, field);
+}
+
+void MacroAssembler::get_empty_inline_type_oop(Register inline_klass, Register temp_reg, Register obj) {
+#ifdef ASSERT
+  {
+    Label done_check;
+    test_klass_is_empty_inline_type(inline_klass, temp_reg, done_check);
+    stop("get_empty_value from non-empty inline klass");
+    bind(done_check);
+  }
+#endif
+  get_default_value_oop(inline_klass, temp_reg, obj);
+}
+
+
 // Look up the method for a megamorphic invokeinterface call.
 // The target method is determined by <intf_klass, itable_index>.
 // The receiver klass is in recv_klass.
 // On success, the result will be in method_result, and execution falls through.
 // On failure, execution transfers to the given label.
@@ -4738,11 +5053,15 @@
     bind(L);
   }
 }
 
 void MacroAssembler::_verify_oop(Register reg, const char* s, const char* file, int line) {
-  if (!VerifyOops) return;
+  if (!VerifyOops || VerifyAdapterSharing) {
+    // Below address of the code string confuses VerifyAdapterSharing
+    // because it may differ between otherwise equivalent adapters.
+    return;
+  }
 
   BLOCK_COMMENT("verify_oop {");
 #ifdef _LP64
   push(rscratch1);
 #endif
@@ -4800,11 +5119,15 @@
   offset += wordSize;           // return PC is on stack
   return Address(rsp, scale_reg, scale_factor, offset);
 }
 
 void MacroAssembler::_verify_oop_addr(Address addr, const char* s, const char* file, int line) {
-  if (!VerifyOops) return;
+  if (!VerifyOops || VerifyAdapterSharing) {
+    // Below address of the code string confuses VerifyAdapterSharing
+    // because it may differ between otherwise equivalent adapters.
+    return;
+  }
 
 #ifdef _LP64
   push(rscratch1);
 #endif
   push(rax); // save rax,
@@ -5287,20 +5610,33 @@
   movptr(holder, Address(method, Method::const_offset()));                      // ConstMethod*
   movptr(holder, Address(holder, ConstMethod::constants_offset()));             // ConstantPool*
   movptr(holder, Address(holder, ConstantPool::pool_holder_offset()));          // InstanceKlass*
 }
 
+void MacroAssembler::load_metadata(Register dst, Register src) {
+  if (UseCompressedClassPointers) {
+    movl(dst, Address(src, oopDesc::klass_offset_in_bytes()));
+  } else {
+    movptr(dst, Address(src, oopDesc::klass_offset_in_bytes()));
+  }
+}
+
 void MacroAssembler::load_klass(Register dst, Register src, Register tmp) {
   assert_different_registers(src, tmp);
   assert_different_registers(dst, tmp);
 #ifdef _LP64
   if (UseCompressedClassPointers) {
     movl(dst, Address(src, oopDesc::klass_offset_in_bytes()));
     decode_klass_not_null(dst, tmp);
   } else
 #endif
-    movptr(dst, Address(src, oopDesc::klass_offset_in_bytes()));
+  movptr(dst, Address(src, oopDesc::klass_offset_in_bytes()));
+}
+
+void MacroAssembler::load_prototype_header(Register dst, Register src, Register tmp) {
+  load_klass(dst, src, tmp);
+  movptr(dst, Address(dst, Klass::prototype_header_offset()));
 }
 
 void MacroAssembler::store_klass(Register dst, Register src, Register tmp) {
   assert_different_registers(src, tmp);
   assert_different_registers(dst, tmp);
@@ -5335,10 +5671,50 @@
   } else {
     bs->store_at(this, decorators, type, dst, val, tmp1, tmp2, tmp3);
   }
 }
 
+void MacroAssembler::access_value_copy(DecoratorSet decorators, Register src, Register dst,
+                                       Register inline_klass) {
+  BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
+  bs->value_copy(this, decorators, src, dst, inline_klass);
+}
+
+void MacroAssembler::first_field_offset(Register inline_klass, Register offset) {
+  movptr(offset, Address(inline_klass, InstanceKlass::adr_inlineklass_fixed_block_offset()));
+  movl(offset, Address(offset, InlineKlass::first_field_offset_offset()));
+}
+
+void MacroAssembler::data_for_oop(Register oop, Register data, Register inline_klass) {
+  // ((address) (void*) o) + vk->first_field_offset();
+  Register offset = (data == oop) ? rscratch1 : data;
+  first_field_offset(inline_klass, offset);
+  if (data == oop) {
+    addptr(data, offset);
+  } else {
+    lea(data, Address(oop, offset));
+  }
+}
+
+void MacroAssembler::data_for_value_array_index(Register array, Register array_klass,
+                                                Register index, Register data) {
+  assert(index != rcx, "index needs to shift by rcx");
+  assert_different_registers(array, array_klass, index);
+  assert_different_registers(rcx, array, index);
+
+  // array->base() + (index << Klass::layout_helper_log2_element_size(lh));
+  movl(rcx, Address(array_klass, Klass::layout_helper_offset()));
+
+  // Klass::layout_helper_log2_element_size(lh)
+  // (lh >> _lh_log2_element_size_shift) & _lh_log2_element_size_mask;
+  shrl(rcx, Klass::_lh_log2_element_size_shift);
+  andl(rcx, Klass::_lh_log2_element_size_mask);
+  shlptr(index); // index << rcx
+
+  lea(data, Address(array, index, Address::times_1, arrayOopDesc::base_offset_in_bytes(T_PRIMITIVE_OBJECT)));
+}
+
 void MacroAssembler::load_heap_oop(Register dst, Address src, Register tmp1,
                                    Register thread_tmp, DecoratorSet decorators) {
   access_load_at(T_OBJECT, IN_HEAP | decorators, dst, src, tmp1, thread_tmp);
 }
 
@@ -5674,21 +6050,24 @@
 #endif // _LP64
 
 #if COMPILER2_OR_JVMCI
 
 // clear memory of size 'cnt' qwords, starting at 'base' using XMM/YMM/ZMM registers
-void MacroAssembler::xmm_clear_mem(Register base, Register cnt, Register rtmp, XMMRegister xtmp, KRegister mask) {
+void MacroAssembler::xmm_clear_mem(Register base, Register cnt, Register val, XMMRegister xtmp, KRegister mask) {
   // cnt - number of qwords (8-byte words).
   // base - start address, qword aligned.
   Label L_zero_64_bytes, L_loop, L_sloop, L_tail, L_end;
   bool use64byteVector = (MaxVectorSize == 64) && (VM_Version::avx3_threshold() == 0);
   if (use64byteVector) {
-    vpxor(xtmp, xtmp, xtmp, AVX_512bit);
+    evpbroadcastq(xtmp, val, AVX_512bit);
   } else if (MaxVectorSize >= 32) {
-    vpxor(xtmp, xtmp, xtmp, AVX_256bit);
+    movdq(xtmp, val);
+    punpcklqdq(xtmp, xtmp);
+    vinserti128_high(xtmp, xtmp);
   } else {
-    pxor(xtmp, xtmp);
+    movdq(xtmp, val);
+    punpcklqdq(xtmp, xtmp);
   }
   jmp(L_zero_64_bytes);
 
   BIND(L_loop);
   if (MaxVectorSize >= 32) {
@@ -5707,11 +6086,11 @@
 
   // Copy trailing 64 bytes
   if (use64byteVector) {
     addptr(cnt, 8);
     jccb(Assembler::equal, L_end);
-    fill64_masked(3, base, 0, xtmp, mask, cnt, rtmp, true);
+    fill64_masked(3, base, 0, xtmp, mask, cnt, val, true);
     jmp(L_end);
   } else {
     addptr(cnt, 4);
     jccb(Assembler::less, L_tail);
     if (MaxVectorSize >= 32) {
@@ -5726,11 +6105,11 @@
 
   BIND(L_tail);
   addptr(cnt, 4);
   jccb(Assembler::lessEqual, L_end);
   if (UseAVX > 2 && MaxVectorSize >= 32 && VM_Version::supports_avx512vl()) {
-    fill32_masked(3, base, 0, xtmp, mask, cnt, rtmp);
+    fill32_masked(3, base, 0, xtmp, mask, cnt, val);
   } else {
     decrement(cnt);
 
     BIND(L_sloop);
     movq(Address(base, 0), xtmp);
@@ -5739,10 +6118,408 @@
     jccb(Assembler::greaterEqual, L_sloop);
   }
   BIND(L_end);
 }
 
+int MacroAssembler::store_inline_type_fields_to_buf(ciInlineKlass* vk, bool from_interpreter) {
+  assert(InlineTypeReturnedAsFields, "Inline types should never be returned as fields");
+  // An inline type might be returned. If fields are in registers we
+  // need to allocate an inline type instance and initialize it with
+  // the value of the fields.
+  Label skip;
+  // We only need a new buffered inline type if a new one is not returned
+  testptr(rax, 1);
+  jcc(Assembler::zero, skip);
+  int call_offset = -1;
+
+#ifdef _LP64
+  // The following code is similar to allocate_instance but has some slight differences,
+  // e.g. object size is always not zero, sometimes it's constant; storing klass ptr after
+  // allocating is not necessary if vk != nullptr, etc. allocate_instance is not aware of these.
+  Label slow_case;
+  // 1. Try to allocate a new buffered inline instance either from TLAB or eden space
+  mov(rscratch1, rax); // save rax for slow_case since *_allocate may corrupt it when allocation failed
+  if (vk != nullptr) {
+    // Called from C1, where the return type is statically known.
+    movptr(rbx, (intptr_t)vk->get_InlineKlass());
+    jint obj_size = vk->layout_helper();
+    assert(obj_size != Klass::_lh_neutral_value, "inline class in return type must have been resolved");
+    if (UseTLAB) {
+      tlab_allocate(r15_thread, rax, noreg, obj_size, r13, r14, slow_case);
+    } else {
+      jmp(slow_case);
+    }
+  } else {
+    // Call from interpreter. RAX contains ((the InlineKlass* of the return type) | 0x01)
+    mov(rbx, rax);
+    andptr(rbx, -2);
+    movl(r14, Address(rbx, Klass::layout_helper_offset()));
+    if (UseTLAB) {
+      tlab_allocate(r15_thread, rax, r14, 0, r13, r14, slow_case);
+    } else {
+      jmp(slow_case);
+    }
+  }
+  if (UseTLAB) {
+    // 2. Initialize buffered inline instance header
+    Register buffer_obj = rax;
+    movptr(Address(buffer_obj, oopDesc::mark_offset_in_bytes()), (intptr_t)markWord::inline_type_prototype().value());
+    xorl(r13, r13);
+    store_klass_gap(buffer_obj, r13);
+    if (vk == nullptr) {
+      // store_klass corrupts rbx(klass), so save it in r13 for later use (interpreter case only).
+      mov(r13, rbx);
+    }
+    store_klass(buffer_obj, rbx, rscratch1);
+    // 3. Initialize its fields with an inline class specific handler
+    if (vk != nullptr) {
+      call(RuntimeAddress(vk->pack_handler())); // no need for call info as this will not safepoint.
+    } else {
+      movptr(rbx, Address(r13, InstanceKlass::adr_inlineklass_fixed_block_offset()));
+      movptr(rbx, Address(rbx, InlineKlass::pack_handler_offset()));
+      call(rbx);
+    }
+    jmp(skip);
+  }
+  bind(slow_case);
+  // We failed to allocate a new inline type, fall back to a runtime
+  // call. Some oop field may be live in some registers but we can't
+  // tell. That runtime call will take care of preserving them
+  // across a GC if there's one.
+  mov(rax, rscratch1);
+#endif
+
+  if (from_interpreter) {
+    super_call_VM_leaf(StubRoutines::store_inline_type_fields_to_buf());
+  } else {
+    call(RuntimeAddress(StubRoutines::store_inline_type_fields_to_buf()));
+    call_offset = offset();
+  }
+
+  bind(skip);
+  return call_offset;
+}
+
+// Move a value between registers/stack slots and update the reg_state
+bool MacroAssembler::move_helper(VMReg from, VMReg to, BasicType bt, RegState reg_state[]) {
+  assert(from->is_valid() && to->is_valid(), "source and destination must be valid");
+  if (reg_state[to->value()] == reg_written) {
+    return true; // Already written
+  }
+  if (from != to && bt != T_VOID) {
+    if (reg_state[to->value()] == reg_readonly) {
+      return false; // Not yet writable
+    }
+    if (from->is_reg()) {
+      if (to->is_reg()) {
+        if (from->is_XMMRegister()) {
+          if (bt == T_DOUBLE) {
+            movdbl(to->as_XMMRegister(), from->as_XMMRegister());
+          } else {
+            assert(bt == T_FLOAT, "must be float");
+            movflt(to->as_XMMRegister(), from->as_XMMRegister());
+          }
+        } else {
+          movq(to->as_Register(), from->as_Register());
+        }
+      } else {
+        int st_off = to->reg2stack() * VMRegImpl::stack_slot_size + wordSize;
+        Address to_addr = Address(rsp, st_off);
+        if (from->is_XMMRegister()) {
+          if (bt == T_DOUBLE) {
+            movdbl(to_addr, from->as_XMMRegister());
+          } else {
+            assert(bt == T_FLOAT, "must be float");
+            movflt(to_addr, from->as_XMMRegister());
+          }
+        } else {
+          movq(to_addr, from->as_Register());
+        }
+      }
+    } else {
+      Address from_addr = Address(rsp, from->reg2stack() * VMRegImpl::stack_slot_size + wordSize);
+      if (to->is_reg()) {
+        if (to->is_XMMRegister()) {
+          if (bt == T_DOUBLE) {
+            movdbl(to->as_XMMRegister(), from_addr);
+          } else {
+            assert(bt == T_FLOAT, "must be float");
+            movflt(to->as_XMMRegister(), from_addr);
+          }
+        } else {
+          movq(to->as_Register(), from_addr);
+        }
+      } else {
+        int st_off = to->reg2stack() * VMRegImpl::stack_slot_size + wordSize;
+        movq(r13, from_addr);
+        movq(Address(rsp, st_off), r13);
+      }
+    }
+  }
+  // Update register states
+  reg_state[from->value()] = reg_writable;
+  reg_state[to->value()] = reg_written;
+  return true;
+}
+
+// Calculate the extra stack space required for packing or unpacking inline
+// args and adjust the stack pointer
+int MacroAssembler::extend_stack_for_inline_args(int args_on_stack) {
+  // Two additional slots to account for return address
+  int sp_inc = (args_on_stack + 2) * VMRegImpl::stack_slot_size;
+  sp_inc = align_up(sp_inc, StackAlignmentInBytes);
+  // Save the return address, adjust the stack (make sure it is properly
+  // 16-byte aligned) and copy the return address to the new top of the stack.
+  // The stack will be repaired on return (see MacroAssembler::remove_frame).
+  assert(sp_inc > 0, "sanity");
+  pop(r13);
+  subptr(rsp, sp_inc);
+  push(r13);
+  return sp_inc;
+}
+
+// Read all fields from an inline type buffer and store the field values in registers/stack slots.
+bool MacroAssembler::unpack_inline_helper(const GrowableArray<SigEntry>* sig, int& sig_index,
+                                          VMReg from, int& from_index, VMRegPair* to, int to_count, int& to_index,
+                                          RegState reg_state[]) {
+  assert(sig->at(sig_index)._bt == T_VOID, "should be at end delimiter");
+  assert(from->is_valid(), "source must be valid");
+  bool progress = false;
+#ifdef ASSERT
+  const int start_offset = offset();
+#endif
+
+  Label L_null, L_notNull;
+  // Don't use r14 as tmp because it's used for spilling (see MacroAssembler::spill_reg_for)
+  Register tmp1 = r10;
+  Register tmp2 = r13;
+  Register fromReg = noreg;
+  ScalarizedInlineArgsStream stream(sig, sig_index, to, to_count, to_index, -1);
+  bool done = true;
+  bool mark_done = true;
+  VMReg toReg;
+  BasicType bt;
+  // Check if argument requires a null check
+  bool null_check = false;
+  VMReg nullCheckReg;
+  while (stream.next(nullCheckReg, bt)) {
+    if (sig->at(stream.sig_index())._offset == -1) {
+      null_check = true;
+      break;
+    }
+  }
+  stream.reset(sig_index, to_index);
+  while (stream.next(toReg, bt)) {
+    assert(toReg->is_valid(), "destination must be valid");
+    int idx = (int)toReg->value();
+    if (reg_state[idx] == reg_readonly) {
+      if (idx != from->value()) {
+        mark_done = false;
+      }
+      done = false;
+      continue;
+    } else if (reg_state[idx] == reg_written) {
+      continue;
+    }
+    assert(reg_state[idx] == reg_writable, "must be writable");
+    reg_state[idx] = reg_written;
+    progress = true;
+
+    if (fromReg == noreg) {
+      if (from->is_reg()) {
+        fromReg = from->as_Register();
+      } else {
+        int st_off = from->reg2stack() * VMRegImpl::stack_slot_size + wordSize;
+        movq(tmp1, Address(rsp, st_off));
+        fromReg = tmp1;
+      }
+      if (null_check) {
+        // Nullable inline type argument, emit null check
+        testptr(fromReg, fromReg);
+        jcc(Assembler::zero, L_null);
+      }
+    }
+    int off = sig->at(stream.sig_index())._offset;
+    if (off == -1) {
+      assert(null_check, "Missing null check at");
+      if (toReg->is_stack()) {
+        int st_off = toReg->reg2stack() * VMRegImpl::stack_slot_size + wordSize;
+        movq(Address(rsp, st_off), 1);
+      } else {
+        movq(toReg->as_Register(), 1);
+      }
+      continue;
+    }
+    assert(off > 0, "offset in object should be positive");
+    Address fromAddr = Address(fromReg, off);
+    if (!toReg->is_XMMRegister()) {
+      Register dst = toReg->is_stack() ? tmp2 : toReg->as_Register();
+      if (is_reference_type(bt)) {
+        load_heap_oop(dst, fromAddr);
+      } else {
+        bool is_signed = (bt != T_CHAR) && (bt != T_BOOLEAN);
+        load_sized_value(dst, fromAddr, type2aelembytes(bt), is_signed);
+      }
+      if (toReg->is_stack()) {
+        int st_off = toReg->reg2stack() * VMRegImpl::stack_slot_size + wordSize;
+        movq(Address(rsp, st_off), dst);
+      }
+    } else if (bt == T_DOUBLE) {
+      movdbl(toReg->as_XMMRegister(), fromAddr);
+    } else {
+      assert(bt == T_FLOAT, "must be float");
+      movflt(toReg->as_XMMRegister(), fromAddr);
+    }
+  }
+  if (progress && null_check) {
+    if (done) {
+      jmp(L_notNull);
+      bind(L_null);
+      // Set IsInit field to zero to signal that the argument is null.
+      // Also set all oop fields to zero to make the GC happy.
+      stream.reset(sig_index, to_index);
+      while (stream.next(toReg, bt)) {
+        if (sig->at(stream.sig_index())._offset == -1 ||
+            bt == T_OBJECT || bt == T_ARRAY) {
+          if (toReg->is_stack()) {
+            int st_off = toReg->reg2stack() * VMRegImpl::stack_slot_size + wordSize;
+            movq(Address(rsp, st_off), 0);
+          } else {
+            xorq(toReg->as_Register(), toReg->as_Register());
+          }
+        }
+      }
+      bind(L_notNull);
+    } else {
+      bind(L_null);
+    }
+  }
+
+  sig_index = stream.sig_index();
+  to_index = stream.regs_index();
+
+  if (mark_done && reg_state[from->value()] != reg_written) {
+    // This is okay because no one else will write to that slot
+    reg_state[from->value()] = reg_writable;
+  }
+  from_index--;
+  assert(progress || (start_offset == offset()), "should not emit code");
+  return done;
+}
+
+bool MacroAssembler::pack_inline_helper(const GrowableArray<SigEntry>* sig, int& sig_index, int vtarg_index,
+                                        VMRegPair* from, int from_count, int& from_index, VMReg to,
+                                        RegState reg_state[], Register val_array) {
+  assert(sig->at(sig_index)._bt == T_METADATA, "should be at delimiter");
+  assert(to->is_valid(), "destination must be valid");
+
+  if (reg_state[to->value()] == reg_written) {
+    skip_unpacked_fields(sig, sig_index, from, from_count, from_index);
+    return true; // Already written
+  }
+
+  // TODO 8284443 Isn't it an issue if below code uses r14 as tmp when it contains a spilled value?
+  // Be careful with r14 because it's used for spilling (see MacroAssembler::spill_reg_for).
+  Register val_obj_tmp = r11;
+  Register from_reg_tmp = r14;
+  Register tmp1 = r10;
+  Register tmp2 = r13;
+  Register tmp3 = rbx;
+  Register val_obj = to->is_stack() ? val_obj_tmp : to->as_Register();
+
+  assert_different_registers(val_obj_tmp, from_reg_tmp, tmp1, tmp2, tmp3, val_array);
+
+  if (reg_state[to->value()] == reg_readonly) {
+    if (!is_reg_in_unpacked_fields(sig, sig_index, to, from, from_count, from_index)) {
+      skip_unpacked_fields(sig, sig_index, from, from_count, from_index);
+      return false; // Not yet writable
+    }
+    val_obj = val_obj_tmp;
+  }
+
+  int index = arrayOopDesc::base_offset_in_bytes(T_OBJECT) + vtarg_index * type2aelembytes(T_OBJECT);
+  load_heap_oop(val_obj, Address(val_array, index));
+
+  ScalarizedInlineArgsStream stream(sig, sig_index, from, from_count, from_index);
+  VMReg fromReg;
+  BasicType bt;
+  Label L_null;
+  while (stream.next(fromReg, bt)) {
+    assert(fromReg->is_valid(), "source must be valid");
+    reg_state[fromReg->value()] = reg_writable;
+
+    int off = sig->at(stream.sig_index())._offset;
+    if (off == -1) {
+      // Nullable inline type argument, emit null check
+      Label L_notNull;
+      if (fromReg->is_stack()) {
+        int ld_off = fromReg->reg2stack() * VMRegImpl::stack_slot_size + wordSize;
+        testb(Address(rsp, ld_off), 1);
+      } else {
+        testb(fromReg->as_Register(), 1);
+      }
+      jcc(Assembler::notZero, L_notNull);
+      movptr(val_obj, 0);
+      jmp(L_null);
+      bind(L_notNull);
+      continue;
+    }
+
+    assert(off > 0, "offset in object should be positive");
+    size_t size_in_bytes = is_java_primitive(bt) ? type2aelembytes(bt) : wordSize;
+
+    Address dst(val_obj, off);
+    if (!fromReg->is_XMMRegister()) {
+      Register src;
+      if (fromReg->is_stack()) {
+        src = from_reg_tmp;
+        int ld_off = fromReg->reg2stack() * VMRegImpl::stack_slot_size + wordSize;
+        load_sized_value(src, Address(rsp, ld_off), size_in_bytes, /* is_signed */ false);
+      } else {
+        src = fromReg->as_Register();
+      }
+      assert_different_registers(dst.base(), src, tmp1, tmp2, tmp3, val_array);
+      if (is_reference_type(bt)) {
+        store_heap_oop(dst, src, tmp1, tmp2, tmp3, IN_HEAP | ACCESS_WRITE | IS_DEST_UNINITIALIZED);
+      } else {
+        store_sized_value(dst, src, size_in_bytes);
+      }
+    } else if (bt == T_DOUBLE) {
+      movdbl(dst, fromReg->as_XMMRegister());
+    } else {
+      assert(bt == T_FLOAT, "must be float");
+      movflt(dst, fromReg->as_XMMRegister());
+    }
+  }
+  bind(L_null);
+  sig_index = stream.sig_index();
+  from_index = stream.regs_index();
+
+  assert(reg_state[to->value()] == reg_writable, "must have already been read");
+  bool success = move_helper(val_obj->as_VMReg(), to, T_OBJECT, reg_state);
+  assert(success, "to register must be writeable");
+  return true;
+}
+
+VMReg MacroAssembler::spill_reg_for(VMReg reg) {
+  return reg->is_XMMRegister() ? xmm8->as_VMReg() : r14->as_VMReg();
+}
+
+void MacroAssembler::remove_frame(int initial_framesize, bool needs_stack_repair) {
+  assert((initial_framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned");
+  if (needs_stack_repair) {
+    movq(rbp, Address(rsp, initial_framesize));
+    // The stack increment resides just below the saved rbp
+    addq(rsp, Address(rsp, initial_framesize - wordSize));
+  } else {
+    if (initial_framesize > 0) {
+      addq(rsp, initial_framesize);
+    }
+    pop(rbp);
+  }
+}
+
 // Clearing constant sized memory using YMM/ZMM registers.
 void MacroAssembler::clear_mem(Register base, int cnt, Register rtmp, XMMRegister xtmp, KRegister mask) {
   assert(UseAVX > 2 && VM_Version::supports_avx512vlbw(), "");
   bool use64byteVector = (MaxVectorSize > 32) && (VM_Version::avx3_threshold() == 0);
 
@@ -5828,25 +6605,22 @@
         break;
     }
   }
 }
 
-void MacroAssembler::clear_mem(Register base, Register cnt, Register tmp, XMMRegister xtmp,
-                               bool is_large, KRegister mask) {
+void MacroAssembler::clear_mem(Register base, Register cnt, Register val, XMMRegister xtmp,
+                               bool is_large, bool word_copy_only, KRegister mask) {
   // cnt      - number of qwords (8-byte words).
   // base     - start address, qword aligned.
   // is_large - if optimizers know cnt is larger than InitArrayShortSize
   assert(base==rdi, "base register must be edi for rep stos");
-  assert(tmp==rax,   "tmp register must be eax for rep stos");
+  assert(val==rax,   "val register must be eax for rep stos");
   assert(cnt==rcx,   "cnt register must be ecx for rep stos");
   assert(InitArrayShortSize % BytesPerLong == 0,
     "InitArrayShortSize should be the multiple of BytesPerLong");
 
   Label DONE;
-  if (!is_large || !UseXMMForObjInit) {
-    xorptr(tmp, tmp);
-  }
 
   if (!is_large) {
     Label LOOP, LONG;
     cmpptr(cnt, InitArrayShortSize/BytesPerLong);
     jccb(Assembler::greater, LONG);
@@ -5856,24 +6630,24 @@
     decrement(cnt);
     jccb(Assembler::negative, DONE); // Zero length
 
     // Use individual pointer-sized stores for small counts:
     BIND(LOOP);
-    movptr(Address(base, cnt, Address::times_ptr), tmp);
+    movptr(Address(base, cnt, Address::times_ptr), val);
     decrement(cnt);
     jccb(Assembler::greaterEqual, LOOP);
     jmpb(DONE);
 
     BIND(LONG);
   }
 
   // Use longer rep-prefixed ops for non-small counts:
-  if (UseFastStosb) {
+  if (UseFastStosb && !word_copy_only) {
     shlptr(cnt, 3); // convert to number of bytes
     rep_stosb();
   } else if (UseXMMForObjInit) {
-    xmm_clear_mem(base, cnt, tmp, xtmp, mask);
+    xmm_clear_mem(base, cnt, val, xtmp, mask);
   } else {
     NOT_LP64(shlptr(cnt, 1);) // convert to number of 32-bit words for 32-bit VM
     rep_stos();
   }
 
@@ -9897,10 +10671,14 @@
   // Clear lock_mask bits (locked state).
   andptr(hdr, ~(int32_t)markWord::lock_mask_in_place);
   movptr(tmp, hdr);
   // Set unlocked_value bit.
   orptr(hdr, markWord::unlocked_value);
+  if (EnableValhalla) {
+    // Mask inline_type bit such that we go to the slow path if object is an inline type
+    andptr(hdr, ~((int) markWord::inline_type_bit_in_place));
+  }
   lock();
   cmpxchgptr(tmp, Address(obj, oopDesc::mark_offset_in_bytes()));
   jcc(Assembler::notEqual, slow);
 
   // If successful, push object to lock-stack.
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
@@ -29,13 +29,16 @@
 #include "asm/register.hpp"
 #include "code/vmreg.inline.hpp"
 #include "compiler/oopMap.hpp"
 #include "utilities/macros.hpp"
 #include "runtime/rtmLocking.hpp"
+#include "runtime/signature.hpp"
 #include "runtime/vm_version.hpp"
 #include "utilities/checkedCast.hpp"
 
+class ciInlineKlass;
+
 // MacroAssembler extends Assembler by frequently used macros.
 //
 // Instructions for which a 'better' code sequence exists depending
 // on arguments should also go in here.
 
@@ -101,10 +104,42 @@
 
   void null_check(Register reg, int offset = -1);
   static bool needs_explicit_null_check(intptr_t offset);
   static bool uses_implicit_null_check(void* address);
 
+  // markWord tests, kills markWord reg
+  void test_markword_is_inline_type(Register markword, Label& is_inline_type);
+
+  // inlineKlass queries, kills temp_reg
+  void test_klass_is_inline_type(Register klass, Register temp_reg, Label& is_inline_type);
+  void test_klass_is_empty_inline_type(Register klass, Register temp_reg, Label& is_empty_inline_type);
+  void test_oop_is_not_inline_type(Register object, Register tmp, Label& not_inline_type);
+
+  // Get the default value oop for the given InlineKlass
+  void get_default_value_oop(Register inline_klass, Register temp_reg, Register obj);
+  // The empty value oop, for the given InlineKlass ("empty" as in no instance fields)
+  // get_default_value_oop with extra assertion for empty inline klass
+  void get_empty_inline_type_oop(Register inline_klass, Register temp_reg, Register obj);
+
+  void test_field_is_null_free_inline_type(Register flags, Register temp_reg, Label& is_null_free);
+  void test_field_is_not_null_free_inline_type(Register flags, Register temp_reg, Label& not_null_free);
+  void test_field_is_flat(Register flags, Register temp_reg, Label& is_flat);
+  void test_field_has_null_marker(Register flags, Register temp_reg, Label& has_null_marker);
+
+  // Check oops for special arrays, i.e. flat arrays and/or null-free arrays
+  void test_oop_prototype_bit(Register oop, Register temp_reg, int32_t test_bit, bool jmp_set, Label& jmp_label);
+  void test_flat_array_oop(Register oop, Register temp_reg, Label& is_flat_array);
+  void test_non_flat_array_oop(Register oop, Register temp_reg, Label& is_non_flat_array);
+  void test_null_free_array_oop(Register oop, Register temp_reg, Label& is_null_free_array);
+  void test_non_null_free_array_oop(Register oop, Register temp_reg, Label& is_non_null_free_array);
+
+  // Check array klass layout helper for flat or null-free arrays...
+  void test_flat_array_layout(Register lh, Label& is_flat_array);
+  void test_non_flat_array_layout(Register lh, Label& is_non_flat_array);
+  void test_null_free_array_layout(Register lh, Label& is_null_free_array);
+  void test_non_null_free_array_layout(Register lh, Label& is_non_null_free_array);
+
   // Required platform-specific helpers for Label::patch_instructions.
   // They _shadow_ the declarations in AbstractAssembler, which are undefined.
   void pd_patch_instruction(address branch, address target, const char* file, int line) {
     unsigned char op = branch[0];
     assert(op == 0xE8 /* call */ ||
@@ -362,18 +397,28 @@
   void load_method_holder_cld(Register rresult, Register rmethod);
 
   void load_method_holder(Register holder, Register method);
 
   // oop manipulations
+  void load_metadata(Register dst, Register src);
   void load_klass(Register dst, Register src, Register tmp);
   void store_klass(Register dst, Register src, Register tmp);
 
   void access_load_at(BasicType type, DecoratorSet decorators, Register dst, Address src,
                       Register tmp1, Register thread_tmp);
   void access_store_at(BasicType type, DecoratorSet decorators, Address dst, Register val,
                        Register tmp1, Register tmp2, Register tmp3);
 
+  void access_value_copy(DecoratorSet decorators, Register src, Register dst, Register inline_klass);
+
+  // inline type data payload offsets...
+  void first_field_offset(Register inline_klass, Register offset);
+  void data_for_oop(Register oop, Register data, Register inline_klass);
+  // get data payload ptr a flat value array at index, kills rcx and index
+  void data_for_value_array_index(Register array, Register array_klass,
+                                  Register index, Register data);
+
   void load_heap_oop(Register dst, Address src, Register tmp1 = noreg,
                      Register thread_tmp = noreg, DecoratorSet decorators = 0);
   void load_heap_oop_not_null(Register dst, Address src, Register tmp1 = noreg,
                               Register thread_tmp = noreg, DecoratorSet decorators = 0);
   void store_heap_oop(Address dst, Register val, Register tmp1 = noreg,
@@ -381,10 +426,12 @@
 
   // Used for storing null. All other oop constants should be
   // stored using routines that take a jobject.
   void store_heap_oop_null(Address dst);
 
+  void load_prototype_header(Register dst, Register src, Register tmp);
+
 #ifdef _LP64
   void store_klass_gap(Register dst, Register src);
 
   // This dummy is to prevent a call to store_heap_oop from
   // converting a zero (like null) into a Register by giving
@@ -582,10 +629,19 @@
   void pop_call_clobbered_registers(bool restore_fpu = true) {
     pop_call_clobbered_registers_except(RegSet(), restore_fpu);
   }
 
   // allocation
+
+  // Object / value buffer allocation...
+  // Allocate instance of klass, assumes klass initialized by caller
+  // new_obj prefers to be rax
+  // Kills t1 and t2, perserves klass, return allocation in new_obj (rsi on LP64)
+  void allocate_instance(Register klass, Register new_obj,
+                         Register t1, Register t2,
+                         bool clear_fields, Label& alloc_failed);
+
   void tlab_allocate(
     Register thread,                   // Current thread
     Register obj,                      // result: pointer to object after successful allocation
     Register var_size_in_bytes,        // object size in bytes if unknown at compile time; invalid otherwise
     int      con_size_in_bytes,        // object size in bytes if   known at compile time
@@ -593,10 +649,13 @@
     Register t2,                       // temp register
     Label&   slow_case                 // continuation point if fast allocation fails
   );
   void zero_memory(Register address, Register length_in_bytes, int offset_in_bytes, Register temp);
 
+  // For field "index" within "klass", return inline_klass ...
+  void get_inline_type_field_klass(Register klass, Register index, Register inline_klass);
+
   // interface method calling
   void lookup_interface_method(Register recv_klass,
                                Register intf_klass,
                                RegisterOrConstant itable_index,
                                Register method_result,
@@ -753,11 +812,12 @@
     if (src.is_constant()) addptr(dst, checked_cast<int>(src.as_constant()));
     else                   addptr(dst, src.as_register());
   }
 
   void andptr(Register dst, int32_t src);
-  void andptr(Register src1, Register src2) { LP64_ONLY(andq(src1, src2)) NOT_LP64(andl(src1, src2)) ; }
+  void andptr(Register dst, Register src) { LP64_ONLY(andq(dst, src)) NOT_LP64(andl(dst, src)) ; }
+  void andptr(Register dst, Address src) { LP64_ONLY(andq(dst, src)) NOT_LP64(andl(dst, src)) ; }
 
 #ifdef _LP64
   using Assembler::andq;
   void andq(Register dst, AddressLiteral src, Register rscratch = noreg);
 #endif
@@ -1861,13 +1921,28 @@
   void movl2ptr(Register dst, Address src) { LP64_ONLY(movslq(dst, src)) NOT_LP64(movl(dst, src)); }
   void movl2ptr(Register dst, Register src) { LP64_ONLY(movslq(dst, src)) NOT_LP64(if (dst != src) movl(dst, src)); }
 
 
  public:
+  // Inline type specific methods
+  #include "asm/macroAssembler_common.hpp"
+
+  int store_inline_type_fields_to_buf(ciInlineKlass* vk, bool from_interpreter = true);
+  bool move_helper(VMReg from, VMReg to, BasicType bt, RegState reg_state[]);
+  bool unpack_inline_helper(const GrowableArray<SigEntry>* sig, int& sig_index,
+                            VMReg from, int& from_index, VMRegPair* to, int to_count, int& to_index,
+                            RegState reg_state[]);
+  bool pack_inline_helper(const GrowableArray<SigEntry>* sig, int& sig_index, int vtarg_index,
+                          VMRegPair* from, int from_count, int& from_index, VMReg to,
+                          RegState reg_state[], Register val_array);
+  int extend_stack_for_inline_args(int args_on_stack);
+  void remove_frame(int initial_framesize, bool needs_stack_repair);
+  VMReg spill_reg_for(VMReg reg);
+
   // clear memory of size 'cnt' qwords, starting at 'base';
   // if 'is_large' is set, do not try to produce short loop
-  void clear_mem(Register base, Register cnt, Register rtmp, XMMRegister xtmp, bool is_large, KRegister mask=knoreg);
+  void clear_mem(Register base, Register cnt, Register val, XMMRegister xtmp, bool is_large, bool word_copy_only, KRegister mask=knoreg);
 
   // clear memory initialization sequence for constant size;
   void clear_mem(Register base, int cnt, Register rtmp, XMMRegister xtmp, KRegister mask=knoreg);
 
   // clear memory of size 'cnt' qwords, starting at 'base' using XMM/YMM registers
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
@@ -152,11 +152,15 @@
     __ jccb(Assembler::zero, run_compiled_code);
     __ jmp(Address(method, Method::interpreter_entry_offset()));
     __ BIND(run_compiled_code);
   }
 
-  const ByteSize entry_offset = for_compiler_entry ? Method::from_compiled_offset() :
+  // The following jump might pass an inline type argument that was erased to Object as oop to a
+  // callee that expects inline type arguments to be passed as fields. We need to call the compiled
+  // value entry (_code->inline_entry_point() or _adapter->c2i_inline_entry()) which will take care
+  // of translating between the calling conventions.
+  const ByteSize entry_offset = for_compiler_entry ? Method::from_compiled_inline_offset() :
                                                      Method::from_interpreted_offset();
   __ jmp(Address(method, entry_offset));
 
   __ bind(L_no_such_method);
   __ jump(RuntimeAddress(StubRoutines::throw_AbstractMethodError_entry()));
diff a/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp
--- a/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp
+++ b/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp
@@ -529,10 +529,19 @@
   }
 
   return stack;
 }
 
+const uint SharedRuntime::java_return_convention_max_int = 1;
+const uint SharedRuntime::java_return_convention_max_float = 1;
+int SharedRuntime::java_return_convention(const BasicType *sig_bt,
+                                          VMRegPair *regs,
+                                          int total_args_passed) {
+  Unimplemented();
+  return 0;
+}
+
 // Patch the callers callsite with entry to compiled code if it exists.
 static void patch_callers_callsite(MacroAssembler *masm) {
   Label L;
   __ cmpptr(Address(rbx, in_bytes(Method::code_offset())), NULL_WORD);
   __ jcc(Assembler::equal, L);
@@ -590,15 +599,17 @@
   int next_off = st_off - Interpreter::stackElementSize;
   __ movdbl(Address(rsp, next_off), r);
 }
 
 static void gen_c2i_adapter(MacroAssembler *masm,
-                            int total_args_passed,
-                            int comp_args_on_stack,
-                            const BasicType *sig_bt,
+                            const GrowableArray<SigEntry>& sig_extended,
                             const VMRegPair *regs,
-                            Label& skip_fixup) {
+                            Label& skip_fixup,
+                            address start,
+                            OopMapSet*& oop_maps,
+                            int& frame_complete,
+                            int& frame_size_in_words) {
   // Before we get into the guts of the C2I adapter, see if we should be here
   // at all.  We've come from compiled code and are attempting to jump to the
   // interpreter, which means the caller made a static call to get here
   // (vcalls always get a compiled target if there is one).  Check for a
   // compiled target.  If there is one, we need to patch the caller's call.
@@ -616,29 +627,29 @@
 #endif /* COMPILER2 */
 
   // Since all args are passed on the stack, total_args_passed * interpreter_
   // stack_element_size  is the
   // space we need.
-  int extraspace = total_args_passed * Interpreter::stackElementSize;
+  int extraspace = sig_extended.length() * Interpreter::stackElementSize;
 
   // Get return address
   __ pop(rax);
 
   // set senderSP value
   __ movptr(rsi, rsp);
 
   __ subptr(rsp, extraspace);
 
   // Now write the args into the outgoing interpreter space
-  for (int i = 0; i < total_args_passed; i++) {
-    if (sig_bt[i] == T_VOID) {
-      assert(i > 0 && (sig_bt[i-1] == T_LONG || sig_bt[i-1] == T_DOUBLE), "missing half");
+  for (int i = 0; i < sig_extended.length(); i++) {
+    if (sig_extended.at(i)._bt == T_VOID) {
+      assert(i > 0 && (sig_extended.at(i-1)._bt == T_LONG || sig_extended.at(i-1)._bt == T_DOUBLE), "missing half");
       continue;
     }
 
     // st_off points to lowest address on stack.
-    int st_off = ((total_args_passed - 1) - i) * Interpreter::stackElementSize;
+    int st_off = ((sig_extended.length() - 1) - i) * Interpreter::stackElementSize;
     int next_off = st_off - Interpreter::stackElementSize;
 
     // Say 4 args:
     // i   st_off
     // 0   12 T_LONG
@@ -680,11 +691,11 @@
     } else {
       assert(r_1->is_XMMRegister(), "");
       if (!r_2->is_valid()) {
         __ movflt(Address(rsp, st_off), r_1->as_XMMRegister());
       } else {
-        assert(sig_bt[i] == T_DOUBLE || sig_bt[i] == T_LONG, "wrong type");
+        assert(sig_extended.at(i)._bt == T_DOUBLE || sig_extended.at(i)._bt == T_LONG, "wrong type");
         move_c2i_double(masm, r_1->as_XMMRegister(), st_off);
       }
     }
   }
 
@@ -713,14 +724,14 @@
   __ jcc(Assembler::below, L_ok);
   __ bind(L_fail);
 }
 
 void SharedRuntime::gen_i2c_adapter(MacroAssembler *masm,
-                                    int total_args_passed,
                                     int comp_args_on_stack,
-                                    const BasicType *sig_bt,
+                                    const GrowableArray<SigEntry>& sig_extended,
                                     const VMRegPair *regs) {
+
   // Note: rsi contains the senderSP on entry. We must preserve it since
   // we may do a i2c -> c2i transition if we lose a race where compiled
   // code goes non-entrant while we get args ready.
 
   // Adapters can be frameless because they do not require the caller
@@ -810,24 +821,24 @@
   // Pre-load the register-jump target early, to schedule it better.
   __ movptr(rdi, Address(rbx, in_bytes(Method::from_compiled_offset())));
 
   // Now generate the shuffle code.  Pick up all register args and move the
   // rest through the floating point stack top.
-  for (int i = 0; i < total_args_passed; i++) {
-    if (sig_bt[i] == T_VOID) {
+  for (int i = 0; i < sig_extended.length(); i++) {
+    if (sig_extended.at(i)._bt == T_VOID) {
       // Longs and doubles are passed in native word order, but misaligned
       // in the 32-bit build.
-      assert(i > 0 && (sig_bt[i-1] == T_LONG || sig_bt[i-1] == T_DOUBLE), "missing half");
+      assert(i > 0 && (sig_extended.at(i-1)._bt == T_LONG || sig_extended.at(i-1)._bt == T_DOUBLE), "missing half");
       continue;
     }
 
     // Pick up 0, 1 or 2 words from SP+offset.
 
     assert(!regs[i].second()->is_valid() || regs[i].first()->next() == regs[i].second(),
             "scrambled load targets?");
     // Load in argument order going down.
-    int ld_off = (total_args_passed - i) * Interpreter::stackElementSize;
+    int ld_off = (sig_extended.length() - i) * Interpreter::stackElementSize;
     // Point to interpreter value (vs. tag)
     int next_off = ld_off - Interpreter::stackElementSize;
     //
     //
     //
@@ -921,18 +932,18 @@
   __ jmp(rdi);
 }
 
 // ---------------------------------------------------------------
 AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm,
-                                                            int total_args_passed,
                                                             int comp_args_on_stack,
-                                                            const BasicType *sig_bt,
+                                                            const GrowableArray<SigEntry>& sig_extended,
                                                             const VMRegPair *regs,
-                                                            AdapterFingerPrint* fingerprint) {
+                                                            AdapterFingerPrint* fingerprint,
+                                                            AdapterBlob*& new_adapter) {
   address i2c_entry = __ pc();
 
-  gen_i2c_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs);
+  gen_i2c_adapter(masm, comp_args_on_stack, sig_extended, regs);
 
   // -------------------------------------------------------------------------
   // Generate a C2I adapter.  On entry we know rbx, holds the Method* during calls
   // to the interpreter.  The args start out packed in the compiled layout.  They
   // need to be unpacked into the interpreter layout.  This will almost always
@@ -968,12 +979,16 @@
   address c2i_entry = __ pc();
 
   BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
   bs->c2i_entry_barrier(masm);
 
-  gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup);
+  OopMapSet* oop_maps = nullptr;
+  int frame_complete = CodeOffsets::frame_never_safe;
+  int frame_size_in_words = 0;
+  gen_c2i_adapter(masm, sig_extended, regs, skip_fixup, i2c_entry, oop_maps, frame_complete, frame_size_in_words);
 
+  new_adapter = AdapterBlob::create(masm->code(), frame_complete, frame_size_in_words, oop_maps);
   return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
 }
 
 int SharedRuntime::c_calling_convention(const BasicType *sig_bt,
                                          VMRegPair *regs,
@@ -2849,5 +2864,10 @@
 
   // return the  blob
   // frame_size_words or bytes??
   return RuntimeStub::new_runtime_stub(name, &buffer, frame_complete, frame_size_words, oop_maps, true);
 }
+
+BufferedInlineTypeBlob* SharedRuntime::generate_buffered_inline_type_adapter(const InlineKlass* vk) {
+  Unimplemented();
+  return nullptr;
+}
diff a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp
--- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp
+++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp
@@ -26,10 +26,11 @@
 #ifndef _WINDOWS
 #include "alloca.h"
 #endif
 #include "asm/macroAssembler.hpp"
 #include "asm/macroAssembler.inline.hpp"
+#include "classfile/symbolTable.hpp"
 #include "code/compiledIC.hpp"
 #include "code/debugInfoRec.hpp"
 #include "code/icBuffer.hpp"
 #include "code/nativeInst.hpp"
 #include "code/vtableStubs.hpp"
@@ -560,10 +561,91 @@
   }
 
   return stk_args;
 }
 
+// Same as java_calling_convention() but for multiple return
+// values. There's no way to store them on the stack so if we don't
+// have enough registers, multiple values can't be returned.
+const uint SharedRuntime::java_return_convention_max_int = Argument::n_int_register_parameters_j+1;
+const uint SharedRuntime::java_return_convention_max_float = Argument::n_float_register_parameters_j;
+int SharedRuntime::java_return_convention(const BasicType *sig_bt,
+                                          VMRegPair *regs,
+                                          int total_args_passed) {
+  // Create the mapping between argument positions and
+  // registers.
+  static const Register INT_ArgReg[java_return_convention_max_int] = {
+    rax, j_rarg5, j_rarg4, j_rarg3, j_rarg2, j_rarg1, j_rarg0
+  };
+  static const XMMRegister FP_ArgReg[java_return_convention_max_float] = {
+    j_farg0, j_farg1, j_farg2, j_farg3,
+    j_farg4, j_farg5, j_farg6, j_farg7
+  };
+
+
+  uint int_args = 0;
+  uint fp_args = 0;
+
+  for (int i = 0; i < total_args_passed; i++) {
+    switch (sig_bt[i]) {
+    case T_BOOLEAN:
+    case T_CHAR:
+    case T_BYTE:
+    case T_SHORT:
+    case T_INT:
+      if (int_args < Argument::n_int_register_parameters_j+1) {
+        regs[i].set1(INT_ArgReg[int_args]->as_VMReg());
+        int_args++;
+      } else {
+        return -1;
+      }
+      break;
+    case T_VOID:
+      // halves of T_LONG or T_DOUBLE
+      assert(i != 0 && (sig_bt[i - 1] == T_LONG || sig_bt[i - 1] == T_DOUBLE), "expecting half");
+      regs[i].set_bad();
+      break;
+    case T_LONG:
+      assert(sig_bt[i + 1] == T_VOID, "expecting half");
+      // fall through
+    case T_OBJECT:
+    case T_ARRAY:
+    case T_ADDRESS:
+    case T_METADATA:
+      if (int_args < Argument::n_int_register_parameters_j+1) {
+        regs[i].set2(INT_ArgReg[int_args]->as_VMReg());
+        int_args++;
+      } else {
+        return -1;
+      }
+      break;
+    case T_FLOAT:
+      if (fp_args < Argument::n_float_register_parameters_j) {
+        regs[i].set1(FP_ArgReg[fp_args]->as_VMReg());
+        fp_args++;
+      } else {
+        return -1;
+      }
+      break;
+    case T_DOUBLE:
+      assert(sig_bt[i + 1] == T_VOID, "expecting half");
+      if (fp_args < Argument::n_float_register_parameters_j) {
+        regs[i].set2(FP_ArgReg[fp_args]->as_VMReg());
+        fp_args++;
+      } else {
+        return -1;
+      }
+      break;
+    default:
+      ShouldNotReachHere();
+      break;
+    }
+  }
+
+  return int_args + fp_args;
+}
+
 // Patch the callers callsite with entry to compiled code if it exists.
 static void patch_callers_callsite(MacroAssembler *masm) {
   Label L;
   __ cmpptr(Address(rbx, in_bytes(Method::code_offset())), NULL_WORD);
   __ jcc(Assembler::equal, L);
@@ -602,29 +684,205 @@
   // restore sp
   __ mov(rsp, r13);
   __ bind(L);
 }
 
+// For each inline type argument, sig includes the list of fields of
+// the inline type. This utility function computes the number of
+// arguments for the call if inline types are passed by reference (the
+// calling convention the interpreter expects).
+static int compute_total_args_passed_int(const GrowableArray<SigEntry>* sig_extended) {
+  int total_args_passed = 0;
+  if (InlineTypePassFieldsAsArgs) {
+    for (int i = 0; i < sig_extended->length(); i++) {
+      BasicType bt = sig_extended->at(i)._bt;
+      if (bt == T_METADATA) {
+        // In sig_extended, an inline type argument starts with:
+        // T_METADATA, followed by the types of the fields of the
+        // inline type and T_VOID to mark the end of the value
+        // type. Inline types are flattened so, for instance, in the
+        // case of an inline type with an int field and an inline type
+        // field that itself has 2 fields, an int and a long:
+        // T_METADATA T_INT T_METADATA T_INT T_LONG T_VOID (second
+        // slot for the T_LONG) T_VOID (inner inline type) T_VOID
+        // (outer inline type)
+        total_args_passed++;
+        int vt = 1;
+        do {
+          i++;
+          BasicType bt = sig_extended->at(i)._bt;
+          BasicType prev_bt = sig_extended->at(i-1)._bt;
+          if (bt == T_METADATA) {
+            vt++;
+          } else if (bt == T_VOID &&
+                     prev_bt != T_LONG &&
+                     prev_bt != T_DOUBLE) {
+            vt--;
+          }
+        } while (vt != 0);
+      } else {
+        total_args_passed++;
+      }
+    }
+  } else {
+    total_args_passed = sig_extended->length();
+  }
+  return total_args_passed;
+}
+
+
+static void gen_c2i_adapter_helper(MacroAssembler* masm,
+                                   BasicType bt,
+                                   BasicType prev_bt,
+                                   size_t size_in_bytes,
+                                   const VMRegPair& reg_pair,
+                                   const Address& to,
+                                   int extraspace,
+                                   bool is_oop) {
+  if (bt == T_VOID) {
+    assert(prev_bt == T_LONG || prev_bt == T_DOUBLE, "missing half");
+    return;
+  }
+
+  // Say 4 args:
+  // i   st_off
+  // 0   32 T_LONG
+  // 1   24 T_VOID
+  // 2   16 T_OBJECT
+  // 3    8 T_BOOL
+  // -    0 return address
+  //
+  // However to make thing extra confusing. Because we can fit a long/double in
+  // a single slot on a 64 bt vm and it would be silly to break them up, the interpreter
+  // leaves one slot empty and only stores to a single slot. In this case the
+  // slot that is occupied is the T_VOID slot. See I said it was confusing.
+
+  bool wide = (size_in_bytes == wordSize);
+  VMReg r_1 = reg_pair.first();
+  VMReg r_2 = reg_pair.second();
+  assert(r_2->is_valid() == wide, "invalid size");
+  if (!r_1->is_valid()) {
+    assert(!r_2->is_valid(), "must be invalid");
+    return;
+  }
+
+  if (!r_1->is_XMMRegister()) {
+    Register val = rax;
+    if (r_1->is_stack()) {
+      int ld_off = r_1->reg2stack() * VMRegImpl::stack_slot_size + extraspace;
+      __ load_sized_value(val, Address(rsp, ld_off), size_in_bytes, /* is_signed */ false);
+    } else {
+      val = r_1->as_Register();
+    }
+    assert_different_registers(to.base(), val, rscratch1);
+    if (is_oop) {
+      __ push(r13);
+      __ push(rbx);
+      __ store_heap_oop(to, val, rscratch1, r13, rbx, IN_HEAP | ACCESS_WRITE | IS_DEST_UNINITIALIZED);
+      __ pop(rbx);
+      __ pop(r13);
+    } else {
+      __ store_sized_value(to, val, size_in_bytes);
+    }
+  } else {
+    if (wide) {
+      __ movdbl(to, r_1->as_XMMRegister());
+    } else {
+      __ movflt(to, r_1->as_XMMRegister());
+    }
+  }
+}
 
 static void gen_c2i_adapter(MacroAssembler *masm,
-                            int total_args_passed,
-                            int comp_args_on_stack,
-                            const BasicType *sig_bt,
+                            const GrowableArray<SigEntry>* sig_extended,
                             const VMRegPair *regs,
-                            Label& skip_fixup) {
+                            bool requires_clinit_barrier,
+                            address& c2i_no_clinit_check_entry,
+                            Label& skip_fixup,
+                            address start,
+                            OopMapSet* oop_maps,
+                            int& frame_complete,
+                            int& frame_size_in_words,
+                            bool alloc_inline_receiver) {
+  if (requires_clinit_barrier && VM_Version::supports_fast_class_init_checks()) {
+    Label L_skip_barrier;
+    Register method = rbx;
+
+    { // Bypass the barrier for non-static methods
+      Register flags = rscratch1;
+      __ movl(flags, Address(method, Method::access_flags_offset()));
+      __ testl(flags, JVM_ACC_STATIC);
+      __ jcc(Assembler::zero, L_skip_barrier); // non-static
+    }
+
+    Register klass = rscratch1;
+    __ load_method_holder(klass, method);
+    __ clinit_barrier(klass, r15_thread, &L_skip_barrier /*L_fast_path*/);
+
+    __ jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); // slow path
+
+    __ bind(L_skip_barrier);
+    c2i_no_clinit_check_entry = __ pc();
+  }
+
+  BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
+  bs->c2i_entry_barrier(masm);
+
   // Before we get into the guts of the C2I adapter, see if we should be here
   // at all.  We've come from compiled code and are attempting to jump to the
   // interpreter, which means the caller made a static call to get here
   // (vcalls always get a compiled target if there is one).  Check for a
   // compiled target.  If there is one, we need to patch the caller's call.
   patch_callers_callsite(masm);
 
   __ bind(skip_fixup);
 
+  if (InlineTypePassFieldsAsArgs) {
+    // Is there an inline type argument?
+    bool has_inline_argument = false;
+    for (int i = 0; i < sig_extended->length() && !has_inline_argument; i++) {
+      has_inline_argument = (sig_extended->at(i)._bt == T_METADATA);
+    }
+    if (has_inline_argument) {
+      // There is at least an inline type argument: we're coming from
+      // compiled code so we have no buffers to back the inline types.
+      // Allocate the buffers here with a runtime call.
+      OopMap* map = RegisterSaver::save_live_registers(masm, 0, &frame_size_in_words, /*save_vectors*/ false);
+
+      frame_complete = __ offset();
+
+      __ set_last_Java_frame(noreg, noreg, nullptr, rscratch1);
+
+      __ mov(c_rarg0, r15_thread);
+      __ mov(c_rarg1, rbx);
+      __ mov64(c_rarg2, (int64_t)alloc_inline_receiver);
+      __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::allocate_inline_types)));
+
+      oop_maps->add_gc_map((int)(__ pc() - start), map);
+      __ reset_last_Java_frame(false);
+
+      RegisterSaver::restore_live_registers(masm);
+
+      Label no_exception;
+      __ cmpptr(Address(r15_thread, Thread::pending_exception_offset()), NULL_WORD);
+      __ jcc(Assembler::equal, no_exception);
+
+      __ movptr(Address(r15_thread, JavaThread::vm_result_offset()), NULL_WORD);
+      __ movptr(rax, Address(r15_thread, Thread::pending_exception_offset()));
+      __ jump(RuntimeAddress(StubRoutines::forward_exception_entry()));
+
+      __ bind(no_exception);
+
+      // We get an array of objects from the runtime call
+      __ get_vm_result(rscratch2, r15_thread); // Use rscratch2 (r11) as temporary because rscratch1 (r10) is trashed by movptr()
+      __ get_vm_result_2(rbx, r15_thread); // TODO: required to keep the callee Method live?
+    }
+  }
+
   // Since all args are passed on the stack, total_args_passed *
   // Interpreter::stackElementSize is the space we need.
-
+  int total_args_passed = compute_total_args_passed_int(sig_extended);
   assert(total_args_passed >= 0, "total_args_passed is %d", total_args_passed);
 
   int extraspace = (total_args_passed * Interpreter::stackElementSize);
 
   // stack is aligned, keep it that way
@@ -655,100 +913,96 @@
 #ifdef ASSERT
   __ check_stack_alignment(rsp, "callee stack not aligned", wordSize, rax);
 #endif
 
   // Now write the args into the outgoing interpreter space
-  for (int i = 0; i < total_args_passed; i++) {
-    if (sig_bt[i] == T_VOID) {
-      assert(i > 0 && (sig_bt[i-1] == T_LONG || sig_bt[i-1] == T_DOUBLE), "missing half");
-      continue;
-    }
-
-    // offset to start parameters
-    int st_off   = (total_args_passed - i) * Interpreter::stackElementSize;
-    int next_off = st_off - Interpreter::stackElementSize;
 
-    // Say 4 args:
-    // i   st_off
-    // 0   32 T_LONG
-    // 1   24 T_VOID
-    // 2   16 T_OBJECT
-    // 3    8 T_BOOL
-    // -    0 return address
-    //
-    // However to make thing extra confusing. Because we can fit a long/double in
-    // a single slot on a 64 bt vm and it would be silly to break them up, the interpreter
-    // leaves one slot empty and only stores to a single slot. In this case the
-    // slot that is occupied is the T_VOID slot. See I said it was confusing.
-
-    VMReg r_1 = regs[i].first();
-    VMReg r_2 = regs[i].second();
-    if (!r_1->is_valid()) {
-      assert(!r_2->is_valid(), "");
-      continue;
-    }
-    if (r_1->is_stack()) {
-      // memory to memory use rax
-      int ld_off = r_1->reg2stack() * VMRegImpl::stack_slot_size + extraspace;
-      if (!r_2->is_valid()) {
-        // sign extend??
-        __ movl(rax, Address(rsp, ld_off));
-        __ movptr(Address(rsp, st_off), rax);
-
-      } else {
-
-        __ movq(rax, Address(rsp, ld_off));
-
-        // Two VMREgs|OptoRegs can be T_OBJECT, T_ADDRESS, T_DOUBLE, T_LONG
-        // T_DOUBLE and T_LONG use two slots in the interpreter
-        if ( sig_bt[i] == T_LONG || sig_bt[i] == T_DOUBLE) {
-          // ld_off == LSW, ld_off+wordSize == MSW
-          // st_off == MSW, next_off == LSW
-          __ movq(Address(rsp, next_off), rax);
+  // next_arg_comp is the next argument from the compiler point of
+  // view (inline type fields are passed in registers/on the stack). In
+  // sig_extended, an inline type argument starts with: T_METADATA,
+  // followed by the types of the fields of the inline type and T_VOID
+  // to mark the end of the inline type. ignored counts the number of
+  // T_METADATA/T_VOID. next_vt_arg is the next inline type argument:
+  // used to get the buffer for that argument from the pool of buffers
+  // we allocated above and want to pass to the
+  // interpreter. next_arg_int is the next argument from the
+  // interpreter point of view (inline types are passed by reference).
+  for (int next_arg_comp = 0, ignored = 0, next_vt_arg = 0, next_arg_int = 0;
+       next_arg_comp < sig_extended->length(); next_arg_comp++) {
+    assert(ignored <= next_arg_comp, "shouldn't skip over more slots than there are arguments");
+    assert(next_arg_int <= total_args_passed, "more arguments for the interpreter than expected?");
+    BasicType bt = sig_extended->at(next_arg_comp)._bt;
+    int st_off = (total_args_passed - next_arg_int) * Interpreter::stackElementSize;
+    if (!InlineTypePassFieldsAsArgs || bt != T_METADATA) {
+      int next_off = st_off - Interpreter::stackElementSize;
+      const int offset = (bt == T_LONG || bt == T_DOUBLE) ? next_off : st_off;
+      const VMRegPair reg_pair = regs[next_arg_comp-ignored];
+      size_t size_in_bytes = reg_pair.second()->is_valid() ? 8 : 4;
+      gen_c2i_adapter_helper(masm, bt, next_arg_comp > 0 ? sig_extended->at(next_arg_comp-1)._bt : T_ILLEGAL,
+                             size_in_bytes, reg_pair, Address(rsp, offset), extraspace, false);
+      next_arg_int++;
 #ifdef ASSERT
-          // Overwrite the unused slot with known junk
-          __ mov64(rax, CONST64(0xdeadffffdeadaaaa));
-          __ movptr(Address(rsp, st_off), rax);
-#endif /* ASSERT */
-        } else {
-          __ movq(Address(rsp, st_off), rax);
-        }
+      if (bt == T_LONG || bt == T_DOUBLE) {
+        // Overwrite the unused slot with known junk
+        __ mov64(rax, CONST64(0xdeadffffdeadaaaa));
+        __ movptr(Address(rsp, st_off), rax);
       }
-    } else if (r_1->is_Register()) {
-      Register r = r_1->as_Register();
-      if (!r_2->is_valid()) {
-        // must be only an int (or less ) so move only 32bits to slot
-        // why not sign extend??
-        __ movl(Address(rsp, st_off), r);
-      } else {
-        // Two VMREgs|OptoRegs can be T_OBJECT, T_ADDRESS, T_DOUBLE, T_LONG
-        // T_DOUBLE and T_LONG use two slots in the interpreter
-        if ( sig_bt[i] == T_LONG || sig_bt[i] == T_DOUBLE) {
-          // long/double in gpr
-#ifdef ASSERT
-          // Overwrite the unused slot with known junk
-          __ mov64(rax, CONST64(0xdeadffffdeadaaab));
-          __ movptr(Address(rsp, st_off), rax);
 #endif /* ASSERT */
-          __ movq(Address(rsp, next_off), r);
+    } else {
+      ignored++;
+      // get the buffer from the just allocated pool of buffers
+      int index = arrayOopDesc::base_offset_in_bytes(T_OBJECT) + next_vt_arg * type2aelembytes(T_OBJECT);
+      __ load_heap_oop(r14, Address(rscratch2, index));
+      next_vt_arg++; next_arg_int++;
+      int vt = 1;
+      // write fields we get from compiled code in registers/stack
+      // slots to the buffer: we know we are done with that inline type
+      // argument when we hit the T_VOID that acts as an end of inline
+      // type delimiter for this inline type. Inline types are flattened
+      // so we might encounter embedded inline types. Each entry in
+      // sig_extended contains a field offset in the buffer.
+      Label L_null;
+      do {
+        next_arg_comp++;
+        BasicType bt = sig_extended->at(next_arg_comp)._bt;
+        BasicType prev_bt = sig_extended->at(next_arg_comp-1)._bt;
+        if (bt == T_METADATA) {
+          vt++;
+          ignored++;
+        } else if (bt == T_VOID &&
+                   prev_bt != T_LONG &&
+                   prev_bt != T_DOUBLE) {
+          vt--;
+          ignored++;
         } else {
-          __ movptr(Address(rsp, st_off), r);
+          int off = sig_extended->at(next_arg_comp)._offset;
+          if (off == -1) {
+            // Nullable inline type argument, emit null check
+            VMReg reg = regs[next_arg_comp-ignored].first();
+            Label L_notNull;
+            if (reg->is_stack()) {
+              int ld_off = reg->reg2stack() * VMRegImpl::stack_slot_size + extraspace;
+              __ testb(Address(rsp, ld_off), 1);
+            } else {
+              __ testb(reg->as_Register(), 1);
+            }
+            __ jcc(Assembler::notZero, L_notNull);
+            __ movptr(Address(rsp, st_off), 0);
+            __ jmp(L_null);
+            __ bind(L_notNull);
+            continue;
+          }
+          assert(off > 0, "offset in object should be positive");
+          size_t size_in_bytes = is_java_primitive(bt) ? type2aelembytes(bt) : wordSize;
+          bool is_oop = is_reference_type(bt);
+          gen_c2i_adapter_helper(masm, bt, next_arg_comp > 0 ? sig_extended->at(next_arg_comp-1)._bt : T_ILLEGAL,
+                                 size_in_bytes, regs[next_arg_comp-ignored], Address(r14, off), extraspace, is_oop);
         }
-      }
-    } else {
-      assert(r_1->is_XMMRegister(), "");
-      if (!r_2->is_valid()) {
-        // only a float use just part of the slot
-        __ movflt(Address(rsp, st_off), r_1->as_XMMRegister());
-      } else {
-#ifdef ASSERT
-        // Overwrite the unused slot with known junk
-        __ mov64(rax, CONST64(0xdeadffffdeadaaac));
-        __ movptr(Address(rsp, st_off), rax);
-#endif /* ASSERT */
-        __ movdbl(Address(rsp, next_off), r_1->as_XMMRegister());
-      }
+      } while (vt != 0);
+      // pass the buffer to the interpreter
+      __ movptr(Address(rsp, st_off), r14);
+      __ bind(L_null);
     }
   }
 
   // Schedule the branch target address early.
   __ movptr(rcx, Address(rbx, in_bytes(Method::interpreter_entry_offset())));
@@ -767,13 +1021,12 @@
   __ jcc(Assembler::below, L_ok);
   __ bind(L_fail);
 }
 
 void SharedRuntime::gen_i2c_adapter(MacroAssembler *masm,
-                                    int total_args_passed,
                                     int comp_args_on_stack,
-                                    const BasicType *sig_bt,
+                                    const GrowableArray<SigEntry>* sig,
                                     const VMRegPair *regs) {
 
   // Note: r13 contains the senderSP on entry. We must preserve it since
   // we may do a i2c -> c2i transition if we lose a race where compiled
   // code goes non-entrant while we get args ready.
@@ -860,11 +1113,11 @@
   const Register saved_sp = rax;
   __ movptr(saved_sp, r11);
 
   // Will jump to the compiled code just as if compiled code was doing it.
   // Pre-load the register-jump target early, to schedule it better.
-  __ movptr(r11, Address(rbx, in_bytes(Method::from_compiled_offset())));
+  __ movptr(r11, Address(rbx, in_bytes(Method::from_compiled_inline_offset())));
 
 #if INCLUDE_JVMCI
   if (EnableJVMCI) {
     // check if this call should be routed towards a specific entry point
     __ cmpptr(Address(r15_thread, in_bytes(JavaThread::jvmci_alternate_call_target_offset())), 0);
@@ -874,17 +1127,21 @@
     __ movptr(Address(r15_thread, in_bytes(JavaThread::jvmci_alternate_call_target_offset())), 0);
     __ bind(no_alternative_target);
   }
 #endif // INCLUDE_JVMCI
 
+  int total_args_passed = sig->length();
+
   // Now generate the shuffle code.  Pick up all register args and move the
   // rest through the floating point stack top.
   for (int i = 0; i < total_args_passed; i++) {
-    if (sig_bt[i] == T_VOID) {
+    BasicType bt = sig->at(i)._bt;
+    if (bt == T_VOID) {
       // Longs and doubles are passed in native word order, but misaligned
       // in the 32-bit build.
-      assert(i > 0 && (sig_bt[i-1] == T_LONG || sig_bt[i-1] == T_DOUBLE), "missing half");
+      BasicType prev_bt = (i > 0) ? sig->at(i-1)._bt : T_ILLEGAL;
+      assert(i > 0 && (prev_bt == T_LONG || prev_bt == T_DOUBLE), "missing half");
       continue;
     }
 
     // Pick up 0, 1 or 2 words from SP+offset.
 
@@ -922,11 +1179,11 @@
         //
         // Interpreter local[n] == MSW, local[n+1] == LSW however locals
         // are accessed as negative so LSW is at LOW address
 
         // ld_off is MSW so get LSW
-        const int offset = (sig_bt[i]==T_LONG||sig_bt[i]==T_DOUBLE)?
+        const int offset = (bt==T_LONG||bt==T_DOUBLE)?
                            next_off : ld_off;
         __ movq(r13, Address(saved_sp, offset));
         // st_off is LSW (i.e. reg.first())
         __ movq(Address(rsp, st_off), r13);
       }
@@ -937,11 +1194,11 @@
         //
         // We are using two VMRegs. This can be either T_OBJECT, T_ADDRESS, T_LONG, or T_DOUBLE
         // the interpreter allocates two slots but only uses one for thr T_LONG or T_DOUBLE case
         // So we must adjust where to pick up the data to match the interpreter.
 
-        const int offset = (sig_bt[i]==T_LONG||sig_bt[i]==T_DOUBLE)?
+        const int offset = (bt==T_LONG||bt==T_DOUBLE)?
                            next_off : ld_off;
 
         // this can be a misaligned move
         __ movq(r, Address(saved_sp, offset));
       } else {
@@ -970,91 +1227,108 @@
   // and the vm will find there should this case occur.
 
   __ movptr(Address(r15_thread, JavaThread::callee_target_offset()), rbx);
 
   // put Method* where a c2i would expect should we end up there
-  // only needed because eof c2 resolve stubs return Method* as a result in
+  // only needed because of c2 resolve stubs return Method* as a result in
   // rax
   __ mov(rax, rbx);
   __ jmp(r11);
 }
 
+static void gen_inline_cache_check(MacroAssembler *masm, Label& skip_fixup) {
+  Label ok;
+
+  Register holder = rax;
+  Register receiver = j_rarg0;
+  Register temp = rbx;
+
+  __ load_klass(temp, receiver, rscratch1);
+  __ cmpptr(temp, Address(holder, CompiledICHolder::holder_klass_offset()));
+  __ movptr(rbx, Address(holder, CompiledICHolder::holder_metadata_offset()));
+  __ jcc(Assembler::equal, ok);
+  __ jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub()));
+
+  __ bind(ok);
+  // Method might have been compiled since the call site was patched to
+  // interpreted if that is the case treat it as a miss so we can get
+  // the call site corrected.
+  __ cmpptr(Address(rbx, in_bytes(Method::code_offset())), NULL_WORD);
+  __ jcc(Assembler::equal, skip_fixup);
+  __ jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub()));
+}
+
 // ---------------------------------------------------------------
-AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm,
-                                                            int total_args_passed,
+AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler* masm,
                                                             int comp_args_on_stack,
-                                                            const BasicType *sig_bt,
-                                                            const VMRegPair *regs,
-                                                            AdapterFingerPrint* fingerprint) {
+                                                            const GrowableArray<SigEntry>* sig,
+                                                            const VMRegPair* regs,
+                                                            const GrowableArray<SigEntry>* sig_cc,
+                                                            const VMRegPair* regs_cc,
+                                                            const GrowableArray<SigEntry>* sig_cc_ro,
+                                                            const VMRegPair* regs_cc_ro,
+                                                            AdapterFingerPrint* fingerprint,
+                                                            AdapterBlob*& new_adapter,
+                                                            bool allocate_code_blob) {
   address i2c_entry = __ pc();
-
-  gen_i2c_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs);
+  gen_i2c_adapter(masm, comp_args_on_stack, sig, regs);
 
   // -------------------------------------------------------------------------
   // Generate a C2I adapter.  On entry we know rbx holds the Method* during calls
   // to the interpreter.  The args start out packed in the compiled layout.  They
   // need to be unpacked into the interpreter layout.  This will almost always
   // require some stack space.  We grow the current (compiled) stack, then repack
   // the args.  We  finally end in a jump to the generic interpreter entry point.
   // On exit from the interpreter, the interpreter will restore our SP (lest the
   // compiled code, which relies solely on SP and not RBP, get sick).
 
-  address c2i_unverified_entry = __ pc();
+  address c2i_unverified_entry        = __ pc();
+  address c2i_unverified_inline_entry = __ pc();
   Label skip_fixup;
-  Label ok;
-
-  Register holder = rax;
-  Register receiver = j_rarg0;
-  Register temp = rbx;
 
-  {
-    __ load_klass(temp, receiver, rscratch1);
-    __ cmpptr(temp, Address(holder, CompiledICHolder::holder_klass_offset()));
-    __ movptr(rbx, Address(holder, CompiledICHolder::holder_metadata_offset()));
-    __ jcc(Assembler::equal, ok);
-    __ jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub()));
+  gen_inline_cache_check(masm, skip_fixup);
 
-    __ bind(ok);
-    // Method might have been compiled since the call site was patched to
-    // interpreted if that is the case treat it as a miss so we can get
-    // the call site corrected.
-    __ cmpptr(Address(rbx, in_bytes(Method::code_offset())), NULL_WORD);
-    __ jcc(Assembler::equal, skip_fixup);
-    __ jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub()));
-  }
-
-  address c2i_entry = __ pc();
+  OopMapSet* oop_maps = new OopMapSet();
+  int frame_complete = CodeOffsets::frame_never_safe;
+  int frame_size_in_words = 0;
 
-  // Class initialization barrier for static methods
+  // Scalarized c2i adapter with non-scalarized receiver (i.e., don't pack receiver)
   address c2i_no_clinit_check_entry = nullptr;
-  if (VM_Version::supports_fast_class_init_checks()) {
-    Label L_skip_barrier;
-    Register method = rbx;
-
-    { // Bypass the barrier for non-static methods
-      Register flags = rscratch1;
-      __ movl(flags, Address(method, Method::access_flags_offset()));
-      __ testl(flags, JVM_ACC_STATIC);
-      __ jcc(Assembler::zero, L_skip_barrier); // non-static
-    }
-
-    Register klass = rscratch1;
-    __ load_method_holder(klass, method);
-    __ clinit_barrier(klass, r15_thread, &L_skip_barrier /*L_fast_path*/);
-
-    __ jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); // slow path
+  address c2i_inline_ro_entry = __ pc();
+  if (regs_cc != regs_cc_ro) {
+    // No class init barrier needed because method is guaranteed to be non-static
+    gen_c2i_adapter(masm, sig_cc_ro, regs_cc_ro, /* requires_clinit_barrier = */ false, c2i_no_clinit_check_entry,
+                    skip_fixup, i2c_entry, oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ false);
+    skip_fixup.reset();
+  }
 
-    __ bind(L_skip_barrier);
-    c2i_no_clinit_check_entry = __ pc();
+  // Scalarized c2i adapter
+  address c2i_entry        = __ pc();
+  address c2i_inline_entry = __ pc();
+  gen_c2i_adapter(masm, sig_cc, regs_cc, /* requires_clinit_barrier = */ true, c2i_no_clinit_check_entry,
+                  skip_fixup, i2c_entry, oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ true);
+
+  // Non-scalarized c2i adapter
+  if (regs != regs_cc) {
+    c2i_unverified_inline_entry = __ pc();
+    Label inline_entry_skip_fixup;
+    gen_inline_cache_check(masm, inline_entry_skip_fixup);
+
+    c2i_inline_entry = __ pc();
+    gen_c2i_adapter(masm, sig, regs, /* requires_clinit_barrier = */ true, c2i_no_clinit_check_entry,
+                    inline_entry_skip_fixup, i2c_entry, oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ false);
   }
 
-  BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
-  bs->c2i_entry_barrier(masm);
 
-  gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup);
+  // The c2i adapters might safepoint and trigger a GC. The caller must make sure that
+  // the GC knows about the location of oop argument locations passed to the c2i adapter.
+  if (allocate_code_blob) {
+    bool caller_must_gc_arguments = (regs != regs_cc);
+    new_adapter = AdapterBlob::create(masm->code(), frame_complete, frame_size_in_words, oop_maps, caller_must_gc_arguments);
+  }
 
-  return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
+  return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_inline_entry, c2i_inline_ro_entry, c2i_unverified_entry, c2i_unverified_inline_entry, c2i_no_clinit_check_entry);
 }
 
 int SharedRuntime::c_calling_convention(const BasicType *sig_bt,
                                          VMRegPair *regs,
                                          int total_args_passed) {
@@ -2159,10 +2433,14 @@
       // Load immediate 1 into swap_reg %rax
       __ movl(swap_reg, 1);
 
       // Load (object->mark() | 1) into swap_reg %rax
       __ orptr(swap_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes()));
+      if (EnableValhalla) {
+        // Mask inline_type bit such that we go to the slow path if object is an inline type
+        __ andptr(swap_reg, ~((int) markWord::inline_type_bit_in_place));
+      }
 
       // Save (object->mark() | 1) into BasicLock's displaced header
       __ movptr(Address(lock_reg, mark_word_offset), swap_reg);
 
       // src -> dest iff dest == rax else rax <- dest
@@ -3720,5 +3998,115 @@
   // Set exception blob
   _exception_blob =  ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1);
 }
 #endif // COMPILER2
 
+BufferedInlineTypeBlob* SharedRuntime::generate_buffered_inline_type_adapter(const InlineKlass* vk) {
+  BufferBlob* buf = BufferBlob::create("inline types pack/unpack", 16 * K);
+  CodeBuffer buffer(buf);
+  short buffer_locs[20];
+  buffer.insts()->initialize_shared_locs((relocInfo*)buffer_locs,
+                                         sizeof(buffer_locs)/sizeof(relocInfo));
+
+  MacroAssembler* masm = new MacroAssembler(&buffer);
+
+  const Array<SigEntry>* sig_vk = vk->extended_sig();
+  const Array<VMRegPair>* regs = vk->return_regs();
+
+  int pack_fields_jobject_off = __ offset();
+  // Resolve pre-allocated buffer from JNI handle.
+  // We cannot do this in generate_call_stub() because it requires GC code to be initialized.
+  __ movptr(rax, Address(r13, 0));
+  __ resolve_jobject(rax /* value */,
+                     r15_thread /* thread */,
+                     r12 /* tmp */);
+  __ movptr(Address(r13, 0), rax);
+
+  int pack_fields_off = __ offset();
+
+  int j = 1;
+  for (int i = 0; i < sig_vk->length(); i++) {
+    BasicType bt = sig_vk->at(i)._bt;
+    if (bt == T_METADATA) {
+      continue;
+    }
+    if (bt == T_VOID) {
+      if (sig_vk->at(i-1)._bt == T_LONG ||
+          sig_vk->at(i-1)._bt == T_DOUBLE) {
+        j++;
+      }
+      continue;
+    }
+    int off = sig_vk->at(i)._offset;
+    assert(off > 0, "offset in object should be positive");
+    VMRegPair pair = regs->at(j);
+    VMReg r_1 = pair.first();
+    VMReg r_2 = pair.second();
+    Address to(rax, off);
+    if (bt == T_FLOAT) {
+      __ movflt(to, r_1->as_XMMRegister());
+    } else if (bt == T_DOUBLE) {
+      __ movdbl(to, r_1->as_XMMRegister());
+    } else {
+      Register val = r_1->as_Register();
+      assert_different_registers(to.base(), val, r14, r13, rbx, rscratch1);
+      if (is_reference_type(bt)) {
+        __ store_heap_oop(to, val, r14, r13, rbx, IN_HEAP | ACCESS_WRITE | IS_DEST_UNINITIALIZED);
+      } else {
+        __ store_sized_value(to, r_1->as_Register(), type2aelembytes(bt));
+      }
+    }
+    j++;
+  }
+  assert(j == regs->length(), "missed a field?");
+
+  __ ret(0);
+
+  int unpack_fields_off = __ offset();
+
+  Label skip;
+  __ testptr(rax, rax);
+  __ jcc(Assembler::zero, skip);
+
+  j = 1;
+  for (int i = 0; i < sig_vk->length(); i++) {
+    BasicType bt = sig_vk->at(i)._bt;
+    if (bt == T_METADATA) {
+      continue;
+    }
+    if (bt == T_VOID) {
+      if (sig_vk->at(i-1)._bt == T_LONG ||
+          sig_vk->at(i-1)._bt == T_DOUBLE) {
+        j++;
+      }
+      continue;
+    }
+    int off = sig_vk->at(i)._offset;
+    assert(off > 0, "offset in object should be positive");
+    VMRegPair pair = regs->at(j);
+    VMReg r_1 = pair.first();
+    VMReg r_2 = pair.second();
+    Address from(rax, off);
+    if (bt == T_FLOAT) {
+      __ movflt(r_1->as_XMMRegister(), from);
+    } else if (bt == T_DOUBLE) {
+      __ movdbl(r_1->as_XMMRegister(), from);
+    } else if (bt == T_OBJECT || bt == T_ARRAY) {
+      assert_different_registers(rax, r_1->as_Register());
+      __ load_heap_oop(r_1->as_Register(), from);
+    } else {
+      assert(is_java_primitive(bt), "unexpected basic type");
+      assert_different_registers(rax, r_1->as_Register());
+      size_t size_in_bytes = type2aelembytes(bt);
+      __ load_sized_value(r_1->as_Register(), from, size_in_bytes, bt != T_CHAR && bt != T_BOOLEAN);
+    }
+    j++;
+  }
+  assert(j == regs->length(), "missed a field?");
+
+  __ bind(skip);
+  __ ret(0);
+
+  __ flush();
+
+  return BufferedInlineTypeBlob::create(&buffer, pack_fields_off, pack_fields_jobject_off, unpack_fields_off);
+}
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
@@ -21,10 +21,11 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "asm/assembler.hpp"
 #include "asm/macroAssembler.hpp"
 #include "classfile/vmIntrinsics.hpp"
 #include "compiler/oopMap.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/barrierSetAssembler.hpp"
@@ -35,10 +36,12 @@
 #include "prims/upcallLinker.hpp"
 #include "runtime/arguments.hpp"
 #include "runtime/javaThread.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/stubRoutines.hpp"
+#include "utilities/macros.hpp"
+#include "vmreg_x86.inline.hpp"
 #include "stubGenerator_x86_64.hpp"
 #ifdef COMPILER2
 #include "opto/runtime.hpp"
 #include "opto/c2_globals.hpp"
 #endif
@@ -303,34 +306,34 @@
   BLOCK_COMMENT("call_stub_return_address:");
   return_address = __ pc();
 
   // store result depending on type (everything that is not
   // T_OBJECT, T_LONG, T_FLOAT or T_DOUBLE is treated as T_INT)
-  __ movptr(c_rarg0, result);
-  Label is_long, is_float, is_double, exit;
-  __ movl(c_rarg1, result_type);
-  __ cmpl(c_rarg1, T_OBJECT);
+  __ movptr(r13, result);
+  Label is_long, is_float, is_double, check_prim, exit;
+  __ movl(rbx, result_type);
+  __ cmpl(rbx, T_OBJECT);
+  __ jcc(Assembler::equal, check_prim);
+  __ cmpl(rbx, T_LONG);
   __ jcc(Assembler::equal, is_long);
-  __ cmpl(c_rarg1, T_LONG);
-  __ jcc(Assembler::equal, is_long);
-  __ cmpl(c_rarg1, T_FLOAT);
+  __ cmpl(rbx, T_FLOAT);
   __ jcc(Assembler::equal, is_float);
-  __ cmpl(c_rarg1, T_DOUBLE);
+  __ cmpl(rbx, T_DOUBLE);
   __ jcc(Assembler::equal, is_double);
 #ifdef ASSERT
   // make sure the type is INT
   {
     Label L;
-    __ cmpl(c_rarg1, T_INT);
+    __ cmpl(rbx, T_INT);
     __ jcc(Assembler::equal, L);
     __ stop("StubRoutines::call_stub: unexpected result type");
     __ bind(L);
   }
 #endif
 
   // handle T_INT case
-  __ movl(Address(c_rarg0, 0), rax);
+  __ movl(Address(r13, 0), rax);
 
   __ BIND(exit);
 
   // pop parameters
   __ lea(rsp, rsp_after_call);
@@ -384,20 +387,33 @@
   __ vzeroupper();
   __ pop(rbp);
   __ ret(0);
 
   // handle return types different from T_INT
+  __ BIND(check_prim);
+  if (InlineTypeReturnedAsFields) {
+    // Check for scalarized return value
+    __ testptr(rax, 1);
+    __ jcc(Assembler::zero, is_long);
+    // Load pack handler address
+    __ andptr(rax, -2);
+    __ movptr(rax, Address(rax, InstanceKlass::adr_inlineklass_fixed_block_offset()));
+    __ movptr(rbx, Address(rax, InlineKlass::pack_handler_jobject_offset()));
+    // Call pack handler to initialize the buffer
+    __ call(rbx);
+    __ jmp(exit);
+  }
   __ BIND(is_long);
-  __ movq(Address(c_rarg0, 0), rax);
+  __ movq(Address(r13, 0), rax);
   __ jmp(exit);
 
   __ BIND(is_float);
-  __ movflt(Address(c_rarg0, 0), xmm0);
+  __ movflt(Address(r13, 0), xmm0);
   __ jmp(exit);
 
   __ BIND(is_double);
-  __ movdbl(Address(c_rarg0, 0), xmm0);
+  __ movdbl(Address(r13, 0), xmm0);
   __ jmp(exit);
 
   return start;
 }
 
@@ -3933,10 +3949,20 @@
   // much more complicated generator structure. See also comment in
   // stubRoutines.hpp.
 
   StubRoutines::_forward_exception_entry = generate_forward_exception();
 
+  // Generate these first because they are called from other stubs
+  if (InlineTypeReturnedAsFields) {
+    StubRoutines::_load_inline_type_fields_in_regs =
+      generate_return_value_stub(CAST_FROM_FN_PTR(address, SharedRuntime::load_inline_type_fields_in_regs),
+                                 "load_inline_type_fields_in_regs", false);
+    StubRoutines::_store_inline_type_fields_to_buf =
+      generate_return_value_stub(CAST_FROM_FN_PTR(address, SharedRuntime::store_inline_type_fields_to_buf),
+                                 "store_inline_type_fields_to_buf", true);
+  }
+
   StubRoutines::_call_stub_entry =
     generate_call_stub(StubRoutines::_call_stub_return_address);
 
   // is referenced by megamorphic call
   StubRoutines::_catch_exception_entry = generate_catch_exception();
@@ -3996,10 +4022,154 @@
   generate_libm_stubs();
 
   StubRoutines::_fmod = generate_libmFmod(); // from stubGenerator_x86_64_fmod.cpp
 }
 
+// Call here from the interpreter or compiled code to either load
+// multiple returned values from the inline type instance being
+// returned to registers or to store returned values to a newly
+// allocated inline type instance.
+// Register is a class, but it would be assigned numerical value.
+// "0" is assigned for xmm0. Thus we need to ignore -Wnonnull.
+PRAGMA_DIAG_PUSH
+PRAGMA_NONNULL_IGNORED
+address StubGenerator::generate_return_value_stub(address destination, const char* name, bool has_res) {
+  // We need to save all registers the calling convention may use so
+  // the runtime calls read or update those registers. This needs to
+  // be in sync with SharedRuntime::java_return_convention().
+  enum layout {
+    pad_off = frame::arg_reg_save_area_bytes/BytesPerInt, pad_off_2,
+    rax_off, rax_off_2,
+    j_rarg5_off, j_rarg5_2,
+    j_rarg4_off, j_rarg4_2,
+    j_rarg3_off, j_rarg3_2,
+    j_rarg2_off, j_rarg2_2,
+    j_rarg1_off, j_rarg1_2,
+    j_rarg0_off, j_rarg0_2,
+    j_farg0_off, j_farg0_2,
+    j_farg1_off, j_farg1_2,
+    j_farg2_off, j_farg2_2,
+    j_farg3_off, j_farg3_2,
+    j_farg4_off, j_farg4_2,
+    j_farg5_off, j_farg5_2,
+    j_farg6_off, j_farg6_2,
+    j_farg7_off, j_farg7_2,
+    rbp_off, rbp_off_2,
+    return_off, return_off_2,
+
+    framesize
+  };
+
+  CodeBuffer buffer(name, 1000, 512);
+  MacroAssembler* _masm = new MacroAssembler(&buffer);
+
+  int frame_size_in_bytes = align_up(framesize*BytesPerInt, 16);
+  assert(frame_size_in_bytes == framesize*BytesPerInt, "misaligned");
+  int frame_size_in_slots = frame_size_in_bytes / BytesPerInt;
+  int frame_size_in_words = frame_size_in_bytes / wordSize;
+
+  OopMapSet *oop_maps = new OopMapSet();
+  OopMap* map = new OopMap(frame_size_in_slots, 0);
+
+  map->set_callee_saved(VMRegImpl::stack2reg(rax_off), rax->as_VMReg());
+  map->set_callee_saved(VMRegImpl::stack2reg(j_rarg5_off), j_rarg5->as_VMReg());
+  map->set_callee_saved(VMRegImpl::stack2reg(j_rarg4_off), j_rarg4->as_VMReg());
+  map->set_callee_saved(VMRegImpl::stack2reg(j_rarg3_off), j_rarg3->as_VMReg());
+  map->set_callee_saved(VMRegImpl::stack2reg(j_rarg2_off), j_rarg2->as_VMReg());
+  map->set_callee_saved(VMRegImpl::stack2reg(j_rarg1_off), j_rarg1->as_VMReg());
+  map->set_callee_saved(VMRegImpl::stack2reg(j_rarg0_off), j_rarg0->as_VMReg());
+  map->set_callee_saved(VMRegImpl::stack2reg(j_farg0_off), j_farg0->as_VMReg());
+  map->set_callee_saved(VMRegImpl::stack2reg(j_farg1_off), j_farg1->as_VMReg());
+  map->set_callee_saved(VMRegImpl::stack2reg(j_farg2_off), j_farg2->as_VMReg());
+  map->set_callee_saved(VMRegImpl::stack2reg(j_farg3_off), j_farg3->as_VMReg());
+  map->set_callee_saved(VMRegImpl::stack2reg(j_farg4_off), j_farg4->as_VMReg());
+  map->set_callee_saved(VMRegImpl::stack2reg(j_farg5_off), j_farg5->as_VMReg());
+  map->set_callee_saved(VMRegImpl::stack2reg(j_farg6_off), j_farg6->as_VMReg());
+  map->set_callee_saved(VMRegImpl::stack2reg(j_farg7_off), j_farg7->as_VMReg());
+
+  int start = __ offset();
+
+  __ subptr(rsp, frame_size_in_bytes - 8 /* return address*/);
+
+  __ movptr(Address(rsp, rbp_off * BytesPerInt), rbp);
+  __ movdbl(Address(rsp, j_farg7_off * BytesPerInt), j_farg7);
+  __ movdbl(Address(rsp, j_farg6_off * BytesPerInt), j_farg6);
+  __ movdbl(Address(rsp, j_farg5_off * BytesPerInt), j_farg5);
+  __ movdbl(Address(rsp, j_farg4_off * BytesPerInt), j_farg4);
+  __ movdbl(Address(rsp, j_farg3_off * BytesPerInt), j_farg3);
+  __ movdbl(Address(rsp, j_farg2_off * BytesPerInt), j_farg2);
+  __ movdbl(Address(rsp, j_farg1_off * BytesPerInt), j_farg1);
+  __ movdbl(Address(rsp, j_farg0_off * BytesPerInt), j_farg0);
+
+  __ movptr(Address(rsp, j_rarg0_off * BytesPerInt), j_rarg0);
+  __ movptr(Address(rsp, j_rarg1_off * BytesPerInt), j_rarg1);
+  __ movptr(Address(rsp, j_rarg2_off * BytesPerInt), j_rarg2);
+  __ movptr(Address(rsp, j_rarg3_off * BytesPerInt), j_rarg3);
+  __ movptr(Address(rsp, j_rarg4_off * BytesPerInt), j_rarg4);
+  __ movptr(Address(rsp, j_rarg5_off * BytesPerInt), j_rarg5);
+  __ movptr(Address(rsp, rax_off * BytesPerInt), rax);
+
+  int frame_complete = __ offset();
+
+  __ set_last_Java_frame(noreg, noreg, nullptr, rscratch1);
+
+  __ mov(c_rarg0, r15_thread);
+  __ mov(c_rarg1, rax);
+
+  __ call(RuntimeAddress(destination));
+
+  // Set an oopmap for the call site.
+
+  oop_maps->add_gc_map( __ offset() - start, map);
+
+  // clear last_Java_sp
+  __ reset_last_Java_frame(false);
+
+  __ movptr(rbp, Address(rsp, rbp_off * BytesPerInt));
+  __ movdbl(j_farg7, Address(rsp, j_farg7_off * BytesPerInt));
+  __ movdbl(j_farg6, Address(rsp, j_farg6_off * BytesPerInt));
+  __ movdbl(j_farg5, Address(rsp, j_farg5_off * BytesPerInt));
+  __ movdbl(j_farg4, Address(rsp, j_farg4_off * BytesPerInt));
+  __ movdbl(j_farg3, Address(rsp, j_farg3_off * BytesPerInt));
+  __ movdbl(j_farg2, Address(rsp, j_farg2_off * BytesPerInt));
+  __ movdbl(j_farg1, Address(rsp, j_farg1_off * BytesPerInt));
+  __ movdbl(j_farg0, Address(rsp, j_farg0_off * BytesPerInt));
+
+  __ movptr(j_rarg0, Address(rsp, j_rarg0_off * BytesPerInt));
+  __ movptr(j_rarg1, Address(rsp, j_rarg1_off * BytesPerInt));
+  __ movptr(j_rarg2, Address(rsp, j_rarg2_off * BytesPerInt));
+  __ movptr(j_rarg3, Address(rsp, j_rarg3_off * BytesPerInt));
+  __ movptr(j_rarg4, Address(rsp, j_rarg4_off * BytesPerInt));
+  __ movptr(j_rarg5, Address(rsp, j_rarg5_off * BytesPerInt));
+  __ movptr(rax, Address(rsp, rax_off * BytesPerInt));
+
+  __ addptr(rsp, frame_size_in_bytes-8);
+
+  // check for pending exceptions
+  Label pending;
+  __ cmpptr(Address(r15_thread, Thread::pending_exception_offset()), (int32_t)NULL_WORD);
+  __ jcc(Assembler::notEqual, pending);
+
+  if (has_res) {
+    __ get_vm_result(rax, r15_thread);
+  }
+
+  __ ret(0);
+
+  __ bind(pending);
+
+  __ movptr(rax, Address(r15_thread, Thread::pending_exception_offset()));
+  __ jump(RuntimeAddress(StubRoutines::forward_exception_entry()));
+
+  // -------------
+  // make sure all code is generated
+  _masm->flush();
+
+  RuntimeStub* stub = RuntimeStub::new_runtime_stub(name, &buffer, frame_complete, frame_size_in_words, oop_maps, false);
+  return stub->entry_point();
+}
+
 void StubGenerator::generate_continuation_stubs() {
   // Continuation stubs:
   StubRoutines::_cont_thaw          = generate_cont_thaw();
   StubRoutines::_cont_returnBarrier = generate_cont_returnBarrier();
   StubRoutines::_cont_returnBarrierExc = generate_cont_returnBarrier_exception();
diff a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp
--- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp
@@ -578,10 +578,13 @@
                                    Register arg2 = noreg);
 
   // shared exception handler for FFM upcall stubs
   address generate_upcall_stub_exception_handler();
 
+  // interpreter or compiled code marshalling registers to/from inline type instance
+  address generate_return_value_stub(address destination, const char* name, bool has_res);
+
   void create_control_words();
 
   // Initialization
   void generate_initial_stubs();
   void generate_continuation_stubs();
diff a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp
--- a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp
@@ -2659,21 +2659,31 @@
   __ jcc(Assembler::notEqual, L_failed);
 
   const Register rax_lh = rax;  // layout helper
   __ movl(rax_lh, Address(r10_src_klass, lh_offset));
 
+  // Check for flat inline type array -> return -1
+  __ testl(rax_lh, Klass::_lh_array_tag_flat_value_bit_inplace);
+  __ jcc(Assembler::notZero, L_failed);
+
+  // Check for null-free (non-flat) inline type array -> handle as object array
+  __ testl(rax_lh, Klass::_lh_null_free_array_bit_inplace);
+  __ jcc(Assembler::notZero, L_objArray);
+
   //  if (!src->is_Array()) return -1;
   __ cmpl(rax_lh, Klass::_lh_neutral_value);
   __ jcc(Assembler::greaterEqual, L_failed);
 
   // At this point, it is known to be a typeArray (array_tag 0x3).
 #ifdef ASSERT
   {
     BLOCK_COMMENT("assert primitive array {");
     Label L;
-    __ cmpl(rax_lh, (Klass::_lh_array_tag_type_value << Klass::_lh_array_tag_shift));
-    __ jcc(Assembler::greaterEqual, L);
+    __ movl(rklass_tmp, rax_lh);
+    __ sarl(rklass_tmp, Klass::_lh_array_tag_shift);
+    __ cmpl(rklass_tmp, Klass::_lh_array_tag_type_value);
+    __ jcc(Assembler::equal, L);
     __ stop("must be a primitive array");
     __ bind(L);
     BLOCK_COMMENT("} assert primitive array done");
   }
 #endif
@@ -2777,13 +2787,27 @@
 
 __ BIND(L_checkcast_copy);
   // live at this point:  r10_src_klass, r11_length, rax (dst_klass)
   {
     // Before looking at dst.length, make sure dst is also an objArray.
+    // This check also fails for flat/null-free arrays which are not supported.
     __ cmpl(Address(rax, lh_offset), objArray_lh);
     __ jcc(Assembler::notEqual, L_failed);
 
+#ifdef ASSERT
+    {
+      BLOCK_COMMENT("assert not null-free array {");
+      Label L;
+      __ movl(rklass_tmp, Address(rax, lh_offset));
+      __ testl(rklass_tmp, Klass::_lh_null_free_array_bit_inplace);
+      __ jcc(Assembler::zero, L);
+      __ stop("unexpected null-free array");
+      __ bind(L);
+      BLOCK_COMMENT("} assert not null-free array");
+    }
+#endif
+
     // It is safe to examine both src.length and dst.length.
     arraycopy_range_checks(src, src_pos, dst, dst_pos, r11_length,
                            rax, L_failed);
 
     const Register r11_dst_klass = r11;
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
@@ -37,10 +37,11 @@
 #include "oops/arrayOop.hpp"
 #include "oops/methodCounters.hpp"
 #include "oops/methodData.hpp"
 #include "oops/method.hpp"
 #include "oops/oop.inline.hpp"
+#include "oops/inlineKlass.hpp"
 #include "oops/resolvedIndyEntry.hpp"
 #include "oops/resolvedMethodEntry.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "prims/jvmtiThreadState.hpp"
 #include "runtime/continuation.hpp"
@@ -63,11 +64,11 @@
 // fail with a guarantee ("not enough space for interpreter generation");
 // if too small.
 // Run with +PrintInterpreter to get the VM to print out the size.
 // Max size with JVMTI
 #ifdef AMD64
-int TemplateInterpreter::InterpreterCodeSize = JVMCI_ONLY(268) NOT_JVMCI(256) * 1024;
+int TemplateInterpreter::InterpreterCodeSize = JVMCI_ONLY(280) NOT_JVMCI(268) * 1024;
 #else
 int TemplateInterpreter::InterpreterCodeSize = 224 * 1024;
 #endif // AMD64
 
 // Global Register Names
@@ -208,15 +209,19 @@
     __ MacroAssembler::verify_FPU(UseSSE >= 2 ? 0 : 1, "generate_return_entry_for in interpreter");
   }
 #endif // _LP64
 
   // Restore stack bottom in case i2c adjusted stack
-  __ movptr(rcx, Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize));
-  __ lea(rsp, Address(rbp, rcx, Address::times_ptr));
+  __ movptr(rscratch1, Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize));
+  __ lea(rsp, Address(rbp, rscratch1, Address::times_ptr));
   // and null it as marker that esp is now tos until next java call
   __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), NULL_WORD);
 
+  if (state == atos && InlineTypeReturnedAsFields) {
+    __ store_inline_type_fields_to_buf(NULL);
+  }
+
   __ restore_bcp();
   __ restore_locals();
 
   if (state == atos) {
     Register mdp = rbx;
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
@@ -35,10 +35,11 @@
 #include "memory/universe.hpp"
 #include "oops/methodCounters.hpp"
 #include "oops/methodData.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "oops/oop.inline.hpp"
+#include "oops/inlineKlass.hpp"
 #include "oops/resolvedFieldEntry.hpp"
 #include "oops/resolvedIndyEntry.hpp"
 #include "oops/resolvedMethodEntry.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "prims/methodHandles.hpp"
@@ -181,10 +182,11 @@
                                    int byte_no) {
   if (!RewriteBytecodes)  return;
   Label L_patch_done;
 
   switch (bc) {
+  case Bytecodes::_fast_vputfield:
   case Bytecodes::_fast_aputfield:
   case Bytecodes::_fast_bputfield:
   case Bytecodes::_fast_zputfield:
   case Bytecodes::_fast_cputfield:
   case Bytecodes::_fast_dputfield:
@@ -828,19 +830,37 @@
                     noreg, noreg);
 }
 
 void TemplateTable::aaload() {
   transition(itos, atos);
-  // rax: index
-  // rdx: array
-  index_check(rdx, rax); // kills rbx
-  do_oop_load(_masm,
-              Address(rdx, rax,
-                      UseCompressedOops ? Address::times_4 : Address::times_ptr,
-                      arrayOopDesc::base_offset_in_bytes(T_OBJECT)),
-              rax,
-              IS_ARRAY);
+  Register array = rdx;
+  Register index = rax;
+
+  index_check(array, index); // kills rbx
+  __ profile_array_type<ArrayLoadData>(rbx, array, rcx);
+  if (UseFlatArray) {
+    Label is_flat_array, done;
+    __ test_flat_array_oop(array, rbx, is_flat_array);
+    do_oop_load(_masm,
+                Address(array, index,
+                        UseCompressedOops ? Address::times_4 : Address::times_ptr,
+                        arrayOopDesc::base_offset_in_bytes(T_OBJECT)),
+                rax,
+                IS_ARRAY);
+    __ jmp(done);
+    __ bind(is_flat_array);
+    __ read_flat_element(array, index, rbx, rcx, rax);
+    __ bind(done);
+  } else {
+    do_oop_load(_masm,
+                Address(array, index,
+                        UseCompressedOops ? Address::times_4 : Address::times_ptr,
+                        arrayOopDesc::base_offset_in_bytes(T_OBJECT)),
+                rax,
+                IS_ARRAY);
+  }
+  __ profile_element_type(rbx, rax, rcx);
 }
 
 void TemplateTable::baload() {
   transition(itos, itos);
   // rax: index
@@ -1122,11 +1142,11 @@
                              arrayOopDesc::base_offset_in_bytes(T_DOUBLE)),
                      noreg /* dtos */, noreg, noreg, noreg);
 }
 
 void TemplateTable::aastore() {
-  Label is_null, ok_is_subtype, done;
+  Label is_null, is_flat_array, ok_is_subtype, done;
   transition(vtos, vtos);
   // stack: ..., array, index, value
   __ movptr(rax, at_tos());    // value
   __ movl(rcx, at_tos_p1()); // index
   __ movptr(rdx, at_tos_p2()); // array
@@ -1134,23 +1154,34 @@
   Address element_address(rdx, rcx,
                           UseCompressedOops? Address::times_4 : Address::times_ptr,
                           arrayOopDesc::base_offset_in_bytes(T_OBJECT));
 
   index_check_without_pop(rdx, rcx);     // kills rbx
+
+  __ profile_array_type<ArrayStoreData>(rdi, rdx, rbx);
+  __ profile_multiple_element_types(rdi, rax, rbx, rcx);
+
   __ testptr(rax, rax);
   __ jcc(Assembler::zero, is_null);
 
+  // Move array class to rdi
+  __ load_klass(rdi, rdx, rscratch1);
+  if (UseFlatArray) {
+    __ movl(rbx, Address(rdi, Klass::layout_helper_offset()));
+    __ test_flat_array_layout(rbx, is_flat_array);
+  }
+
   // Move subklass into rbx
   __ load_klass(rbx, rax, rscratch1);
-  // Move superklass into rax
-  __ load_klass(rax, rdx, rscratch1);
-  __ movptr(rax, Address(rax,
+  // Move array element superklass into rax
+  __ movptr(rax, Address(rdi,
                          ObjArrayKlass::element_klass_offset()));
 
   // Generate subtype check.  Blows rcx, rdi
   // Superklass in rax.  Subklass in rbx.
-  __ gen_subtype_check(rbx, ok_is_subtype);
+  // is "rbx <: rax" ? (value subclass <: array element superclass)
+  __ gen_subtype_check(rbx, ok_is_subtype, false);
 
   // Come here on failure
   // object is at TOS
   __ jump(ExternalAddress(Interpreter::_throw_ArrayStoreException_entry));
 
@@ -1164,15 +1195,59 @@
   do_oop_store(_masm, element_address, rax, IS_ARRAY);
   __ jmp(done);
 
   // Have a null in rax, rdx=array, ecx=index.  Store null at ary[idx]
   __ bind(is_null);
-  __ profile_null_seen(rbx);
+  if (EnableValhalla) {
+    Label is_null_into_value_array_npe, store_null;
+
+    // No way to store null in null-free array
+    __ test_null_free_array_oop(rdx, rbx, is_null_into_value_array_npe);
+    __ jmp(store_null);
 
+    __ bind(is_null_into_value_array_npe);
+    __ jump(ExternalAddress(Interpreter::_throw_NullPointerException_entry));
+
+    __ bind(store_null);
+  }
   // Store a null
   do_oop_store(_masm, element_address, noreg, IS_ARRAY);
+  __ jmp(done);
+
+  if (UseFlatArray) {
+    Label is_type_ok;
+    __ bind(is_flat_array); // Store non-null value to flat
 
+    // Simplistic type check...
+
+    // Profile the not-null value's klass.
+    __ load_klass(rbx, rax, rscratch1);
+    // Move element klass into rax
+    __ movptr(rax, Address(rdi, ArrayKlass::element_klass_offset()));
+    // flat value array needs exact type match
+    // is "rax == rbx" (value subclass == array element superclass)
+    __ cmpptr(rax, rbx);
+    __ jccb(Assembler::equal, is_type_ok);
+
+    __ jump(ExternalAddress(Interpreter::_throw_ArrayStoreException_entry));
+
+    __ bind(is_type_ok);
+    // rbx: value's klass
+    // rdx: array
+    // rdi: array klass
+    __ test_klass_is_empty_inline_type(rbx, rax, done);
+
+    // calc dst for copy
+    __ movl(rax, at_tos_p1()); // index
+    __ data_for_value_array_index(rdx, rdi, rax, rax);
+
+    // ...and src for copy
+    __ movptr(rcx, at_tos());  // value
+    __ data_for_oop(rcx, rcx, rbx);
+
+    __ access_value_copy(IN_HEAP, rcx, rax, rbx);
+  }
   // Pop stack arguments
   __ bind(done);
   __ addptr(rsp, 3 * Interpreter::stackElementSize);
 }
 
@@ -2337,17 +2412,63 @@
 }
 
 void TemplateTable::if_acmp(Condition cc) {
   transition(atos, vtos);
   // assume branch is more often taken than not (loops use backward branches)
-  Label not_taken;
+  Label taken, not_taken;
   __ pop_ptr(rdx);
+
+  __ profile_acmp(rbx, rdx, rax, rcx);
+
+  const int is_inline_type_mask = markWord::inline_type_pattern;
+  if (EnableValhalla) {
+    __ cmpoop(rdx, rax);
+    __ jcc(Assembler::equal, (cc == equal) ? taken : not_taken);
+
+    // might be substitutable, test if either rax or rdx is null
+    __ testptr(rax, rax);
+    __ jcc(Assembler::zero, (cc == equal) ? not_taken : taken);
+    __ testptr(rdx, rdx);
+    __ jcc(Assembler::zero, (cc == equal) ? not_taken : taken);
+
+    // and both are values ?
+    __ movptr(rbx, Address(rdx, oopDesc::mark_offset_in_bytes()));
+    __ andptr(rbx, Address(rax, oopDesc::mark_offset_in_bytes()));
+    __ andptr(rbx, is_inline_type_mask);
+    __ cmpptr(rbx, is_inline_type_mask);
+    __ jcc(Assembler::notEqual, (cc == equal) ? not_taken : taken);
+
+    // same value klass ?
+    __ load_metadata(rbx, rdx);
+    __ load_metadata(rcx, rax);
+    __ cmpptr(rbx, rcx);
+    __ jcc(Assembler::notEqual, (cc == equal) ? not_taken : taken);
+
+    // Know both are the same type, let's test for substitutability...
+    if (cc == equal) {
+      invoke_is_substitutable(rax, rdx, taken, not_taken);
+    } else {
+      invoke_is_substitutable(rax, rdx, not_taken, taken);
+    }
+    __ stop("Not reachable");
+  }
+
   __ cmpoop(rdx, rax);
   __ jcc(j_not(cc), not_taken);
+  __ bind(taken);
   branch(false, false);
   __ bind(not_taken);
-  __ profile_not_taken_branch(rax);
+  __ profile_not_taken_branch(rax, true);
+}
+
+void TemplateTable::invoke_is_substitutable(Register aobj, Register bobj,
+                                            Label& is_subst, Label& not_subst) {
+  __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::is_substitutable), aobj, bobj);
+  // Restored...rax answer, jmp to outcome...
+  __ testl(rax, rax);
+  __ jcc(Assembler::zero, not_subst);
+  __ jmp(is_subst);
 }
 
 void TemplateTable::ret() {
   transition(vtos, vtos);
   locals_index(rbx);
@@ -2613,11 +2734,12 @@
   // Need to narrow in the return bytecode rather than in generate_return_entry
   // since compiled code callers expect the result to already be narrowed.
   if (state == itos) {
     __ narrow(rax);
   }
-  __ remove_activation(state, rbcp);
+
+  __ remove_activation(state, rbcp, true, true, true);
 
   __ jmp(rbcp);
 }
 
 // ----------------------------------------------------------------------------
@@ -2979,11 +3101,11 @@
 }
 
 void TemplateTable::getfield_or_static(int byte_no, bool is_static, RewriteControl rc) {
   transition(vtos, vtos);
 
-  const Register obj   = LP64_ONLY(c_rarg3) NOT_LP64(rcx);
+  const Register obj   = LP64_ONLY(r9) NOT_LP64(rcx);
   const Register cache = rcx;
   const Register index = rdx;
   const Register off   = rbx;
   const Register tos_state   = rax;
   const Register flags = rdx;
@@ -2991,22 +3113,21 @@
 
   resolve_cache_and_index_for_field(byte_no, cache, index);
   jvmti_post_field_access(cache, index, is_static, false);
   load_resolved_field_entry(obj, cache, tos_state, off, flags, is_static);
 
-  if (!is_static) pop_and_check_object(obj);
-
   const Address field(obj, off, Address::times_1, 0*wordSize);
 
-  Label Done, notByte, notBool, notInt, notShort, notChar, notLong, notFloat, notObj;
+  Label Done, notByte, notBool, notInt, notShort, notChar, notLong, notFloat, notObj, notInlineType;
 
   // Make sure we don't need to mask edx after the above shift
   assert(btos == 0, "change code, btos != 0");
   __ testl(tos_state, tos_state);
   __ jcc(Assembler::notZero, notByte);
 
   // btos
+  if (!is_static) pop_and_check_object(obj);
   __ access_load_at(T_BYTE, IN_HEAP, rax, field, noreg, noreg);
   __ push(btos);
   // Rewrite bytecode to be faster
   if (!is_static && rc == may_rewrite) {
     patch_bytecode(Bytecodes::_fast_bgetfield, bc, rbx);
@@ -3014,11 +3135,11 @@
   __ jmp(Done);
 
   __ bind(notByte);
   __ cmpl(tos_state, ztos);
   __ jcc(Assembler::notEqual, notBool);
-
+   if (!is_static) pop_and_check_object(obj);
   // ztos (same code as btos)
   __ access_load_at(T_BOOLEAN, IN_HEAP, rax, field, noreg, noreg);
   __ push(ztos);
   // Rewrite bytecode to be faster
   if (!is_static && rc == may_rewrite) {
@@ -3029,18 +3150,104 @@
 
   __ bind(notBool);
   __ cmpl(tos_state, atos);
   __ jcc(Assembler::notEqual, notObj);
   // atos
-  do_oop_load(_masm, field, rax);
-  __ push(atos);
-  if (!is_static && rc == may_rewrite) {
-    patch_bytecode(Bytecodes::_fast_agetfield, bc, rbx);
+  if (!EnableValhalla) {
+    if (!is_static) pop_and_check_object(obj);
+    do_oop_load(_masm, field, rax);
+    __ push(atos);
+    if (!is_static && rc == may_rewrite) {
+      patch_bytecode(Bytecodes::_fast_agetfield, bc, rbx);
+    }
+    __ jmp(Done);
+  } else {
+    if (is_static) {
+      __ load_heap_oop(rax, field);
+      Label is_null_free_inline_type, uninitialized;
+      // Issue below if the static field has not been initialized yet
+      __ test_field_is_null_free_inline_type(flags, rscratch1, is_null_free_inline_type);
+        // field is not a null free inline type
+        __ push(atos);
+        __ jmp(Done);
+      // field is a null free inline type, must not return null even if uninitialized
+      __ bind(is_null_free_inline_type);
+          __ testptr(rax, rax);
+        __ jcc(Assembler::zero, uninitialized);
+          __ push(atos);
+          __ jmp(Done);
+        __ bind(uninitialized);
+#ifdef _LP64
+          Label slow_case, finish;
+          __ movptr(rbx, Address(obj, java_lang_Class::klass_offset()));
+          __ cmpb(Address(rbx, InstanceKlass::init_state_offset()), InstanceKlass::fully_initialized);
+          __ jcc(Assembler::notEqual, slow_case);
+        __ get_default_value_oop(rbx, rscratch1, rax);
+        __ jmp(finish);
+        __ bind(slow_case);
+#endif // LP64
+          __ call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::uninitialized_static_inline_type_field),
+                obj, cache);
+#ifdef _LP64
+          __ bind(finish);
+  #endif // _LP64
+        __ verify_oop(rax);
+        __ push(atos);
+        __ jmp(Done);
+    } else {
+      Label is_flat, nonnull, is_inline_type, rewrite_inline, has_null_marker;
+      __ test_field_is_null_free_inline_type(flags, rscratch1, is_inline_type);
+      __ test_field_has_null_marker(flags, rscratch1, has_null_marker);
+      // field is not a null free inline type
+      pop_and_check_object(obj);
+      __ load_heap_oop(rax, field);
+      __ push(atos);
+      if (rc == may_rewrite) {
+        patch_bytecode(Bytecodes::_fast_agetfield, bc, rbx);
+      }
+      __ jmp(Done);
+      __ bind(is_inline_type);
+      __ test_field_is_flat(flags, rscratch1, is_flat);
+          // field is not flat
+          pop_and_check_object(obj);
+          __ load_heap_oop(rax, field);
+          __ testptr(rax, rax);
+          __ jcc(Assembler::notZero, nonnull);
+            __ load_unsigned_short(flags, Address(cache, in_bytes(ResolvedFieldEntry::field_index_offset())));
+            __ movptr(rcx, Address(cache, ResolvedFieldEntry::field_holder_offset()));
+            __ get_inline_type_field_klass(rcx, flags, rbx);
+            __ get_default_value_oop(rbx, rcx, rax);
+          __ bind(nonnull);
+          __ verify_oop(rax);
+          __ push(atos);
+          __ jmp(rewrite_inline);
+        __ bind(is_flat);
+          pop_and_check_object(rax);
+          __ load_unsigned_short(rdx, Address(cache, in_bytes(ResolvedFieldEntry::field_index_offset())));
+          __ movptr(rcx, Address(cache, ResolvedFieldEntry::field_holder_offset()));
+          __ read_flat_field(rcx, rdx, rbx, rax);
+          __ verify_oop(rax);
+          __ push(atos);
+          __ jmp(rewrite_inline);
+      __ bind(has_null_marker);
+        pop_and_check_object(rax);
+        __ load_field_entry(rcx, rbx);
+        call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::read_nullable_flat_field), rax, rcx);
+        __ get_vm_result(rax, r15_thread);
+        __ push(atos);
+      __ bind(rewrite_inline);
+      if (rc == may_rewrite) {
+        patch_bytecode(Bytecodes::_fast_vgetfield, bc, rbx);
+      }
+        __ jmp(Done);
+    }
   }
-  __ jmp(Done);
 
   __ bind(notObj);
+
+  if (!is_static) pop_and_check_object(obj);
+
   __ cmpl(tos_state, itos);
   __ jcc(Assembler::notEqual, notInt);
   // itos
   __ access_load_at(T_INT, IN_HEAP, rax, field, noreg, noreg);
   __ push(itos);
@@ -3136,11 +3343,10 @@
 
 void TemplateTable::getstatic(int byte_no) {
   getfield_or_static(byte_no, true);
 }
 
-
 // The registers cache and index expected to be set before call.
 // The function may destroy various registers, just not the cache and index registers.
 void TemplateTable::jvmti_post_field_mod(Register cache, Register index, bool is_static) {
   // Cache is rcx and index is rdx
   const Register entry = LP64_ONLY(c_rarg2) NOT_LP64(rax); // ResolvedFieldEntry
@@ -3218,11 +3424,11 @@
   const Register obj = rcx;
   const Register cache = rcx;
   const Register index = rdx;
   const Register tos_state   = rdx;
   const Register off   = rbx;
-  const Register flags = rax;
+  const Register flags = r9;
 
   resolve_cache_and_index_for_field(byte_no, cache, index);
   jvmti_post_field_mod(cache, index, is_static);
   load_resolved_field_entry(obj, cache, tos_state, off, flags, is_static);
 
@@ -3231,34 +3437,35 @@
   //                                              Assembler::StoreStore));
 
   Label notVolatile, Done;
 
   // Check for volatile store
-  __ andl(flags, (1 << ResolvedFieldEntry::is_volatile_shift));
-  __ testl(flags, flags);
+  __ movl(rscratch1, flags);
+  __ andl(rscratch1, (1 << ResolvedFieldEntry::is_volatile_shift));
+  __ testl(rscratch1, rscratch1);
   __ jcc(Assembler::zero, notVolatile);
 
-  putfield_or_static_helper(byte_no, is_static, rc, obj, off, tos_state);
+  putfield_or_static_helper(byte_no, is_static, rc, obj, off, tos_state, flags);
   volatile_barrier(Assembler::Membar_mask_bits(Assembler::StoreLoad |
                                                Assembler::StoreStore));
   __ jmp(Done);
   __ bind(notVolatile);
 
-  putfield_or_static_helper(byte_no, is_static, rc, obj, off, tos_state);
+  putfield_or_static_helper(byte_no, is_static, rc, obj, off, tos_state, flags);
 
   __ bind(Done);
 }
 
 void TemplateTable::putfield_or_static_helper(int byte_no, bool is_static, RewriteControl rc,
-                                              Register obj, Register off, Register tos_state) {
+                                              Register obj, Register off, Register tos_state, Register flags) {
 
   // field addresses
   const Address field(obj, off, Address::times_1, 0*wordSize);
   NOT_LP64( const Address hi(obj, off, Address::times_1, 1*wordSize);)
 
   Label notByte, notBool, notInt, notShort, notChar,
-        notLong, notFloat, notObj;
+        notLong, notFloat, notObj, notInlineType;
   Label Done;
 
   const Register bc    = LP64_ONLY(c_rarg3) NOT_LP64(rcx);
 
   // Test TOS state
@@ -3295,18 +3502,71 @@
   __ cmpl(tos_state, atos);
   __ jcc(Assembler::notEqual, notObj);
 
   // atos
   {
-    __ pop(atos);
-    if (!is_static) pop_and_check_object(obj);
-    // Store into the field
-    do_oop_store(_masm, field, rax);
-    if (!is_static && rc == may_rewrite) {
-      patch_bytecode(Bytecodes::_fast_aputfield, bc, rbx, true, byte_no);
+    if (!EnableValhalla) {
+      __ pop(atos);
+      if (!is_static) pop_and_check_object(obj);
+      // Store into the field
+      do_oop_store(_masm, field, rax);
+      if (!is_static && rc == may_rewrite) {
+        patch_bytecode(Bytecodes::_fast_aputfield, bc, rbx, true, byte_no);
+      }
+      __ jmp(Done);
+    } else {
+      __ pop(atos);
+      if (is_static) {
+        Label is_inline_type;
+        __ test_field_is_not_null_free_inline_type(flags, rscratch1, is_inline_type);
+        __ null_check(rax);
+        __ bind(is_inline_type);
+        do_oop_store(_masm, field, rax);
+        __ jmp(Done);
+      } else {
+        Label is_null_free_inline_type, is_flat, has_null_marker,
+              write_null, rewrite_not_inline, rewrite_inline;
+        __ test_field_is_null_free_inline_type(flags, rscratch1, is_null_free_inline_type);
+        __ test_field_has_null_marker(flags, rscratch1, has_null_marker);
+          // Not an inline type
+          pop_and_check_object(obj);
+          // Store into the field
+          do_oop_store(_masm, field, rax);
+          __ bind(rewrite_not_inline);
+          if (rc == may_rewrite) {
+            patch_bytecode(Bytecodes::_fast_aputfield, bc, rbx, true, byte_no);
+          }
+          __ jmp(Done);
+        // Implementation of the inline type semantic
+        __ bind(is_null_free_inline_type);
+          __ null_check(rax);
+          __ test_field_is_flat(flags, rscratch1, is_flat);
+            // field is not flat
+            pop_and_check_object(obj);
+            // Store into the field
+            do_oop_store(_masm, field, rax);
+          __ jmp(rewrite_inline);
+          __ bind(is_flat);
+            // field is flat
+            pop_and_check_object(obj);
+            assert_different_registers(rax, rdx, obj, off);
+            __ load_klass(rdx, rax, rscratch1);
+            __ data_for_oop(rax, rax, rdx);
+            __ addptr(obj, off);
+            __ access_value_copy(IN_HEAP, rax, obj, rdx);
+            __ jmp(rewrite_inline);
+        __ bind(has_null_marker); // has null marker means the field is flat with a null marker
+          pop_and_check_object(rbx);
+          __ load_field_entry(rcx, rdx);
+          call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::write_nullable_flat_field), rbx, rax, rcx);
+        __ bind(rewrite_inline);
+        if (rc == may_rewrite) {
+          patch_bytecode(Bytecodes::_fast_vputfield, bc, rbx, true, byte_no);
+        }
+        __ jmp(Done);
+      }
     }
-    __ jmp(Done);
   }
 
   __ bind(notObj);
   __ cmpl(tos_state, itos);
   __ jcc(Assembler::notEqual, notInt);
@@ -3441,10 +3701,11 @@
     __ push_ptr(rbx);                 // put the object pointer back on tos
     // Save tos values before call_VM() clobbers them. Since we have
     // to do it for every data type, we use the saved values as the
     // jvalue object.
     switch (bytecode()) {          // load values into the jvalue object
+    case Bytecodes::_fast_vputfield: //fall through
     case Bytecodes::_fast_aputfield: __ push_ptr(rax); break;
     case Bytecodes::_fast_bputfield: // fall through
     case Bytecodes::_fast_zputfield: // fall through
     case Bytecodes::_fast_sputfield: // fall through
     case Bytecodes::_fast_cputfield: // fall through
@@ -3466,10 +3727,11 @@
     // c_rarg3: jvalue object on the stack
     LP64_ONLY(__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_modification), rbx, c_rarg2, c_rarg3));
     NOT_LP64(__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_modification), rbx, rax, rcx));
 
     switch (bytecode()) {             // restore tos values
+    case Bytecodes::_fast_vputfield: // fall through
     case Bytecodes::_fast_aputfield: __ pop_ptr(rax); break;
     case Bytecodes::_fast_bputfield: // fall through
     case Bytecodes::_fast_zputfield: // fall through
     case Bytecodes::_fast_sputfield: // fall through
     case Bytecodes::_fast_cputfield: // fall through
@@ -3484,50 +3746,77 @@
 }
 
 void TemplateTable::fast_storefield(TosState state) {
   transition(state, vtos);
 
-  Register cache = rcx;
-
   Label notVolatile, Done;
 
   jvmti_post_fast_field_mod();
 
   __ push(rax);
   __ load_field_entry(rcx, rax);
-  load_resolved_field_entry(noreg, cache, rax, rbx, rdx);
-  // RBX: field offset, RCX: RAX: TOS, RDX: flags
-  __ andl(rdx, (1 << ResolvedFieldEntry::is_volatile_shift));
+  load_resolved_field_entry(noreg, rcx, rax, rbx, rdx);
   __ pop(rax);
+  // RBX: field offset, RCX: RAX: TOS, RDX: flags
 
   // Get object from stack
   pop_and_check_object(rcx);
 
   // field address
   const Address field(rcx, rbx, Address::times_1);
 
   // Check for volatile store
-  __ testl(rdx, rdx);
+  __ movl(rscratch2, rdx);  // saving flags for is_flat test
+  __ andl(rscratch2, (1 << ResolvedFieldEntry::is_volatile_shift));
+  __ testl(rscratch2, rscratch2);
   __ jcc(Assembler::zero, notVolatile);
 
-  fast_storefield_helper(field, rax);
+  fast_storefield_helper(field, rax, rdx);
   volatile_barrier(Assembler::Membar_mask_bits(Assembler::StoreLoad |
                                                Assembler::StoreStore));
   __ jmp(Done);
   __ bind(notVolatile);
 
-  fast_storefield_helper(field, rax);
+  fast_storefield_helper(field, rax, rdx);
 
   __ bind(Done);
 }
 
-void TemplateTable::fast_storefield_helper(Address field, Register rax) {
+void TemplateTable::fast_storefield_helper(Address field, Register rax, Register flags) {
+
+  // DANGER: 'field' argument depends on rcx and rbx
 
   // access field
   switch (bytecode()) {
+  case Bytecodes::_fast_vputfield:
+    {
+      Label is_flat, has_null_marker, write_null, done;
+      __ test_field_has_null_marker(flags, rscratch1, has_null_marker);
+      // Null free field cases: flat or not flat
+      __ null_check(rax);
+      __ test_field_is_flat(flags, rscratch1, is_flat);
+        // field is not flat
+        do_oop_store(_masm, field, rax);
+        __ jmp(done);
+      __ bind(is_flat);
+        // field is flat
+        __ load_klass(rdx, rax, rscratch1);
+        __ data_for_oop(rax, rax, rdx);
+        __ lea(rcx, field);
+        __ access_value_copy(IN_HEAP, rax, rcx, rdx);
+        __ jmp(done);
+      __ bind(has_null_marker); // has null marker means the field is flat with a null marker
+        __ movptr(rbx, rcx);
+        __ load_field_entry(rcx, rdx);
+        call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::write_nullable_flat_field), rbx, rax, rcx);
+      __ bind(done);
+    }
+    break;
   case Bytecodes::_fast_aputfield:
-    do_oop_store(_masm, field, rax);
+    {
+      do_oop_store(_masm, field, rax);
+    }
     break;
   case Bytecodes::_fast_lputfield:
 #ifdef _LP64
     __ access_store_at(T_LONG, IN_HEAP, field, noreg /* ltos */, noreg, noreg, noreg);
 #else
@@ -3585,19 +3874,52 @@
     __ bind(L1);
   }
 
   // access constant pool cache
   __ load_field_entry(rcx, rbx);
-  __ load_sized_value(rbx, Address(rcx, in_bytes(ResolvedFieldEntry::field_offset_offset())), sizeof(int), true /*is_signed*/);
+  __ load_sized_value(rdx, Address(rcx, in_bytes(ResolvedFieldEntry::field_offset_offset())), sizeof(int), true /*is_signed*/);
 
   // rax: object
   __ verify_oop(rax);
   __ null_check(rax);
-  Address field(rax, rbx, Address::times_1);
+  Address field(rax, rdx, Address::times_1);
 
   // access field
   switch (bytecode()) {
+  case Bytecodes::_fast_vgetfield:
+    {
+      Label is_flat, nonnull, Done, has_null_marker;
+      __ load_unsigned_byte(rscratch1, Address(rcx, in_bytes(ResolvedFieldEntry::flags_offset())));
+      __ test_field_has_null_marker(rscratch1, rscratch2, has_null_marker);
+      __ test_field_is_flat(rscratch1, rscratch2, is_flat);
+        // field is not flat
+        __ load_heap_oop(rax, field);
+        __ testptr(rax, rax);
+        __ jcc(Assembler::notZero, nonnull);
+          __ load_unsigned_short(rdx, Address(rcx, in_bytes(ResolvedFieldEntry::field_index_offset())));
+          __ movptr(rcx, Address(rcx, ResolvedFieldEntry::field_holder_offset()));
+          __ get_inline_type_field_klass(rcx, rdx, rbx);
+          __ get_default_value_oop(rbx, rcx, rax);
+        __ bind(nonnull);
+        __ verify_oop(rax);
+        __ jmp(Done);
+      __ bind(is_flat);
+      // field is flat
+        __ push(rdx); // save offset
+        __ load_unsigned_short(rdx, Address(rcx, in_bytes(ResolvedFieldEntry::field_index_offset())));
+        __ movptr(rcx, Address(rcx, ResolvedFieldEntry::field_holder_offset()));
+        __ pop(rbx); // restore offset
+        __ read_flat_field(rcx, rdx, rbx, rax);
+        __ jmp(Done);
+      __ bind(has_null_marker);
+        // rax = instance, rcx = resolved entry
+        call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::read_nullable_flat_field), rax, rcx);
+        __ get_vm_result(rax, r15_thread);
+      __ bind(Done);
+      __ verify_oop(rax);
+    }
+    break;
   case Bytecodes::_fast_agetfield:
     do_oop_load(_masm, field, rax);
     __ verify_oop(rax);
     break;
   case Bytecodes::_fast_lgetfield:
@@ -4029,119 +4351,38 @@
 
 void TemplateTable::_new() {
   transition(vtos, atos);
   __ get_unsigned_2_byte_index_at_bcp(rdx, 1);
   Label slow_case;
-  Label slow_case_no_pop;
   Label done;
-  Label initialize_header;
 
   __ get_cpool_and_tags(rcx, rax);
 
   // Make sure the class we're about to instantiate has been resolved.
   // This is done before loading InstanceKlass to be consistent with the order
   // how Constant Pool is updated (see ConstantPool::klass_at_put)
   const int tags_offset = Array<u1>::base_offset_in_bytes();
   __ cmpb(Address(rax, rdx, Address::times_1, tags_offset), JVM_CONSTANT_Class);
-  __ jcc(Assembler::notEqual, slow_case_no_pop);
+  __ jcc(Assembler::notEqual, slow_case);
 
   // get InstanceKlass
   __ load_resolved_klass_at_index(rcx, rcx, rdx);
-  __ push(rcx);  // save the contexts of klass for initializing the header
 
   // make sure klass is initialized
 #ifdef _LP64
   assert(VM_Version::supports_fast_class_init_checks(), "must support fast class initialization checks");
   __ clinit_barrier(rcx, r15_thread, nullptr /*L_fast_path*/, &slow_case);
 #else
   __ cmpb(Address(rcx, InstanceKlass::init_state_offset()), InstanceKlass::fully_initialized);
   __ jcc(Assembler::notEqual, slow_case);
 #endif
 
-  // make sure klass doesn't have finalizer
-  // get instance_size in InstanceKlass (scaled to a count of bytes)
-  __ movl(rdx, Address(rcx, Klass::layout_helper_offset()));
-  // test to see if it has a finalizer or is malformed in some way
-  __ testl(rdx, Klass::_lh_instance_slow_path_bit);
-  __ jcc(Assembler::notZero, slow_case);
-
-  // Allocate the instance:
-  //  If TLAB is enabled:
-  //    Try to allocate in the TLAB.
-  //    If fails, go to the slow path.
-  //    Initialize the allocation.
-  //    Exit.
-  //
-  //  Go to slow path.
-
-  const Register thread = LP64_ONLY(r15_thread) NOT_LP64(rcx);
-
-  if (UseTLAB) {
-    NOT_LP64(__ get_thread(thread);)
-    __ tlab_allocate(thread, rax, rdx, 0, rcx, rbx, slow_case);
-    if (ZeroTLAB) {
-      // the fields have been already cleared
-      __ jmp(initialize_header);
-    }
-
-    // The object is initialized before the header.  If the object size is
-    // zero, go directly to the header initialization.
-    __ decrement(rdx, sizeof(oopDesc));
-    __ jcc(Assembler::zero, initialize_header);
-
-    // Initialize topmost object field, divide rdx by 8, check if odd and
-    // test if zero.
-    __ xorl(rcx, rcx);    // use zero reg to clear memory (shorter code)
-    __ shrl(rdx, LogBytesPerLong); // divide by 2*oopSize and set carry flag if odd
-
-    // rdx must have been multiple of 8
-#ifdef ASSERT
-    // make sure rdx was multiple of 8
-    Label L;
-    // Ignore partial flag stall after shrl() since it is debug VM
-    __ jcc(Assembler::carryClear, L);
-    __ stop("object size is not multiple of 2 - adjust this code");
-    __ bind(L);
-    // rdx must be > 0, no extra check needed here
-#endif
-
-    // initialize remaining object fields: rdx was a multiple of 8
-    { Label loop;
-    __ bind(loop);
-    __ movptr(Address(rax, rdx, Address::times_8, sizeof(oopDesc) - 1*oopSize), rcx);
-    NOT_LP64(__ movptr(Address(rax, rdx, Address::times_8, sizeof(oopDesc) - 2*oopSize), rcx));
-    __ decrement(rdx);
-    __ jcc(Assembler::notZero, loop);
-    }
-
-    // initialize object header only.
-    __ bind(initialize_header);
-    __ movptr(Address(rax, oopDesc::mark_offset_in_bytes()),
-              (intptr_t)markWord::prototype().value()); // header
-    __ pop(rcx);   // get saved klass back in the register.
-#ifdef _LP64
-    __ xorl(rsi, rsi); // use zero reg to clear memory (shorter code)
-    __ store_klass_gap(rax, rsi);  // zero klass gap for compressed oops
-#endif
-    __ store_klass(rax, rcx, rscratch1);  // klass
-
-    {
-      SkipIfEqual skip_if(_masm, &DTraceAllocProbes, 0, rscratch1);
-      // Trigger dtrace event for fastpath
-      __ push(atos);
-      __ call_VM_leaf(
-           CAST_FROM_FN_PTR(address, static_cast<int (*)(oopDesc*)>(SharedRuntime::dtrace_object_alloc)), rax);
-      __ pop(atos);
-    }
-
-    __ jmp(done);
-  }
+  __ allocate_instance(rcx, rax, rdx, rbx, true, slow_case);
+  __ jmp(done);
 
   // slow case
   __ bind(slow_case);
-  __ pop(rcx);   // restore stack pointer to what it was when we came in.
-  __ bind(slow_case_no_pop);
 
   Register rarg1 = LP64_ONLY(c_rarg1) NOT_LP64(rax);
   Register rarg2 = LP64_ONLY(c_rarg2) NOT_LP64(rdx);
 
   __ get_constant_pool(rarg1);
@@ -4186,14 +4427,14 @@
 
   // Get cpool & tags index
   __ get_cpool_and_tags(rcx, rdx); // rcx=cpool, rdx=tags array
   __ get_unsigned_2_byte_index_at_bcp(rbx, 1); // rbx=index
   // See if bytecode has already been quicked
-  __ cmpb(Address(rdx, rbx,
-                  Address::times_1,
-                  Array<u1>::base_offset_in_bytes()),
-          JVM_CONSTANT_Class);
+  __ movzbl(rdx, Address(rdx, rbx,
+      Address::times_1,
+      Array<u1>::base_offset_in_bytes()));
+  __ cmpl(rdx, JVM_CONSTANT_Class);
   __ jcc(Assembler::equal, quicked);
   __ push(atos); // save receiver for result, and for GC
   call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc));
 
   // vm_result_2 has metadata result
@@ -4227,19 +4468,19 @@
   __ jump(ExternalAddress(Interpreter::_throw_ClassCastException_entry));
 
   // Come here on success
   __ bind(ok_is_subtype);
   __ mov(rax, rdx); // Restore object in rdx
+  __ jmp(done);
+
+  __ bind(is_null);
 
   // Collect counts on whether this check-cast sees nulls a lot or not.
   if (ProfileInterpreter) {
-    __ jmp(done);
-    __ bind(is_null);
     __ profile_null_seen(rcx);
-  } else {
-    __ bind(is_null);   // same as 'done'
   }
+
   __ bind(done);
 }
 
 void TemplateTable::instanceof() {
   transition(atos, itos);
@@ -4249,14 +4490,14 @@
 
   // Get cpool & tags index
   __ get_cpool_and_tags(rcx, rdx); // rcx=cpool, rdx=tags array
   __ get_unsigned_2_byte_index_at_bcp(rbx, 1); // rbx=index
   // See if bytecode has already been quicked
-  __ cmpb(Address(rdx, rbx,
-                  Address::times_1,
-                  Array<u1>::base_offset_in_bytes()),
-          JVM_CONSTANT_Class);
+  __ movzbl(rdx, Address(rdx, rbx,
+        Address::times_1,
+        Array<u1>::base_offset_in_bytes()));
+  __ cmpl(rdx, JVM_CONSTANT_Class);
   __ jcc(Assembler::equal, quicked);
 
   __ push(atos); // save receiver for result, and for GC
   call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc));
   // vm_result_2 has metadata result
@@ -4304,11 +4545,10 @@
   __ bind(done);
   // rax = 0: obj == nullptr or  obj is not an instanceof the specified klass
   // rax = 1: obj != nullptr and obj is     an instanceof the specified klass
 }
 
-
 //----------------------------------------------------------------------------------------------------
 // Breakpoints
 void TemplateTable::_breakpoint() {
   // Note: We get here even if we are single stepping..
   // jbug insists on setting breakpoints at every bytecode
@@ -4366,10 +4606,14 @@
   transition(atos, vtos);
 
   // check for null object
   __ null_check(rax);
 
+  Label is_inline_type;
+  __ movptr(rbx, Address(rax, oopDesc::mark_offset_in_bytes()));
+  __ test_markword_is_inline_type(rbx, is_inline_type);
+
   const Address monitor_block_top(
         rbp, frame::interpreter_frame_monitor_block_top_offset * wordSize);
   const Address monitor_block_bot(
         rbp, frame::interpreter_frame_initial_sp_offset * wordSize);
   const int entry_size = frame::interpreter_frame_monitor_size_in_bytes();
@@ -4458,18 +4702,34 @@
   __ generate_stack_overflow_check(0);
 
   // The bcp has already been incremented. Just need to dispatch to
   // next instruction.
   __ dispatch_next(vtos);
+
+  __ bind(is_inline_type);
+  __ call_VM(noreg, CAST_FROM_FN_PTR(address,
+                    InterpreterRuntime::throw_illegal_monitor_state_exception));
+  __ should_not_reach_here();
 }
 
 void TemplateTable::monitorexit() {
   transition(atos, vtos);
 
   // check for null object
   __ null_check(rax);
 
+  const int is_inline_type_mask = markWord::inline_type_pattern;
+  Label has_identity;
+  __ movptr(rbx, Address(rax, oopDesc::mark_offset_in_bytes()));
+  __ andptr(rbx, is_inline_type_mask);
+  __ cmpl(rbx, is_inline_type_mask);
+  __ jcc(Assembler::notEqual, has_identity);
+  __ call_VM(noreg, CAST_FROM_FN_PTR(address,
+                     InterpreterRuntime::throw_illegal_monitor_state_exception));
+  __ should_not_reach_here();
+  __ bind(has_identity);
+
   const Address monitor_block_top(
         rbp, frame::interpreter_frame_monitor_block_top_offset * wordSize);
   const Address monitor_block_bot(
         rbp, frame::interpreter_frame_initial_sp_offset * wordSize);
   const int entry_size = frame::interpreter_frame_monitor_size_in_bytes();
diff a/src/hotspot/cpu/x86/templateTable_x86.hpp b/src/hotspot/cpu/x86/templateTable_x86.hpp
--- a/src/hotspot/cpu/x86/templateTable_x86.hpp
+++ b/src/hotspot/cpu/x86/templateTable_x86.hpp
@@ -35,9 +35,11 @@
   // Helpers
   static void index_check(Register array, Register index);
   static void index_check_without_pop(Register array, Register index);
 
   static void putfield_or_static_helper(int byte_no, bool is_static, RewriteControl rc,
-                                        Register obj, Register off, Register flags);
-  static void fast_storefield_helper(Address field, Register rax);
+                                        Register obj, Register off, Register tos_state, Register flags);
+  static void fast_storefield_helper(Address field, Register obj, Register flags);
+
+  static void invoke_is_substitutable(Register aobj, Register bobj, Label& is_subst, Label& not_subst);
 
 #endif // CPU_X86_TEMPLATETABLE_X86_HPP
diff a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp
--- a/src/hotspot/cpu/x86/vm_version_x86.cpp
+++ b/src/hotspot/cpu/x86/vm_version_x86.cpp
@@ -1792,11 +1792,11 @@
     }
   }
 #endif
 
   // Use XMM/YMM MOVDQU instruction for Object Initialization
-  if (!UseFastStosb && UseSSE >= 2 && UseUnalignedLoadStores) {
+  if (UseSSE >= 2 && UseUnalignedLoadStores) {
     if (FLAG_IS_DEFAULT(UseXMMForObjInit)) {
       UseXMMForObjInit = true;
     }
   } else if (UseXMMForObjInit) {
     warning("UseXMMForObjInit requires SSE2 and unaligned load/stores. Feature is switched off.");
diff a/src/hotspot/cpu/x86/vtableStubs_x86_64.cpp b/src/hotspot/cpu/x86/vtableStubs_x86_64.cpp
--- a/src/hotspot/cpu/x86/vtableStubs_x86_64.cpp
+++ b/src/hotspot/cpu/x86/vtableStubs_x86_64.cpp
@@ -43,15 +43,15 @@
 
 #ifndef PRODUCT
 extern "C" void bad_compiled_vtable_index(JavaThread* thread, oop receiver, int index);
 #endif
 
-VtableStub* VtableStubs::create_vtable_stub(int vtable_index) {
+VtableStub* VtableStubs::create_vtable_stub(int vtable_index, bool caller_is_c1) {
   // Read "A word on VtableStub sizing" in share/code/vtableStubs.hpp for details on stub sizing.
   const int stub_code_length = code_size_limit(true);
-  VtableStub* s = new(stub_code_length) VtableStub(true, vtable_index);
-  // Can be null if there is no free space in the code cache.
+  VtableStub* s = new(stub_code_length) VtableStub(true, vtable_index, caller_is_c1);
+  // Can be nullptr if there is no free space in the code cache.
   if (s == nullptr) {
     return nullptr;
   }
 
   // Count unused bytes in instruction sequences of variable size.
@@ -60,10 +60,11 @@
   address   start_pc;
   int       slop_bytes = 0;
   int       slop_delta = 0;
   // No variance was detected in vtable stub sizes. Setting index_dependent_slop == 0 will unveil any deviation from this observation.
   const int index_dependent_slop     = 0;
+  ByteSize  entry_offset = caller_is_c1 ? Method::from_compiled_inline_offset() :  Method::from_compiled_inline_ro_offset();
 
   ResourceMark    rm;
   CodeBuffer      cb(s->entry_point(), stub_code_length);
   MacroAssembler* masm = new MacroAssembler(&cb);
 
@@ -116,36 +117,37 @@
 #ifndef PRODUCT
   if (DebugVtables) {
     Label L;
     __ cmpptr(method, NULL_WORD);
     __ jcc(Assembler::equal, L);
-    __ cmpptr(Address(method, Method::from_compiled_offset()), NULL_WORD);
+    __ cmpptr(Address(method, entry_offset), NULL_WORD);
     __ jcc(Assembler::notZero, L);
     __ stop("Vtable entry is null");
     __ bind(L);
   }
 #endif // PRODUCT
 
   // rax: receiver klass
   // method (rbx): Method*
   // rcx: receiver
   address ame_addr = __ pc();
-  __ jmp( Address(rbx, Method::from_compiled_offset()));
+  __ jmp( Address(rbx, entry_offset));
 
   masm->flush();
   slop_bytes += index_dependent_slop; // add'l slop for size variance due to large itable offsets
   bookkeeping(masm, tty, s, npe_addr, ame_addr, true, vtable_index, slop_bytes, index_dependent_slop);
 
   return s;
 }
 
 
-VtableStub* VtableStubs::create_itable_stub(int itable_index) {
+VtableStub* VtableStubs::create_itable_stub(int itable_index, bool caller_is_c1) {
   // Read "A word on VtableStub sizing" in share/code/vtableStubs.hpp for details on stub sizing.
   const int stub_code_length = code_size_limit(false);
-  VtableStub* s = new(stub_code_length) VtableStub(false, itable_index);
-  // Can be null if there is no free space in the code cache.
+  ByteSize  entry_offset = caller_is_c1 ? Method::from_compiled_inline_offset() :  Method::from_compiled_inline_ro_offset();
+  VtableStub* s = new(stub_code_length) VtableStub(false, itable_index, caller_is_c1);
+  // Can be nullptr if there is no free space in the code cache.
   if (s == nullptr) {
     return nullptr;
   }
 
   // Count unused bytes in instruction sequences of variable size.
@@ -208,11 +210,11 @@
   const ptrdiff_t  lookupSize = __ pc() - start_pc;
 
   // We expect we need index_dependent_slop extra bytes. Reason:
   // The emitted code in lookup_interface_method changes when itable_index exceeds 15.
   // For linux, a very narrow estimate would be 112, but Solaris requires some more space (130).
-  const ptrdiff_t estimate = 136;
+  const ptrdiff_t estimate = 144;
   const ptrdiff_t codesize = lookupSize + index_dependent_slop;
   slop_delta  = (int)(estimate - codesize);
   slop_bytes += slop_delta;
   assert(slop_delta >= 0, "itable #%d: Code size estimate (%d) for lookup_interface_method too small, required: %d", itable_index, (int)estimate, (int)codesize);
 
@@ -227,19 +229,19 @@
 #ifdef ASSERT
   if (DebugVtables) {
     Label L2;
     __ cmpptr(method, NULL_WORD);
     __ jcc(Assembler::equal, L2);
-    __ cmpptr(Address(method, Method::from_compiled_offset()), NULL_WORD);
+    __ cmpptr(Address(method, entry_offset), NULL_WORD);
     __ jcc(Assembler::notZero, L2);
     __ stop("compiler entrypoint is null");
     __ bind(L2);
   }
 #endif // ASSERT
 
   address ame_addr = __ pc();
-  __ jmp(Address(method, Method::from_compiled_offset()));
+  __ jmp(Address(method, entry_offset));
 
   __ bind(L_no_such_interface);
   // Handle IncompatibleClassChangeError in itable stubs.
   // More detailed error message.
   // We force resolving of the call site by jumping to the "handle
diff a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad
--- a/src/hotspot/cpu/x86/x86.ad
+++ b/src/hotspot/cpu/x86/x86.ad
@@ -1310,11 +1310,11 @@
 
   // Note that the code buffer's insts_mark is always relative to insts.
   // That's why we must use the macroassembler to generate a handler.
   C2_MacroAssembler _masm(&cbuf);
   address base = __ start_a_stub(size_exception_handler());
-  if (base == NULL) {
+  if (base == nullptr) {
     ciEnv::current()->record_failure("CodeCache is full");
     return 0;  // CodeBuffer::expand failed
   }
   int offset = __ offset();
   __ jump(RuntimeAddress(OptoRuntime::exception_blob()->entry_point()));
@@ -1328,11 +1328,11 @@
 
   // Note that the code buffer's insts_mark is always relative to insts.
   // That's why we must use the macroassembler to generate a handler.
   C2_MacroAssembler _masm(&cbuf);
   address base = __ start_a_stub(size_deopt_handler());
-  if (base == NULL) {
+  if (base == nullptr) {
     ciEnv::current()->record_failure("CodeCache is full");
     return 0;  // CodeBuffer::expand failed
   }
   int offset = __ offset();
 
@@ -2184,11 +2184,11 @@
       case Op_VecY: return new vecYOper();
       case Op_VecZ: return new vecZOper();
     }
   }
   ShouldNotReachHere();
-  return NULL;
+  return nullptr;
 }
 
 bool Matcher::is_reg2reg_move(MachNode* m) {
   switch (m->rule()) {
     case MoveVec2Leg_rule:
@@ -2353,11 +2353,11 @@
   Node* _op1_node;
   Node* _mop_node;
   int _con_op;
 
   static int match_next(Node* n, int next_op, int next_op_idx) {
-    if (n->in(1) == NULL || n->in(2) == NULL) {
+    if (n->in(1) == nullptr || n->in(2) == nullptr) {
       return -1;
     }
 
     if (next_op_idx == -1) { // n is commutative, try rotations
       if (n->in(1)->Opcode() == next_op) {
@@ -2420,11 +2420,11 @@
   }
 };
 
 static bool is_bmi_pattern(Node* n, Node* m) {
   assert(UseBMI1Instructions, "sanity");
-  if (n != NULL && m != NULL) {
+  if (n != nullptr && m != nullptr) {
     if (m->Opcode() == Op_LoadI) {
       FusedPatternMatcher<TypeInt> bmii(n, m, Op_ConI);
       return bmii.match(Op_AndI, -1, Op_SubI,  1,  0)  ||
              bmii.match(Op_AndI, -1, Op_AddI, -1, -1)  ||
              bmii.match(Op_XorI, -1, Op_AddI, -1, -1);
@@ -2786,10 +2786,44 @@
       __ jccb(Assembler::equal, L);
       // Die if stack mismatch
       __ int3();
       __ bind(L);
     }
+    if (tf()->returns_inline_type_as_fields() && !_method->is_method_handle_intrinsic()) {
+      C2_MacroAssembler _masm(&cbuf);
+      // The last return value is not set by the callee but used to pass IsInit information to compiled code.
+      // Search for the corresponding projection, get the register and emit code that initialized it.
+      uint con = (tf()->range_cc()->cnt() - 1);
+      for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) {
+        ProjNode* proj = fast_out(i)->as_Proj();
+        if (proj->_con == con) {
+          // Set IsInit if rax is non-null (a non-null value is returned buffered or scalarized)
+          OptoReg::Name optoReg = ra_->get_reg_first(proj);
+          VMReg reg = OptoReg::as_VMReg(optoReg, ra_->_framesize, OptoReg::reg2stack(ra_->_matcher._new_SP));
+          Register toReg = reg->is_reg() ? reg->as_Register() : rscratch1;
+          __ testq(rax, rax);
+          __ setb(Assembler::notZero, toReg);
+          __ movzbl(toReg, toReg);
+          if (reg->is_stack()) {
+            int st_off = reg->reg2stack() * VMRegImpl::stack_slot_size;
+            __ movq(Address(rsp, st_off), toReg);
+          }
+          break;
+        }
+      }
+      if (return_value_is_used()) {
+        // An inline type is returned as fields in multiple registers.
+        // Rax either contains an oop if the inline type is buffered or a pointer
+        // to the corresponding InlineKlass with the lowest bit set to 1. Zero rax
+        // if the lowest bit is set to allow C2 to use the oop after null checking.
+        // rax &= (rax & 1) - 1
+        __ movptr(rscratch1, rax);
+        __ andptr(rscratch1, 0x1);
+        __ subptr(rscratch1, 0x1);
+        __ andptr(rax, rscratch1);
+      }
+    }
   %}
 
 %}
 
 // Operands for bound floating pointer register arguments
@@ -7444,11 +7478,11 @@
 #endif // _LP64
 
 // --------------------------------- VectorMaskCmp --------------------------------------
 
 instruct vcmpFD(legVec dst, legVec src1, legVec src2, immI8 cond) %{
-  predicate(n->bottom_type()->isa_vectmask() == NULL &&
+  predicate(n->bottom_type()->isa_vectmask() == nullptr &&
             Matcher::vector_length_in_bytes(n->in(1)->in(1)) >=  8 && // src1
             Matcher::vector_length_in_bytes(n->in(1)->in(1)) <= 32 && // src1
             is_floating_point_type(Matcher::vector_element_basic_type(n->in(1)->in(1)))); // src1 T_FLOAT, T_DOUBLE
   match(Set dst (VectorMaskCmp (Binary src1 src2) cond));
   format %{ "vector_compare $dst,$src1,$src2,$cond\t!" %}
@@ -7464,11 +7498,11 @@
   ins_pipe( pipe_slow );
 %}
 
 instruct evcmpFD64(vec dst, vec src1, vec src2, immI8 cond, kReg ktmp) %{
   predicate(Matcher::vector_length_in_bytes(n->in(1)->in(1)) == 64 && // src1
-            n->bottom_type()->isa_vectmask() == NULL &&
+            n->bottom_type()->isa_vectmask() == nullptr &&
             is_floating_point_type(Matcher::vector_element_basic_type(n->in(1)->in(1)))); // src1 T_FLOAT, T_DOUBLE
   match(Set dst (VectorMaskCmp (Binary src1 src2) cond));
   effect(TEMP ktmp);
   format %{ "vector_compare $dst,$src1,$src2,$cond" %}
   ins_encode %{
@@ -7504,11 +7538,11 @@
   %}
   ins_pipe( pipe_slow );
 %}
 
 instruct vcmp_direct(legVec dst, legVec src1, legVec src2, immI8 cond) %{
-  predicate(n->bottom_type()->isa_vectmask() == NULL &&
+  predicate(n->bottom_type()->isa_vectmask() == nullptr &&
             !Matcher::is_unsigned_booltest_pred(n->in(2)->get_int()) &&
             Matcher::vector_length_in_bytes(n->in(1)->in(1)) >=  4 && // src1
             Matcher::vector_length_in_bytes(n->in(1)->in(1)) <= 32 && // src1
             is_integral_type(Matcher::vector_element_basic_type(n->in(1)->in(1))) &&
             (n->in(2)->get_int() == BoolTest::eq ||
@@ -7524,11 +7558,11 @@
   %}
   ins_pipe( pipe_slow );
 %}
 
 instruct vcmp_negate(legVec dst, legVec src1, legVec src2, immI8 cond, legVec xtmp) %{
-  predicate(n->bottom_type()->isa_vectmask() == NULL &&
+  predicate(n->bottom_type()->isa_vectmask() == nullptr &&
             !Matcher::is_unsigned_booltest_pred(n->in(2)->get_int()) &&
             Matcher::vector_length_in_bytes(n->in(1)->in(1)) >=  4 && // src1
             Matcher::vector_length_in_bytes(n->in(1)->in(1)) <= 32 && // src1
             is_integral_type(Matcher::vector_element_basic_type(n->in(1)->in(1))) &&
             (n->in(2)->get_int() == BoolTest::ne ||
@@ -7545,11 +7579,11 @@
   %}
   ins_pipe( pipe_slow );
 %}
 
 instruct vcmpu(legVec dst, legVec src1, legVec src2, immI8 cond, legVec xtmp) %{
-  predicate(n->bottom_type()->isa_vectmask() == NULL &&
+  predicate(n->bottom_type()->isa_vectmask() == nullptr &&
             Matcher::is_unsigned_booltest_pred(n->in(2)->get_int()) &&
             Matcher::vector_length_in_bytes(n->in(1)->in(1)) >=  4 && // src1
             Matcher::vector_length_in_bytes(n->in(1)->in(1)) <= 32 && // src1
             is_integral_type(Matcher::vector_element_basic_type(n->in(1)->in(1)))); // src1
   match(Set dst (VectorMaskCmp (Binary src1 src2) cond));
@@ -7572,11 +7606,11 @@
   %}
   ins_pipe( pipe_slow );
 %}
 
 instruct vcmp64(vec dst, vec src1, vec src2, immI8 cond, kReg ktmp) %{
-  predicate((n->bottom_type()->isa_vectmask() == NULL &&
+  predicate((n->bottom_type()->isa_vectmask() == nullptr &&
              Matcher::vector_length_in_bytes(n->in(1)->in(1)) == 64) && // src1
              is_integral_type(Matcher::vector_element_basic_type(n->in(1)->in(1)))); // src1
   match(Set dst (VectorMaskCmp (Binary src1 src2) cond));
   effect(TEMP ktmp);
   format %{ "vector_compare $dst,$src1,$src2,$cond" %}
@@ -7788,11 +7822,11 @@
   ins_pipe( pipe_slow );
 %}
 
 instruct vblendvpI(legVec dst, legVec src1, legVec src2, legVec mask) %{
   predicate(UseAVX > 0 && !EnableX86ECoreOpts &&
-            n->in(2)->bottom_type()->isa_vectmask() == NULL &&
+            n->in(2)->bottom_type()->isa_vectmask() == nullptr &&
             Matcher::vector_length_in_bytes(n) <= 32 &&
             is_integral_type(Matcher::vector_element_basic_type(n)));
   match(Set dst (VectorBlend (Binary src1 src2) mask));
   format %{ "vector_blend  $dst,$src1,$src2,$mask\t!" %}
   ins_encode %{
@@ -7802,11 +7836,11 @@
   ins_pipe( pipe_slow );
 %}
 
 instruct vblendvpFD(legVec dst, legVec src1, legVec src2, legVec mask) %{
   predicate(UseAVX > 0 && !EnableX86ECoreOpts &&
-            n->in(2)->bottom_type()->isa_vectmask() == NULL &&
+            n->in(2)->bottom_type()->isa_vectmask() == nullptr &&
             Matcher::vector_length_in_bytes(n) <= 32 &&
             !is_integral_type(Matcher::vector_element_basic_type(n)));
   match(Set dst (VectorBlend (Binary src1 src2) mask));
   format %{ "vector_blend  $dst,$src1,$src2,$mask\t!" %}
   ins_encode %{
@@ -7832,11 +7866,11 @@
   ins_pipe( pipe_slow );
 %}
 
 instruct evblendvp64(vec dst, vec src1, vec src2, vec mask, kReg ktmp) %{
   predicate(Matcher::vector_length_in_bytes(n) == 64 &&
-            n->in(2)->bottom_type()->isa_vectmask() == NULL);
+            n->in(2)->bottom_type()->isa_vectmask() == nullptr);
   match(Set dst (VectorBlend (Binary src1 src2) mask));
   format %{ "vector_blend  $dst,$src1,$src2,$mask\t! using k2 as TEMP" %}
   effect(TEMP ktmp);
   ins_encode %{
      int vlen_enc = Assembler::AVX_512bit;
@@ -8049,11 +8083,11 @@
 #endif
 
 //------------------------------------- LoadMask --------------------------------------------
 
 instruct loadMask(legVec dst, legVec src) %{
-  predicate(n->bottom_type()->isa_vectmask() == NULL && !VM_Version::supports_avx512vlbw());
+  predicate(n->bottom_type()->isa_vectmask() == nullptr && !VM_Version::supports_avx512vlbw());
   match(Set dst (VectorLoadMask src));
   effect(TEMP dst);
   format %{ "vector_loadmask_byte $dst, $src\n\t" %}
   ins_encode %{
     int vlen_in_bytes = Matcher::vector_length_in_bytes(this);
@@ -8089,11 +8123,11 @@
 %}
 
 //------------------------------------- StoreMask --------------------------------------------
 
 instruct vstoreMask1B(vec dst, vec src, immI_1 size) %{
-  predicate(Matcher::vector_length(n) < 64 && n->in(1)->bottom_type()->isa_vectmask() == NULL);
+  predicate(Matcher::vector_length(n) < 64 && n->in(1)->bottom_type()->isa_vectmask() == nullptr);
   match(Set dst (VectorStoreMask src size));
   format %{ "vector_store_mask $dst, $src \t! elem size is $size byte[s]" %}
   ins_encode %{
     int vlen = Matcher::vector_length(this);
     if (vlen <= 16 && UseAVX <= 2) {
@@ -8107,11 +8141,11 @@
   %}
   ins_pipe( pipe_slow );
 %}
 
 instruct vstoreMask2B(vec dst, vec src, vec xtmp, immI_2 size) %{
-  predicate(Matcher::vector_length(n) <= 16 && n->in(1)->bottom_type()->isa_vectmask() == NULL);
+  predicate(Matcher::vector_length(n) <= 16 && n->in(1)->bottom_type()->isa_vectmask() == nullptr);
   match(Set dst (VectorStoreMask src size));
   effect(TEMP_DEF dst, TEMP xtmp);
   format %{ "vector_store_mask $dst, $src \t! elem size is $size byte[s]" %}
   ins_encode %{
     int vlen_enc = Assembler::AVX_128bit;
@@ -8130,11 +8164,11 @@
   %}
   ins_pipe( pipe_slow );
 %}
 
 instruct vstoreMask4B(vec dst, vec src, vec xtmp, immI_4 size) %{
-  predicate(UseAVX <= 2 && Matcher::vector_length(n) <= 8 && n->in(1)->bottom_type()->isa_vectmask() == NULL);
+  predicate(UseAVX <= 2 && Matcher::vector_length(n) <= 8 && n->in(1)->bottom_type()->isa_vectmask() == nullptr);
   match(Set dst (VectorStoreMask src size));
   format %{ "vector_store_mask $dst, $src \t! elem size is $size byte[s]" %}
   effect(TEMP_DEF dst, TEMP xtmp);
   ins_encode %{
     int vlen_enc = Assembler::AVX_128bit;
@@ -8190,11 +8224,11 @@
   %}
   ins_pipe( pipe_slow );
 %}
 
 instruct vstoreMask4B_evex_novectmask(vec dst, vec src, immI_4 size) %{
-  predicate(UseAVX > 2 && n->in(1)->bottom_type()->isa_vectmask() == NULL);
+  predicate(UseAVX > 2 && n->in(1)->bottom_type()->isa_vectmask() == nullptr);
   match(Set dst (VectorStoreMask src size));
   format %{ "vector_store_mask $dst, $src \t! elem size is $size byte[s]" %}
   ins_encode %{
     int src_vlen_enc = vector_length_encoding(this, $src);
     int dst_vlen_enc = vector_length_encoding(this);
@@ -8206,11 +8240,11 @@
   %}
   ins_pipe( pipe_slow );
 %}
 
 instruct vstoreMask8B_evex_novectmask(vec dst, vec src, immI_8 size) %{
-  predicate(UseAVX > 2 && n->in(1)->bottom_type()->isa_vectmask() == NULL);
+  predicate(UseAVX > 2 && n->in(1)->bottom_type()->isa_vectmask() == nullptr);
   match(Set dst (VectorStoreMask src size));
   format %{ "vector_store_mask $dst, $src \t! elem size is $size byte[s]" %}
   ins_encode %{
     int src_vlen_enc = vector_length_encoding(this, $src);
     int dst_vlen_enc = vector_length_encoding(this);
@@ -9043,11 +9077,11 @@
   %}
   ins_pipe( pipe_slow );
 %}
 
 instruct vmask_tolong_bool(rRegL dst, vec mask, vec xtmp, rFlagsReg cr) %{
-  predicate(n->in(1)->bottom_type()->isa_vectmask() == NULL);
+  predicate(n->in(1)->bottom_type()->isa_vectmask() == nullptr);
   match(Set dst (VectorMaskToLong mask));
   format %{ "vector_tolong_bool $dst, $mask \t! using $xtmp as TEMP" %}
   effect(TEMP_DEF dst, TEMP xtmp, KILL cr);
   ins_encode %{
     int opcode = this->ideal_Opcode();
@@ -9059,11 +9093,11 @@
   %}
   ins_pipe( pipe_slow );
 %}
 
 instruct vmask_tolong_avx(rRegL dst, vec mask, immI size, vec xtmp, rFlagsReg cr) %{
-  predicate(n->in(1)->in(1)->bottom_type()->isa_vectmask() == NULL);
+  predicate(n->in(1)->in(1)->bottom_type()->isa_vectmask() == nullptr);
   match(Set dst (VectorMaskToLong (VectorStoreMask mask size)));
   format %{ "vector_tolong_avx $dst, $mask \t! using $xtmp as TEMP" %}
   effect(TEMP_DEF dst, TEMP xtmp, KILL cr);
   ins_encode %{
     int opcode = this->ideal_Opcode();
@@ -9092,11 +9126,11 @@
   %}
   ins_pipe( pipe_slow );
 %}
 
 instruct vmask_truecount_bool(rRegI dst, vec mask, rRegL tmp, vec xtmp, rFlagsReg cr) %{
-  predicate(n->in(1)->bottom_type()->isa_vectmask() == NULL);
+  predicate(n->in(1)->bottom_type()->isa_vectmask() == nullptr);
   match(Set dst (VectorMaskTrueCount mask));
   effect(TEMP_DEF dst, TEMP tmp, TEMP xtmp, KILL cr);
   format %{ "vector_truecount_bool $dst, $mask \t! using $tmp, $xtmp as TEMP" %}
   ins_encode %{
     int opcode = this->ideal_Opcode();
@@ -9108,11 +9142,11 @@
   %}
   ins_pipe( pipe_slow );
 %}
 
 instruct vmask_truecount_avx(rRegI dst, vec mask, immI size, rRegL tmp, vec xtmp, rFlagsReg cr) %{
-  predicate(n->in(1)->in(1)->bottom_type()->isa_vectmask() == NULL);
+  predicate(n->in(1)->in(1)->bottom_type()->isa_vectmask() == nullptr);
   match(Set dst (VectorMaskTrueCount (VectorStoreMask mask size)));
   effect(TEMP_DEF dst, TEMP tmp, TEMP xtmp, KILL cr);
   format %{ "vector_truecount_avx $dst, $mask \t! using $tmp, $xtmp as TEMP" %}
   ins_encode %{
     int opcode = this->ideal_Opcode();
@@ -9142,11 +9176,11 @@
   %}
   ins_pipe( pipe_slow );
 %}
 
 instruct vmask_first_or_last_true_bool(rRegI dst, vec mask, rRegL tmp, vec xtmp, rFlagsReg cr) %{
-  predicate(n->in(1)->bottom_type()->isa_vectmask() == NULL);
+  predicate(n->in(1)->bottom_type()->isa_vectmask() == nullptr);
   match(Set dst (VectorMaskFirstTrue mask));
   match(Set dst (VectorMaskLastTrue mask));
   effect(TEMP_DEF dst, TEMP tmp, TEMP xtmp, KILL cr);
   format %{ "vector_mask_first_or_last_true_bool $dst, $mask \t! using $tmp, $xtmp as TEMP" %}
   ins_encode %{
@@ -9159,11 +9193,11 @@
   %}
   ins_pipe( pipe_slow );
 %}
 
 instruct vmask_first_or_last_true_avx(rRegI dst, vec mask, immI size, rRegL tmp, vec xtmp, rFlagsReg cr) %{
-  predicate(n->in(1)->in(1)->bottom_type()->isa_vectmask() == NULL);
+  predicate(n->in(1)->in(1)->bottom_type()->isa_vectmask() == nullptr);
   match(Set dst (VectorMaskFirstTrue (VectorStoreMask mask size)));
   match(Set dst (VectorMaskLastTrue (VectorStoreMask mask size)));
   effect(TEMP_DEF dst, TEMP tmp, TEMP xtmp, KILL cr);
   format %{ "vector_mask_first_or_last_true_avx $dst, $mask \t! using $tmp, $xtmp as TEMP" %}
   ins_encode %{
@@ -9996,11 +10030,11 @@
   %}
   ins_pipe( pipe_slow );
 %}
 
 instruct long_to_maskLE8_avx(vec dst, rRegL src, rRegL rtmp1, rRegL rtmp2, vec xtmp) %{
-  predicate(n->bottom_type()->isa_vectmask() == NULL && Matcher::vector_length(n) <= 8);
+  predicate(n->bottom_type()->isa_vectmask() == nullptr && Matcher::vector_length(n) <= 8);
   match(Set dst (VectorLongToMask src));
   effect(TEMP dst, TEMP rtmp1, TEMP rtmp2, TEMP xtmp);
   format %{ "long_to_mask_avx $dst, $src\t! using $rtmp1, $rtmp2, $xtmp as TEMP" %}
   ins_encode %{
     int mask_len = Matcher::vector_length(this);
@@ -10011,11 +10045,11 @@
   ins_pipe( pipe_slow );
 %}
 
 
 instruct long_to_maskGT8_avx(vec dst, rRegL src, rRegL rtmp1, rRegL rtmp2, vec xtmp1, rFlagsReg cr) %{
-  predicate(n->bottom_type()->isa_vectmask() == NULL && Matcher::vector_length(n) > 8);
+  predicate(n->bottom_type()->isa_vectmask() == nullptr && Matcher::vector_length(n) > 8);
   match(Set dst (VectorLongToMask src));
   effect(TEMP dst, TEMP rtmp1, TEMP rtmp2, TEMP xtmp1, KILL cr);
   format %{ "long_to_mask_avx $dst, $src\t! using $rtmp1, $rtmp2, $xtmp1, as TEMP" %}
   ins_encode %{
     int mask_len = Matcher::vector_length(this);
diff a/src/hotspot/cpu/x86/x86_32.ad b/src/hotspot/cpu/x86/x86_32.ad
--- a/src/hotspot/cpu/x86/x86_32.ad
+++ b/src/hotspot/cpu/x86/x86_32.ad
@@ -609,14 +609,11 @@
 
 void MachPrologNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
   Compile* C = ra_->C;
   C2_MacroAssembler _masm(&cbuf);
 
-  int framesize = C->output()->frame_size_in_bytes();
-  int bangsize = C->output()->bang_size_in_bytes();
-
-  __ verified_entry(framesize, C->output()->need_stack_bang(bangsize)?bangsize:0, C->in_24_bit_fp_mode(), C->stub_function() != NULL);
+  __ verified_entry(C);
 
   C->output()->set_frame_complete(cbuf.insts_size());
 
   if (C->has_mach_constant_base_node()) {
     // NOTE: We set the table base offset here because users might be
@@ -1050,11 +1047,11 @@
   int size = 0;
 
   if( src_first == dst_first && src_second == dst_second )
     return size;            // Self copy, no move
 
-  if (bottom_type()->isa_vect() != NULL && bottom_type()->isa_vectmask() == NULL) {
+  if (bottom_type()->isa_vect() != nullptr && bottom_type()->isa_vectmask() == nullptr) {
     uint ireg = ideal_reg();
     assert((src_first_rc != rc_int && dst_first_rc != rc_int), "sanity");
     assert((src_first_rc != rc_float && dst_first_rc != rc_float), "sanity");
     assert((ireg == Op_VecS || ireg == Op_VecD || ireg == Op_VecX || ireg == Op_VecY || ireg == Op_VecZ ), "sanity");
     if( src_first_rc == rc_stack && dst_first_rc == rc_stack ) {
@@ -1318,16 +1315,16 @@
   return 0; // Mute compiler
 }
 
 #ifndef PRODUCT
 void MachSpillCopyNode::format(PhaseRegAlloc *ra_, outputStream* st) const {
-  implementation( NULL, ra_, false, st );
+  implementation( nullptr, ra_, false, st );
 }
 #endif
 
 void MachSpillCopyNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
-  implementation( &cbuf, ra_, false, NULL );
+  implementation( &cbuf, ra_, false, nullptr );
 }
 
 uint MachSpillCopyNode::size(PhaseRegAlloc *ra_) const {
   return MachNode::size(ra_);
 }
@@ -1723,11 +1720,11 @@
     Register Resi = as_Register(ESI_enc); // sub class
     Label miss;
 
     MacroAssembler _masm(&cbuf);
     __ check_klass_subtype_slow_path(Resi, Reax, Recx, Redi,
-                                     NULL, &miss,
+                                     nullptr, &miss,
                                      /*set_cond_codes:*/ true);
     if ($primary) {
       __ xorptr(Redi, Redi);
     }
     __ bind(miss);
@@ -1841,11 +1838,11 @@
         // a stub to the interpreter.
         cbuf.shared_stub_to_interp_for(_method, cbuf.insts()->mark_off());
       } else {
         // Emit stubs for static call.
         address stub = CompiledStaticCall::emit_to_interp_stub(cbuf, mark);
-        if (stub == NULL) {
+        if (stub == nullptr) {
           ciEnv::current()->record_failure("CodeCache is full");
           return;
         }
       }
     }
@@ -3394,11 +3391,11 @@
   op_cost(10);
   format %{ %}
   interface(CONST_INTER);
 %}
 
-// NULL Pointer Immediate
+// nullptr Pointer Immediate
 operand immP0() %{
   predicate( n->get_ptr() == 0 );
   match(ConP);
   op_cost(0);
 
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
@@ -488,16 +488,21 @@
   offset += clear_avx_size();
   return offset;
 }
 
 int MachCallRuntimeNode::ret_addr_offset() {
+  if (_entry_point == nullptr) {
+    // CallLeafNoFPInDirect
+    return 3; // callq (register)
+  }
   int offset = 13; // movq r10,#addr; callq (r10)
   if (this->ideal_Opcode() != Op_CallLeafVector) {
     offset += clear_avx_size();
   }
   return offset;
 }
+
 //
 // Compute padding required for nodes which need alignment
 //
 
 // The address of the call instruction needs to be 4-byte aligned to
@@ -704,11 +709,11 @@
 #ifdef ASSERT
     st->print("\n\t");
     st->print("# stack alignment check");
 #endif
   }
-  if (C->stub_function() != NULL && BarrierSet::barrier_set()->barrier_set_nmethod() != NULL) {
+  if (C->stub_function() != nullptr && BarrierSet::barrier_set()->barrier_set_nmethod() != nullptr) {
     st->print("\n\t");
     st->print("cmpl    [r15_thread + #disarmed_guard_value_offset], #disarmed_guard_value\t");
     st->print("\n\t");
     st->print("je      fast_entry\t");
     st->print("\n\t");
@@ -721,29 +726,19 @@
 
 void MachPrologNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
   Compile* C = ra_->C;
   C2_MacroAssembler _masm(&cbuf);
 
-  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");
-
-    Label L_skip_barrier;
-    Register klass = rscratch1;
-
-    __ mov_metadata(klass, C->method()->holder()->constant_encoding());
-    __ clinit_barrier(klass, r15_thread, &L_skip_barrier /*L_fast_path*/);
-
-    __ jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); // slow path
+  __ verified_entry(C);
 
-    __ bind(L_skip_barrier);
+  if (ra_->C->stub_function() == nullptr) {
+    __ entry_barrier();
   }
 
-  __ verified_entry(framesize, C->output()->need_stack_bang(bangsize)?bangsize:0, false, C->stub_function() != NULL);
+  if (!Compile::current()->output()->in_scratch_emit_size()) {
+    __ bind(*_verified_entry);
+  }
 
   C->output()->set_frame_complete(cbuf.insts_size());
 
   if (C->has_mach_constant_base_node()) {
     // NOTE: We set the table base offset here because users might be
@@ -751,16 +746,10 @@
     ConstantTable& constant_table = C->output()->constant_table();
     constant_table.set_table_base_offset(constant_table.calculate_table_base_offset());
   }
 }
 
-uint MachPrologNode::size(PhaseRegAlloc* ra_) const
-{
-  return MachNode::size(ra_); // too many variables; just compute it
-                              // the hard way
-}
-
 int MachPrologNode::reloc() const
 {
   return 0; // a large enough number
 }
 
@@ -804,23 +793,13 @@
     // Clear upper bits of YMM registers when current compiled code uses
     // wide vectors to avoid AVX <-> SSE transition penalty during call.
     __ vzeroupper();
   }
 
-  int framesize = C->output()->frame_size_in_bytes();
-  assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned");
-  // Remove word for return adr already pushed
-  // and RBP
-  framesize -= 2*wordSize;
-
-  // Note that VerifyStackAtCalls' Majik cookie does not change the frame size popped here
-
-  if (framesize) {
-    __ addq(rsp, framesize);
-  }
-
-  __ popq(rbp);
+  // Subtract two words to account for return address and rbp
+  int initial_framesize = C->output()->frame_size_in_bytes() - 2*wordSize;
+  __ remove_frame(initial_framesize, C->needs_stack_repair());
 
   if (StackReservedPages > 0 && C->has_reserved_stack_access()) {
     __ reserved_stack_check();
   }
 
@@ -836,16 +815,10 @@
     __ relocate(relocInfo::poll_return_type);
     __ safepoint_poll(*code_stub, r15_thread, true /* at_return */, true /* in_nmethod */);
   }
 }
 
-uint MachEpilogNode::size(PhaseRegAlloc* ra_) const
-{
-  return MachNode::size(ra_); // too many variables; just compute it
-                              // the hard way
-}
-
 int MachEpilogNode::reloc() const
 {
   return 2; // a large enough number
 }
 
@@ -968,11 +941,11 @@
 
 uint MachSpillCopyNode::implementation(CodeBuffer* cbuf,
                                        PhaseRegAlloc* ra_,
                                        bool do_size,
                                        outputStream* st) const {
-  assert(cbuf != NULL || st  != NULL, "sanity");
+  assert(cbuf != nullptr || st  != nullptr, "sanity");
   // Get registers to move
   OptoReg::Name src_second = ra_->get_reg_second(in(1));
   OptoReg::Name src_first = ra_->get_reg_first(in(1));
   OptoReg::Name dst_second = ra_->get_reg_second(this);
   OptoReg::Name dst_first = ra_->get_reg_first(this);
@@ -987,11 +960,11 @@
 
   if (src_first == dst_first && src_second == dst_second) {
     // Self copy, no move
     return 0;
   }
-  if (bottom_type()->isa_vect() != NULL && bottom_type()->isa_vectmask() == NULL) {
+  if (bottom_type()->isa_vect() != nullptr && bottom_type()->isa_vectmask() == nullptr) {
     uint ireg = ideal_reg();
     assert((src_first_rc != rc_int && dst_first_rc != rc_int), "sanity");
     assert((ireg == Op_VecS || ireg == Op_VecD || ireg == Op_VecX || ireg == Op_VecY || ireg == Op_VecZ ), "sanity");
     if( src_first_rc == rc_stack && dst_first_rc == rc_stack ) {
       // mem -> mem
@@ -1426,16 +1399,16 @@
   return 0;
 }
 
 #ifndef PRODUCT
 void MachSpillCopyNode::format(PhaseRegAlloc *ra_, outputStream* st) const {
-  implementation(NULL, ra_, false, st);
+  implementation(nullptr, ra_, false, st);
 }
 #endif
 
 void MachSpillCopyNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
-  implementation(&cbuf, ra_, false, NULL);
+  implementation(&cbuf, ra_, false, nullptr);
 }
 
 uint MachSpillCopyNode::size(PhaseRegAlloc *ra_) const {
   return MachNode::size(ra_);
 }
@@ -1464,10 +1437,59 @@
 {
   int offset = ra_->reg2offset(in_RegMask(0).find_first_elem());
   return (offset < 0x80) ? 5 : 8; // REX
 }
 
+//=============================================================================
+#ifndef PRODUCT
+void MachVEPNode::format(PhaseRegAlloc* ra_, outputStream* st) const
+{
+  st->print_cr("MachVEPNode");
+}
+#endif
+
+void MachVEPNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const
+{
+  C2_MacroAssembler _masm(&cbuf);
+  uint insts_size = cbuf.insts_size();
+  if (!_verified) {
+    if (UseCompressedClassPointers) {
+      __ load_klass(rscratch1, j_rarg0, rscratch2);
+      __ cmpptr(rax, rscratch1);
+    } else {
+      __ cmpptr(rax, Address(j_rarg0, oopDesc::klass_offset_in_bytes()));
+    }
+    __ jump_cc(Assembler::notEqual, RuntimeAddress(SharedRuntime::get_ic_miss_stub()));
+  } else {
+    // TODO 8284443 Avoid creation of temporary frame
+    if (ra_->C->stub_function() == nullptr) {
+      __ verified_entry(ra_->C, 0);
+      __ entry_barrier();
+      int initial_framesize = ra_->C->output()->frame_size_in_bytes() - 2*wordSize;
+      __ remove_frame(initial_framesize, false);
+    }
+    // Unpack inline type args passed as oop and then jump to
+    // the verified entry point (skipping the unverified entry).
+    int sp_inc = __ unpack_inline_args(ra_->C, _receiver_only);
+    // Emit code for verified entry and save increment for stack repair on return
+    __ verified_entry(ra_->C, sp_inc);
+    if (Compile::current()->output()->in_scratch_emit_size()) {
+      Label dummy_verified_entry;
+      __ jmp(dummy_verified_entry);
+    } else {
+      __ jmp(*_verified_entry);
+    }
+  }
+  /* WARNING these NOPs are critical so that verified entry point is properly
+     4 bytes aligned for patching by NativeJump::patch_verified_entry() */
+  int nops_cnt = 4 - ((cbuf.insts_size() - insts_size) & 0x3);
+  nops_cnt &= 0x3; // Do not add nops if code is aligned.
+  if (nops_cnt > 0) {
+    __ nop(nops_cnt);
+  }
+}
+
 //=============================================================================
 #ifndef PRODUCT
 void MachUEPNode::format(PhaseRegAlloc* ra_, outputStream* st) const
 {
   if (UseCompressedClassPointers) {
@@ -1506,17 +1528,10 @@
   nops_cnt &= 0x3; // Do not add nops if code is aligned.
   if (nops_cnt > 0)
     masm.nop(nops_cnt);
 }
 
-uint MachUEPNode::size(PhaseRegAlloc* ra_) const
-{
-  return MachNode::size(ra_); // too many variables; just compute it
-                              // the hard way
-}
-
-
 //=============================================================================
 
 bool Matcher::supports_vector_calling_convention(void) {
   if (EnableVectorSupport && UseVectorStubs) {
     return true;
@@ -1782,11 +1797,11 @@
     Label miss;
     const bool set_cond_codes = true;
 
     MacroAssembler _masm(&cbuf);
     __ check_klass_subtype_slow_path(Rrsi, Rrax, Rrcx, Rrdi,
-                                     NULL, &miss,
+                                     nullptr, &miss,
                                      /*set_cond_codes:*/ true);
     if ($primary) {
       __ xorptr(Rrdi, Rrdi);
     }
     __ bind(miss);
@@ -1839,11 +1854,11 @@
         // a stub to the interpreter.
         cbuf.shared_stub_to_interp_for(_method, call_offset);
       } else {
         // Emit stubs for static call.
         address stub = CompiledStaticCall::emit_to_interp_stub(cbuf, mark);
-        if (stub == NULL) {
+        if (stub == nullptr) {
           ciEnv::current()->record_failure("CodeCache is full");
           return;
         }
       }
     }
@@ -2177,11 +2192,11 @@
   op_cost(10);
   format %{ %}
   interface(CONST_INTER);
 %}
 
-// NULL Pointer Immediate
+// nullptr Pointer Immediate
 operand immP0()
 %{
   predicate(n->get_ptr() == 0);
   match(ConP);
 
@@ -2205,11 +2220,11 @@
   op_cost(10);
   format %{ %}
   interface(CONST_INTER);
 %}
 
-// NULL Pointer Immediate
+// nullptr Pointer Immediate
 operand immN0() %{
   predicate(n->get_narrowcon() == 0);
   match(ConN);
 
   op_cost(5);
@@ -3117,13 +3132,29 @@
     scale($scale);
     disp($off);
   %}
 %}
 
+// Indirect Narrow Oop Operand
+operand indCompressedOop(rRegN reg) %{
+  predicate(UseCompressedOops && (CompressedOops::shift() == Address::times_8));
+  constraint(ALLOC_IN_RC(ptr_reg));
+  match(DecodeN reg);
+
+  op_cost(10);
+  format %{"[R12 + $reg << 3] (compressed oop addressing)" %}
+  interface(MEMORY_INTER) %{
+    base(0xc); // R12
+    index($reg);
+    scale(0x3);
+    disp(0x0);
+  %}
+%}
+
 // Indirect Narrow Oop Plus Offset Operand
 // Note: x86 architecture doesn't support "scale * index + offset" without a base
-// we can't free r12 even with CompressedOops::base() == NULL.
+// we can't free r12 even with CompressedOops::base() == nullptr.
 operand indCompressedOopOffset(rRegN reg, immL32 off) %{
   predicate(UseCompressedOops && (CompressedOops::shift() == Address::times_8));
   constraint(ALLOC_IN_RC(ptr_reg));
   match(AddP (DecodeN reg) off);
 
@@ -3463,11 +3494,11 @@
 // multiple operand types with the same basic encoding and format.  The classic
 // case of this is memory operands.
 
 opclass memory(indirect, indOffset8, indOffset32, indIndexOffset, indIndex,
                indIndexScale, indPosIndexScale, indIndexScaleOffset, indPosIndexOffset, indPosIndexScaleOffset,
-               indCompressedOopOffset,
+               indCompressedOop, indCompressedOopOffset,
                indirectNarrow, indOffset8Narrow, indOffset32Narrow,
                indIndexOffsetNarrow, indIndexNarrow, indIndexScaleNarrow,
                indIndexScaleOffsetNarrow, indPosIndexOffsetNarrow, indPosIndexScaleOffsetNarrow);
 
 //----------PIPELINE-----------------------------------------------------------
@@ -4900,11 +4931,11 @@
 %}
 
 instruct loadConN0(rRegN dst, immN0 src, rFlagsReg cr) %{
   match(Set dst src);
   effect(KILL cr);
-  format %{ "xorq    $dst, $src\t# compressed NULL ptr" %}
+  format %{ "xorq    $dst, $src\t# compressed nullptr ptr" %}
   ins_encode %{
     __ xorq($dst$$Register, $dst$$Register);
   %}
   ins_pipe(ialu_reg);
 %}
@@ -4914,11 +4945,11 @@
 
   ins_cost(125);
   format %{ "movl    $dst, $src\t# compressed ptr" %}
   ins_encode %{
     address con = (address)$src$$constant;
-    if (con == NULL) {
+    if (con == nullptr) {
       ShouldNotReachHere();
     } else {
       __ set_narrow_oop($dst$$Register, (jobject)$src$$constant);
     }
   %}
@@ -4930,11 +4961,11 @@
 
   ins_cost(125);
   format %{ "movl    $dst, $src\t# compressed klass ptr" %}
   ins_encode %{
     address con = (address)$src$$constant;
-    if (con == NULL) {
+    if (con == nullptr) {
       ShouldNotReachHere();
     } else {
       __ set_narrow_klass($dst$$Register, (Klass*)$src$$constant);
     }
   %}
@@ -5156,22 +5187,22 @@
   ins_pipe(ialu_mem_reg);
 %}
 
 instruct storeImmP0(memory mem, immP0 zero)
 %{
-  predicate(UseCompressedOops && (CompressedOops::base() == NULL) && n->as_Store()->barrier_data() == 0);
+  predicate(UseCompressedOops && (CompressedOops::base() == nullptr) && n->as_Store()->barrier_data() == 0);
   match(Set mem (StoreP mem zero));
 
   ins_cost(125); // XXX
   format %{ "movq    $mem, R12\t# ptr (R12_heapbase==0)" %}
   ins_encode %{
     __ movq($mem$$Address, r12);
   %}
   ins_pipe(ialu_mem_reg);
 %}
 
-// Store NULL Pointer, mark word, or other simple pointer constant.
+// Store nullptr Pointer, mark word, or other simple pointer constant.
 instruct storeImmP(memory mem, immP31 src)
 %{
   predicate(n->as_Store()->barrier_data() == 0);
   match(Set mem (StoreP mem src));
 
@@ -5208,11 +5239,11 @@
   ins_pipe(ialu_mem_reg);
 %}
 
 instruct storeImmN0(memory mem, immN0 zero)
 %{
-  predicate(CompressedOops::base() == NULL);
+  predicate(CompressedOops::base() == nullptr);
   match(Set mem (StoreN mem zero));
 
   ins_cost(125); // XXX
   format %{ "movl    $mem, R12\t# compressed ptr (R12_heapbase==0)" %}
   ins_encode %{
@@ -5227,11 +5258,11 @@
 
   ins_cost(150); // XXX
   format %{ "movl    $mem, $src\t# compressed ptr" %}
   ins_encode %{
     address con = (address)$src$$constant;
-    if (con == NULL) {
+    if (con == nullptr) {
       __ movl($mem$$Address, 0);
     } else {
       __ set_narrow_oop($mem$$Address, (jobject)$src$$constant);
     }
   %}
@@ -5251,11 +5282,11 @@
 %}
 
 // Store Integer Immediate
 instruct storeImmI0(memory mem, immI_0 zero)
 %{
-  predicate(UseCompressedOops && (CompressedOops::base() == NULL));
+  predicate(UseCompressedOops && (CompressedOops::base() == nullptr));
   match(Set mem (StoreI mem zero));
 
   ins_cost(125); // XXX
   format %{ "movl    $mem, R12\t# int (R12_heapbase==0)" %}
   ins_encode %{
@@ -5277,11 +5308,11 @@
 %}
 
 // Store Long Immediate
 instruct storeImmL0(memory mem, immL0 zero)
 %{
-  predicate(UseCompressedOops && (CompressedOops::base() == NULL));
+  predicate(UseCompressedOops && (CompressedOops::base() == nullptr));
   match(Set mem (StoreL mem zero));
 
   ins_cost(125); // XXX
   format %{ "movq    $mem, R12\t# long (R12_heapbase==0)" %}
   ins_encode %{
@@ -5303,11 +5334,11 @@
 %}
 
 // Store Short/Char Immediate
 instruct storeImmC0(memory mem, immI_0 zero)
 %{
-  predicate(UseCompressedOops && (CompressedOops::base() == NULL));
+  predicate(UseCompressedOops && (CompressedOops::base() == nullptr));
   match(Set mem (StoreC mem zero));
 
   ins_cost(125); // XXX
   format %{ "movw    $mem, R12\t# short/char (R12_heapbase==0)" %}
   ins_encode %{
@@ -5330,11 +5361,11 @@
 %}
 
 // Store Byte Immediate
 instruct storeImmB0(memory mem, immI_0 zero)
 %{
-  predicate(UseCompressedOops && (CompressedOops::base() == NULL));
+  predicate(UseCompressedOops && (CompressedOops::base() == nullptr));
   match(Set mem (StoreB mem zero));
 
   ins_cost(125); // XXX
   format %{ "movb    $mem, R12\t# short/char (R12_heapbase==0)" %}
   ins_encode %{
@@ -5356,11 +5387,11 @@
 %}
 
 // Store CMS card-mark Immediate
 instruct storeImmCM0_reg(memory mem, immI_0 zero)
 %{
-  predicate(UseCompressedOops && (CompressedOops::base() == NULL));
+  predicate(UseCompressedOops && (CompressedOops::base() == nullptr));
   match(Set mem (StoreCM mem zero));
 
   ins_cost(125); // XXX
   format %{ "movb    $mem, R12\t# CMS card-mark byte 0 (R12_heapbase==0)" %}
   ins_encode %{
@@ -5395,11 +5426,11 @@
 %}
 
 // Store immediate Float value (it is faster than store from XMM register)
 instruct storeF0(memory mem, immF0 zero)
 %{
-  predicate(UseCompressedOops && (CompressedOops::base() == NULL));
+  predicate(UseCompressedOops && (CompressedOops::base() == nullptr));
   match(Set mem (StoreF mem zero));
 
   ins_cost(25); // XXX
   format %{ "movl    $mem, R12\t# float 0. (R12_heapbase==0)" %}
   ins_encode %{
@@ -5434,11 +5465,11 @@
 %}
 
 // Store immediate double 0.0 (it is faster than store from XMM register)
 instruct storeD0_imm(memory mem, immD0 src)
 %{
-  predicate(!UseCompressedOops || (CompressedOops::base() != NULL));
+  predicate(!UseCompressedOops || (CompressedOops::base() != nullptr));
   match(Set mem (StoreD mem src));
 
   ins_cost(50);
   format %{ "movq    $mem, $src\t# double 0." %}
   ins_encode %{
@@ -5447,11 +5478,11 @@
   ins_pipe(ialu_mem_imm);
 %}
 
 instruct storeD0(memory mem, immD0 zero)
 %{
-  predicate(UseCompressedOops && (CompressedOops::base() == NULL));
+  predicate(UseCompressedOops && (CompressedOops::base() == nullptr));
   match(Set mem (StoreD mem zero));
 
   ins_cost(25); // XXX
   format %{ "movq    $mem, R12\t# double 0. (R12_heapbase==0)" %}
   ins_encode %{
@@ -5994,10 +6025,23 @@
     }
   %}
   ins_pipe(ialu_reg_reg); // XXX
 %}
 
+instruct castN2X(rRegL dst, rRegN src)
+%{
+  match(Set dst (CastP2X src));
+
+  format %{ "movq    $dst, $src\t# ptr -> long" %}
+  ins_encode %{
+    if ($dst$$reg != $src$$reg) {
+      __ movptr($dst$$Register, $src$$Register);
+    }
+  %}
+  ins_pipe(ialu_reg_reg); // XXX
+%}
+
 instruct castP2X(rRegL dst, rRegP src)
 %{
   match(Set dst (CastP2X src));
 
   format %{ "movq    $dst, $src\t# ptr -> long" %}
@@ -10535,18 +10579,136 @@
      __ movdq($dst$$XMMRegister, $src$$Register);
   %}
   ins_pipe( pipe_slow );
 %}
 
+
 // Fast clearing of an array
 // Small ClearArray non-AVX512.
-instruct rep_stos(rcx_RegL cnt, rdi_RegP base, regD tmp, rax_RegI zero,
+instruct rep_stos(rcx_RegL cnt, rdi_RegP base, regD tmp, rax_RegL val,
                   Universe dummy, rFlagsReg cr)
 %{
-  predicate(!((ClearArrayNode*)n)->is_large() && (UseAVX <= 2));
-  match(Set dummy (ClearArray cnt base));
-  effect(USE_KILL cnt, USE_KILL base, TEMP tmp, KILL zero, KILL cr);
+  predicate(!((ClearArrayNode*)n)->is_large() && !((ClearArrayNode*)n)->word_copy_only() && (UseAVX <= 2));
+  match(Set dummy (ClearArray (Binary cnt base) val));
+  effect(USE_KILL cnt, USE_KILL base, TEMP tmp, USE_KILL val, KILL cr);
+
+  format %{ $$template
+    $$emit$$"cmp     InitArrayShortSize,rcx\n\t"
+    $$emit$$"jg      LARGE\n\t"
+    $$emit$$"dec     rcx\n\t"
+    $$emit$$"js      DONE\t# Zero length\n\t"
+    $$emit$$"mov     rax,(rdi,rcx,8)\t# LOOP\n\t"
+    $$emit$$"dec     rcx\n\t"
+    $$emit$$"jge     LOOP\n\t"
+    $$emit$$"jmp     DONE\n\t"
+    $$emit$$"# LARGE:\n\t"
+    if (UseFastStosb) {
+       $$emit$$"shlq    rcx,3\t# Convert doublewords to bytes\n\t"
+       $$emit$$"rep     stosb\t# Store rax to *rdi++ while rcx--\n\t"
+    } else if (UseXMMForObjInit) {
+       $$emit$$"movdq   $tmp, $val\n\t"
+       $$emit$$"punpcklqdq $tmp, $tmp\n\t"
+       $$emit$$"vinserti128_high $tmp, $tmp\n\t"
+       $$emit$$"jmpq    L_zero_64_bytes\n\t"
+       $$emit$$"# L_loop:\t# 64-byte LOOP\n\t"
+       $$emit$$"vmovdqu $tmp,(rax)\n\t"
+       $$emit$$"vmovdqu $tmp,0x20(rax)\n\t"
+       $$emit$$"add     0x40,rax\n\t"
+       $$emit$$"# L_zero_64_bytes:\n\t"
+       $$emit$$"sub     0x8,rcx\n\t"
+       $$emit$$"jge     L_loop\n\t"
+       $$emit$$"add     0x4,rcx\n\t"
+       $$emit$$"jl      L_tail\n\t"
+       $$emit$$"vmovdqu $tmp,(rax)\n\t"
+       $$emit$$"add     0x20,rax\n\t"
+       $$emit$$"sub     0x4,rcx\n\t"
+       $$emit$$"# L_tail:\t# Clearing tail bytes\n\t"
+       $$emit$$"add     0x4,rcx\n\t"
+       $$emit$$"jle     L_end\n\t"
+       $$emit$$"dec     rcx\n\t"
+       $$emit$$"# L_sloop:\t# 8-byte short loop\n\t"
+       $$emit$$"vmovq   xmm0,(rax)\n\t"
+       $$emit$$"add     0x8,rax\n\t"
+       $$emit$$"dec     rcx\n\t"
+       $$emit$$"jge     L_sloop\n\t"
+       $$emit$$"# L_end:\n\t"
+    } else {
+       $$emit$$"rep     stosq\t# Store rax to *rdi++ while rcx--\n\t"
+    }
+    $$emit$$"# DONE"
+  %}
+  ins_encode %{
+    __ clear_mem($base$$Register, $cnt$$Register, $val$$Register,
+                 $tmp$$XMMRegister, false, false);
+  %}
+  ins_pipe(pipe_slow);
+%}
+
+instruct rep_stos_word_copy(rcx_RegL cnt, rdi_RegP base, regD tmp, rax_RegL val,
+                            Universe dummy, rFlagsReg cr)
+%{
+  predicate(!((ClearArrayNode*)n)->is_large() && ((ClearArrayNode*)n)->word_copy_only() && (UseAVX <= 2));
+  match(Set dummy (ClearArray (Binary cnt base) val));
+  effect(USE_KILL cnt, USE_KILL base, TEMP tmp, USE_KILL val, KILL cr);
+
+  format %{ $$template
+    $$emit$$"cmp     InitArrayShortSize,rcx\n\t"
+    $$emit$$"jg      LARGE\n\t"
+    $$emit$$"dec     rcx\n\t"
+    $$emit$$"js      DONE\t# Zero length\n\t"
+    $$emit$$"mov     rax,(rdi,rcx,8)\t# LOOP\n\t"
+    $$emit$$"dec     rcx\n\t"
+    $$emit$$"jge     LOOP\n\t"
+    $$emit$$"jmp     DONE\n\t"
+    $$emit$$"# LARGE:\n\t"
+    if (UseXMMForObjInit) {
+       $$emit$$"movdq   $tmp, $val\n\t"
+       $$emit$$"punpcklqdq $tmp, $tmp\n\t"
+       $$emit$$"vinserti128_high $tmp, $tmp\n\t"
+       $$emit$$"jmpq    L_zero_64_bytes\n\t"
+       $$emit$$"# L_loop:\t# 64-byte LOOP\n\t"
+       $$emit$$"vmovdqu $tmp,(rax)\n\t"
+       $$emit$$"vmovdqu $tmp,0x20(rax)\n\t"
+       $$emit$$"add     0x40,rax\n\t"
+       $$emit$$"# L_zero_64_bytes:\n\t"
+       $$emit$$"sub     0x8,rcx\n\t"
+       $$emit$$"jge     L_loop\n\t"
+       $$emit$$"add     0x4,rcx\n\t"
+       $$emit$$"jl      L_tail\n\t"
+       $$emit$$"vmovdqu $tmp,(rax)\n\t"
+       $$emit$$"add     0x20,rax\n\t"
+       $$emit$$"sub     0x4,rcx\n\t"
+       $$emit$$"# L_tail:\t# Clearing tail bytes\n\t"
+       $$emit$$"add     0x4,rcx\n\t"
+       $$emit$$"jle     L_end\n\t"
+       $$emit$$"dec     rcx\n\t"
+       $$emit$$"# L_sloop:\t# 8-byte short loop\n\t"
+       $$emit$$"vmovq   xmm0,(rax)\n\t"
+       $$emit$$"add     0x8,rax\n\t"
+       $$emit$$"dec     rcx\n\t"
+       $$emit$$"jge     L_sloop\n\t"
+       $$emit$$"# L_end:\n\t"
+    } else {
+       $$emit$$"rep     stosq\t# Store rax to *rdi++ while rcx--\n\t"
+    }
+    $$emit$$"# DONE"
+  %}
+  ins_encode %{
+    __ clear_mem($base$$Register, $cnt$$Register, $val$$Register,
+                 $tmp$$XMMRegister, false, true);
+  %}
+  ins_pipe(pipe_slow);
+%}
+
+// Small ClearArray AVX512 non-constant length.
+instruct rep_stos_evex(rcx_RegL cnt, rdi_RegP base, legRegD tmp, kReg ktmp, rax_RegL val,
+                       Universe dummy, rFlagsReg cr)
+%{
+  predicate(!((ClearArrayNode*)n)->is_large() && !((ClearArrayNode*)n)->word_copy_only() && (UseAVX > 2));
+  match(Set dummy (ClearArray (Binary cnt base) val));
+  ins_cost(125);
+  effect(USE_KILL cnt, USE_KILL base, TEMP tmp, TEMP ktmp, USE_KILL val, KILL cr);
 
   format %{ $$template
     $$emit$$"xorq    rax, rax\t# ClearArray:\n\t"
     $$emit$$"cmp     InitArrayShortSize,rcx\n\t"
     $$emit$$"jg      LARGE\n\t"
@@ -10590,24 +10752,23 @@
        $$emit$$"rep     stosq\t# Store rax to *rdi++ while rcx--\n\t"
     }
     $$emit$$"# DONE"
   %}
   ins_encode %{
-    __ clear_mem($base$$Register, $cnt$$Register, $zero$$Register,
-                 $tmp$$XMMRegister, false, knoreg);
+    __ clear_mem($base$$Register, $cnt$$Register, $val$$Register,
+                 $tmp$$XMMRegister, false, false, $ktmp$$KRegister);
   %}
   ins_pipe(pipe_slow);
 %}
 
-// Small ClearArray AVX512 non-constant length.
-instruct rep_stos_evex(rcx_RegL cnt, rdi_RegP base, legRegD tmp, kReg ktmp, rax_RegI zero,
-                       Universe dummy, rFlagsReg cr)
+instruct rep_stos_evex_word_copy(rcx_RegL cnt, rdi_RegP base, legRegD tmp, kReg ktmp, rax_RegL val,
+                                 Universe dummy, rFlagsReg cr)
 %{
-  predicate(!((ClearArrayNode*)n)->is_large() && (UseAVX > 2));
-  match(Set dummy (ClearArray cnt base));
+  predicate(!((ClearArrayNode*)n)->is_large() && ((ClearArrayNode*)n)->word_copy_only() && (UseAVX > 2));
+  match(Set dummy (ClearArray (Binary cnt base) val));
   ins_cost(125);
-  effect(USE_KILL cnt, USE_KILL base, TEMP tmp, TEMP ktmp, KILL zero, KILL cr);
+  effect(USE_KILL cnt, USE_KILL base, TEMP tmp, TEMP ktmp, USE_KILL val, KILL cr);
 
   format %{ $$template
     $$emit$$"xorq    rax, rax\t# ClearArray:\n\t"
     $$emit$$"cmp     InitArrayShortSize,rcx\n\t"
     $$emit$$"jg      LARGE\n\t"
@@ -10651,23 +10812,119 @@
        $$emit$$"rep     stosq\t# Store rax to *rdi++ while rcx--\n\t"
     }
     $$emit$$"# DONE"
   %}
   ins_encode %{
-    __ clear_mem($base$$Register, $cnt$$Register, $zero$$Register,
-                 $tmp$$XMMRegister, false, $ktmp$$KRegister);
+    __ clear_mem($base$$Register, $cnt$$Register, $val$$Register,
+                 $tmp$$XMMRegister, false, true, $ktmp$$KRegister);
   %}
   ins_pipe(pipe_slow);
 %}
 
 // Large ClearArray non-AVX512.
-instruct rep_stos_large(rcx_RegL cnt, rdi_RegP base, regD tmp, rax_RegI zero,
+instruct rep_stos_large(rcx_RegL cnt, rdi_RegP base, regD tmp, rax_RegL val,
                         Universe dummy, rFlagsReg cr)
 %{
-  predicate((UseAVX <=2) && ((ClearArrayNode*)n)->is_large());
-  match(Set dummy (ClearArray cnt base));
-  effect(USE_KILL cnt, USE_KILL base, TEMP tmp, KILL zero, KILL cr);
+  predicate(((ClearArrayNode*)n)->is_large() && !((ClearArrayNode*)n)->word_copy_only() && (UseAVX <= 2));
+  match(Set dummy (ClearArray (Binary cnt base) val));
+  effect(USE_KILL cnt, USE_KILL base, TEMP tmp, USE_KILL val, KILL cr);
+
+  format %{ $$template
+    if (UseFastStosb) {
+       $$emit$$"shlq    rcx,3\t# Convert doublewords to bytes\n\t"
+       $$emit$$"rep     stosb\t# Store rax to *rdi++ while rcx--"
+    } else if (UseXMMForObjInit) {
+       $$emit$$"movdq   $tmp, $val\n\t"
+       $$emit$$"punpcklqdq $tmp, $tmp\n\t"
+       $$emit$$"vinserti128_high $tmp, $tmp\n\t"
+       $$emit$$"jmpq    L_zero_64_bytes\n\t"
+       $$emit$$"# L_loop:\t# 64-byte LOOP\n\t"
+       $$emit$$"vmovdqu $tmp,(rax)\n\t"
+       $$emit$$"vmovdqu $tmp,0x20(rax)\n\t"
+       $$emit$$"add     0x40,rax\n\t"
+       $$emit$$"# L_zero_64_bytes:\n\t"
+       $$emit$$"sub     0x8,rcx\n\t"
+       $$emit$$"jge     L_loop\n\t"
+       $$emit$$"add     0x4,rcx\n\t"
+       $$emit$$"jl      L_tail\n\t"
+       $$emit$$"vmovdqu $tmp,(rax)\n\t"
+       $$emit$$"add     0x20,rax\n\t"
+       $$emit$$"sub     0x4,rcx\n\t"
+       $$emit$$"# L_tail:\t# Clearing tail bytes\n\t"
+       $$emit$$"add     0x4,rcx\n\t"
+       $$emit$$"jle     L_end\n\t"
+       $$emit$$"dec     rcx\n\t"
+       $$emit$$"# L_sloop:\t# 8-byte short loop\n\t"
+       $$emit$$"vmovq   xmm0,(rax)\n\t"
+       $$emit$$"add     0x8,rax\n\t"
+       $$emit$$"dec     rcx\n\t"
+       $$emit$$"jge     L_sloop\n\t"
+       $$emit$$"# L_end:\n\t"
+    } else {
+       $$emit$$"rep     stosq\t# Store rax to *rdi++ while rcx--"
+    }
+  %}
+  ins_encode %{
+    __ clear_mem($base$$Register, $cnt$$Register, $val$$Register,
+                 $tmp$$XMMRegister, true, false);
+  %}
+  ins_pipe(pipe_slow);
+%}
+
+instruct rep_stos_large_word_copy(rcx_RegL cnt, rdi_RegP base, regD tmp, rax_RegL val,
+                                  Universe dummy, rFlagsReg cr)
+%{
+  predicate(((ClearArrayNode*)n)->is_large() && ((ClearArrayNode*)n)->word_copy_only() && (UseAVX <= 2));
+  match(Set dummy (ClearArray (Binary cnt base) val));
+  effect(USE_KILL cnt, USE_KILL base, TEMP tmp, USE_KILL val, KILL cr);
+
+  format %{ $$template
+    if (UseXMMForObjInit) {
+       $$emit$$"movdq   $tmp, $val\n\t"
+       $$emit$$"punpcklqdq $tmp, $tmp\n\t"
+       $$emit$$"vinserti128_high $tmp, $tmp\n\t"
+       $$emit$$"jmpq    L_zero_64_bytes\n\t"
+       $$emit$$"# L_loop:\t# 64-byte LOOP\n\t"
+       $$emit$$"vmovdqu $tmp,(rax)\n\t"
+       $$emit$$"vmovdqu $tmp,0x20(rax)\n\t"
+       $$emit$$"add     0x40,rax\n\t"
+       $$emit$$"# L_zero_64_bytes:\n\t"
+       $$emit$$"sub     0x8,rcx\n\t"
+       $$emit$$"jge     L_loop\n\t"
+       $$emit$$"add     0x4,rcx\n\t"
+       $$emit$$"jl      L_tail\n\t"
+       $$emit$$"vmovdqu $tmp,(rax)\n\t"
+       $$emit$$"add     0x20,rax\n\t"
+       $$emit$$"sub     0x4,rcx\n\t"
+       $$emit$$"# L_tail:\t# Clearing tail bytes\n\t"
+       $$emit$$"add     0x4,rcx\n\t"
+       $$emit$$"jle     L_end\n\t"
+       $$emit$$"dec     rcx\n\t"
+       $$emit$$"# L_sloop:\t# 8-byte short loop\n\t"
+       $$emit$$"vmovq   xmm0,(rax)\n\t"
+       $$emit$$"add     0x8,rax\n\t"
+       $$emit$$"dec     rcx\n\t"
+       $$emit$$"jge     L_sloop\n\t"
+       $$emit$$"# L_end:\n\t"
+    } else {
+       $$emit$$"rep     stosq\t# Store rax to *rdi++ while rcx--"
+    }
+  %}
+  ins_encode %{
+    __ clear_mem($base$$Register, $cnt$$Register, $val$$Register,
+                 $tmp$$XMMRegister, true, true);
+  %}
+  ins_pipe(pipe_slow);
+%}
+
+// Large ClearArray AVX512.
+instruct rep_stos_large_evex(rcx_RegL cnt, rdi_RegP base, legRegD tmp, kReg ktmp, rax_RegL val,
+                             Universe dummy, rFlagsReg cr)
+%{
+  predicate(((ClearArrayNode*)n)->is_large() && !((ClearArrayNode*)n)->word_copy_only() && (UseAVX > 2));
+  match(Set dummy (ClearArray (Binary cnt base) val));
+  effect(USE_KILL cnt, USE_KILL base, TEMP tmp, TEMP ktmp, USE_KILL val, KILL cr);
 
   format %{ $$template
     if (UseFastStosb) {
        $$emit$$"xorq    rax, rax\t# ClearArray:\n\t"
        $$emit$$"shlq    rcx,3\t# Convert doublewords to bytes\n\t"
@@ -10702,23 +10959,22 @@
        $$emit$$"xorq    rax, rax\t# ClearArray:\n\t"
        $$emit$$"rep     stosq\t# Store rax to *rdi++ while rcx--"
     }
   %}
   ins_encode %{
-    __ clear_mem($base$$Register, $cnt$$Register, $zero$$Register,
-                 $tmp$$XMMRegister, true, knoreg);
+    __ clear_mem($base$$Register, $cnt$$Register, $val$$Register,
+                 $tmp$$XMMRegister, true, false, $ktmp$$KRegister);
   %}
   ins_pipe(pipe_slow);
 %}
 
-// Large ClearArray AVX512.
-instruct rep_stos_large_evex(rcx_RegL cnt, rdi_RegP base, legRegD tmp, kReg ktmp, rax_RegI zero,
-                             Universe dummy, rFlagsReg cr)
+instruct rep_stos_large_evex_word_copy(rcx_RegL cnt, rdi_RegP base, legRegD tmp, kReg ktmp, rax_RegL val,
+                                       Universe dummy, rFlagsReg cr)
 %{
-  predicate((UseAVX > 2) && ((ClearArrayNode*)n)->is_large());
-  match(Set dummy (ClearArray cnt base));
-  effect(USE_KILL cnt, USE_KILL base, TEMP tmp, TEMP ktmp, KILL zero, KILL cr);
+  predicate(((ClearArrayNode*)n)->is_large() && ((ClearArrayNode*)n)->word_copy_only() && (UseAVX > 2));
+  match(Set dummy (ClearArray (Binary cnt base) val));
+  effect(USE_KILL cnt, USE_KILL base, TEMP tmp, TEMP ktmp, USE_KILL val, KILL cr);
 
   format %{ $$template
     if (UseFastStosb) {
        $$emit$$"xorq    rax, rax\t# ClearArray:\n\t"
        $$emit$$"shlq    rcx,3\t# Convert doublewords to bytes\n\t"
@@ -10753,27 +11009,27 @@
        $$emit$$"xorq    rax, rax\t# ClearArray:\n\t"
        $$emit$$"rep     stosq\t# Store rax to *rdi++ while rcx--"
     }
   %}
   ins_encode %{
-    __ clear_mem($base$$Register, $cnt$$Register, $zero$$Register,
-                 $tmp$$XMMRegister, true, $ktmp$$KRegister);
+    __ clear_mem($base$$Register, $cnt$$Register, $val$$Register,
+                 $tmp$$XMMRegister, true, true, $ktmp$$KRegister);
   %}
   ins_pipe(pipe_slow);
 %}
 
 // Small ClearArray AVX512 constant length.
-instruct rep_stos_im(immL cnt, rRegP base, regD tmp, rRegI zero, kReg ktmp, Universe dummy, rFlagsReg cr)
+instruct rep_stos_im(immL cnt, rRegP base, regD tmp, rax_RegL val, kReg ktmp, Universe dummy, rFlagsReg cr)
 %{
-  predicate(!((ClearArrayNode*)n)->is_large() &&
-              ((UseAVX > 2) && VM_Version::supports_avx512vlbw()));
-  match(Set dummy (ClearArray cnt base));
+  predicate(!((ClearArrayNode*)n)->is_large() && !((ClearArrayNode*)n)->word_copy_only() &&
+            ((UseAVX > 2) && VM_Version::supports_avx512vlbw()));
+  match(Set dummy (ClearArray (Binary cnt base) val));
   ins_cost(100);
-  effect(TEMP tmp, TEMP zero, TEMP ktmp, KILL cr);
+  effect(TEMP tmp, USE_KILL val, TEMP ktmp, KILL cr);
   format %{ "clear_mem_imm $base , $cnt  \n\t" %}
   ins_encode %{
-   __ clear_mem($base$$Register, $cnt$$constant, $zero$$Register, $tmp$$XMMRegister, $ktmp$$KRegister);
+    __ clear_mem($base$$Register, $cnt$$constant, $val$$Register, $tmp$$XMMRegister, $ktmp$$KRegister);
   %}
   ins_pipe(pipe_slow);
 %}
 
 instruct string_compareL(rdi_RegP str1, rcx_RegI cnt1, rsi_RegP str2, rdx_RegI cnt2,
@@ -11678,11 +11934,11 @@
 
 // This will generate a signed flags result. This should be OK since
 // any compare to a zero should be eq/neq.
 instruct testP_mem(rFlagsReg cr, memory op, immP0 zero)
 %{
-  predicate((!UseCompressedOops || (CompressedOops::base() != NULL)) &&
+  predicate((!UseCompressedOops || (CompressedOops::base() != nullptr)) &&
             n->in(1)->as_Load()->barrier_data() == 0);
   match(Set cr (CmpP (LoadP op) zero));
 
   ins_cost(500); // XXX
   format %{ "testq   $op, 0xffffffffffffffff\t# ptr" %}
@@ -11692,11 +11948,11 @@
   ins_pipe(ialu_cr_reg_imm);
 %}
 
 instruct testP_mem_reg0(rFlagsReg cr, memory mem, immP0 zero)
 %{
-  predicate(UseCompressedOops && (CompressedOops::base() == NULL) &&
+  predicate(UseCompressedOops && (CompressedOops::base() == nullptr) &&
             n->in(1)->as_Load()->barrier_data() == 0);
   match(Set cr (CmpP (LoadP mem) zero));
 
   format %{ "cmpq    R12, $mem\t# ptr (R12_heapbase==0)" %}
   ins_encode %{
@@ -11775,11 +12031,11 @@
   ins_pipe(ialu_cr_reg_imm);
 %}
 
 instruct testN_mem(rFlagsReg cr, memory mem, immN0 zero)
 %{
-  predicate(CompressedOops::base() != NULL);
+  predicate(CompressedOops::base() != nullptr);
   match(Set cr (CmpN (LoadN mem) zero));
 
   ins_cost(500); // XXX
   format %{ "testl   $mem, 0xffffffff\t# compressed ptr" %}
   ins_encode %{
@@ -11788,11 +12044,11 @@
   ins_pipe(ialu_cr_reg_mem);
 %}
 
 instruct testN_mem_reg0(rFlagsReg cr, memory mem, immN0 zero)
 %{
-  predicate(CompressedOops::base() == NULL);
+  predicate(CompressedOops::base() == nullptr);
   match(Set cr (CmpN (LoadN mem) zero));
 
   format %{ "cmpl    R12, $mem\t# compressed ptr (R12_heapbase==0)" %}
   ins_encode %{
     __ cmpl(r12, $mem$$Address);
@@ -12539,12 +12795,28 @@
   ins_encode(Java_To_Runtime(meth));
   ins_pipe(pipe_slow);
 %}
 
 // Call runtime without safepoint
+// entry point is null, target holds the address to call
+instruct CallLeafNoFPInDirect(rRegP target)
+%{
+  predicate(n->as_Call()->entry_point() == nullptr);
+  match(CallLeafNoFP target);
+
+  ins_cost(300);
+  format %{ "call_leaf_nofp,runtime indirect " %}
+  ins_encode %{
+     __ call($target$$Register);
+  %}
+
+  ins_pipe(pipe_slow);
+%}
+
 instruct CallLeafNoFPDirect(method meth)
 %{
+  predicate(n->as_Call()->entry_point() != nullptr);
   match(CallLeafNoFP);
   effect(USE meth);
 
   ins_cost(300);
   format %{ "call_leaf_nofp,runtime " %}
diff a/src/hotspot/cpu/zero/globals_zero.hpp b/src/hotspot/cpu/zero/globals_zero.hpp
--- a/src/hotspot/cpu/zero/globals_zero.hpp
+++ b/src/hotspot/cpu/zero/globals_zero.hpp
@@ -71,10 +71,13 @@
 
 define_pd_global(uintx, TypeProfileLevel, 0);
 
 define_pd_global(bool, PreserveFramePointer, false);
 
+define_pd_global(bool, InlineTypePassFieldsAsArgs, false);
+define_pd_global(bool, InlineTypeReturnedAsFields, false);
+
 define_pd_global(bool, CompactStrings, true);
 
 #define ARCH_FLAGS(develop,                                                 \
                    product,                                                 \
                    notproduct,                                              \
diff a/src/hotspot/cpu/zero/sharedRuntime_zero.cpp b/src/hotspot/cpu/zero/sharedRuntime_zero.cpp
--- a/src/hotspot/cpu/zero/sharedRuntime_zero.cpp
+++ b/src/hotspot/cpu/zero/sharedRuntime_zero.cpp
@@ -51,20 +51,43 @@
                                            VMRegPair *regs,
                                            int total_args_passed) {
   return 0;
 }
 
-AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(
-                        MacroAssembler *masm,
-                        int total_args_passed,
-                        int comp_args_on_stack,
-                        const BasicType *sig_bt,
-                        const VMRegPair *regs,
-                        AdapterFingerPrint *fingerprint) {
+int SharedRuntime::java_return_convention(const BasicType *sig_bt,
+                                           VMRegPair *regs,
+                                           int total_args_passed) {
+  Unimplemented();
+  return 0;
+}
+
+BufferedInlineTypeBlob* SharedRuntime::generate_buffered_inline_type_adapter(const InlineKlass* vk) {
+  Unimplemented();
+  return NULL;
+}
+
+AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler* masm,
+                                                            int comp_args_on_stack,
+                                                            const GrowableArray <SigEntry>* sig,
+                                                            const VMRegPair* regs,
+                                                            const GrowableArray <SigEntry>* sig_cc,
+                                                            const VMRegPair* regs_cc,
+                                                            const GrowableArray <SigEntry>* sig_cc_ro,
+                                                            const VMRegPair* regs_cc_ro,
+                                                            AdapterFingerPrint* fingerprint,
+                                                            AdapterBlob*& new_adapter,
+                                                            bool allocate_code_blob) {
+  if (allocate_code_blob) {
+    new_adapter = AdapterBlob::create(masm->code(), 0, 0, NULL);
+  }
   return AdapterHandlerLibrary::new_entry(
     fingerprint,
     CAST_FROM_FN_PTR(address,zero_null_code_stub),
+    CAST_FROM_FN_PTR(address,zero_null_code_stub),
+    CAST_FROM_FN_PTR(address,zero_null_code_stub),
+    CAST_FROM_FN_PTR(address,zero_null_code_stub),
+    CAST_FROM_FN_PTR(address,zero_null_code_stub),
     CAST_FROM_FN_PTR(address,zero_null_code_stub),
     CAST_FROM_FN_PTR(address,zero_null_code_stub));
 }
 
 nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm,
diff a/src/hotspot/cpu/zero/vtableStubs_zero.cpp b/src/hotspot/cpu/zero/vtableStubs_zero.cpp
--- a/src/hotspot/cpu/zero/vtableStubs_zero.cpp
+++ b/src/hotspot/cpu/zero/vtableStubs_zero.cpp
@@ -25,16 +25,16 @@
 
 #include "precompiled.hpp"
 #include "code/vtableStubs.hpp"
 #include "utilities/debug.hpp"
 
-VtableStub* VtableStubs::create_vtable_stub(int vtable_index) {
+VtableStub* VtableStubs::create_vtable_stub(int vtable_index, bool caller_is_c1) {
   ShouldNotCallThis();
   return nullptr;
 }
 
-VtableStub* VtableStubs::create_itable_stub(int vtable_index) {
+VtableStub* VtableStubs::create_itable_stub(int vtable_index, bool caller_is_c1) {
   ShouldNotCallThis();
   return nullptr;
 }
 
 int VtableStub::pd_code_alignment() {
diff a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp
--- a/src/hotspot/share/adlc/formssel.cpp
+++ b/src/hotspot/share/adlc/formssel.cpp
@@ -803,11 +803,11 @@
   if (needs_base_oop_edge(globals)) return true;
 
   if (is_vector()) return true;
   if (is_mach_constant()) return true;
 
-  return  false;
+  return false;
 }
 
 
 // Access instr_cost attribute or return null.
 const char* InstructForm::cost() {
@@ -893,11 +893,12 @@
   if( strcmp(_matrule->_opType,"Return"    )==0 ||
       strcmp(_matrule->_opType,"Rethrow"   )==0 ||
       strcmp(_matrule->_opType,"TailCall"  )==0 ||
       strcmp(_matrule->_opType,"TailJump"  )==0 ||
       strcmp(_matrule->_opType,"SafePoint" )==0 ||
-      strcmp(_matrule->_opType,"Halt"      )==0 )
+      strcmp(_matrule->_opType,"Halt"      )==0 ||
+      strcmp(_matrule->_opType,"CallLeafNoFP")==0)
     return AdlcVMDeps::Parms;   // Skip the machine-state edges
 
   if( _matrule->_rChild &&
       ( strcmp(_matrule->_rChild->_opType,"AryEq"     )==0 ||
         strcmp(_matrule->_rChild->_opType,"VectorizedHashCode")==0 ||
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
@@ -213,10 +213,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, "gc/shared/barrierSetAssembler.hpp");
   AD.addInclude(AD._CPP_file, "gc/shared/collectedHeap.inline.hpp");
   AD.addInclude(AD._CPP_file, "oops/compiledICHolder.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");
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
@@ -50,10 +50,13 @@
 
 class CodeOffsets: public StackObj {
 public:
   enum Entries { Entry,
                  Verified_Entry,
+                 Inline_Entry,
+                 Verified_Inline_Entry,
+                 Verified_Inline_Entry_RO,
                  Frame_Complete, // Offset in the code where the frame setup is (for forte stackwalks) is complete
                  OSR_Entry,
                  Exceptions,     // Offset where exception handler lives
                  Deopt,          // Offset where deopt handler lives
                  DeoptMH,        // Offset where MethodHandle deopt handler lives
@@ -65,25 +68,29 @@
 
   enum { frame_never_safe = -1 };
 
 private:
   int _values[max_Entries];
+  void check(int e) const { assert(0 <= e && e < max_Entries, "must be"); }
 
 public:
   CodeOffsets() {
     _values[Entry         ] = 0;
     _values[Verified_Entry] = 0;
+    _values[Inline_Entry  ] = 0;
+    _values[Verified_Inline_Entry] = -1;
+    _values[Verified_Inline_Entry_RO] = -1;
     _values[Frame_Complete] = frame_never_safe;
     _values[OSR_Entry     ] = 0;
     _values[Exceptions    ] = -1;
     _values[Deopt         ] = -1;
     _values[DeoptMH       ] = -1;
     _values[UnwindHandler ] = -1;
   }
 
-  int value(Entries e) { return _values[e]; }
-  void set_value(Entries e, int val) { _values[e] = val; }
+  int value(Entries e) const { check(e); return _values[e]; }
+  void set_value(Entries e, int val) { check(e); _values[e] = val; }
 };
 
 // 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.
diff a/src/hotspot/share/asm/macroAssembler.hpp b/src/hotspot/share/asm/macroAssembler.hpp
--- a/src/hotspot/share/asm/macroAssembler.hpp
+++ b/src/hotspot/share/asm/macroAssembler.hpp
@@ -25,9 +25,10 @@
 #ifndef SHARE_ASM_MACROASSEMBLER_HPP
 #define SHARE_ASM_MACROASSEMBLER_HPP
 
 #include "asm/assembler.hpp"
 #include "utilities/macros.hpp"
+#include "utilities/growableArray.hpp"
 
 #include CPU_HEADER(macroAssembler)
 
 #endif // SHARE_ASM_MACROASSEMBLER_HPP
diff a/src/hotspot/share/asm/macroAssembler_common.cpp b/src/hotspot/share/asm/macroAssembler_common.cpp
--- /dev/null
+++ b/src/hotspot/share/asm/macroAssembler_common.cpp
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2019, 2021, 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 "jvm.h"
+#include "asm/assembler.hpp"
+#include "asm/assembler.inline.hpp"
+#include "asm/macroAssembler.hpp"
+#include "oops/inlineKlass.inline.hpp"
+#include "runtime/signature_cc.hpp"
+#include "runtime/sharedRuntime.hpp"
+#ifdef COMPILER2
+#include "opto/compile.hpp"
+#include "opto/node.hpp"
+#endif
+
+void MacroAssembler::skip_unpacked_fields(const GrowableArray<SigEntry>* sig, int& sig_index, VMRegPair* regs_from, int regs_from_count, int& from_index) {
+  ScalarizedInlineArgsStream stream(sig, sig_index, regs_from, regs_from_count, from_index);
+  VMReg reg;
+  BasicType bt;
+  while (stream.next(reg, bt)) {}
+  sig_index = stream.sig_index();
+  from_index = stream.regs_index();
+}
+
+bool MacroAssembler::is_reg_in_unpacked_fields(const GrowableArray<SigEntry>* sig, int sig_index, VMReg to, VMRegPair* regs_from, int regs_from_count, int from_index) {
+  ScalarizedInlineArgsStream stream(sig, sig_index, regs_from, regs_from_count, from_index);
+  VMReg reg;
+  BasicType bt;
+  while (stream.next(reg, bt)) {
+    if (reg == to) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void MacroAssembler::mark_reg_writable(const VMRegPair* regs, int num_regs, int reg_index, MacroAssembler::RegState* reg_state) {
+  assert(0 <= reg_index && reg_index < num_regs, "sanity");
+  VMReg from_reg = regs[reg_index].first();
+  if (from_reg->is_valid()) {
+    assert(from_reg->is_stack(), "reserved entries must be stack");
+    reg_state[from_reg->value()] = MacroAssembler::reg_writable;
+  }
+}
+
+MacroAssembler::RegState* MacroAssembler::init_reg_state(VMRegPair* regs, int num_regs, int sp_inc, int max_stack) {
+  int max_reg = VMRegImpl::stack2reg(max_stack)->value();
+  MacroAssembler::RegState* reg_state = NEW_RESOURCE_ARRAY(MacroAssembler::RegState, max_reg);
+
+  // Make all writable
+  for (int i = 0; i < max_reg; ++i) {
+    reg_state[i] = MacroAssembler::reg_writable;
+  }
+  // Set all source registers/stack slots to readonly to prevent accidental overwriting
+  for (int i = 0; i < num_regs; ++i) {
+    VMReg reg = regs[i].first();
+    if (!reg->is_valid()) continue;
+    if (reg->is_stack()) {
+      // Update source stack location by adding stack increment
+      reg = VMRegImpl::stack2reg(reg->reg2stack() + sp_inc/VMRegImpl::stack_slot_size);
+      regs[i] = reg;
+    }
+    assert(reg->value() >= 0 && reg->value() < max_reg, "reg value out of bounds");
+    reg_state[reg->value()] = MacroAssembler::reg_readonly;
+  }
+  return reg_state;
+}
+
+#ifdef COMPILER2
+int MacroAssembler::unpack_inline_args(Compile* C, bool receiver_only) {
+  assert(C->has_scalarized_args(), "inline type argument scalarization is disabled");
+  Method* method = C->method()->get_Method();
+  const GrowableArray<SigEntry>* sig = method->adapter()->get_sig_cc();
+  assert(sig != nullptr, "must have scalarized signature");
+
+  // Get unscalarized calling convention
+  BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, 256);
+  int args_passed = 0;
+  if (!method->is_static()) {
+    sig_bt[args_passed++] = T_OBJECT;
+  }
+  if (!receiver_only) {
+    for (SignatureStream ss(method->signature()); !ss.at_return_type(); ss.next()) {
+      BasicType bt = ss.type();
+      sig_bt[args_passed++] = bt;
+      if (type2size[bt] == 2) {
+        sig_bt[args_passed++] = T_VOID;
+      }
+    }
+  } else {
+    // Only unpack the receiver, all other arguments are already scalarized
+    InstanceKlass* holder = method->method_holder();
+    int rec_len = (holder->is_inline_klass() && method->is_scalarized_arg(0)) ? InlineKlass::cast(holder)->extended_sig()->length() : 1;
+    // Copy scalarized signature but skip receiver and inline type delimiters
+    for (int i = 0; i < sig->length(); i++) {
+      if (SigEntry::skip_value_delimiters(sig, i) && rec_len <= 0) {
+        sig_bt[args_passed++] = sig->at(i)._bt;
+      }
+      rec_len--;
+    }
+  }
+  VMRegPair* regs = NEW_RESOURCE_ARRAY(VMRegPair, args_passed);
+  int args_on_stack = SharedRuntime::java_calling_convention(sig_bt, regs, args_passed);
+
+  // Get scalarized calling convention
+  int args_passed_cc = SigEntry::fill_sig_bt(sig, sig_bt);
+  VMRegPair* regs_cc = NEW_RESOURCE_ARRAY(VMRegPair, sig->length());
+  int args_on_stack_cc = SharedRuntime::java_calling_convention(sig_bt, regs_cc, args_passed_cc);
+
+  // Check if we need to extend the stack for unpacking
+  int sp_inc = 0;
+  if (args_on_stack_cc > args_on_stack) {
+    sp_inc = extend_stack_for_inline_args(args_on_stack_cc);
+  }
+  shuffle_inline_args(false, receiver_only, sig,
+                      args_passed, args_on_stack, regs,           // from
+                      args_passed_cc, args_on_stack_cc, regs_cc,  // to
+                      sp_inc, noreg);
+  return sp_inc;
+}
+#endif // COMPILER2
+
+void MacroAssembler::shuffle_inline_args(bool is_packing, bool receiver_only,
+                                         const GrowableArray<SigEntry>* sig,
+                                         int args_passed, int args_on_stack, VMRegPair* regs,
+                                         int args_passed_to, int args_on_stack_to, VMRegPair* regs_to,
+                                         int sp_inc, Register val_array) {
+  int max_stack = MAX2(args_on_stack + sp_inc/VMRegImpl::stack_slot_size, args_on_stack_to);
+  RegState* reg_state = init_reg_state(regs, args_passed, sp_inc, max_stack);
+
+  // Emit code for packing/unpacking inline type arguments
+  // We try multiple times and eventually start spilling to resolve (circular) dependencies
+  bool done = (args_passed_to == 0);
+  for (int i = 0; i < 2*args_passed_to && !done; ++i) {
+    done = true;
+    bool spill = (i > args_passed_to); // Start spilling?
+    // Iterate over all arguments (when unpacking, do in reverse)
+    int step = is_packing ? 1 : -1;
+    int from_index    = is_packing ? 0 : args_passed      - 1;
+    int to_index      = is_packing ? 0 : args_passed_to   - 1;
+    int sig_index     = is_packing ? 0 : sig->length()    - 1;
+    int sig_index_end = is_packing ? sig->length() : -1;
+    int vtarg_index = 0;
+    for (; sig_index != sig_index_end; sig_index += step) {
+      assert(0 <= sig_index && sig_index < sig->length(), "index out of bounds");
+      if (spill) {
+        // This call returns true IFF we should keep trying to spill in this round.
+        spill = shuffle_inline_args_spill(is_packing, sig, sig_index, regs, from_index, args_passed,
+                                          reg_state);
+      }
+      BasicType bt = sig->at(sig_index)._bt;
+      if (SigEntry::skip_value_delimiters(sig, sig_index)) {
+        VMReg from_reg = regs[from_index].first();
+        if (from_reg->is_valid()) {
+          done &= move_helper(from_reg, regs_to[to_index].first(), bt, reg_state);
+        } else {
+          // halves of T_LONG or T_DOUBLE
+          assert(bt == T_VOID, "unexpected basic type");
+        }
+        to_index += step;
+        from_index += step;
+      } else if (is_packing) {
+        assert(val_array != noreg, "must be");
+        VMReg reg_to = regs_to[to_index].first();
+        done &= pack_inline_helper(sig, sig_index, vtarg_index,
+                                   regs, args_passed, from_index, reg_to,
+                                   reg_state, val_array);
+        vtarg_index++;
+        to_index++;
+      } else if (!receiver_only || (from_index == 0 && bt == T_VOID)) {
+        VMReg from_reg = regs[from_index].first();
+        done &= unpack_inline_helper(sig, sig_index,
+                                     from_reg, from_index, regs_to, args_passed_to, to_index,
+                                     reg_state);
+        if (from_index == -1 && sig_index != 0) {
+          // This can happen when we are confusing an empty inline type argument which is
+          // not counted in the scalarized signature for the receiver. Just ignore it.
+          assert(receiver_only, "sanity");
+          from_index = 0;
+        }
+      }
+    }
+  }
+  guarantee(done, "Could not resolve circular dependency when shuffling inline type arguments");
+}
+
+bool MacroAssembler::shuffle_inline_args_spill(bool is_packing, const GrowableArray<SigEntry>* sig, int sig_index,
+                                               VMRegPair* regs_from, int from_index, int regs_from_count, RegState* reg_state) {
+  VMReg reg;
+  if (!is_packing || SigEntry::skip_value_delimiters(sig, sig_index)) {
+    reg = regs_from[from_index].first();
+    if (!reg->is_valid() || reg_state[reg->value()] != reg_readonly) {
+      // Spilling this won't break cycles
+      return true;
+    }
+  } else {
+    ScalarizedInlineArgsStream stream(sig, sig_index, regs_from, regs_from_count, from_index);
+    VMReg from_reg;
+    BasicType bt;
+    bool found = false;
+    while (stream.next(from_reg, bt)) {
+      reg = from_reg;
+      assert(from_reg->is_valid(), "must be");
+      if (reg_state[from_reg->value()] == reg_readonly) {
+        found = true;
+        break;
+      }
+    }
+    if (!found) {
+      // Spilling fields in this inline type arg won't break cycles
+      return true;
+    }
+  }
+
+  // Spill argument to be able to write the source and resolve circular dependencies
+  VMReg spill_reg = spill_reg_for(reg);
+  if (reg_state[spill_reg->value()] == reg_readonly) {
+    // We have already spilled (in previous round). The spilled register should be consumed by this round.
+  } else {
+    bool res = move_helper(reg, spill_reg, T_DOUBLE, reg_state);
+    assert(res, "Spilling should not fail");
+    // Set spill_reg as new source and update state
+    reg = spill_reg;
+    regs_from[from_index].set1(reg);
+    reg_state[reg->value()] = reg_readonly;
+  }
+
+  return false; // Do not spill again in this round
+}
diff a/src/hotspot/share/asm/macroAssembler_common.hpp b/src/hotspot/share/asm/macroAssembler_common.hpp
--- /dev/null
+++ b/src/hotspot/share/asm/macroAssembler_common.hpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019, 2021, 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_ASM_MACROASSEMBLER_COMMON_HPP
+#define SHARE_ASM_MACROASSEMBLER_COMMON_HPP
+
+// These are part of the MacroAssembler class that are common for all CPUs
+
+// class MacroAssembler ... {
+
+  enum RegState {
+    reg_readonly,
+    reg_writable,
+    reg_written
+  };
+
+  void skip_unpacked_fields(const GrowableArray<SigEntry>* sig, int& sig_index, VMRegPair* regs_from,
+                            int regs_from_count, int& from_index);
+  bool is_reg_in_unpacked_fields(const GrowableArray<SigEntry>* sig, int sig_index, VMReg to, VMRegPair* regs_from,
+                                 int regs_from_count, int from_index);
+  void mark_reg_writable(const VMRegPair* regs, int num_regs, int reg_index, RegState* reg_state);
+  RegState* init_reg_state(VMRegPair* regs, int num_regs, int sp_inc, int max_stack);
+  int unpack_inline_args(Compile* C, bool receiver_only);
+  void shuffle_inline_args(bool is_packing, bool receiver_only,
+                           const GrowableArray<SigEntry>* sig,
+                           int args_passed, int args_on_stack, VMRegPair* regs,
+                           int args_passed_to, int args_on_stack_to, VMRegPair* regs_to,
+                           int sp_inc, Register val_array);
+  bool shuffle_inline_args_spill(bool is_packing, const GrowableArray<SigEntry>* sig, int sig_index,
+                                 VMRegPair* regs_from, int from_index, int regs_from_count, RegState* reg_state);
+// };
+
+#endif // SHARE_ASM_MACROASSEMBLER_COMMON_HPP
diff a/src/hotspot/share/c1/c1_Canonicalizer.cpp b/src/hotspot/share/c1/c1_Canonicalizer.cpp
--- a/src/hotspot/share/c1/c1_Canonicalizer.cpp
+++ b/src/hotspot/share/c1/c1_Canonicalizer.cpp
@@ -279,11 +279,11 @@
   IntConstant* index = x->index()->type()->as_IntConstant();
 
   assert(array == nullptr || FoldStableValues, "not enabled");
 
   // Constant fold loads from stable arrays.
-  if (!x->mismatched() && array != nullptr && index != nullptr) {
+  if (!x->should_profile() && !x->mismatched() && array != nullptr && index != nullptr) {
     jint idx = index->value();
     if (idx < 0 || idx >= array->value()->length()) {
       // Leave the load as is. The range check will handle it.
       return;
     }
@@ -672,17 +672,18 @@
     if (klass != nullptr && klass->is_loaded()) {
       bool is_interface = klass->is_instance_klass() &&
                           klass->as_instance_klass()->is_interface();
       // Interface casts can't be statically optimized away since verifier doesn't
       // enforce interface types in bytecode.
-      if (!is_interface && klass->is_subtype_of(x->klass())) {
+      if (!is_interface && klass->is_subtype_of(x->klass()) && (!x->is_null_free() || obj->is_null_free())) {
+        assert(!x->klass()->is_inlinetype() || x->klass() == klass, "Inline klasses can't have subtypes");
         set_canonical(obj);
         return;
       }
     }
-    // checkcast of null returns null
-    if (obj->as_Constant() && obj->type()->as_ObjectType()->constant_value()->is_null_object()) {
+    // checkcast of null returns null for non null-free klasses
+    if (!x->is_null_free() && obj->is_null_obj()) {
       set_canonical(obj);
     }
   }
 }
 void Canonicalizer::do_InstanceOf     (InstanceOf*      x) {
@@ -692,11 +693,11 @@
     if (exact != nullptr && exact->is_loaded() && (obj->as_NewInstance() || obj->as_NewArray())) {
       set_constant(exact->is_subtype_of(x->klass()) ? 1 : 0);
       return;
     }
     // instanceof null returns false
-    if (obj->as_Constant() && obj->type()->as_ObjectType()->constant_value()->is_null_object()) {
+    if (obj->as_Constant() && obj->is_null_obj()) {
       set_constant(0);
     }
   }
 
 }
@@ -861,12 +862,13 @@
 void Canonicalizer::do_UnsafeGet      (UnsafeGet*       x) {}
 void Canonicalizer::do_UnsafePut      (UnsafePut*       x) {}
 void Canonicalizer::do_UnsafeGetAndSet(UnsafeGetAndSet* x) {}
 void Canonicalizer::do_ProfileCall    (ProfileCall*     x) {}
 void Canonicalizer::do_ProfileReturnType(ProfileReturnType* x) {}
-void Canonicalizer::do_ProfileInvoke  (ProfileInvoke*   x) {}
-void Canonicalizer::do_RuntimeCall    (RuntimeCall*     x) {}
+void Canonicalizer::do_ProfileInvoke    (ProfileInvoke* x) {}
+void Canonicalizer::do_ProfileACmpTypes (ProfileACmpTypes* x) {}
+void Canonicalizer::do_RuntimeCall      (RuntimeCall* x) {}
 void Canonicalizer::do_RangeCheckPredicate(RangeCheckPredicate* x) {}
 #ifdef ASSERT
 void Canonicalizer::do_Assert         (Assert*          x) {}
 #endif
 void Canonicalizer::do_MemBar         (MemBar*          x) {}
diff a/src/hotspot/share/c1/c1_Canonicalizer.hpp b/src/hotspot/share/c1/c1_Canonicalizer.hpp
--- a/src/hotspot/share/c1/c1_Canonicalizer.hpp
+++ b/src/hotspot/share/c1/c1_Canonicalizer.hpp
@@ -92,10 +92,11 @@
   virtual void do_UnsafeGet      (UnsafeGet*       x);
   virtual void do_UnsafePut      (UnsafePut*       x);
   virtual void do_UnsafeGetAndSet(UnsafeGetAndSet* x);
   virtual void do_ProfileCall    (ProfileCall*     x);
   virtual void do_ProfileReturnType (ProfileReturnType*  x);
+  virtual void do_ProfileACmpTypes(ProfileACmpTypes*  x);
   virtual void do_ProfileInvoke  (ProfileInvoke*   x);
   virtual void do_RuntimeCall    (RuntimeCall*     x);
   virtual void do_MemBar         (MemBar*          x);
   virtual void do_RangeCheckPredicate(RangeCheckPredicate* x);
 #ifdef ASSERT
diff a/src/hotspot/share/c1/c1_CodeStubs.hpp b/src/hotspot/share/c1/c1_CodeStubs.hpp
--- a/src/hotspot/share/c1/c1_CodeStubs.hpp
+++ b/src/hotspot/share/c1/c1_CodeStubs.hpp
@@ -256,10 +256,87 @@
   virtual void print_name(outputStream* out) const { out->print("ImplicitNullCheckStub"); }
 #endif // PRODUCT
 };
 
 
+class LoadFlattenedArrayStub: public CodeStub {
+ private:
+  LIR_Opr          _array;
+  LIR_Opr          _index;
+  LIR_Opr          _result;
+  LIR_Opr          _scratch_reg;
+  CodeEmitInfo*    _info;
+
+ public:
+  LoadFlattenedArrayStub(LIR_Opr array, LIR_Opr index, LIR_Opr result, CodeEmitInfo* info);
+  virtual void emit_code(LIR_Assembler* e);
+  virtual CodeEmitInfo* info() const             { return _info; }
+  virtual void visit(LIR_OpVisitState* visitor) {
+    visitor->do_slow_case(_info);
+    visitor->do_input(_array);
+    visitor->do_input(_index);
+    visitor->do_output(_result);
+    if (_scratch_reg != LIR_OprFact::illegalOpr) {
+      visitor->do_temp(_scratch_reg);
+    }
+  }
+
+#ifndef PRODUCT
+  virtual void print_name(outputStream* out) const { out->print("LoadFlattenedArrayStub"); }
+#endif // PRODUCT
+};
+
+
+class StoreFlattenedArrayStub: public CodeStub {
+ private:
+  LIR_Opr          _array;
+  LIR_Opr          _index;
+  LIR_Opr          _value;
+  LIR_Opr          _scratch_reg;
+  CodeEmitInfo*    _info;
+
+ public:
+  StoreFlattenedArrayStub(LIR_Opr array, LIR_Opr index, LIR_Opr value, CodeEmitInfo* info);
+  virtual void emit_code(LIR_Assembler* e);
+  virtual CodeEmitInfo* info() const             { return _info; }
+  virtual void visit(LIR_OpVisitState* visitor) {
+    visitor->do_slow_case(_info);
+    visitor->do_input(_array);
+    visitor->do_input(_index);
+    visitor->do_input(_value);
+    if (_scratch_reg != LIR_OprFact::illegalOpr) {
+      visitor->do_temp(_scratch_reg);
+    }
+  }
+#ifndef PRODUCT
+  virtual void print_name(outputStream* out) const { out->print("StoreFlattenedArrayStub"); }
+#endif // PRODUCT
+};
+
+class SubstitutabilityCheckStub: public CodeStub {
+ private:
+  LIR_Opr          _left;
+  LIR_Opr          _right;
+  LIR_Opr          _scratch_reg;
+  CodeEmitInfo*    _info;
+ public:
+  SubstitutabilityCheckStub(LIR_Opr left, LIR_Opr right, CodeEmitInfo* info);
+  virtual void emit_code(LIR_Assembler* e);
+  virtual CodeEmitInfo* info() const             { return _info; }
+  virtual void visit(LIR_OpVisitState* visitor) {
+    visitor->do_slow_case(_info);
+    visitor->do_input(_left);
+    visitor->do_input(_right);
+    if (_scratch_reg != LIR_OprFact::illegalOpr) {
+      visitor->do_temp(_scratch_reg);
+    }
+  }
+#ifndef PRODUCT
+  virtual void print_name(outputStream* out) const { out->print("SubstitutabilityCheckStub"); }
+#endif // PRODUCT
+};
+
 class NewInstanceStub: public CodeStub {
  private:
   ciInstanceKlass* _klass;
   LIR_Opr          _klass_reg;
   LIR_Opr          _result;
@@ -308,13 +385,13 @@
  private:
   LIR_Opr        _klass_reg;
   LIR_Opr        _length;
   LIR_Opr        _result;
   CodeEmitInfo*  _info;
-
+  bool           _is_null_free;
  public:
-  NewObjectArrayStub(LIR_Opr klass_reg, LIR_Opr length, LIR_Opr result, CodeEmitInfo* info);
+  NewObjectArrayStub(LIR_Opr klass_reg, LIR_Opr length, LIR_Opr result, CodeEmitInfo* info, bool is_null_free);
   virtual void emit_code(LIR_Assembler* e);
   virtual CodeEmitInfo* info() const             { return _info; }
   virtual void visit(LIR_OpVisitState* visitor) {
     visitor->do_slow_case(_info);
     visitor->do_input(_klass_reg);
@@ -345,24 +422,35 @@
 
 
 class MonitorEnterStub: public MonitorAccessStub {
  private:
   CodeEmitInfo* _info;
+  CodeStub* _throw_imse_stub;
+  LIR_Opr _scratch_reg;
 
  public:
-  MonitorEnterStub(LIR_Opr obj_reg, LIR_Opr lock_reg, CodeEmitInfo* info)
+  MonitorEnterStub(LIR_Opr obj_reg, LIR_Opr lock_reg, CodeEmitInfo* info,
+                   CodeStub* throw_imse_stub = nullptr, LIR_Opr scratch_reg = LIR_OprFact::illegalOpr)
     : MonitorAccessStub(obj_reg, lock_reg) {
     _info = new CodeEmitInfo(info);
+    _scratch_reg = scratch_reg;
+    _throw_imse_stub = throw_imse_stub;
+    if (_throw_imse_stub != nullptr) {
+      assert(_scratch_reg != LIR_OprFact::illegalOpr, "must be");
+    }
     FrameMap* f = Compilation::current()->frame_map();
     f->update_reserved_argument_area_size(2 * BytesPerWord);
   }
 
   virtual void emit_code(LIR_Assembler* e);
   virtual CodeEmitInfo* info() const             { return _info; }
   virtual void visit(LIR_OpVisitState* visitor) {
     visitor->do_input(_obj_reg);
     visitor->do_input(_lock_reg);
+    if (_scratch_reg != LIR_OprFact::illegalOpr) {
+      visitor->do_temp(_scratch_reg);
+    }
     visitor->do_slow_case(_info);
   }
 #ifndef PRODUCT
   virtual void print_name(outputStream* out) const { out->print("MonitorEnterStub"); }
 #endif // PRODUCT
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
@@ -584,10 +584,11 @@
 , _exception_info_list(nullptr)
 , _allocator(nullptr)
 , _code(buffer_blob)
 , _has_access_indexed(false)
 , _interpreter_frame_size(0)
+, _compiled_entry_signature(method->get_Method())
 , _immediate_oops_patched(0)
 , _current_instruction(nullptr)
 #ifndef PRODUCT
 , _last_instruction_printed(nullptr)
 , _cfg_printer_output(nullptr)
@@ -601,13 +602,18 @@
 #ifndef PRODUCT
   if (PrintCFGToFile) {
     _cfg_printer_output = new CFGPrinterOutput(this);
   }
 #endif
-
   CompilationMemoryStatisticMark cmsm(directive);
 
+  {
+    ResetNoHandleMark rnhm; // Huh? Required when doing class lookup of the Q-types
+    // TODO 8284443 Should only be computed once
+    _compiled_entry_signature.compute_calling_conventions(false);
+  }
+
   compile_method();
   if (bailed_out()) {
     _env->record_method_not_compilable(bailout_msg());
     if (is_profiling()) {
       // Compilation failed, create MDO, which would signal the interpreter
diff a/src/hotspot/share/c1/c1_Compilation.hpp b/src/hotspot/share/c1/c1_Compilation.hpp
--- a/src/hotspot/share/c1/c1_Compilation.hpp
+++ b/src/hotspot/share/c1/c1_Compilation.hpp
@@ -31,10 +31,11 @@
 #include "compiler/compiler_globals.hpp"
 #include "compiler/compilerDefinitions.inline.hpp"
 #include "compiler/compilerDirectives.hpp"
 #include "memory/resourceArea.hpp"
 #include "runtime/deoptimization.hpp"
+#include "runtime/sharedRuntime.hpp"
 
 class CompilationFailureInfo;
 class CompilationResourceObj;
 class XHandlers;
 class ExceptionInfo;
@@ -94,10 +95,11 @@
   LinearScan*        _allocator;
   CodeOffsets        _offsets;
   CodeBuffer         _code;
   bool               _has_access_indexed;
   int                _interpreter_frame_size; // Stack space needed in case of a deoptimization
+  CompiledEntrySignature _compiled_entry_signature;
   int                _immediate_oops_patched;
 
   // compilation helpers
   void initialize();
   void build_hir();
@@ -259,10 +261,14 @@
   }
   bool profile_return() {
     return env()->comp_level() == CompLevel_full_profile &&
       C1UpdateMethodData && MethodData::profile_return();
   }
+  bool profile_array_accesses() {
+    return env()->comp_level() == CompLevel_full_profile &&
+      C1UpdateMethodData;
+  }
 
   // will compilation make optimistic assumptions that might lead to
   // deoptimization and that the runtime will account for?
   bool is_optimistic() {
     return CompilerConfig::is_c1_only_no_jvmci() && !is_profiling() &&
@@ -284,10 +290,17 @@
   }
 
   int interpreter_frame_size() const {
     return _interpreter_frame_size;
   }
+
+  const CompiledEntrySignature* compiled_entry_signature() const {
+    return &_compiled_entry_signature;
+  }
+  bool needs_stack_repair() const {
+    return compiled_entry_signature()->c1_needs_stack_repair();
+  }
 };
 
 
 // Macro definitions for unified bailout-support
 // The methods bailout() and bailed_out() are present in all classes
diff a/src/hotspot/share/c1/c1_FrameMap.cpp b/src/hotspot/share/c1/c1_FrameMap.cpp
--- a/src/hotspot/share/c1/c1_FrameMap.cpp
+++ b/src/hotspot/share/c1/c1_FrameMap.cpp
@@ -185,18 +185,19 @@
   }
 
 }
 
 
-bool FrameMap::finalize_frame(int nof_slots) {
+bool FrameMap::finalize_frame(int nof_slots, bool needs_stack_repair) {
   assert(nof_slots >= 0, "must be positive");
   assert(_num_spills == -1, "can only be set once");
   _num_spills = nof_slots;
   assert(_framesize == -1, "should only be calculated once");
   _framesize =  align_up(in_bytes(sp_offset_for_monitor_base(0)) +
                          _num_monitors * (int)sizeof(BasicObjectLock) +
-                         (int)sizeof(intptr_t) +                        // offset of deopt orig pc
+                         (int)sizeof(intptr_t) +                             // offset of deopt orig pc
+                         (needs_stack_repair ? (int)sizeof(intptr_t) : 0) +  // stack increment value
                          frame_pad_in_bytes,
                          StackAlignmentInBytes) / 4;
   int java_index = 0;
   for (int i = 0; i < _incoming_arguments->length(); i++) {
     LIR_Opr opr = _incoming_arguments->at(i);
diff a/src/hotspot/share/c1/c1_FrameMap.hpp b/src/hotspot/share/c1/c1_FrameMap.hpp
--- a/src/hotspot/share/c1/c1_FrameMap.hpp
+++ b/src/hotspot/share/c1/c1_FrameMap.hpp
@@ -183,11 +183,11 @@
   static LIR_Opr as_address_opr(Register r) {
     return LIR_OprFact::single_cpu_address(cpu_reg2rnr(r));
   }
 
   FrameMap(ciMethod* method, int monitors, int reserved_argument_area_size);
-  bool finalize_frame(int nof_slots);
+  bool finalize_frame(int nof_slots, bool needs_stack_repair);
 
   int   reserved_argument_area_size () const     { return _reserved_argument_area_size; }
   int   framesize                   () const     { assert(_framesize != -1, "hasn't been calculated"); return _framesize; }
   ByteSize framesize_in_bytes       () const     { return in_ByteSize(framesize() * 4); }
   int   num_monitors                () const     { return _num_monitors; }
@@ -209,10 +209,13 @@
     return make_new_address(sp_offset_for_monitor_lock(monitor_index));
   }
   Address address_for_monitor_object(int monitor_index) const {
     return make_new_address(sp_offset_for_monitor_object(monitor_index));
   }
+  Address address_for_orig_pc_addr() const {
+    return make_new_address(sp_offset_for_monitor_base(_num_monitors));
+  }
 
   // Creates Location describing desired slot and returns it via pointer
   // to Location object. Returns true if the stack frame offset was legal
   // (as defined by Location::legal_offset_in_bytes()), false otherwise.
   // Do not use the returned location if this returns false.
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
@@ -28,10 +28,12 @@
 #include "c1/c1_Compilation.hpp"
 #include "c1/c1_GraphBuilder.hpp"
 #include "c1/c1_InstructionPrinter.hpp"
 #include "ci/ciCallSite.hpp"
 #include "ci/ciField.hpp"
+#include "ci/ciFlatArrayKlass.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "ci/ciKlass.hpp"
 #include "ci/ciMemberName.hpp"
 #include "ci/ciSymbols.hpp"
 #include "ci/ciUtilities.inline.hpp"
 #include "classfile/javaClasses.hpp"
@@ -1061,11 +1063,19 @@
 }
 
 
 void GraphBuilder::load_indexed(BasicType type) {
   // In case of in block code motion in range check elimination
-  ValueStack* state_before = copy_state_indexed_access();
+  ValueStack* state_before = nullptr;
+  int array_idx = state()->stack_size() - 2;
+  if (type == T_OBJECT && state()->stack_at(array_idx)->maybe_flat_array()) {
+    // Save the entire state and re-execute on deopt when accessing flat arrays
+    state_before = copy_state_before();
+    state_before->set_should_reexecute(true);
+  } else {
+    state_before = copy_state_indexed_access();
+  }
   compilation()->set_has_access_indexed(true);
   Value index = ipop();
   Value array = apop();
   Value length = nullptr;
   if (CSEArrayLength ||
@@ -1073,17 +1083,85 @@
       (array->as_AccessField() && array->as_AccessField()->field()->is_constant()) ||
       (array->as_NewArray() && array->as_NewArray()->length() && array->as_NewArray()->length()->type()->is_constant()) ||
       (array->as_NewMultiArray() && array->as_NewMultiArray()->dims()->at(0)->type()->is_constant())) {
     length = append(new ArrayLength(array, state_before));
   }
-  push(as_ValueType(type), append(new LoadIndexed(array, index, length, type, state_before)));
+
+  bool need_membar = false;
+  LoadIndexed* load_indexed = nullptr;
+  Instruction* result = nullptr;
+  if (array->is_loaded_flat_array()) {
+    ciType* array_type = array->declared_type();
+    ciInlineKlass* elem_klass = array_type->as_flat_array_klass()->element_klass()->as_inline_klass();
+
+    bool can_delay_access = false;
+    ciBytecodeStream s(method());
+    s.force_bci(bci());
+    s.next();
+    if (s.cur_bc() == Bytecodes::_getfield) {
+      bool will_link;
+      ciField* next_field = s.get_field(will_link);
+      bool next_needs_patching = !next_field->holder()->is_initialized() ||
+                                 !next_field->will_link(method(), Bytecodes::_getfield) ||
+                                 PatchALot;
+      can_delay_access = C1UseDelayedFlattenedFieldReads && !next_needs_patching;
+    }
+    if (can_delay_access) {
+      // potentially optimizable array access, storing information for delayed decision
+      LoadIndexed* li = new LoadIndexed(array, index, length, type, state_before);
+      DelayedLoadIndexed* dli = new DelayedLoadIndexed(li, state_before);
+      li->set_delayed(dli);
+      set_pending_load_indexed(dli);
+      return; // Nothing else to do for now
+    } else {
+      if (elem_klass->is_empty()) {
+        // No need to create a new instance, the default instance will be used instead
+        load_indexed = new LoadIndexed(array, index, length, type, state_before);
+        apush(append(load_indexed));
+      } else {
+        NewInstance* new_instance = new NewInstance(elem_klass, state_before, false, true);
+        _memory->new_instance(new_instance);
+        apush(append_split(new_instance));
+        load_indexed = new LoadIndexed(array, index, length, type, state_before);
+        load_indexed->set_vt(new_instance);
+        // The LoadIndexed node will initialise this instance by copying from
+        // the flat field.  Ensure these stores are visible before any
+        // subsequent store that publishes this reference.
+        need_membar = true;
+      }
+    }
+  } else {
+    load_indexed = new LoadIndexed(array, index, length, type, state_before);
+    if (profile_array_accesses() && is_reference_type(type)) {
+      compilation()->set_would_profile(true);
+      load_indexed->set_should_profile(true);
+      load_indexed->set_profiled_method(method());
+      load_indexed->set_profiled_bci(bci());
+    }
+  }
+  result = append(load_indexed);
+  if (need_membar) {
+    append(new MemBar(lir_membar_storestore));
+  }
+  assert(!load_indexed->should_profile() || load_indexed == result, "should not be optimized out");
+  if (!array->is_loaded_flat_array()) {
+    push(as_ValueType(type), result);
+  }
 }
 
 
 void GraphBuilder::store_indexed(BasicType type) {
   // In case of in block code motion in range check elimination
-  ValueStack* state_before = copy_state_indexed_access();
+  ValueStack* state_before = nullptr;
+  int array_idx = state()->stack_size() - 3;
+  if (type == T_OBJECT && state()->stack_at(array_idx)->maybe_flat_array()) {
+    // Save the entire state and re-execute on deopt when accessing flat arrays
+    state_before = copy_state_before();
+    state_before->set_should_reexecute(true);
+  } else {
+    state_before = copy_state_indexed_access();
+  }
   compilation()->set_has_access_indexed(true);
   Value value = pop(as_ValueType(type));
   Value index = ipop();
   Value array = apop();
   Value length = nullptr;
@@ -1104,36 +1182,32 @@
       value = append(new LogicOp(Bytecodes::_iand, value, mask));
     }
   } else if (type == T_BYTE) {
     check_boolean = true;
   }
-  StoreIndexed* result = new StoreIndexed(array, index, length, type, value, state_before, check_boolean);
-  append(result);
-  _memory->store_value(value);
 
-  if (type == T_OBJECT && is_profiling()) {
-    // Note that we'd collect profile data in this method if we wanted it.
+  StoreIndexed* store_indexed = new StoreIndexed(array, index, length, type, value, state_before, check_boolean);
+  if (profile_array_accesses() && is_reference_type(type) && !array->is_loaded_flat_array()) {
     compilation()->set_would_profile(true);
-
-    if (profile_checkcasts()) {
-      result->set_profiled_method(method());
-      result->set_profiled_bci(bci());
-      result->set_should_profile(true);
-    }
+    store_indexed->set_should_profile(true);
+    store_indexed->set_profiled_method(method());
+    store_indexed->set_profiled_bci(bci());
   }
+  Instruction* result = append(store_indexed);
+  assert(!store_indexed->should_profile() || store_indexed == result, "should not be optimized out");
+  _memory->store_value(value);
 }
 
-
 void GraphBuilder::stack_op(Bytecodes::Code code) {
   switch (code) {
     case Bytecodes::_pop:
-      { state()->raw_pop();
+      { Value w = state()->raw_pop();
       }
       break;
     case Bytecodes::_pop2:
-      { state()->raw_pop();
-        state()->raw_pop();
+      { Value w1 = state()->raw_pop();
+        Value w2 = state()->raw_pop();
       }
       break;
     case Bytecodes::_dup:
       { Value w = state()->raw_pop();
         state()->raw_push(w);
@@ -1308,13 +1382,40 @@
 
 void GraphBuilder::if_node(Value x, If::Condition cond, Value y, ValueStack* state_before) {
   BlockBegin* tsux = block_at(stream()->get_dest());
   BlockBegin* fsux = block_at(stream()->next_bci());
   bool is_bb = tsux->bci() < stream()->cur_bci() || fsux->bci() < stream()->cur_bci();
+
+  bool subst_check = false;
+  if (EnableValhalla && (stream()->cur_bc() == Bytecodes::_if_acmpeq || stream()->cur_bc() == Bytecodes::_if_acmpne)) {
+    ValueType* left_vt = x->type();
+    ValueType* right_vt = y->type();
+    if (left_vt->is_object()) {
+      assert(right_vt->is_object(), "must be");
+      ciKlass* left_klass = x->as_loaded_klass_or_null();
+      ciKlass* right_klass = y->as_loaded_klass_or_null();
+
+      if (left_klass == nullptr || right_klass == nullptr) {
+        // The klass is still unloaded, or came from a Phi node. Go slow case;
+        subst_check = true;
+      } else if (left_klass->can_be_inline_klass() || right_klass->can_be_inline_klass()) {
+        // Either operand may be a value object, but we're not sure. Go slow case;
+        subst_check = true;
+      } else {
+        // No need to do substitutability check
+      }
+    }
+  }
+  if ((stream()->cur_bc() == Bytecodes::_if_acmpeq || stream()->cur_bc() == Bytecodes::_if_acmpne) &&
+      is_profiling() && profile_branches()) {
+    compilation()->set_would_profile(true);
+    append(new ProfileACmpTypes(method(), bci(), x, y));
+  }
+
   // In case of loop invariant code motion or predicate insertion
   // before the body of a loop the state is needed
-  Instruction *i = append(new If(x, cond, false, y, tsux, fsux, (is_bb || compilation()->is_optimistic()) ? state_before : nullptr, is_bb));
+  Instruction *i = append(new If(x, cond, false, y, tsux, fsux, (is_bb || compilation()->is_optimistic() || subst_check) ? state_before : nullptr, is_bb, subst_check));
 
   assert(i->as_Goto() == nullptr ||
          (i->as_Goto()->sux_at(0) == tsux  && i->as_Goto()->is_safepoint() == (tsux->bci() < stream()->cur_bci())) ||
          (i->as_Goto()->sux_at(0) == fsux  && i->as_Goto()->is_safepoint() == (fsux->bci() < stream()->cur_bci())),
          "safepoint state of Goto returned by canonicalizer incorrect");
@@ -1561,11 +1662,11 @@
     call_register_finalizer();
   }
 
   // The conditions for a memory barrier are described in Parse::do_exits().
   bool need_mem_bar = false;
-  if (method()->name() == ciSymbols::object_initializer_name() &&
+  if (method()->is_object_constructor() &&
        (scope()->wrote_final() ||
          (AlwaysSafeConstructors && scope()->wrote_fields()) ||
          (support_IRIW_for_not_multiple_copy_atomic_cpu && scope()->wrote_volatile()))) {
     need_mem_bar = true;
   }
@@ -1712,20 +1813,34 @@
     default:
       return new Constant(value);
   }
 }
 
+void GraphBuilder::copy_inline_content(ciInlineKlass* vk, Value src, int src_off, Value dest, int dest_off, ValueStack* state_before, ciField* enclosing_field) {
+  for (int i = 0; i < vk->nof_nonstatic_fields(); i++) {
+    ciField* inner_field = vk->nonstatic_field_at(i);
+    assert(!inner_field->is_flat(), "the iteration over nested fields is handled by the loop itself");
+    int off = inner_field->offset_in_bytes() - vk->first_field_offset();
+    LoadField* load = new LoadField(src, src_off + off, inner_field, false, state_before, false);
+    Value replacement = append(load);
+    StoreField* store = new StoreField(dest, dest_off + off, inner_field, replacement, false, state_before, false);
+    store->set_enclosing_field(enclosing_field);
+    append(store);
+  }
+}
+
 void GraphBuilder::access_field(Bytecodes::Code code) {
   bool will_link;
   ciField* field = stream()->get_field(will_link);
   ciInstanceKlass* holder = field->holder();
   BasicType field_type = field->type()->basic_type();
   ValueType* type = as_ValueType(field_type);
+
   // call will_link again to determine if the field is valid.
   const bool needs_patching = !holder->is_loaded() ||
                               !field->will_link(method(), code) ||
-                              PatchALot;
+                              (!field->is_flat() && PatchALot);
 
   ValueStack* state_before = nullptr;
   if (!holder->is_initialized() || needs_patching) {
     // save state before instruction for debug info when
     // deoptimization happens during patching
@@ -1740,72 +1855,101 @@
     } else {
       obj = new Constant(new InstanceConstant(holder->java_mirror()));
     }
   }
 
-  if (field->is_final() && (code == Bytecodes::_putfield)) {
+  if (field->is_final() && code == Bytecodes::_putfield) {
     scope()->set_wrote_final();
   }
 
   if (code == Bytecodes::_putfield) {
     scope()->set_wrote_fields();
     if (field->is_volatile()) {
       scope()->set_wrote_volatile();
     }
   }
 
-  const int offset = !needs_patching ? field->offset_in_bytes() : -1;
+  int offset = !needs_patching ? field->offset_in_bytes() : -1;
   switch (code) {
     case Bytecodes::_getstatic: {
       // check for compile-time constants, i.e., initialized static final fields
       Value constant = nullptr;
       if (field->is_static_constant() && !PatchALot) {
         ciConstant field_value = field->constant_value();
         assert(!field->is_stable() || !field_value.is_null_or_zero(),
                "stable static w/ default value shouldn't be a constant");
         constant = make_constant(field_value, field);
+      } else if (field->is_null_free() && field->type()->as_instance_klass()->is_initialized() &&
+                 field->type()->as_inline_klass()->is_empty()) {
+        // Loading from a field of an empty inline type. Just return the default instance.
+        constant = new Constant(new InstanceConstant(field->type()->as_inline_klass()->default_instance()));
       }
       if (constant != nullptr) {
         push(type, append(constant));
       } else {
         if (state_before == nullptr) {
           state_before = copy_state_for_exception();
         }
-        push(type, append(new LoadField(append(obj), offset, field, true,
-                                        state_before, needs_patching)));
+        LoadField* load_field = new LoadField(append(obj), offset, field, true,
+                                        state_before, needs_patching);
+        push(type, append(load_field));
       }
       break;
     }
     case Bytecodes::_putstatic: {
       Value val = pop(type);
       if (state_before == nullptr) {
         state_before = copy_state_for_exception();
       }
-      if (field->type()->basic_type() == T_BOOLEAN) {
+      if (field_type == T_BOOLEAN) {
         Value mask = append(new Constant(new IntConstant(1)));
         val = append(new LogicOp(Bytecodes::_iand, val, mask));
       }
+      if (field->is_null_free()) {
+        null_check(val);
+      }
+      if (field->is_null_free() && field->type()->is_loaded() && field->type()->as_inline_klass()->is_empty()) {
+        // Storing to a field of an empty inline type. Ignore.
+        break;
+      }
       append(new StoreField(append(obj), offset, field, val, true, state_before, needs_patching));
       break;
     }
     case Bytecodes::_getfield: {
       // Check for compile-time constants, i.e., trusted final non-static fields.
       Value constant = nullptr;
-      obj = apop();
-      ObjectType* obj_type = obj->type()->as_ObjectType();
-      if (field->is_constant() && obj_type->is_constant() && !PatchALot) {
-        ciObject* const_oop = obj_type->constant_value();
-        if (!const_oop->is_null_object() && const_oop->is_loaded()) {
-          ciConstant field_value = field->constant_value_of(const_oop);
-          if (field_value.is_valid()) {
-            constant = make_constant(field_value, field);
-            // For CallSite objects add a dependency for invalidation of the optimization.
-            if (field->is_call_site_target()) {
-              ciCallSite* call_site = const_oop->as_call_site();
-              if (!call_site->is_fully_initialized_constant_call_site()) {
-                ciMethodHandle* target = field_value.as_object()->as_method_handle();
-                dependency_recorder()->assert_call_site_target_value(call_site, target);
+      if (state_before == nullptr && field->is_flat()) {
+        // Save the entire state and re-execute on deopt when accessing flat fields
+        assert(Interpreter::bytecode_should_reexecute(code), "should reexecute");
+        state_before = copy_state_before();
+      }
+      if (!has_pending_field_access() && !has_pending_load_indexed()) {
+        obj = apop();
+        ObjectType* obj_type = obj->type()->as_ObjectType();
+        if (field->is_null_free() && field->type()->as_instance_klass()->is_initialized()
+            && field->type()->as_inline_klass()->is_empty()) {
+          // Loading from a field of an empty inline type. Just return the default instance.
+          null_check(obj);
+          constant = new Constant(new InstanceConstant(field->type()->as_inline_klass()->default_instance()));
+        } else if (field->is_constant() && !field->is_flat() && obj_type->is_constant() && !PatchALot) {
+          ciObject* const_oop = obj_type->constant_value();
+          if (!const_oop->is_null_object() && const_oop->is_loaded()) {
+            ciConstant field_value = field->constant_value_of(const_oop);
+            if (field_value.is_valid()) {
+              if (field->is_null_free() && field_value.is_null_or_zero()) {
+                // Non-flat inline type field. Replace null by the default value.
+                constant = new Constant(new InstanceConstant(field->type()->as_inline_klass()->default_instance()));
+              } else {
+                constant = make_constant(field_value, field);
+              }
+              // For CallSite objects add a dependency for invalidation of the optimization.
+              if (field->is_call_site_target()) {
+                ciCallSite* call_site = const_oop->as_call_site();
+                if (!call_site->is_fully_initialized_constant_call_site()) {
+                  ciMethodHandle* target = field_value.as_object()->as_method_handle();
+                  dependency_recorder()->assert_call_site_target_value(call_site, target);
+                }
               }
             }
           }
         }
       }
@@ -1813,62 +1957,158 @@
         push(type, append(constant));
       } else {
         if (state_before == nullptr) {
           state_before = copy_state_for_exception();
         }
-        LoadField* load = new LoadField(obj, offset, field, false, state_before, needs_patching);
-        Value replacement = !needs_patching ? _memory->load(load) : load;
-        if (replacement != load) {
-          assert(replacement->is_linked() || !replacement->can_be_linked(), "should already by linked");
-          // Writing an (integer) value to a boolean, byte, char or short field includes an implicit narrowing
-          // conversion. Emit an explicit conversion here to get the correct field value after the write.
-          BasicType bt = field->type()->basic_type();
-          switch (bt) {
-          case T_BOOLEAN:
-          case T_BYTE:
-            replacement = append(new Convert(Bytecodes::_i2b, replacement, as_ValueType(bt)));
-            break;
-          case T_CHAR:
-            replacement = append(new Convert(Bytecodes::_i2c, replacement, as_ValueType(bt)));
-            break;
-          case T_SHORT:
-            replacement = append(new Convert(Bytecodes::_i2s, replacement, as_ValueType(bt)));
-            break;
-          default:
+        if (!field->is_flat()) {
+          if (has_pending_field_access()) {
+            assert(!needs_patching, "Can't patch delayed field access");
+            obj = pending_field_access()->obj();
+            offset += pending_field_access()->offset() - field->holder()->as_inline_klass()->first_field_offset();
+            field = pending_field_access()->holder()->get_field_by_offset(offset, false);
+            assert(field != nullptr, "field not found");
+            set_pending_field_access(nullptr);
+          } else if (has_pending_load_indexed()) {
+            assert(!needs_patching, "Can't patch delayed field access");
+            pending_load_indexed()->update(field, offset - field->holder()->as_inline_klass()->first_field_offset());
+            LoadIndexed* li = pending_load_indexed()->load_instr();
+            li->set_type(type);
+            push(type, append(li));
+            set_pending_load_indexed(nullptr);
             break;
           }
-          push(type, replacement);
-        } else {
-          push(type, append(load));
+          LoadField* load = new LoadField(obj, offset, field, false, state_before, needs_patching);
+          Value replacement = !needs_patching ? _memory->load(load) : load;
+          if (replacement != load) {
+            assert(replacement->is_linked() || !replacement->can_be_linked(), "should already by linked");
+            // Writing an (integer) value to a boolean, byte, char or short field includes an implicit narrowing
+            // conversion. Emit an explicit conversion here to get the correct field value after the write.
+            switch (field_type) {
+            case T_BOOLEAN:
+            case T_BYTE:
+              replacement = append(new Convert(Bytecodes::_i2b, replacement, type));
+              break;
+            case T_CHAR:
+              replacement = append(new Convert(Bytecodes::_i2c, replacement, type));
+              break;
+            case T_SHORT:
+              replacement = append(new Convert(Bytecodes::_i2s, replacement, type));
+              break;
+            default:
+              break;
+            }
+            push(type, replacement);
+          } else {
+            push(type, append(load));
+          }
+        } else {  // field is flat
+          // Look at the next bytecode to check if we can delay the field access
+          bool can_delay_access = false;
+          ciBytecodeStream s(method());
+          s.force_bci(bci());
+          s.next();
+          if (s.cur_bc() == Bytecodes::_getfield && !needs_patching) {
+            ciField* next_field = s.get_field(will_link);
+            bool next_needs_patching = !next_field->holder()->is_loaded() ||
+                                       !next_field->will_link(method(), Bytecodes::_getfield) ||
+                                       PatchALot;
+            can_delay_access = C1UseDelayedFlattenedFieldReads && !next_needs_patching;
+          }
+          if (can_delay_access) {
+            if (has_pending_load_indexed()) {
+              pending_load_indexed()->update(field, offset - field->holder()->as_inline_klass()->first_field_offset());
+            } else if (has_pending_field_access()) {
+              pending_field_access()->inc_offset(offset - field->holder()->as_inline_klass()->first_field_offset());
+            } else {
+              null_check(obj);
+              DelayedFieldAccess* dfa = new DelayedFieldAccess(obj, field->holder(), field->offset_in_bytes());
+              set_pending_field_access(dfa);
+            }
+          } else {
+            ciInlineKlass* inline_klass = field->type()->as_inline_klass();
+            scope()->set_wrote_final();
+            scope()->set_wrote_fields();
+            bool need_membar = false;
+            if (inline_klass->is_initialized() && inline_klass->is_empty()) {
+              apush(append(new Constant(new InstanceConstant(inline_klass->default_instance()))));
+              if (has_pending_field_access()) {
+                set_pending_field_access(nullptr);
+              } else if (has_pending_load_indexed()) {
+                set_pending_load_indexed(nullptr);
+              }
+            } else if (has_pending_load_indexed()) {
+              assert(!needs_patching, "Can't patch delayed field access");
+              pending_load_indexed()->update(field, offset - field->holder()->as_inline_klass()->first_field_offset());
+              NewInstance* vt = new NewInstance(inline_klass, pending_load_indexed()->state_before(), false, true);
+              _memory->new_instance(vt);
+              pending_load_indexed()->load_instr()->set_vt(vt);
+              apush(append_split(vt));
+              append(pending_load_indexed()->load_instr());
+              set_pending_load_indexed(nullptr);
+              need_membar = true;
+            } else {
+              NewInstance* new_instance = new NewInstance(inline_klass, state_before, false, true);
+              _memory->new_instance(new_instance);
+              apush(append_split(new_instance));
+              assert(!needs_patching, "Can't patch flat inline type field access");
+              if (has_pending_field_access()) {
+                copy_inline_content(inline_klass, pending_field_access()->obj(),
+                                    pending_field_access()->offset() + field->offset_in_bytes() - field->holder()->as_inline_klass()->first_field_offset(),
+                                    new_instance, inline_klass->first_field_offset(), state_before);
+                set_pending_field_access(nullptr);
+              } else {
+                copy_inline_content(inline_klass, obj, field->offset_in_bytes(), new_instance, inline_klass->first_field_offset(), state_before);
+              }
+              need_membar = true;
+            }
+            if (need_membar) {
+              // If we allocated a new instance ensure the stores to copy the
+              // field contents are visible before any subsequent store that
+              // publishes this reference.
+              append(new MemBar(lir_membar_storestore));
+            }
+          }
         }
       }
       break;
     }
     case Bytecodes::_putfield: {
       Value val = pop(type);
       obj = apop();
       if (state_before == nullptr) {
         state_before = copy_state_for_exception();
       }
-      if (field->type()->basic_type() == T_BOOLEAN) {
+      if (field_type == T_BOOLEAN) {
         Value mask = append(new Constant(new IntConstant(1)));
         val = append(new LogicOp(Bytecodes::_iand, val, mask));
       }
-      StoreField* store = new StoreField(obj, offset, field, val, false, state_before, needs_patching);
-      if (!needs_patching) store = _memory->store(store);
-      if (store != nullptr) {
-        append(store);
+      if (field->is_null_free() && field->type()->is_loaded() && field->type()->as_inline_klass()->is_empty()) {
+        // Storing to a field of an empty inline type. Ignore.
+        null_check(obj);
+        null_check(val);
+      } else if (!field->is_flat()) {
+        if (field->is_null_free()) {
+          null_check(val);
+        }
+        StoreField* store = new StoreField(obj, offset, field, val, false, state_before, needs_patching);
+        if (!needs_patching) store = _memory->store(store);
+        if (store != nullptr) {
+          append(store);
+        }
+      } else {
+        assert(!needs_patching, "Can't patch flat inline type field access");
+        ciInlineKlass* inline_klass = field->type()->as_inline_klass();
+        copy_inline_content(inline_klass, val, inline_klass->first_field_offset(), obj, offset, state_before, field);
       }
       break;
     }
     default:
       ShouldNotReachHere();
       break;
   }
 }
 
-
 Dependencies* GraphBuilder::dependency_recorder() const {
   assert(DeoptC1, "need debug information");
   return compilation()->dependency_recorder();
 }
 
@@ -1981,11 +2221,11 @@
     // Additional receiver subtype checks for interface calls via invokespecial or invokeinterface.
     ciKlass* receiver_constraint = nullptr;
 
     if (bc_raw == Bytecodes::_invokeinterface) {
       receiver_constraint = holder;
-    } else if (bc_raw == Bytecodes::_invokespecial && !target->is_object_initializer() && calling_klass->is_interface()) {
+    } else if (bc_raw == Bytecodes::_invokespecial && !target->is_object_constructor() && calling_klass->is_interface()) {
       receiver_constraint = calling_klass;
     }
 
     if (receiver_constraint != nullptr) {
       int index = state()->stack_size() - (target->arg_size_no_receiver() + 1);
@@ -2234,16 +2474,21 @@
 
 void GraphBuilder::new_instance(int klass_index) {
   ValueStack* state_before = copy_state_exhandling();
   ciKlass* klass = stream()->get_klass();
   assert(klass->is_instance_klass(), "must be an instance klass");
-  NewInstance* new_instance = new NewInstance(klass->as_instance_klass(), state_before, stream()->is_unresolved_klass());
-  _memory->new_instance(new_instance);
-  apush(append_split(new_instance));
+  if (!stream()->is_unresolved_klass() && klass->is_inlinetype() &&
+      klass->as_inline_klass()->is_initialized() && klass->as_inline_klass()->is_empty()) {
+    ciInlineKlass* vk = klass->as_inline_klass();
+    apush(append(new Constant(new InstanceConstant(vk->default_instance()))));
+  } else {
+    NewInstance* new_instance = new NewInstance(klass->as_instance_klass(), state_before, stream()->is_unresolved_klass(), false);
+    _memory->new_instance(new_instance);
+    apush(append_split(new_instance));
+  }
 }
 
-
 void GraphBuilder::new_type_array() {
   ValueStack* state_before = copy_state_exhandling();
   apush(append_split(new NewTypeArray(ipop(), (BasicType)stream()->get_index(), state_before)));
 }
 
@@ -2312,13 +2557,32 @@
   }
 }
 
 
 void GraphBuilder::monitorenter(Value x, int bci) {
+  bool maybe_inlinetype = false;
+  if (bci == InvocationEntryBci) {
+    // Called by GraphBuilder::inline_sync_entry.
+#ifdef ASSERT
+    ciType* obj_type = x->declared_type();
+    assert(obj_type == nullptr || !obj_type->is_inlinetype(), "inline types cannot have synchronized methods");
+#endif
+  } else {
+    // We are compiling a monitorenter bytecode
+    if (EnableValhalla) {
+      ciType* obj_type = x->declared_type();
+      if (obj_type == nullptr || obj_type->as_klass()->can_be_inline_klass()) {
+        // If we're (possibly) locking on an inline type, check for markWord::always_locked_pattern
+        // and throw IMSE. (obj_type is null for Phi nodes, so let's just be conservative).
+        maybe_inlinetype = true;
+      }
+    }
+  }
+
   // save state before locking in case of deoptimization after a NullPointerException
   ValueStack* state_before = copy_state_for_exception_with_bci(bci);
-  append_with_bci(new MonitorEnter(x, state()->lock(x), state_before), bci);
+  append_with_bci(new MonitorEnter(x, state()->lock(x), state_before, maybe_inlinetype), bci);
   kill_all();
 }
 
 
 void GraphBuilder::monitorexit(Value x, int bci) {
@@ -2462,10 +2726,11 @@
         if (!oc || !oc->value()->is_null_object()) {
           return;
         }
       }
     }
+    if (value->is_null_free()) return;
   }
   append(new NullCheck(value, copy_state_for_exception()));
 }
 
 
@@ -2487,11 +2752,13 @@
 
   assert(cur_state != nullptr, "state_before must be set");
   do {
     int cur_bci = cur_state->bci();
     assert(cur_scope_data->scope() == cur_state->scope(), "scopes do not match");
-    assert(cur_bci == SynchronizationEntryBCI || cur_bci == cur_scope_data->stream()->cur_bci(), "invalid bci");
+    assert(cur_bci == SynchronizationEntryBCI || cur_bci == cur_scope_data->stream()->cur_bci()
+           || has_pending_field_access() || has_pending_load_indexed(), "invalid bci");
+
 
     // join with all potential exception handlers
     XHandlers* list = cur_scope_data->xhandlers();
     const int n = list->length();
     for (int i = 0; i < n; i++) {
@@ -3269,11 +3536,12 @@
 
   // Set up locals for receiver
   int idx = 0;
   if (!method()->is_static()) {
     // we should always see the receiver
-    state->store_local(idx, new Local(method()->holder(), objectType, idx, true));
+    state->store_local(idx, new Local(method()->holder(), objectType, idx,
+             /*receiver*/ true, /*null_free*/ method()->holder()->is_flat_array_klass()));
     idx = 1;
   }
 
   // Set up locals for incoming arguments
   ciSignature* sig = method()->signature();
@@ -3281,11 +3549,11 @@
     ciType* type = sig->type_at(i);
     BasicType basic_type = type->basic_type();
     // don't allow T_ARRAY to propagate into locals types
     if (is_reference_type(basic_type)) basic_type = T_OBJECT;
     ValueType* vt = as_ValueType(basic_type);
-    state->store_local(idx, new Local(type, vt, idx, false));
+    state->store_local(idx, new Local(type, vt, idx, false, false));
     idx += type->size();
   }
 
   // lock synchronized method
   if (method()->is_synchronized()) {
@@ -3301,10 +3569,12 @@
   , _compilation(compilation)
   , _memory(new MemoryBuffer())
   , _inline_bailout_msg(nullptr)
   , _instruction_count(0)
   , _osr_entry(nullptr)
+  , _pending_field_access(nullptr)
+  , _pending_load_indexed(nullptr)
 {
   int osr_bci = compilation->osr_bci();
 
   // determine entry points and bci2block mapping
   BlockListBuilder blm(compilation, scope, osr_bci);
diff a/src/hotspot/share/c1/c1_GraphBuilder.hpp b/src/hotspot/share/c1/c1_GraphBuilder.hpp
--- a/src/hotspot/share/c1/c1_GraphBuilder.hpp
+++ b/src/hotspot/share/c1/c1_GraphBuilder.hpp
@@ -33,10 +33,25 @@
 #include "ci/ciStreams.hpp"
 #include "compiler/compileLog.hpp"
 
 class MemoryBuffer;
 
+class DelayedFieldAccess : public CompilationResourceObj {
+private:
+  Value            _obj;
+  ciInstanceKlass* _holder;
+  int              _offset;
+public:
+  DelayedFieldAccess(Value obj, ciInstanceKlass* holder, int offset)
+  : _obj(obj), _holder(holder) , _offset(offset) { }
+
+  Value obj() const               { return _obj; }
+  ciInstanceKlass* holder() const { return _holder; }
+  int offset() const              { return _offset; }
+  void inc_offset(int offset)     { _offset += offset; }
+};
+
 class GraphBuilder {
   friend class JfrResolution;
  private:
   // Per-scope data. These are pushed and popped as we descend into
   // inlined methods. Currently in order to generate good code in the
@@ -190,10 +205,14 @@
   BlockBegin*       _block;                      // the current block
   ValueStack*       _state;                      // the current execution state
   Instruction*      _last;                       // the last instruction added
   bool              _skip_block;                 // skip processing of the rest of this block
 
+  // support for optimization of accesses to flat fields and flat arrays
+  DelayedFieldAccess* _pending_field_access;
+  DelayedLoadIndexed* _pending_load_indexed;
+
   // accessors
   ScopeData*        scope_data() const           { return _scope_data; }
   Compilation*      compilation() const          { return _compilation; }
   BlockList*        bci2block() const            { return scope_data()->bci2block(); }
   ValueMap*         vmap() const                 { assert(UseLocalValueNumbering, "should not access otherwise"); return _vmap; }
@@ -207,10 +226,16 @@
   ciBytecodeStream* stream() const               { return scope_data()->stream(); }
   Instruction*      last() const                 { return _last; }
   Bytecodes::Code   code() const                 { return stream()->cur_bc(); }
   int               bci() const                  { return stream()->cur_bci(); }
   int               next_bci() const             { return stream()->next_bci(); }
+  bool              has_pending_field_access()   { return _pending_field_access != nullptr; }
+  DelayedFieldAccess* pending_field_access()     { return _pending_field_access; }
+  void              set_pending_field_access(DelayedFieldAccess* delayed) { _pending_field_access = delayed; }
+  bool              has_pending_load_indexed()   { return _pending_load_indexed != nullptr; }
+  DelayedLoadIndexed* pending_load_indexed()     { return _pending_load_indexed; }
+  void              set_pending_load_indexed(DelayedLoadIndexed* delayed) { _pending_load_indexed = delayed; }
 
   // unified bailout support
   void bailout(const char* msg) const            { compilation()->bailout(msg); }
   bool bailed_out() const                        { return compilation()->bailed_out(); }
 
@@ -266,10 +291,13 @@
   void monitorexit(Value x, int bci);
   void new_multi_array(int dimensions);
   void throw_op(int bci);
   Value round_fp(Value fp_value);
 
+  // inline types
+  void copy_inline_content(ciInlineKlass* vk, Value src, int src_off, Value dest, int dest_off, ValueStack* state_before, ciField* encloding_field = nullptr);
+
   // stack/code manipulation helpers
   Instruction* append_with_bci(Instruction* instr, int bci);
   Instruction* append(Instruction* instr);
   Instruction* append_split(StateSplit* instr);
 
@@ -393,10 +421,11 @@
   bool profile_inlined_calls() { return _compilation->profile_inlined_calls(); }
   bool profile_checkcasts()    { return _compilation->profile_checkcasts();    }
   bool profile_parameters()    { return _compilation->profile_parameters();    }
   bool profile_arguments()     { return _compilation->profile_arguments();     }
   bool profile_return()        { return _compilation->profile_return();        }
+  bool profile_array_accesses(){ return _compilation->profile_array_accesses();}
 
   Values* args_list_for_profiling(ciMethod* target, int& start, bool may_have_receiver);
   Values* collect_args_for_profiling(Values* args, ciMethod* target, bool may_have_receiver);
   void check_args_for_profiling(Values* obj_args, int expected);
 
diff a/src/hotspot/share/c1/c1_IR.cpp b/src/hotspot/share/c1/c1_IR.cpp
--- a/src/hotspot/share/c1/c1_IR.cpp
+++ b/src/hotspot/share/c1/c1_IR.cpp
@@ -169,20 +169,22 @@
   return my_max + callee_max;
 }
 
 
 bool IRScopeDebugInfo::should_reexecute() {
+  if (_should_reexecute) {
+    return true;
+  }
   ciMethod* cur_method = scope()->method();
   int       cur_bci    = bci();
   if (cur_method != nullptr && cur_bci != SynchronizationEntryBCI) {
     Bytecodes::Code code = cur_method->java_code_at_bci(cur_bci);
     return Interpreter::bytecode_should_reexecute(code);
   } else
     return false;
 }
 
-
 // Implementation of CodeEmitInfo
 
 // Stack must be NON-null
 CodeEmitInfo::CodeEmitInfo(ValueStack* stack, XHandlers* exception_handlers, bool deoptimize_on_exception)
   : _scope_debug_info(nullptr)
@@ -212,15 +214,15 @@
     _exception_handlers = new XHandlers(info->_exception_handlers);
   }
 }
 
 
-void CodeEmitInfo::record_debug_info(DebugInformationRecorder* recorder, int pc_offset) {
+void CodeEmitInfo::record_debug_info(DebugInformationRecorder* recorder, int pc_offset, bool maybe_return_as_fields) {
   // record the safepoint before recording the debug info for enclosing scopes
   recorder->add_safepoint(pc_offset, _oop_map->deep_copy());
   bool reexecute = _force_reexecute || _scope_debug_info->should_reexecute();
-  _scope_debug_info->record_debug_info(recorder, pc_offset, reexecute, _is_method_handle_invoke);
+  _scope_debug_info->record_debug_info(recorder, pc_offset, reexecute, _is_method_handle_invoke, maybe_return_as_fields);
   recorder->end_safepoint(pc_offset);
 }
 
 
 void CodeEmitInfo::add_register_oop(LIR_Opr opr) {
diff a/src/hotspot/share/c1/c1_IR.hpp b/src/hotspot/share/c1/c1_IR.hpp
--- a/src/hotspot/share/c1/c1_IR.hpp
+++ b/src/hotspot/share/c1/c1_IR.hpp
@@ -204,24 +204,27 @@
   int                           _bci;
   GrowableArray<ScopeValue*>*   _locals;
   GrowableArray<ScopeValue*>*   _expressions;
   GrowableArray<MonitorValue*>* _monitors;
   IRScopeDebugInfo*             _caller;
+  bool                          _should_reexecute;
 
  public:
   IRScopeDebugInfo(IRScope*                      scope,
                    int                           bci,
                    GrowableArray<ScopeValue*>*   locals,
                    GrowableArray<ScopeValue*>*   expressions,
                    GrowableArray<MonitorValue*>* monitors,
-                   IRScopeDebugInfo*             caller):
+                   IRScopeDebugInfo*             caller,
+                   bool                          should_reexecute):
       _scope(scope)
     , _bci(bci)
     , _locals(locals)
     , _expressions(expressions)
     , _monitors(monitors)
-    , _caller(caller) {}
+    , _caller(caller)
+    , _should_reexecute(should_reexecute) {}
 
 
   IRScope*                      scope()       { return _scope;       }
   int                           bci()         { return _bci;         }
   GrowableArray<ScopeValue*>*   locals()      { return _locals;      }
@@ -230,25 +233,30 @@
   IRScopeDebugInfo*             caller()      { return _caller;      }
 
   //Whether we should reexecute this bytecode for deopt
   bool should_reexecute();
 
-  void record_debug_info(DebugInformationRecorder* recorder, int pc_offset, bool reexecute, bool is_method_handle_invoke = false) {
+  void record_debug_info(DebugInformationRecorder* recorder, int pc_offset, bool reexecute, bool is_method_handle_invoke = false, bool maybe_return_as_fields = false) {
     if (caller() != nullptr) {
       // Order is significant:  Must record caller first.
       caller()->record_debug_info(recorder, pc_offset, false/*reexecute*/);
     }
     DebugToken* locvals = recorder->create_scope_values(locals());
     DebugToken* expvals = recorder->create_scope_values(expressions());
     DebugToken* monvals = recorder->create_monitor_values(monitors());
     // reexecute allowed only for the topmost frame
-    bool return_oop = false; // This flag will be ignored since it used only for C2 with escape analysis.
+    bool return_oop = false;
+    bool return_scalarized = false;
+    if (maybe_return_as_fields) {
+      return_oop = true;
+      return_scalarized = true;
+    }
     bool rethrow_exception = false;
     bool has_ea_local_in_scope = false;
     bool arg_escape = false;
     recorder->describe_scope(pc_offset, methodHandle(), scope()->method(), bci(),
-                             reexecute, rethrow_exception, is_method_handle_invoke, return_oop,
+                             reexecute, rethrow_exception, is_method_handle_invoke, return_oop, return_scalarized,
                              has_ea_local_in_scope, arg_escape, locvals, expvals, monvals);
   }
 };
 
 
@@ -282,11 +290,11 @@
   XHandlers* exception_handlers() const          { return _exception_handlers; }
   ValueStack* stack() const                      { return _stack; }
   bool deoptimize_on_exception() const           { return _deoptimize_on_exception; }
 
   void add_register_oop(LIR_Opr opr);
-  void record_debug_info(DebugInformationRecorder* recorder, int pc_offset);
+  void record_debug_info(DebugInformationRecorder* recorder, int pc_offset, bool maybe_return_as_fields = false);
 
   bool     is_method_handle_invoke() const { return _is_method_handle_invoke;     }
   void set_is_method_handle_invoke(bool x) {        _is_method_handle_invoke = x; }
 
   bool     force_reexecute() const         { return _force_reexecute;             }
diff a/src/hotspot/share/c1/c1_Instruction.cpp b/src/hotspot/share/c1/c1_Instruction.cpp
--- a/src/hotspot/share/c1/c1_Instruction.cpp
+++ b/src/hotspot/share/c1/c1_Instruction.cpp
@@ -25,10 +25,12 @@
 #include "precompiled.hpp"
 #include "c1/c1_IR.hpp"
 #include "c1/c1_Instruction.hpp"
 #include "c1/c1_InstructionPrinter.hpp"
 #include "c1/c1_ValueStack.hpp"
+#include "ci/ciFlatArrayKlass.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "ci/ciObjArrayKlass.hpp"
 #include "ci/ciTypeArrayKlass.hpp"
 #include "utilities/bitMap.inline.hpp"
 
 
@@ -104,17 +106,78 @@
     exception_state()->values_do(f);
   }
 }
 
 ciType* Instruction::exact_type() const {
-  ciType* t =  declared_type();
+  ciType* t = declared_type();
   if (t != nullptr && t->is_klass()) {
     return t->as_klass()->exact_klass();
   }
   return nullptr;
 }
 
+ciKlass* Instruction::as_loaded_klass_or_null() const {
+  ciType* type = declared_type();
+  if (type != nullptr && type->is_klass()) {
+    ciKlass* klass = type->as_klass();
+    if (klass->is_loaded()) {
+      return klass;
+    }
+  }
+  return nullptr;
+}
+
+bool Instruction::is_loaded_flat_array() const {
+  if (UseFlatArray) {
+    ciType* type = declared_type();
+    return type != nullptr && type->is_flat_array_klass();
+  }
+  return false;
+}
+
+bool Instruction::maybe_flat_array() {
+  if (UseFlatArray) {
+    ciType* type = declared_type();
+    if (type != nullptr) {
+      if (type->is_obj_array_klass()) {
+        // TODO 8325106 Fix comment
+        // The runtime type of [LMyValue might be [QMyValue due to [QMyValue <: [LMyValue.
+        ciKlass* element_klass = type->as_obj_array_klass()->element_klass();
+        if (element_klass->can_be_inline_klass() && (!element_klass->is_inlinetype() || element_klass->as_inline_klass()->flat_in_array())) {
+          return true;
+        }
+      } else if (type->is_flat_array_klass()) {
+        return true;
+      } else if (type->is_klass() && type->as_klass()->is_java_lang_Object()) {
+        // This can happen as a parameter to System.arraycopy()
+        return true;
+      }
+    } else {
+      // Type info gets lost during Phi merging (Phi, IfOp, etc), but we might be storing into a
+      // flat array, so we should do a runtime check.
+      return true;
+    }
+  }
+  return false;
+}
+
+bool Instruction::maybe_null_free_array() {
+  ciType* type = declared_type();
+  if (type != nullptr) {
+    if (type->is_obj_array_klass()) {
+      // Due to array covariance, the runtime type might be a null-free array.
+      if (type->as_obj_array_klass()->can_be_inline_array_klass()) {
+        return true;
+      }
+    }
+  } else {
+    // Type info gets lost during Phi merging (Phi, IfOp, etc), but we might be storing into a
+    // null-free array, so we should do a runtime check.
+    return true;
+  }
+  return false;
+}
 
 #ifndef PRODUCT
 void Instruction::check_state(ValueStack* state) {
   if (state != nullptr) {
     state->verify();
@@ -171,11 +234,11 @@
   return nullptr;
 }
 
 ciType* LoadIndexed::exact_type() const {
   ciType* array_type = array()->exact_type();
-  if (array_type != nullptr) {
+  if (delayed() == nullptr && array_type != nullptr) {
     assert(array_type->is_array_klass(), "what else?");
     ciArrayKlass* ak = (ciArrayKlass*)array_type;
 
     if (ak->element_type()->is_instance_klass()) {
       ciInstanceKlass* ik = (ciInstanceKlass*)ak->element_type();
@@ -185,21 +248,37 @@
     }
   }
   return Instruction::exact_type();
 }
 
-
 ciType* LoadIndexed::declared_type() const {
+  if (delayed() != nullptr) {
+    return delayed()->field()->type();
+  }
   ciType* array_type = array()->declared_type();
   if (array_type == nullptr || !array_type->is_loaded()) {
     return nullptr;
   }
   assert(array_type->is_array_klass(), "what else?");
   ciArrayKlass* ak = (ciArrayKlass*)array_type;
   return ak->element_type();
 }
 
+bool StoreIndexed::is_exact_flat_array_store() const {
+  if (array()->is_loaded_flat_array() && value()->as_Constant() == nullptr && value()->declared_type() != nullptr) {
+    ciKlass* element_klass = array()->declared_type()->as_flat_array_klass()->element_klass();
+    ciKlass* actual_klass = value()->declared_type()->as_klass();
+
+    // The following check can fail with inlining:
+    //     void test45_inline(Object[] oa, Object o, int index) { oa[index] = o; }
+    //     void test45(MyValue1[] va, int index, MyValue2 v) { test45_inline(va, v, index); }
+    if (element_klass == actual_klass) {
+      return true;
+    }
+  }
+  return false;
+}
 
 ciType* LoadField::declared_type() const {
   return field()->type();
 }
 
@@ -207,11 +286,15 @@
 ciType* NewTypeArray::exact_type() const {
   return ciTypeArrayKlass::make(elt_type());
 }
 
 ciType* NewObjectArray::exact_type() const {
-  return ciObjArrayKlass::make(klass());
+  return ciArrayKlass::make(klass());
+}
+
+ciType* NewMultiArray::exact_type() const {
+  return _klass;
 }
 
 ciType* NewArray::declared_type() const {
   return exact_type();
 }
@@ -317,10 +400,39 @@
     }
   }
 }
 
 
+StoreField::StoreField(Value obj, int offset, ciField* field, Value value, bool is_static,
+                       ValueStack* state_before, bool needs_patching)
+  : AccessField(obj, offset, field, is_static, state_before, needs_patching)
+  , _value(value)
+  , _enclosing_field(nullptr)
+{
+  set_flag(NeedsWriteBarrierFlag, as_ValueType(field_type())->is_object());
+#ifdef ASSERT
+  AssertValues assert_value;
+  values_do(&assert_value);
+#endif
+  pin();
+}
+
+StoreIndexed::StoreIndexed(Value array, Value index, Value length, BasicType elt_type, Value value,
+                           ValueStack* state_before, bool check_boolean, bool mismatched)
+  : AccessIndexed(array, index, length, elt_type, state_before, mismatched)
+  , _value(value), _check_boolean(check_boolean)
+{
+  set_flag(NeedsWriteBarrierFlag, (as_ValueType(elt_type)->is_object()));
+  set_flag(NeedsStoreCheckFlag, (as_ValueType(elt_type)->is_object()));
+#ifdef ASSERT
+  AssertValues assert_value;
+  values_do(&assert_value);
+#endif
+  pin();
+}
+
+
 // Implementation of Invoke
 
 
 Invoke::Invoke(Bytecodes::Code code, ValueType* result_type, Value recv, Values* args,
                ciMethod* target, ValueStack* state_before)
@@ -343,11 +455,12 @@
   _signature = new BasicTypeList(number_of_arguments() + (has_receiver() ? 1 : 0));
   if (has_receiver()) {
     _signature->append(as_BasicType(receiver()->type()));
   }
   for (int i = 0; i < number_of_arguments(); i++) {
-    ValueType* t = argument_at(i)->type();
+    Value v = argument_at(i);
+    ValueType* t = v->type();
     BasicType bt = as_BasicType(t);
     _signature->append(bt);
   }
 }
 
@@ -986,5 +1099,6 @@
 }
 
 void ProfileInvoke::state_values_do(ValueVisitor* f) {
   if (state() != nullptr) state()->values_do(f);
 }
+
diff a/src/hotspot/share/c1/c1_Instruction.hpp b/src/hotspot/share/c1/c1_Instruction.hpp
--- a/src/hotspot/share/c1/c1_Instruction.hpp
+++ b/src/hotspot/share/c1/c1_Instruction.hpp
@@ -72,10 +72,11 @@
 class     NewInstance;
 class     NewArray;
 class       NewTypeArray;
 class       NewObjectArray;
 class       NewMultiArray;
+class     Deoptimize;
 class     TypeCheck;
 class       CheckCast;
 class       InstanceOf;
 class     AccessMonitor;
 class       MonitorEnter;
@@ -96,10 +97,11 @@
 class     UnsafeGet;
 class     UnsafePut;
 class     UnsafeGetAndSet;
 class   ProfileCall;
 class   ProfileReturnType;
+class   ProfileACmpTypes;
 class   ProfileInvoke;
 class   RuntimeCall;
 class   MemBar;
 class   RangeCheckPredicate;
 #ifdef ASSERT
@@ -191,10 +193,11 @@
   virtual void do_UnsafeGet      (UnsafeGet*       x) = 0;
   virtual void do_UnsafePut      (UnsafePut*       x) = 0;
   virtual void do_UnsafeGetAndSet(UnsafeGetAndSet* x) = 0;
   virtual void do_ProfileCall    (ProfileCall*     x) = 0;
   virtual void do_ProfileReturnType (ProfileReturnType*  x) = 0;
+  virtual void do_ProfileACmpTypes(ProfileACmpTypes*  x) = 0;
   virtual void do_ProfileInvoke  (ProfileInvoke*   x) = 0;
   virtual void do_RuntimeCall    (RuntimeCall*     x) = 0;
   virtual void do_MemBar         (MemBar*          x) = 0;
   virtual void do_RangeCheckPredicate(RangeCheckPredicate* x) = 0;
 #ifdef ASSERT
@@ -207,13 +210,14 @@
 //
 // Note: This hash functions affect the performance
 //       of ValueMap - make changes carefully!
 
 #define HASH1(x1            )                    ((intx)(x1))
-#define HASH2(x1, x2        )                    ((HASH1(x1        ) << 7) ^ HASH1(x2))
-#define HASH3(x1, x2, x3    )                    ((HASH2(x1, x2    ) << 7) ^ HASH1(x3))
-#define HASH4(x1, x2, x3, x4)                    ((HASH3(x1, x2, x3) << 7) ^ HASH1(x4))
+#define HASH2(x1, x2        )                    ((HASH1(x1            ) << 7) ^ HASH1(x2))
+#define HASH3(x1, x2, x3    )                    ((HASH2(x1, x2        ) << 7) ^ HASH1(x3))
+#define HASH4(x1, x2, x3, x4)                    ((HASH3(x1, x2, x3    ) << 7) ^ HASH1(x4))
+#define HASH5(x1, x2, x3, x4, x5)                ((HASH4(x1, x2, x3, x4) << 7) ^ HASH1(x5))
 
 
 // The following macros are used to implement instruction-specific hashing.
 // By default, each instruction implements hash() and is_equal(Value), used
 // for value numbering/common subexpression elimination. The default imple-
@@ -268,10 +272,25 @@
     if (f2 != _v->f2) return false;                   \
     if (f3 != _v->f3) return false;                   \
     return true;                                      \
   }                                                   \
 
+#define HASHING4(class_name, enabled, f1, f2, f3, f4) \
+  virtual intx hash() const {                         \
+    return (enabled) ? HASH5(name(), f1, f2, f3, f4) : 0; \
+  }                                                   \
+  virtual bool is_equal(Value v) const {              \
+    if (!(enabled)  ) return false;                   \
+    class_name* _v = v->as_##class_name();            \
+    if (_v == nullptr  ) return false;                   \
+    if (f1 != _v->f1) return false;                   \
+    if (f2 != _v->f2) return false;                   \
+    if (f3 != _v->f3) return false;                   \
+    if (f4 != _v->f4) return false;                   \
+    return true;                                      \
+  }                                                   \
+
 
 // The mother of all instructions...
 
 class Instruction: public CompilationResourceObj {
  private:
@@ -290,10 +309,11 @@
   ValueStack*  _state_before;                    // Copy of state with input operands still on stack (or null)
   ValueStack*  _exception_state;                 // Copy of state for exception handling
   XHandlers*   _exception_handlers;              // Flat list of exception handlers covering this instruction
 
   friend class UseCountComputer;
+  friend class GraphBuilder;
 
   void update_exception_state(ValueStack* state);
 
  protected:
   BlockBegin*  _block;                           // Block that contains this instruction
@@ -342,10 +362,11 @@
 
   static const int no_bci = -99;
 
   enum InstructionFlag {
     NeedsNullCheckFlag = 0,
+    NeverNullFlag,          // For "Q" signatures
     CanTrapFlag,
     DirectCompareFlag,
     IsEliminatedFlag,
     IsSafepointFlag,
     IsStaticFlag,
@@ -435,20 +456,23 @@
   Instruction* subst()                           { return _subst == nullptr ? this : _subst->subst(); }
   LIR_Opr operand() const                        { return _operand; }
 
   void set_needs_null_check(bool f)              { set_flag(NeedsNullCheckFlag, f); }
   bool needs_null_check() const                  { return check_flag(NeedsNullCheckFlag); }
+  void set_null_free(bool f)                     { set_flag(NeverNullFlag, f); }
+  bool is_null_free() const                      { return check_flag(NeverNullFlag); }
   bool is_linked() const                         { return check_flag(IsLinkedInBlockFlag); }
   bool can_be_linked()                           { return as_Local() == nullptr && as_Phi() == nullptr; }
 
   bool is_null_obj()                             { return as_Constant() != nullptr && type()->as_ObjectType()->constant_value()->is_null_object(); }
 
   bool has_uses() const                          { return use_count() > 0; }
   ValueStack* state_before() const               { return _state_before; }
   ValueStack* exception_state() const            { return _exception_state; }
   virtual bool needs_exception_state() const     { return true; }
   XHandlers* exception_handlers() const          { return _exception_handlers; }
+  ciKlass* as_loaded_klass_or_null() const;
 
   // manipulation
   void pin(PinReason reason)                     { _pin_state |= reason; }
   void pin()                                     { _pin_state |= PinUnknown; }
   // DANGEROUS: only used by EliminateStores
@@ -489,10 +513,14 @@
     set_next(i);
     i->set_next(n);
     return _next;
   }
 
+  bool is_loaded_flat_array() const;
+  bool maybe_flat_array();
+  bool maybe_null_free_array();
+
   Instruction *insert_after_same_bci(Instruction *i) {
 #ifndef PRODUCT
     i->set_printable_bci(printable_bci());
 #endif
     return insert_after(i);
@@ -688,16 +716,17 @@
   int      _java_index;                          // the local index within the method to which the local belongs
   bool     _is_receiver;                         // if local variable holds the receiver: "this" for non-static methods
   ciType*  _declared_type;
  public:
   // creation
-  Local(ciType* declared, ValueType* type, int index, bool receiver)
+  Local(ciType* declared, ValueType* type, int index, bool receiver, bool null_free)
     : Instruction(type)
     , _java_index(index)
     , _is_receiver(receiver)
     , _declared_type(declared)
   {
+    set_null_free(null_free);
     NOT_PRODUCT(set_printable_bci(-1));
   }
 
   // accessors
   int java_index() const                         { return _java_index; }
@@ -815,13 +844,16 @@
 
 LEAF(LoadField, AccessField)
  public:
   // creation
   LoadField(Value obj, int offset, ciField* field, bool is_static,
-            ValueStack* state_before, bool needs_patching)
+            ValueStack* state_before, bool needs_patching,
+            ciInlineKlass* inline_klass = nullptr, Value default_value = nullptr )
   : AccessField(obj, offset, field, is_static, state_before, needs_patching)
-  {}
+  {
+    set_null_free(field->is_null_free());
+  }
 
   ciType* declared_type() const;
 
   // generic; cannot be eliminated if needs patching or if volatile.
   HASHING3(LoadField, !needs_patching() && !field()->is_volatile(), obj()->subst(), offset(), declared_type())
@@ -829,26 +861,22 @@
 
 
 LEAF(StoreField, AccessField)
  private:
   Value _value;
+  ciField* _enclosing_field;   // enclosing field (the flat one) for nested fields
 
  public:
   // creation
   StoreField(Value obj, int offset, ciField* field, Value value, bool is_static,
-             ValueStack* state_before, bool needs_patching)
-  : AccessField(obj, offset, field, is_static, state_before, needs_patching)
-  , _value(value)
-  {
-    set_flag(NeedsWriteBarrierFlag, as_ValueType(field_type())->is_object());
-    ASSERT_VALUES
-    pin();
-  }
+             ValueStack* state_before, bool needs_patching);
 
   // accessors
   Value value() const                            { return _value; }
   bool needs_write_barrier() const               { return check_flag(NeedsWriteBarrierFlag); }
+  ciField* enclosing_field() const               { return _enclosing_field; }
+  void set_enclosing_field(ciField* field)       { _enclosing_field = field; }
 
   // generic
   virtual void input_values_do(ValueVisitor* f)   { AccessField::input_values_do(f); f->visit(&_value); }
 };
 
@@ -902,19 +930,22 @@
  private:
   Value     _index;
   Value     _length;
   BasicType _elt_type;
   bool      _mismatched;
+  ciMethod* _profiled_method;
+  int       _profiled_bci;
 
  public:
   // creation
   AccessIndexed(Value array, Value index, Value length, BasicType elt_type, ValueStack* state_before, bool mismatched)
   : AccessArray(as_ValueType(elt_type), array, state_before)
   , _index(index)
   , _length(length)
   , _elt_type(elt_type)
   , _mismatched(mismatched)
+  , _profiled_method(nullptr), _profiled_bci(0)
   {
     set_flag(Instruction::NeedsRangeCheckFlag, true);
     ASSERT_VALUES
   }
 
@@ -926,24 +957,36 @@
 
   void clear_length()                            { _length = nullptr; }
   // perform elimination of range checks involving constants
   bool compute_needs_range_check();
 
+  // Helpers for MethodData* profiling
+  void set_should_profile(bool value)                { set_flag(ProfileMDOFlag, value); }
+  void set_profiled_method(ciMethod* method)         { _profiled_method = method;   }
+  void set_profiled_bci(int bci)                     { _profiled_bci = bci;         }
+  bool      should_profile() const                   { return check_flag(ProfileMDOFlag); }
+  ciMethod* profiled_method() const                  { return _profiled_method;     }
+  int       profiled_bci() const                     { return _profiled_bci;        }
+
+
   // generic
   virtual void input_values_do(ValueVisitor* f)   { AccessArray::input_values_do(f); f->visit(&_index); if (_length != nullptr) f->visit(&_length); }
 };
 
+class DelayedLoadIndexed;
 
 LEAF(LoadIndexed, AccessIndexed)
  private:
   NullCheck*  _explicit_null_check;              // For explicit null check elimination
+  NewInstance* _vt;
+  DelayedLoadIndexed* _delayed;
 
  public:
   // creation
   LoadIndexed(Value array, Value index, Value length, BasicType elt_type, ValueStack* state_before, bool mismatched = false)
   : AccessIndexed(array, index, length, elt_type, state_before, mismatched)
-  , _explicit_null_check(nullptr) {}
+  , _explicit_null_check(nullptr), _vt(nullptr), _delayed(nullptr) {}
 
   // accessors
   NullCheck* explicit_null_check() const         { return _explicit_null_check; }
 
   // setters
@@ -951,48 +994,63 @@
   void set_explicit_null_check(NullCheck* check) { _explicit_null_check = check; }
 
   ciType* exact_type() const;
   ciType* declared_type() const;
 
+  NewInstance* vt() const { return _vt; }
+  void set_vt(NewInstance* vt) { _vt = vt; }
+
+  DelayedLoadIndexed* delayed() const { return _delayed; }
+  void set_delayed(DelayedLoadIndexed* delayed) { _delayed = delayed; }
+
   // generic;
-  HASHING3(LoadIndexed, true, elt_type(), array()->subst(), index()->subst())
+  HASHING4(LoadIndexed, delayed() == nullptr && !should_profile(), elt_type(), array()->subst(), index()->subst(), vt())
 };
 
+class DelayedLoadIndexed : public CompilationResourceObj {
+private:
+  LoadIndexed* _load_instr;
+  ValueStack* _state_before;
+  ciField* _field;
+  int _offset;
+ public:
+  DelayedLoadIndexed(LoadIndexed* load, ValueStack* state_before)
+  : _load_instr(load)
+  , _state_before(state_before)
+  , _field(nullptr)
+  , _offset(0) { }
+
+  void update(ciField* field, int offset) {
+    _field = field;
+    _offset += offset;
+  }
+
+  LoadIndexed* load_instr() const { return _load_instr; }
+  ValueStack* state_before() const { return _state_before; }
+  ciField* field() const { return _field; }
+  int offset() const { return _offset; }
+};
 
 LEAF(StoreIndexed, AccessIndexed)
  private:
   Value       _value;
 
-  ciMethod* _profiled_method;
-  int       _profiled_bci;
   bool      _check_boolean;
 
  public:
   // creation
   StoreIndexed(Value array, Value index, Value length, BasicType elt_type, Value value, ValueStack* state_before,
-               bool check_boolean, bool mismatched = false)
-  : AccessIndexed(array, index, length, elt_type, state_before, mismatched)
-  , _value(value), _profiled_method(nullptr), _profiled_bci(0), _check_boolean(check_boolean)
-  {
-    set_flag(NeedsWriteBarrierFlag, (as_ValueType(elt_type)->is_object()));
-    set_flag(NeedsStoreCheckFlag, (as_ValueType(elt_type)->is_object()));
-    ASSERT_VALUES
-    pin();
-  }
+               bool check_boolean, bool mismatched = false);
 
   // accessors
   Value value() const                            { return _value; }
   bool needs_write_barrier() const               { return check_flag(NeedsWriteBarrierFlag); }
   bool needs_store_check() const                 { return check_flag(NeedsStoreCheckFlag); }
   bool check_boolean() const                     { return _check_boolean; }
-  // Helpers for MethodData* profiling
-  void set_should_profile(bool value)                { set_flag(ProfileMDOFlag, value); }
-  void set_profiled_method(ciMethod* method)         { _profiled_method = method;   }
-  void set_profiled_bci(int bci)                     { _profiled_bci = bci;         }
-  bool      should_profile() const                   { return check_flag(ProfileMDOFlag); }
-  ciMethod* profiled_method() const                  { return _profiled_method;     }
-  int       profiled_bci() const                     { return _profiled_bci;        }
+
+  // Flattened array support
+  bool is_exact_flat_array_store() const;
   // generic
   virtual void input_values_do(ValueVisitor* f)   { AccessIndexed::input_values_do(f); f->visit(&_value); }
 };
 
 
@@ -1099,29 +1157,32 @@
 
 LEAF(IfOp, Op2)
  private:
   Value _tval;
   Value _fval;
+  bool _substitutability_check;
 
  public:
   // creation
-  IfOp(Value x, Condition cond, Value y, Value tval, Value fval)
+  IfOp(Value x, Condition cond, Value y, Value tval, Value fval, ValueStack* state_before, bool substitutability_check)
   : Op2(tval->type()->meet(fval->type()), (Bytecodes::Code)cond, x, y)
   , _tval(tval)
   , _fval(fval)
+  , _substitutability_check(substitutability_check)
   {
     ASSERT_VALUES
     assert(tval->type()->tag() == fval->type()->tag(), "types must match");
+    set_state_before(state_before);
   }
 
   // accessors
   virtual bool is_commutative() const;
   Bytecodes::Code op() const                     { ShouldNotCallThis(); return Bytecodes::_illegal; }
   Condition cond() const                         { return (Condition)Op2::op(); }
   Value tval() const                             { return _tval; }
   Value fval() const                             { return _fval; }
-
+  bool substitutability_check() const             { return _substitutability_check; }
   // generic
   virtual void input_values_do(ValueVisitor* f)   { Op2::input_values_do(f); f->visit(&_tval); f->visit(&_fval); }
 };
 
 
@@ -1274,31 +1335,32 @@
 
 LEAF(NewInstance, StateSplit)
  private:
   ciInstanceKlass* _klass;
   bool _is_unresolved;
+  bool _needs_state_before;
 
  public:
   // creation
-  NewInstance(ciInstanceKlass* klass, ValueStack* state_before, bool is_unresolved)
+  NewInstance(ciInstanceKlass* klass, ValueStack* state_before, bool is_unresolved, bool needs_state_before)
   : StateSplit(instanceType, state_before)
-  , _klass(klass), _is_unresolved(is_unresolved)
+  , _klass(klass), _is_unresolved(is_unresolved), _needs_state_before(needs_state_before)
   {}
 
   // accessors
   ciInstanceKlass* klass() const                 { return _klass; }
   bool is_unresolved() const                     { return _is_unresolved; }
+  bool needs_state_before() const                { return _needs_state_before; }
 
   virtual bool needs_exception_state() const     { return false; }
 
   // generic
   virtual bool can_trap() const                  { return true; }
   ciType* exact_type() const;
   ciType* declared_type() const;
 };
 
-
 BASE(NewArray, StateSplit)
  private:
   Value       _length;
 
  public:
@@ -1345,11 +1407,12 @@
  private:
   ciKlass* _klass;
 
  public:
   // creation
-  NewObjectArray(ciKlass* klass, Value length, ValueStack* state_before) : NewArray(length, state_before), _klass(klass) {}
+  NewObjectArray(ciKlass* klass, Value length, ValueStack* state_before)
+  : NewArray(length, state_before), _klass(klass) { }
 
   // accessors
   ciKlass* klass() const                         { return _klass; }
   ciType* exact_type() const;
 };
@@ -1380,10 +1443,12 @@
     // get updated, and the value must not be traversed twice. Was bug
     // - kbr 4/10/2001
     StateSplit::input_values_do(f);
     for (int i = 0; i < _dims->length(); i++) f->visit(_dims->adr_at(i));
   }
+
+  ciType* exact_type() const;
 };
 
 
 BASE(TypeCheck, StateSplit)
  private:
@@ -1427,11 +1492,11 @@
 
 LEAF(CheckCast, TypeCheck)
  public:
   // creation
   CheckCast(ciKlass* klass, Value obj, ValueStack* state_before)
-  : TypeCheck(klass, obj, objectType, state_before) {}
+  : TypeCheck(klass, obj, objectType, state_before) { }
 
   void set_incompatible_class_change_check() {
     set_flag(ThrowIncompatibleClassChangeErrorFlag, true);
   }
   bool is_incompatible_class_change_check() const {
@@ -1485,18 +1550,23 @@
   virtual void input_values_do(ValueVisitor* f)   { StateSplit::input_values_do(f); f->visit(&_obj); }
 };
 
 
 LEAF(MonitorEnter, AccessMonitor)
+  bool _maybe_inlinetype;
  public:
   // creation
-  MonitorEnter(Value obj, int monitor_no, ValueStack* state_before)
+  MonitorEnter(Value obj, int monitor_no, ValueStack* state_before, bool maybe_inlinetype)
   : AccessMonitor(obj, monitor_no, state_before)
+  , _maybe_inlinetype(maybe_inlinetype)
   {
     ASSERT_VALUES
   }
 
+  // accessors
+  bool maybe_inlinetype() const                   { return _maybe_inlinetype; }
+
   // generic
   virtual bool can_trap() const                  { return true; }
 };
 
 
@@ -1952,21 +2022,23 @@
   Value       _y;
   ciMethod*   _profiled_method;
   int         _profiled_bci; // Canonicalizer may alter bci of If node
   bool        _swapped;      // Is the order reversed with respect to the original If in the
                              // bytecode stream?
+  bool        _substitutability_check;
  public:
   // creation
   // unordered_is_true is valid for float/double compares only
-  If(Value x, Condition cond, bool unordered_is_true, Value y, BlockBegin* tsux, BlockBegin* fsux, ValueStack* state_before, bool is_safepoint)
+  If(Value x, Condition cond, bool unordered_is_true, Value y, BlockBegin* tsux, BlockBegin* fsux, ValueStack* state_before, bool is_safepoint, bool substitutability_check=false)
     : BlockEnd(illegalType, state_before, is_safepoint)
   , _x(x)
   , _cond(cond)
   , _y(y)
   , _profiled_method(nullptr)
   , _profiled_bci(0)
   , _swapped(false)
+  , _substitutability_check(substitutability_check)
   {
     ASSERT_VALUES
     set_flag(UnorderedIsTrueFlag, unordered_is_true);
     assert(x->type()->tag() == y->type()->tag(), "types must match");
     BlockList* s = new BlockList(2);
@@ -1997,10 +2069,11 @@
 
   void set_should_profile(bool value)             { set_flag(ProfileMDOFlag, value); }
   void set_profiled_method(ciMethod* method)      { _profiled_method = method; }
   void set_profiled_bci(int bci)                  { _profiled_bci = bci;       }
   void set_swapped(bool value)                    { _swapped = value;         }
+  bool substitutability_check() const              { return _substitutability_check; }
   // generic
   virtual void input_values_do(ValueVisitor* f)   { BlockEnd::input_values_do(f); f->visit(&_x); f->visit(&_y); }
 };
 
 
@@ -2331,11 +2404,11 @@
     , _callee(callee)
     , _bci_of_invoke(bci)
     , _ret(ret)
   {
     set_needs_null_check(true);
-    // The ProfileType has side-effects and must occur precisely where located
+    // The ProfileReturnType has side-effects and must occur precisely where located
     pin();
   }
 
   ciMethod* method()             const { return _method; }
   ciMethod* callee()             const { return _callee; }
@@ -2347,10 +2420,52 @@
       f->visit(&_ret);
     }
   }
 };
 
+LEAF(ProfileACmpTypes, Instruction)
+ private:
+  ciMethod*        _method;
+  int              _bci;
+  Value            _left;
+  Value            _right;
+  bool             _left_maybe_null;
+  bool             _right_maybe_null;
+
+ public:
+  ProfileACmpTypes(ciMethod* method, int bci, Value left, Value right)
+    : Instruction(voidType)
+    , _method(method)
+    , _bci(bci)
+    , _left(left)
+    , _right(right)
+  {
+    // The ProfileACmp has side-effects and must occur precisely where located
+    pin();
+    _left_maybe_null = true;
+    _right_maybe_null = true;
+  }
+
+  ciMethod* method()             const { return _method; }
+  int bci()                      const { return _bci; }
+  Value left()                   const { return _left; }
+  Value right()                  const { return _right; }
+  bool left_maybe_null()         const { return _left_maybe_null; }
+  bool right_maybe_null()        const { return _right_maybe_null; }
+  void set_left_maybe_null(bool v)     { _left_maybe_null = v; }
+  void set_right_maybe_null(bool v)    { _right_maybe_null = v; }
+
+  virtual void input_values_do(ValueVisitor* f)   {
+    if (_left != nullptr) {
+      f->visit(&_left);
+    }
+    if (_right != nullptr) {
+      f->visit(&_right);
+    }
+  }
+};
+
 // Call some C runtime function that doesn't safepoint,
 // optionally passing the current thread as the first argument.
 LEAF(RuntimeCall, Instruction)
  private:
   const char* _entry_name;
diff a/src/hotspot/share/c1/c1_InstructionPrinter.cpp b/src/hotspot/share/c1/c1_InstructionPrinter.cpp
--- a/src/hotspot/share/c1/c1_InstructionPrinter.cpp
+++ b/src/hotspot/share/c1/c1_InstructionPrinter.cpp
@@ -25,10 +25,11 @@
 #include "precompiled.hpp"
 #include "classfile/vmSymbols.hpp"
 #include "c1/c1_InstructionPrinter.hpp"
 #include "c1/c1_ValueStack.hpp"
 #include "ci/ciArray.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "ci/ciInstance.hpp"
 #include "ci/ciObject.hpp"
 
 
 #ifndef PRODUCT
@@ -379,11 +380,16 @@
 }
 
 
 void InstructionPrinter::do_LoadIndexed(LoadIndexed* x) {
   print_indexed(x);
-  output()->print(" (%c)", type2char(x->elt_type()));
+  if (x->delayed() != nullptr) {
+    output()->print(" +%d", x->delayed()->offset());
+    output()->print(" (%c)", type2char(x->delayed()->field()->type()->basic_type()));
+  } else {
+    output()->print(" (%c)", type2char(x->elt_type()));
+  }
   if (x->check_flag(Instruction::NeedsRangeCheckFlag)) {
     output()->print(" [rc]");
   }
 }
 
@@ -493,11 +499,10 @@
   output()->print("new %s array [", basic_type_name(x->elt_type()));
   print_value(x->length());
   output()->put(']');
 }
 
-
 void InstructionPrinter::do_NewObjectArray(NewObjectArray* x) {
   output()->print("new object array [");
   print_value(x->length());
   output()->print("] ");
   print_klass(x->klass());
@@ -513,11 +518,10 @@
   }
   output()->print("] ");
   print_klass(x->klass());
 }
 
-
 void InstructionPrinter::do_MonitorEnter(MonitorEnter* x) {
   output()->print("enter ");
   print_monitor(x);
 }
 
@@ -850,17 +854,25 @@
   output()->print("profile ret type ");
   print_value(x->ret());
   output()->print(" %s.%s", x->method()->holder()->name()->as_utf8(), x->method()->name()->as_utf8());
   output()->put(')');
 }
+
 void InstructionPrinter::do_ProfileInvoke(ProfileInvoke* x) {
   output()->print("profile_invoke ");
   output()->print(" %s.%s", x->inlinee()->holder()->name()->as_utf8(), x->inlinee()->name()->as_utf8());
   output()->put(')');
 
 }
 
+void InstructionPrinter::do_ProfileACmpTypes(ProfileACmpTypes* x) {
+  output()->print("profile acmp types ");
+  print_value(x->left());
+  output()->print(", ");
+  print_value(x->right());
+}
+
 void InstructionPrinter::do_RuntimeCall(RuntimeCall* x) {
   output()->print("call_rt %s(", x->entry_name());
   for (int i = 0; i < x->number_of_arguments(); i++) {
     if (i > 0) output()->print(", ");
     print_value(x->argument_at(i));
diff a/src/hotspot/share/c1/c1_InstructionPrinter.hpp b/src/hotspot/share/c1/c1_InstructionPrinter.hpp
--- a/src/hotspot/share/c1/c1_InstructionPrinter.hpp
+++ b/src/hotspot/share/c1/c1_InstructionPrinter.hpp
@@ -124,10 +124,11 @@
   virtual void do_UnsafeGet      (UnsafeGet*       x);
   virtual void do_UnsafePut      (UnsafePut*       x);
   virtual void do_UnsafeGetAndSet(UnsafeGetAndSet* x);
   virtual void do_ProfileCall    (ProfileCall*     x);
   virtual void do_ProfileReturnType (ProfileReturnType*  x);
+  virtual void do_ProfileACmpTypes(ProfileACmpTypes*  x);
   virtual void do_ProfileInvoke  (ProfileInvoke*   x);
   virtual void do_RuntimeCall    (RuntimeCall*     x);
   virtual void do_MemBar         (MemBar*          x);
   virtual void do_RangeCheckPredicate(RangeCheckPredicate* x);
 #ifdef ASSERT
diff a/src/hotspot/share/c1/c1_LIR.cpp b/src/hotspot/share/c1/c1_LIR.cpp
--- a/src/hotspot/share/c1/c1_LIR.cpp
+++ b/src/hotspot/share/c1/c1_LIR.cpp
@@ -26,10 +26,11 @@
 #include "c1/c1_CodeStubs.hpp"
 #include "c1/c1_InstructionPrinter.hpp"
 #include "c1/c1_LIR.hpp"
 #include "c1/c1_LIRAssembler.hpp"
 #include "c1/c1_ValueStack.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "ci/ciInstance.hpp"
 #include "runtime/safepointMechanism.inline.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/vm_version.hpp"
 
@@ -287,11 +288,11 @@
 
 
 LIR_OpTypeCheck::LIR_OpTypeCheck(LIR_Code code, LIR_Opr result, LIR_Opr object, ciKlass* klass,
                                  LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3,
                                  bool fast_check, CodeEmitInfo* info_for_exception, CodeEmitInfo* info_for_patch,
-                                 CodeStub* stub)
+                                 CodeStub* stub, bool need_null_check)
 
   : LIR_Op(code, result, nullptr)
   , _object(object)
   , _array(LIR_OprFact::illegalOpr)
   , _klass(klass)
@@ -303,10 +304,11 @@
   , _info_for_exception(info_for_exception)
   , _stub(stub)
   , _profiled_method(nullptr)
   , _profiled_bci(-1)
   , _should_profile(false)
+  , _need_null_check(need_null_check)
 {
   if (code == lir_checkcast) {
     assert(info_for_exception != nullptr, "checkcast throws exceptions");
   } else if (code == lir_instanceof) {
     assert(info_for_exception == nullptr, "instanceof throws no exceptions");
@@ -330,19 +332,51 @@
   , _info_for_exception(info_for_exception)
   , _stub(nullptr)
   , _profiled_method(nullptr)
   , _profiled_bci(-1)
   , _should_profile(false)
+  , _need_null_check(true)
 {
   if (code == lir_store_check) {
     _stub = new ArrayStoreExceptionStub(object, info_for_exception);
     assert(info_for_exception != nullptr, "store_check throws exceptions");
   } else {
     ShouldNotReachHere();
   }
 }
 
+LIR_OpFlattenedArrayCheck::LIR_OpFlattenedArrayCheck(LIR_Opr array, LIR_Opr value, LIR_Opr tmp, CodeStub* stub)
+  : LIR_Op(lir_flat_array_check, LIR_OprFact::illegalOpr, nullptr)
+  , _array(array)
+  , _value(value)
+  , _tmp(tmp)
+  , _stub(stub) {}
+
+
+LIR_OpNullFreeArrayCheck::LIR_OpNullFreeArrayCheck(LIR_Opr array, LIR_Opr tmp)
+  : LIR_Op(lir_null_free_array_check, LIR_OprFact::illegalOpr, nullptr)
+  , _array(array)
+  , _tmp(tmp) {}
+
+
+LIR_OpSubstitutabilityCheck::LIR_OpSubstitutabilityCheck(LIR_Opr result, LIR_Opr left, LIR_Opr right, LIR_Opr equal_result, LIR_Opr not_equal_result,
+                                                         LIR_Opr tmp1, LIR_Opr tmp2,
+                                                         ciKlass* left_klass, ciKlass* right_klass, LIR_Opr left_klass_op, LIR_Opr right_klass_op,
+                                                         CodeEmitInfo* info, CodeStub* stub)
+  : LIR_Op(lir_substitutability_check, result, info)
+  , _left(left)
+  , _right(right)
+  , _equal_result(equal_result)
+  , _not_equal_result(not_equal_result)
+  , _tmp1(tmp1)
+  , _tmp2(tmp2)
+  , _left_klass(left_klass)
+  , _right_klass(right_klass)
+  , _left_klass_op(left_klass_op)
+  , _right_klass_op(right_klass_op)
+  , _stub(stub) {}
+
 
 LIR_OpArrayCopy::LIR_OpArrayCopy(LIR_Opr src, LIR_Opr src_pos, LIR_Opr dst, LIR_Opr dst_pos, LIR_Opr length,
                                  LIR_Opr tmp, ciArrayKlass* expected_type, int flags, CodeEmitInfo* info)
   : LIR_Op(lir_arraycopy, LIR_OprFact::illegalOpr, info)
   , _src(src)
@@ -405,10 +439,11 @@
     case lir_membar_release:           // result and info always invalid
     case lir_membar_loadload:          // result and info always invalid
     case lir_membar_storestore:        // result and info always invalid
     case lir_membar_loadstore:         // result and info always invalid
     case lir_membar_storeload:         // result and info always invalid
+    case lir_check_orig_pc:            // result and info always invalid
     case lir_on_spin_wait:
     {
       assert(op->as_Op0() != nullptr, "must be");
       assert(op->_info == nullptr, "info not used by this instruction");
       assert(op->_result->is_illegal(), "not used");
@@ -801,10 +836,11 @@
 
       if (opLock->_scratch->is_valid())           do_temp(opLock->_scratch);
       assert(opLock->_result->is_illegal(), "unused");
 
       do_stub(opLock->_stub);
+      do_stub(opLock->_throw_imse_stub);
 
       break;
     }
 
 
@@ -837,10 +873,57 @@
       if (opTypeCheck->_result->is_valid())       do_output(opTypeCheck->_result);
       if (opTypeCheck->_stub != nullptr)          do_stub(opTypeCheck->_stub);
       break;
     }
 
+// LIR_OpFlattenedArrayCheck
+    case lir_flat_array_check: {
+      assert(op->as_OpFlattenedArrayCheck() != nullptr, "must be");
+      LIR_OpFlattenedArrayCheck* opFlattenedArrayCheck = (LIR_OpFlattenedArrayCheck*)op;
+
+      if (opFlattenedArrayCheck->_array->is_valid()) do_input(opFlattenedArrayCheck->_array);
+      if (opFlattenedArrayCheck->_value->is_valid()) do_input(opFlattenedArrayCheck->_value);
+      if (opFlattenedArrayCheck->_tmp->is_valid())   do_temp(opFlattenedArrayCheck->_tmp);
+
+      do_stub(opFlattenedArrayCheck->_stub);
+
+      break;
+    }
+
+// LIR_OpNullFreeArrayCheck
+    case lir_null_free_array_check: {
+      assert(op->as_OpNullFreeArrayCheck() != nullptr, "must be");
+      LIR_OpNullFreeArrayCheck* opNullFreeArrayCheck = (LIR_OpNullFreeArrayCheck*)op;
+
+      if (opNullFreeArrayCheck->_array->is_valid()) do_input(opNullFreeArrayCheck->_array);
+      if (opNullFreeArrayCheck->_tmp->is_valid())   do_temp(opNullFreeArrayCheck->_tmp);
+      break;
+    }
+
+// LIR_OpSubstitutabilityCheck
+    case lir_substitutability_check: {
+      assert(op->as_OpSubstitutabilityCheck() != nullptr, "must be");
+      LIR_OpSubstitutabilityCheck* opSubstitutabilityCheck = (LIR_OpSubstitutabilityCheck*)op;
+                                                                do_input(opSubstitutabilityCheck->_left);
+                                                                do_temp (opSubstitutabilityCheck->_left);
+                                                                do_input(opSubstitutabilityCheck->_right);
+                                                                do_temp (opSubstitutabilityCheck->_right);
+                                                                do_input(opSubstitutabilityCheck->_equal_result);
+                                                                do_temp (opSubstitutabilityCheck->_equal_result);
+                                                                do_input(opSubstitutabilityCheck->_not_equal_result);
+                                                                do_temp (opSubstitutabilityCheck->_not_equal_result);
+      if (opSubstitutabilityCheck->_tmp1->is_valid())           do_temp(opSubstitutabilityCheck->_tmp1);
+      if (opSubstitutabilityCheck->_tmp2->is_valid())           do_temp(opSubstitutabilityCheck->_tmp2);
+      if (opSubstitutabilityCheck->_left_klass_op->is_valid())  do_temp(opSubstitutabilityCheck->_left_klass_op);
+      if (opSubstitutabilityCheck->_right_klass_op->is_valid()) do_temp(opSubstitutabilityCheck->_right_klass_op);
+      if (opSubstitutabilityCheck->_result->is_valid())         do_output(opSubstitutabilityCheck->_result);
+
+      do_info(opSubstitutabilityCheck->_info);
+      do_stub(opSubstitutabilityCheck->_stub);
+      break;
+    }
+
 // LIR_OpCompareAndSwap
     case lir_cas_long:
     case lir_cas_obj:
     case lir_cas_int: {
       assert(op->as_OpCompareAndSwap() != nullptr, "must be");
@@ -914,11 +997,22 @@
       do_input(opProfileType->_mdp); do_temp(opProfileType->_mdp);
       do_input(opProfileType->_obj);
       do_temp(opProfileType->_tmp);
       break;
     }
-  default:
+
+    // LIR_OpProfileInlineType:
+    case lir_profile_inline_type: {
+      assert(op->as_OpProfileInlineType() != nullptr, "must be");
+      LIR_OpProfileInlineType* opProfileInlineType = (LIR_OpProfileInlineType*)op;
+
+      do_input(opProfileInlineType->_mdp); do_temp(opProfileInlineType->_mdp);
+      do_input(opProfileInlineType->_obj);
+      do_temp(opProfileInlineType->_tmp);
+      break;
+    }
+default:
     op->visit(this);
   }
 }
 
 void LIR_Op::visit(LIR_OpVisitState* state) {
@@ -987,10 +1081,38 @@
 
 void LIR_OpJavaCall::emit_code(LIR_Assembler* masm) {
   masm->emit_call(this);
 }
 
+bool LIR_OpJavaCall::maybe_return_as_fields(ciInlineKlass** vk_ret) const {
+  ciType* return_type = method()->return_type();
+  if (InlineTypeReturnedAsFields) {
+    if (return_type->is_inlinetype()) {
+      ciInlineKlass* vk = return_type->as_inline_klass();
+      if (vk->can_be_returned_as_fields()) {
+        if (vk_ret != nullptr) {
+          *vk_ret = vk;
+        }
+        return true;
+      }
+    } else if (return_type->is_instance_klass() &&
+               (method()->is_method_handle_intrinsic() || !return_type->is_loaded() ||
+                StressCallingConvention)) {
+      // An inline type might be returned from the call but we don't know its type.
+      // This can happen with method handle intrinsics or when the return type is
+      // not loaded (method holder is not loaded or preload attribute is missing).
+      // If an inline type is returned, we either get an oop to a buffer and nothing
+      // needs to be done or one of the values being returned is the klass of the
+      // inline type (RAX on x64, with LSB set to 1) and we need to allocate an inline
+      // type instance of that type and initialize it with the fields values being
+      // returned in other registers.
+      return true;
+    }
+  }
+  return false;
+}
+
 void LIR_OpRTCall::emit_code(LIR_Assembler* masm) {
   masm->emit_rtcall(this);
 }
 
 void LIR_OpLabel::emit_code(LIR_Assembler* masm) {
@@ -1047,10 +1169,28 @@
   if (stub()) {
     masm->append_code_stub(stub());
   }
 }
 
+void LIR_OpFlattenedArrayCheck::emit_code(LIR_Assembler* masm) {
+  masm->emit_opFlattenedArrayCheck(this);
+  if (stub() != nullptr) {
+    masm->append_code_stub(stub());
+  }
+}
+
+void LIR_OpNullFreeArrayCheck::emit_code(LIR_Assembler* masm) {
+  masm->emit_opNullFreeArrayCheck(this);
+}
+
+void LIR_OpSubstitutabilityCheck::emit_code(LIR_Assembler* masm) {
+  masm->emit_opSubstitutabilityCheck(this);
+  if (stub() != nullptr) {
+    masm->append_code_stub(stub());
+  }
+}
+
 void LIR_OpCompareAndSwap::emit_code(LIR_Assembler* masm) {
   masm->emit_compare_and_swap(this);
 }
 
 void LIR_Op3::emit_code(LIR_Assembler* masm) {
@@ -1064,10 +1204,13 @@
 void LIR_OpLock::emit_code(LIR_Assembler* masm) {
   masm->emit_lock(this);
   if (stub()) {
     masm->append_code_stub(stub());
   }
+  if (throw_imse_stub()) {
+    masm->append_code_stub(throw_imse_stub());
+  }
 }
 
 void LIR_OpLoadKlass::emit_code(LIR_Assembler* masm) {
   masm->emit_load_klass(this);
 }
@@ -1088,10 +1231,14 @@
 
 void LIR_OpProfileType::emit_code(LIR_Assembler* masm) {
   masm->emit_profile_type(this);
 }
 
+void LIR_OpProfileInlineType::emit_code(LIR_Assembler* masm) {
+  masm->emit_profile_inline_type(this);
+}
+
 // LIR_List
 LIR_List::LIR_List(Compilation* compilation, BlockBegin* block)
   : _operations(8)
   , _compilation(compilation)
 #ifndef PRODUCT
@@ -1363,21 +1510,22 @@
                            object_size,
                            init_check,
                            stub));
 }
 
-void LIR_List::allocate_array(LIR_Opr dst, LIR_Opr len, LIR_Opr t1,LIR_Opr t2, LIR_Opr t3,LIR_Opr t4, BasicType type, LIR_Opr klass, CodeStub* stub) {
+void LIR_List::allocate_array(LIR_Opr dst, LIR_Opr len, LIR_Opr t1,LIR_Opr t2, LIR_Opr t3,LIR_Opr t4, BasicType type, LIR_Opr klass, CodeStub* stub, bool is_null_free) {
   append(new LIR_OpAllocArray(
                            klass,
                            len,
                            dst,
                            t1,
                            t2,
                            t3,
                            t4,
                            type,
-                           stub));
+                           stub,
+                           is_null_free));
 }
 
 void LIR_List::shift_left(LIR_Opr value, LIR_Opr count, LIR_Opr dst, LIR_Opr tmp) {
  append(new LIR_Op2(
                     lir_shl,
@@ -1411,19 +1559,20 @@
                      left,
                      right,
                      dst));
 }
 
-void LIR_List::lock_object(LIR_Opr hdr, LIR_Opr obj, LIR_Opr lock, LIR_Opr scratch, CodeStub* stub, CodeEmitInfo* info) {
+void LIR_List::lock_object(LIR_Opr hdr, LIR_Opr obj, LIR_Opr lock, LIR_Opr scratch, CodeStub* stub, CodeEmitInfo* info, CodeStub* throw_imse_stub) {
   append(new LIR_OpLock(
                     lir_lock,
                     hdr,
                     obj,
                     lock,
                     scratch,
                     stub,
-                    info));
+                    info,
+                    throw_imse_stub));
 }
 
 void LIR_List::unlock_object(LIR_Opr hdr, LIR_Opr obj, LIR_Opr lock, LIR_Opr scratch, CodeStub* stub) {
   append(new LIR_OpLock(
                     lir_unlock,
@@ -1444,13 +1593,17 @@
 
 
 void LIR_List::checkcast (LIR_Opr result, LIR_Opr object, ciKlass* klass,
                           LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, bool fast_check,
                           CodeEmitInfo* info_for_exception, CodeEmitInfo* info_for_patch, CodeStub* stub,
-                          ciMethod* profiled_method, int profiled_bci) {
+                          ciMethod* profiled_method, int profiled_bci, bool is_null_free) {
+  // If klass is non-nullable,  LIRGenerator::do_CheckCast has already performed null-check
+  // on the object.
+  bool need_null_check = !is_null_free;
   LIR_OpTypeCheck* c = new LIR_OpTypeCheck(lir_checkcast, result, object, klass,
-                                           tmp1, tmp2, tmp3, fast_check, info_for_exception, info_for_patch, stub);
+                                           tmp1, tmp2, tmp3, fast_check, info_for_exception, info_for_patch, stub,
+                                           need_null_check);
   if (profiled_method != nullptr) {
     c->set_profiled_method(profiled_method);
     c->set_profiled_bci(profiled_bci);
     c->set_should_profile(true);
   }
@@ -1468,10 +1621,11 @@
 }
 
 
 void LIR_List::store_check(LIR_Opr object, LIR_Opr array, LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3,
                            CodeEmitInfo* info_for_exception, ciMethod* profiled_method, int profiled_bci) {
+  // FIXME -- if the types of the array and/or the object are known statically, we can avoid loading the klass
   LIR_OpTypeCheck* c = new LIR_OpTypeCheck(lir_store_check, object, array, tmp1, tmp2, tmp3, info_for_exception);
   if (profiled_method != nullptr) {
     c->set_profiled_method(profiled_method);
     c->set_profiled_bci(profiled_bci);
     c->set_should_profile(true);
@@ -1489,10 +1643,31 @@
     // Emit an implicit null check
     append(new LIR_Op1(lir_null_check, opr, info));
   }
 }
 
+void LIR_List::check_flat_array(LIR_Opr array, LIR_Opr value, LIR_Opr tmp, CodeStub* stub) {
+  LIR_OpFlattenedArrayCheck* c = new LIR_OpFlattenedArrayCheck(array, value, tmp, stub);
+  append(c);
+}
+
+void LIR_List::check_null_free_array(LIR_Opr array, LIR_Opr tmp) {
+  LIR_OpNullFreeArrayCheck* c = new LIR_OpNullFreeArrayCheck(array, tmp);
+  append(c);
+}
+
+void LIR_List::substitutability_check(LIR_Opr result, LIR_Opr left, LIR_Opr right, LIR_Opr equal_result, LIR_Opr not_equal_result,
+                                      LIR_Opr tmp1, LIR_Opr tmp2,
+                                      ciKlass* left_klass, ciKlass* right_klass, LIR_Opr left_klass_op, LIR_Opr right_klass_op,
+                                      CodeEmitInfo* info, CodeStub* stub) {
+  LIR_OpSubstitutabilityCheck* c = new LIR_OpSubstitutabilityCheck(result, left, right, equal_result, not_equal_result,
+                                                                   tmp1, tmp2,
+                                                                   left_klass, right_klass, left_klass_op, right_klass_op,
+                                                                   info, stub);
+  append(c);
+}
+
 void LIR_List::cas_long(LIR_Opr addr, LIR_Opr cmp_value, LIR_Opr new_value,
                         LIR_Opr t1, LIR_Opr t2, LIR_Opr result) {
   append(new LIR_OpCompareAndSwap(lir_cas_long, addr, cmp_value, new_value, t1, t2, result));
 }
 
@@ -1705,10 +1880,11 @@
      case lir_std_entry:             s = "std_entry";     break;
      case lir_osr_entry:             s = "osr_entry";     break;
      case lir_fpop_raw:              s = "fpop_raw";      break;
      case lir_breakpoint:            s = "breakpoint";    break;
      case lir_get_thread:            s = "get_thread";    break;
+     case lir_check_orig_pc:         s = "check_orig_pc"; break;
      // LIR_Op1
      case lir_fxch:                  s = "fxch";          break;
      case lir_fld:                   s = "fld";           break;
      case lir_push:                  s = "push";          break;
      case lir_pop:                   s = "pop";           break;
@@ -1773,18 +1949,26 @@
      case lir_delay_slot:            s = "delay";         break;
      // LIR_OpTypeCheck
      case lir_instanceof:            s = "instanceof";    break;
      case lir_checkcast:             s = "checkcast";     break;
      case lir_store_check:           s = "store_check";   break;
+     // LIR_OpFlattenedArrayCheck
+     case lir_flat_array_check:      s = "flat_array_check"; break;
+     // LIR_OpNullFreeArrayCheck
+     case lir_null_free_array_check: s = "null_free_array_check"; break;
+     // LIR_OpSubstitutabilityCheck
+     case lir_substitutability_check: s = "substitutability_check"; break;
      // LIR_OpCompareAndSwap
      case lir_cas_long:              s = "cas_long";      break;
      case lir_cas_obj:               s = "cas_obj";      break;
      case lir_cas_int:               s = "cas_int";      break;
      // LIR_OpProfileCall
      case lir_profile_call:          s = "profile_call";  break;
      // LIR_OpProfileType
      case lir_profile_type:          s = "profile_type";  break;
+     // LIR_OpProfileInlineType
+     case lir_profile_inline_type:   s = "profile_inline_type"; break;
      // LIR_OpAssert
 #ifdef ASSERT
      case lir_assert:                s = "assert";        break;
 #endif
      case lir_none:                  ShouldNotReachHere();break;
@@ -2012,10 +2196,48 @@
   tmp3()->print(out);                    out->print(" ");
   result_opr()->print(out);              out->print(" ");
   if (info_for_exception() != nullptr) out->print(" [bci:%d]", info_for_exception()->stack()->bci());
 }
 
+void LIR_OpFlattenedArrayCheck::print_instr(outputStream* out) const {
+  array()->print(out);                   out->print(" ");
+  value()->print(out);                   out->print(" ");
+  tmp()->print(out);                     out->print(" ");
+  if (stub() != nullptr) {
+    out->print("[label:" INTPTR_FORMAT "]", p2i(stub()->entry()));
+  }
+}
+
+void LIR_OpNullFreeArrayCheck::print_instr(outputStream* out) const {
+  array()->print(out);                   out->print(" ");
+  tmp()->print(out);                     out->print(" ");
+}
+
+void LIR_OpSubstitutabilityCheck::print_instr(outputStream* out) const {
+  result_opr()->print(out);              out->print(" ");
+  left()->print(out);                    out->print(" ");
+  right()->print(out);                   out->print(" ");
+  equal_result()->print(out);            out->print(" ");
+  not_equal_result()->print(out);        out->print(" ");
+  tmp1()->print(out);                    out->print(" ");
+  tmp2()->print(out);                    out->print(" ");
+  if (left_klass() == nullptr) {
+    out->print("unknown ");
+  } else {
+    left_klass()->print(out);            out->print(" ");
+  }
+  if (right_klass() == nullptr) {
+    out->print("unknown ");
+  } else {
+    right_klass()->print(out);           out->print(" ");
+  }
+  left_klass_op()->print(out);           out->print(" ");
+  right_klass_op()->print(out);          out->print(" ");
+  if (stub() != nullptr) {
+    out->print("[label:" INTPTR_FORMAT "]", p2i(stub()->entry()));
+  }
+}
 
 // LIR_Op3
 void LIR_Op3::print_instr(outputStream* out) const {
   in_opr1()->print(out);    out->print(" ");
   in_opr2()->print(out);    out->print(" ");
@@ -2087,10 +2309,18 @@
   mdp()->print(out);          out->print(" ");
   obj()->print(out);          out->print(" ");
   tmp()->print(out);          out->print(" ");
 }
 
+// LIR_OpProfileInlineType
+void LIR_OpProfileInlineType::print_instr(outputStream* out) const {
+  out->print(" flag = %x ", flag());
+  mdp()->print(out);          out->print(" ");
+  obj()->print(out);          out->print(" ");
+  tmp()->print(out);          out->print(" ");
+}
+
 #endif // PRODUCT
 
 // Implementation of LIR_InsertionBuffer
 
 void LIR_InsertionBuffer::append(int index, LIR_Op* op) {
diff a/src/hotspot/share/c1/c1_LIR.hpp b/src/hotspot/share/c1/c1_LIR.hpp
--- a/src/hotspot/share/c1/c1_LIR.hpp
+++ b/src/hotspot/share/c1/c1_LIR.hpp
@@ -894,14 +894,18 @@
 class      LIR_OpRTCall;
 class    LIR_OpArrayCopy;
 class    LIR_OpUpdateCRC32;
 class    LIR_OpLock;
 class    LIR_OpTypeCheck;
+class    LIR_OpFlattenedArrayCheck;
+class    LIR_OpNullFreeArrayCheck;
+class    LIR_OpSubstitutabilityCheck;
 class    LIR_OpCompareAndSwap;
 class    LIR_OpLoadKlass;
 class    LIR_OpProfileCall;
 class    LIR_OpProfileType;
+class    LIR_OpProfileInlineType;
 #ifdef ASSERT
 class    LIR_OpAssert;
 #endif
 
 // LIR operation codes
@@ -922,10 +926,11 @@
       , lir_membar_storestore
       , lir_membar_loadstore
       , lir_membar_storeload
       , lir_get_thread
       , lir_on_spin_wait
+      , lir_check_orig_pc
   , end_op0
   , begin_op1
       , lir_fxch
       , lir_fld
       , lir_push
@@ -1003,18 +1008,28 @@
   , begin_opTypeCheck
     , lir_instanceof
     , lir_checkcast
     , lir_store_check
   , end_opTypeCheck
+  , begin_opFlattenedArrayCheck
+    , lir_flat_array_check
+  , end_opFlattenedArrayCheck
+  , begin_opNullFreeArrayCheck
+    , lir_null_free_array_check
+  , end_opNullFreeArrayCheck
+  , begin_opSubstitutabilityCheck
+    , lir_substitutability_check
+  , end_opSubstitutabilityCheck
   , begin_opCompareAndSwap
     , lir_cas_long
     , lir_cas_obj
     , lir_cas_int
   , end_opCompareAndSwap
   , begin_opMDOProfile
     , lir_profile_call
     , lir_profile_type
+    , lir_profile_inline_type
   , end_opMDOProfile
   , begin_opAssert
     , lir_assert
   , end_opAssert
 #ifdef INCLUDE_ZGC
@@ -1159,14 +1174,18 @@
   virtual LIR_Op3* as_Op3() { return nullptr; }
   virtual LIR_Op4* as_Op4() { return nullptr; }
   virtual LIR_OpArrayCopy* as_OpArrayCopy() { return nullptr; }
   virtual LIR_OpUpdateCRC32* as_OpUpdateCRC32() { return nullptr; }
   virtual LIR_OpTypeCheck* as_OpTypeCheck() { return nullptr; }
+  virtual LIR_OpFlattenedArrayCheck* as_OpFlattenedArrayCheck() { return nullptr; }
+  virtual LIR_OpNullFreeArrayCheck* as_OpNullFreeArrayCheck() { return nullptr; }
+  virtual LIR_OpSubstitutabilityCheck* as_OpSubstitutabilityCheck() { return nullptr; }
   virtual LIR_OpCompareAndSwap* as_OpCompareAndSwap() { return nullptr; }
   virtual LIR_OpLoadKlass* as_OpLoadKlass() { return nullptr; }
   virtual LIR_OpProfileCall* as_OpProfileCall() { return nullptr; }
   virtual LIR_OpProfileType* as_OpProfileType() { return nullptr; }
+  virtual LIR_OpProfileInlineType* as_OpProfileInlineType() { return nullptr; }
 #ifdef ASSERT
   virtual LIR_OpAssert* as_OpAssert() { return nullptr; }
 #endif
 
   virtual void verify() const {}
@@ -1235,10 +1254,12 @@
   }
 
   virtual void emit_code(LIR_Assembler* masm);
   virtual LIR_OpJavaCall* as_OpJavaCall() { return this; }
   virtual void print_instr(outputStream* out) const PRODUCT_RETURN;
+
+  bool maybe_return_as_fields(ciInlineKlass** vk = nullptr) const;
 };
 
 // --------------------------------------------------
 // LIR_OpLabel
 // --------------------------------------------------
@@ -1286,11 +1307,14 @@
     type_check             = 1 << 7,
     overlapping            = 1 << 8,
     unaligned              = 1 << 9,
     src_objarray           = 1 << 10,
     dst_objarray           = 1 << 11,
-    all_flags              = (1 << 12) - 1
+    always_slow_path       = 1 << 12,
+    src_inlinetype_check   = 1 << 13,
+    dst_inlinetype_check   = 1 << 14,
+    all_flags              = (1 << 15) - 1
   };
 
   LIR_OpArrayCopy(LIR_Opr src, LIR_Opr src_pos, LIR_Opr dst, LIR_Opr dst_pos, LIR_Opr length, LIR_Opr tmp,
                   ciArrayKlass* expected_type, int flags, CodeEmitInfo* info);
 
@@ -1549,15 +1573,16 @@
   CodeEmitInfo* _info_for_exception;
   CodeStub*     _stub;
   ciMethod*     _profiled_method;
   int           _profiled_bci;
   bool          _should_profile;
+  bool          _need_null_check;
 
 public:
   LIR_OpTypeCheck(LIR_Code code, LIR_Opr result, LIR_Opr object, ciKlass* klass,
                   LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, bool fast_check,
-                  CodeEmitInfo* info_for_exception, CodeEmitInfo* info_for_patch, CodeStub* stub);
+                  CodeEmitInfo* info_for_exception, CodeEmitInfo* info_for_patch, CodeStub* stub, bool need_null_check = true);
   LIR_OpTypeCheck(LIR_Code code, LIR_Opr object, LIR_Opr array,
                   LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, CodeEmitInfo* info_for_exception);
 
   LIR_Opr object() const                         { return _object;         }
   LIR_Opr array() const                          { assert(code() == lir_store_check, "not valid"); return _array;         }
@@ -1575,17 +1600,93 @@
   void set_profiled_bci(int bci)                 { _profiled_bci = bci;       }
   void set_should_profile(bool b)                { _should_profile = b;       }
   ciMethod* profiled_method() const              { return _profiled_method;   }
   int       profiled_bci() const                 { return _profiled_bci;      }
   bool      should_profile() const               { return _should_profile;    }
-
+  bool      need_null_check() const              { return _need_null_check;   }
   virtual bool is_patching() { return _info_for_patch != nullptr; }
   virtual void emit_code(LIR_Assembler* masm);
   virtual LIR_OpTypeCheck* as_OpTypeCheck() { return this; }
   void print_instr(outputStream* out) const PRODUCT_RETURN;
 };
 
+// LIR_OpFlattenedArrayCheck
+class LIR_OpFlattenedArrayCheck: public LIR_Op {
+ friend class LIR_OpVisitState;
+
+ private:
+  LIR_Opr       _array;
+  LIR_Opr       _value;
+  LIR_Opr       _tmp;
+  CodeStub*     _stub;
+public:
+  LIR_OpFlattenedArrayCheck(LIR_Opr array, LIR_Opr value, LIR_Opr tmp, CodeStub* stub);
+  LIR_Opr array() const                          { return _array;         }
+  LIR_Opr value() const                          { return _value;         }
+  LIR_Opr tmp() const                            { return _tmp;           }
+  CodeStub* stub() const                         { return _stub;          }
+
+  virtual void emit_code(LIR_Assembler* masm);
+  virtual LIR_OpFlattenedArrayCheck* as_OpFlattenedArrayCheck() { return this; }
+  virtual void print_instr(outputStream* out) const PRODUCT_RETURN;
+};
+
+// LIR_OpNullFreeArrayCheck
+class LIR_OpNullFreeArrayCheck: public LIR_Op {
+ friend class LIR_OpVisitState;
+
+ private:
+  LIR_Opr       _array;
+  LIR_Opr       _tmp;
+public:
+  LIR_OpNullFreeArrayCheck(LIR_Opr array, LIR_Opr tmp);
+  LIR_Opr array() const                          { return _array;         }
+  LIR_Opr tmp() const                            { return _tmp;           }
+
+  virtual void emit_code(LIR_Assembler* masm);
+  virtual LIR_OpNullFreeArrayCheck* as_OpNullFreeArrayCheck() { return this; }
+  virtual void print_instr(outputStream* out) const PRODUCT_RETURN;
+};
+
+class LIR_OpSubstitutabilityCheck: public LIR_Op {
+ friend class LIR_OpVisitState;
+
+ private:
+  LIR_Opr       _left;
+  LIR_Opr       _right;
+  LIR_Opr       _equal_result;
+  LIR_Opr       _not_equal_result;
+  LIR_Opr       _tmp1;
+  LIR_Opr       _tmp2;
+  ciKlass*      _left_klass;
+  ciKlass*      _right_klass;
+  LIR_Opr       _left_klass_op;
+  LIR_Opr       _right_klass_op;
+  CodeStub*     _stub;
+public:
+  LIR_OpSubstitutabilityCheck(LIR_Opr result, LIR_Opr left, LIR_Opr right, LIR_Opr equal_result, LIR_Opr not_equal_result,
+                              LIR_Opr tmp1, LIR_Opr tmp2,
+                              ciKlass* left_klass, ciKlass* right_klass, LIR_Opr left_klass_op, LIR_Opr right_klass_op,
+                              CodeEmitInfo* info, CodeStub* stub);
+
+  LIR_Opr left() const             { return _left; }
+  LIR_Opr right() const            { return _right; }
+  LIR_Opr equal_result() const     { return _equal_result; }
+  LIR_Opr not_equal_result() const { return _not_equal_result; }
+  LIR_Opr tmp1() const             { return _tmp1; }
+  LIR_Opr tmp2() const             { return _tmp2; }
+  ciKlass* left_klass() const      { return _left_klass; }
+  ciKlass* right_klass() const     { return _right_klass; }
+  LIR_Opr left_klass_op() const    { return _left_klass_op; }
+  LIR_Opr right_klass_op() const   { return _right_klass_op; }
+  CodeStub* stub() const           { return _stub; }
+
+  virtual void emit_code(LIR_Assembler* masm);
+  virtual LIR_OpSubstitutabilityCheck* as_OpSubstitutabilityCheck() { return this; }
+  virtual void print_instr(outputStream* out) const PRODUCT_RETURN;
+};
+
 // LIR_Op2
 class LIR_Op2: public LIR_Op {
  friend class LIR_OpVisitState;
 
   int  _fpu_stack_size; // for sin/cos implementation on Intel
@@ -1748,32 +1849,35 @@
   LIR_Opr   _tmp2;
   LIR_Opr   _tmp3;
   LIR_Opr   _tmp4;
   BasicType _type;
   CodeStub* _stub;
+  bool      _is_null_free;
 
  public:
-  LIR_OpAllocArray(LIR_Opr klass, LIR_Opr len, LIR_Opr result, LIR_Opr t1, LIR_Opr t2, LIR_Opr t3, LIR_Opr t4, BasicType type, CodeStub* stub)
+  LIR_OpAllocArray(LIR_Opr klass, LIR_Opr len, LIR_Opr result, LIR_Opr t1, LIR_Opr t2, LIR_Opr t3, LIR_Opr t4, BasicType type, CodeStub* stub, bool is_null_free)
     : LIR_Op(lir_alloc_array, result, nullptr)
     , _klass(klass)
     , _len(len)
     , _tmp1(t1)
     , _tmp2(t2)
     , _tmp3(t3)
     , _tmp4(t4)
     , _type(type)
-    , _stub(stub) {}
+    , _stub(stub)
+    , _is_null_free(is_null_free) {}
 
   LIR_Opr   klass()   const                      { return _klass;       }
   LIR_Opr   len()     const                      { return _len;         }
   LIR_Opr   obj()     const                      { return result_opr(); }
   LIR_Opr   tmp1()    const                      { return _tmp1;        }
   LIR_Opr   tmp2()    const                      { return _tmp2;        }
   LIR_Opr   tmp3()    const                      { return _tmp3;        }
   LIR_Opr   tmp4()    const                      { return _tmp4;        }
   BasicType type()    const                      { return _type;        }
   CodeStub* stub()    const                      { return _stub;        }
+  bool      is_null_free() const                 { return _is_null_free;}
 
   virtual void emit_code(LIR_Assembler* masm);
   virtual LIR_OpAllocArray * as_OpAllocArray () { return this; }
   virtual void print_instr(outputStream* out) const PRODUCT_RETURN;
 };
@@ -1876,24 +1980,27 @@
   LIR_Opr _hdr;
   LIR_Opr _obj;
   LIR_Opr _lock;
   LIR_Opr _scratch;
   CodeStub* _stub;
+  CodeStub* _throw_imse_stub;
  public:
-  LIR_OpLock(LIR_Code code, LIR_Opr hdr, LIR_Opr obj, LIR_Opr lock, LIR_Opr scratch, CodeStub* stub, CodeEmitInfo* info)
+  LIR_OpLock(LIR_Code code, LIR_Opr hdr, LIR_Opr obj, LIR_Opr lock, LIR_Opr scratch, CodeStub* stub, CodeEmitInfo* info, CodeStub* throw_imse_stub=nullptr)
     : LIR_Op(code, LIR_OprFact::illegalOpr, info)
     , _hdr(hdr)
     , _obj(obj)
     , _lock(lock)
     , _scratch(scratch)
-    , _stub(stub)                      {}
+    , _stub(stub)
+    , _throw_imse_stub(throw_imse_stub)                    {}
 
   LIR_Opr hdr_opr() const                        { return _hdr; }
   LIR_Opr obj_opr() const                        { return _obj; }
   LIR_Opr lock_opr() const                       { return _lock; }
   LIR_Opr scratch_opr() const                    { return _scratch; }
   CodeStub* stub() const                         { return _stub; }
+  CodeStub* throw_imse_stub() const              { return _throw_imse_stub; }
 
   virtual void emit_code(LIR_Assembler* masm);
   virtual LIR_OpLock* as_OpLock() { return this; }
   void print_instr(outputStream* out) const PRODUCT_RETURN;
 };
@@ -2073,10 +2180,42 @@
   virtual void emit_code(LIR_Assembler* masm);
   virtual LIR_OpProfileType* as_OpProfileType() { return this; }
   virtual void print_instr(outputStream* out) const PRODUCT_RETURN;
 };
 
+// LIR_OpProfileInlineType
+class LIR_OpProfileInlineType : public LIR_Op {
+ friend class LIR_OpVisitState;
+
+ private:
+  LIR_Opr      _mdp;
+  LIR_Opr      _obj;
+  int          _flag;
+  LIR_Opr      _tmp;
+  bool         _not_null;      // true if we know statically that _obj cannot be null
+
+ public:
+  // Destroys recv
+  LIR_OpProfileInlineType(LIR_Opr mdp, LIR_Opr obj, int flag, LIR_Opr tmp, bool not_null)
+    : LIR_Op(lir_profile_inline_type, LIR_OprFact::illegalOpr, nullptr)  // no result, no info
+    , _mdp(mdp)
+    , _obj(obj)
+    , _flag(flag)
+    , _tmp(tmp)
+    , _not_null(not_null) { }
+
+  LIR_Opr      mdp()              const             { return _mdp;              }
+  LIR_Opr      obj()              const             { return _obj;              }
+  int          flag()             const             { return _flag;             }
+  LIR_Opr      tmp()              const             { return _tmp;              }
+  bool         not_null()         const             { return _not_null;         }
+
+  virtual void emit_code(LIR_Assembler* masm);
+  virtual LIR_OpProfileInlineType* as_OpProfileInlineType() { return this; }
+  virtual void print_instr(outputStream* out) const PRODUCT_RETURN;
+};
+
 class LIR_InsertionBuffer;
 
 //--------------------------------LIR_List---------------------------------------------------
 // Maintains a list of LIR instructions (one instance of LIR_List per basic block)
 // The LIR instructions are appended by the LIR_List class itself;
@@ -2300,11 +2439,11 @@
   void idiv(LIR_Opr left, int   right, LIR_Opr res, LIR_Opr tmp, CodeEmitInfo* info);
   void irem(LIR_Opr left, LIR_Opr right, LIR_Opr res, LIR_Opr tmp, CodeEmitInfo* info);
   void irem(LIR_Opr left, int   right, LIR_Opr res, LIR_Opr tmp, CodeEmitInfo* info);
 
   void allocate_object(LIR_Opr dst, LIR_Opr t1, LIR_Opr t2, LIR_Opr t3, LIR_Opr t4, int header_size, int object_size, LIR_Opr klass, bool init_check, CodeStub* stub);
-  void allocate_array(LIR_Opr dst, LIR_Opr len, LIR_Opr t1,LIR_Opr t2, LIR_Opr t3,LIR_Opr t4, BasicType type, LIR_Opr klass, CodeStub* stub);
+  void allocate_array(LIR_Opr dst, LIR_Opr len, LIR_Opr t1,LIR_Opr t2, LIR_Opr t3,LIR_Opr t4, BasicType type, LIR_Opr klass, CodeStub* stub, bool is_null_free);
 
   // jump is an unconditional branch
   void jump(BlockBegin* block) {
     append(new LIR_OpBranch(lir_cond_always, block));
   }
@@ -2347,32 +2486,41 @@
     append(new LIR_OpRTCall(routine, tmp, result, arguments, info));
   }
 
   void load_stack_address_monitor(int monitor_ix, LIR_Opr dst)  { append(new LIR_Op1(lir_monaddr, LIR_OprFact::intConst(monitor_ix), dst)); }
   void unlock_object(LIR_Opr hdr, LIR_Opr obj, LIR_Opr lock, LIR_Opr scratch, CodeStub* stub);
-  void lock_object(LIR_Opr hdr, LIR_Opr obj, LIR_Opr lock, LIR_Opr scratch, CodeStub* stub, CodeEmitInfo* info);
+  void lock_object(LIR_Opr hdr, LIR_Opr obj, LIR_Opr lock, LIR_Opr scratch, CodeStub* stub, CodeEmitInfo* info, CodeStub* throw_imse_stub=nullptr);
 
   void breakpoint()                                                  { append(new LIR_Op0(lir_breakpoint)); }
 
   void arraycopy(LIR_Opr src, LIR_Opr src_pos, LIR_Opr dst, LIR_Opr dst_pos, LIR_Opr length, LIR_Opr tmp, ciArrayKlass* expected_type, int flags, CodeEmitInfo* info) { append(new LIR_OpArrayCopy(src, src_pos, dst, dst_pos, length, tmp, expected_type, flags, info)); }
 
   void update_crc32(LIR_Opr crc, LIR_Opr val, LIR_Opr res)  { append(new LIR_OpUpdateCRC32(crc, val, res)); }
 
   void instanceof(LIR_Opr result, LIR_Opr object, ciKlass* klass, LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, bool fast_check, CodeEmitInfo* info_for_patch, ciMethod* profiled_method, int profiled_bci);
   void store_check(LIR_Opr object, LIR_Opr array, LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, CodeEmitInfo* info_for_exception, ciMethod* profiled_method, int profiled_bci);
+  void check_flat_array(LIR_Opr array, LIR_Opr value, LIR_Opr tmp, CodeStub* stub);
+  void check_null_free_array(LIR_Opr array, LIR_Opr tmp);
+  void substitutability_check(LIR_Opr result, LIR_Opr left, LIR_Opr right, LIR_Opr equal_result, LIR_Opr not_equal_result,
+                              LIR_Opr tmp1, LIR_Opr tmp2,
+                              ciKlass* left_klass, ciKlass* right_klass, LIR_Opr left_klass_op, LIR_Opr right_klass_op,
+                              CodeEmitInfo* info, CodeStub* stub);
 
   void checkcast (LIR_Opr result, LIR_Opr object, ciKlass* klass,
                   LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, bool fast_check,
                   CodeEmitInfo* info_for_exception, CodeEmitInfo* info_for_patch, CodeStub* stub,
-                  ciMethod* profiled_method, int profiled_bci);
+                  ciMethod* profiled_method, int profiled_bci, bool is_null_free);
   // MethodData* profiling
   void profile_call(ciMethod* method, int bci, ciMethod* callee, LIR_Opr mdo, LIR_Opr recv, LIR_Opr t1, ciKlass* cha_klass) {
     append(new LIR_OpProfileCall(method, bci, callee, mdo, recv, t1, cha_klass));
   }
   void profile_type(LIR_Address* mdp, LIR_Opr obj, ciKlass* exact_klass, intptr_t current_klass, LIR_Opr tmp, bool not_null, bool no_conflict) {
     append(new LIR_OpProfileType(LIR_OprFact::address(mdp), obj, exact_klass, current_klass, tmp, not_null, no_conflict));
   }
+  void profile_inline_type(LIR_Address* mdp, LIR_Opr obj, int flag, LIR_Opr tmp, bool not_null) {
+    append(new LIR_OpProfileInlineType(LIR_OprFact::address(mdp), obj, flag, tmp, not_null));
+  }
 
   void xadd(LIR_Opr src, LIR_Opr add, LIR_Opr res, LIR_Opr tmp) { append(new LIR_Op2(lir_xadd, src, add, res, tmp)); }
   void xchg(LIR_Opr src, LIR_Opr set, LIR_Opr res, LIR_Opr tmp) { append(new LIR_Op2(lir_xchg, src, set, res, tmp)); }
 
   void load_klass(LIR_Opr obj, LIR_Opr result, CodeEmitInfo* info) { append(new LIR_OpLoadKlass(obj, result, info)); }
diff a/src/hotspot/share/c1/c1_LIRAssembler.cpp b/src/hotspot/share/c1/c1_LIRAssembler.cpp
--- a/src/hotspot/share/c1/c1_LIRAssembler.cpp
+++ b/src/hotspot/share/c1/c1_LIRAssembler.cpp
@@ -28,14 +28,16 @@
 #include "c1/c1_Instruction.hpp"
 #include "c1/c1_InstructionPrinter.hpp"
 #include "c1/c1_LIRAssembler.hpp"
 #include "c1/c1_MacroAssembler.hpp"
 #include "c1/c1_ValueStack.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "ci/ciInstance.hpp"
 #include "compiler/compilerDefinitions.inline.hpp"
 #include "compiler/oopMap.hpp"
 #include "runtime/os.hpp"
+#include "runtime/sharedRuntime.hpp"
 #include "runtime/vm_version.hpp"
 
 void LIR_Assembler::patching_epilog(PatchingStub* patch, LIR_PatchCode patch_code, Register obj, CodeEmitInfo* info) {
   // We must have enough patching space so that call can be inserted.
   // We cannot use fat nops here, since the concurrent code rewrite may transiently
@@ -117,10 +119,11 @@
 
 LIR_Assembler::~LIR_Assembler() {
   // The unwind handler label may be unnbound if this destructor is invoked because of a bail-out.
   // Reset it here to avoid an assertion.
   _unwind_handler_entry.reset();
+  _verified_inline_entry.reset();
 }
 
 
 void LIR_Assembler::check_codespace() {
   CodeSection* cs = _masm->code_section();
@@ -328,24 +331,22 @@
 }
 #endif
 
 //----------------------------------debug info--------------------------------
 
-
 void LIR_Assembler::add_debug_info_for_branch(CodeEmitInfo* info) {
   int pc_offset = code_offset();
   flush_debug_info(pc_offset);
   info->record_debug_info(compilation()->debug_info_recorder(), pc_offset);
   if (info->exception_handlers() != nullptr) {
     compilation()->add_exception_handlers_for_pco(pc_offset, info->exception_handlers());
   }
 }
 
-
-void LIR_Assembler::add_call_info(int pc_offset, CodeEmitInfo* cinfo) {
+void LIR_Assembler::add_call_info(int pc_offset, CodeEmitInfo* cinfo, bool maybe_return_as_fields) {
   flush_debug_info(pc_offset);
-  cinfo->record_debug_info(compilation()->debug_info_recorder(), pc_offset);
+  cinfo->record_debug_info(compilation()->debug_info_recorder(), pc_offset, maybe_return_as_fields);
   if (cinfo->exception_handlers() != nullptr) {
     compilation()->add_exception_handlers_for_pco(pc_offset, cinfo->exception_handlers());
   }
 }
 
@@ -486,10 +487,16 @@
   // Record if this method has MethodHandle invokes.
   if (op->is_method_handle_invoke()) {
     compilation()->set_has_method_handle_invokes(true);
   }
 
+  ciInlineKlass* vk = nullptr;
+  if (op->maybe_return_as_fields(&vk)) {
+    int offset = store_inline_type_fields_to_buf(vk);
+    add_call_info(offset, op->info(), true);
+  }
+
 #if defined(IA32) && defined(COMPILER2)
   // C2 leave fpu stack dirty clean it
   if (UseSSE < 2 && !CompilerConfig::is_c1_only_no_jvmci()) {
     int i;
     for ( i = 1; i <= 7 ; i++ ) {
@@ -592,10 +599,145 @@
       Unimplemented();
       break;
   }
 }
 
+void LIR_Assembler::add_scalarized_entry_info(int pc_offset) {
+  flush_debug_info(pc_offset);
+  DebugInformationRecorder* debug_info = compilation()->debug_info_recorder();
+  // The VEP and VIEP(RO) of a C1-compiled method call buffer_inline_args_xxx()
+  // before doing any argument shuffling. This call may cause GC. When GC happens,
+  // all the parameters are still as passed by the caller, so we just use
+  // map->set_include_argument_oops() inside frame::sender_for_compiled_frame(RegisterMap* map).
+  // There's no need to build a GC map here.
+  OopMap* oop_map = new OopMap(0, 0);
+  debug_info->add_safepoint(pc_offset, oop_map);
+  DebugToken* locvals = debug_info->create_scope_values(nullptr); // FIXME is this needed (for Java debugging to work properly??)
+  DebugToken* expvals = debug_info->create_scope_values(nullptr); // FIXME is this needed (for Java debugging to work properly??)
+  DebugToken* monvals = debug_info->create_monitor_values(nullptr); // FIXME: need testing with synchronized method
+  bool reexecute = false;
+  bool return_oop = false; // This flag will be ignored since it used only for C2 with escape analysis.
+  bool rethrow_exception = false;
+  bool is_method_handle_invoke = false;
+  debug_info->describe_scope(pc_offset, methodHandle(), method(), 0, reexecute, rethrow_exception, is_method_handle_invoke, return_oop, false, locvals, expvals, monvals);
+  debug_info->end_safepoint(pc_offset);
+}
+
+// The entries points of C1-compiled methods can have the following types:
+// (1) Methods with no inline type args
+// (2) Methods with inline type receiver but no inline type args
+//     VIEP_RO is the same as VIEP
+// (3) Methods with non-inline type receiver and some inline type args
+//     VIEP_RO is the same as VEP
+// (4) Methods with inline type receiver and other inline type args
+//     Separate VEP, VIEP and VIEP_RO
+//
+// (1)               (2)                 (3)                    (4)
+// UEP/UIEP:         VEP:                UEP:                   UEP:
+//   check_icache      pack receiver       check_icache           check_icache
+// VEP/VIEP/VIEP_RO    jump to VIEP      VEP/VIEP_RO:           VIEP_RO:
+//   body            UEP/UIEP:             pack inline args       pack inline args (except receiver)
+//                     check_icache        jump to VIEP           jump to VIEP
+//                   VIEP/VIEP_RO        UIEP:                  VEP:
+//                     body                check_icache           pack all inline args
+//                                       VIEP:                    jump to VIEP
+//                                         body                 UIEP:
+//                                                                check_icache
+//                                                              VIEP:
+//                                                                body
+void LIR_Assembler::emit_std_entries() {
+  offsets()->set_value(CodeOffsets::OSR_Entry, _masm->offset());
+
+  _masm->align(CodeEntryAlignment);
+  const CompiledEntrySignature* ces = compilation()->compiled_entry_signature();
+  if (ces->has_scalarized_args()) {
+    assert(InlineTypePassFieldsAsArgs && method()->get_Method()->has_scalarized_args(), "must be");
+    CodeOffsets::Entries ro_entry_type = ces->c1_inline_ro_entry_type();
+
+    // UEP: check icache and fall-through
+    if (ro_entry_type != CodeOffsets::Verified_Inline_Entry) {
+      offsets()->set_value(CodeOffsets::Entry, _masm->offset());
+      if (needs_icache(method())) {
+        check_icache();
+      }
+    }
+
+    // VIEP_RO: pack all value parameters, except the receiver
+    if (ro_entry_type == CodeOffsets::Verified_Inline_Entry_RO) {
+      emit_std_entry(CodeOffsets::Verified_Inline_Entry_RO, ces);
+    }
+
+    // VEP: pack all value parameters
+    _masm->align(CodeEntryAlignment);
+    emit_std_entry(CodeOffsets::Verified_Entry, ces);
+
+    // UIEP: check icache and fall-through
+    _masm->align(CodeEntryAlignment);
+    offsets()->set_value(CodeOffsets::Inline_Entry, _masm->offset());
+    if (ro_entry_type == CodeOffsets::Verified_Inline_Entry) {
+      // Special case if we have VIEP == VIEP(RO):
+      // this means UIEP (called by C1) == UEP (called by C2).
+      offsets()->set_value(CodeOffsets::Entry, _masm->offset());
+    }
+    if (needs_icache(method())) {
+      check_icache();
+    }
+
+    // VIEP: all value parameters are passed as refs - no packing.
+    emit_std_entry(CodeOffsets::Verified_Inline_Entry, nullptr);
+
+    if (ro_entry_type != CodeOffsets::Verified_Inline_Entry_RO) {
+      // The VIEP(RO) is the same as VEP or VIEP
+      assert(ro_entry_type == CodeOffsets::Verified_Entry ||
+             ro_entry_type == CodeOffsets::Verified_Inline_Entry, "must be");
+      offsets()->set_value(CodeOffsets::Verified_Inline_Entry_RO,
+                           offsets()->value(ro_entry_type));
+    }
+  } else {
+    // All 3 entries are the same (no inline type packing)
+    offsets()->set_value(CodeOffsets::Entry, _masm->offset());
+    offsets()->set_value(CodeOffsets::Inline_Entry, _masm->offset());
+    if (needs_icache(method())) {
+      check_icache();
+    }
+    emit_std_entry(CodeOffsets::Verified_Inline_Entry, nullptr);
+    offsets()->set_value(CodeOffsets::Verified_Entry, offsets()->value(CodeOffsets::Verified_Inline_Entry));
+    offsets()->set_value(CodeOffsets::Verified_Inline_Entry_RO, offsets()->value(CodeOffsets::Verified_Inline_Entry));
+  }
+}
+
+void LIR_Assembler::emit_std_entry(CodeOffsets::Entries entry, const CompiledEntrySignature* ces) {
+  offsets()->set_value(entry, _masm->offset());
+  _masm->verified_entry(compilation()->directive()->BreakAtExecuteOption);
+  switch (entry) {
+  case CodeOffsets::Verified_Entry: {
+    if (needs_clinit_barrier_on_entry(method())) {
+      clinit_barrier(method());
+    }
+    int rt_call_offset = _masm->verified_entry(ces, initial_frame_size_in_bytes(), bang_size_in_bytes(), in_bytes(frame_map()->sp_offset_for_orig_pc()), _verified_inline_entry);
+    add_scalarized_entry_info(rt_call_offset);
+    break;
+  }
+  case CodeOffsets::Verified_Inline_Entry_RO: {
+    assert(!needs_clinit_barrier_on_entry(method()), "can't be static");
+    int rt_call_offset = _masm->verified_inline_ro_entry(ces, initial_frame_size_in_bytes(), bang_size_in_bytes(), in_bytes(frame_map()->sp_offset_for_orig_pc()), _verified_inline_entry);
+    add_scalarized_entry_info(rt_call_offset);
+    break;
+  }
+  case CodeOffsets::Verified_Inline_Entry: {
+    if (needs_clinit_barrier_on_entry(method())) {
+      clinit_barrier(method());
+    }
+    build_frame();
+    offsets()->set_value(CodeOffsets::Frame_Complete, _masm->offset());
+    break;
+  }
+  default:
+    ShouldNotReachHere();
+    break;
+  }
+}
 
 void LIR_Assembler::emit_op0(LIR_Op0* op) {
   switch (op->code()) {
     case lir_nop:
       assert(op->info() == nullptr, "not supported");
@@ -605,23 +747,11 @@
     case lir_label:
       Unimplemented();
       break;
 
     case lir_std_entry:
-      // init offsets
-      offsets()->set_value(CodeOffsets::OSR_Entry, _masm->offset());
-      _masm->align(CodeEntryAlignment);
-      if (needs_icache(compilation()->method())) {
-        check_icache();
-      }
-      offsets()->set_value(CodeOffsets::Verified_Entry, _masm->offset());
-      _masm->verified_entry(compilation()->directive()->BreakAtExecuteOption);
-      if (needs_clinit_barrier_on_entry(compilation()->method())) {
-        clinit_barrier(compilation()->method());
-      }
-      build_frame();
-      offsets()->set_value(CodeOffsets::Frame_Complete, _masm->offset());
+      emit_std_entries();
       break;
 
     case lir_osr_entry:
       offsets()->set_value(CodeOffsets::OSR_Entry, _masm->offset());
       osr_entry();
@@ -671,10 +801,14 @@
 
     case lir_on_spin_wait:
       on_spin_wait();
       break;
 
+    case lir_check_orig_pc:
+      check_orig_pc();
+      break;
+
     default:
       ShouldNotReachHere();
       break;
   }
 }
@@ -771,11 +905,12 @@
       break;
   }
 }
 
 void LIR_Assembler::build_frame() {
-  _masm->build_frame(initial_frame_size_in_bytes(), bang_size_in_bytes());
+  _masm->build_frame(initial_frame_size_in_bytes(), bang_size_in_bytes(), in_bytes(frame_map()->sp_offset_for_orig_pc()),
+                     needs_stack_repair(), method()->has_scalarized_args(), &_verified_inline_entry);
 }
 
 
 void LIR_Assembler::roundfp_op(LIR_Opr src, LIR_Opr tmp, LIR_Opr dest, bool pop_fpu_stack) {
   assert(strict_fp_requires_explicit_rounding, "not required");
diff a/src/hotspot/share/c1/c1_LIRAssembler.hpp b/src/hotspot/share/c1/c1_LIRAssembler.hpp
--- a/src/hotspot/share/c1/c1_LIRAssembler.hpp
+++ b/src/hotspot/share/c1/c1_LIRAssembler.hpp
@@ -29,10 +29,11 @@
 #include "ci/ciMethodData.hpp"
 #include "oops/methodData.hpp"
 #include "utilities/macros.hpp"
 
 class Compilation;
+class CompiledEntrySignature;
 class ScopeValue;
 
 class LIR_Assembler: public CompilationResourceObj {
  private:
   C1_MacroAssembler* _masm;
@@ -45,10 +46,11 @@
   Instruction*       _pending_non_safepoint;
   int                _pending_non_safepoint_offset;
   int                _immediate_oops_patched;
 
   Label              _unwind_handler_entry;
+  Label              _verified_inline_entry;
 
 #ifdef ASSERT
   BlockList          _branch_target_blocks;
   void check_no_unbound_labels();
 #endif
@@ -89,18 +91,22 @@
   void metadata2reg(Metadata* o, Register reg);
   void klass2reg_with_patching(Register reg, CodeEmitInfo* info);
 
   void emit_stubs(CodeStubList* stub_list);
 
+  bool needs_stack_repair() const {
+    return compilation()->needs_stack_repair();
+  }
+
  public:
   // addresses
   Address as_Address(LIR_Address* addr);
   Address as_Address_lo(LIR_Address* addr);
   Address as_Address_hi(LIR_Address* addr);
 
   // debug information
-  void add_call_info(int pc_offset, CodeEmitInfo* cinfo);
+  void add_call_info(int pc_offset, CodeEmitInfo* cinfo, bool maybe_return_as_fields = false);
   void add_debug_info_for_branch(CodeEmitInfo* info);
   void add_debug_info_for_div0(int pc_offset, CodeEmitInfo* cinfo);
   void add_debug_info_for_div0_here(CodeEmitInfo* info);
   ImplicitNullCheckStub* add_debug_info_for_null_check(int pc_offset, CodeEmitInfo* cinfo);
   ImplicitNullCheckStub* add_debug_info_for_null_check_here(CodeEmitInfo* info);
@@ -194,19 +200,26 @@
   void emit_updatecrc32(LIR_OpUpdateCRC32* op);
   void emit_opConvert(LIR_OpConvert* op);
   void emit_alloc_obj(LIR_OpAllocObj* op);
   void emit_alloc_array(LIR_OpAllocArray* op);
   void emit_opTypeCheck(LIR_OpTypeCheck* op);
+  void emit_opFlattenedArrayCheck(LIR_OpFlattenedArrayCheck* op);
+  void emit_opNullFreeArrayCheck(LIR_OpNullFreeArrayCheck* op);
+  void emit_opSubstitutabilityCheck(LIR_OpSubstitutabilityCheck* op);
   void emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, Label* failure, Label* obj_is_null);
   void emit_compare_and_swap(LIR_OpCompareAndSwap* op);
   void emit_lock(LIR_OpLock* op);
   void emit_load_klass(LIR_OpLoadKlass* op);
   void emit_call(LIR_OpJavaCall* op);
   void emit_rtcall(LIR_OpRTCall* op);
   void emit_profile_call(LIR_OpProfileCall* op);
   void emit_profile_type(LIR_OpProfileType* op);
+  void emit_profile_inline_type(LIR_OpProfileInlineType* op);
   void emit_delay(LIR_OpDelay* op);
+  void emit_std_entries();
+  void emit_std_entry(CodeOffsets::Entries entry, const CompiledEntrySignature* ces);
+  void add_scalarized_entry_info(int call_offset);
 
   void arith_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dest, CodeEmitInfo* info, bool pop_fpu_stack);
   void arithmetic_idiv(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr temp, LIR_Opr result, CodeEmitInfo* info);
   void intrinsic_op(LIR_Code code, LIR_Opr value, LIR_Opr unused, LIR_Opr dest, LIR_Op* op);
 #ifdef ASSERT
@@ -224,10 +237,11 @@
   void cmove(LIR_Condition code, LIR_Opr left, LIR_Opr right, LIR_Opr result, BasicType type,
              LIR_Opr cmp_opr1 = LIR_OprFact::illegalOpr, LIR_Opr cmp_opr2 = LIR_OprFact::illegalOpr);
   void call(        LIR_OpJavaCall* op, relocInfo::relocType rtype);
   void ic_call(     LIR_OpJavaCall* op);
   void vtable_call( LIR_OpJavaCall* op);
+  int  store_inline_type_fields_to_buf(ciInlineKlass* vk);
 
   void osr_entry();
 
   void build_frame();
 
@@ -250,10 +264,11 @@
   void membar_storestore();
   void membar_loadstore();
   void membar_storeload();
   void on_spin_wait();
   void get_thread(LIR_Opr result);
+  void check_orig_pc();
 
   void verify_oop_map(CodeEmitInfo* info);
 
   void atomic_op(LIR_Code code, LIR_Opr src, LIR_Opr data, LIR_Opr dest, LIR_Opr tmp);
 
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
@@ -29,10 +29,12 @@
 #include "c1/c1_Instruction.hpp"
 #include "c1/c1_LIRAssembler.hpp"
 #include "c1/c1_LIRGenerator.hpp"
 #include "c1/c1_ValueStack.hpp"
 #include "ci/ciArrayKlass.hpp"
+#include "ci/ciFlatArrayKlass.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "ci/ciInstance.hpp"
 #include "ci/ciObjArray.hpp"
 #include "ci/ciUtilities.hpp"
 #include "compiler/compilerDefinitions.inline.hpp"
 #include "gc/shared/barrierSet.hpp"
@@ -211,10 +213,12 @@
 
   _result = opr;
 }
 
 void LIRItem::load_item() {
+  assert(!_gen->in_conditional_code(), "LIRItem cannot be loaded in conditional code");
+
   if (result()->is_illegal()) {
     // update the items result
     _result = value()->operand();
   }
   if (!result()->is_register()) {
@@ -618,17 +622,18 @@
     default: ShouldNotReachHere();
   }
 }
 
 
-void LIRGenerator::monitor_enter(LIR_Opr object, LIR_Opr lock, LIR_Opr hdr, LIR_Opr scratch, int monitor_no, CodeEmitInfo* info_for_exception, CodeEmitInfo* info) {
+void LIRGenerator::monitor_enter(LIR_Opr object, LIR_Opr lock, LIR_Opr hdr, LIR_Opr scratch, int monitor_no,
+                                 CodeEmitInfo* info_for_exception, CodeEmitInfo* info, CodeStub* throw_imse_stub) {
   if (!GenerateSynchronizationCode) return;
   // for slow path, use debug info for state after successful locking
-  CodeStub* slow_path = new MonitorEnterStub(object, lock, info);
+  CodeStub* slow_path = new MonitorEnterStub(object, lock, info, throw_imse_stub, scratch);
   __ load_stack_address_monitor(monitor_no, lock);
   // for handling NullPointerException, use debug info representing just the lock stack before this monitorenter
-  __ lock_object(hdr, object, lock, scratch, slow_path, info_for_exception);
+  __ lock_object(hdr, object, lock, scratch, slow_path, info_for_exception, throw_imse_stub);
 }
 
 
 void LIRGenerator::monitor_exit(LIR_Opr object, LIR_Opr lock, LIR_Opr new_hdr, LIR_Opr scratch, int monitor_no) {
   if (!GenerateSynchronizationCode) return;
@@ -648,14 +653,19 @@
     tty->print_cr("   ###class not resolved at new bci %d", new_instance->printable_bci());
   }
 }
 #endif
 
-void LIRGenerator::new_instance(LIR_Opr dst, ciInstanceKlass* klass, bool is_unresolved, LIR_Opr scratch1, LIR_Opr scratch2, LIR_Opr scratch3, LIR_Opr scratch4, LIR_Opr klass_reg, CodeEmitInfo* info) {
-  klass2reg_with_patching(klass_reg, klass, info, is_unresolved);
-  // If klass is not loaded we do not know if the klass has finalizers:
-  if (UseFastNewInstance && klass->is_loaded()
+void LIRGenerator::new_instance(LIR_Opr dst, ciInstanceKlass* klass, bool is_unresolved, bool allow_inline, LIR_Opr scratch1, LIR_Opr scratch2, LIR_Opr scratch3, LIR_Opr scratch4, LIR_Opr klass_reg, CodeEmitInfo* info) {
+  if (allow_inline) {
+    assert(!is_unresolved && klass->is_loaded(), "inline type klass should be resolved");
+    __ metadata2reg(klass->constant_encoding(), klass_reg);
+  } else {
+    klass2reg_with_patching(klass_reg, klass, info, is_unresolved);
+  }
+  // If klass is not loaded we do not know if the klass has finalizers or is an unexpected inline klass
+  if (UseFastNewInstance && klass->is_loaded() && (allow_inline || !klass->is_inlinetype())
       && !Klass::layout_helper_needs_slow_path(klass->layout_helper())) {
 
     Runtime1::StubID stub_id = klass->is_initialized() ? Runtime1::fast_new_instance_id : Runtime1::fast_new_instance_init_check_id;
 
     CodeStub* slow_path = new NewInstanceStub(klass_reg, dst, klass, info, stub_id);
@@ -666,11 +676,11 @@
     const int instance_size = align_object_size(klass->size_helper());
     __ allocate_object(dst, scratch1, scratch2, scratch3, scratch4,
                        oopDesc::header_size(), instance_size, klass_reg, !klass->is_initialized(), slow_path);
   } else {
     CodeStub* slow_path = new NewInstanceStub(klass_reg, dst, klass, info, Runtime1::new_instance_id);
-    __ branch(lir_cond_always, slow_path);
+    __ jump(slow_path);
     __ branch_destination(slow_path->continuation());
   }
 }
 
 
@@ -766,10 +776,20 @@
 
   // if a probable array type has been identified, figure out if any
   // of the required checks for a fast case can be elided.
   int flags = LIR_OpArrayCopy::all_flags;
 
+  if (!src->is_loaded_flat_array() && !dst->is_loaded_flat_array()) {
+    flags &= ~LIR_OpArrayCopy::always_slow_path;
+  }
+  if (!src->maybe_flat_array()) {
+    flags &= ~LIR_OpArrayCopy::src_inlinetype_check;
+  }
+  if (!dst->maybe_flat_array() && !dst->maybe_null_free_array()) {
+    flags &= ~LIR_OpArrayCopy::dst_inlinetype_check;
+  }
+
   if (!src_objarray)
     flags &= ~LIR_OpArrayCopy::src_objarray;
   if (!dst_objarray)
     flags &= ~LIR_OpArrayCopy::dst_objarray;
 
@@ -1571,15 +1591,23 @@
     }
   }
 
   LIR_Opr result = new_register(t);
   __ move((LIR_Opr)c, result);
-  _constants.append(c);
-  _reg_for_constants.append(result);
+  if (!in_conditional_code()) {
+    _constants.append(c);
+    _reg_for_constants.append(result);
+  }
   return result;
 }
 
+void LIRGenerator::set_in_conditional_code(bool v) {
+  assert(v != _in_conditional_code, "must change state");
+  _in_conditional_code = v;
+}
+
+
 //------------------------field access--------------------------------------
 
 void LIRGenerator::do_CompareAndSwap(Intrinsic* x, ValueType* type) {
   assert(x->number_of_arguments() == 4, "wrong type");
   LIRItem obj   (x->argument_at(0), this);  // object
@@ -1688,18 +1716,193 @@
 
   access_store_at(decorators, field_type, object, LIR_OprFact::intConst(x->offset()),
                   value.result(), info != nullptr ? new CodeEmitInfo(info) : nullptr, info);
 }
 
+// FIXME -- I can't find any other way to pass an address to access_load_at().
+class TempResolvedAddress: public Instruction {
+ public:
+  TempResolvedAddress(ValueType* type, LIR_Opr addr) : Instruction(type) {
+    set_operand(addr);
+  }
+  virtual void input_values_do(ValueVisitor*) {}
+  virtual void visit(InstructionVisitor* v)   {}
+  virtual const char* name() const  { return "TempResolvedAddress"; }
+};
+
+LIR_Opr LIRGenerator::get_and_load_element_address(LIRItem& array, LIRItem& index) {
+  ciType* array_type = array.value()->declared_type();
+  ciFlatArrayKlass* flat_array_klass = array_type->as_flat_array_klass();
+  assert(flat_array_klass->is_loaded(), "must be");
+
+  int array_header_size = flat_array_klass->array_header_in_bytes();
+  int shift = flat_array_klass->log2_element_size();
+
+#ifndef _LP64
+  LIR_Opr index_op = new_register(T_INT);
+  // FIXME -- on 32-bit, the shift below can overflow, so we need to check that
+  // the top (shift+1) bits of index_op must be zero, or
+  // else throw ArrayIndexOutOfBoundsException
+  if (index.result()->is_constant()) {
+    jint const_index = index.result()->as_jint();
+    __ move(LIR_OprFact::intConst(const_index << shift), index_op);
+  } else {
+    __ shift_left(index_op, shift, index.result());
+  }
+#else
+  LIR_Opr index_op = new_register(T_LONG);
+  if (index.result()->is_constant()) {
+    jint const_index = index.result()->as_jint();
+    __ move(LIR_OprFact::longConst(const_index << shift), index_op);
+  } else {
+    __ convert(Bytecodes::_i2l, index.result(), index_op);
+    // Need to shift manually, as LIR_Address can scale only up to 3.
+    __ shift_left(index_op, shift, index_op);
+  }
+#endif
+
+  LIR_Opr elm_op = new_pointer_register();
+  LIR_Address* elm_address = generate_address(array.result(), index_op, 0, array_header_size, T_ADDRESS);
+  __ leal(LIR_OprFact::address(elm_address), elm_op);
+  return elm_op;
+}
+
+void LIRGenerator::access_sub_element(LIRItem& array, LIRItem& index, LIR_Opr& result, ciField* field, int sub_offset) {
+  assert(field != nullptr, "Need a subelement type specified");
+
+  // Find the starting address of the source (inside the array)
+  LIR_Opr elm_op = get_and_load_element_address(array, index);
+
+  BasicType subelt_type = field->type()->basic_type();
+  TempResolvedAddress* elm_resolved_addr = new TempResolvedAddress(as_ValueType(subelt_type), elm_op);
+  LIRItem elm_item(elm_resolved_addr, this);
+
+  DecoratorSet decorators = IN_HEAP;
+  access_load_at(decorators, subelt_type,
+                     elm_item, LIR_OprFact::intConst(sub_offset), result,
+                     nullptr, nullptr);
+
+  if (field->is_null_free()) {
+    assert(field->type()->is_loaded(), "Must be");
+    assert(field->type()->is_inlinetype(), "Must be if loaded");
+    assert(field->type()->as_inline_klass()->is_initialized(), "Must be");
+    LabelObj* L_end = new LabelObj();
+    __ cmp(lir_cond_notEqual, result, LIR_OprFact::oopConst(nullptr));
+    __ branch(lir_cond_notEqual, L_end->label());
+    set_in_conditional_code(true);
+    Constant* default_value = new Constant(new InstanceConstant(field->type()->as_inline_klass()->default_instance()));
+    if (default_value->is_pinned()) {
+      __ move(LIR_OprFact::value_type(default_value->type()), result);
+    } else {
+      __ move(load_constant(default_value), result);
+    }
+    __ branch_destination(L_end->label());
+    set_in_conditional_code(false);
+  }
+}
+
+void LIRGenerator::access_flat_array(bool is_load, LIRItem& array, LIRItem& index, LIRItem& obj_item,
+                                          ciField* field, int sub_offset) {
+  assert(sub_offset == 0 || field != nullptr, "Sanity check");
+
+  // Find the starting address of the source (inside the array)
+  LIR_Opr elm_op = get_and_load_element_address(array, index);
+
+  ciInlineKlass* elem_klass = nullptr;
+  if (field != nullptr) {
+    elem_klass = field->type()->as_inline_klass();
+  } else {
+    elem_klass = array.value()->declared_type()->as_flat_array_klass()->element_klass()->as_inline_klass();
+  }
+  for (int i = 0; i < elem_klass->nof_nonstatic_fields(); i++) {
+    ciField* inner_field = elem_klass->nonstatic_field_at(i);
+    assert(!inner_field->is_flat(), "flat fields must have been expanded");
+    int obj_offset = inner_field->offset_in_bytes();
+    int elm_offset = obj_offset - elem_klass->first_field_offset() + sub_offset; // object header is not stored in array.
+    BasicType field_type = inner_field->type()->basic_type();
+
+    // Types which are smaller than int are still passed in an int register.
+    BasicType reg_type = field_type;
+    switch (reg_type) {
+    case T_BYTE:
+    case T_BOOLEAN:
+    case T_SHORT:
+    case T_CHAR:
+      reg_type = T_INT;
+      break;
+    default:
+      break;
+    }
+
+    LIR_Opr temp = new_register(reg_type);
+    TempResolvedAddress* elm_resolved_addr = new TempResolvedAddress(as_ValueType(field_type), elm_op);
+    LIRItem elm_item(elm_resolved_addr, this);
+
+    DecoratorSet decorators = IN_HEAP;
+    if (is_load) {
+      access_load_at(decorators, field_type,
+                     elm_item, LIR_OprFact::intConst(elm_offset), temp,
+                     nullptr, nullptr);
+      access_store_at(decorators, field_type,
+                      obj_item, LIR_OprFact::intConst(obj_offset), temp,
+                      nullptr, nullptr);
+    } else {
+      access_load_at(decorators, field_type,
+                     obj_item, LIR_OprFact::intConst(obj_offset), temp,
+                     nullptr, nullptr);
+      access_store_at(decorators, field_type,
+                      elm_item, LIR_OprFact::intConst(elm_offset), temp,
+                      nullptr, nullptr);
+    }
+  }
+}
+
+void LIRGenerator::check_flat_array(LIR_Opr array, LIR_Opr value, CodeStub* slow_path) {
+  LIR_Opr tmp = new_register(T_METADATA);
+  __ check_flat_array(array, value, tmp, slow_path);
+}
+
+void LIRGenerator::check_null_free_array(LIRItem& array, LIRItem& value, CodeEmitInfo* info) {
+  LabelObj* L_end = new LabelObj();
+  LIR_Opr tmp = new_register(T_METADATA);
+  __ check_null_free_array(array.result(), tmp);
+  __ branch(lir_cond_equal, L_end->label());
+  __ null_check(value.result(), info);
+  __ branch_destination(L_end->label());
+}
+
+bool LIRGenerator::needs_flat_array_store_check(StoreIndexed* x) {
+  if (x->elt_type() == T_OBJECT && x->array()->maybe_flat_array()) {
+    ciType* type = x->value()->declared_type();
+    if (type != nullptr && type->is_klass()) {
+      ciKlass* klass = type->as_klass();
+      if (!klass->can_be_inline_klass() || (klass->is_inlinetype() && !klass->as_inline_klass()->flat_in_array())) {
+        // This is known to be a non-flat object. If the array is a flat array,
+        // it will be caught by the code generated by array_store_check().
+        return false;
+      }
+    }
+    // We're not 100% sure, so let's do the flat_array_store_check.
+    return true;
+  }
+  return false;
+}
+
+bool LIRGenerator::needs_null_free_array_store_check(StoreIndexed* x) {
+  return x->elt_type() == T_OBJECT && x->array()->maybe_null_free_array();
+}
+
 void LIRGenerator::do_StoreIndexed(StoreIndexed* x) {
   assert(x->is_pinned(),"");
+  assert(x->elt_type() != T_ARRAY, "never used");
+  bool is_loaded_flat_array = x->array()->is_loaded_flat_array();
   bool needs_range_check = x->compute_needs_range_check();
   bool use_length = x->length() != nullptr;
   bool obj_store = is_reference_type(x->elt_type());
-  bool needs_store_check = obj_store && (x->value()->as_Constant() == nullptr ||
-                                         !get_jobject_constant(x->value())->is_null_object() ||
-                                         x->should_profile());
+  bool needs_store_check = obj_store && !(is_loaded_flat_array && x->is_exact_flat_array_store()) &&
+                                        (x->value()->as_Constant() == nullptr ||
+                                         !get_jobject_constant(x->value())->is_null_object());
 
   LIRItem array(x->array(), this);
   LIRItem index(x->index(), this);
   LIRItem value(x->value(), this);
   LIRItem length(this);
@@ -1708,13 +1911,14 @@
   index.load_nonconstant();
 
   if (use_length && needs_range_check) {
     length.set_instruction(x->length());
     length.load_item();
-
   }
-  if (needs_store_check || x->check_boolean()) {
+
+  if (needs_store_check || x->check_boolean()
+      || is_loaded_flat_array || needs_flat_array_store_check(x) || needs_null_free_array_store_check(x)) {
     value.load_item();
   } else {
     value.load_for_store(x->elt_type());
   }
 
@@ -1738,22 +1942,69 @@
       // range_check also does the null check
       null_check_info = nullptr;
     }
   }
 
+  if (x->should_profile()) {
+    if (x->array()->is_loaded_flat_array()) {
+      // No need to profile a store to a flat array of known type. This can happen if
+      // the type only became known after optimizations (for example, after the PhiSimplifier).
+      x->set_should_profile(false);
+    } else {
+      int bci = x->profiled_bci();
+      ciMethodData* md = x->profiled_method()->method_data();
+      assert(md != nullptr, "Sanity");
+      ciProfileData* data = md->bci_to_data(bci);
+      assert(data != nullptr && data->is_ArrayStoreData(), "incorrect profiling entry");
+      ciArrayStoreData* store_data = (ciArrayStoreData*)data;
+      profile_array_type(x, md, store_data);
+      assert(store_data->is_ArrayStoreData(), "incorrect profiling entry");
+      if (x->array()->maybe_null_free_array()) {
+        profile_null_free_array(array, md, store_data);
+      }
+    }
+  }
+
   if (GenerateArrayStoreCheck && needs_store_check) {
     CodeEmitInfo* store_check_info = new CodeEmitInfo(range_check_info);
     array_store_check(value.result(), array.result(), store_check_info, x->profiled_method(), x->profiled_bci());
   }
 
-  DecoratorSet decorators = IN_HEAP | IS_ARRAY;
-  if (x->check_boolean()) {
-    decorators |= C1_MASK_BOOLEAN;
-  }
+  if (is_loaded_flat_array) {
+    if (!x->value()->is_null_free()) {
+      __ null_check(value.result(), new CodeEmitInfo(range_check_info));
+    }
+    // If array element is an empty inline type, no need to copy anything
+    if (!x->array()->declared_type()->as_flat_array_klass()->element_klass()->as_inline_klass()->is_empty()) {
+      access_flat_array(false, array, index, value);
+    }
+  } else {
+    StoreFlattenedArrayStub* slow_path = nullptr;
+
+    if (needs_flat_array_store_check(x)) {
+      // Check if we indeed have a flat array
+      index.load_item();
+      slow_path = new StoreFlattenedArrayStub(array.result(), index.result(), value.result(), state_for(x, x->state_before()));
+      check_flat_array(array.result(), value.result(), slow_path);
+      set_in_conditional_code(true);
+    } else if (needs_null_free_array_store_check(x)) {
+      CodeEmitInfo* info = new CodeEmitInfo(range_check_info);
+      check_null_free_array(array, value, info);
+    }
+
+    DecoratorSet decorators = IN_HEAP | IS_ARRAY;
+    if (x->check_boolean()) {
+      decorators |= C1_MASK_BOOLEAN;
+    }
 
-  access_store_at(decorators, x->elt_type(), array, index.result(), value.result(),
-                  nullptr, null_check_info);
+    access_store_at(decorators, x->elt_type(), array, index.result(), value.result(),
+                    nullptr, null_check_info);
+    if (slow_path != nullptr) {
+      __ branch_destination(slow_path->continuation());
+      set_in_conditional_code(false);
+    }
+  }
 }
 
 void LIRGenerator::access_load_at(DecoratorSet decorators, BasicType type,
                                   LIRItem& base, LIR_Opr offset, LIR_Opr result,
                                   CodeEmitInfo* patch_info, CodeEmitInfo* load_emit_info) {
@@ -1887,10 +2138,44 @@
 
   LIR_Opr result = rlock_result(x, field_type);
   access_load_at(decorators, field_type,
                  object, LIR_OprFact::intConst(x->offset()), result,
                  info ? new CodeEmitInfo(info) : nullptr, info);
+
+  ciField* field = x->field();
+  if (field->is_null_free()) {
+    // Load from non-flat inline type field requires
+    // a null check to replace null with the default value.
+    ciInstanceKlass* holder = field->holder();
+    if (field->is_static() && holder->is_loaded()) {
+      ciObject* val = holder->java_mirror()->field_value(field).as_object();
+      if (!val->is_null_object()) {
+        // Static field is initialized, we don't need to perform a null check.
+        return;
+      }
+    }
+    ciInlineKlass* inline_klass = field->type()->as_inline_klass();
+    if (inline_klass->is_initialized()) {
+      LabelObj* L_end = new LabelObj();
+      __ cmp(lir_cond_notEqual, result, LIR_OprFact::oopConst(nullptr));
+      __ branch(lir_cond_notEqual, L_end->label());
+      set_in_conditional_code(true);
+      Constant* default_value = new Constant(new InstanceConstant(inline_klass->default_instance()));
+      if (default_value->is_pinned()) {
+        __ move(LIR_OprFact::value_type(default_value->type()), result);
+      } else {
+        __ move(load_constant(default_value), result);
+      }
+      __ branch_destination(L_end->label());
+      set_in_conditional_code(false);
+    } else {
+      info = state_for(x, x->state_before());
+      __ cmp(lir_cond_equal, result, LIR_OprFact::oopConst(nullptr));
+      __ branch(lir_cond_equal, new DeoptimizeStub(info, Deoptimization::Reason_uninitialized,
+                                                         Deoptimization::Action_make_not_entrant));
+    }
+  }
 }
 
 // int/long jdk.internal.util.Preconditions.checkIndex
 void LIRGenerator::do_PreconditionsCheckIndex(Intrinsic* x, BasicType type) {
   assert(x->number_of_arguments() == 3, "wrong type");
@@ -2031,16 +2316,88 @@
       // The range check performs the null check, so clear it out for the load
       null_check_info = nullptr;
     }
   }
 
-  DecoratorSet decorators = IN_HEAP | IS_ARRAY;
+  ciMethodData* md = nullptr;
+  ciArrayLoadData* load_data = nullptr;
+  if (x->should_profile()) {
+    if (x->array()->is_loaded_flat_array()) {
+      // No need to profile a load from a flat array of known type. This can happen if
+      // the type only became known after optimizations (for example, after the PhiSimplifier).
+      x->set_should_profile(false);
+    } else {
+      int bci = x->profiled_bci();
+      md = x->profiled_method()->method_data();
+      assert(md != nullptr, "Sanity");
+      ciProfileData* data = md->bci_to_data(bci);
+      assert(data != nullptr && data->is_ArrayLoadData(), "incorrect profiling entry");
+      load_data = (ciArrayLoadData*)data;
+      profile_array_type(x, md, load_data);
+    }
+  }
+
+  Value element;
+  if (x->vt() != nullptr) {
+    assert(x->array()->is_loaded_flat_array(), "must be");
+    // Find the destination address (of the NewInlineTypeInstance).
+    LIRItem obj_item(x->vt(), this);
+
+    access_flat_array(true, array, index, obj_item,
+                      x->delayed() == nullptr ? 0 : x->delayed()->field(),
+                      x->delayed() == nullptr ? 0 : x->delayed()->offset());
+    set_no_result(x);
+  } else if (x->delayed() != nullptr) {
+    assert(x->array()->is_loaded_flat_array(), "must be");
+    LIR_Opr result = rlock_result(x, x->delayed()->field()->type()->basic_type());
+    access_sub_element(array, index, result, x->delayed()->field(), x->delayed()->offset());
+  } else if (x->array() != nullptr && x->array()->is_loaded_flat_array() &&
+             x->array()->declared_type()->as_flat_array_klass()->element_klass()->as_inline_klass()->is_initialized() &&
+             x->array()->declared_type()->as_flat_array_klass()->element_klass()->as_inline_klass()->is_empty()) {
+    // Load the default instance instead of reading the element
+    ciInlineKlass* elem_klass = x->array()->declared_type()->as_flat_array_klass()->element_klass()->as_inline_klass();
+    LIR_Opr result = rlock_result(x, x->elt_type());
+    assert(elem_klass->is_initialized(), "Must be");
+    Constant* default_value = new Constant(new InstanceConstant(elem_klass->default_instance()));
+    if (default_value->is_pinned()) {
+      __ move(LIR_OprFact::value_type(default_value->type()), result);
+    } else {
+      __ move(load_constant(default_value), result);
+    }
+  } else {
+    LIR_Opr result = rlock_result(x, x->elt_type());
+    LoadFlattenedArrayStub* slow_path = nullptr;
+
+    if (x->should_profile() && x->array()->maybe_null_free_array()) {
+      profile_null_free_array(array, md, load_data);
+    }
+
+    if (x->elt_type() == T_OBJECT && x->array()->maybe_flat_array()) {
+      assert(x->delayed() == nullptr, "Delayed LoadIndexed only apply to loaded_flat_arrays");
+      index.load_item();
+      // if we are loading from a flat array, load it using a runtime call
+      slow_path = new LoadFlattenedArrayStub(array.result(), index.result(), result, state_for(x, x->state_before()));
+      check_flat_array(array.result(), LIR_OprFact::illegalOpr, slow_path);
+      set_in_conditional_code(true);
+    }
+
+    DecoratorSet decorators = IN_HEAP | IS_ARRAY;
+    access_load_at(decorators, x->elt_type(),
+                   array, index.result(), result,
+                   nullptr, null_check_info);
+
+    if (slow_path != nullptr) {
+      __ branch_destination(slow_path->continuation());
+      set_in_conditional_code(false);
+    }
+
+    element = x;
+  }
 
-  LIR_Opr result = rlock_result(x, x->elt_type());
-  access_load_at(decorators, x->elt_type(),
-                 array, index.result(), result,
-                 nullptr, null_check_info);
+  if (x->should_profile()) {
+    profile_element_type(element, md, load_data);
+  }
 }
 
 
 void LIRGenerator::do_NullCheck(NullCheck* x) {
   if (x->can_trap()) {
@@ -2538,11 +2895,11 @@
   if (!do_null && !do_update) {
     return result;
   }
 
   ciKlass* exact_signature_k = nullptr;
-  if (do_update) {
+  if (do_update && signature_at_call_k != nullptr) {
     // Is the type from the signature exact (the only one possible)?
     exact_signature_k = signature_at_call_k->exact_klass();
     if (exact_signature_k == nullptr) {
       exact_signature_k = comp->cha_exact_type(signature_at_call_k);
     } else {
@@ -2623,10 +2980,50 @@
       }
     }
   }
 }
 
+void LIRGenerator::profile_flags(ciMethodData* md, ciProfileData* data, int flag, LIR_Condition condition) {
+  assert(md != nullptr && data != nullptr, "should have been initialized");
+  LIR_Opr mdp = new_register(T_METADATA);
+  __ metadata2reg(md->constant_encoding(), mdp);
+  LIR_Address* addr = new LIR_Address(mdp, md->byte_offset_of_slot(data, DataLayout::flags_offset()), T_BYTE);
+  LIR_Opr flags = new_register(T_INT);
+  __ move(addr, flags);
+  if (condition != lir_cond_always) {
+    LIR_Opr update = new_register(T_INT);
+    __ cmove(condition, LIR_OprFact::intConst(0), LIR_OprFact::intConst(flag), update, T_INT);
+  } else {
+    __ logical_or(flags, LIR_OprFact::intConst(flag), flags);
+  }
+  __ store(flags, addr);
+}
+
+template <class ArrayData> void LIRGenerator::profile_null_free_array(LIRItem array, ciMethodData* md, ArrayData* load_store) {
+  assert(compilation()->profile_array_accesses(), "array access profiling is disabled");
+  LabelObj* L_end = new LabelObj();
+  LIR_Opr tmp = new_register(T_METADATA);
+  __ check_null_free_array(array.result(), tmp);
+
+  profile_flags(md, load_store, ArrayStoreData::null_free_array_byte_constant(), lir_cond_equal);
+}
+
+template <class ArrayData> void LIRGenerator::profile_array_type(AccessIndexed* x, ciMethodData*& md, ArrayData*& load_store) {
+  assert(compilation()->profile_array_accesses(), "array access profiling is disabled");
+  LIR_Opr mdp = LIR_OprFact::illegalOpr;
+  profile_type(md, md->byte_offset_of_slot(load_store, ArrayData::array_offset()), 0,
+               load_store->array()->type(), x->array(), mdp, true, nullptr, nullptr);
+}
+
+void LIRGenerator::profile_element_type(Value element, ciMethodData* md, ciArrayLoadData* load_data) {
+  assert(compilation()->profile_array_accesses(), "array access profiling is disabled");
+  assert(md != nullptr && load_data != nullptr, "should have been initialized");
+  LIR_Opr mdp = LIR_OprFact::illegalOpr;
+  profile_type(md, md->byte_offset_of_slot(load_data, ArrayLoadData::element_offset()), 0,
+               load_data->element()->type(), element, mdp, false, nullptr, nullptr);
+}
+
 void LIRGenerator::do_Base(Base* x) {
   __ std_entry(LIR_OprFact::illegalOpr);
   // Emit moves from physical registers / stack slots to virtual registers
   CallingConvention* args = compilation()->frame_map()->incoming_arguments();
   IRScope* irScope = compilation()->hir()->top_scope();
@@ -2703,10 +3100,18 @@
   if (!method()->is_accessor()) { // Accessors do not have MDOs, so no counting.
     profile_parameters(x);
     CodeEmitInfo* info = new CodeEmitInfo(scope()->start()->state()->copy(ValueStack::StateBefore, SynchronizationEntryBCI), nullptr, false);
     increment_invocation_counter(info);
   }
+  if (method()->has_scalarized_args()) {
+    // Check if deoptimization was triggered (i.e. orig_pc was set) while buffering scalarized inline type arguments
+    // in the entry point (see comments in frame::deoptimize). If so, deoptimize only now that we have the right state.
+    CodeEmitInfo* info = new CodeEmitInfo(scope()->start()->state()->copy(ValueStack::StateBefore, 0), nullptr, false);
+    CodeStub* deopt_stub = new DeoptimizeStub(info, Deoptimization::Reason_none, Deoptimization::Action_none);
+    __ append(new LIR_Op0(lir_check_orig_pc));
+    __ branch(lir_cond_notEqual, deopt_stub);
+  }
 
   // all blocks with a successor must end with an unconditional jump
   // to the successor even if they are consecutive
   __ jump(x->default_sux());
 }
@@ -2718,27 +3123,31 @@
   __ osr_entry(LIR_Assembler::osrBufferPointer());
   LIR_Opr result = rlock_result(x);
   __ move(LIR_Assembler::osrBufferPointer(), result);
 }
 
+void LIRGenerator::invoke_load_one_argument(LIRItem* param, LIR_Opr loc) {
+  if (loc->is_register()) {
+    param->load_item_force(loc);
+  } else {
+    LIR_Address* addr = loc->as_address_ptr();
+    param->load_for_store(addr->type());
+    if (addr->type() == T_OBJECT) {
+      __ move_wide(param->result(), addr);
+    } else {
+      __ move(param->result(), addr);
+    }
+  }
+}
 
 void LIRGenerator::invoke_load_arguments(Invoke* x, LIRItemList* args, const LIR_OprList* arg_list) {
   assert(args->length() == arg_list->length(),
          "args=%d, arg_list=%d", args->length(), arg_list->length());
   for (int i = x->has_receiver() ? 1 : 0; i < args->length(); i++) {
     LIRItem* param = args->at(i);
     LIR_Opr loc = arg_list->at(i);
-    if (loc->is_register()) {
-      param->load_item_force(loc);
-    } else {
-      LIR_Address* addr = loc->as_address_ptr();
-      param->load_for_store(addr->type());
-      if (addr->type() == T_OBJECT) {
-        __ move_wide(param->result(), addr);
-      } else
-        __ move(param->result(), addr);
-    }
+    invoke_load_one_argument(param, loc);
   }
 
   if (x->has_receiver()) {
     LIRItem* receiver = args->at(0);
     LIR_Opr loc = arg_list->at(0);
@@ -2900,24 +3309,82 @@
 #endif
 
   LIRItem left(x->x(), this);
   LIRItem right(x->y(), this);
   left.load_item();
-  if (can_inline_as_constant(right.value())) {
+  if (can_inline_as_constant(right.value()) && !x->substitutability_check()) {
     right.dont_load_item();
   } else {
+    // substitutability_check() needs to use right as a base register.
     right.load_item();
   }
 
   LIRItem t_val(x->tval(), this);
   LIRItem f_val(x->fval(), this);
   t_val.dont_load_item();
   f_val.dont_load_item();
-  LIR_Opr reg = rlock_result(x);
 
-  __ cmp(lir_cond(x->cond()), left.result(), right.result());
-  __ cmove(lir_cond(x->cond()), t_val.result(), f_val.result(), reg, as_BasicType(x->x()->type()));
+  if (x->substitutability_check()) {
+    substitutability_check(x, left, right, t_val, f_val);
+  } else {
+    LIR_Opr reg = rlock_result(x);
+    __ cmp(lir_cond(x->cond()), left.result(), right.result());
+    __ cmove(lir_cond(x->cond()), t_val.result(), f_val.result(), reg, as_BasicType(x->x()->type()));
+  }
+}
+
+void LIRGenerator::substitutability_check(IfOp* x, LIRItem& left, LIRItem& right, LIRItem& t_val, LIRItem& f_val) {
+  assert(x->cond() == If::eql || x->cond() == If::neq, "must be");
+  bool is_acmpeq = (x->cond() == If::eql);
+  LIR_Opr equal_result     = is_acmpeq ? t_val.result() : f_val.result();
+  LIR_Opr not_equal_result = is_acmpeq ? f_val.result() : t_val.result();
+  LIR_Opr result = rlock_result(x);
+  CodeEmitInfo* info = state_for(x, x->state_before());
+
+  substitutability_check_common(x->x(), x->y(), left, right, equal_result, not_equal_result, result, info);
+}
+
+void LIRGenerator::substitutability_check(If* x, LIRItem& left, LIRItem& right) {
+  LIR_Opr equal_result     = LIR_OprFact::intConst(1);
+  LIR_Opr not_equal_result = LIR_OprFact::intConst(0);
+  LIR_Opr result = new_register(T_INT);
+  CodeEmitInfo* info = state_for(x, x->state_before());
+
+  substitutability_check_common(x->x(), x->y(), left, right, equal_result, not_equal_result, result, info);
+
+  assert(x->cond() == If::eql || x->cond() == If::neq, "must be");
+  __ cmp(lir_cond(x->cond()), result, equal_result);
+}
+
+void LIRGenerator::substitutability_check_common(Value left_val, Value right_val, LIRItem& left, LIRItem& right,
+                                                 LIR_Opr equal_result, LIR_Opr not_equal_result, LIR_Opr result,
+                                                 CodeEmitInfo* info) {
+  LIR_Opr tmp1 = LIR_OprFact::illegalOpr;
+  LIR_Opr tmp2 = LIR_OprFact::illegalOpr;
+  LIR_Opr left_klass_op = LIR_OprFact::illegalOpr;
+  LIR_Opr right_klass_op = LIR_OprFact::illegalOpr;
+
+  ciKlass* left_klass  = left_val ->as_loaded_klass_or_null();
+  ciKlass* right_klass = right_val->as_loaded_klass_or_null();
+
+  if ((left_klass == nullptr || right_klass == nullptr) ||// The klass is still unloaded, or came from a Phi node.
+      !left_klass->is_inlinetype() || !right_klass->is_inlinetype()) {
+    init_temps_for_substitutability_check(tmp1, tmp2);
+  }
+
+  if (left_klass != nullptr && left_klass->is_inlinetype() && left_klass == right_klass) {
+    // No need to load klass -- the operands are statically known to be the same inline klass.
+  } else {
+    BasicType t_klass = UseCompressedOops ? T_INT : T_METADATA;
+    left_klass_op = new_register(t_klass);
+    right_klass_op = new_register(t_klass);
+  }
+
+  CodeStub* slow_path = new SubstitutabilityCheckStub(left.result(), right.result(), info);
+  __ substitutability_check(result, left.result(), right.result(), equal_result, not_equal_result,
+                            tmp1, tmp2,
+                            left_klass, right_klass, left_klass_op, right_klass_op, info, slow_path);
 }
 
 void LIRGenerator::do_RuntimeCall(address routine, Intrinsic* x) {
   assert(x->number_of_arguments() == 0, "wrong type");
   // Enforce computation of _reserved_argument_area_size which is required on some platforms.
@@ -3187,11 +3654,11 @@
   ciMethodData* md = x->method()->method_data_or_null();
   assert(md != nullptr, "Sanity");
   ciProfileData* data = md->bci_to_data(bci);
   if (data != nullptr) {
     assert(data->is_CallTypeData() || data->is_VirtualCallTypeData(), "wrong profile data type");
-    ciReturnTypeEntry* ret = data->is_CallTypeData() ? ((ciCallTypeData*)data)->ret() : ((ciVirtualCallTypeData*)data)->ret();
+    ciSingleTypeEntry* ret = data->is_CallTypeData() ? ((ciCallTypeData*)data)->ret() : ((ciVirtualCallTypeData*)data)->ret();
     LIR_Opr mdp = LIR_OprFact::illegalOpr;
 
     bool ignored_will_link;
     ciSignature* signature_at_call = nullptr;
     x->method()->get_method_at_bci(bci, ignored_will_link, &signature_at_call);
@@ -3208,10 +3675,57 @@
       md->set_return_type(bci, exact);
     }
   }
 }
 
+bool LIRGenerator::profile_inline_klass(ciMethodData* md, ciProfileData* data, Value value, int flag) {
+  ciKlass* klass = value->as_loaded_klass_or_null();
+  if (klass != nullptr) {
+    if (klass->is_inlinetype()) {
+      profile_flags(md, data, flag, lir_cond_always);
+    } else if (klass->can_be_inline_klass()) {
+      return false;
+    }
+  } else {
+    return false;
+  }
+  return true;
+}
+
+
+void LIRGenerator::do_ProfileACmpTypes(ProfileACmpTypes* x) {
+  ciMethod* method = x->method();
+  assert(method != nullptr, "method should be set if branch is profiled");
+  ciMethodData* md = method->method_data_or_null();
+  assert(md != nullptr, "Sanity");
+  ciProfileData* data = md->bci_to_data(x->bci());
+  assert(data != nullptr, "must have profiling data");
+  assert(data->is_ACmpData(), "need BranchData for two-way branches");
+  ciACmpData* acmp = (ciACmpData*)data;
+  LIR_Opr mdp = LIR_OprFact::illegalOpr;
+  profile_type(md, md->byte_offset_of_slot(acmp, ACmpData::left_offset()), 0,
+               acmp->left()->type(), x->left(), mdp, !x->left_maybe_null(), nullptr, nullptr);
+  int flags_offset = md->byte_offset_of_slot(data, DataLayout::flags_offset());
+  if (!profile_inline_klass(md, acmp, x->left(), ACmpData::left_inline_type_byte_constant())) {
+    LIR_Opr mdp = new_register(T_METADATA);
+    __ metadata2reg(md->constant_encoding(), mdp);
+    LIRItem value(x->left(), this);
+    value.load_item();
+    __ profile_inline_type(new LIR_Address(mdp, flags_offset, T_INT), value.result(), ACmpData::left_inline_type_byte_constant(), new_register(T_INT), !x->left_maybe_null());
+  }
+  profile_type(md, md->byte_offset_of_slot(acmp, ACmpData::left_offset()),
+               in_bytes(ACmpData::right_offset()) - in_bytes(ACmpData::left_offset()),
+               acmp->right()->type(), x->right(), mdp, !x->right_maybe_null(), nullptr, nullptr);
+  if (!profile_inline_klass(md, acmp, x->right(), ACmpData::right_inline_type_byte_constant())) {
+    LIR_Opr mdp = new_register(T_METADATA);
+    __ metadata2reg(md->constant_encoding(), mdp);
+    LIRItem value(x->right(), this);
+    value.load_item();
+    __ profile_inline_type(new LIR_Address(mdp, flags_offset, T_INT), value.result(), ACmpData::right_inline_type_byte_constant(), new_register(T_INT), !x->left_maybe_null());
+  }
+}
+
 void LIRGenerator::do_ProfileInvoke(ProfileInvoke* x) {
   // We can safely ignore accessors here, since c2 will inline them anyway,
   // accessors are also always mature.
   if (!x->inlinee()->is_accessor()) {
     CodeEmitInfo* info = state_for(x, x->state(), true);
diff a/src/hotspot/share/c1/c1_LIRGenerator.hpp b/src/hotspot/share/c1/c1_LIRGenerator.hpp
--- a/src/hotspot/share/c1/c1_LIRGenerator.hpp
+++ b/src/hotspot/share/c1/c1_LIRGenerator.hpp
@@ -167,10 +167,11 @@
   BlockBegin*   _block;
   int           _virtual_register_number;
   Values        _instruction_for_operand;
   BitMap2D      _vreg_flags; // flags which can be set on a per-vreg basis
   LIR_List*     _lir;
+  bool          _in_conditional_code;
 
   LIRGenerator* gen() {
     return this;
   }
 
@@ -193,10 +194,11 @@
   LIR_OprList                     _reg_for_constants;
   Values                          _unpinned_constants;
 
   friend class PhiResolver;
 
+  void set_in_conditional_code(bool v);
  public:
   // unified bailout support
   void bailout(const char* msg) const            { compilation()->bailout(msg); }
   bool bailed_out() const                        { return compilation()->bailed_out(); }
 
@@ -212,10 +214,11 @@
 
   // get a constant into a register and get track of what register was used
   LIR_Opr load_constant(Constant* x);
   LIR_Opr load_constant(LIR_Const* constant);
 
+  bool in_conditional_code() { return _in_conditional_code; }
   // Given an immediate value, return an operand usable in logical ops.
   LIR_Opr load_immediate(jlong x, BasicType type);
 
   void  set_result(Value x, LIR_Opr opr)           {
     assert(opr->is_valid(), "must set to valid value");
@@ -271,10 +274,23 @@
   void do_update_CRC32(Intrinsic* x);
   void do_update_CRC32C(Intrinsic* x);
   void do_vectorizedMismatch(Intrinsic* x);
   void do_blackhole(Intrinsic* x);
 
+  void access_flat_array(bool is_load, LIRItem& array, LIRItem& index, LIRItem& obj_item, ciField* field = nullptr, int offset = 0);
+  void access_sub_element(LIRItem& array, LIRItem& index, LIR_Opr& result, ciField* field, int sub_offset);
+  LIR_Opr get_and_load_element_address(LIRItem& array, LIRItem& index);
+  bool needs_flat_array_store_check(StoreIndexed* x);
+  void check_flat_array(LIR_Opr array, LIR_Opr value, CodeStub* slow_path);
+  bool needs_null_free_array_store_check(StoreIndexed* x);
+  void check_null_free_array(LIRItem& array, LIRItem& value,  CodeEmitInfo* info);
+  void substitutability_check(IfOp* x, LIRItem& left, LIRItem& right, LIRItem& t_val, LIRItem& f_val);
+  void substitutability_check(If* x, LIRItem& left, LIRItem& right);
+  void substitutability_check_common(Value left_val, Value right_val, LIRItem& left, LIRItem& right,
+                                     LIR_Opr equal_result, LIR_Opr not_equal_result, LIR_Opr result, CodeEmitInfo* info);
+  void init_temps_for_substitutability_check(LIR_Opr& tmp1, LIR_Opr& tmp2);
+
  public:
   LIR_Opr call_runtime(BasicTypeArray* signature, LIRItemList* args, address entry, ValueType* result_type, CodeEmitInfo* info);
   LIR_Opr call_runtime(BasicTypeArray* signature, LIR_OprList* args, address entry, ValueType* result_type, CodeEmitInfo* info);
 
   // convenience functions
@@ -324,11 +340,11 @@
 
   ciObject* get_jobject_constant(Value value);
 
   LIRItemList* invoke_visit_arguments(Invoke* x);
   void invoke_load_arguments(Invoke* x, LIRItemList* args, const LIR_OprList* arg_list);
-
+  void invoke_load_one_argument(LIRItem* param, LIR_Opr loc);
   void trace_block_entry(BlockBegin* block);
 
   // volatile field operations are never patchable because a klass
   // must be loaded to know it's volatile which means that the offset
   // it always known as well.
@@ -360,14 +376,14 @@
 
   void shift_op   (Bytecodes::Code code, LIR_Opr dst_reg, LIR_Opr value, LIR_Opr count, LIR_Opr tmp);
 
   void logic_op   (Bytecodes::Code code, LIR_Opr dst_reg, LIR_Opr left, LIR_Opr right);
 
-  void monitor_enter (LIR_Opr object, LIR_Opr lock, LIR_Opr hdr, LIR_Opr scratch, int monitor_no, CodeEmitInfo* info_for_exception, CodeEmitInfo* info);
+  void monitor_enter (LIR_Opr object, LIR_Opr lock, LIR_Opr hdr, LIR_Opr scratch, int monitor_no, CodeEmitInfo* info_for_exception, CodeEmitInfo* info, CodeStub* throw_imse_stub);
   void monitor_exit  (LIR_Opr object, LIR_Opr lock, LIR_Opr hdr, LIR_Opr scratch, int monitor_no);
 
-  void new_instance    (LIR_Opr  dst, ciInstanceKlass* klass, bool is_unresolved, LIR_Opr  scratch1, LIR_Opr  scratch2, LIR_Opr  scratch3,  LIR_Opr scratch4, LIR_Opr  klass_reg, CodeEmitInfo* info);
+  void new_instance(LIR_Opr dst, ciInstanceKlass* klass, bool is_unresolved, bool allow_inline, LIR_Opr scratch1, LIR_Opr scratch2, LIR_Opr scratch3, LIR_Opr scratch4, LIR_Opr klass_reg, CodeEmitInfo* info);
 
   // machine dependent
   void cmp_mem_int(LIR_Condition condition, LIR_Opr base, int disp, int c, CodeEmitInfo* info);
   void cmp_reg_mem(LIR_Condition condition, LIR_Opr reg, LIR_Opr base, int disp, BasicType type, CodeEmitInfo* info);
 
@@ -475,10 +491,15 @@
                         Value arg, LIR_Opr& mdp, bool not_null, ciKlass* signature_at_call_k,
                         ciKlass* callee_signature_k);
   void profile_arguments(ProfileCall* x);
   void profile_parameters(Base* x);
   void profile_parameters_at_call(ProfileCall* x);
+  void profile_flags(ciMethodData* md, ciProfileData* load_store, int flag, LIR_Condition condition = lir_cond_always);
+  template <class ArrayData> void profile_null_free_array(LIRItem array, ciMethodData* md, ArrayData* load_store);
+  template <class ArrayData> void profile_array_type(AccessIndexed* x, ciMethodData*& md, ArrayData*& load_store);
+  void profile_element_type(Value element, ciMethodData* md, ciArrayLoadData* load_store);
+  bool profile_inline_klass(ciMethodData* md, ciProfileData* data, Value value, int flag);
   LIR_Opr mask_boolean(LIR_Opr array, LIR_Opr value, CodeEmitInfo*& null_check_info);
 
  public:
   Compilation*  compilation() const              { return _compilation; }
   FrameMap*     frame_map() const                { return _compilation->frame_map(); }
@@ -502,10 +523,11 @@
   LIRGenerator(Compilation* compilation, ciMethod* method)
     : _compilation(compilation)
     , _method(method)
     , _virtual_register_number(LIR_Opr::vreg_base)
     , _vreg_flags(num_vreg_flags)
+    , _in_conditional_code(false)
     , _barrier_set(BarrierSet::barrier_set()->barrier_set_c1()) {
   }
 
   // for virtual registers, maps them back to Phi's or Local's
   Instruction* instruction_for_opr(LIR_Opr opr);
@@ -580,10 +602,11 @@
   virtual void do_UnsafePut      (UnsafePut*       x);
   virtual void do_UnsafeGetAndSet(UnsafeGetAndSet* x);
   virtual void do_ProfileCall    (ProfileCall*     x);
   virtual void do_ProfileReturnType (ProfileReturnType* x);
   virtual void do_ProfileInvoke  (ProfileInvoke*   x);
+  virtual void do_ProfileACmpTypes(ProfileACmpTypes* x);
   virtual void do_RuntimeCall    (RuntimeCall*     x);
   virtual void do_MemBar         (MemBar*          x);
   virtual void do_RangeCheckPredicate(RangeCheckPredicate* x);
 #ifdef ASSERT
   virtual void do_Assert         (Assert*          x);
diff a/src/hotspot/share/c1/c1_LinearScan.cpp b/src/hotspot/share/c1/c1_LinearScan.cpp
--- a/src/hotspot/share/c1/c1_LinearScan.cpp
+++ b/src/hotspot/share/c1/c1_LinearScan.cpp
@@ -61,13 +61,13 @@
   #define TRACE_LINEAR_SCAN(level, code)
 #endif
 
 // Map BasicType to spill size in 32-bit words, matching VMReg's notion of words
 #ifdef _LP64
-static int type2spill_size[T_CONFLICT+1]={ -1, 0, 0, 0, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 0, 2,  1, 2, 1, -1};
+static int type2spill_size[T_CONFLICT+1]={ -1, 0, 0, 0, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2, 0, 2,  1, 2, 1, -1};
 #else
-static int type2spill_size[T_CONFLICT+1]={ -1, 0, 0, 0, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 0, 1, -1, 1, 1, -1};
+static int type2spill_size[T_CONFLICT+1]={ -1, 0, 0, 0, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 0, 1, -1, 1, 1, -1};
 #endif
 
 
 // Implementation of LinearScan
 
@@ -258,11 +258,11 @@
     it->assign_reg(spill);
   }
 }
 
 void LinearScan::propagate_spill_slots() {
-  if (!frame_map()->finalize_frame(max_spills())) {
+  if (!frame_map()->finalize_frame(max_spills(), compilation()->needs_stack_repair())) {
     bailout("frame too large");
   }
 }
 
 // create a new interval with a predefined reg_num
@@ -2949,11 +2949,11 @@
     for (int i = 0; i < nof_locks; i++) {
       monitors->append(location_for_monitor_index(lock_offset + i));
     }
   }
 
-  return new IRScopeDebugInfo(cur_scope, cur_state->bci(), locals, expressions, monitors, caller_debug_info);
+  return new IRScopeDebugInfo(cur_scope, cur_state->bci(), locals, expressions, monitors, caller_debug_info, cur_state->should_reexecute());
 }
 
 
 void LinearScan::compute_debug_info(CodeEmitInfo* info, int op_id) {
   TRACE_LINEAR_SCAN(3, tty->print_cr("creating debug information at op_id %d", op_id));
diff a/src/hotspot/share/c1/c1_MacroAssembler.hpp b/src/hotspot/share/c1/c1_MacroAssembler.hpp
--- a/src/hotspot/share/c1/c1_MacroAssembler.hpp
+++ b/src/hotspot/share/c1/c1_MacroAssembler.hpp
@@ -27,24 +27,33 @@
 
 #include "asm/macroAssembler.hpp"
 #include "utilities/macros.hpp"
 
 class CodeEmitInfo;
-
+class CompiledEntrySignature;
 class C1_MacroAssembler: public MacroAssembler {
+ private:
+  int scalarized_entry(const CompiledEntrySignature* ces, int frame_size_in_bytes, int bang_size_in_bytes, int sp_offset_for_orig_pc, Label& verified_inline_entry_label, bool is_inline_ro_entry);
+  void build_frame_helper(int frame_size_in_bytes, int sp_offset_for_orig_pc, int sp_inc, bool reset_orig_pc, bool needs_stack_repair);
  public:
   // creation
   C1_MacroAssembler(CodeBuffer* code) : MacroAssembler(code) { pd_init(); }
 
   //----------------------------------------------------
   void explicit_null_check(Register base);
 
   void inline_cache_check(Register receiver, Register iCache);
-  void build_frame(int frame_size_in_bytes, int bang_size_in_bytes);
-  void remove_frame(int frame_size_in_bytes);
-
+  void build_frame(int frame_size_in_bytes, int bang_size_in_bytes, int sp_offset_for_orig_pc = 0, bool needs_stack_repair = false, bool has_scalarized_args = false, Label* verified_inline_entry_label = nullptr);
+
+  int verified_entry(const CompiledEntrySignature* ces, int frame_size_in_bytes, int bang_size_in_bytes, int sp_offset_for_orig_pc, Label& verified_inline_entry_label) {
+    return scalarized_entry(ces, frame_size_in_bytes, bang_size_in_bytes, sp_offset_for_orig_pc, verified_inline_entry_label, false);
+  }
+  int verified_inline_ro_entry(const CompiledEntrySignature* ces, int frame_size_in_bytes, int bang_size_in_bytes, int sp_offset_for_orig_pc, Label& verified_inline_entry_label) {
+    return scalarized_entry(ces, frame_size_in_bytes, bang_size_in_bytes, sp_offset_for_orig_pc, verified_inline_entry_label, true);
+  }
   void verified_entry(bool breakAtEntry);
+
   void verify_stack_oop(int offset) PRODUCT_RETURN;
   void verify_not_null_oop(Register r)  PRODUCT_RETURN;
 
 #include CPU_HEADER(c1_MacroAssembler)
 
diff a/src/hotspot/share/c1/c1_Optimizer.cpp b/src/hotspot/share/c1/c1_Optimizer.cpp
--- a/src/hotspot/share/c1/c1_Optimizer.cpp
+++ b/src/hotspot/share/c1/c1_Optimizer.cpp
@@ -87,11 +87,12 @@
   }
 
   virtual void block_do(BlockBegin* block);
 
  private:
-  Value make_ifop(Value x, Instruction::Condition cond, Value y, Value tval, Value fval);
+  Value make_ifop(Value x, Instruction::Condition cond, Value y, Value tval, Value fval,
+                  ValueStack* state_before, bool substitutability_check);
 };
 
 void CE_Eliminator::block_do(BlockBegin* block) {
   // 1) find conditional expression
   // check if block ends with an If
@@ -216,11 +217,12 @@
     f_value = new Constant(f_const->type());
     NOT_PRODUCT(f_value->set_printable_bci(if_->printable_bci()));
     cur_end = cur_end->set_next(f_value);
   }
 
-  Value result = make_ifop(if_->x(), if_->cond(), if_->y(), t_value, f_value);
+  Value result = make_ifop(if_->x(), if_->cond(), if_->y(), t_value, f_value,
+                           if_->state_before(), if_->substitutability_check());
   assert(result != nullptr, "make_ifop must return a non-null instruction");
   if (!result->is_linked() && result->can_be_linked()) {
     NOT_PRODUCT(result->set_printable_bci(if_->printable_bci()));
     cur_end = cur_end->set_next(result);
   }
@@ -271,13 +273,14 @@
   _hir->verify_local(blocks_to_verify_later);
 #endif // DO_DELAYED_VERIFICATION
 
 }
 
-Value CE_Eliminator::make_ifop(Value x, Instruction::Condition cond, Value y, Value tval, Value fval) {
+Value CE_Eliminator::make_ifop(Value x, Instruction::Condition cond, Value y, Value tval, Value fval,
+                               ValueStack* state_before, bool substitutability_check) {
   if (!OptimizeIfOps) {
-    return new IfOp(x, cond, y, tval, fval);
+    return new IfOp(x, cond, y, tval, fval, state_before, substitutability_check);
   }
 
   tval = tval->subst();
   fval = fval->subst();
   if (tval == fval) {
@@ -308,11 +311,11 @@
 
           _ifop_count++;
           if (new_tval == new_fval) {
             return new_tval;
           } else {
-            return new IfOp(x_ifop->x(), x_ifop_cond, x_ifop->y(), new_tval, new_fval);
+            return new IfOp(x_ifop->x(), x_ifop_cond, x_ifop->y(), new_tval, new_fval, state_before, substitutability_check);
           }
         }
       }
     } else {
       Constant* x_const = x->as_Constant();
@@ -324,11 +327,11 @@
           return x_compare_res == Constant::cond_true ? tval : fval;
         }
       }
     }
   }
-  return new IfOp(x, cond, y, tval, fval);
+  return new IfOp(x, cond, y, tval, fval, state_before, substitutability_check);
 }
 
 void Optimizer::eliminate_conditional_expressions() {
   // find conditional expressions & replace them with IfOps
   CE_Eliminator ce(ir());
@@ -464,11 +467,11 @@
       if (!con || !ifop) {
         ifop = if_->y()->as_IfOp();
         con  = if_->x()->as_Constant();
         swapped = true;
       }
-      if (con && ifop) {
+      if (con && ifop && !ifop->substitutability_check()) {
         Constant* tval = ifop->tval()->as_Constant();
         Constant* fval = ifop->fval()->as_Constant();
         if (tval && fval) {
           // Find the instruction before if_, starting with ifop.
           // When if_ and ifop are not in the same block, prev
@@ -489,11 +492,11 @@
 
             BlockBegin* tblock = tval->compare(cond, con, tsux, fsux);
             BlockBegin* fblock = fval->compare(cond, con, tsux, fsux);
             if (tblock != fblock && !if_->is_safepoint()) {
               If* newif = new If(ifop->x(), ifop->cond(), false, ifop->y(),
-                                 tblock, fblock, if_->state_before(), if_->is_safepoint());
+                                 tblock, fblock, if_->state_before(), if_->is_safepoint(), ifop->substitutability_check());
               newif->set_state(if_->state()->copy());
 
               assert(prev->next() == if_, "must be guaranteed by above search");
               NOT_PRODUCT(newif->set_printable_bci(if_->printable_bci()));
               prev->set_next(newif);
@@ -584,10 +587,11 @@
   void do_UnsafeGet      (UnsafeGet*       x);
   void do_UnsafePut      (UnsafePut*       x);
   void do_UnsafeGetAndSet(UnsafeGetAndSet* x);
   void do_ProfileCall    (ProfileCall*     x);
   void do_ProfileReturnType (ProfileReturnType*  x);
+  void do_ProfileACmpTypes(ProfileACmpTypes*  x);
   void do_ProfileInvoke  (ProfileInvoke*   x);
   void do_RuntimeCall    (RuntimeCall*     x);
   void do_MemBar         (MemBar*          x);
   void do_RangeCheckPredicate(RangeCheckPredicate* x);
 #ifdef ASSERT
@@ -712,10 +716,11 @@
   void handle_Intrinsic       (Intrinsic* x);
   void handle_ExceptionObject (ExceptionObject* x);
   void handle_Phi             (Phi* x);
   void handle_ProfileCall     (ProfileCall* x);
   void handle_ProfileReturnType (ProfileReturnType* x);
+  void handle_ProfileACmpTypes(ProfileACmpTypes* x);
 };
 
 
 // NEEDS_CLEANUP
 // There may be other instructions which need to clear the last
@@ -769,10 +774,11 @@
 void NullCheckVisitor::do_UnsafeGetAndSet(UnsafeGetAndSet* x) {}
 void NullCheckVisitor::do_ProfileCall    (ProfileCall*     x) { nce()->clear_last_explicit_null_check();
                                                                 nce()->handle_ProfileCall(x); }
 void NullCheckVisitor::do_ProfileReturnType (ProfileReturnType* x) { nce()->handle_ProfileReturnType(x); }
 void NullCheckVisitor::do_ProfileInvoke  (ProfileInvoke*   x) {}
+void NullCheckVisitor::do_ProfileACmpTypes(ProfileACmpTypes* x) { nce()->handle_ProfileACmpTypes(x); }
 void NullCheckVisitor::do_RuntimeCall    (RuntimeCall*     x) {}
 void NullCheckVisitor::do_MemBar         (MemBar*          x) {}
 void NullCheckVisitor::do_RangeCheckPredicate(RangeCheckPredicate* x) {}
 #ifdef ASSERT
 void NullCheckVisitor::do_Assert         (Assert*          x) {}
@@ -1196,10 +1202,15 @@
 
 void NullCheckEliminator::handle_ProfileReturnType(ProfileReturnType* x) {
   x->set_needs_null_check(!set_contains(x->ret()));
 }
 
+void NullCheckEliminator::handle_ProfileACmpTypes(ProfileACmpTypes* x) {
+  x->set_left_maybe_null(!set_contains(x->left()));
+  x->set_right_maybe_null(!set_contains(x->right()));
+}
+
 void Optimizer::eliminate_null_checks() {
   ResourceMark rm;
 
   NullCheckEliminator nce(this);
 
diff a/src/hotspot/share/c1/c1_RangeCheckElimination.hpp b/src/hotspot/share/c1/c1_RangeCheckElimination.hpp
--- a/src/hotspot/share/c1/c1_RangeCheckElimination.hpp
+++ b/src/hotspot/share/c1/c1_RangeCheckElimination.hpp
@@ -158,10 +158,11 @@
     void do_UnsafePut      (UnsafePut*       x) { /* nothing to do */ };
     void do_UnsafeGet      (UnsafeGet*       x) { /* nothing to do */ };
     void do_UnsafeGetAndSet(UnsafeGetAndSet* x) { /* nothing to do */ };
     void do_ProfileCall    (ProfileCall*     x) { /* nothing to do */ };
     void do_ProfileReturnType (ProfileReturnType*  x) { /* nothing to do */ };
+    void do_ProfileACmpTypes(ProfileACmpTypes*  x) { /* nothing to do */ };
     void do_ProfileInvoke  (ProfileInvoke*   x) { /* nothing to do */ };
     void do_RuntimeCall    (RuntimeCall*     x) { /* nothing to do */ };
     void do_MemBar         (MemBar*          x) { /* nothing to do */ };
     void do_RangeCheckPredicate(RangeCheckPredicate* x) { /* nothing to do */ };
 #ifdef ASSERT
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
@@ -51,10 +51,12 @@
 #include "memory/allocation.inline.hpp"
 #include "memory/oopFactory.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
 #include "oops/access.inline.hpp"
+#include "oops/flatArrayKlass.hpp"
+#include "oops/flatArrayOop.inline.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/objArrayOop.inline.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "oops/oop.inline.hpp"
 #include "prims/jvmtiExport.hpp"
@@ -120,21 +122,28 @@
 uint Runtime1::_arraycopy_slowcase_cnt = 0;
 uint Runtime1::_arraycopy_checkcast_cnt = 0;
 uint Runtime1::_arraycopy_checkcast_attempt_cnt = 0;
 uint Runtime1::_new_type_array_slowcase_cnt = 0;
 uint Runtime1::_new_object_array_slowcase_cnt = 0;
+uint Runtime1::_new_flat_array_slowcase_cnt = 0;
 uint Runtime1::_new_instance_slowcase_cnt = 0;
 uint Runtime1::_new_multi_array_slowcase_cnt = 0;
+uint Runtime1::_load_flat_array_slowcase_cnt = 0;
+uint Runtime1::_store_flat_array_slowcase_cnt = 0;
+uint Runtime1::_substitutability_check_slowcase_cnt = 0;
+uint Runtime1::_buffer_inline_args_slowcase_cnt = 0;
+uint Runtime1::_buffer_inline_args_no_receiver_slowcase_cnt = 0;
 uint Runtime1::_monitorenter_slowcase_cnt = 0;
 uint Runtime1::_monitorexit_slowcase_cnt = 0;
 uint Runtime1::_patch_code_slowcase_cnt = 0;
 uint Runtime1::_throw_range_check_exception_count = 0;
 uint Runtime1::_throw_index_exception_count = 0;
 uint Runtime1::_throw_div0_exception_count = 0;
 uint Runtime1::_throw_null_pointer_exception_count = 0;
 uint Runtime1::_throw_class_cast_exception_count = 0;
 uint Runtime1::_throw_incompatible_class_change_error_count = 0;
+uint Runtime1::_throw_illegal_monitor_state_exception_count = 0;
 uint Runtime1::_throw_count = 0;
 
 static uint _byte_arraycopy_stub_cnt = 0;
 static uint _short_arraycopy_stub_cnt = 0;
 static uint _int_arraycopy_stub_cnt = 0;
@@ -346,28 +355,36 @@
 
   // Soft float adds more runtime names.
   return pd_name_for_address(entry);
 }
 
-
-JRT_ENTRY(void, Runtime1::new_instance(JavaThread* current, Klass* klass))
+static void allocate_instance(JavaThread* current, Klass* klass, TRAPS) {
 #ifndef PRODUCT
   if (PrintC1Statistics) {
-    _new_instance_slowcase_cnt++;
+    Runtime1::_new_instance_slowcase_cnt++;
   }
 #endif
   assert(klass->is_klass(), "not a class");
   Handle holder(current, klass->klass_holder()); // keep the klass alive
   InstanceKlass* h = InstanceKlass::cast(klass);
   h->check_valid_for_instantiation(true, CHECK);
   // make sure klass is initialized
   h->initialize(CHECK);
-  // allocate instance and return via TLS
-  oop obj = h->allocate_instance(CHECK);
+  oop obj = nullptr;
+  if (h->is_empty_inline_type()) {
+    obj = InlineKlass::cast(h)->default_value();
+    assert(obj != nullptr, "default value must exist");
+  } else {
+    // allocate instance and return via TLS
+    obj = h->allocate_instance(CHECK);
+  }
   current->set_vm_result(obj);
 JRT_END
 
+JRT_ENTRY(void, Runtime1::new_instance(JavaThread* current, Klass* klass))
+  allocate_instance(current, klass, CHECK);
+JRT_END
 
 JRT_ENTRY(void, Runtime1::new_type_array(JavaThread* current, Klass* klass, jint length))
 #ifndef PRODUCT
   if (PrintC1Statistics) {
     _new_type_array_slowcase_cnt++;
@@ -398,21 +415,43 @@
   // Note: no handle for klass needed since they are not used
   //       anymore after new_objArray() and no GC can happen before.
   //       (This may have to change if this code changes!)
   assert(array_klass->is_klass(), "not a class");
   Handle holder(current, array_klass->klass_holder()); // keep the klass alive
-  Klass* elem_klass = ObjArrayKlass::cast(array_klass)->element_klass();
+  Klass* elem_klass = ArrayKlass::cast(array_klass)->element_klass();
   objArrayOop obj = oopFactory::new_objArray(elem_klass, length, CHECK);
   current->set_vm_result(obj);
   // This is pretty rare but this runtime patch is stressful to deoptimization
   // if we deoptimize here so force a deopt to stress the path.
   if (DeoptimizeALot) {
     deopt_caller(current);
   }
 JRT_END
 
 
+JRT_ENTRY(void, Runtime1::new_flat_array(JavaThread* current, Klass* array_klass, jint length))
+  NOT_PRODUCT(_new_flat_array_slowcase_cnt++;)
+
+  // Note: no handle for klass needed since they are not used
+  //       anymore after new_objArray() and no GC can happen before.
+  //       (This may have to change if this code changes!)
+  assert(array_klass->is_klass(), "not a class");
+  Handle holder(THREAD, array_klass->klass_holder()); // keep the klass alive
+  Klass* elem_klass = ArrayKlass::cast(array_klass)->element_klass();
+  assert(elem_klass->is_inline_klass(), "must be");
+  // Logically creates elements, ensure klass init
+  elem_klass->initialize(CHECK);
+  arrayOop obj = oopFactory::new_valueArray(elem_klass, length, CHECK);
+  current->set_vm_result(obj);
+  // This is pretty rare but this runtime patch is stressful to deoptimization
+  // if we deoptimize here so force a deopt to stress the path.
+  if (DeoptimizeALot) {
+    deopt_caller(current);
+  }
+JRT_END
+
+
 JRT_ENTRY(void, Runtime1::new_multi_array(JavaThread* current, Klass* klass, int rank, jint* dims))
 #ifndef PRODUCT
   if (PrintC1Statistics) {
     _new_multi_array_slowcase_cnt++;
   }
@@ -423,10 +462,99 @@
   oop obj = ArrayKlass::cast(klass)->multi_allocate(rank, dims, CHECK);
   current->set_vm_result(obj);
 JRT_END
 
 
+static void profile_flat_array(JavaThread* current, bool load) {
+  ResourceMark rm(current);
+  vframeStream vfst(current, true);
+  assert(!vfst.at_end(), "Java frame must exist");
+  // Check if array access profiling is enabled
+  if (vfst.nm()->comp_level() != CompLevel_full_profile || !C1UpdateMethodData) {
+    return;
+  }
+  int bci = vfst.bci();
+  Method* method = vfst.method();
+  MethodData* md = method->method_data();
+  if (md != nullptr) {
+    ProfileData* data = md->bci_to_data(bci);
+    assert(data != nullptr, "incorrect profiling entry");
+    if (data->is_ArrayLoadData()) {
+      assert(load, "should be an array load");
+      ArrayLoadData* load_data = (ArrayLoadData*) data;
+      load_data->set_flat_array();
+    } else {
+      assert(data->is_ArrayStoreData(), "");
+      assert(!load, "should be an array store");
+      ArrayStoreData* store_data = (ArrayStoreData*) data;
+      store_data->set_flat_array();
+    }
+  }
+}
+
+JRT_ENTRY(void, Runtime1::load_flat_array(JavaThread* current, flatArrayOopDesc* array, int index))
+  assert(array->klass()->is_flatArray_klass(), "should not be called");
+  profile_flat_array(current, true);
+
+  NOT_PRODUCT(_load_flat_array_slowcase_cnt++;)
+  assert(array->length() > 0 && index < array->length(), "already checked");
+  flatArrayHandle vah(current, array);
+  oop obj = flatArrayOopDesc::value_alloc_copy_from_index(vah, index, CHECK);
+  current->set_vm_result(obj);
+JRT_END
+
+
+JRT_ENTRY(void, Runtime1::store_flat_array(JavaThread* current, flatArrayOopDesc* array, int index, oopDesc* value))
+  if (array->klass()->is_flatArray_klass()) {
+    profile_flat_array(current, false);
+  }
+
+  NOT_PRODUCT(_store_flat_array_slowcase_cnt++;)
+  if (value == nullptr) {
+    assert(array->klass()->is_flatArray_klass() || array->klass()->is_null_free_array_klass(), "should not be called");
+    SharedRuntime::throw_and_post_jvmti_exception(current, vmSymbols::java_lang_NullPointerException());
+  } else {
+    assert(array->klass()->is_flatArray_klass(), "should not be called");
+    array->value_copy_to_index(value, index);
+  }
+JRT_END
+
+
+JRT_ENTRY(int, Runtime1::substitutability_check(JavaThread* current, oopDesc* left, oopDesc* right))
+  NOT_PRODUCT(_substitutability_check_slowcase_cnt++;)
+  JavaCallArguments args;
+  args.push_oop(Handle(THREAD, left));
+  args.push_oop(Handle(THREAD, right));
+  JavaValue result(T_BOOLEAN);
+  JavaCalls::call_static(&result,
+                         vmClasses::ValueObjectMethods_klass(),
+                         vmSymbols::isSubstitutable_name(),
+                         vmSymbols::object_object_boolean_signature(),
+                         &args, CHECK_0);
+  return result.get_jboolean() ? 1 : 0;
+JRT_END
+
+
+extern "C" void ps();
+
+void Runtime1::buffer_inline_args_impl(JavaThread* current, Method* m, bool allocate_receiver) {
+  JavaThread* THREAD = current;
+  methodHandle method(current, m); // We are inside the verified_entry or verified_inline_ro_entry of this method.
+  oop obj = SharedRuntime::allocate_inline_types_impl(current, method, allocate_receiver, CHECK);
+  current->set_vm_result(obj);
+}
+
+JRT_ENTRY(void, Runtime1::buffer_inline_args(JavaThread* current, Method* method))
+  NOT_PRODUCT(_buffer_inline_args_slowcase_cnt++;)
+  buffer_inline_args_impl(current, method, true);
+JRT_END
+
+JRT_ENTRY(void, Runtime1::buffer_inline_args_no_receiver(JavaThread* current, Method* method))
+  NOT_PRODUCT(_buffer_inline_args_no_receiver_slowcase_cnt++;)
+  buffer_inline_args_impl(current, method, false);
+JRT_END
+
 JRT_ENTRY(void, Runtime1::unimplemented_entry(JavaThread* current, StubID id))
   tty->print_cr("Runtime1::entry_for(%d) returned unimplemented entry point", id);
 JRT_END
 
 
@@ -746,10 +874,16 @@
   ResourceMark rm(current);
   SharedRuntime::throw_and_post_jvmti_exception(current, vmSymbols::java_lang_IncompatibleClassChangeError());
 JRT_END
 
 
+JRT_ENTRY(void, Runtime1::throw_illegal_monitor_state_exception(JavaThread* current))
+  NOT_PRODUCT(_throw_illegal_monitor_state_exception_count++;)
+  ResourceMark rm(current);
+  SharedRuntime::throw_and_post_jvmti_exception(current, vmSymbols::java_lang_IllegalMonitorStateException());
+JRT_END
+
 JRT_BLOCK_ENTRY(void, Runtime1::monitorenter(JavaThread* current, oopDesc* obj, BasicObjectLock* lock))
 #ifndef PRODUCT
   if (PrintC1Statistics) {
     _monitorenter_slowcase_cnt++;
   }
@@ -951,10 +1085,12 @@
 
   // this is used by assertions in the access_field_patching_id
   BasicType patch_field_type = T_ILLEGAL;
   bool deoptimize_for_volatile = false;
   bool deoptimize_for_atomic = false;
+  bool deoptimize_for_null_free = false;
+  bool deoptimize_for_flat = false;
   int patch_field_offset = -1;
   Klass* init_klass = nullptr; // klass needed by load_klass_patching code
   Klass* load_klass = nullptr; // klass needed by load_klass_patching code
   Handle mirror(current, nullptr); // oop needed by load_mirror_patching code
   Handle appendix(current, nullptr); // oop needed by appendix_patching code
@@ -994,10 +1130,20 @@
     // accesses.
 
     patch_field_type = result.field_type();
     deoptimize_for_atomic = (AlwaysAtomicAccesses && (patch_field_type == T_DOUBLE || patch_field_type == T_LONG));
 
+    // The field we are patching is null-free. Deoptimize and regenerate
+    // the compiled code if we patch a putfield/putstatic because it
+    // does not contain the required null check.
+    deoptimize_for_null_free = result.is_null_free_inline_type() && (field_access.is_putfield() || field_access.is_putstatic());
+
+    // The field we are patching is flat. Deoptimize and regenerate
+    // the compiled code which can't handle the layout of the flat
+    // field because it was unknown at compile time.
+    deoptimize_for_flat = result.is_flat();
+
   } else if (load_klass_or_mirror_patch_id) {
     Klass* k = nullptr;
     switch (code) {
       case Bytecodes::_putstatic:
       case Bytecodes::_getstatic:
@@ -1067,21 +1213,27 @@
     }
   } else {
     ShouldNotReachHere();
   }
 
-  if (deoptimize_for_volatile || deoptimize_for_atomic) {
+  if (deoptimize_for_volatile || deoptimize_for_atomic || deoptimize_for_null_free || deoptimize_for_flat) {
     // At compile time we assumed the field wasn't volatile/atomic but after
     // loading it turns out it was volatile/atomic so we have to throw the
     // compiled code out and let it be regenerated.
     if (TracePatching) {
       if (deoptimize_for_volatile) {
         tty->print_cr("Deoptimizing for patching volatile field reference");
       }
       if (deoptimize_for_atomic) {
         tty->print_cr("Deoptimizing for patching atomic field reference");
       }
+      if (deoptimize_for_null_free) {
+        tty->print_cr("Deoptimizing for patching null-free field reference");
+      }
+      if (deoptimize_for_flat) {
+        tty->print_cr("Deoptimizing for patching flat field reference");
+      }
     }
 
     // It's possible the nmethod was invalidated in the last
     // safepoint, but if it's still alive then make it not_entrant.
     nmethod* nm = CodeCache::find_nmethod(caller_frame.pc());
@@ -1532,22 +1684,30 @@
   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_flat_array_slowcase_cnt:    %u", _new_flat_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(" _load_flat_array_slowcase_cnt:   %u", _load_flat_array_slowcase_cnt);
+  tty->print_cr(" _store_flat_array_slowcase_cnt:  %u", _store_flat_array_slowcase_cnt);
+  tty->print_cr(" _substitutability_check_slowcase_cnt: %u", _substitutability_check_slowcase_cnt);
+  tty->print_cr(" _buffer_inline_args_slowcase_cnt:%u", _buffer_inline_args_slowcase_cnt);
+  tty->print_cr(" _buffer_inline_args_no_receiver_slowcase_cnt:%u", _buffer_inline_args_no_receiver_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_illegal_monitor_state_exception_count:  %u:", _throw_illegal_monitor_state_exception_count);
   tty->print_cr(" _throw_count:                                  %u:", _throw_count);
 
   SharedRuntime::print_ic_miss_histogram();
   tty->cr();
 }
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
@@ -49,17 +49,24 @@
   stub(new_instance)                 \
   stub(fast_new_instance)            \
   stub(fast_new_instance_init_check) \
   stub(new_type_array)               \
   stub(new_object_array)             \
+  stub(new_flat_array)               \
   stub(new_multi_array)              \
+  stub(load_flat_array)              \
+  stub(store_flat_array)             \
+  stub(substitutability_check)       \
+  stub(buffer_inline_args)           \
+  stub(buffer_inline_args_no_receiver)\
   stub(handle_exception_nofpu)         /* optimized version that does not preserve fpu registers */ \
   stub(handle_exception)             \
   stub(handle_exception_from_callee) \
   stub(throw_array_store_exception)  \
   stub(throw_class_cast_exception)   \
   stub(throw_incompatible_class_change_error)   \
+  stub(throw_illegal_monitor_state_exception)   \
   stub(slow_subtype_check)           \
   stub(monitorenter)                 \
   stub(monitorenter_nofpu)             /* optimized version that does not preserve fpu registers */ \
   stub(monitorexit)                  \
   stub(monitorexit_nofpu)              /* optimized version that does not preserve fpu registers */ \
@@ -98,27 +105,35 @@
   static uint _arraycopy_slowcase_cnt;
   static uint _arraycopy_checkcast_cnt;
   static uint _arraycopy_checkcast_attempt_cnt;
   static uint _new_type_array_slowcase_cnt;
   static uint _new_object_array_slowcase_cnt;
+  static uint _new_flat_array_slowcase_cnt;
   static uint _new_instance_slowcase_cnt;
   static uint _new_multi_array_slowcase_cnt;
+  static uint _load_flat_array_slowcase_cnt;
+  static uint _store_flat_array_slowcase_cnt;
+  static uint _substitutability_check_slowcase_cnt;
+  static uint _buffer_inline_args_slowcase_cnt;
+  static uint _buffer_inline_args_no_receiver_slowcase_cnt;
   static uint _monitorenter_slowcase_cnt;
   static uint _monitorexit_slowcase_cnt;
   static uint _patch_code_slowcase_cnt;
   static uint _throw_range_check_exception_count;
   static uint _throw_index_exception_count;
   static uint _throw_div0_exception_count;
   static uint _throw_null_pointer_exception_count;
   static uint _throw_class_cast_exception_count;
   static uint _throw_incompatible_class_change_error_count;
+  static uint _throw_illegal_monitor_state_exception_count;
   static uint _throw_count;
 #endif
 
  private:
   static CodeBlob* _blobs[number_of_ids];
   static const char* _blob_names[];
+  static void buffer_inline_args_impl(JavaThread* current, Method* m, bool allocate_receiver);
 
   // stub generation
  public:
   static CodeBlob*  generate_blob(BufferBlob* buffer_blob, int stub_id, const char* name, bool expect_oop_map, StubAssemblerCodeGenClosure *cl);
   static void       generate_blob_for(BufferBlob* blob, StubID id);
@@ -132,13 +147,20 @@
   static OopMapSet* generate_stub_call(StubAssembler* sasm, Register result, address entry,
                                        Register arg1 = noreg, Register arg2 = noreg, Register arg3 = noreg);
 
   // runtime entry points
   static void new_instance    (JavaThread* current, Klass* klass);
+  static void new_instance_no_inline(JavaThread* current, Klass* klass);
   static void new_type_array  (JavaThread* current, Klass* klass, jint length);
   static void new_object_array(JavaThread* current, Klass* klass, jint length);
+  static void new_flat_array (JavaThread* current, Klass* klass, jint length);
   static void new_multi_array (JavaThread* current, Klass* klass, int rank, jint* dims);
+  static void load_flat_array(JavaThread* current, flatArrayOopDesc* array, int index);
+  static void store_flat_array(JavaThread* current, flatArrayOopDesc* array, int index, oopDesc* value);
+  static int  substitutability_check(JavaThread* current, oopDesc* left, oopDesc* right);
+  static void buffer_inline_args(JavaThread* current, Method* method);
+  static void buffer_inline_args_no_receiver(JavaThread* current, Method* method);
 
   static address counter_overflow(JavaThread* current, int bci, Method* method);
 
   static void unimplemented_entry(JavaThread* current, StubID id);
 
@@ -148,10 +170,11 @@
   static void throw_index_exception(JavaThread* current, int index);
   static void throw_div0_exception(JavaThread* current);
   static void throw_null_pointer_exception(JavaThread* current);
   static void throw_class_cast_exception(JavaThread* current, oopDesc* object);
   static void throw_incompatible_class_change_error(JavaThread* current);
+  static void throw_illegal_monitor_state_exception(JavaThread* current);
   static void throw_array_store_exception(JavaThread* current, oopDesc* object);
 
   static void monitorenter(JavaThread* current, oopDesc* obj, BasicObjectLock* lock);
   static void monitorexit (JavaThread* current, BasicObjectLock* lock);
 
diff a/src/hotspot/share/c1/c1_ValueMap.cpp b/src/hotspot/share/c1/c1_ValueMap.cpp
--- a/src/hotspot/share/c1/c1_ValueMap.cpp
+++ b/src/hotspot/share/c1/c1_ValueMap.cpp
@@ -612,10 +612,12 @@
 
 void GlobalValueNumbering::substitute(Instruction* instr) {
   assert(!instr->has_subst(), "substitution already set");
   Value subst = current_map()->find_insert(instr);
   if (subst != instr) {
+    assert(instr->as_LoadIndexed() == nullptr || !instr->as_LoadIndexed()->should_profile(), "should not be optimized out");
+    assert(instr->as_StoreIndexed() == nullptr, "should not be optimized out");
     assert(!subst->has_subst(), "can't have a substitution");
 
     TRACE_VALUE_NUMBERING(tty->print_cr("substitution for %c%d set to %c%d", instr->type()->tchar(), instr->id(), subst->type()->tchar(), subst->id()));
     instr->set_subst(subst);
     _has_substitutions = true;
diff a/src/hotspot/share/c1/c1_ValueMap.hpp b/src/hotspot/share/c1/c1_ValueMap.hpp
--- a/src/hotspot/share/c1/c1_ValueMap.hpp
+++ b/src/hotspot/share/c1/c1_ValueMap.hpp
@@ -145,10 +145,13 @@
         // but possible future optimizations might require this.
         x->field()->is_volatile()) {
       kill_memory();
     } else {
       kill_field(x->field(), x->needs_patching());
+      if (x->enclosing_field() != nullptr) {
+        kill_field(x->enclosing_field(), true);
+      }
     }
   }
   void do_StoreIndexed   (StoreIndexed*    x) { kill_array(x->type()); }
   void do_MonitorEnter   (MonitorEnter*    x) { kill_memory(); }
   void do_MonitorExit    (MonitorExit*     x) { kill_memory(); }
@@ -204,10 +207,11 @@
   void do_OsrEntry       (OsrEntry*        x) { /* nothing to do */ }
   void do_ExceptionObject(ExceptionObject* x) { /* nothing to do */ }
   void do_RoundFP        (RoundFP*         x) { /* nothing to do */ }
   void do_ProfileCall    (ProfileCall*     x) { /* nothing to do */ }
   void do_ProfileReturnType (ProfileReturnType*  x) { /* nothing to do */ }
+  void do_ProfileACmpTypes(ProfileACmpTypes*  x) { /* nothing to do */ }
   void do_ProfileInvoke  (ProfileInvoke*   x) { /* nothing to do */ };
   void do_RuntimeCall    (RuntimeCall*     x) { /* nothing to do */ };
   void do_MemBar         (MemBar*          x) { /* nothing to do */ };
   void do_RangeCheckPredicate(RangeCheckPredicate* x) { /* nothing to do */ };
 #ifdef ASSERT
diff a/src/hotspot/share/c1/c1_ValueStack.cpp b/src/hotspot/share/c1/c1_ValueStack.cpp
--- a/src/hotspot/share/c1/c1_ValueStack.cpp
+++ b/src/hotspot/share/c1/c1_ValueStack.cpp
@@ -33,22 +33,24 @@
 ValueStack::ValueStack(IRScope* scope, ValueStack* caller_state)
 : _scope(scope)
 , _caller_state(caller_state)
 , _bci(-99)
 , _kind(Parsing)
+, _should_reexecute(false)
 , _locals(scope->method()->max_locals(), scope->method()->max_locals(), nullptr)
 , _stack(scope->method()->max_stack())
 , _locks(nullptr)
 {
   verify();
 }
 
-ValueStack::ValueStack(ValueStack* copy_from, Kind kind, int bci)
+ValueStack::ValueStack(ValueStack* copy_from, Kind kind, int bci, bool reexecute)
   : _scope(copy_from->scope())
   , _caller_state(copy_from->caller_state())
   , _bci(bci)
   , _kind(kind)
+  , _should_reexecute(reexecute)
   , _locals(copy_from->locals_size_for_copy(kind))
   , _stack(copy_from->stack_size_for_copy(kind))
   , _locks(copy_from->locks_size() == 0 ? nullptr : new Values(copy_from->locks_size()))
 {
   switch (kind) {
@@ -207,11 +209,10 @@
   assert(locks_size() > 0, "sanity");
   _locks->pop();
   return total_locks_size();
 }
 
-
 void ValueStack::setup_phi_for_stack(BlockBegin* b, int index) {
   assert(stack_at(index)->as_Phi() == nullptr || stack_at(index)->as_Phi()->block() != b, "phi function already created");
 
   ValueType* t = stack_at(index)->type();
   Value phi = new Phi(t, b, -index - 1);
diff a/src/hotspot/share/c1/c1_ValueStack.hpp b/src/hotspot/share/c1/c1_ValueStack.hpp
--- a/src/hotspot/share/c1/c1_ValueStack.hpp
+++ b/src/hotspot/share/c1/c1_ValueStack.hpp
@@ -52,10 +52,11 @@
  private:
   IRScope* _scope;                               // the enclosing scope
   ValueStack* _caller_state;
   int      _bci;
   Kind     _kind;
+  bool     _should_reexecute;
 
   Values   _locals;                              // the locals
   Values   _stack;                               // the expression stack
   Values*  _locks;                               // the monitor stack (holding the locked values)
 
@@ -71,21 +72,21 @@
 
   // helper routine
   static void apply(const Values& list, ValueVisitor* f);
 
   // for simplified copying
-  ValueStack(ValueStack* copy_from, Kind kind, int bci);
+  ValueStack(ValueStack* copy_from, Kind kind, int bci, bool reexecute);
 
   int locals_size_for_copy(Kind kind) const;
   int stack_size_for_copy(Kind kind) const;
  public:
   // creation
   ValueStack(IRScope* scope, ValueStack* caller_state);
 
-  ValueStack* copy()                             { return new ValueStack(this, _kind, _bci); }
-  ValueStack* copy(Kind new_kind, int new_bci)   { return new ValueStack(this, new_kind, new_bci); }
-  ValueStack* copy_for_parsing()                 { return new ValueStack(this, Parsing, -99); }
+  ValueStack* copy()                             { return new ValueStack(this, _kind, _bci, _should_reexecute); }
+  ValueStack* copy(Kind new_kind, int new_bci)   { return new ValueStack(this, new_kind, new_bci, _should_reexecute); }
+  ValueStack* copy_for_parsing()                 { return new ValueStack(this, Parsing, -99, false); }
 
   // Used when no exception handler is found
   static Kind empty_exception_kind(bool caller = false) {
     return Compilation::current()->env()->should_retain_local_variables() ?
       (caller ? CallerExceptionState : ExceptionState) : // retain locals
@@ -103,10 +104,12 @@
   // accessors
   IRScope* scope() const                         { return _scope; }
   ValueStack* caller_state() const               { return _caller_state; }
   int bci() const                                { return _bci; }
   Kind kind() const                              { return _kind; }
+  bool should_reexecute() const                  { return _should_reexecute; }
+  void set_should_reexecute(bool reexec)         { _should_reexecute = reexec; }
 
   int locals_size() const                        { return _locals.length(); }
   int stack_size() const                         { return _stack.length(); }
   int locks_size() const                         { return _locks == nullptr ? 0 : _locks->length(); }
   bool stack_is_empty() const                    { return _stack.is_empty(); }
diff a/src/hotspot/share/c1/c1_globals.hpp b/src/hotspot/share/c1/c1_globals.hpp
--- a/src/hotspot/share/c1/c1_globals.hpp
+++ b/src/hotspot/share/c1/c1_globals.hpp
@@ -314,11 +314,14 @@
                                                                             \
   product(bool, C1UpdateMethodData, true,                                   \
           "Update MethodData*s in Tier 3 C1 generated code")                \
                                                                             \
   develop(bool, PrintCFGToFile, false,                                      \
-          "print control flow graph to a separate file during compilation")
+          "print control flow graph to a separate file during compilation") \
+                                                                            \
+  develop(bool, C1UseDelayedFlattenedFieldReads, true,                      \
+          "Use delayed reads of flat fields to reduce heap buffering")
 
 // end of C1_FLAGS
 
 DECLARE_FLAGS(C1_FLAGS)
 
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
@@ -475,13 +475,13 @@
 
   // We need to retain the identity_hash, because it may have been used by some hashtables
   // in the shared heap. This also has the side effect of pre-initializing the
   // identity_hash for all shared objects, so they are less likely to be written
   // into during run time, increasing the potential of memory sharing.
-  if (src_obj != nullptr) {
+  if (src_obj != nullptr && (!(EnableValhalla && src_obj->mark().is_inline_type()))) {
     intptr_t src_hash = src_obj->identity_hash();
-    fake_oop->set_mark(markWord::prototype().copy_set_hash(src_hash));
+    fake_oop->set_mark(src_klass->prototype_header().copy_set_hash(src_hash));
     assert(fake_oop->mark().is_unlocked(), "sanity");
 
     DEBUG_ONLY(intptr_t archived_hash = fake_oop->identity_hash());
     assert(src_hash == archived_hash, "Different hash codes: original " INTPTR_FORMAT ", archived " INTPTR_FORMAT, src_hash, archived_hash);
   }
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, 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.
@@ -29,10 +29,11 @@
 #include "classfile/classLoaderDataShared.hpp"
 #include "classfile/moduleEntry.hpp"
 #include "include/jvm_io.h"
 #include "logging/log.hpp"
 #include "runtime/arguments.hpp"
+#include "runtime/globals.hpp"
 #include "runtime/java.hpp"
 #include "utilities/defaultStream.hpp"
 
 bool CDSConfig::_is_dumping_static_archive = false;
 bool CDSConfig::_is_dumping_dynamic_archive = false;
@@ -41,10 +42,18 @@
 // is_dumping_full_module_graph(), but can be unconditionally disabled by
 // _dumping_full_module_graph_disabled. (Ditto for loading the FMG).
 bool CDSConfig::_dumping_full_module_graph_disabled = false;
 bool CDSConfig::_loading_full_module_graph_disabled = false;
 
+bool CDSConfig::_module_patching_disables_cds = false;
+bool CDSConfig::_java_base_module_patching_disables_cds = false;
+
+bool CDSConfig::is_valhalla_preview() {
+  return Arguments::enable_preview() && EnableValhalla;
+}
+
+
 char* CDSConfig::_default_archive_path = nullptr;
 char* CDSConfig::_static_archive_path = nullptr;
 char* CDSConfig::_dynamic_archive_path = nullptr;
 
 void CDSConfig::initialize() {
@@ -70,15 +79,19 @@
     os::jvm_path(jvm_path, sizeof(jvm_path));
     char *end = strrchr(jvm_path, *os::file_separator());
     if (end != nullptr) *end = '\0';
     size_t jvm_path_len = strlen(jvm_path);
     size_t file_sep_len = strlen(os::file_separator());
-    const size_t len = jvm_path_len + file_sep_len + 20;
+    const size_t len = jvm_path_len + file_sep_len + strlen("classes_nocoops_valhalla.jsa") + 1;
     _default_archive_path = NEW_C_HEAP_ARRAY(char, len, mtArguments);
-    jio_snprintf(_default_archive_path, len,
-                LP64_ONLY(!UseCompressedOops ? "%s%sclasses_nocoops.jsa":) "%s%sclasses.jsa",
-                jvm_path, os::file_separator());
+    LP64_ONLY(bool nocoops = !UseCompressedOops);
+    NOT_LP64(bool nocoops = false);
+    bool valhalla = is_valhalla_preview();
+    jio_snprintf(_default_archive_path, len, "%s%sclasses%s%s.jsa",
+                jvm_path, os::file_separator(),
+                 nocoops ? "_nocoops" : "",
+                 valhalla ? "_valhalla" : "");
   }
   return _default_archive_path;
 }
 
 int CDSConfig::num_archives(const char* archive_path) {
@@ -238,16 +251,14 @@
 }
 
 static const char* unsupported_properties[] = {
   "jdk.module.limitmods",
   "jdk.module.upgrade.path",
-  "jdk.module.patch.0"
 };
 static const char* unsupported_options[] = {
   "--limit-modules",
   "--upgrade-module-path",
-  "--patch-module"
 };
 
 void CDSConfig::check_unsupported_dumping_properties() {
   assert(is_dumping_archive(), "this function is only used with CDS dump time");
   assert(ARRAY_SIZE(unsupported_properties) == ARRAY_SIZE(unsupported_options), "must be");
@@ -261,10 +272,16 @@
       }
     }
     sp = sp->next();
   }
 
+  if (module_patching_disables_cds()) {
+    vm_exit_during_initialization(
+            "Cannot use the following option when dumping the shared archive", "--patch-module");
+  }
+
+
   // Check for an exploded module build in use with -Xshare:dump.
   if (!Arguments::has_jimage()) {
     vm_exit_during_initialization("Dumping the shared archive is not supported with an exploded module build");
   }
 }
@@ -287,14 +304,24 @@
         log_info(cds)("CDS is disabled when the %s option is specified.", unsupported_options[i]);
       }
       return true;
     }
   }
+
+  if (module_patching_disables_cds()) {
+    if (RequireSharedSpaces) {
+      warning("CDS is disabled when the %s option is specified.", "--patch-module");
+    } else {
+      log_info(cds)("CDS is disabled when the %s option is specified.", "--patch-module");
+    }
+    return true;
+  }
+
   return false;
 }
 
-bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase,  bool mode_flag_cmd_line) {
+bool CDSConfig::check_vm_args_consistency(bool mode_flag_cmd_line) {
   if (is_dumping_static_archive()) {
     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
@@ -335,11 +362,11 @@
       log_warning(cds)("-XX:+AutoCreateSharedArchive does not work with ArchiveClassesAtExit");
       return false;
     }
   }
 
-  if (UseSharedSpaces && patch_mod_javabase) {
+  if (UseSharedSpaces && java_base_module_patching_disables_cds() && module_patching_disables_cds()) {
     Arguments::no_shared_spaces("CDS is disabled when " JAVA_BASE_NAME " module is patched.");
   }
   if (UseSharedSpaces && check_unsupported_cds_runtime_properties()) {
     UseSharedSpaces = false;
   }
@@ -355,10 +382,14 @@
   return true;
 }
 
 #if INCLUDE_CDS_JAVA_HEAP
 bool CDSConfig::is_dumping_heap() {
+  if (is_valhalla_preview()) {
+    // Not working yet -- e.g., HeapShared::oop_hash() needs to be implemented for value oops
+    return false;
+  }
   // heap dump is not supported in dynamic dump
   return is_dumping_static_archive() && HeapShared::can_write();
 }
 
 bool CDSConfig::is_dumping_full_module_graph() {
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
@@ -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.
@@ -34,10 +34,13 @@
   static bool _is_dumping_static_archive;
   static bool _is_dumping_dynamic_archive;
   static bool _dumping_full_module_graph_disabled;
   static bool _loading_full_module_graph_disabled;
 
+  static bool _module_patching_disables_cds;
+  static bool _java_base_module_patching_disables_cds;
+
   static char*  _default_archive_path;
   static char*  _static_archive_path;
   static char*  _dynamic_archive_path;
 #endif
 
@@ -50,11 +53,16 @@
 public:
   // Initialization and command-line checking
   static void initialize() NOT_CDS_RETURN;
   static void check_system_property(const char* key, const char* value) NOT_CDS_RETURN;
   static void check_unsupported_dumping_properties() NOT_CDS_RETURN;
-  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 mode_flag_cmd_line) NOT_CDS_RETURN_(true);
+
+  static bool module_patching_disables_cds() { return _module_patching_disables_cds; }
+  static void set_module_patching_disables_cds() { _module_patching_disables_cds = true; }
+  static bool java_base_module_patching_disables_cds() { return _java_base_module_patching_disables_cds; }
+  static void set_java_base_module_patching_disables_cds() { _java_base_module_patching_disables_cds = true; }
 
   // Basic CDS features
   static bool      is_dumping_archive()                      { return is_dumping_static_archive() || is_dumping_dynamic_archive(); }
   static bool      is_dumping_static_archive()               { return CDS_ONLY(_is_dumping_static_archive) NOT_CDS(false); }
   static void  enable_dumping_static_archive()               { CDS_ONLY(_is_dumping_static_archive = true); }
@@ -78,8 +86,9 @@
   static void disable_dumping_full_module_graph(const char* reason = nullptr) NOT_CDS_JAVA_HEAP_RETURN;
   static bool      is_dumping_full_module_graph()            NOT_CDS_JAVA_HEAP_RETURN_(false);
   static void disable_loading_full_module_graph(const char* reason = nullptr) NOT_CDS_JAVA_HEAP_RETURN;
   static bool      is_loading_full_module_graph()            NOT_CDS_JAVA_HEAP_RETURN_(false);
 
+  static bool      is_valhalla_preview();
 };
 
 #endif // SHARE_CDS_CDSCONFIG_HPP
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
@@ -472,15 +472,19 @@
           _class_name, _source);
     THROW_NULL(vmSymbols::java_lang_ClassNotFoundException());
   }
 
   InstanceKlass* k = UnregisteredClasses::load_class(class_name, _source, CHECK_NULL);
-  if (k->local_interfaces()->length() != _interfaces->length()) {
+  const int actual_num_interfaces = k->local_interfaces()->length();
+  const int specified_num_interfaces = _interfaces->length(); // specified in classlist
+  int expected_num_interfaces = actual_num_interfaces;
+
+  if (specified_num_interfaces != expected_num_interfaces) {
     print_specified_interfaces();
     print_actual_interfaces(k);
     error("The number of interfaces (%d) specified in class list does not match the class file (%d)",
-          _interfaces->length(), k->local_interfaces()->length());
+          specified_num_interfaces, expected_num_interfaces);
   }
 
   assert(k->is_shared_unregistered_class(), "must be");
 
   bool added = SystemDictionaryShared::add_unregistered_class(THREAD, k);
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
@@ -27,11 +27,14 @@
 #include "cds/archiveBuilder.hpp"
 #include "cds/cdsConfig.hpp"
 #include "cds/cppVtables.hpp"
 #include "cds/metaspaceShared.hpp"
 #include "logging/log.hpp"
+#include "oops/flatArrayKlass.hpp"
+#include "oops/inlineKlass.hpp"
 #include "oops/instanceClassLoaderKlass.hpp"
+#include "oops/instanceKlass.inline.hpp"
 #include "oops/instanceMirrorKlass.hpp"
 #include "oops/instanceRefKlass.hpp"
 #include "oops/instanceStackChunkKlass.hpp"
 #include "oops/methodData.hpp"
 #include "oops/objArrayKlass.hpp"
@@ -51,20 +54,23 @@
 //                  the CDS image
 // + at run time:   we clone the actual contents of the vtables from libjvm.so
 //                  into our own tables.
 
 // Currently, the archive contains ONLY the following types of objects that have C++ vtables.
+// NOTE: this table must be in-sync with sun.jvm.hotspot.memory.FileMapInfo::populateMetadataTypeArray().
 #define CPP_VTABLE_TYPES_DO(f) \
   f(ConstantPool) \
   f(InstanceKlass) \
   f(InstanceClassLoaderKlass) \
   f(InstanceMirrorKlass) \
   f(InstanceRefKlass) \
   f(InstanceStackChunkKlass) \
   f(Method) \
   f(ObjArrayKlass) \
-  f(TypeArrayKlass)
+  f(TypeArrayKlass) \
+  f(FlatArrayKlass) \
+  f(InlineKlass)
 
 class CppVtableInfo {
   intptr_t _vtable_size;
   intptr_t _cloned_vtable[1];
 public:
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
@@ -79,10 +79,75 @@
 
 #ifndef O_BINARY       // if defined (Win32) use binary files.
 #define O_BINARY 0     // otherwise do nothing.
 #endif
 
+inline void CDSMustMatchFlags::do_print(outputStream* st, bool v) {
+  st->print("%s", v ? "true" : "false");
+}
+
+inline void CDSMustMatchFlags::do_print(outputStream* st, intx v) {
+  st->print(INTX_FORMAT, v);
+}
+
+inline void CDSMustMatchFlags::do_print(outputStream* st, uintx v) {
+  st->print(UINTX_FORMAT, v);
+}
+
+inline void CDSMustMatchFlags::do_print(outputStream* st, double v) {
+  st->print("%f", v);
+}
+
+void CDSMustMatchFlags::init() {
+  assert(CDSConfig::is_dumping_archive(), "sanity");
+  _max_name_width = 0;
+
+#define INIT_CDS_MUST_MATCH_FLAG(n) \
+  _v_##n = n; \
+  _max_name_width = MAX2(_max_name_width,strlen(#n));
+  CDS_MUST_MATCH_FLAGS_DO(INIT_CDS_MUST_MATCH_FLAG);
+#undef INIT_CDS_MUST_MATCH_FLAG
+}
+
+bool CDSMustMatchFlags::runtime_check() const {
+#define CHECK_CDS_MUST_MATCH_FLAG(n) \
+  if (_v_##n != n) { \
+    ResourceMark rm; \
+    stringStream ss; \
+    ss.print("VM option %s is different between dumptime (", #n);  \
+    do_print(&ss, _v_ ## n); \
+    ss.print(") and runtime ("); \
+    do_print(&ss, n); \
+    ss.print(")"); \
+    log_info(cds)("%s", ss.as_string()); \
+    return false; \
+  }
+  CDS_MUST_MATCH_FLAGS_DO(CHECK_CDS_MUST_MATCH_FLAG);
+#undef CHECK_CDS_MUST_MATCH_FLAG
+
+  return true;
+}
+
+void CDSMustMatchFlags::print_info() const {
+  LogTarget(Info, cds) lt;
+  if (lt.is_enabled()) {
+    LogStream ls(lt);
+    ls.print_cr("Recorded VM flags during dumptime:");
+    print(&ls);
+  }
+}
+
+void CDSMustMatchFlags::print(outputStream* st) const {
+#define PRINT_CDS_MUST_MATCH_FLAG(n) \
+  st->print("- %-s ", #n);                   \
+  st->sp(int(_max_name_width - strlen(#n))); \
+  do_print(st, _v_##n);                      \
+  st->cr();
+  CDS_MUST_MATCH_FLAGS_DO(PRINT_CDS_MUST_MATCH_FLAG);
+#undef PRINT_CDS_MUST_MATCH_FLAG
+}
+
 // Fill in the fileMapInfo structure with data about this VM instance.
 
 // This method copies the vm version info into header_version.  If the version is too
 // long then a truncated version, which has a hash code appended to it, is copied.
 //
@@ -212,11 +277,11 @@
   _compressed_oops = UseCompressedOops;
   _compressed_class_ptrs = UseCompressedClassPointers;
   _max_heap_size = MaxHeapSize;
   _use_optimized_module_handling = MetaspaceShared::use_optimized_module_handling();
   _has_full_module_graph = CDSConfig::is_dumping_full_module_graph();
-
+  _has_valhalla_patched_classes = CDSConfig::is_valhalla_preview();
   // The following fields are for sanity checks for whether this archive
   // will function correctly with this JVM and the bootclasspath it's
   // invoked with.
 
   // JVM version string ... changes on each build.
@@ -232,10 +297,11 @@
   _has_platform_or_app_classes = ClassLoaderExt::has_platform_or_app_classes();
   _has_non_jar_in_classpath = ClassLoaderExt::has_non_jar_in_classpath();
   _requested_base_address = (char*)SharedBaseAddress;
   _mapped_base_address = (char*)SharedBaseAddress;
   _allow_archiving_with_java_agent = AllowArchivingWithJavaAgent;
+  _must_match.init();
 
   if (!CDSConfig::is_dumping_dynamic_archive()) {
     set_shared_path_table(info->_shared_path_table);
   }
 }
@@ -290,11 +356,13 @@
   st->print_cr("- mapped_base_address:            " INTPTR_FORMAT, p2i(_mapped_base_address));
   st->print_cr("- heap_roots_offset:              " SIZE_FORMAT, _heap_roots_offset);
   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_valhalla_patched_classes    %d", _has_valhalla_patched_classes);
   st->print_cr("- ptrmap_size_in_bits:            " SIZE_FORMAT, _ptrmap_size_in_bits);
+  _must_match.print(st);
 }
 
 void SharedClassPathEntry::init_as_non_existent(const char* path, TRAPS) {
   _type = non_existent_entry;
   set_name(path, CHECK);
@@ -1345,10 +1413,14 @@
       log_warning(cds)("The shared archive file has been truncated.");
       return false;
     }
   }
 
+  if (!header()->check_must_match_flags()) {
+    return false;
+  }
+
   return true;
 }
 
 void FileMapInfo::seek_to_position(size_t pos) {
   if (os::lseek(_fd, (long)pos, SEEK_SET) < 0) {
@@ -2387,10 +2459,28 @@
     log_info(cds)("Unable to use shared archive.\nThe saved state of UseCompressedOops and UseCompressedClassPointers is "
                                "different from runtime, CDS will be disabled.");
     return false;
   }
 
+  if (is_static()) {
+    const char* err = nullptr;
+    if (CDSConfig::is_valhalla_preview()) {
+      if (!_has_valhalla_patched_classes) {
+        err = "not created";
+      }
+    } else {
+      if (_has_valhalla_patched_classes) {
+        err = "created";
+      }
+    }
+    if (err != nullptr) {
+      log_warning(cds)("This archive was %s with --enable-preview -XX:+EnableValhalla. It is "
+                         "incompatible with the current JVM setting", err);
+      return false;
+    }
+  }
+
   if (!_use_optimized_module_handling) {
     MetaspaceShared::disable_optimized_module_handling();
     log_info(cds)("optimized module handling: disabled because archive was created without optimized module handling");
   }
 
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
@@ -29,10 +29,11 @@
 #include "include/cds.h"
 #include "logging/logLevel.hpp"
 #include "memory/allocation.hpp"
 #include "oops/array.hpp"
 #include "oops/compressedOops.hpp"
+#include "runtime/globals.hpp"
 #include "utilities/align.hpp"
 
 // To understand the layout of the CDS archive file:
 //
 // java -Xlog:cds+map=info:file=cds.map:none:filesize=0
@@ -173,10 +174,38 @@
 
   bool check_region_crc(char* base) const;
   void print(outputStream* st, int region_index);
 };
 
+#define CDS_MUST_MATCH_FLAGS_DO(f) \
+  f(EnableValhalla) \
+  f(FlatArrayElementMaxOops) \
+  f(FlatArrayElementMaxSize) \
+  f(InlineFieldMaxFlatSize) \
+  f(InlineTypePassFieldsAsArgs) \
+  f(InlineTypeReturnedAsFields)
+
+class CDSMustMatchFlags {
+private:
+  size_t _max_name_width;
+#define DECLARE_CDS_MUST_MATCH_FLAG(n) \
+  decltype(n) _v_##n;
+  CDS_MUST_MATCH_FLAGS_DO(DECLARE_CDS_MUST_MATCH_FLAG);
+#undef DECLARE_CDS_MUST_MATCH_FLAG
+
+  inline static void do_print(outputStream* st, bool v);
+  inline static void do_print(outputStream* st, intx v);
+  inline static void do_print(outputStream* st, uintx v);
+  inline static void do_print(outputStream* st, double v);
+  void print_info() const;
+
+public:
+  void init();
+  bool runtime_check() const;
+  void print(outputStream* st) const;
+};
+
 class FileMapHeader: private CDSFileMapHeaderBase {
   friend class CDSConstants;
   friend class VMStructs;
 
 private:
@@ -223,11 +252,13 @@
 
   bool   _allow_archiving_with_java_agent; // setting of the AllowArchivingWithJavaAgent option
   bool   _use_optimized_module_handling;// No module-relation VM options were specified, so we can skip
                                         // some expensive operations.
   bool   _has_full_module_graph;        // Does this CDS archive contain the full archived module graph?
+  bool   _has_valhalla_patched_classes; // Is this archived dumped with --enable-preview -XX:+EnableValhalla?
   size_t _ptrmap_size_in_bits;          // Size of pointer relocation bitmap
+  CDSMustMatchFlags _must_match;        // These flags must be the same between dumptime and runtime
   size_t _heap_roots_offset;            // Offset of the HeapShared::roots() object, from the bottom
                                         // of the archived heap objects, in bytes.
   char* from_mapped_offset(size_t offset) const {
     return mapped_base_address() + offset;
   }
@@ -310,10 +341,14 @@
                 size_t common_app_classpath_size);
   static bool is_valid_region(int region) {
     return (0 <= region && region < NUM_CDS_REGIONS);
   }
 
+  bool check_must_match_flags() const {
+    return _must_match.runtime_check();
+  }
+
   void print(outputStream* st);
 };
 
 class FileMapInfo : public CHeapObj<mtInternal> {
 private:
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, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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.
@@ -1603,10 +1603,17 @@
   //     are restored by HeapShared::initialize_from_archived_subgraph().
   int i;
   for (int i = 0; fields[i].valid(); ) {
     ArchivableStaticFieldInfo* info = &fields[i];
     const char* klass_name = info->klass_name;
+
+    if (CDSConfig::is_valhalla_preview() && strcmp(klass_name, "jdk/internal/module/ArchivedModuleGraph") == 0) {
+      // FIXME -- ArchivedModuleGraph doesn't work when java.base is patched with valhalla classes.
+      i++;
+      continue;
+    }
+
     start_recording_subgraph(info->klass, klass_name, is_full_module_graph);
 
     // 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
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
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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.
@@ -65,10 +65,12 @@
 #include "memory/metaspaceClosure.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
 #include "nmt/memTracker.hpp"
 #include "oops/compressedKlass.hpp"
+#include "oops/flatArrayKlass.hpp"
+#include "oops/inlineKlass.hpp"
 #include "oops/instanceMirrorKlass.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/objArrayOop.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/oopHandle.hpp"
@@ -836,10 +838,14 @@
   }
 }
 
 #if INCLUDE_CDS_JAVA_HEAP
 void VM_PopulateDumpSharedSpace::dump_java_heap_objects(GrowableArray<Klass*>* klasses) {
+  if (CDSConfig::is_valhalla_preview()) {
+    log_info(cds)("Archived java heap is not yet supported with Valhalla preview");
+    return;
+  }
   if(!HeapShared::can_write()) {
     log_info(cds)(
       "Archived java heap is not supported as UseG1GC "
       "and UseCompressedClassPointers are required."
       "Current settings: UseG1GC=%s, UseCompressedClassPointers=%s.",
diff a/src/hotspot/share/ci/bcEscapeAnalyzer.cpp b/src/hotspot/share/ci/bcEscapeAnalyzer.cpp
--- a/src/hotspot/share/ci/bcEscapeAnalyzer.cpp
+++ b/src/hotspot/share/ci/bcEscapeAnalyzer.cpp
@@ -553,10 +553,13 @@
       case Bytecodes::_aastore:
       {
         set_global_escape(state.apop());
         state.spop();
         ArgumentMap arr = state.apop();
+        // If the array is a flat array, a larger part of it is modified than
+        // the size of a reference. However, if OFFSET_ANY is given as
+        // parameter to set_modified(), size is not taken into account.
         set_modified(arr, OFFSET_ANY, type2size[T_OBJECT]*HeapWordSize);
         break;
       }
       case Bytecodes::_pop:
         state.raw_pop();
diff a/src/hotspot/share/ci/ciArray.hpp b/src/hotspot/share/ci/ciArray.hpp
--- a/src/hotspot/share/ci/ciArray.hpp
+++ b/src/hotspot/share/ci/ciArray.hpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2020, 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.
@@ -27,10 +27,11 @@
 
 #include "ci/ciArrayKlass.hpp"
 #include "ci/ciConstant.hpp"
 #include "ci/ciObject.hpp"
 #include "oops/arrayOop.hpp"
+#include "oops/flatArrayOop.hpp"
 #include "oops/objArrayOop.hpp"
 #include "oops/typeArrayOop.hpp"
 
 // ciArray
 //
@@ -41,10 +42,11 @@
   int _length;
 
 protected:
   ciArray( objArrayHandle h_a) : ciObject(h_a), _length(h_a()->length()) {}
   ciArray(typeArrayHandle h_a) : ciObject(h_a), _length(h_a()->length()) {}
+  ciArray(flatArrayHandle h_a) : ciObject(h_a), _length(h_a()->length()) {}
 
   arrayOop get_arrayOop() const { return (arrayOop)get_oop(); }
 
   const char* type_string() { return "ciArray"; }
 
diff a/src/hotspot/share/ci/ciArrayKlass.cpp b/src/hotspot/share/ci/ciArrayKlass.cpp
--- a/src/hotspot/share/ci/ciArrayKlass.cpp
+++ b/src/hotspot/share/ci/ciArrayKlass.cpp
@@ -22,13 +22,16 @@
  *
  */
 
 #include "precompiled.hpp"
 #include "ci/ciArrayKlass.hpp"
+#include "ci/ciFlatArrayKlass.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "ci/ciObjArrayKlass.hpp"
 #include "ci/ciTypeArrayKlass.hpp"
 #include "ci/ciUtilities.inline.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "memory/universe.hpp"
 
 // ciArrayKlass
 //
 // This class represents a Klass* in the HotSpot virtual machine
@@ -58,11 +61,11 @@
 // What type is obtained when this array is indexed once?
 ciType* ciArrayKlass::element_type() {
   if (is_type_array_klass()) {
     return ciType::make(as_type_array_klass()->element_type());
   } else {
-    return as_obj_array_klass()->element_klass()->as_klass();
+    return element_klass()->as_klass();
   }
 }
 
 
 // ------------------------------------------------------------------
@@ -70,16 +73,18 @@
 //
 // What type is obtained when this array is indexed as many times as possible?
 ciType* ciArrayKlass::base_element_type() {
   if (is_type_array_klass()) {
     return ciType::make(as_type_array_klass()->element_type());
-  } else {
+  } else if (is_obj_array_klass()) {
     ciKlass* ek = as_obj_array_klass()->base_element_klass();
     if (ek->is_type_array_klass()) {
       return ciType::make(ek->as_type_array_klass()->element_type());
     }
     return ek;
+  } else {
+    return as_flat_array_klass()->base_element_klass();
   }
 }
 
 
 // ------------------------------------------------------------------
@@ -95,13 +100,38 @@
 
 // ------------------------------------------------------------------
 // ciArrayKlass::make
 //
 // Make an array klass of the specified element type.
-ciArrayKlass* ciArrayKlass::make(ciType* element_type) {
+ciArrayKlass* ciArrayKlass::make(ciType* element_type, bool null_free) {
   if (element_type->is_primitive_type()) {
     return ciTypeArrayKlass::make(element_type->basic_type());
   } else {
-    return ciObjArrayKlass::make(element_type->as_klass());
+    ciKlass* klass = element_type->as_klass();
+    if (null_free && klass->is_loaded()) {
+      GUARDED_VM_ENTRY(
+        EXCEPTION_CONTEXT;
+        Klass* ak = InlineKlass::cast(klass->get_Klass())->value_array_klass(THREAD);
+        if (HAS_PENDING_EXCEPTION) {
+          CLEAR_PENDING_EXCEPTION;
+        } else if (ak->is_flatArray_klass()) {
+          return CURRENT_THREAD_ENV->get_flat_array_klass(ak);
+        } else if (ak->is_objArray_klass()) {
+          return CURRENT_THREAD_ENV->get_obj_array_klass(ak);
+        }
+      )
+    }
+    return ciObjArrayKlass::make(klass);
   }
 }
 
+int ciArrayKlass::array_header_in_bytes() {
+  return get_ArrayKlass()->array_header_in_bytes();
+}
+
+ciInstance* ciArrayKlass::component_mirror_instance() const {
+  GUARDED_VM_ENTRY(
+    oop component_mirror = ArrayKlass::cast(get_Klass())->component_mirror();
+    return CURRENT_ENV->get_instance(component_mirror);
+  )
+}
+
diff a/src/hotspot/share/ci/ciArrayKlass.hpp b/src/hotspot/share/ci/ciArrayKlass.hpp
--- a/src/hotspot/share/ci/ciArrayKlass.hpp
+++ b/src/hotspot/share/ci/ciArrayKlass.hpp
@@ -53,9 +53,17 @@
   bool is_leaf_type();          // No subtypes of this array type.
 
   // What kind of vmObject is this?
   bool is_array_klass() const { return true; }
 
-  static ciArrayKlass* make(ciType* element_type);
+  // The one-level type of the array elements.
+  virtual ciKlass* element_klass() { return nullptr; }
+
+  static ciArrayKlass* make(ciType* klass, bool null_free = false);
+
+  int array_header_in_bytes();
+  ciInstance* component_mirror_instance() const;
+
+  virtual bool is_elem_null_free() const { return false; }
 };
 
 #endif // SHARE_CI_CIARRAYKLASS_HPP
diff a/src/hotspot/share/ci/ciClassList.hpp b/src/hotspot/share/ci/ciClassList.hpp
--- a/src/hotspot/share/ci/ciClassList.hpp
+++ b/src/hotspot/share/ci/ciClassList.hpp
@@ -57,14 +57,17 @@
 class   ciMetadata;
 class   ciMethod;
 class   ciMethodData;
 class     ciReceiverTypeData;  // part of ciMethodData
 class   ciType;
+class    ciWrapper;
 class    ciReturnAddress;
 class    ciKlass;
 class     ciInstanceKlass;
+class       ciInlineKlass;
 class     ciArrayKlass;
+class       ciFlatArrayKlass;
 class       ciObjArrayKlass;
 class       ciTypeArrayKlass;
 
 // Simulate Java Language style package-private access with
 // friend declarations.
@@ -110,12 +113,15 @@
 friend class ciMetadata;               \
 friend class ciReplay;                 \
 friend class ciTypeArray;              \
 friend class ciType;                   \
 friend class ciReturnAddress;          \
+friend class  ciWrapper;               \
 friend class ciKlass;                  \
 friend class ciInstanceKlass;          \
+friend class ciInlineKlass;            \
 friend class ciArrayKlass;             \
+friend class ciFlatArrayKlass;         \
 friend class ciObjArrayKlass;          \
 friend class ciTypeArrayKlass;         \
 
 #endif // SHARE_CI_CICLASSLIST_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
@@ -24,10 +24,11 @@
 
 #include "precompiled.hpp"
 #include "ci/ciConstant.hpp"
 #include "ci/ciEnv.hpp"
 #include "ci/ciField.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "ci/ciInstance.hpp"
 #include "ci/ciInstanceKlass.hpp"
 #include "ci/ciMethod.hpp"
 #include "ci/ciNullObject.hpp"
 #include "ci/ciReplay.hpp"
@@ -525,11 +526,12 @@
   // In either case, if we can find the element type in the system dictionary,
   // we must build an array type around it.  The CI requires array klasses
   // to be loaded if their element klasses are loaded, except when memory
   // is exhausted.
   if (Signature::is_array(sym) &&
-      (sym->char_at(1) == JVM_SIGNATURE_ARRAY || sym->char_at(1) == JVM_SIGNATURE_CLASS)) {
+      (sym->char_at(1) == JVM_SIGNATURE_ARRAY ||
+       sym->char_at(1) == JVM_SIGNATURE_CLASS )) {
     // We have an unloaded array.
     // Build it on the fly if the element class exists.
     SignatureStream ss(sym, false);
     ss.skip_array_prefix(1);
     // Get element ciKlass recursively.
@@ -538,11 +540,11 @@
                              cpool,
                              get_symbol(ss.as_symbol()),
                              require_local);
     if (elem_klass != nullptr && elem_klass->is_loaded()) {
       // Now make an array for it
-      return ciObjArrayKlass::make_impl(elem_klass);
+      return ciArrayKlass::make(elem_klass);
     }
   }
 
   if (found_klass == nullptr && !cpool.is_null() && cpool->has_preresolution()) {
     // Look inside the constant pool for pre-resolved class entries.
@@ -564,10 +566,14 @@
 
   if (require_local)  return nullptr;
 
   // Not yet loaded into the VM, or not governed by loader constraints.
   // Make a CI representative for it.
+  int i = 0;
+  while (sym->char_at(i) == JVM_SIGNATURE_ARRAY) {
+    i++;
+  }
   return get_unloaded_klass(accessing_klass, name);
 }
 
 // ------------------------------------------------------------------
 // ciEnv::get_klass_by_name
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
@@ -200,10 +200,14 @@
 
   ciInstance* get_instance(oop o) {
     if (o == nullptr) return nullptr;
     return get_object(o)->as_instance();
   }
+  ciFlatArrayKlass* get_flat_array_klass(Klass* o) {
+    if (o == nullptr) return nullptr;
+    return get_metadata(o)->as_flat_array_klass();
+  }
   ciObjArrayKlass* get_obj_array_klass(Klass* o) {
     if (o == nullptr) return nullptr;
     return get_metadata(o)->as_obj_array_klass();
   }
   ciTypeArrayKlass* get_type_array_klass(Klass* o) {
@@ -496,10 +500,14 @@
   void dump_replay_data_unsafe(outputStream* out);
   void dump_replay_data_helper(outputStream* out);
   void dump_compile_data(outputStream* out);
   void dump_replay_data_version(outputStream* out);
 
+  ciWrapper* make_null_free_wrapper(ciType* type) {
+    return _factory->make_null_free_wrapper(type);
+  }
+
   const char *dyno_name(const InstanceKlass* ik) const;
   const char *replay_name(const InstanceKlass* ik) const;
   const char *replay_name(ciKlass* i) const;
 
   void record_lambdaform(Thread* thread, oop obj);
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
@@ -69,11 +69,11 @@
 // decreases for complex compilation tasks.
 
 // ------------------------------------------------------------------
 // ciField::ciField
 ciField::ciField(ciInstanceKlass* klass, int index, Bytecodes::Code bc) :
-    _known_to_link_with_put(nullptr), _known_to_link_with_get(nullptr) {
+  _original_holder(nullptr), _is_flat(false), _known_to_link_with_put(nullptr), _known_to_link_with_get(nullptr) {
   ASSERT_IN_VM;
   CompilerThread *THREAD = CompilerThread::current();
 
   assert(ciObjectFactory::is_initialized(), "not a shared field");
 
@@ -103,10 +103,12 @@
     _type = ciType::make(field_type);
   }
 
   _name = (ciSymbol*)ciEnv::current(THREAD)->get_symbol(name);
 
+  _is_null_free = false;
+
   // Get the field's declared holder.
   //
   // Note: we actually create a ciInstanceKlass for this klass,
   // even though we may not need to.
   int holder_index = cpool->klass_ref_index_at(index, bc);
@@ -214,10 +216,35 @@
   // Either (a) it is marked shared, or else (b) we are done bootstrapping.
   assert(is_shared() || ciObjectFactory::is_initialized(),
          "bootstrap classes must not create & cache unshared fields");
 }
 
+// Special copy constructor used to flatten inline type fields by
+// copying the fields of the inline type to a new holder klass.
+ciField::ciField(ciField* field, ciInstanceKlass* holder, int offset, bool is_final) {
+  assert(field->holder()->is_inlinetype(), "should only be used for inline type field flattening");
+  // Set the is_final flag
+  jint final = is_final ? JVM_ACC_FINAL : ~JVM_ACC_FINAL;
+  AccessFlags flags(field->flags().as_int() & final);
+  _flags = ciFlags(flags);
+  _holder = holder;
+  _offset = offset;
+  // Copy remaining fields
+  _name = field->_name;
+  _signature = field->_signature;
+  _type = field->_type;
+  // Trust final flat fields
+  _is_constant = is_final;
+  _known_to_link_with_put = field->_known_to_link_with_put;
+  _known_to_link_with_get = field->_known_to_link_with_get;
+  _constant_value = field->_constant_value;
+  assert(!field->is_flat(), "field must not be flat");
+  _is_flat = false;
+  _is_null_free = field->_is_null_free;
+  _original_holder = (field->_original_holder != nullptr) ? field->_original_holder : field->_holder;
+}
+
 static bool trust_final_non_static_fields(ciInstanceKlass* holder) {
   if (holder == nullptr)
     return false;
   if (holder->name() == ciSymbols::java_lang_System())
     // Never trust strangely unstable finals:  System.out, etc.
@@ -231,10 +258,13 @@
     return true;
   // Trust hidden classes. They are created via Lookup.defineHiddenClass and
   // can't be serialized, so there is no hacking of finals going on with them.
   if (holder->is_hidden())
     return true;
+  // Trust final fields in inline type buffers
+  if (holder->is_inlinetype())
+    return true;
   // Trust final fields in all boxed classes
   if (holder->is_box_klass())
     return true;
   // Trust final fields in records
   if (holder->is_record())
@@ -258,10 +288,13 @@
   _flags = ciFlags(fd->access_flags(), fd->field_flags().is_stable(), fd->field_status().is_initialized_final_update());
   _offset = fd->offset();
   Klass* field_holder = fd->field_holder();
   assert(field_holder != nullptr, "null field_holder");
   _holder = CURRENT_ENV->get_instance_klass(field_holder);
+  _is_flat = fd->is_flat();
+  _is_null_free = fd->is_null_free_inline_type();
+  _original_holder = nullptr;
 
   // Check to see if the field is constant.
   Klass* k = _holder->get_Klass();
   bool is_stable_field = FoldStableValues && is_stable();
   if ((is_final() && !has_initialized_final_update()) || is_stable_field) {
@@ -340,11 +373,13 @@
 ciType* ciField::compute_type() {
   GUARDED_VM_ENTRY(return compute_type_impl();)
 }
 
 ciType* ciField::compute_type_impl() {
-  ciKlass* type = CURRENT_ENV->get_klass_by_name_impl(_holder, constantPoolHandle(), _signature, false);
+  // Use original holder for fields that came in through flattening
+  ciKlass* accessing_klass = (_original_holder != nullptr) ? _original_holder : _holder;
+  ciKlass* type = CURRENT_ENV->get_klass_by_name_impl(accessing_klass, constantPoolHandle(), _signature, false);
   if (!type->is_primitive_type() && is_shared()) {
     // We must not cache a pointer to an unshared type, in a shared field.
     bool type_is_also_shared = false;
     if (type->is_type_array_klass()) {
       type_is_also_shared = true;  // int[] etc. are explicitly bootstrapped
@@ -453,10 +488,12 @@
   tty->print(" is_constant=%s", bool_to_str(_is_constant));
   if (_is_constant && is_static()) {
     tty->print(" constant_value=");
     _constant_value.print();
   }
+  tty->print(" is_flat=%s", bool_to_str(_is_flat));
+  tty->print(" is_null_free=%s", bool_to_str(_is_null_free));
   tty->print(">");
 }
 
 // ------------------------------------------------------------------
 // ciField::print_name_on
diff a/src/hotspot/share/ci/ciField.hpp b/src/hotspot/share/ci/ciField.hpp
--- a/src/hotspot/share/ci/ciField.hpp
+++ b/src/hotspot/share/ci/ciField.hpp
@@ -42,24 +42,28 @@
   friend class ciInstanceKlass;
 
 private:
   ciFlags          _flags;
   ciInstanceKlass* _holder;
+  ciInstanceKlass* _original_holder; // For fields nested in flat fields
   ciSymbol*        _name;
   ciSymbol*        _signature;
   ciType*          _type;
   int              _offset;
   bool             _is_constant;
+  bool             _is_flat;
+  bool             _is_null_free;
   ciMethod*        _known_to_link_with_put;
   ciInstanceKlass* _known_to_link_with_get;
   ciConstant       _constant_value;
 
   ciType* compute_type();
   ciType* compute_type_impl();
 
   ciField(ciInstanceKlass* klass, int index, Bytecodes::Code bc);
   ciField(fieldDescriptor* fd);
+  ciField(ciField* field, ciInstanceKlass* holder, int offset, bool is_final);
 
   // shared constructor code
   void initialize_from(fieldDescriptor* fd);
 
 public:
@@ -167,10 +171,13 @@
   bool is_static               () const { return flags().is_static(); }
   bool is_final                () const { return flags().is_final(); }
   bool is_stable               () const { return flags().is_stable(); }
   bool is_volatile             () const { return flags().is_volatile(); }
   bool is_transient            () const { return flags().is_transient(); }
+  bool is_flat                 () const { return _is_flat; }
+  bool is_null_free            () const { return _is_null_free; }
+
   // The field is modified outside of instance initializer methods
   // (or class/initializer methods if the field is static).
   bool has_initialized_final_update() const { return flags().has_initialized_final_update(); }
 
   bool is_call_site_target();
diff a/src/hotspot/share/ci/ciFlags.cpp b/src/hotspot/share/ci/ciFlags.cpp
--- a/src/hotspot/share/ci/ciFlags.cpp
+++ b/src/hotspot/share/ci/ciFlags.cpp
@@ -39,13 +39,10 @@
   }
 
   if (is_final()) {
     st->print(",final");
   }
-  if (is_super()) {
-    st->print(",super");
-  }
   if (is_interface()) {
     st->print(",interface");
   }
   if (is_abstract()) {
     st->print(",abstract");
diff a/src/hotspot/share/ci/ciFlags.hpp b/src/hotspot/share/ci/ciFlags.hpp
--- a/src/hotspot/share/ci/ciFlags.hpp
+++ b/src/hotspot/share/ci/ciFlags.hpp
@@ -53,16 +53,16 @@
   bool is_private              () const { return (_flags & JVM_ACC_PRIVATE                   ) != 0; }
   bool is_protected            () const { return (_flags & JVM_ACC_PROTECTED                 ) != 0; }
   bool is_static               () const { return (_flags & JVM_ACC_STATIC                    ) != 0; }
   bool is_final                () const { return (_flags & JVM_ACC_FINAL                     ) != 0; }
   bool is_synchronized         () const { return (_flags & JVM_ACC_SYNCHRONIZED              ) != 0; }
-  bool is_super                () const { return (_flags & JVM_ACC_SUPER                     ) != 0; }
   bool is_volatile             () const { return (_flags & JVM_ACC_VOLATILE                  ) != 0; }
   bool is_transient            () const { return (_flags & JVM_ACC_TRANSIENT                 ) != 0; }
   bool is_native               () const { return (_flags & JVM_ACC_NATIVE                    ) != 0; }
   bool is_interface            () const { return (_flags & JVM_ACC_INTERFACE                 ) != 0; }
   bool is_abstract             () const { return (_flags & JVM_ACC_ABSTRACT                  ) != 0; }
+  bool has_vararg              () const { return (_flags & JVM_ACC_VARARGS                   ) != 0; }
   bool is_stable               () const { return _stable; }
   // In case the current object represents a field, return true if
   // the field is modified outside of instance initializer methods
   // (or class/initializer methods if the field is static) and false
   // otherwise.
diff a/src/hotspot/share/ci/ciFlatArray.hpp b/src/hotspot/share/ci/ciFlatArray.hpp
--- /dev/null
+++ b/src/hotspot/share/ci/ciFlatArray.hpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2017, 2020, 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_VM_CI_CIFLATARRAY_HPP
+#define SHARE_VM_CI_CIFLATARRAY_HPP
+
+#include "ci/ciArray.hpp"
+#include "ci/ciClassList.hpp"
+#include "oops/flatArrayOop.hpp"
+
+// ciFlatArray
+//
+// This class represents a flatArrayOop in the HotSpot virtual machine.
+class ciFlatArray : public ciArray {
+  CI_PACKAGE_ACCESS
+
+protected:
+  ciFlatArray(flatArrayHandle h_o) : ciArray(h_o) {}
+
+  const char* type_string() { return "ciFlatArray"; }
+};
+
+#endif // SHARE_VM_CI_CIFLATARRAY_HPP
diff a/src/hotspot/share/ci/ciFlatArrayKlass.cpp b/src/hotspot/share/ci/ciFlatArrayKlass.cpp
--- /dev/null
+++ b/src/hotspot/share/ci/ciFlatArrayKlass.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2016, 2021, 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/ciFlatArrayKlass.hpp"
+#include "ci/ciInlineKlass.hpp"
+#include "ci/ciInstanceKlass.hpp"
+#include "ci/ciObjArrayKlass.hpp"
+#include "ci/ciSymbol.hpp"
+#include "ci/ciUtilities.hpp"
+#include "ci/ciUtilities.inline.hpp"
+#include "oops/flatArrayKlass.hpp"
+#include "oops/inlineKlass.inline.hpp"
+
+// ciFlatArrayKlass
+//
+// This class represents a Klass* in the HotSpot virtual machine
+// whose Klass part is a FlatArrayKlass.
+
+// ------------------------------------------------------------------
+// ciFlatArrayKlass::ciFlatArrayKlass
+//
+// Constructor for loaded inline type array klasses.
+ciFlatArrayKlass::ciFlatArrayKlass(Klass* h_k) : ciArrayKlass(h_k) {
+  assert(get_Klass()->is_flatArray_klass(), "wrong type");
+  InlineKlass* element_Klass = get_FlatArrayKlass()->element_klass();
+  _base_element_klass = CURRENT_ENV->get_klass(element_Klass);
+  assert(_base_element_klass->is_inlinetype(), "bad base klass");
+  if (dimension() == 1) {
+    _element_klass = _base_element_klass;
+  } else {
+    _element_klass = nullptr;
+  }
+  if (!ciObjectFactory::is_initialized()) {
+    assert(_element_klass->is_java_lang_Object(), "only arrays of object are shared");
+  }
+}
+
+// ------------------------------------------------------------------
+// ciFlatArrayKlass::element_klass
+//
+// What is the one-level element type of this array?
+ciKlass* ciFlatArrayKlass::element_klass() {
+  if (_element_klass == nullptr) {
+    assert(dimension() > 1, "_element_klass should not be nullptr");
+    assert(is_loaded(), "FlatArrayKlass must be loaded");
+    // Produce the element klass.
+    VM_ENTRY_MARK;
+    Klass* element_Klass = get_FlatArrayKlass()->element_klass();
+    _element_klass = CURRENT_THREAD_ENV->get_klass(element_Klass);
+  }
+  return _element_klass;
+}
+
+ciKlass* ciFlatArrayKlass::exact_klass() {
+  assert(element_klass()->is_loaded() && element_klass()->as_inline_klass()->exact_klass() != nullptr, "must have exact klass");
+  return this;
+}
diff a/src/hotspot/share/ci/ciFlatArrayKlass.hpp b/src/hotspot/share/ci/ciFlatArrayKlass.hpp
--- /dev/null
+++ b/src/hotspot/share/ci/ciFlatArrayKlass.hpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2016, 2021, 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_VM_CI_CIFLATARRAYKLASS_HPP
+#define SHARE_VM_CI_CIFLATARRAYKLASS_HPP
+
+#include "ci/ciArrayKlass.hpp"
+
+// ciFlatArrayKlass
+//
+// This class represents a Klass* in the HotSpot virtual machine
+// whose Klass part is a FlatArrayKlass.
+class ciFlatArrayKlass : public ciArrayKlass {
+  CI_PACKAGE_ACCESS
+  friend class ciEnv;
+
+private:
+  ciKlass* _element_klass;
+  ciKlass* _base_element_klass;
+
+protected:
+  ciFlatArrayKlass(Klass* h_k);
+
+  FlatArrayKlass* get_FlatArrayKlass() {
+    return (FlatArrayKlass*)get_Klass();
+  }
+
+  const char* type_string() { return "ciFlatArrayKlass"; }
+
+  oop     loader()        { return _base_element_klass->loader(); }
+  jobject loader_handle() { return _base_element_klass->loader_handle(); }
+
+  oop     protection_domain()        { return _base_element_klass->protection_domain(); }
+  jobject protection_domain_handle() { return _base_element_klass->protection_domain_handle(); }
+
+public:
+  // The one-level type of the array elements.
+  ciKlass* element_klass();
+
+  int log2_element_size() {
+    return Klass::layout_helper_log2_element_size(layout_helper());
+  }
+  int element_byte_size() { return 1 << log2_element_size(); }
+
+  // The innermost type of the array elements.
+  ciKlass* base_element_klass() { return _base_element_klass; }
+
+  // What kind of ciObject is this?
+  bool is_flat_array_klass() const { return true; }
+
+  virtual ciKlass* exact_klass();
+
+  virtual bool can_be_inline_array_klass() {
+    return true;
+  }
+
+  virtual bool is_elem_null_free() const { return true; }
+};
+
+
+#endif // SHARE_VM_CI_CIFLATARRAYKLASS_HPP
diff a/src/hotspot/share/ci/ciInlineKlass.cpp b/src/hotspot/share/ci/ciInlineKlass.cpp
--- /dev/null
+++ b/src/hotspot/share/ci/ciInlineKlass.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2017, 2019, 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/ciField.hpp"
+#include "ci/ciInlineKlass.hpp"
+#include "ci/ciUtilities.inline.hpp"
+#include "oops/inlineKlass.inline.hpp"
+
+int ciInlineKlass::compute_nonstatic_fields() {
+  int result = ciInstanceKlass::compute_nonstatic_fields();
+  assert(super() == nullptr || !super()->has_nonstatic_fields(), "an inline type must not inherit fields from its superclass");
+
+  // Compute declared non-static fields (without flattening of inline type fields)
+  GrowableArray<ciField*>* fields = nullptr;
+  GUARDED_VM_ENTRY(fields = compute_nonstatic_fields_impl(nullptr, false /* no flattening */);)
+  Arena* arena = CURRENT_ENV->arena();
+  _declared_nonstatic_fields = (fields != nullptr) ? fields : new (arena) GrowableArray<ciField*>(arena, 0, 0, 0);
+  return result;
+}
+
+// Offset of the first field in the inline type
+int ciInlineKlass::first_field_offset() const {
+  GUARDED_VM_ENTRY(return to_InlineKlass()->first_field_offset();)
+}
+
+// Returns the index of the field with the given offset. If the field at 'offset'
+// belongs to a flat field, return the index of the field in the inline type of the flat field.
+int ciInlineKlass::field_index_by_offset(int offset) {
+  assert(contains_field_offset(offset), "invalid field offset");
+  int best_offset = 0;
+  int best_index = -1;
+  // Search the field with the given offset
+  for (int i = 0; i < nof_declared_nonstatic_fields(); ++i) {
+    int field_offset = _declared_nonstatic_fields->at(i)->offset_in_bytes();
+    if (field_offset == offset) {
+      // Exact match
+      return i;
+    } else if (field_offset < offset && field_offset > best_offset) {
+      // No exact match. Save the index of the field with the closest offset that
+      // is smaller than the given field offset. This index corresponds to the
+      // flat field that holds the field we are looking for.
+      best_offset = field_offset;
+      best_index = i;
+    }
+  }
+  assert(best_index >= 0, "field not found");
+  assert(best_offset == offset || _declared_nonstatic_fields->at(best_index)->type()->is_inlinetype(), "offset should match for non-inline types");
+  return best_index;
+}
+
+// Are arrays containing this inline type flat arrays?
+bool ciInlineKlass::flat_in_array() const {
+  GUARDED_VM_ENTRY(return to_InlineKlass()->flat_array();)
+}
+
+// Can this inline type be passed as multiple values?
+bool ciInlineKlass::can_be_passed_as_fields() const {
+  GUARDED_VM_ENTRY(return to_InlineKlass()->can_be_passed_as_fields();)
+}
+
+// Can this inline type be returned as multiple values?
+bool ciInlineKlass::can_be_returned_as_fields() const {
+  GUARDED_VM_ENTRY(return to_InlineKlass()->can_be_returned_as_fields();)
+}
+
+bool ciInlineKlass::is_empty() {
+  // Do not use InlineKlass::is_empty_inline_type here because it does
+  // consider the container empty even if fields of empty inline types
+  // are not flat
+  return nof_nonstatic_fields() == 0;
+}
+
+// When passing an inline type's fields as arguments, count the number
+// of argument slots that are needed
+int ciInlineKlass::inline_arg_slots() {
+  int slots = 0;
+  for (int j = 0; j < nof_nonstatic_fields(); j++) {
+    ciField* field = nonstatic_field_at(j);
+    slots += type2size[field->type()->basic_type()];
+  }
+  return slots;
+}
+
+// Offset of the default oop in the mirror
+int ciInlineKlass::default_value_offset() const {
+  GUARDED_VM_ENTRY(return to_InlineKlass()->default_value_offset();)
+}
+
+ciInstance* ciInlineKlass::default_instance() const {
+  GUARDED_VM_ENTRY(
+    oop default_value = to_InlineKlass()->default_value();
+    return CURRENT_ENV->get_instance(default_value);
+  )
+}
+
+bool ciInlineKlass::contains_oops() const {
+  GUARDED_VM_ENTRY(return get_InlineKlass()->contains_oops();)
+}
+
+int ciInlineKlass::oop_count() const {
+  GUARDED_VM_ENTRY(return get_InlineKlass()->nonstatic_oop_count();)
+}
+
+address ciInlineKlass::pack_handler() const {
+  GUARDED_VM_ENTRY(return get_InlineKlass()->pack_handler();)
+}
+
+address ciInlineKlass::unpack_handler() const {
+  GUARDED_VM_ENTRY(return get_InlineKlass()->unpack_handler();)
+}
+
+InlineKlass* ciInlineKlass::get_InlineKlass() const {
+  GUARDED_VM_ENTRY(return to_InlineKlass();)
+}
+
diff a/src/hotspot/share/ci/ciInlineKlass.hpp b/src/hotspot/share/ci/ciInlineKlass.hpp
--- /dev/null
+++ b/src/hotspot/share/ci/ciInlineKlass.hpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2017, 2019, 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_VM_CI_CIINLINEKLASS_HPP
+#define SHARE_VM_CI_CIINLINEKLASS_HPP
+
+#include "ci/ciConstantPoolCache.hpp"
+#include "ci/ciEnv.hpp"
+#include "ci/ciFlags.hpp"
+#include "ci/ciInstanceKlass.hpp"
+#include "ci/ciSymbol.hpp"
+#include "oops/inlineKlass.hpp"
+
+// ciInlineKlass
+//
+// Specialized ciInstanceKlass for inline types.
+class ciInlineKlass : public ciInstanceKlass {
+  CI_PACKAGE_ACCESS
+
+private:
+  // Fields declared in the bytecode (without nested fields in flat fields)
+  GrowableArray<ciField*>* _declared_nonstatic_fields;
+
+  InlineKlass* to_InlineKlass() const {
+    return InlineKlass::cast(get_Klass());
+  }
+
+protected:
+  ciInlineKlass(Klass* h_k) : ciInstanceKlass(h_k), _declared_nonstatic_fields(nullptr) {
+    assert(is_final(), "InlineKlass must be final");
+  };
+
+  ciInlineKlass(ciSymbol* name, jobject loader, jobject protection_domain) :
+    ciInstanceKlass(name, loader, protection_domain, T_OBJECT) {}
+
+  int compute_nonstatic_fields();
+  const char* type_string() { return "ciInlineKlass"; }
+
+public:
+  bool is_inlinetype() const { return true; }
+
+  int nof_declared_nonstatic_fields() {
+    if (_declared_nonstatic_fields == nullptr) {
+      compute_nonstatic_fields();
+    }
+    return _declared_nonstatic_fields->length();
+  }
+
+  // ith non-static declared field (presented by ascending address)
+  ciField* declared_nonstatic_field_at(int i) {
+    assert(_declared_nonstatic_fields != nullptr, "should be initialized");
+    return _declared_nonstatic_fields->at(i);
+  }
+
+  // Inline type fields
+  int first_field_offset() const;
+  int field_index_by_offset(int offset);
+
+  bool flat_in_array() const;
+  bool can_be_passed_as_fields() const;
+  bool can_be_returned_as_fields() const;
+  bool is_empty();
+  int inline_arg_slots();
+  int default_value_offset() const;
+  ciInstance* default_instance() const;
+  bool contains_oops() const;
+  int oop_count() const;
+  address pack_handler() const;
+  address unpack_handler() const;
+  InlineKlass* get_InlineKlass() const;
+};
+
+#endif // SHARE_VM_CI_CIINLINEKLASS_HPP
diff a/src/hotspot/share/ci/ciInstance.cpp b/src/hotspot/share/ci/ciInstance.cpp
--- a/src/hotspot/share/ci/ciInstance.cpp
+++ b/src/hotspot/share/ci/ciInstance.cpp
@@ -106,11 +106,13 @@
 //
 // Constant value of a field.
 ciConstant ciInstance::field_value(ciField* field) {
   assert(is_loaded(), "invalid access - must be loaded");
   assert(field->holder()->is_loaded(), "invalid access - holder must be loaded");
-  assert(field->is_static() || klass()->is_subclass_of(field->holder()), "invalid access - must be subclass");
+  assert(field->is_static() || field->holder()->is_inlinetype() || klass()->is_subclass_of(field->holder()),
+         "invalid access - must be subclass");
+
   return field_value_impl(field->type()->basic_type(), field->offset_in_bytes());
 }
 
 // ------------------------------------------------------------------
 // ciInstance::field_value_by_offset
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
@@ -22,22 +22,25 @@
  *
  */
 
 #include "precompiled.hpp"
 #include "ci/ciField.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "ci/ciInstance.hpp"
 #include "ci/ciInstanceKlass.hpp"
 #include "ci/ciUtilities.inline.hpp"
 #include "classfile/javaClasses.hpp"
+#include "classfile/systemDictionary.hpp"
 #include "classfile/vmClasses.hpp"
 #include "memory/allocation.hpp"
 #include "memory/allocation.inline.hpp"
 #include "memory/resourceArea.hpp"
 #include "oops/instanceKlass.inline.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/fieldStreams.inline.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "runtime/fieldDescriptor.inline.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/jniHandles.inline.hpp"
 
 // ciInstanceKlass
@@ -117,17 +120,18 @@
   _field_cache = nullptr;
 }
 
 // Version for unloaded classes:
 ciInstanceKlass::ciInstanceKlass(ciSymbol* name,
-                                 jobject loader, jobject protection_domain)
-  : ciKlass(name, T_OBJECT)
+                                 jobject loader, jobject protection_domain,
+                                 BasicType bt)
+  : ciKlass(name, bt)
 {
   assert(name->char_at(0) != JVM_SIGNATURE_ARRAY, "not an instance klass");
   _init_state = (InstanceKlass::ClassState)0;
   _has_nonstatic_fields = false;
-  _nonstatic_fields = nullptr;
+  _nonstatic_fields = nullptr;         // initialized lazily by compute_nonstatic_fields
   _has_injected_fields = -1;
   _is_hidden = false;
   _is_record = false;
   _loader = loader;
   _protection_domain = protection_domain;
@@ -335,15 +339,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");
     }
   }
@@ -433,10 +437,33 @@
   }
   ciField* field = new (CURRENT_THREAD_ENV->arena()) ciField(&fd);
   return field;
 }
 
+ciField* ciInstanceKlass::get_non_flat_field_by_offset(int field_offset) {
+  if (super() != nullptr && super()->has_nonstatic_fields()) {
+    ciField* f = super()->get_non_flat_field_by_offset(field_offset);
+    if (f != nullptr) {
+      return f;
+    }
+  }
+
+  VM_ENTRY_MARK;
+  InstanceKlass* k = get_instanceKlass();
+  Arena* arena = CURRENT_ENV->arena();
+  for (JavaFieldStream fs(k); !fs.done(); fs.next()) {
+    if (fs.access_flags().is_static())  continue;
+    fieldDescriptor& fd = fs.field_descriptor();
+    if (fd.offset() == field_offset) {
+      ciField* f = new (arena) ciField(&fd);
+      return f;
+    }
+  }
+
+  return nullptr;
+}
+
 // ------------------------------------------------------------------
 // ciInstanceKlass::get_field_by_name
 ciField* ciInstanceKlass::get_field_by_name(ciSymbol* name, ciSymbol* signature, bool is_static) {
   VM_ENTRY_MARK;
   InstanceKlass* k = get_instanceKlass();
@@ -491,22 +518,15 @@
     } else {
       return 0;
     }
   }
 
-  int flen = fields->length();
-
-  // Now sort them by offset, ascending.
-  // (In principle, they could mix with superclass fields.)
-  fields->sort(sort_field_by_offset);
   _nonstatic_fields = fields;
-  return flen;
+  return fields->length();
 }
 
-GrowableArray<ciField*>*
-ciInstanceKlass::compute_nonstatic_fields_impl(GrowableArray<ciField*>*
-                                               super_fields) {
+GrowableArray<ciField*>* ciInstanceKlass::compute_nonstatic_fields_impl(GrowableArray<ciField*>* super_fields, bool is_flat) {
   ASSERT_IN_VM;
   Arena* arena = CURRENT_ENV->arena();
   int flen = 0;
   GrowableArray<ciField*>* fields = nullptr;
   InstanceKlass* k = get_instanceKlass();
@@ -528,14 +548,37 @@
   }
 
   for (JavaFieldStream fs(k); !fs.done(); fs.next()) {
     if (fs.access_flags().is_static())  continue;
     fieldDescriptor& fd = fs.field_descriptor();
-    ciField* field = new (arena) ciField(&fd);
-    fields->append(field);
+    if (fd.is_flat() && is_flat) {
+      // Inline type fields are embedded
+      int field_offset = fd.offset();
+      // Get InlineKlass and adjust number of fields
+      Klass* k = get_instanceKlass()->get_inline_type_field_klass(fd.index());
+      ciInlineKlass* vk = CURRENT_ENV->get_klass(k)->as_inline_klass();
+      flen += vk->nof_nonstatic_fields() - 1;
+      // Iterate over fields of the flat inline type and copy them to 'this'
+      for (int i = 0; i < vk->nof_nonstatic_fields(); ++i) {
+        ciField* flat_field = vk->nonstatic_field_at(i);
+        // Adjust offset to account for missing oop header
+        int offset = field_offset + (flat_field->offset_in_bytes() - vk->first_field_offset());
+        // A flat field can be treated as final if the non-flat
+        // field is declared final or the holder klass is an inline type itself.
+        bool is_final = fd.is_final() || is_inlinetype();
+        ciField* field = new (arena) ciField(flat_field, this, offset, is_final);
+        fields->append(field);
+      }
+    } else {
+      ciField* field = new (arena) ciField(&fd);
+      fields->append(field);
+    }
   }
   assert(fields->length() == flen, "sanity");
+  // Now sort them by offset, ascending.
+  // (In principle, they could mix with superclass fields.)
+  fields->sort(sort_field_by_offset);
   return fields;
 }
 
 bool ciInstanceKlass::compute_injected_fields_helper() {
   ASSERT_IN_VM;
@@ -638,82 +681,153 @@
     _implementor = impl;
   }
   return impl;
 }
 
+bool ciInstanceKlass::can_be_inline_klass(bool is_exact) {
+  if (!EnableValhalla) {
+    return false;
+  }
+  if (!is_loaded() || is_inlinetype()) {
+    // Not loaded or known to be an inline klass
+    return true;
+  }
+  if (!is_exact) {
+    // Not exact, check if this is a valid super for an inline klass
+    VM_ENTRY_MARK;
+    return !get_instanceKlass()->access_flags().is_identity_class() || is_java_lang_Object() ;
+  }
+  return false;
+}
+
 // Utility class for printing of the contents of the static fields for
 // use by compilation replay.  It only prints out the information that
 // could be consumed by the compiler, so for primitive types it prints
 // out the actual value.  For Strings it's the actual string value.
 // For array types it it's first level array size since that's the
 // only value which statically unchangeable.  For all other reference
 // types it simply prints out the dynamic type.
 
-class StaticFinalFieldPrinter : public FieldClosure {
+class StaticFieldPrinter : public FieldClosure {
+protected:
   outputStream* _out;
+public:
+  StaticFieldPrinter(outputStream* out) :
+    _out(out) {
+  }
+  void do_field_helper(fieldDescriptor* fd, oop obj, bool is_flat);
+};
+
+class StaticFinalFieldPrinter : public StaticFieldPrinter {
   const char*   _holder;
  public:
   StaticFinalFieldPrinter(outputStream* out, const char* holder) :
-    _out(out),
-    _holder(holder) {
+    StaticFieldPrinter(out), _holder(holder) {
   }
   void do_field(fieldDescriptor* fd) {
     if (fd->is_final() && !fd->has_initial_value()) {
       ResourceMark rm;
-      oop mirror = fd->field_holder()->java_mirror();
-      _out->print("staticfield %s %s %s ", _holder, fd->name()->as_quoted_ascii(), fd->signature()->as_quoted_ascii());
-      switch (fd->field_type()) {
-        case T_BYTE:    _out->print_cr("%d", mirror->byte_field(fd->offset()));   break;
-        case T_BOOLEAN: _out->print_cr("%d", mirror->bool_field(fd->offset()));   break;
-        case T_SHORT:   _out->print_cr("%d", mirror->short_field(fd->offset()));  break;
-        case T_CHAR:    _out->print_cr("%d", mirror->char_field(fd->offset()));   break;
-        case T_INT:     _out->print_cr("%d", mirror->int_field(fd->offset()));    break;
-        case T_LONG:    _out->print_cr(INT64_FORMAT, (int64_t)(mirror->long_field(fd->offset())));   break;
-        case T_FLOAT: {
-          float f = mirror->float_field(fd->offset());
-          _out->print_cr("%d", *(int*)&f);
-          break;
-        }
-        case T_DOUBLE: {
-          double d = mirror->double_field(fd->offset());
-          _out->print_cr(INT64_FORMAT, *(int64_t*)&d);
-          break;
-        }
-        case T_ARRAY:  // fall-through
-        case T_OBJECT: {
-          oop value =  mirror->obj_field_acquire(fd->offset());
-          if (value == nullptr) {
-            _out->print_cr("null");
-          } else if (value->is_instance()) {
-            assert(fd->field_type() == T_OBJECT, "");
-            if (value->is_a(vmClasses::String_klass())) {
-              const char* ascii_value = java_lang_String::as_quoted_ascii(value);
-              _out->print_cr("\"%s\"", (ascii_value != nullptr) ? ascii_value : "");
-            } else {
-              const char* klass_name  = value->klass()->name()->as_quoted_ascii();
-              _out->print_cr("%s", klass_name);
-            }
-          } else if (value->is_array()) {
-            typeArrayOop ta = (typeArrayOop)value;
-            _out->print("%d", ta->length());
-            if (value->is_objArray()) {
-              objArrayOop oa = (objArrayOop)value;
-              const char* klass_name  = value->klass()->name()->as_quoted_ascii();
-              _out->print(" %s", klass_name);
-            }
-            _out->cr();
+      InstanceKlass* holder = fd->field_holder();
+      oop mirror = holder->java_mirror();
+      _out->print("staticfield %s %s ", _holder, fd->name()->as_quoted_ascii());
+      BasicType bt = fd->field_type();
+      if (bt != T_OBJECT && bt != T_ARRAY) {
+        _out->print("%s ", fd->signature()->as_quoted_ascii());
+      }
+      do_field_helper(fd, mirror, false);
+      _out->cr();
+    }
+  }
+};
+
+class InlineTypeFieldPrinter : public StaticFieldPrinter {
+  oop _obj;
+public:
+  InlineTypeFieldPrinter(outputStream* out, oop obj) :
+    StaticFieldPrinter(out), _obj(obj) {
+  }
+  void do_field(fieldDescriptor* fd) {
+    do_field_helper(fd, _obj, true);
+    _out->print(" ");
+  }
+};
+
+void StaticFieldPrinter::do_field_helper(fieldDescriptor* fd, oop mirror, bool is_flat) {
+  BasicType bt = fd->field_type();
+  switch (bt) {
+    case T_BYTE:    _out->print("%d", mirror->byte_field(fd->offset()));   break;
+    case T_BOOLEAN: _out->print("%d", mirror->bool_field(fd->offset()));   break;
+    case T_SHORT:   _out->print("%d", mirror->short_field(fd->offset()));  break;
+    case T_CHAR:    _out->print("%d", mirror->char_field(fd->offset()));   break;
+    case T_INT:     _out->print("%d", mirror->int_field(fd->offset()));    break;
+    case T_LONG:    _out->print(INT64_FORMAT, (int64_t)(mirror->long_field(fd->offset())));   break;
+    case T_FLOAT: {
+      float f = mirror->float_field(fd->offset());
+      _out->print("%d", *(int*)&f);
+      break;
+    }
+    case T_DOUBLE: {
+      double d = mirror->double_field(fd->offset());
+      _out->print(INT64_FORMAT, *(int64_t*)&d);
+      break;
+    }
+    case T_ARRAY:  // fall-through
+    case T_OBJECT:
+      if (!fd->is_null_free_inline_type()) {
+        _out->print("%s ", fd->signature()->as_quoted_ascii());
+        oop value =  mirror->obj_field_acquire(fd->offset());
+        if (value == nullptr) {
+          _out->print_cr("null");
+        } else if (value->is_instance()) {
+          assert(fd->field_type() == T_OBJECT, "");
+          if (value->is_a(vmClasses::String_klass())) {
+            const char* ascii_value = java_lang_String::as_quoted_ascii(value);
+            _out->print("\"%s\"", (ascii_value != nullptr) ? ascii_value : "");
           } else {
-            ShouldNotReachHere();
+            const char* klass_name  = value->klass()->name()->as_quoted_ascii();
+            _out->print("%s", klass_name);
           }
-          break;
-        }
-        default:
+        } else if (value->is_array()) {
+          typeArrayOop ta = (typeArrayOop)value;
+          _out->print("%d", ta->length());
+          if (value->is_objArray() || value->is_flatArray()) {
+            objArrayOop oa = (objArrayOop)value;
+            const char* klass_name  = value->klass()->name()->as_quoted_ascii();
+            _out->print(" %s", klass_name);
+          }
+        } else {
           ShouldNotReachHere();
         }
-    }
+        break;
+      } else {
+        // handling of null free inline type
+        ResetNoHandleMark rnhm;
+        Thread* THREAD = Thread::current();
+        SignatureStream ss(fd->signature(), false);
+        Symbol* name = ss.as_symbol();
+        assert(!HAS_PENDING_EXCEPTION, "can resolve klass?");
+        InstanceKlass* holder = fd->field_holder();
+        InstanceKlass* k = SystemDictionary::find_instance_klass(THREAD, name,
+                                                                 Handle(THREAD, holder->class_loader()),
+                                                                 Handle(THREAD, holder->protection_domain()));
+        assert(k != nullptr && !HAS_PENDING_EXCEPTION, "can resolve klass?");
+        InlineKlass* vk = InlineKlass::cast(k);
+        oop obj;
+        if (is_flat) {
+          int field_offset = fd->offset() - vk->first_field_offset();
+          obj = cast_to_oop(cast_from_oop<address>(mirror) + field_offset);
+        } else {
+          obj = mirror->obj_field_acquire(fd->offset());
+        }
+        InlineTypeFieldPrinter print_field(_out, obj);
+        vk->do_nonstatic_fields(&print_field);
+        break;
+      }
+    default:
+      ShouldNotReachHere();
   }
-};
+}
 
 const char *ciInstanceKlass::replay_name() const {
   return CURRENT_ENV->replay_name(get_instanceKlass());
 }
 
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
@@ -67,10 +67,11 @@
   ciInstanceKlass*       _super;
   ciInstance*            _java_mirror;
 
   ciConstantPoolCache*   _field_cache;  // cached map index->field
   GrowableArray<ciField*>* _nonstatic_fields;
+
   int                    _has_injected_fields; // any non static injected fields? lazily initialized.
 
   // The possible values of the _implementor fall into following three cases:
   //   null: no implementor.
   //   A ciInstanceKlass that's not itself: one implementor.
@@ -82,11 +83,11 @@
   bool compute_injected_fields_helper();
   void compute_transitive_interfaces();
 
 protected:
   ciInstanceKlass(Klass* k);
-  ciInstanceKlass(ciSymbol* name, jobject loader, jobject protection_domain);
+  ciInstanceKlass(ciSymbol* name, jobject loader, jobject protection_domain, BasicType bt = T_OBJECT); // for unloaded klasses
 
   InstanceKlass* get_instanceKlass() const {
     return InstanceKlass::cast(get_Klass());
   }
 
@@ -106,12 +107,12 @@
 
   bool is_shared() { return _is_shared; }
 
   void compute_shared_init_state();
   bool compute_shared_has_subklass();
-  int  compute_nonstatic_fields();
-  GrowableArray<ciField*>* compute_nonstatic_fields_impl(GrowableArray<ciField*>* super_fields);
+  virtual int compute_nonstatic_fields();
+  GrowableArray<ciField*>* compute_nonstatic_fields_impl(GrowableArray<ciField*>* super_fields, bool flatten = true);
   bool compute_has_trusted_loader();
 
   // Update the init_state for shared klasses
   void update_if_shared(InstanceKlass::ClassState expected) {
     if (_is_shared && _init_state != expected) {
@@ -205,17 +206,20 @@
   }
 
   ciInstanceKlass* get_canonical_holder(int offset);
   ciField* get_field_by_offset(int field_offset, bool is_static);
   ciField* get_field_by_name(ciSymbol* name, ciSymbol* signature, bool is_static);
+  // get field descriptor at field_offset ignoring flattening
+  ciField* get_non_flat_field_by_offset(int field_offset);
 
   // total number of nonstatic fields (including inherited):
   int nof_nonstatic_fields() {
-    if (_nonstatic_fields == nullptr)
+    if (_nonstatic_fields == nullptr) {
       return compute_nonstatic_fields();
-    else
+    } else {
       return _nonstatic_fields->length();
+    }
   }
 
   bool has_injected_fields() {
     if (_has_injected_fields == -1) {
       compute_injected_fields();
@@ -242,11 +246,10 @@
   ciInstance*            java_mirror();
 
   // Java access flags
   bool is_public      () { return flags().is_public(); }
   bool is_final       () { return flags().is_final(); }
-  bool is_super       () { return flags().is_super(); }
   bool is_interface   () { return flags().is_interface(); }
   bool is_abstract    () { return flags().is_abstract(); }
 
   ciMethod* find_method(ciSymbol* name, ciSymbol* signature);
   // Note:  To find a method from name and type strings, use ciSymbol::make,
@@ -259,10 +262,12 @@
     assert(is_loaded(), "must be loaded");
     ciInstanceKlass* impl = implementor();
     return (impl != this ? impl : nullptr);
   }
 
+  virtual bool can_be_inline_klass(bool is_exact = false);
+
   // Is the defining class loader of this class the default loader?
   bool uses_default_loader() const;
 
   bool is_java_lang_Object() const;
 
diff a/src/hotspot/share/ci/ciKlass.cpp b/src/hotspot/share/ci/ciKlass.cpp
--- a/src/hotspot/share/ci/ciKlass.cpp
+++ b/src/hotspot/share/ci/ciKlass.cpp
@@ -224,10 +224,19 @@
   GUARDED_VM_ENTRY(
     return get_Klass()->access_flags().as_int();
   )
 }
 
+// ------------------------------------------------------------------
+// ciKlass::prototype_header
+markWord ciKlass::prototype_header() const {
+  assert(is_loaded(), "not loaded");
+  GUARDED_VM_ENTRY(
+    return get_Klass()->prototype_header();
+  )
+}
+
 // ------------------------------------------------------------------
 // ciKlass::print_impl
 //
 // Implementation of the print method
 void ciKlass::print_impl(outputStream* st) {
diff a/src/hotspot/share/ci/ciKlass.hpp b/src/hotspot/share/ci/ciKlass.hpp
--- a/src/hotspot/share/ci/ciKlass.hpp
+++ b/src/hotspot/share/ci/ciKlass.hpp
@@ -42,12 +42,14 @@
   friend class ciEnv;
   friend class ciField;
   friend class ciMethod;
   friend class ciMethodData;
   friend class ciObjArrayKlass;
-  friend class ciSignature;
   friend class ciReceiverTypeData;
+  friend class ciSignature;
+  friend class ciFlatArrayKlass;
+  friend class ciArrayKlass;
 
 private:
   ciSymbol* _name;
   jint _layout_helper;
 
@@ -105,10 +107,18 @@
   // Does this type (array, class, interface) have no subtypes?
   virtual bool is_leaf_type() {
     return false;
   }
 
+  virtual bool can_be_inline_klass(bool is_exact = false) {
+    return false;
+  }
+
+  virtual bool can_be_inline_array_klass() {
+    return EnableValhalla && is_java_lang_Object();
+  }
+
   // Attempt to get a klass using this ciKlass's loader.
   ciKlass* find_klass(ciSymbol* klass_name);
   // Note:  To find a class from its name string, use ciSymbol::make,
   // but consider adding to vmSymbols.hpp instead.
 
@@ -119,10 +129,12 @@
   jint                   modifier_flags();
 
   // Fetch Klass::access_flags.
   jint                   access_flags();
 
+  markWord prototype_header() const;
+
   // What kind of ciObject is this?
   bool is_klass() const { return true; }
 
   virtual ciKlass* exact_klass() = 0;
 
diff a/src/hotspot/share/ci/ciMetadata.hpp b/src/hotspot/share/ci/ciMetadata.hpp
--- a/src/hotspot/share/ci/ciMetadata.hpp
+++ b/src/hotspot/share/ci/ciMetadata.hpp
@@ -53,13 +53,17 @@
   virtual bool is_return_address() const    { return false; }
   virtual bool is_method() const            { return false; }
   virtual bool is_method_data() const       { return false; }
   virtual bool is_klass() const             { return false; }
   virtual bool is_instance_klass() const    { return false; }
+  virtual bool is_inlinetype() const        { return false; }
   virtual bool is_array_klass() const       { return false; }
+  virtual bool is_flat_array_klass() const  { return false; }
   virtual bool is_obj_array_klass() const   { return false; }
   virtual bool is_type_array_klass() const  { return false; }
+  virtual bool is_wrapper() const           { return false; }
+  virtual bool flat_in_array() const        { return false; }
   virtual void dump_replay_data(outputStream* st) { /* do nothing */ }
 
   ciMethod*                as_method() {
     assert(is_method(), "bad cast");
     return (ciMethod*)this;
@@ -90,18 +94,30 @@
   }
   ciArrayKlass*            as_array_klass() {
     assert(is_array_klass(), "bad cast");
     return (ciArrayKlass*)this;
   }
+  ciFlatArrayKlass*        as_flat_array_klass() {
+    assert(is_flat_array_klass(), "bad cast");
+    return (ciFlatArrayKlass*)this;
+  }
   ciObjArrayKlass*         as_obj_array_klass() {
     assert(is_obj_array_klass(), "bad cast");
     return (ciObjArrayKlass*)this;
   }
   ciTypeArrayKlass*        as_type_array_klass() {
     assert(is_type_array_klass(), "bad cast");
     return (ciTypeArrayKlass*)this;
   }
+  ciInlineKlass*           as_inline_klass() {
+    assert(is_inlinetype(), "bad cast");
+    return (ciInlineKlass*)this;
+  }
+  ciWrapper*               as_wrapper() {
+    assert(is_wrapper(), "bad cast");
+    return (ciWrapper*)this;
+  }
 
   Metadata* constant_encoding() { return _metadata; }
 
   bool equals(ciMetadata* obj) const { return (this == obj); }
 
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
@@ -48,10 +48,11 @@
 #include "oops/method.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "prims/methodHandles.hpp"
 #include "runtime/deoptimization.hpp"
 #include "runtime/handles.inline.hpp"
+#include "runtime/sharedRuntime.hpp"
 #include "utilities/bitMap.inline.hpp"
 #include "utilities/xmlstream.hpp"
 #ifdef COMPILER2
 #include "ci/bcEscapeAnalyzer.hpp"
 #include "ci/ciTypeFlow.hpp"
@@ -657,10 +658,64 @@
     }
   }
   return false;
 }
 
+bool ciMethod::array_access_profiled_type(int bci, ciKlass*& array_type, ciKlass*& element_type, ProfilePtrKind& element_ptr, bool &flat_array, bool &null_free_array) {
+  if (method_data() != nullptr && method_data()->is_mature()) {
+    ciProfileData* data = method_data()->bci_to_data(bci);
+    if (data != nullptr) {
+      if (data->is_ArrayLoadData()) {
+        ciArrayLoadData* array_access = (ciArrayLoadData*) data->as_ArrayLoadData();
+        array_type = array_access->array()->valid_type();
+        element_type = array_access->element()->valid_type();
+        element_ptr = array_access->element()->ptr_kind();
+        flat_array = array_access->flat_array();
+        null_free_array = array_access->null_free_array();
+        return true;
+      } else if (data->is_ArrayStoreData()) {
+        ciArrayStoreData* array_access = (ciArrayStoreData*) data->as_ArrayStoreData();
+        array_type = array_access->array()->valid_type();
+        flat_array = array_access->flat_array();
+        null_free_array = array_access->null_free_array();
+        ciCallProfile call_profile = call_profile_at_bci(bci);
+        if (call_profile.morphism() == 1) {
+          element_type = call_profile.receiver(0);
+        } else {
+          element_type = nullptr;
+        }
+        if (!array_access->null_seen()) {
+          element_ptr = ProfileNeverNull;
+        } else if (call_profile.count() == 0) {
+          element_ptr = ProfileAlwaysNull;
+        } else {
+          element_ptr = ProfileMaybeNull;
+        }
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+bool ciMethod::acmp_profiled_type(int bci, ciKlass*& left_type, ciKlass*& right_type, ProfilePtrKind& left_ptr, ProfilePtrKind& right_ptr, bool &left_inline_type, bool &right_inline_type) {
+  if (method_data() != nullptr && method_data()->is_mature()) {
+    ciProfileData* data = method_data()->bci_to_data(bci);
+    if (data != nullptr && data->is_ACmpData()) {
+      ciACmpData* acmp = (ciACmpData*)data->as_ACmpData();
+      left_type = acmp->left()->valid_type();
+      right_type = acmp->right()->valid_type();
+      left_ptr = acmp->left()->ptr_kind();
+      right_ptr = acmp->right()->ptr_kind();
+      left_inline_type = acmp->left_inline_type();
+      right_inline_type = acmp->right_inline_type();
+      return true;
+    }
+  }
+  return false;
+}
+
 
 // ------------------------------------------------------------------
 // ciMethod::find_monomorphic_target
 //
 // Given a certain calling environment, find the monomorphic target
@@ -957,14 +1012,17 @@
   vmIntrinsics::ID iid = _intrinsic_id;  // do not check if loaded
   return iid == vmIntrinsics::_compiledLambdaForm;
 }
 
 // ------------------------------------------------------------------
-// ciMethod::is_object_initializer
+// ciMethod::is_object_constructor
 //
-bool ciMethod::is_object_initializer() const {
-   return name() == ciSymbols::object_initializer_name();
+bool ciMethod::is_object_constructor() const {
+   return (name() == ciSymbols::object_initializer_name()
+           && signature()->return_type()->is_void());
+   // Note:  We can't test is_static, because that would
+   // require the method to be loaded.  Sometimes it isn't.
 }
 
 // ------------------------------------------------------------------
 // ciMethod::has_member_arg
 //
@@ -1228,11 +1286,10 @@
 bool ciMethod::has_loops      () const {         FETCH_FLAG_FROM_VM(has_loops); }
 bool ciMethod::has_jsrs       () const {         FETCH_FLAG_FROM_VM(has_jsrs);  }
 bool ciMethod::is_getter      () const {         FETCH_FLAG_FROM_VM(is_getter); }
 bool ciMethod::is_setter      () const {         FETCH_FLAG_FROM_VM(is_setter); }
 bool ciMethod::is_accessor    () const {         FETCH_FLAG_FROM_VM(is_accessor); }
-bool ciMethod::is_initializer () const {         FETCH_FLAG_FROM_VM(is_initializer); }
 bool ciMethod::is_empty       () const {         FETCH_FLAG_FROM_VM(is_empty_method); }
 
 bool ciMethod::is_boxing_method() const {
   if (intrinsic_id() != vmIntrinsics::_none && holder()->is_box_klass()) {
     switch (intrinsic_id()) {
@@ -1475,5 +1532,23 @@
   }
   return true; // no mismatch found
 }
 
 // ------------------------------------------------------------------
+
+bool ciMethod::is_scalarized_arg(int idx) const {
+  VM_ENTRY_MARK;
+  return get_Method()->is_scalarized_arg(idx);
+}
+
+bool ciMethod::has_scalarized_args() const {
+  VM_ENTRY_MARK;
+  return get_Method()->has_scalarized_args();
+}
+
+const GrowableArray<SigEntry>* ciMethod::get_sig_cc() const {
+  VM_ENTRY_MARK;
+  if (get_Method()->adapter() == nullptr) {
+    return nullptr;
+  }
+  return get_Method()->adapter()->get_sig_cc();
+}
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
@@ -44,10 +44,11 @@
 class xmlStream;
 
 // Whether profiling found an oop to be always, never or sometimes
 // null
 enum ProfilePtrKind {
+  ProfileUnknownNull,
   ProfileAlwaysNull,
   ProfileNeverNull,
   ProfileMaybeNull
 };
 
@@ -196,11 +197,11 @@
 
   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 is_class_initializer()   const { return get_Method()->is_class_initializer();   }
   bool changes_current_thread() const { return get_Method()->changes_current_thread(); }
   bool deprecated()             const { return is_loaded() && get_Method()->deprecated(); }
 
   bool check_intrinsic_candidate() const {
     if (intrinsic_id() == vmIntrinsics::_blackhole) {
@@ -267,11 +268,14 @@
 
   // Does type profiling provide any useful information at this point?
   bool          argument_profiled_type(int bci, int i, ciKlass*& type, ProfilePtrKind& ptr_kind);
   bool          parameter_profiled_type(int i, ciKlass*& type, ProfilePtrKind& ptr_kind);
   bool          return_profiled_type(int bci, ciKlass*& type, ProfilePtrKind& ptr_kind);
-
+  bool          array_access_profiled_type(int bci, ciKlass*& array_type, ciKlass*& element_type, ProfilePtrKind& element_ptr, bool &flat_array, bool &null_free);
+  bool          acmp_profiled_type(int bci, ciKlass*& left_type, ciKlass*& right_type,
+                                   ProfilePtrKind& left_ptr, ProfilePtrKind& right_ptr,
+                                   bool &left_inline_type, bool &right_inline_type);
   ciField*      get_field_at_bci( int bci, bool &will_link);
   ciMethod*     get_method_at_bci(int bci, bool &will_link, ciSignature* *declared_signature);
   ciMethod*     get_method_at_bci(int bci) {
     bool ignored_will_link;
     ciSignature* ignored_declared_signature;
@@ -339,10 +343,11 @@
   bool is_final       () const                   { return flags().is_final(); }
   bool is_synchronized() const                   { return flags().is_synchronized(); }
   bool is_native      () const                   { return flags().is_native(); }
   bool is_interface   () const                   { return flags().is_interface(); }
   bool is_abstract    () const                   { return flags().is_abstract(); }
+  bool has_vararg     () const                   { return flags().has_vararg(); }
 
   // Other flags
   bool is_final_method() const                   { return is_final() || holder()->is_final(); }
   bool is_default_method() const                 { return !is_abstract() && !is_private() &&
                                                           holder()->is_interface(); }
@@ -350,18 +355,17 @@
   bool has_loops      () const;
   bool has_jsrs       () const;
   bool is_getter      () const;
   bool is_setter      () const;
   bool is_accessor    () const;
-  bool is_initializer () const;
   bool is_empty       () const;
   bool can_be_statically_bound() const           { return _can_be_statically_bound; }
   bool has_reserved_stack_access() const         { return _has_reserved_stack_access; }
   bool is_boxing_method() const;
   bool is_unboxing_method() const;
+  bool is_object_constructor() const;
   bool is_vector_method() const;
-  bool is_object_initializer() const;
 
   bool can_be_statically_bound(ciInstanceKlass* context) const;
 
   bool can_omit_stack_trace() const;
 
@@ -380,8 +384,13 @@
   // 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);
+
+  // Support for the inline type calling convention
+  bool is_scalarized_arg(int idx) const;
+  bool has_scalarized_args() const;
+  const GrowableArray<SigEntry>* get_sig_cc() const;
 };
 
 #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
@@ -315,16 +315,16 @@
       TypeStackSlotEntries::set_type(i, translate_klass(k));
     }
   }
 }
 
-void ciReturnTypeEntry::translate_type_data_from(const ReturnTypeEntry* ret) {
+void ciSingleTypeEntry::translate_type_data_from(const SingleTypeEntry* ret) {
   intptr_t k = ret->type();
   Klass* klass = (Klass*)klass_part(k);
   if (klass != 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));
+    set_type(SingleTypeEntry::with_status((Klass*)nullptr, k));
   } else {
     set_type(translate_klass(k));
   }
 }
 
@@ -371,10 +371,16 @@
     return new ciCallTypeData(data_layout);
   case DataLayout::virtual_call_type_data_tag:
     return new ciVirtualCallTypeData(data_layout);
   case DataLayout::parameters_type_data_tag:
     return new ciParametersTypeData(data_layout);
+  case DataLayout::array_store_data_tag:
+    return new ciArrayStoreData(data_layout);
+  case DataLayout::array_load_data_tag:
+    return new ciArrayLoadData(data_layout);
+  case DataLayout::acmp_data_tag:
+    return new ciACmpData(data_layout);
   };
 }
 
 // Iteration over data.
 ciProfileData* ciMethodData::next_data(ciProfileData* current) {
@@ -788,10 +794,28 @@
         ciReceiverTypeData* vdata = (ciReceiverTypeData*)pdata;
         dump_replay_data_receiver_type_helper<ciReceiverTypeData>(out, round, count, vdata);
       } else if (pdata->is_CallTypeData()) {
           ciCallTypeData* call_type_data = (ciCallTypeData*)pdata;
           dump_replay_data_call_type_helper<ciCallTypeData>(out, round, count, call_type_data);
+      } else if (pdata->is_ArrayStoreData()) {
+        ciArrayStoreData* array_store_data = (ciArrayStoreData*)pdata;
+        dump_replay_data_type_helper(out, round, count, array_store_data, ciArrayStoreData::array_offset(),
+                                     array_store_data->array()->valid_type());
+        dump_replay_data_receiver_type_helper<ciArrayStoreData>(out, round, count, array_store_data);
+      } else if (pdata->is_ArrayLoadData()) {
+        ciArrayLoadData* array_load_data = (ciArrayLoadData*)pdata;
+        dump_replay_data_type_helper(out, round, count, array_load_data, ciArrayLoadData::array_offset(),
+                                     array_load_data->array()->valid_type());
+        dump_replay_data_type_helper(out, round, count, array_load_data, ciArrayLoadData::element_offset(),
+                                     array_load_data->element()->valid_type());
+      } else if (pdata->is_ACmpData()) {
+        ciACmpData* acmp_data = (ciACmpData*)pdata;
+        dump_replay_data_type_helper(out, round, count, acmp_data, ciACmpData::left_offset(),
+                                     acmp_data->left()->valid_type());
+        dump_replay_data_type_helper(out, round, count, acmp_data, ciACmpData::right_offset(),
+                                     acmp_data->right()->valid_type());
+
       }
     }
     if (parameters != nullptr) {
       for (int i = 0; i < parameters->number_of_parameters(); i++) {
         dump_replay_data_type_helper(out, round, count, parameters, ParametersTypeData::type_offset(i), parameters->valid_parameter_type(i));
@@ -870,11 +894,11 @@
     print_ciklass(st, type(i));
     st->cr();
   }
 }
 
-void ciReturnTypeEntry::print_data_on(outputStream* st) const {
+void ciSingleTypeEntry::print_data_on(outputStream* st) const {
   _pd->tab(st);
   st->print("ret ");
   print_ciklass(st, type());
   st->cr();
 }
@@ -943,6 +967,37 @@
   st->print_cr("ciSpeculativeTrapData");
   tab(st);
   method()->print_short_name(st);
   st->cr();
 }
+
+void ciArrayStoreData::print_data_on(outputStream* st, const char* extra) const {
+  print_shared(st, "ciArrayLoadStoreData", extra);
+  tab(st, true);
+  st->print("array");
+  array()->print_data_on(st);
+  tab(st, true);
+  st->print("element");
+  print_receiver_data_on(st);
+}
+
+void ciArrayLoadData::print_data_on(outputStream* st, const char* extra) const {
+  print_shared(st, "ciArrayLoadStoreData", extra);
+  tab(st, true);
+  st->print("array");
+  array()->print_data_on(st);
+  tab(st, true);
+  st->print("element");
+  element()->print_data_on(st);
+}
+
+void ciACmpData::print_data_on(outputStream* st, const char* extra) const {
+  BranchData::print_data_on(st, extra);
+  st->cr();
+  tab(st, true);
+  st->print("left");
+  left()->print_data_on(st);
+  tab(st, true);
+  st->print("right");
+  right()->print_data_on(st);
+}
 #endif
diff a/src/hotspot/share/ci/ciMethodData.hpp b/src/hotspot/share/ci/ciMethodData.hpp
--- a/src/hotspot/share/ci/ciMethodData.hpp
+++ b/src/hotspot/share/ci/ciMethodData.hpp
@@ -122,13 +122,13 @@
 #ifndef PRODUCT
   void print_data_on(outputStream* st) const;
 #endif
 };
 
-class ciReturnTypeEntry : public ReturnTypeEntry, ciTypeEntries {
+class ciSingleTypeEntry : public SingleTypeEntry, ciTypeEntries {
 public:
-  void translate_type_data_from(const ReturnTypeEntry* ret);
+  void translate_type_data_from(const SingleTypeEntry* ret);
 
   ciKlass* valid_type() const {
     return valid_ciklass(type());
   }
 
@@ -144,11 +144,11 @@
 class ciCallTypeData : public CallTypeData {
 public:
   ciCallTypeData(DataLayout* layout) : CallTypeData(layout) {}
 
   ciTypeStackSlotEntries* args() const { return (ciTypeStackSlotEntries*)CallTypeData::args(); }
-  ciReturnTypeEntry* ret() const { return (ciReturnTypeEntry*)CallTypeData::ret(); }
+  ciSingleTypeEntry* ret() const { return (ciSingleTypeEntry*)CallTypeData::ret(); }
 
   void translate_from(const ProfileData* data) {
     if (has_arguments()) {
       args()->translate_type_data_from(data->as_CallTypeData()->args());
     }
@@ -256,11 +256,11 @@
   ciKlass* receiver(uint row) const {
     return rtd_super()->receiver(row);
   }
 
   ciTypeStackSlotEntries* args() const { return (ciTypeStackSlotEntries*)VirtualCallTypeData::args(); }
-  ciReturnTypeEntry* ret() const { return (ciReturnTypeEntry*)VirtualCallTypeData::ret(); }
+  ciSingleTypeEntry* ret() const { return (ciSingleTypeEntry*)VirtualCallTypeData::ret(); }
 
   // Copy & translate from oop based VirtualCallData
   virtual void translate_from(const ProfileData* data) {
     rtd_super()->translate_receiver_data_from(data);
     if (has_arguments()) {
@@ -360,10 +360,67 @@
 #ifndef PRODUCT
   void print_data_on(outputStream* st, const char* extra = nullptr) const;
 #endif
 };
 
+class ciArrayStoreData : public ArrayStoreData {
+  // Fake multiple inheritance...  It's a ciReceiverTypeData also.
+  ciReceiverTypeData* rtd_super() const { return (ciReceiverTypeData*) this; }
+
+public:
+  ciArrayStoreData(DataLayout* layout) : ArrayStoreData(layout) {}
+
+  ciSingleTypeEntry* array() const { return (ciSingleTypeEntry*)ArrayStoreData::array(); }
+
+  virtual void translate_from(const ProfileData* data) {
+    array()->translate_type_data_from(data->as_ArrayStoreData()->array());
+    rtd_super()->translate_receiver_data_from(data);
+  }
+
+  ciKlass* receiver(uint row) {
+    return rtd_super()->receiver(row);
+  }
+#ifndef PRODUCT
+  void print_data_on(outputStream* st, const char* extra = nullptr) const;
+#endif
+};
+
+class ciArrayLoadData : public ArrayLoadData {
+public:
+  ciArrayLoadData(DataLayout* layout) : ArrayLoadData(layout) {}
+
+  ciSingleTypeEntry* array() const { return (ciSingleTypeEntry*)ArrayLoadData::array(); }
+  ciSingleTypeEntry* element() const { return (ciSingleTypeEntry*)ArrayLoadData::element(); }
+
+  virtual void translate_from(const ProfileData* data) {
+    array()->translate_type_data_from(data->as_ArrayLoadData()->array());
+    element()->translate_type_data_from(data->as_ArrayLoadData()->element());
+  }
+
+#ifndef PRODUCT
+  void print_data_on(outputStream* st, const char* extra = nullptr) const;
+#endif
+};
+
+
+class ciACmpData : public ACmpData {
+public:
+  ciACmpData(DataLayout* layout) : ACmpData(layout) {}
+
+  ciSingleTypeEntry* left() const { return (ciSingleTypeEntry*)ACmpData::left(); }
+  ciSingleTypeEntry* right() const { return (ciSingleTypeEntry*)ACmpData::right(); }
+
+  virtual void translate_from(const ProfileData* data) {
+    left()->translate_type_data_from(data->as_ACmpData()->left());
+    right()->translate_type_data_from(data->as_ACmpData()->right());
+  }
+
+#ifndef PRODUCT
+  void print_data_on(outputStream* st, const char* extra = nullptr) const;
+#endif
+};
+
 // ciMethodData
 //
 // This class represents a MethodData* in the HotSpot virtual
 // machine.
 
diff a/src/hotspot/share/ci/ciObjArrayKlass.cpp b/src/hotspot/share/ci/ciObjArrayKlass.cpp
--- a/src/hotspot/share/ci/ciObjArrayKlass.cpp
+++ b/src/hotspot/share/ci/ciObjArrayKlass.cpp
@@ -25,10 +25,11 @@
 #include "precompiled.hpp"
 #include "ci/ciInstanceKlass.hpp"
 #include "ci/ciObjArrayKlass.hpp"
 #include "ci/ciSymbol.hpp"
 #include "ci/ciUtilities.inline.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "runtime/signature.hpp"
 
 // ciObjArrayKlass
 //
@@ -62,18 +63,19 @@
 ciObjArrayKlass::ciObjArrayKlass(ciSymbol* array_name,
                                  ciKlass* base_element_klass,
                                  int dimension)
   : ciArrayKlass(array_name,
                  dimension, T_OBJECT) {
-    _base_element_klass = base_element_klass;
-    assert(_base_element_klass->is_instance_klass() ||
-           _base_element_klass->is_type_array_klass(), "bad base klass");
-    if (dimension == 1) {
-      _element_klass = base_element_klass;
-    } else {
-      _element_klass = nullptr;
-    }
+  _base_element_klass = base_element_klass;
+  assert(_base_element_klass->is_instance_klass() ||
+         _base_element_klass->is_type_array_klass() ||
+         _base_element_klass->is_flat_array_klass(), "bad base klass");
+  if (dimension == 1) {
+    _element_klass = base_element_klass;
+  } else {
+    _element_klass = nullptr;
+  }
 }
 
 // ------------------------------------------------------------------
 // ciObjArrayKlass::element_klass
 //
@@ -114,11 +116,10 @@
   int pos = 0;
   for ( ; pos < dimension; pos++) {
     name[pos] = JVM_SIGNATURE_ARRAY;
   }
   Symbol* base_name_sym = element_name->get_symbol();
-
   if (Signature::is_array(base_name_sym) ||
       Signature::has_envelope(base_name_sym)) {
     strncpy(&name[pos], (char*)element_name->base(), element_len);
     name[pos + element_len] = '\0';
   } else {
@@ -133,11 +134,10 @@
 // ------------------------------------------------------------------
 // ciObjArrayKlass::make_impl
 //
 // Implementation of make.
 ciObjArrayKlass* ciObjArrayKlass::make_impl(ciKlass* element_klass) {
-
   if (element_klass->is_loaded()) {
     EXCEPTION_CONTEXT;
     // The element klass is loaded
     Klass* array = element_klass->get_Klass()->array_klass(THREAD);
     if (HAS_PENDING_EXCEPTION) {
@@ -146,12 +146,11 @@
       return ciEnv::unloaded_ciobjarrayklass();
     }
     return CURRENT_THREAD_ENV->get_obj_array_klass(array);
   }
 
-  // The array klass was unable to be made or the element klass was
-  // not loaded.
+  // The array klass was unable to be made or the element klass was not loaded.
   ciSymbol* array_name = construct_array_name(element_klass->name(), 1);
   if (array_name == ciEnv::unloaded_cisymbol()) {
     return ciEnv::unloaded_ciobjarrayklass();
   }
   return
@@ -174,10 +173,15 @@
   }
   return klass->as_obj_array_klass();
 }
 
 ciKlass* ciObjArrayKlass::exact_klass() {
+  // TODO 8325106 Fix comment
+  // Even if MyValue is exact, [LMyValue is not exact due to [QMyValue <: [LMyValue.
+  if (!is_loaded() || element_klass()->is_inlinetype()) {
+    return nullptr;
+  }
   ciType* base = base_element_type();
   if (base->is_instance_klass()) {
     ciInstanceKlass* ik = base->as_instance_klass();
     if (ik->exact_klass() != nullptr) {
       return this;
diff a/src/hotspot/share/ci/ciObjArrayKlass.hpp b/src/hotspot/share/ci/ciObjArrayKlass.hpp
--- a/src/hotspot/share/ci/ciObjArrayKlass.hpp
+++ b/src/hotspot/share/ci/ciObjArrayKlass.hpp
@@ -74,8 +74,12 @@
 
   static ciObjArrayKlass* make(ciKlass* element_klass);
   static ciObjArrayKlass* make(ciKlass* element_klass, int dims);
 
   virtual ciKlass* exact_klass();
+
+  virtual bool can_be_inline_array_klass() {
+    return element_klass()->can_be_inline_klass();
+  }
 };
 
 #endif // SHARE_CI_CIOBJARRAYKLASS_HPP
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
@@ -22,10 +22,12 @@
  *
  */
 
 #include "precompiled.hpp"
 #include "ci/ciCallSite.hpp"
+#include "ci/ciFlatArray.hpp"
+#include "ci/ciFlatArrayKlass.hpp"
 #include "ci/ciInstance.hpp"
 #include "ci/ciInstanceKlass.hpp"
 #include "ci/ciMemberName.hpp"
 #include "ci/ciMethod.hpp"
 #include "ci/ciMethodData.hpp"
@@ -40,10 +42,11 @@
 #include "ci/ciSymbol.hpp"
 #include "ci/ciSymbols.hpp"
 #include "ci/ciTypeArray.hpp"
 #include "ci/ciTypeArrayKlass.hpp"
 #include "ci/ciUtilities.inline.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "classfile/javaClasses.inline.hpp"
 #include "classfile/vmClasses.hpp"
 #include "compiler/compiler_globals.hpp"
 #include "gc/shared/collectedHeap.inline.hpp"
 #include "memory/allocation.inline.hpp"
@@ -363,10 +366,13 @@
     objArrayHandle h_oa(THREAD, (objArrayOop)o);
     return new (arena()) ciObjArray(h_oa);
   } else if (o->is_typeArray()) {
     typeArrayHandle h_ta(THREAD, (typeArrayOop)o);
     return new (arena()) ciTypeArray(h_ta);
+  } else if (o->is_flatArray()) {
+    flatArrayHandle h_ta(THREAD, (flatArrayOop)o);
+    return new (arena()) ciFlatArray(h_ta);
   }
 
   // The oop is of some type not supported by the compiler interface.
   ShouldNotReachHere();
   return nullptr;
@@ -382,13 +388,17 @@
 ciMetadata* ciObjectFactory::create_new_metadata(Metadata* o) {
   EXCEPTION_CONTEXT;
 
   if (o->is_klass()) {
     Klass* k = (Klass*)o;
-    if (k->is_instance_klass()) {
+    if (k->is_inline_klass()) {
+      return new (arena()) ciInlineKlass(k);
+    } else if (k->is_instance_klass()) {
       assert(!ReplayCompiles || ciReplay::no_replay_state() || !ciReplay::is_klass_unresolved((InstanceKlass*)k), "must be whitelisted for replay compilation");
       return new (arena()) ciInstanceKlass(k);
+    } else if (k->is_flatArray_klass()) {
+      return new (arena()) ciFlatArrayKlass(k);
     } else if (k->is_objArray_klass()) {
       return new (arena()) ciObjArrayKlass(k);
     } else if (k->is_typeArray_klass()) {
       return new (arena()) ciTypeArrayKlass(k);
     }
@@ -625,10 +635,16 @@
   init_ident_of(new_ret_addr);
   _return_addresses.append(new_ret_addr);
   return new_ret_addr;
 }
 
+ciWrapper* ciObjectFactory::make_null_free_wrapper(ciType* type) {
+  ciWrapper* wrapper = new (arena()) ciWrapper(type);
+  init_ident_of(wrapper);
+  return wrapper;
+}
+
 // ------------------------------------------------------------------
 // ciObjectFactory::init_ident_of
 void ciObjectFactory::init_ident_of(ciBaseObject* obj) {
   obj->set_ident(_next_ident++);
 }
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
@@ -137,10 +137,12 @@
   // with none.
   ciMethodData* get_empty_methodData();
 
   ciReturnAddress* get_return_address(int bci);
 
+  ciWrapper* make_null_free_wrapper(ciType* type);
+
   GrowableArray<ciMetadata*>* get_ci_metadata() { return &_ci_metadata; }
   // RedefineClasses support
   void metadata_do(MetadataClosure* f);
 
   void print_contents();
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
@@ -40,10 +40,11 @@
 #include "memory/oopFactory.hpp"
 #include "memory/resourceArea.hpp"
 #include "oops/constantPool.inline.hpp"
 #include "oops/cpCache.inline.hpp"
 #include "oops/fieldStreams.inline.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/method.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/resolvedIndyEntry.hpp"
 #include "prims/jvmtiExport.hpp"
@@ -975,10 +976,11 @@
             tty->print_cr("Resolving klass %s at %d", cp->klass_name_at(i)->as_utf8(), i);
             Klass* k = cp->klass_at(i, CHECK);
           }
           break;
         }
+
         case JVM_CONSTANT_Long:
         case JVM_CONSTANT_Double:
           parsed_two_word = i + 1;
 
         case JVM_CONSTANT_ClassIndex:
@@ -1021,125 +1023,232 @@
       }
 
     }
   }
 
-  // staticfield <klass> <name> <signature> <value>
-  //
-  // Initialize a class and fill in the value for a static field.
-  // This is useful when the compile was dependent on the value of
-  // static fields but it's impossible to properly rerun the static
-  // initializer.
-  void process_staticfield(TRAPS) {
-    InstanceKlass* k = (InstanceKlass *)parse_klass(CHECK);
-
-    if (k == nullptr || ReplaySuppressInitializers == 0 ||
-        (ReplaySuppressInitializers == 2 && k->class_loader() == nullptr)) {
-      skip_remaining();
-      return;
-    }
-
-    assert(k->is_initialized(), "must be");
-
-    const char* field_name = parse_escaped_string();
-    const char* field_signature = parse_string();
-    fieldDescriptor fd;
-    Symbol* name = SymbolTable::new_symbol(field_name);
-    Symbol* sig = SymbolTable::new_symbol(field_signature);
-    if (!k->find_local_field(name, sig, &fd) ||
-        !fd.is_static() ||
-        fd.has_initial_value()) {
-      report_error(field_name);
-      return;
+  class InlineTypeFieldInitializer : public FieldClosure {
+    oop _vt;
+    CompileReplay* _replay;
+  public:
+    InlineTypeFieldInitializer(oop vt, CompileReplay* replay)
+  : _vt(vt), _replay(replay) {}
+
+    void do_field(fieldDescriptor* fd) {
+      BasicType bt = fd->field_type();
+      const char* string_value = fd->is_null_free_inline_type() ? nullptr : _replay->parse_escaped_string();
+      switch (bt) {
+      case T_BYTE: {
+        int value = atoi(string_value);
+        _vt->byte_field_put(fd->offset(), value);
+        break;
+      }
+      case T_BOOLEAN: {
+        int value = atoi(string_value);
+        _vt->bool_field_put(fd->offset(), value);
+        break;
+      }
+      case T_SHORT: {
+        int value = atoi(string_value);
+        _vt->short_field_put(fd->offset(), value);
+        break;
+      }
+      case T_CHAR: {
+        int value = atoi(string_value);
+        _vt->char_field_put(fd->offset(), value);
+        break;
+      }
+      case T_INT: {
+        int value = atoi(string_value);
+        _vt->int_field_put(fd->offset(), value);
+        break;
+      }
+      case T_LONG: {
+        jlong value;
+        if (sscanf(string_value, JLONG_FORMAT, &value) != 1) {
+          fprintf(stderr, "Error parsing long: %s\n", string_value);
+          break;
+        }
+        _vt->long_field_put(fd->offset(), value);
+        break;
+      }
+      case T_FLOAT: {
+        float value = atof(string_value);
+        _vt->float_field_put(fd->offset(), value);
+        break;
+      }
+      case T_DOUBLE: {
+        double value = atof(string_value);
+        _vt->double_field_put(fd->offset(), value);
+        break;
+      }
+      case T_ARRAY:
+      case T_OBJECT:
+        if (!fd->is_null_free_inline_type()) {
+          JavaThread* THREAD = JavaThread::current();
+          bool res = _replay->process_staticfield_reference(string_value, _vt, fd, THREAD);
+          assert(res, "should succeed for arrays & objects");
+          break;
+        } else {
+          InlineKlass* vk = InlineKlass::cast(fd->field_holder()->get_inline_type_field_klass(fd->index()));
+          if (fd->is_flat()) {
+            int field_offset = fd->offset() - vk->first_field_offset();
+            oop obj = cast_to_oop(cast_from_oop<address>(_vt) + field_offset);
+            InlineTypeFieldInitializer init_fields(obj, _replay);
+            vk->do_nonstatic_fields(&init_fields);
+          } else {
+            oop value = vk->allocate_instance(JavaThread::current());
+            _vt->obj_field_put(fd->offset(), value);
+          }
+          break;
+        }
+      default: {
+        fatal("Unhandled type: %s", type2name(bt));
+      }
+      }
     }
+  };
 
-    oop java_mirror = k->java_mirror();
+  bool process_staticfield_reference(const char* field_signature, oop java_mirror, fieldDescriptor* fd, TRAPS) {
     if (field_signature[0] == JVM_SIGNATURE_ARRAY) {
       int length = parse_int("array length");
       oop value = nullptr;
 
       if (field_signature[1] == JVM_SIGNATURE_ARRAY) {
         // multi dimensional array
-        ArrayKlass* kelem = (ArrayKlass *)parse_klass(CHECK);
-        if (kelem == nullptr) {
-          return;
-        }
+        Klass* k = resolve_klass(field_signature, CHECK_(true));
+        ArrayKlass* kelem = (ArrayKlass *)k;
         int rank = 0;
         while (field_signature[rank] == JVM_SIGNATURE_ARRAY) {
           rank++;
         }
         jint* dims = NEW_RESOURCE_ARRAY(jint, rank);
         dims[0] = length;
         for (int i = 1; i < rank; i++) {
           dims[i] = 1; // These aren't relevant to the compiler
         }
-        value = kelem->multi_allocate(rank, dims, CHECK);
+        value = kelem->multi_allocate(rank, dims, CHECK_(true));
       } else {
         if (strcmp(field_signature, "[B") == 0) {
-          value = oopFactory::new_byteArray(length, CHECK);
+          value = oopFactory::new_byteArray(length, CHECK_(true));
         } else if (strcmp(field_signature, "[Z") == 0) {
-          value = oopFactory::new_boolArray(length, CHECK);
+          value = oopFactory::new_boolArray(length, CHECK_(true));
         } else if (strcmp(field_signature, "[C") == 0) {
-          value = oopFactory::new_charArray(length, CHECK);
+          value = oopFactory::new_charArray(length, CHECK_(true));
         } else if (strcmp(field_signature, "[S") == 0) {
-          value = oopFactory::new_shortArray(length, CHECK);
+          value = oopFactory::new_shortArray(length, CHECK_(true));
         } else if (strcmp(field_signature, "[F") == 0) {
-          value = oopFactory::new_floatArray(length, CHECK);
+          value = oopFactory::new_floatArray(length, CHECK_(true));
         } else if (strcmp(field_signature, "[D") == 0) {
-          value = oopFactory::new_doubleArray(length, CHECK);
+          value = oopFactory::new_doubleArray(length, CHECK_(true));
         } else if (strcmp(field_signature, "[I") == 0) {
-          value = oopFactory::new_intArray(length, CHECK);
+          value = oopFactory::new_intArray(length, CHECK_(true));
         } else if (strcmp(field_signature, "[J") == 0) {
-          value = oopFactory::new_longArray(length, CHECK);
+          value = oopFactory::new_longArray(length, CHECK_(true));
         } else if (field_signature[0] == JVM_SIGNATURE_ARRAY &&
                    field_signature[1] == JVM_SIGNATURE_CLASS) {
-          parse_klass(CHECK); // eat up the array class name
-          Klass* kelem = resolve_klass(field_signature + 1, CHECK);
-          value = oopFactory::new_objArray(kelem, length, CHECK);
+          Klass* kelem = resolve_klass(field_signature + 1, CHECK_(true));
+          parse_klass(CHECK_(true)); // eat up the array class name
+          value = oopFactory::new_objArray(kelem, length, CHECK_(true));
+        } else if (field_signature[0] == JVM_SIGNATURE_ARRAY) {
+          Klass* kelem = resolve_klass(field_signature + 1, CHECK_(true));
+          parse_klass(CHECK_(true)); // eat up the array class name
+          value = oopFactory::new_valueArray(kelem, length, CHECK_(true));
         } else {
           report_error("unhandled array staticfield");
         }
       }
+      java_mirror->obj_field_put(fd->offset(), value);
+      return true;
+    } else if (strcmp(field_signature, "Ljava/lang/String;") == 0) {
+      const char* string_value = parse_escaped_string();
+      Handle value = java_lang_String::create_from_str(string_value, CHECK_(true));
+      java_mirror->obj_field_put(fd->offset(), value());
+      return true;
+    } else if (field_signature[0] == 'L') {
+      const char* instance = parse_escaped_string();
+      Klass* k = resolve_klass(instance, CHECK_(true));
+      oop value = InstanceKlass::cast(k)->allocate_instance(CHECK_(true));
+      java_mirror->obj_field_put(fd->offset(), value);
+      return true;
+    }
+    return false;
+  }
+
+  // Initialize a class and fill in the value for a static field.
+  // This is useful when the compile was dependent on the value of
+  // static fields but it's impossible to properly rerun the static
+  // initializer.
+  void process_staticfield(TRAPS) {
+    InstanceKlass* k = (InstanceKlass *)parse_klass(CHECK);
+
+    if (k == nullptr || ReplaySuppressInitializers == 0 ||
+        (ReplaySuppressInitializers == 2 && k->class_loader() == nullptr)) {
+        skip_remaining();
+      return;
+    }
+
+    assert(k->is_initialized(), "must be");
+
+    const char* field_name = parse_escaped_string();
+    const char* field_signature = parse_string();
+    fieldDescriptor fd;
+    Symbol* name = SymbolTable::new_symbol(field_name);
+    Symbol* sig = SymbolTable::new_symbol(field_signature);
+    if (!k->find_local_field(name, sig, &fd) ||
+        !fd.is_static() ||
+        fd.has_initial_value()) {
+      report_error(field_name);
+      return;
+    }
+
+    oop java_mirror = k->java_mirror();
+    if (strcmp(field_signature, "I") == 0) {
+      const char* string_value = parse_escaped_string();
+      int value = atoi(string_value);
+      java_mirror->int_field_put(fd.offset(), value);
+    } else if (strcmp(field_signature, "B") == 0) {
+      const char* string_value = parse_escaped_string();
+      int value = atoi(string_value);
+      java_mirror->byte_field_put(fd.offset(), value);
+    } else if (strcmp(field_signature, "C") == 0) {
+      const char* string_value = parse_escaped_string();
+      int value = atoi(string_value);
+      java_mirror->char_field_put(fd.offset(), value);
+    } else if (strcmp(field_signature, "S") == 0) {
+      const char* string_value = parse_escaped_string();
+      int value = atoi(string_value);
+      java_mirror->short_field_put(fd.offset(), value);
+    } else if (strcmp(field_signature, "Z") == 0) {
+      const char* string_value = parse_escaped_string();
+      int value = atoi(string_value);
+      java_mirror->bool_field_put(fd.offset(), value);
+    } else if (strcmp(field_signature, "J") == 0) {
+      const char* string_value = parse_escaped_string();
+      jlong value;
+      if (sscanf(string_value, JLONG_FORMAT, &value) != 1) {
+        fprintf(stderr, "Error parsing long: %s\n", string_value);
+        return;
+      }
+      java_mirror->long_field_put(fd.offset(), value);
+    } else if (strcmp(field_signature, "F") == 0) {
+      const char* string_value = parse_escaped_string();
+      float value = atof(string_value);
+      java_mirror->float_field_put(fd.offset(), value);
+    } else if (strcmp(field_signature, "D") == 0) {
+      const char* string_value = parse_escaped_string();
+      double value = atof(string_value);
+      java_mirror->double_field_put(fd.offset(), value);
+    } else if (fd.is_null_free_inline_type()) {
+      Klass* kelem = resolve_klass(field_signature, CHECK);
+      InlineKlass* vk = InlineKlass::cast(kelem);
+      oop value = vk->allocate_instance(CHECK);
+      InlineTypeFieldInitializer init_fields(value, this);
+      vk->do_nonstatic_fields(&init_fields);
       java_mirror->obj_field_put(fd.offset(), value);
     } else {
-      const char* string_value = parse_escaped_string();
-      if (strcmp(field_signature, "I") == 0) {
-        int value = atoi(string_value);
-        java_mirror->int_field_put(fd.offset(), value);
-      } else if (strcmp(field_signature, "B") == 0) {
-        int value = atoi(string_value);
-        java_mirror->byte_field_put(fd.offset(), value);
-      } else if (strcmp(field_signature, "C") == 0) {
-        int value = atoi(string_value);
-        java_mirror->char_field_put(fd.offset(), value);
-      } else if (strcmp(field_signature, "S") == 0) {
-        int value = atoi(string_value);
-        java_mirror->short_field_put(fd.offset(), value);
-      } else if (strcmp(field_signature, "Z") == 0) {
-        int value = atoi(string_value);
-        java_mirror->bool_field_put(fd.offset(), value);
-      } else if (strcmp(field_signature, "J") == 0) {
-        jlong value;
-        if (sscanf(string_value, JLONG_FORMAT, &value) != 1) {
-          fprintf(stderr, "Error parsing long: %s\n", string_value);
-          return;
-        }
-        java_mirror->long_field_put(fd.offset(), value);
-      } else if (strcmp(field_signature, "F") == 0) {
-        float value = atof(string_value);
-        java_mirror->float_field_put(fd.offset(), value);
-      } else if (strcmp(field_signature, "D") == 0) {
-        double value = atof(string_value);
-        java_mirror->double_field_put(fd.offset(), value);
-      } else if (strcmp(field_signature, "Ljava/lang/String;") == 0) {
-        Handle value = java_lang_String::create_from_str(string_value, CHECK);
-        java_mirror->obj_field_put(fd.offset(), value());
-      } else if (field_signature[0] == JVM_SIGNATURE_CLASS) {
-        Klass* k = resolve_klass(string_value, CHECK);
-        oop value = InstanceKlass::cast(k)->allocate_instance(CHECK);
-        java_mirror->obj_field_put(fd.offset(), value);
-      } else {
+      bool res = process_staticfield_reference(field_signature, java_mirror, &fd, CHECK);
+      if (!res)  {
         report_error("unhandled staticfield");
       }
     }
   }
 
diff a/src/hotspot/share/ci/ciSignature.hpp b/src/hotspot/share/ci/ciSignature.hpp
--- a/src/hotspot/share/ci/ciSignature.hpp
+++ b/src/hotspot/share/ci/ciSignature.hpp
@@ -25,10 +25,11 @@
 #ifndef SHARE_CI_CISIGNATURE_HPP
 #define SHARE_CI_CISIGNATURE_HPP
 
 #include "ci/ciClassList.hpp"
 #include "ci/ciSymbol.hpp"
+#include "ci/ciType.hpp"
 #include "interpreter/bytecodes.hpp"
 #include "utilities/globalDefinitions.hpp"
 #include "utilities/growableArray.hpp"
 
 // ciSignature
@@ -54,11 +55,11 @@
 public:
   ciSymbol* as_symbol() const                    { return _symbol; }
   ciKlass*  accessing_klass() const              { return _accessing_klass; }
 
   ciType*   return_type() const                  { return _return_type; }
-  ciType*   type_at(int index) const             { return _types.at(index); }
+  ciType*   type_at(int index) const             { return _types.at(index)->unwrap(); }
 
   int       size() const                         { return _size; }
   int       count() const                        { return _types.length(); }
 
   int       arg_size_for_bc(Bytecodes::Code bc)  { return size() + (Bytecodes::has_receiver(bc) ? 1 : 0); }
diff a/src/hotspot/share/ci/ciSymbol.cpp b/src/hotspot/share/ci/ciSymbol.cpp
--- a/src/hotspot/share/ci/ciSymbol.cpp
+++ b/src/hotspot/share/ci/ciSymbol.cpp
@@ -78,10 +78,24 @@
 //
 // Tests if the symbol starts with the given prefix.
 bool ciSymbol::starts_with(const char* prefix, int len) const {
   GUARDED_VM_ENTRY(return get_symbol()->starts_with(prefix, len);)
 }
+bool ciSymbol::starts_with(char prefix_char) const {
+  GUARDED_VM_ENTRY(return get_symbol()->starts_with(prefix_char);)
+}
+
+// ------------------------------------------------------------------
+// ciSymbol::ends_with
+//
+// Tests if the symbol ends with the given suffix.
+bool ciSymbol::ends_with(const char* suffix, int len) const {
+  GUARDED_VM_ENTRY(return get_symbol()->ends_with(suffix, len);)
+}
+bool ciSymbol::ends_with(char suffix_char) const {
+  GUARDED_VM_ENTRY(return get_symbol()->ends_with(suffix_char);)
+}
 
 bool ciSymbol::is_signature_polymorphic_name()  const {
   GUARDED_VM_ENTRY(return MethodHandles::is_signature_polymorphic_name(get_symbol());)
 }
 
diff a/src/hotspot/share/ci/ciSymbol.hpp b/src/hotspot/share/ci/ciSymbol.hpp
--- a/src/hotspot/share/ci/ciSymbol.hpp
+++ b/src/hotspot/share/ci/ciSymbol.hpp
@@ -43,10 +43,11 @@
   friend class ciInstanceKlass;
   friend class ciSignature;
   friend class ciMethod;
   friend class ciField;
   friend class ciObjArrayKlass;
+  friend class ciFlatArrayKlass;
 
 private:
   const vmSymbolID _sid;
 
   ciSymbol(Symbol* s, vmSymbolID sid);
@@ -77,10 +78,15 @@
   // Return the i-th utf byte as a char, where i < utf8_length
   char        char_at(int i);
 
   // Tests if the symbol starts with the given prefix.
   bool starts_with(const char* prefix, int len) const;
+  bool starts_with(char prefix_char) const;
+
+  // Tests if the symbol ends with the given suffix.
+  bool ends_with(const char* suffix, int len) const;
+  bool ends_with(char suffix_char) const;
 
   // Determines where the symbol contains the given substring.
   int index_of_at(int i, const char* str, int len) const;
 
   void print_symbol_on(outputStream* st);
diff a/src/hotspot/share/ci/ciType.cpp b/src/hotspot/share/ci/ciType.cpp
--- a/src/hotspot/share/ci/ciType.cpp
+++ b/src/hotspot/share/ci/ciType.cpp
@@ -32,11 +32,11 @@
 
 ciType* ciType::_basic_types[T_CONFLICT+1];
 
 // ciType
 //
-// This class represents a Java reference or primitive type.
+// This class represents a Java reference, inline type or primitive type.
 
 // ------------------------------------------------------------------
 // ciType::ciType
 //
 ciType::ciType(BasicType basic_type) : ciMetadata() {
diff a/src/hotspot/share/ci/ciType.hpp b/src/hotspot/share/ci/ciType.hpp
--- a/src/hotspot/share/ci/ciType.hpp
+++ b/src/hotspot/share/ci/ciType.hpp
@@ -27,16 +27,17 @@
 
 #include "ci/ciMetadata.hpp"
 
 // ciType
 //
-// This class represents a Java reference or primitive type.
+// This class represents a Java reference, inline type or primitive type.
 
 class ciType : public ciMetadata {
   CI_PACKAGE_ACCESS
   friend class ciKlass;
   friend class ciReturnAddress;
+  friend class ciWrapper;
 
 private:
   BasicType _basic_type;
 
   ciType(BasicType t);     // for primitive and unloaded types
@@ -69,10 +70,13 @@
 
   // What kind of ciObject is this?
   bool is_type() const                      { return true; }
   bool is_classless() const                 { return is_primitive_type(); }
 
+  virtual ciType* unwrap()                  { return this; }
+  virtual bool is_null_free() const         { return false; }
+
   const char* name();
   virtual void print_name_on(outputStream* st);
   void print_name() {
     print_name_on(tty);
   }
@@ -104,6 +108,34 @@
   int  bci() { return _bci; }
 
   static ciReturnAddress* make(int bci);
 };
 
+// ciWrapper
+//
+// This class wraps another type to carry additional information.
+// Currently it is only used to mark inline klasses as null-free.
+class ciWrapper : public ciType {
+  CI_PACKAGE_ACCESS
+
+private:
+  ciType* _type;
+
+  ciWrapper(ciType* type) : ciType(type->basic_type()) {
+    assert(type->is_inlinetype()
+          // An unloaded inline type is an instance_klass (see ciEnv::get_klass_by_name_impl())
+          || (type->is_instance_klass() && !type->is_loaded()),
+          "should only be used for inline types");
+    _type = type;
+  }
+
+  const char* type_string() { return "ciWrapper"; }
+
+  void print_impl(outputStream* st) { _type->print_impl(st); }
+
+public:
+  bool is_wrapper()   const { return true; }
+  ciType* unwrap()          { return _type; }
+  bool is_null_free() const { return true; }
+};
+
 #endif // SHARE_CI_CITYPE_HPP
diff a/src/hotspot/share/ci/ciTypeArrayKlass.hpp b/src/hotspot/share/ci/ciTypeArrayKlass.hpp
--- a/src/hotspot/share/ci/ciTypeArrayKlass.hpp
+++ b/src/hotspot/share/ci/ciTypeArrayKlass.hpp
@@ -59,8 +59,12 @@
   static ciTypeArrayKlass* make(BasicType type);
 
   virtual ciKlass* exact_klass() {
     return this;
   }
+
+  virtual bool can_be_inline_array_klass() {
+    return false;
+  }
 };
 
 #endif // SHARE_CI_CITYPEARRAYKLASS_HPP
diff a/src/hotspot/share/ci/ciTypeFlow.cpp b/src/hotspot/share/ci/ciTypeFlow.cpp
--- a/src/hotspot/share/ci/ciTypeFlow.cpp
+++ b/src/hotspot/share/ci/ciTypeFlow.cpp
@@ -23,10 +23,11 @@
  */
 
 #include "precompiled.hpp"
 #include "ci/ciConstant.hpp"
 #include "ci/ciField.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "ci/ciMethod.hpp"
 #include "ci/ciMethodData.hpp"
 #include "ci/ciObjArrayKlass.hpp"
 #include "ci/ciStreams.hpp"
 #include "ci/ciTypeArrayKlass.hpp"
@@ -272,13 +273,23 @@
   assert(t1 != t2, "checked in caller");
   if (t1->equals(top_type())) {
     return t2;
   } else if (t2->equals(top_type())) {
     return t1;
-  } else if (t1->is_primitive_type() || t2->is_primitive_type()) {
+  }
+  // Unwrap after saving nullness information and handling top meets
+  bool null_free1 = t1->is_null_free();
+  bool null_free2 = t2->is_null_free();
+  if (t1->unwrap() == t2->unwrap() && null_free1 == null_free2) {
+    return t1;
+  }
+  t1 = t1->unwrap();
+  t2 = t2->unwrap();
+
+  if (t1->is_primitive_type() || t2->is_primitive_type()) {
     // Special case null_type.  null_type meet any reference type T
-    // is T.  null_type meet null_type is null_type.
+    // is T. null_type meet null_type is null_type.
     if (t1->equals(null_type())) {
       if (!t2->is_primitive_type() || t2->equals(null_type())) {
         return t2;
       }
     } else if (t2->equals(null_type())) {
@@ -288,54 +299,65 @@
     }
 
     // At least one of the two types is a non-top primitive type.
     // The other type is not equal to it.  Fall to bottom.
     return bottom_type();
-  } else {
-    // Both types are non-top non-primitive types.  That is,
-    // both types are either instanceKlasses or arrayKlasses.
-    ciKlass* object_klass = analyzer->env()->Object_klass();
-    ciKlass* k1 = t1->as_klass();
-    ciKlass* k2 = t2->as_klass();
-    if (k1->equals(object_klass) || k2->equals(object_klass)) {
-      return object_klass;
-    } else if (!k1->is_loaded() || !k2->is_loaded()) {
-      // Unloaded classes fall to java.lang.Object at a merge.
-      return object_klass;
-    } else if (k1->is_interface() != k2->is_interface()) {
-      // When an interface meets a non-interface, we get Object;
-      // This is what the verifier does.
-      return object_klass;
-    } else if (k1->is_array_klass() || k2->is_array_klass()) {
-      // When an array meets a non-array, we get Object.
-      // When objArray meets typeArray, we also get Object.
-      // And when typeArray meets different typeArray, we again get Object.
-      // But when objArray meets objArray, we look carefully at element types.
-      if (k1->is_obj_array_klass() && k2->is_obj_array_klass()) {
-        // Meet the element types, then construct the corresponding array type.
-        ciKlass* elem1 = k1->as_obj_array_klass()->element_klass();
-        ciKlass* elem2 = k2->as_obj_array_klass()->element_klass();
-        ciKlass* elem  = type_meet_internal(elem1, elem2, analyzer)->as_klass();
-        // Do an easy shortcut if one type is a super of the other.
-        if (elem == elem1) {
-          assert(k1 == ciObjArrayKlass::make(elem), "shortcut is OK");
-          return k1;
-        } else if (elem == elem2) {
-          assert(k2 == ciObjArrayKlass::make(elem), "shortcut is OK");
-          return k2;
-        } else {
-          return ciObjArrayKlass::make(elem);
-        }
+  }
+
+  // Both types are non-top non-primitive types.  That is,
+  // both types are either instanceKlasses or arrayKlasses.
+  ciKlass* object_klass = analyzer->env()->Object_klass();
+  ciKlass* k1 = t1->as_klass();
+  ciKlass* k2 = t2->as_klass();
+  if (k1->equals(object_klass) || k2->equals(object_klass)) {
+    return object_klass;
+  } else if (!k1->is_loaded() || !k2->is_loaded()) {
+    // Unloaded classes fall to java.lang.Object at a merge.
+    return object_klass;
+  } else if (k1->is_interface() != k2->is_interface()) {
+    // When an interface meets a non-interface, we get Object;
+    // This is what the verifier does.
+    return object_klass;
+  } else if (k1->is_array_klass() || k2->is_array_klass()) {
+    // When an array meets a non-array, we get Object.
+    // When (obj/flat)Array meets typeArray, we also get Object.
+    // And when typeArray meets different typeArray, we again get Object.
+    // But when (obj/flat)Array meets (obj/flat)Array, we look carefully at element types.
+    if ((k1->is_obj_array_klass() || k1->is_flat_array_klass()) &&
+        (k2->is_obj_array_klass() || k2->is_flat_array_klass())) {
+      bool null_free = k1->as_array_klass()->is_elem_null_free() &&
+                       k2->as_array_klass()->is_elem_null_free();
+      ciType* elem1 = k1->as_array_klass()->element_klass();
+      ciType* elem2 = k2->as_array_klass()->element_klass();
+      ciType* elem = elem1;
+      if (elem1 != elem2) {
+        elem = type_meet_internal(elem1, elem2, analyzer)->as_klass();
+      }
+      // Do an easy shortcut if one type is a super of the other.
+      if (elem == elem1 && !elem->is_inlinetype()) {
+        assert(k1 == ciArrayKlass::make(elem, null_free), "shortcut is OK");
+        return k1;
+      } else if (elem == elem2 && !elem->is_inlinetype()) {
+        assert(k2 == ciArrayKlass::make(elem, null_free), "shortcut is OK");
+        return k2;
       } else {
-        return object_klass;
+        // TODO 8325106 Remove
+        assert(!null_free, "should be dead");
+        return ciArrayKlass::make(elem);
       }
     } else {
-      // Must be two plain old instance klasses.
-      assert(k1->is_instance_klass(), "previous cases handle non-instances");
-      assert(k2->is_instance_klass(), "previous cases handle non-instances");
-      return k1->least_common_ancestor(k2);
+      return object_klass;
+    }
+  } else {
+    // Must be two plain old instance klasses.
+    assert(k1->is_instance_klass(), "previous cases handle non-instances");
+    assert(k2->is_instance_klass(), "previous cases handle non-instances");
+    ciType* result = k1->least_common_ancestor(k2);
+    if (null_free1 && null_free2 && result->is_inlinetype()) {
+      result = analyzer->mark_as_null_free(result);
     }
+    return result;
   }
 }
 
 
 // ------------------------------------------------------------------
@@ -393,11 +415,16 @@
     // even if it were possible for an OSR entry point to be at bci zero.
   }
   // "Push" the method signature into the first few locals.
   state->set_stack_size(-max_locals());
   if (!method()->is_static()) {
-    state->push(method()->holder());
+    ciType* holder = method()->holder();
+    if (holder->is_inlinetype()) {
+      // The receiver is null-free
+      holder = mark_as_null_free(holder);
+    }
+    state->push(holder);
     assert(state->tos() == state->local(0), "");
   }
   for (ciSignatureStream str(method()->signature());
        !str.at_return_type();
        str.next()) {
@@ -545,16 +572,16 @@
     }
   }
 }
 
 // ------------------------------------------------------------------
-// ciTypeFlow::StateVector::do_aaload
-void ciTypeFlow::StateVector::do_aaload(ciBytecodeStream* str) {
+// ciTypeFlow::StateVector::do_aload
+void ciTypeFlow::StateVector::do_aload(ciBytecodeStream* str) {
   pop_int();
-  ciObjArrayKlass* array_klass = pop_objArray();
+  ciArrayKlass* array_klass = pop_objOrFlatArray();
   if (array_klass == nullptr) {
-    // Did aaload on a null reference; push a null and ignore the exception.
+    // Did aload on a null reference; push a null and ignore the exception.
     // This instruction will never continue normally.  All we have to do
     // is report a value that will meet correctly with any downstream
     // reference types on paths that will truly be executed.  This null type
     // meets with any reference type to yield that same reference type.
     // (The compiler will generate an unconditional exception here.)
@@ -575,30 +602,46 @@
     trap(str, element_klass,
          Deoptimization::make_trap_request
          (Deoptimization::Reason_unloaded,
           Deoptimization::Action_reinterpret));
   } else {
-    push_object(element_klass);
+    if (array_klass->is_elem_null_free()) {
+      // TODO 8325106 Is this dead?
+      push(outer()->mark_as_null_free(element_klass));
+    } else {
+      push_object(element_klass);
+    }
   }
 }
 
 
 // ------------------------------------------------------------------
 // ciTypeFlow::StateVector::do_checkcast
 void ciTypeFlow::StateVector::do_checkcast(ciBytecodeStream* str) {
   bool will_link;
   ciKlass* klass = str->get_klass(will_link);
   if (!will_link) {
-    // VM's interpreter will not load 'klass' if object is null.
+    // VM's interpreter will not load 'klass' if object is nullptr.
     // Type flow after this block may still be needed in two situations:
     // 1) C2 uses do_null_assert() and continues compilation for later blocks
     // 2) C2 does an OSR compile in a later block (see bug 4778368).
     pop_object();
     do_null_assert(klass);
   } else {
-    pop_object();
-    push_object(klass);
+    ciType* type = pop_value();
+    type = type->unwrap();
+    if (type->is_loaded() && klass->is_loaded() &&
+        type != klass && type->is_subtype_of(klass)) {
+      // Useless cast, propagate more precise type of object
+      klass = type->as_klass();
+    }
+    if (klass->is_inlinetype() && type->is_null_free()) {
+      // TODO 8325106 Is this dead?
+      push(outer()->mark_as_null_free(klass));
+    } else {
+      push_object(klass);
+    }
   }
 }
 
 // ------------------------------------------------------------------
 // ciTypeFlow::StateVector::do_getfield
@@ -615,11 +658,20 @@
   ciField* field = str->get_field(will_link);
   if (!will_link) {
     trap(str, field->holder(), str->get_field_holder_index());
   } else {
     ciType* field_type = field->type();
-    if (!field_type->is_loaded()) {
+    if (field->is_static() && field->is_null_free() &&
+        !field_type->as_instance_klass()->is_initialized()) {
+      // Deoptimize if we load from a static field with an uninitialized inline type
+      // because we need to throw an exception if initialization of the type failed.
+      trap(str, field_type->as_klass(),
+           Deoptimization::make_trap_request
+           (Deoptimization::Reason_unloaded,
+            Deoptimization::Action_reinterpret));
+      return;
+    } else if (!field_type->is_loaded()) {
       // Normally, we need the field's type to be loaded if we are to
       // do anything interesting with its value.
       // We used to do this:  trap(str, str->get_field_signature_index());
       //
       // There is one good reason not to trap here.  Execution can
@@ -636,10 +688,13 @@
       // here can make an OSR entry point unreachable, triggering the
       // assert on non_osr_block in ciTypeFlow::get_start_state.
       // (See bug 4379915.)
       do_null_assert(field_type->as_klass());
     } else {
+      if (field->is_null_free()) {
+        field_type = outer()->mark_as_null_free(field_type);
+      }
       push_translate(field_type);
     }
   }
 }
 
@@ -701,11 +756,21 @@
         // does not need to be loaded!  The compiler must assume that
         // the value of the unloaded class reference is null; if the code
         // ever sees a non-null value, loading has occurred.
         //
         // See do_getstatic() for similar explanation, as well as bug 4684993.
-        do_null_assert(return_type->as_klass());
+        if (InlineTypeReturnedAsFields) {
+          // Return might be in scalarized form but we can't handle it because we
+          // don't know the type. This can happen due to a missing preload attribute.
+          // TODO 8284443 Use PhaseMacroExpand::expand_mh_intrinsic_return for this
+          trap(str, nullptr,
+               Deoptimization::make_trap_request
+               (Deoptimization::Reason_uninitialized,
+                Deoptimization::Action_reinterpret));
+        } else {
+          do_null_assert(return_type->as_klass());
+        }
       } else {
         push_translate(return_type);
       }
     }
   }
@@ -733,11 +798,15 @@
       ciObject* obj = con.as_object();
       if (obj->is_null_object()) {
         push_null();
       } else {
         assert(obj->is_instance() || obj->is_array(), "must be java_mirror of klass");
-        push_object(obj->klass());
+        ciType* type = obj->klass();
+        if (type->is_inlinetype()) {
+          type = outer()->mark_as_null_free(type);
+        }
+        push(type);
       }
     } else {
       assert(basic_type == con.basic_type() || con.basic_type() == T_OBJECT,
              "not a boxed form: %s vs %s", type2name(basic_type), type2name(con.basic_type()));
       push_translate(ciType::make(basic_type));
@@ -880,17 +949,17 @@
     tty->print_cr(">> Interpreting bytecode %d:%s", str->cur_bci(),
                   Bytecodes::name(str->cur_bc()));
   }
 
   switch(str->cur_bc()) {
-  case Bytecodes::_aaload: do_aaload(str);                       break;
+  case Bytecodes::_aaload: do_aload(str);                           break;
 
   case Bytecodes::_aastore:
     {
       pop_object();
       pop_int();
-      pop_objArray();
+      pop_objOrFlatArray();
       break;
     }
   case Bytecodes::_aconst_null:
     {
       push_null();
@@ -908,11 +977,11 @@
       bool will_link;
       ciKlass* element_klass = str->get_klass(will_link);
       if (!will_link) {
         trap(str, element_klass, str->get_klass_index());
       } else {
-        push_object(ciObjArrayKlass::make(element_klass));
+        push_object(ciArrayKlass::make(element_klass));
       }
       break;
     }
   case Bytecodes::_areturn:
   case Bytecodes::_ifnonnull:
@@ -1467,10 +1536,11 @@
       ciType* value2 = pop_value();
       push(value1);
       push(value2);
       break;
     }
+
   case Bytecodes::_wide:
   default:
     {
       // The iterator should skip this.
       ShouldNotReachHere();
@@ -1487,11 +1557,11 @@
 
 #ifndef PRODUCT
 // ------------------------------------------------------------------
 // ciTypeFlow::StateVector::print_cell_on
 void ciTypeFlow::StateVector::print_cell_on(outputStream* st, Cell c) const {
-  ciType* type = type_at(c);
+  ciType* type = type_at(c)->unwrap();
   if (type == top_type()) {
     st->print("top");
   } else if (type == bottom_type()) {
     st->print("bottom");
   } else if (type == null_type()) {
@@ -1750,13 +1820,16 @@
           _successors->append_if_missing(block);
         }
         break;
       }
 
-      case Bytecodes::_athrow:     case Bytecodes::_ireturn:
-      case Bytecodes::_lreturn:    case Bytecodes::_freturn:
-      case Bytecodes::_dreturn:    case Bytecodes::_areturn:
+      case Bytecodes::_athrow:
+      case Bytecodes::_ireturn:
+      case Bytecodes::_lreturn:
+      case Bytecodes::_freturn:
+      case Bytecodes::_dreturn:
+      case Bytecodes::_areturn:
       case Bytecodes::_return:
         _successors =
           new (arena) GrowableArray<Block*>(arena, 1, 0, nullptr);
         // No successors
         break;
@@ -3146,10 +3219,15 @@
     // Record the first failure reason.
     _failure_reason = reason;
   }
 }
 
+ciType* ciTypeFlow::mark_as_null_free(ciType* type) {
+  // Wrap the type to carry the information that it is null-free
+  return env()->make_null_free_wrapper(type);
+}
+
 #ifndef PRODUCT
 void ciTypeFlow::print() const       { print_on(tty); }
 
 // ------------------------------------------------------------------
 // ciTypeFlow::print_on
diff a/src/hotspot/share/ci/ciTypeFlow.hpp b/src/hotspot/share/ci/ciTypeFlow.hpp
--- a/src/hotspot/share/ci/ciTypeFlow.hpp
+++ b/src/hotspot/share/ci/ciTypeFlow.hpp
@@ -329,18 +329,19 @@
     void      pop_array() {
       assert(type_at_tos() == null_type() ||
              type_at_tos()->is_array_klass(), "must be array type");
       pop();
     }
-    // pop_objArray and pop_typeArray narrow the tos to ciObjArrayKlass
-    // or ciTypeArrayKlass (resp.).  In the rare case that an explicit
+    // pop_objOrFlatArray and pop_typeArray narrow the tos to ciObjArrayKlass,
+    // ciFlatArrayKlass or ciTypeArrayKlass (resp.). In the rare case that an explicit
     // null is popped from the stack, we return null.  Caller beware.
-    ciObjArrayKlass* pop_objArray() {
+    ciArrayKlass* pop_objOrFlatArray() {
       ciType* array = pop_value();
       if (array == null_type())  return nullptr;
-      assert(array->is_obj_array_klass(), "must be object array type");
-      return array->as_obj_array_klass();
+      assert(array->is_obj_array_klass() || array->is_flat_array_klass(),
+             "must be a flat or an object array type");
+      return array->as_array_klass();
     }
     ciTypeArrayKlass* pop_typeArray() {
       ciType* array = pop_value();
       if (array == null_type())  return nullptr;
       assert(array->is_type_array_klass(), "must be prim array type");
@@ -350,11 +351,11 @@
       push(null_type());
     }
     void      do_null_assert(ciKlass* unloaded_klass);
 
     // Helper convenience routines.
-    void do_aaload(ciBytecodeStream* str);
+    void do_aload(ciBytecodeStream* str);
     void do_checkcast(ciBytecodeStream* str);
     void do_getfield(ciBytecodeStream* str);
     void do_getstatic(ciBytecodeStream* str);
     void do_invoke(ciBytecodeStream* str, bool has_receiver);
     void do_jsr(ciBytecodeStream* str);
@@ -847,10 +848,12 @@
   int start_block_num() const       { return 0; }
   Block* rpo_at(int rpo) const      { assert(0 <= rpo && rpo < block_count(), "out of bounds");
                                       return _block_map[rpo]; }
   int inc_next_pre_order()          { return _next_pre_order++; }
 
+  ciType* mark_as_null_free(ciType* type);
+
 private:
   // A work list used during flow analysis.
   Block* _work_list;
 
   // List of blocks in reverse post order
diff a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp
--- a/src/hotspot/share/classfile/classFileParser.cpp
+++ b/src/hotspot/share/classfile/classFileParser.cpp
@@ -19,10 +19,12 @@
  * 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 "oops/inlineKlass.hpp"
 #include "precompiled.hpp"
 #include "cds/cdsConfig.hpp"
 #include "classfile/classFileParser.hpp"
 #include "classfile/classFileStream.hpp"
 #include "classfile/classLoader.hpp"
@@ -49,10 +51,11 @@
 #include "memory/universe.hpp"
 #include "oops/annotations.hpp"
 #include "oops/constantPool.inline.hpp"
 #include "oops/fieldInfo.hpp"
 #include "oops/fieldStreams.inline.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "oops/instanceKlass.inline.hpp"
 #include "oops/instanceMirrorKlass.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/klassVtable.hpp"
 #include "oops/metadata.hpp"
@@ -83,10 +86,11 @@
 #include "utilities/globalDefinitions.hpp"
 #include "utilities/growableArray.hpp"
 #include "utilities/macros.hpp"
 #include "utilities/ostream.hpp"
 #include "utilities/resourceHash.hpp"
+#include "utilities/stringUtils.hpp"
 #include "utilities/utf8.hpp"
 #if INCLUDE_CDS
 #include "classfile/systemDictionaryShared.hpp"
 #endif
 #if INCLUDE_JFR
@@ -147,10 +151,12 @@
 
 #define JAVA_21_VERSION                   65
 
 #define JAVA_22_VERSION                   66
 
+#define CONSTANT_CLASS_DESCRIPTORS        67
+
 #define JAVA_23_VERSION                   67
 
 void ClassFileParser::set_class_bad_constant_seen(short bad_constant) {
   assert((bad_constant == JVM_CONSTANT_Module ||
           bad_constant == JVM_CONSTANT_Package) && _major_version >= JAVA_9_VERSION,
@@ -187,11 +193,11 @@
     // Each of the following case guarantees one more byte in the stream
     // for the following tag or the access_flags following constant pool,
     // so we don't need bounds-check for reading tag.
     const u1 tag = cfs->get_u1_fast();
     switch (tag) {
-      case JVM_CONSTANT_Class : {
+      case JVM_CONSTANT_Class: {
         cfs->guarantee_more(3, CHECK);  // name_index, tag/access_flags
         const u2 name_index = cfs->get_u2_fast();
         cp->klass_index_at_put(index, name_index);
         break;
       }
@@ -508,10 +514,13 @@
       case JVM_CONSTANT_ClassIndex: {
         const int class_index = cp->klass_index_at(index);
         check_property(valid_symbol_at(class_index),
           "Invalid constant pool index %u in class file %s",
           class_index, CHECK);
+
+        Symbol* const name = cp->symbol_at(class_index);
+        const unsigned int name_len = name->utf8_length();
         cp->unresolved_klass_at_put(index, class_index, num_klasses++);
         break;
       }
       case JVM_CONSTANT_StringIndex: {
         const int string_index = cp->string_index_at(index);
@@ -717,11 +726,11 @@
             if (name != vmSymbols::object_initializer_name()) {
               classfile_parse_error(
                 "Bad method name at constant pool index %u in class file %s",
                 name_ref_index, THREAD);
               return;
-            } else if (!Signature::is_void_method(signature)) { // must have void signature.
+            } else if (!Signature::is_void_method(signature)) {  // must have void signature.
               throwIllegalSignature("Method", name, signature, CHECK);
             }
           }
         }
         break;
@@ -737,19 +746,28 @@
             const int name_and_type_ref_index =
               cp->uncached_name_and_type_ref_index_at(ref_index);
             const int name_ref_index =
               cp->name_ref_index_at(name_and_type_ref_index);
             const Symbol* const name = cp->symbol_at(name_ref_index);
-            if (ref_kind == JVM_REF_newInvokeSpecial) {
-              if (name != vmSymbols::object_initializer_name()) {
+
+            if (name != vmSymbols::object_initializer_name()) { // !<init>
+              if (ref_kind == JVM_REF_newInvokeSpecial) {
                 classfile_parse_error(
                   "Bad constructor name at constant pool index %u in class file %s",
                     name_ref_index, THREAD);
                 return;
               }
-            } else {
-              if (name == vmSymbols::object_initializer_name()) {
+            } else { // <init>
+              // The allowed invocation mode of <init> depends on its signature.
+              // This test corresponds to verify_invoke_instructions in the verifier.
+              const int signature_ref_index =
+                cp->signature_ref_index_at(name_and_type_ref_index);
+              const Symbol* const signature = cp->symbol_at(signature_ref_index);
+              if (signature->is_void_method_signature()
+                  && ref_kind == JVM_REF_newInvokeSpecial) {
+                // OK, could be a constructor call
+              } else {
                 classfile_parse_error(
                   "Bad method name at constant pool index %u in class file %s",
                   name_ref_index, THREAD);
                 return;
               }
@@ -797,66 +815,49 @@
 using NameSigHashtable = ResourceHashtable<NameSigHash, int,
                                            NameSigHash::HASH_ROW_SIZE,
                                            AnyObj::RESOURCE_AREA, mtInternal,
                                            &NameSigHash::hash, &NameSigHash::equals>;
 
-// Side-effects: populates the _local_interfaces field
-void ClassFileParser::parse_interfaces(const ClassFileStream* const stream,
-                                       const int itfs_len,
-                                       ConstantPool* const cp,
+static void check_identity_and_value_modifiers(ClassFileParser* current, const InstanceKlass* super_type, TRAPS) {
+  assert(super_type != nullptr,"Method doesn't support null super type");
+  if (super_type->access_flags().is_identity_class() && !current->access_flags().is_identity_class()
+      && super_type->super() != nullptr /* Super is not j.l.Object */) {
+      THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(),
+                err_msg("Value type %s has an identity type as supertype",
+                current->class_name()->as_klass_external_name()));
+  }
+}
+
+void ClassFileParser::parse_interfaces(const ClassFileStream* stream,
+                                       int itfs_len,
+                                       ConstantPool* cp,
                                        bool* const has_nonstatic_concrete_methods,
+                                       // FIXME: lots of these functions
+                                       // declare their parameters as const,
+                                       // which adds only noise to the code.
+                                       // Remove the spurious const modifiers.
+                                       // Many are of the form "const int x"
+                                       // or "T* const x".
                                        TRAPS) {
   assert(stream != nullptr, "invariant");
   assert(cp != nullptr, "invariant");
   assert(has_nonstatic_concrete_methods != nullptr, "invariant");
 
   if (itfs_len == 0) {
     _local_interfaces = Universe::the_empty_instance_klass_array();
+
   } else {
     assert(itfs_len > 0, "only called for len>0");
-    _local_interfaces = MetadataFactory::new_array<InstanceKlass*>(_loader_data, itfs_len, nullptr, CHECK);
-
-    int index;
+    _local_interface_indexes = new GrowableArray<u2>(itfs_len);
+    int index = 0;
     for (index = 0; index < itfs_len; index++) {
       const u2 interface_index = stream->get_u2(CHECK);
-      Klass* interf;
       check_property(
         valid_klass_reference_at(interface_index),
         "Interface name has bad constant pool index %u in class file %s",
         interface_index, CHECK);
-      if (cp->tag_at(interface_index).is_klass()) {
-        interf = cp->resolved_klass_at(interface_index);
-      } else {
-        Symbol* const unresolved_klass  = cp->klass_name_at(interface_index);
-
-        // Don't need to check legal name because it's checked when parsing constant pool.
-        // But need to make sure it's not an array type.
-        guarantee_property(unresolved_klass->char_at(0) != JVM_SIGNATURE_ARRAY,
-                           "Bad interface name in class file %s", CHECK);
-
-        // Call resolve_super so class circularity is checked
-        interf = SystemDictionary::resolve_super_or_fail(
-                                                  _class_name,
-                                                  unresolved_klass,
-                                                  Handle(THREAD, _loader_data->class_loader()),
-                                                  _protection_domain,
-                                                  false,
-                                                  CHECK);
-      }
-
-      if (!interf->is_interface()) {
-        THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(),
-                  err_msg("class %s can not implement %s, because it is not an interface (%s)",
-                          _class_name->as_klass_external_name(),
-                          interf->external_name(),
-                          interf->class_in_module_of_loader()));
-      }
-
-      if (InstanceKlass::cast(interf)->has_nonstatic_concrete_methods()) {
-        *has_nonstatic_concrete_methods = true;
-      }
-      _local_interfaces->at_put(index, InstanceKlass::cast(interf));
+      _local_interface_indexes->at_put_grow(index, interface_index);
     }
 
     if (!_need_verify || itfs_len <= 1) {
       return;
     }
@@ -864,12 +865,11 @@
     // Check if there's any duplicates in interfaces
     ResourceMark rm(THREAD);
     // Set containing interface names
     ResourceHashtable<Symbol*, int>* interface_names = new ResourceHashtable<Symbol*, int>();
     for (index = 0; index < itfs_len; index++) {
-      const InstanceKlass* const k = _local_interfaces->at(index);
-      Symbol* interface_name = k->name();
+      Symbol* interface_name = cp->klass_name_at(_local_interface_indexes->at(index));
       // If no duplicates, add (name, nullptr) in hashtable interface_names.
       if (!interface_names->put(interface_name, 0)) {
         classfile_parse_error("Duplicate interface name \"%s\" in class file %s",
                                interface_name->as_C_string(), THREAD);
         return;
@@ -951,10 +951,13 @@
     _method_IntrinsicCandidate,
     _jdk_internal_vm_annotation_Contended,
     _field_Stable,
     _jdk_internal_vm_annotation_ReservedStackAccess,
     _jdk_internal_ValueBased,
+    _jdk_internal_ImplicitlyConstructible,
+    _jdk_internal_LooselyConsistentValue,
+    _jdk_internal_NullRestricted,
     _java_lang_Deprecated,
     _java_lang_Deprecated_for_removal,
     _annotation_LIMIT
   };
   const Location _location;
@@ -1376,15 +1379,17 @@
   STATIC_OOP,           // Oops
   STATIC_BYTE,          // Boolean, Byte, char
   STATIC_SHORT,         // shorts
   STATIC_WORD,          // ints
   STATIC_DOUBLE,        // aligned long or double
+  STATIC_INLINE,        // inline type field
   NONSTATIC_OOP,
   NONSTATIC_BYTE,
   NONSTATIC_SHORT,
   NONSTATIC_WORD,
   NONSTATIC_DOUBLE,
+  NONSTATIC_INLINE,
   MAX_FIELD_ALLOCATION_TYPE,
   BAD_ALLOCATION_TYPE = -1
 };
 
 static FieldAllocationType _basic_type_to_atype[2 * (T_CONFLICT + 1)] = {
@@ -1400,16 +1405,17 @@
   NONSTATIC_SHORT,     // T_SHORT       =  9,
   NONSTATIC_WORD,      // T_INT         = 10,
   NONSTATIC_DOUBLE,    // T_LONG        = 11,
   NONSTATIC_OOP,       // T_OBJECT      = 12,
   NONSTATIC_OOP,       // T_ARRAY       = 13,
-  BAD_ALLOCATION_TYPE, // T_VOID        = 14,
-  BAD_ALLOCATION_TYPE, // T_ADDRESS     = 15,
-  BAD_ALLOCATION_TYPE, // T_NARROWOOP   = 16,
-  BAD_ALLOCATION_TYPE, // T_METADATA    = 17,
-  BAD_ALLOCATION_TYPE, // T_NARROWKLASS = 18,
-  BAD_ALLOCATION_TYPE, // T_CONFLICT    = 19,
+  NONSTATIC_OOP,       // T_PRIMITIVE_OBJECT = 14,
+  BAD_ALLOCATION_TYPE, // T_VOID        = 15,
+  BAD_ALLOCATION_TYPE, // T_ADDRESS     = 16,
+  BAD_ALLOCATION_TYPE, // T_NARROWOOP   = 17,
+  BAD_ALLOCATION_TYPE, // T_METADATA    = 18,
+  BAD_ALLOCATION_TYPE, // T_NARROWKLASS = 19,
+  BAD_ALLOCATION_TYPE, // T_CONFLICT    = 20,
   BAD_ALLOCATION_TYPE, // 0
   BAD_ALLOCATION_TYPE, // 1
   BAD_ALLOCATION_TYPE, // 2
   BAD_ALLOCATION_TYPE, // 3
   STATIC_BYTE ,        // T_BOOLEAN     =  4,
@@ -1420,22 +1426,26 @@
   STATIC_SHORT,        // T_SHORT       =  9,
   STATIC_WORD,         // T_INT         = 10,
   STATIC_DOUBLE,       // T_LONG        = 11,
   STATIC_OOP,          // T_OBJECT      = 12,
   STATIC_OOP,          // T_ARRAY       = 13,
-  BAD_ALLOCATION_TYPE, // T_VOID        = 14,
-  BAD_ALLOCATION_TYPE, // T_ADDRESS     = 15,
-  BAD_ALLOCATION_TYPE, // T_NARROWOOP   = 16,
-  BAD_ALLOCATION_TYPE, // T_METADATA    = 17,
-  BAD_ALLOCATION_TYPE, // T_NARROWKLASS = 18,
-  BAD_ALLOCATION_TYPE, // T_CONFLICT    = 19,
+  STATIC_OOP,          // T_PRIMITIVE_OBJECT = 14,
+  BAD_ALLOCATION_TYPE, // T_VOID        = 15,
+  BAD_ALLOCATION_TYPE, // T_ADDRESS     = 16,
+  BAD_ALLOCATION_TYPE, // T_NARROWOOP   = 17,
+  BAD_ALLOCATION_TYPE, // T_METADATA    = 18,
+  BAD_ALLOCATION_TYPE, // T_NARROWKLASS = 19,
+  BAD_ALLOCATION_TYPE, // T_CONFLICT    = 20
 };
 
-static FieldAllocationType basic_type_to_atype(bool is_static, BasicType type) {
+static FieldAllocationType basic_type_to_atype(bool is_static, BasicType type, bool is_inline_type) {
   assert(type >= T_BOOLEAN && type < T_VOID, "only allowable values");
   FieldAllocationType result = _basic_type_to_atype[type + (is_static ? (T_CONFLICT + 1) : 0)];
   assert(result != BAD_ALLOCATION_TYPE, "bad type");
+  if (is_inline_type) {
+    result = is_static ? STATIC_INLINE : NONSTATIC_INLINE;
+  }
   return result;
 }
 
 class ClassFileParser::FieldAllocationCount : public ResourceObj {
  public:
@@ -1445,12 +1455,12 @@
     for (int i = 0; i < MAX_FIELD_ALLOCATION_TYPE; i++) {
       count[i] = 0;
     }
   }
 
-  void update(bool is_static, BasicType type) {
-    FieldAllocationType atype = basic_type_to_atype(is_static, type);
+  void update(bool is_static, BasicType type, bool is_inline_type) {
+    FieldAllocationType atype = basic_type_to_atype(is_static, type, is_inline_type);
     if (atype != BAD_ALLOCATION_TYPE) {
       // Make sure there is no overflow with injected fields.
       assert(count[atype] < 0xFFFF, "More than 65535 fields");
       count[atype]++;
     }
@@ -1458,11 +1468,11 @@
 };
 
 // Side-effects: populates the _fields, _fields_annotations,
 // _fields_type_annotations fields
 void ClassFileParser::parse_fields(const ClassFileStream* const cfs,
-                                   bool is_interface,
+                                   AccessFlags class_access_flags,
                                    FieldAllocationCount* const fac,
                                    ConstantPool* cp,
                                    const int cp_size,
                                    u2* const java_fields_count_ptr,
                                    TRAPS) {
@@ -1473,31 +1483,42 @@
   assert(java_fields_count_ptr != nullptr, "invariant");
 
   assert(nullptr == _fields_annotations, "invariant");
   assert(nullptr == _fields_type_annotations, "invariant");
 
+  bool is_inline_type = !class_access_flags.is_identity_class() && !class_access_flags.is_abstract();
   cfs->guarantee_more(2, CHECK);  // length
   const u2 length = cfs->get_u2_fast();
   *java_fields_count_ptr = length;
 
   int num_injected = 0;
   const InjectedField* const injected = JavaClasses::get_injected(_class_name,
                                                                   &num_injected);
-  const int total_fields = length + num_injected;
+
+  // two more slots are required for inline classes:
+  // one for the static field with a reference to the pre-allocated default value
+  // one for the field the JVM injects when detecting an empty inline class
+  const int total_fields = length + num_injected + (is_inline_type ? 2 : 0);
 
   // Allocate a temporary resource array to collect field data.
   // After parsing all fields, data are stored in a UNSIGNED5 compressed stream.
   _temp_field_info = new GrowableArray<FieldInfo>(total_fields);
 
+  int instance_fields_count = 0;
   ResourceMark rm(THREAD);
   for (int n = 0; n < length; n++) {
     // access_flags, name_index, descriptor_index, attributes_count
     cfs->guarantee_more(8, CHECK);
 
+    jint recognized_modifiers = JVM_RECOGNIZED_FIELD_MODIFIERS;
+    if (!supports_inline_types()) {
+      recognized_modifiers &= ~JVM_ACC_STRICT;
+    }
+
+    const jint flags = cfs->get_u2_fast() & recognized_modifiers;
+    verify_legal_field_modifiers(flags, class_access_flags, CHECK);
     AccessFlags access_flags;
-    const jint flags = cfs->get_u2_fast() & JVM_RECOGNIZED_FIELD_MODIFIERS;
-    verify_legal_field_modifiers(flags, is_interface, CHECK);
     access_flags.set_flags(flags);
     FieldInfo::FieldFlags fieldFlags(0);
 
     const u2 name_index = cfs->get_u2_fast();
     check_property(valid_symbol_at(name_index),
@@ -1510,17 +1531,20 @@
     check_property(valid_symbol_at(signature_index),
       "Invalid constant pool index %u for field signature in class file %s",
       signature_index, CHECK);
     const Symbol* const sig = cp->symbol_at(signature_index);
     verify_legal_field_signature(name, sig, CHECK);
+    if (!access_flags.is_static()) instance_fields_count++;
 
     u2 constantvalue_index = 0;
     bool is_synthetic = false;
     u2 generic_signature_index = 0;
     const bool is_static = access_flags.is_static();
     FieldAnnotationCollector parsed_annotations(_loader_data);
 
+    bool is_null_restricted = false;
+
     const u2 attributes_count = cfs->get_u2_fast();
     if (attributes_count > 0) {
       parse_field_attributes(cfs,
                              attributes_count,
                              is_static,
@@ -1536,10 +1560,20 @@
           _fields_annotations = MetadataFactory::new_array<AnnotationArray*>(
                                              _loader_data, length, nullptr,
                                              CHECK);
         }
         _fields_annotations->at_put(n, parsed_annotations.field_annotations());
+        if (parsed_annotations.has_annotation(AnnotationCollector::_jdk_internal_NullRestricted)) {
+          if (!Signature::has_envelope(sig)) {
+            Exceptions::fthrow(
+              THREAD_AND_LOCATION,
+              vmSymbols::java_lang_ClassFormatError(),
+              "Illegal use of @jdk.internal.vm.annotation.NullRestricted annotation on field %s with signature %s (primitive types can never be null)",
+              name->as_C_string(), sig->as_C_string());
+          }
+          is_null_restricted = true;
+        }
         parsed_annotations.set_field_annotations(nullptr);
       }
       if (parsed_annotations.field_type_annotations() != nullptr) {
         if (_fields_type_annotations == nullptr) {
           _fields_type_annotations =
@@ -1561,11 +1595,14 @@
     }
 
     const BasicType type = cp->basic_type_for_signature_at(signature_index);
 
     // Update FieldAllocationCount for this kind of field
-    fac->update(is_static, type);
+    // This use of T_PRIMITIVE_OBJECT is not valid anymore => FIXME (relate to cleanup (removal?) of FiedAllocationCount
+    fac->update(is_static, type, type == T_PRIMITIVE_OBJECT);
+
+    if (is_null_restricted) fieldFlags.update_null_free_inline_type(true);
 
     FieldInfo fi(access_flags, name_index, signature_index, constantvalue_index, fieldFlags);
     fi.set_index(n);
     if (fieldFlags.is_generic()) {
       fi.set_generic_signature_index(generic_signature_index);
@@ -1609,15 +1646,57 @@
       fi.set_index(index);
       _temp_field_info->append(fi);
 
       // Update FieldAllocationCount for this kind of field
       const BasicType type = Signature::basic_type(injected[n].signature());
-      fac->update(false, type);
+      fac->update(false, type, false);
       index++;
     }
   }
 
+  if (is_inline_type) {
+    // Inject static ".default" field
+    FieldInfo::FieldFlags fflags(0);
+    fflags.update_injected(true);
+    AccessFlags aflags(JVM_ACC_STATIC);
+    FieldInfo fi(aflags,
+                 (u2)vmSymbols::as_int(VM_SYMBOL_ENUM_NAME(default_value_name)),
+                 (u2)vmSymbols::as_int(VM_SYMBOL_ENUM_NAME(object_signature)),
+                 0,
+                 fflags);
+      fi.set_index(index);
+      _temp_field_info->append(fi);
+
+    const BasicType type = Signature::basic_type(vmSymbols::object_signature());
+    fac->update(true, type, false);
+    index++;
+  }
+
+  if (is_inline_type && instance_fields_count == 0) {
+    // Inject ".empty" dummy field
+    _is_empty_inline_type = true;
+
+    FieldInfo::FieldFlags fflags(0);
+    fflags.update_injected(true);
+    AccessFlags aflags;
+    FieldInfo fi(aflags,
+                 (u2)vmSymbols::as_int(VM_SYMBOL_ENUM_NAME(empty_marker_name)),
+                 (u2)vmSymbols::as_int(VM_SYMBOL_ENUM_NAME(byte_signature)),
+                 0,
+                 fflags);
+    fi.set_index(index);
+    _temp_field_info->append(fi);
+
+    const BasicType type = Signature::basic_type(vmSymbols::byte_signature());
+    fac->update(false, type, false);
+    index++;
+  }
+
+  if (instance_fields_count > 0) {
+    _has_nonstatic_fields = true;
+  }
+
   assert(_temp_field_info->length() == index, "Must be");
 
   if (_need_verify && length > 1) {
     // Check duplicated fields
     ResourceMark rm(THREAD);
@@ -1988,10 +2067,22 @@
     case VM_SYMBOL_ENUM_NAME(jdk_internal_ValueBased_signature): {
       if (_location != _in_class)   break;  // only allow for classes
       if (!privileged)              break;  // only allow in privileged code
       return _jdk_internal_ValueBased;
     }
+    case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_ImplicitlyConstructible_signature): {
+      if (_location != _in_class)   break; // only allow for classes
+      return _jdk_internal_ImplicitlyConstructible;
+    }
+    case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_LooselyConsistentValue_signature): {
+      if (_location != _in_class)   break; // only allow for classes
+      return _jdk_internal_LooselyConsistentValue;
+    }
+    case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_NullRestricted_signature): {
+      if (_location != _in_field)   break; // only allow for fields
+      return _jdk_internal_NullRestricted;
+    }
     case VM_SYMBOL_ENUM_NAME(java_lang_Deprecated): {
       return _java_lang_Deprecated;
     }
     default: {
       break;
@@ -2226,10 +2317,12 @@
 //
 // The has_localvariable_table parameter is used to pass up the value to InstanceKlass.
 
 Method* ClassFileParser::parse_method(const ClassFileStream* const cfs,
                                       bool is_interface,
+                                      bool is_value_class,
+                                      bool is_abstract_class,
                                       const ConstantPool* cp,
                                       bool* const has_localvariable_table,
                                       TRAPS) {
   assert(cfs != nullptr, "invariant");
   assert(cp != nullptr, "invariant");
@@ -2267,18 +2360,27 @@
     } else {
       classfile_parse_error("Method <clinit> is not static in class file %s", THREAD);
       return nullptr;
     }
   } else {
-    verify_legal_method_modifiers(flags, is_interface, name, CHECK_NULL);
+    verify_legal_method_modifiers(flags, access_flags() , name, CHECK_NULL);
   }
 
   if (name == vmSymbols::object_initializer_name() && is_interface) {
     classfile_parse_error("Interface cannot have a method named <init>, class file %s", THREAD);
     return nullptr;
   }
 
+  if (EnableValhalla) {
+    if (((flags & JVM_ACC_SYNCHRONIZED) == JVM_ACC_SYNCHRONIZED)
+        && ((flags & JVM_ACC_STATIC) == 0 )
+        && !_access_flags.is_identity_class()) {
+      classfile_parse_error("Invalid synchronized method in non-identity class %s", THREAD);
+        return nullptr;
+    }
+  }
+
   int args_size = -1;  // only used when _need_verify is true
   if (_need_verify) {
     verify_legal_name_with_signature(name, signature, CHECK_NULL);
     args_size = ((flags & JVM_ACC_STATIC) ? 0 : 1) +
                  verify_legal_method_signature(name, signature, CHECK_NULL);
@@ -2839,10 +2941,12 @@
 
 
 // Side-effects: populates the _methods field in the parser
 void ClassFileParser::parse_methods(const ClassFileStream* const cfs,
                                     bool is_interface,
+                                    bool is_value_class,
+                                    bool is_abstract_type,
                                     bool* const has_localvariable_table,
                                     bool* has_final_method,
                                     bool* declares_nonstatic_concrete_methods,
                                     TRAPS) {
   assert(cfs != nullptr, "invariant");
@@ -2863,10 +2967,12 @@
                                                    CHECK);
 
     for (int index = 0; index < length; index++) {
       Method* method = parse_method(cfs,
                                     is_interface,
+                                    is_value_class,
+                                    is_abstract_type,
                                     _cp,
                                     has_localvariable_table,
                                     CHECK);
 
       if (method->is_final()) {
@@ -3128,23 +3234,35 @@
       inner_name_index, CHECK_0);
     if (_need_verify) {
       guarantee_property(inner_class_info_index != outer_class_info_index,
                          "Class is both outer and inner class in class file %s", CHECK_0);
     }
-    // Access flags
-    jint flags;
+
+    jint recognized_modifiers = RECOGNIZED_INNER_CLASS_MODIFIERS;
     // JVM_ACC_MODULE is defined in JDK-9 and later.
     if (_major_version >= JAVA_9_VERSION) {
-      flags = cfs->get_u2_fast() & (RECOGNIZED_INNER_CLASS_MODIFIERS | JVM_ACC_MODULE);
-    } else {
-      flags = cfs->get_u2_fast() & RECOGNIZED_INNER_CLASS_MODIFIERS;
+      recognized_modifiers |= JVM_ACC_MODULE;
     }
+
+    // Access flags
+    jint flags = cfs->get_u2_fast() & recognized_modifiers;
+
     if ((flags & JVM_ACC_INTERFACE) && _major_version < JAVA_6_VERSION) {
       // Set abstract bit for old class files for backward compatibility
       flags |= JVM_ACC_ABSTRACT;
     }
-    verify_legal_class_modifiers(flags, CHECK_0);
+
+    if (!supports_inline_types()) {
+      const bool is_module = (flags & JVM_ACC_MODULE) != 0;
+      const bool is_interface = (flags & JVM_ACC_INTERFACE) != 0;
+      if (!is_module && !is_interface) {
+        flags |= JVM_ACC_IDENTITY;
+      }
+    }
+
+    const char* name = inner_name_index == 0 ? "unnamed" : cp->symbol_at(inner_name_index)->as_utf8();
+    verify_legal_class_modifiers(flags, name, false, CHECK_0);
     AccessFlags inner_access_flags(flags);
 
     inner_classes->at_put(index++, inner_class_info_index);
     inner_classes->at_put(index++, outer_class_info_index);
     inner_classes->at_put(index++, inner_name_index);
@@ -3244,10 +3362,43 @@
   cfs->set_current(current_mark);
 
   return length;
 }
 
+u2 ClassFileParser::parse_classfile_preload_attribute(const ClassFileStream* const cfs,
+                                                                   const u1* const preload_attribute_start,
+                                                                   TRAPS) {
+  const u1* const current_mark = cfs->current();
+  u2 length = 0;
+  if (preload_attribute_start != nullptr) {
+    cfs->set_current(preload_attribute_start);
+    cfs->guarantee_more(2, CHECK_0);  // length
+    length = cfs->get_u2_fast();
+  }
+  const int size = length;
+  Array<u2>* const preload_classes = MetadataFactory::new_array<u2>(_loader_data, size, CHECK_0);
+  _preload_classes = preload_classes;
+  if (length > 0) {
+    int index = 0;
+    cfs->guarantee_more(2 * length, CHECK_0);
+    for (int n = 0; n < length; n++) {
+      const u2 class_info_index = cfs->get_u2_fast();
+      check_property(
+        valid_klass_reference_at(class_info_index),
+        "Preload class_info_index %u has bad constant type in class file %s",
+        class_info_index, CHECK_0);
+      preload_classes->at_put(index++, class_info_index);
+    }
+    assert(index == size, "wrong size");
+  }
+
+  // Restore buffer's current position.
+  cfs->set_current(current_mark);
+
+  return length;
+}
+
 //  Record {
 //    u2 attribute_name_index;
 //    u4 attribute_length;
 //    u2 components_count;
 //    component_info components[components_count];
@@ -3528,16 +3679,19 @@
   _inner_classes = Universe::the_empty_short_array();
   // Set nest members attribute to default sentinel
   _nest_members = Universe::the_empty_short_array();
   // Set _permitted_subclasses attribute to default sentinel
   _permitted_subclasses = Universe::the_empty_short_array();
+  // Set _preload_classes attribute to default sentinel
+  _preload_classes = Universe::the_empty_short_array();
   cfs->guarantee_more(2, CHECK);  // attributes_count
   u2 attributes_count = cfs->get_u2_fast();
   bool parsed_sourcefile_attribute = false;
   bool parsed_innerclasses_attribute = false;
   bool parsed_nest_members_attribute = false;
   bool parsed_permitted_subclasses_attribute = false;
+  bool parsed_preload_attribute = false;
   bool parsed_nest_host_attribute = false;
   bool parsed_record_attribute = false;
   bool parsed_enclosingmethod_attribute = false;
   bool parsed_bootstrap_methods_attribute = false;
   const u1* runtime_visible_annotations = nullptr;
@@ -3559,10 +3713,12 @@
   u4  nest_members_attribute_length = 0;
   const u1* record_attribute_start = nullptr;
   u4  record_attribute_length = 0;
   const u1* permitted_subclasses_attribute_start = nullptr;
   u4  permitted_subclasses_attribute_length = 0;
+  const u1* preload_attribute_start = nullptr;
+  u4  preload_attribute_length = 0;
 
   // Iterate over attributes
   while (attributes_count--) {
     cfs->guarantee_more(6, CHECK);  // attribute_name_index, attribute_length
     const u2 attribute_name_index = cfs->get_u2_fast();
@@ -3785,10 +3941,19 @@
               }
               parsed_permitted_subclasses_attribute = true;
               permitted_subclasses_attribute_start = cfs->current();
               permitted_subclasses_attribute_length = attribute_length;
             }
+            if (EnableValhalla && tag == vmSymbols::tag_preload()) {
+              if (parsed_preload_attribute) {
+                classfile_parse_error("Multiple Preload attributes in class file %s", CHECK);
+                return;
+              }
+              parsed_preload_attribute = true;
+              preload_attribute_start = cfs->current();
+              preload_attribute_length = attribute_length;
+            }
           }
           // Skip attribute_length for any attribute where major_verson >= JAVA_17_VERSION
           cfs->skip_u1(attribute_length, CHECK);
         } else {
           // Unknown attribute
@@ -3865,10 +4030,22 @@
         permitted_subclasses_attribute_length == sizeof(num_subclasses) + sizeof(u2) * num_subclasses,
         "Wrong PermittedSubclasses attribute length in class file %s", CHECK);
     }
   }
 
+  if (parsed_preload_attribute) {
+    const u2 num_classes = parse_classfile_preload_attribute(
+                            cfs,
+                            preload_attribute_start,
+                            CHECK);
+    if (_need_verify) {
+      guarantee_property(
+        preload_attribute_length == sizeof(num_classes) + sizeof(u2) * num_classes,
+        "Wrong Preload attribute length in class file %s", CHECK);
+    }
+  }
+
   if (_max_bootstrap_specifier_index >= 0) {
     guarantee_property(parsed_bootstrap_methods_attribute,
                        "Missing BootstrapMethods attribute in class file %s", CHECK);
   }
 }
@@ -3930,13 +4107,16 @@
   this_klass->set_fields_status(_fields_status);
   this_klass->set_methods(_methods);
   this_klass->set_inner_classes(_inner_classes);
   this_klass->set_nest_members(_nest_members);
   this_klass->set_nest_host_index(_nest_host);
+  this_klass->set_preload_classes(_preload_classes);
   this_klass->set_annotations(_combined_annotations);
   this_klass->set_permitted_subclasses(_permitted_subclasses);
   this_klass->set_record_components(_record_components);
+  this_klass->set_inline_type_field_klasses_array(_inline_type_field_klasses);
+  this_klass->set_null_marker_offsets_array(_null_marker_offsets);
   // Delay the setting of _local_interfaces and _transitive_interfaces until after
   // initialize_supers() in fill_instance_klass(). It is because the _local_interfaces could
   // be shared with _transitive_interfaces and _transitive_interfaces may be shared with
   // its _super. If an OOM occurs while loading the current klass, its _super field
   // may not have been set. When GC tries to free the klass, the _transitive_interfaces
@@ -3981,29 +4161,24 @@
   assert(cp != nullptr, "invariant");
   const InstanceKlass* super_klass = nullptr;
 
   if (super_class_index == 0) {
     check_property(_class_name == vmSymbols::java_lang_Object(),
-                   "Invalid superclass index %u in class file %s",
-                   super_class_index,
+                   "Invalid superclass index 0 in class file %s",
                    CHECK_NULL);
   } else {
     check_property(valid_klass_reference_at(super_class_index),
                    "Invalid superclass index %u in class file %s",
                    super_class_index,
                    CHECK_NULL);
     // The class name should be legal because it is checked when parsing constant pool.
     // However, make sure it is not an array type.
-    bool is_array = false;
     if (cp->tag_at(super_class_index).is_klass()) {
       super_klass = InstanceKlass::cast(cp->resolved_klass_at(super_class_index));
-      if (need_verify)
-        is_array = super_klass->is_array_klass();
-    } else if (need_verify) {
-      is_array = (cp->klass_name_at(super_class_index)->char_at(0) == JVM_SIGNATURE_ARRAY);
     }
     if (need_verify) {
+      bool is_array = (cp->klass_name_at(super_class_index)->char_at(0) == JVM_SIGNATURE_ARRAY);
       guarantee_property(!is_array,
                         "Bad superclass name in class file %s", CHECK_NULL);
     }
   }
   return super_klass;
@@ -4123,10 +4298,29 @@
 
 void OopMapBlocksBuilder::print_value_on(outputStream* st) const {
   print_on(st);
 }
 
+void ClassFileParser::throwInlineTypeLimitation(THREAD_AND_LOCATION_DECL,
+                                                const char* msg,
+                                                const Symbol* name,
+                                                const Symbol* sig) const {
+
+  ResourceMark rm(THREAD);
+  if (name == nullptr || sig == nullptr) {
+    Exceptions::fthrow(THREAD_AND_LOCATION_ARGS,
+        vmSymbols::java_lang_ClassFormatError(),
+        "class: %s - %s", _class_name->as_C_string(), msg);
+  }
+  else {
+    Exceptions::fthrow(THREAD_AND_LOCATION_ARGS,
+        vmSymbols::java_lang_ClassFormatError(),
+        "\"%s\" sig: \"%s\" class: %s - %s", name->as_C_string(), sig->as_C_string(),
+        _class_name->as_C_string(), msg);
+  }
+}
+
 void ClassFileParser::set_precomputed_flags(InstanceKlass* ik) {
   assert(ik != nullptr, "invariant");
 
   const InstanceKlass* const super = ik->java_super();
 
@@ -4157,10 +4351,15 @@
 #endif
 
   // Check if this klass supports the java.lang.Cloneable interface
   if (vmClasses::Cloneable_klass_loaded()) {
     if (ik->is_subtype_of(vmClasses::Cloneable_klass())) {
+      if (ik->is_inline_klass()) {
+        JavaThread *THREAD = JavaThread::current();
+        throwInlineTypeLimitation(THREAD_AND_LOCATION, "Inline Types do not support Cloneable");
+        return;
+      }
       ik->set_is_cloneable();
     }
   }
 
   // Check if this klass has a vanilla default constructor
@@ -4197,10 +4396,16 @@
     const jint lh = Klass::instance_layout_helper(ik->size_helper(), true);
     ik->set_layout_helper(lh);
   }
 }
 
+bool ClassFileParser::supports_inline_types() const {
+  // Inline types are only supported by class file version 61.65535 and later
+  return _major_version > JAVA_23_VERSION ||
+         (_major_version == JAVA_23_VERSION && _minor_version == JAVA_PREVIEW_MINOR_VERSION);
+}
+
 // utility methods for appending an array with check for duplicates
 
 static void append_interfaces(GrowableArray<InstanceKlass*>* result,
                               const Array<InstanceKlass*>* const ifs) {
   // iterate over new interfaces
@@ -4240,13 +4445,14 @@
     // no interfaces, use canonicalized array
     return Universe::the_empty_instance_klass_array();
   } else if (max_transitive_size == super_size) {
     // no new local interfaces added, share superklass' transitive interface array
     return super->transitive_interfaces();
-  } else if (max_transitive_size == local_size) {
-    // only local interfaces added, share local interface array
-    return local_ifs;
+    // The three lines below are commented to work around bug JDK-8245487
+//  } else if (max_transitive_size == local_size) {
+//    // only local interfaces added, share local interface array
+//    return local_ifs;
   } else {
     ResourceMark rm;
     GrowableArray<InstanceKlass*>* const result = new GrowableArray<InstanceKlass*>(max_transitive_size);
 
     // Copy down from superclass
@@ -4263,10 +4469,11 @@
     append_interfaces(result, local_ifs);
 
     // length will be less than the max_transitive_size if duplicates were removed
     const int length = result->length();
     assert(length <= max_transitive_size, "just checking");
+
     Array<InstanceKlass*>* const new_result =
       MetadataFactory::new_array<InstanceKlass*>(loader_data, length, CHECK_NULL);
     for (int i = 0; i < length; i++) {
       InstanceKlass* const e = result->at(i);
       assert(e != nullptr, "just checking");
@@ -4291,10 +4498,20 @@
     if (super_ik->is_sealed() && !super_ik->has_as_permitted_subclass(this_klass)) {
       classfile_icce_error("class %s cannot inherit from sealed class %s", super_ik, THREAD);
       return;
     }
 
+    // The JVMS says that super classes for value types must not have the ACC_IDENTITY
+    // flag set. But, java.lang.Object must still be allowed to be a direct super class
+    // for a value classes.  So, it is treated as a special case for now.
+    if (!this_klass->access_flags().is_identity_class() &&
+        super_ik->name() != vmSymbols::java_lang_Object() &&
+        super_ik->is_identity_class()) {
+      classfile_icce_error("value class %s cannot inherit from class %s", super_ik, THREAD);
+      return;
+    }
+
     // If the loader is not the boot loader then throw an exception if its
     // superclass is in package jdk.internal.reflect and its loader is not a
     // special reflection class loader
     if (!this_klass->class_loader_data()->is_the_null_class_loader_data()) {
       PackageEntry* super_package = super->package();
@@ -4476,12 +4693,13 @@
   }
 }
 
 // utility methods for format checking
 
-void ClassFileParser::verify_legal_class_modifiers(jint flags, TRAPS) const {
+void ClassFileParser::verify_legal_class_modifiers(jint flags, const char* name, bool is_Object, TRAPS) const {
   const bool is_module = (flags & JVM_ACC_MODULE) != 0;
+  const bool is_inner_class = name != nullptr;
   assert(_major_version >= JAVA_9_VERSION || !is_module, "JVM_ACC_MODULE should not be set");
   if (is_module) {
     ResourceMark rm(THREAD);
     Exceptions::fthrow(
       THREAD_AND_LOCATION,
@@ -4494,27 +4712,39 @@
   if (!_need_verify) { return; }
 
   const bool is_interface  = (flags & JVM_ACC_INTERFACE)  != 0;
   const bool is_abstract   = (flags & JVM_ACC_ABSTRACT)   != 0;
   const bool is_final      = (flags & JVM_ACC_FINAL)      != 0;
-  const bool is_super      = (flags & JVM_ACC_SUPER)      != 0;
+  const bool is_identity   = (flags & JVM_ACC_IDENTITY)   != 0;
   const bool is_enum       = (flags & JVM_ACC_ENUM)       != 0;
   const bool is_annotation = (flags & JVM_ACC_ANNOTATION) != 0;
   const bool major_gte_1_5 = _major_version >= JAVA_1_5_VERSION;
 
   if ((is_abstract && is_final) ||
       (is_interface && !is_abstract) ||
-      (is_interface && major_gte_1_5 && (is_super || is_enum)) ||
+      (is_interface && major_gte_1_5 && (is_identity || is_enum)) ||   //  ACC_SUPER (now ACC_IDENTITY) was illegal for interfaces
       (!is_interface && major_gte_1_5 && is_annotation)) {
     ResourceMark rm(THREAD);
-    Exceptions::fthrow(
-      THREAD_AND_LOCATION,
-      vmSymbols::java_lang_ClassFormatError(),
-      "Illegal class modifiers in class %s: 0x%X",
-      _class_name->as_C_string(), flags
-    );
-    return;
+    const char* class_note = "";
+    if (!is_identity)  class_note = " (a value class)";
+    if (name == nullptr) { // Not an inner class
+      Exceptions::fthrow(
+        THREAD_AND_LOCATION,
+        vmSymbols::java_lang_ClassFormatError(),
+        "Illegal class modifiers in class %s%s: 0x%X",
+        _class_name->as_C_string(), class_note, flags
+      );
+      return;
+    } else {
+      Exceptions::fthrow(
+        THREAD_AND_LOCATION,
+        vmSymbols::java_lang_ClassFormatError(),
+        "Illegal class modifiers in declaration of inner class %s%s of class %s: 0x%X",
+        name, class_note, _class_name->as_C_string(), flags
+      );
+      return;
+    }
   }
 }
 
 static bool has_illegal_visibility(jint flags) {
   const bool is_public    = (flags & JVM_ACC_PUBLIC)    != 0;
@@ -4576,12 +4806,12 @@
     classfile_ucve_error("%s (class file version %u.%u) was compiled with an invalid non-zero minor version",
                          class_name, major, minor, THREAD);
   }
 }
 
-void ClassFileParser::verify_legal_field_modifiers(jint flags,
-                                                   bool is_interface,
+void ClassFileParser:: verify_legal_field_modifiers(jint flags,
+                                                   AccessFlags class_access_flags,
                                                    TRAPS) const {
   if (!_need_verify) { return; }
 
   const bool is_public    = (flags & JVM_ACC_PUBLIC)    != 0;
   const bool is_protected = (flags & JVM_ACC_PROTECTED) != 0;
@@ -4589,23 +4819,48 @@
   const bool is_static    = (flags & JVM_ACC_STATIC)    != 0;
   const bool is_final     = (flags & JVM_ACC_FINAL)     != 0;
   const bool is_volatile  = (flags & JVM_ACC_VOLATILE)  != 0;
   const bool is_transient = (flags & JVM_ACC_TRANSIENT) != 0;
   const bool is_enum      = (flags & JVM_ACC_ENUM)      != 0;
+  const bool is_strict    = (flags & JVM_ACC_STRICT)    != 0;
   const bool major_gte_1_5 = _major_version >= JAVA_1_5_VERSION;
 
+  const bool is_interface = class_access_flags.is_interface();
+  const bool is_abstract = class_access_flags.is_abstract();
+  const bool is_identity_class = class_access_flags.is_identity_class();
+
   bool is_illegal = false;
 
+  if (supports_inline_types()) {
+    if (is_strict && is_static) {
+      is_illegal = true;
+    }
+    if (is_strict && !is_final) {
+      is_illegal = true;
+    }
+  }
+
   if (is_interface) {
     if (!is_public || !is_static || !is_final || is_private ||
         is_protected || is_volatile || is_transient ||
         (major_gte_1_5 && is_enum)) {
       is_illegal = true;
     }
   } else { // not interface
     if (has_illegal_visibility(flags) || (is_final && is_volatile)) {
       is_illegal = true;
+    } else {
+      if (!is_identity_class && !is_abstract && !is_static && !is_final) {
+        is_illegal = true;
+      } else if (supports_inline_types()) {
+        if (!is_identity_class && !is_static && !is_strict) {
+          /* non-static value class fields must be be strict */
+          is_illegal = true;
+        } else if (is_abstract && !is_identity_class && !is_static) {
+          is_illegal = true;
+        }
+      }
     }
   }
 
   if (is_illegal) {
     ResourceMark rm(THREAD);
@@ -4617,11 +4872,11 @@
     return;
   }
 }
 
 void ClassFileParser::verify_legal_method_modifiers(jint flags,
-                                                    bool is_interface,
+                                                    AccessFlags class_access_flags,
                                                     const Symbol* name,
                                                     TRAPS) const {
   if (!_need_verify) { return; }
 
   const bool is_public       = (flags & JVM_ACC_PUBLIC)       != 0;
@@ -4636,13 +4891,19 @@
   const bool is_protected    = (flags & JVM_ACC_PROTECTED)    != 0;
   const bool major_gte_1_5   = _major_version >= JAVA_1_5_VERSION;
   const bool major_gte_8     = _major_version >= JAVA_8_VERSION;
   const bool major_gte_17    = _major_version >= JAVA_17_VERSION;
   const bool is_initializer  = (name == vmSymbols::object_initializer_name());
+  // LW401 CR required: removal of value factories support
+  const bool is_interface    = class_access_flags.is_interface();
+  const bool is_value_class  = !class_access_flags.is_identity_class();
+  const bool is_identity_class = class_access_flags.is_identity_class();
+  const bool is_abstract_class = class_access_flags.is_abstract();
 
   bool is_illegal = false;
 
+  const char* class_note = "";
   if (is_interface) {
     if (major_gte_8) {
       // Class file version is JAVA_8_VERSION or later Methods of
       // interfaces may set any of the flags except ACC_PROTECTED,
       // ACC_FINAL, ACC_NATIVE, and ACC_SYNCHRONIZED; they must
@@ -4678,27 +4939,41 @@
         if (is_static || is_final || is_synchronized || is_native ||
             is_abstract || (major_gte_1_5 && is_bridge)) {
           is_illegal = true;
         }
       } else { // not initializer
-        if (is_abstract) {
-          if ((is_final || is_native || is_private || is_static ||
-              (major_gte_1_5 && (is_synchronized || (!major_gte_17 && is_strict))))) {
-            is_illegal = true;
+        if (!is_identity_class && is_synchronized && !is_static) {
+          is_illegal = true;
+          class_note = " (not an identity class)";
+        } else {
+          if (is_abstract) {
+            if ((is_final || is_native || is_private || is_static ||
+                (major_gte_1_5 && (is_synchronized || (!major_gte_17 && is_strict))))) {
+              is_illegal = true;
+            }
           }
         }
       }
     }
   }
 
   if (is_illegal) {
     ResourceMark rm(THREAD);
-    Exceptions::fthrow(
-      THREAD_AND_LOCATION,
-      vmSymbols::java_lang_ClassFormatError(),
-      "Method %s in class %s has illegal modifiers: 0x%X",
-      name->as_C_string(), _class_name->as_C_string(), flags);
+    if (is_value_class && is_initializer) {
+      Exceptions::fthrow(
+        THREAD_AND_LOCATION,
+        vmSymbols::java_lang_ClassFormatError(),
+        "Method <init> is not allowed in value class %s",
+        _class_name->as_C_string());
+    } else {
+      Exceptions::fthrow(
+        THREAD_AND_LOCATION,
+        vmSymbols::java_lang_ClassFormatError(),
+        "Method %s in class %s%s has illegal modifiers: 0x%X",
+        name->as_C_string(), _class_name->as_C_string(),
+        class_note, flags);
+    }
     return;
   }
 }
 
 void ClassFileParser::verify_legal_utf8(const unsigned char* buffer,
@@ -4751,10 +5026,19 @@
     }
   }
   return true;
 }
 
+bool ClassFileParser::is_class_in_preload_attribute(Symbol *klass) {
+  if (_preload_classes == nullptr) return false;
+  for (int i = 0; i < _preload_classes->length(); i++) {
+        Symbol* class_name = _cp->klass_at_noresolve(_preload_classes->at(i));
+        if (class_name == klass) return true;
+  }
+  return false;
+}
+
 // Take pointer to a UTF8 byte string (not NUL-terminated).
 // Skip over the longest part of the string that could
 // be taken as a fieldname. Allow non-trailing '/'s if slash_ok is true.
 // Return a pointer to just past the fieldname.
 // Return null if no fieldname at all was found, or in the case of slash_ok
@@ -4852,22 +5136,23 @@
     case JVM_SIGNATURE_INT:
     case JVM_SIGNATURE_FLOAT:
     case JVM_SIGNATURE_LONG:
     case JVM_SIGNATURE_DOUBLE:
       return signature + 1;
-    case JVM_SIGNATURE_CLASS: {
+    case JVM_SIGNATURE_CLASS:
+    {
       if (_major_version < JAVA_1_5_VERSION) {
         // Skip over the class name if one is there
         const char* const p = skip_over_field_name(signature + 1, true, --length);
 
         // The next character better be a semicolon
         if (p && (p - signature) > 1 && p[0] == JVM_SIGNATURE_ENDCLASS) {
           return p + 1;
         }
       }
       else {
-        // Skip leading 'L' and ignore first appearance of ';'
+        // Skip leading 'L' or 'Q' and ignore first appearance of ';'
         signature++;
         const char* c = (const char*) memchr(signature, JVM_SIGNATURE_ENDCLASS, length - 1);
         // Format check signature
         if (c != nullptr) {
           int newlen = pointer_delta_as_int(c, (char*) signature);
@@ -4919,10 +5204,14 @@
     } else if (_major_version < JAVA_1_5_VERSION) {
       if (bytes[0] != JVM_SIGNATURE_SPECIAL) {
         p = skip_over_field_name(bytes, true, length);
         legal = (p != nullptr) && ((p - bytes) == (int)length);
       }
+    } else if ((_major_version >= CONSTANT_CLASS_DESCRIPTORS || _class_name->starts_with("jdk/internal/reflect/"))
+                   && bytes[length - 1] == ';' ) {
+      // Support for L...; and Q...; descriptors
+      legal = verify_unqualified_name(bytes + 1, length - 2, LegalClass);
     } else {
       // 4900761: relax the constraints based on JSR202 spec
       // Class names may be drawn from the entire Unicode character set.
       // Identifiers between '/' must be unqualified names.
       // The utf8 string has been verified when parsing cpool entries.
@@ -4984,11 +5273,12 @@
   unsigned int length = name->utf8_length();
   bool legal = false;
 
   if (length > 0) {
     if (bytes[0] == JVM_SIGNATURE_SPECIAL) {
-      if (name == vmSymbols::object_initializer_name() || name == vmSymbols::class_initializer_name()) {
+      if (name == vmSymbols::object_initializer_name() ||
+          name == vmSymbols::class_initializer_name()) {
         legal = true;
       }
     } else if (_major_version < JAVA_1_5_VERSION) {
       const char* p;
       p = skip_over_field_name(bytes, false, length);
@@ -5045,13 +5335,13 @@
     return;
   }
 
   int sig_length = signature->utf8_length();
   if (name->utf8_length() > 0 &&
-      name->char_at(0) == JVM_SIGNATURE_SPECIAL &&
-      sig_length > 0 &&
-      signature->char_at(sig_length - 1) != JVM_SIGNATURE_VOID) {
+    name->char_at(0) == JVM_SIGNATURE_SPECIAL &&
+    sig_length > 0 &&
+    signature->char_at(sig_length - 1) != JVM_SIGNATURE_VOID) {
     throwIllegalSignature("Method", name, signature, THREAD);
   }
 }
 
 // Checks if signature is a legal method signature.
@@ -5224,11 +5514,10 @@
   }
 
   fill_instance_klass(ik, changed_by_loadhook, cl_inst_info, CHECK_NULL);
 
   assert(_klass == ik, "invariant");
-
   return ik;
 }
 
 void ClassFileParser::fill_instance_klass(InstanceKlass* ik,
                                           bool changed_by_loadhook,
@@ -5260,16 +5549,23 @@
   ik->set_should_verify_class(_need_verify);
 
   // Not yet: supers are done below to support the new subtype-checking fields
   ik->set_nonstatic_field_size(_field_info->_nonstatic_field_size);
   ik->set_has_nonstatic_fields(_field_info->_has_nonstatic_fields);
+  if (_field_info->_is_naturally_atomic && ik->is_inline_klass()) {
+    ik->set_is_naturally_atomic();
+  }
+
   assert(_fac != nullptr, "invariant");
-  ik->set_static_oop_field_count(_fac->count[STATIC_OOP]);
+  ik->set_static_oop_field_count(_fac->count[STATIC_OOP] + _fac->count[STATIC_INLINE]);
 
   // this transfers ownership of a lot of arrays from
   // the parser onto the InstanceKlass*
   apply_parsed_class_metadata(ik, _java_fields_count);
+  if (ik->is_inline_klass()) {
+    InlineKlass::cast(ik)->init_fixed_block();
+  }
 
   // can only set dynamic nest-host after static nest information is set
   if (cl_inst_info.dynamic_nest_host() != nullptr) {
     ik->set_nest_host(cl_inst_info.dynamic_nest_host());
   }
@@ -5279,13 +5575,15 @@
   assert(nullptr == _fieldinfo_stream, "invariant");
   assert(nullptr == _fields_status, "invariant");
   assert(nullptr == _methods, "invariant");
   assert(nullptr == _inner_classes, "invariant");
   assert(nullptr == _nest_members, "invariant");
+  assert(nullptr == _preload_classes, "invariant");
   assert(nullptr == _combined_annotations, "invariant");
   assert(nullptr == _record_components, "invariant");
   assert(nullptr == _permitted_subclasses, "invariant");
+  assert(nullptr == _inline_type_field_klasses, "invariant");
 
   if (_has_localvariable_table) {
     ik->set_has_localvariable_table(true);
   }
 
@@ -5317,10 +5615,16 @@
   ik->set_minor_version(_minor_version);
   ik->set_major_version(_major_version);
   ik->set_has_nonstatic_concrete_methods(_has_nonstatic_concrete_methods);
   ik->set_declares_nonstatic_concrete_methods(_declares_nonstatic_concrete_methods);
 
+  if (_must_be_atomic) {
+    ik->set_must_be_atomic();
+  }
+  if (_is_implicitly_constructible) {
+    ik->set_is_implicitly_constructible();
+  }
   if (_is_hidden) {
     ik->set_is_hidden();
   }
 
   // Set PackageEntry for this_klass
@@ -5423,10 +5727,38 @@
       // We won a potential race
       JvmtiExport::add_default_read_edges(module_handle, THREAD);
     }
   }
 
+  bool all_fields_empty = true;
+  for (AllFieldStream fs(ik); !fs.done(); fs.next()) {
+    if (!fs.access_flags().is_static()) {
+      if (fs.field_descriptor().is_null_free_inline_type()) {
+        Klass* k = ik->inline_type_field_klasses_array()->at(fs.index());
+        assert(k->is_inline_klass(), "must be");
+        if (!InlineKlass::cast(k)->is_empty_inline_type()) { all_fields_empty = false; }
+      } else {
+        all_fields_empty = false;
+      }
+    } else if (is_inline_type() && (fs.name() == vmSymbols::default_value_name())) {
+      InlineKlass::cast(ik)->set_default_value_offset(ik->field_offset(fs.index()));
+    }
+  }
+
+  if (_is_empty_inline_type || (is_inline_type() && all_fields_empty)) {
+    ik->set_is_empty_inline_type();
+  }
+
+  if (is_inline_type()) {
+    InlineKlass* vk = InlineKlass::cast(ik);
+    vk->set_alignment(_alignment);
+    vk->set_first_field_offset(_first_field_offset);
+    vk->set_payload_size_in_bytes(_payload_size_in_bytes);
+    vk->set_internal_null_marker_offset(_internal_null_marker_offset);
+    InlineKlass::cast(ik)->initialize_calling_convention(CHECK);
+  }
+
   ClassLoadingService::notify_class_loaded(ik, false /* not shared class */);
 
   if (!is_internal()) {
     ik->print_class_load_logging(_loader_data, module_entry, _stream);
 
@@ -5514,12 +5846,14 @@
   _methods(nullptr),
   _inner_classes(nullptr),
   _nest_members(nullptr),
   _nest_host(0),
   _permitted_subclasses(nullptr),
+  _preload_classes(nullptr),
   _record_components(nullptr),
   _local_interfaces(nullptr),
+  _local_interface_indexes(nullptr),
   _transitive_interfaces(nullptr),
   _combined_annotations(nullptr),
   _class_annotations(nullptr),
   _class_type_annotations(nullptr),
   _fields_annotations(nullptr),
@@ -5527,10 +5861,12 @@
   _klass(nullptr),
   _klass_to_deallocate(nullptr),
   _parsed_annotations(nullptr),
   _fac(nullptr),
   _field_info(nullptr),
+  _inline_type_field_klasses(nullptr),
+  _null_marker_offsets(nullptr),
   _temp_field_info(nullptr),
   _method_ordering(nullptr),
   _all_mirandas(nullptr),
   _vtable_size(0),
   _itable_size(0),
@@ -5555,10 +5891,18 @@
   _has_nonstatic_concrete_methods(false),
   _declares_nonstatic_concrete_methods(false),
   _has_localvariable_table(false),
   _has_final_method(false),
   _has_contended_fields(false),
+  _has_inline_type_fields(false),
+  _has_nonstatic_fields(false),
+  _is_empty_inline_type(false),
+  _is_naturally_atomic(false),
+  _must_be_atomic(true),
+  _is_implicitly_constructible(false),
+  _has_loosely_consistent_annotation(false),
+  _has_implicitly_constructible_annotation(false),
   _has_finalizer(false),
   _has_empty_finalizer(false),
   _has_vanilla_constructor(false),
   _max_bootstrap_specifier_index(-1) {
 
@@ -5605,14 +5949,17 @@
   _fields_status = nullptr;
   _methods = nullptr;
   _inner_classes = nullptr;
   _nest_members = nullptr;
   _permitted_subclasses = nullptr;
+  _preload_classes = nullptr;
   _combined_annotations = nullptr;
   _class_annotations = _class_type_annotations = nullptr;
   _fields_annotations = _fields_type_annotations = nullptr;
   _record_components = nullptr;
+  _inline_type_field_klasses = nullptr;
+  _null_marker_offsets = nullptr;
 }
 
 // Destructor to clean up
 ClassFileParser::~ClassFileParser() {
   _class_name->decrement_refcount();
@@ -5627,10 +5974,18 @@
 
   if (_fields_status != nullptr) {
     MetadataFactory::free_array<FieldStatus>(_loader_data, _fields_status);
   }
 
+  if (_inline_type_field_klasses != nullptr) {
+     MetadataFactory::free_array<InlineKlass*>(_loader_data, _inline_type_field_klasses);
+  }
+
+  if (_null_marker_offsets != nullptr) {
+     MetadataFactory::free_array<int>(_loader_data, _null_marker_offsets);
+  }
+
   if (_methods != nullptr) {
     // Free methods
     InstanceKlass::deallocate_methods(_loader_data, _methods);
   }
 
@@ -5649,10 +6004,14 @@
 
   if (_permitted_subclasses != nullptr && _permitted_subclasses != Universe::the_empty_short_array()) {
     MetadataFactory::free_array<u2>(_loader_data, _permitted_subclasses);
   }
 
+  if (_preload_classes != nullptr && _preload_classes != Universe::the_empty_short_array()) {
+    MetadataFactory::free_array<u2>(_loader_data, _preload_classes);
+  }
+
   // Free interfaces
   InstanceKlass::deallocate_interfaces(_loader_data, _super_klass,
                                        _local_interfaces, _transitive_interfaces);
 
   if (_combined_annotations != nullptr) {
@@ -5733,35 +6092,33 @@
   assert(cp_size == (u2)cp->length(), "invariant");
 
   // ACCESS FLAGS
   stream->guarantee_more(8, CHECK);  // flags, this_class, super_class, infs_len
 
-  // Access flags
-  jint flags;
+  jint recognized_modifiers = JVM_RECOGNIZED_CLASS_MODIFIERS;
   // JVM_ACC_MODULE is defined in JDK-9 and later.
   if (_major_version >= JAVA_9_VERSION) {
-    flags = stream->get_u2_fast() & (JVM_RECOGNIZED_CLASS_MODIFIERS | JVM_ACC_MODULE);
-  } else {
-    flags = stream->get_u2_fast() & JVM_RECOGNIZED_CLASS_MODIFIERS;
+    recognized_modifiers |= JVM_ACC_MODULE;
   }
 
+  // Access flags
+  jint flags = stream->get_u2_fast() & recognized_modifiers;
+
   if ((flags & JVM_ACC_INTERFACE) && _major_version < JAVA_6_VERSION) {
     // Set abstract bit for old class files for backward compatibility
     flags |= JVM_ACC_ABSTRACT;
   }
 
-  verify_legal_class_modifiers(flags, CHECK);
-
-  short bad_constant = class_bad_constant_seen();
-  if (bad_constant != 0) {
-    // Do not throw CFE until after the access_flags are checked because if
-    // ACC_MODULE is set in the access flags, then NCDFE must be thrown, not CFE.
-    classfile_parse_error("Unknown constant tag %u in class file %s", bad_constant, THREAD);
-    return;
+  // Fixing ACC_SUPER/ACC_IDENTITY for old class files
+  if (!supports_inline_types()) {
+    const bool is_module = (flags & JVM_ACC_MODULE) != 0;
+    const bool is_interface = (flags & JVM_ACC_INTERFACE) != 0;
+    if (!is_module && !is_interface) {
+      flags |= JVM_ACC_IDENTITY;
+    }
   }
 
-  _access_flags.set_flags(flags);
 
   // This class and superclass
   _this_class_index = stream->get_u2_fast();
   check_property(
     valid_cp_range(_this_class_index, cp_size) &&
@@ -5770,10 +6127,24 @@
     _this_class_index, CHECK);
 
   Symbol* const class_name_in_cp = cp->klass_name_at(_this_class_index);
   assert(class_name_in_cp != nullptr, "class_name can't be null");
 
+  bool is_java_lang_Object = class_name_in_cp == vmSymbols::java_lang_Object();
+
+  verify_legal_class_modifiers(flags, nullptr, is_java_lang_Object, CHECK);
+
+  _access_flags.set_flags(flags);
+
+  short bad_constant = class_bad_constant_seen();
+  if (bad_constant != 0) {
+    // Do not throw CFE until after the access_flags are checked because if
+    // ACC_MODULE is set in the access flags, then NCDFE must be thrown, not CFE.
+    classfile_parse_error("Unknown constant tag %u in class file %s", bad_constant, THREAD);
+    return;
+  }
+
   // Don't need to check whether this class name is legal or not.
   // It has been checked when constant pool is parsed.
   // However, make sure it is not an array type.
   if (_need_verify) {
     guarantee_property(class_name_in_cp->char_at(0) != JVM_SIGNATURE_ARRAY,
@@ -5849,27 +6220,27 @@
                    _itfs_len,
                    cp,
                    &_has_nonstatic_concrete_methods,
                    CHECK);
 
-  assert(_local_interfaces != nullptr, "invariant");
-
   // Fields (offsets are filled in later)
   _fac = new FieldAllocationCount();
   parse_fields(stream,
-               _access_flags.is_interface(),
+               _access_flags,
                _fac,
                cp,
                cp_size,
                &_java_fields_count,
                CHECK);
 
   assert(_temp_field_info != nullptr, "invariant");
 
   // Methods
   parse_methods(stream,
-                _access_flags.is_interface(),
+                is_interface(),
+                !is_identity_class(),
+                is_abstract_class(),
                 &_has_localvariable_table,
                 &_has_final_method,
                 &_declares_nonstatic_concrete_methods,
                 CHECK);
 
@@ -5945,17 +6316,17 @@
   assert(cp != nullptr, "invariant");
   assert(_loader_data != nullptr, "invariant");
 
   if (_class_name == vmSymbols::java_lang_Object()) {
     check_property(_local_interfaces == Universe::the_empty_instance_klass_array(),
-                   "java.lang.Object cannot implement an interface in class file %s",
-                   CHECK);
+        "java.lang.Object cannot implement an interface in class file %s",
+        CHECK);
   }
   // We check super class after class file is parsed and format is checked
   if (_super_class_index > 0 && nullptr == _super_klass) {
     Symbol* const super_class_name = cp->klass_name_at(_super_class_index);
-    if (_access_flags.is_interface()) {
+    if (is_interface()) {
       // Before attempting to resolve the superclass, check for class format
       // errors not checked yet.
       guarantee_property(super_class_name == vmSymbols::java_lang_Object(),
         "Interfaces must have java.lang.Object as superclass in class file %s",
         CHECK);
@@ -5963,29 +6334,126 @@
     Handle loader(THREAD, _loader_data->class_loader());
     if (loader.is_null() && super_class_name == vmSymbols::java_lang_Object()) {
       _super_klass = vmClasses::Object_klass();
     } else {
       _super_klass = (const InstanceKlass*)
-                       SystemDictionary::resolve_super_or_fail(_class_name,
+                       SystemDictionary::resolve_with_circularity_detection_or_fail(_class_name,
                                                                super_class_name,
                                                                loader,
                                                                _protection_domain,
                                                                true,
                                                                CHECK);
     }
   }
 
   if (_super_klass != nullptr) {
+    if (_super_klass->is_interface()) {
+      classfile_icce_error("class %s has interface %s as super class", _super_klass, THREAD);
+      return;
+    }
+
+    if (EnableValhalla) {
+      check_identity_and_value_modifiers(this, _super_klass, CHECK);
+    }
+
     if (_super_klass->has_nonstatic_concrete_methods()) {
       _has_nonstatic_concrete_methods = true;
     }
 
-    if (_super_klass->is_interface()) {
-      classfile_icce_error("class %s has interface %s as super class", _super_klass, THREAD);
-      return;
+    if (EnableValhalla && !_access_flags.is_identity_class()) {
+      const InstanceKlass* k = _super_klass;
+      int inherited_instance_fields = 0;
+      while (k != nullptr) {
+        for (AllFieldStream fs(k); !fs.done(); fs.next()) {
+          if (!fs.access_flags().is_static()) inherited_instance_fields++;
+        }
+        k = k->super() == nullptr ? nullptr :  InstanceKlass::cast(k->super());
+      }
+      if (inherited_instance_fields > 0) {
+        THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), "Value classes don't support inherited non-static fields yet");
+      }
+    }
+  }
+
+  if (_parsed_annotations->has_annotation(AnnotationCollector::_jdk_internal_LooselyConsistentValue) && _access_flags.is_identity_class()) {
+    THROW_MSG(vmSymbols::java_lang_ClassFormatError(),
+          err_msg("class %s cannot have annotation jdk.internal.vm.annotation.LooselyConsistentValue, because it is not a value class",
+                  _class_name->as_klass_external_name()));
+  }
+  if (_parsed_annotations->has_annotation(AnnotationCollector::_jdk_internal_ImplicitlyConstructible) && _access_flags.is_identity_class()) {
+    THROW_MSG(vmSymbols::java_lang_ClassFormatError(),
+          err_msg("class %s cannot have annotation jdk.internal.vm.annotation.ImplicitlyConstructible, because it is not a value class",
+                  _class_name->as_klass_external_name()));
+  }
+
+  // Determining is the class allows tearing or not (default is not)
+  // Test might need extensions when field inheritance is added for value classes
+  if (EnableValhalla && !_access_flags.is_identity_class()) {
+    if (_super_klass != nullptr  // not j.l.Object
+              && _parsed_annotations->has_annotation(ClassAnnotationCollector::_jdk_internal_LooselyConsistentValue)
+              && (_super_klass == vmClasses::Object_klass() || !_super_klass->must_be_atomic())) {
+      _must_be_atomic = false;
+    }
+    if (_parsed_annotations->has_annotation(ClassAnnotationCollector::_jdk_internal_ImplicitlyConstructible)) {
+      _is_implicitly_constructible = true;
+    }
+    // Apply VM options override
+    if (*ForceNonTearable != '\0') {
+      // Allow a command line switch to force the same atomicity property:
+      const char* class_name_str = _class_name->as_C_string();
+      if (StringUtils::class_list_match(ForceNonTearable, class_name_str)) {
+        _must_be_atomic = true;
+      }
+    }
+  }
+
+  int itfs_len = _local_interface_indexes == nullptr ? 0 : _local_interface_indexes->length();
+  _local_interfaces = MetadataFactory::new_array<InstanceKlass*>(_loader_data, itfs_len, nullptr, CHECK);
+  if (_local_interface_indexes != nullptr) {
+    for (int i = 0; i < _local_interface_indexes->length(); i++) {
+      u2 interface_index = _local_interface_indexes->at(i);
+      Klass* interf;
+      if (cp->tag_at(interface_index).is_klass()) {
+        interf = cp->resolved_klass_at(interface_index);
+      } else {
+        Symbol* const unresolved_klass  = cp->klass_name_at(interface_index);
+
+        // Don't need to check legal name because it's checked when parsing constant pool.
+        // But need to make sure it's not an array type.
+        guarantee_property(unresolved_klass->char_at(0) != JVM_SIGNATURE_ARRAY,
+                            "Bad interface name in class file %s", CHECK);
+
+        // Call resolve_super so class circularity is checked
+        interf = SystemDictionary::resolve_with_circularity_detection_or_fail(
+                                                  _class_name,
+                                                  unresolved_klass,
+                                                  Handle(THREAD, _loader_data->class_loader()),
+                                                  _protection_domain,
+                                                  false,
+                                                  CHECK);
+      }
+
+      if (!interf->is_interface()) {
+        THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(),
+                  err_msg("class %s can not implement %s, because it is not an interface (%s)",
+                          _class_name->as_klass_external_name(),
+                          interf->external_name(),
+                          interf->class_in_module_of_loader()));
+      }
+
+      if (EnableValhalla) {
+        // Check modifiers and set carries_identity_modifier/carries_value_modifier flags
+        check_identity_and_value_modifiers(this, InstanceKlass::cast(interf), CHECK);
+      }
+
+      if (InstanceKlass::cast(interf)->has_nonstatic_concrete_methods()) {
+        _has_nonstatic_concrete_methods = true;
+      }
+      _local_interfaces->at_put(i, InstanceKlass::cast(interf));
     }
   }
+  assert(_local_interfaces != nullptr, "invariant");
 
   // Compute the transitive list of all unique interfaces implemented by this class
   _transitive_interfaces =
     compute_transitive_interfaces(_super_klass,
                                   _local_interfaces,
@@ -6010,28 +6478,124 @@
                                                     loader,
                                                     _class_name,
                                                     _local_interfaces);
 
   // Size of Java itable (in words)
-  _itable_size = _access_flags.is_interface() ? 0 :
+  _itable_size = is_interface() ? 0 :
     klassItable::compute_itable_size(_transitive_interfaces);
 
   assert(_fac != nullptr, "invariant");
   assert(_parsed_annotations != nullptr, "invariant");
 
+  if (EnableValhalla) {
+    _inline_type_field_klasses = MetadataFactory::new_array<InlineKlass*>(_loader_data,
+                                                   java_fields_count(),
+                                                   nullptr,
+                                                   CHECK);
+    for (GrowableArrayIterator<FieldInfo> it = _temp_field_info->begin(); it != _temp_field_info->end(); ++it) {
+      FieldInfo fieldinfo = *it;
+      if (fieldinfo.access_flags().is_static()) continue;  // Only non-static fields are processed at load time
+      Symbol* sig = fieldinfo.signature(cp);
+      if (fieldinfo.field_flags().is_null_free_inline_type()) {
+        // Pre-load classes of null-free fields that are candidate for flattening
+        TempNewSymbol s = Signature::strip_envelope(sig);
+        if (s == _class_name) {
+          THROW_MSG(vmSymbols::java_lang_ClassCircularityError(), err_msg("Class %s cannot have a null-free non-static field of its own type", _class_name->as_C_string()));
+        }
+        log_info(class, preload)("Preloading class %s during loading of class %s. Cause: a null-free non-static field is declared with this type", s->as_C_string(), _class_name->as_C_string());
+        Klass* klass = SystemDictionary::resolve_with_circularity_detection_or_fail(_class_name, s, Handle(THREAD, _loader_data->class_loader()), _protection_domain, false, THREAD);
+        if (HAS_PENDING_EXCEPTION) {
+          log_warning(class, preload)("Preloading of class %s during loading of class %s (cause: null-free non-static field) failed: %s",
+                                      s->as_C_string(), _class_name->as_C_string(), PENDING_EXCEPTION->klass()->name()->as_C_string());
+          return; // Exception is still pending
+        }
+        assert(klass != nullptr, "Sanity check");
+        if (klass->access_flags().is_identity_class()) {
+          assert(klass->is_instance_klass(), "Sanity check");
+          ResourceMark rm(THREAD);
+          THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(),
+                    err_msg("Class %s expects class %s to be a value class, but it is an identity class",
+                    _class_name->as_C_string(),
+                    InstanceKlass::cast(klass)->external_name()));
+        }
+        if (klass->is_abstract()) {
+          assert(klass->is_instance_klass(), "Sanity check");
+          ResourceMark rm(THREAD);
+          THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(),
+                    err_msg("Class %s expects class %s to be concrete value type, but it is an abstract class",
+                    _class_name->as_C_string(),
+                    InstanceKlass::cast(klass)->external_name()));
+        }
+        InlineKlass* vk = InlineKlass::cast(klass);
+        if (!vk->is_implicitly_constructible()) {
+          THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(),
+                    err_msg("class %s is not implicitly constructible and it is used in a null restricted non-static field (not supported)",
+                    klass->name()->as_C_string()));
+        }
+        _inline_type_field_klasses->at_put(fieldinfo.index(), vk);
+        log_info(class, preload)("Preloading of class %s during loading of class %s (cause: null-free non-static field) succeeded", s->as_C_string(), _class_name->as_C_string());
+      } else if (Signature::has_envelope(sig)) {
+        // Preloading classes for nullable fields that are listed in the Preload attribute
+        // Those classes would be required later for the flattening of nullable inline type fields
+        TempNewSymbol name = Signature::strip_envelope(sig);
+        if (name != _class_name && is_class_in_preload_attribute(name)) {
+          log_info(class, preload)("Preloading class %s during loading of class %s. Cause: field type in Preload attribute", name->as_C_string(), _class_name->as_C_string());
+          oop loader = loader_data()->class_loader();
+          Klass* klass = SystemDictionary::resolve_with_circularity_detection_or_fail(_class_name, name, Handle(THREAD, loader), _protection_domain, false, THREAD);
+          if (klass != nullptr) {
+            if (klass->is_inline_klass()) {
+              _inline_type_field_klasses->at_put(fieldinfo.index(), InlineKlass::cast(klass));
+              log_info(class, preload)("Preloading of class %s during loading of class %s (cause: field type in Preload attribute) succeeded", name->as_C_string(), _class_name->as_C_string());
+            } else {
+              // Non value class are allowed by the current spec, but it could be an indication of an issue so let's log a warning
+              log_warning(class, preload)("Preloading class %s during loading of class %s (cause: field type in Preload attribute) but loaded class is not a value class", name->as_C_string(), _class_name->as_C_string());
+            }
+            } else {
+            log_warning(class, preload)("Preloading of class %s during loading of class %s (cause: field type in Preload attribute) failed : %s",
+                                          name->as_C_string(), _class_name->as_C_string(), PENDING_EXCEPTION->klass()->name()->as_C_string());
+          }
+          // Loads triggered by the preload attribute are speculative, failures must not impact loading of current class
+          if (HAS_PENDING_EXCEPTION) {
+            CLEAR_PENDING_EXCEPTION;
+          }
+        }
+      }
+    }
+  }
+
   _field_info = new FieldLayoutInfo();
-  FieldLayoutBuilder lb(class_name(), super_klass(), _cp, /*_fields*/ _temp_field_info,
-                        _parsed_annotations->is_contended(), _field_info);
+  FieldLayoutBuilder lb(class_name(), loader_data(), super_klass(), _cp, /*_fields*/ _temp_field_info,
+      _parsed_annotations->is_contended(), is_inline_type(),
+      _field_info, _inline_type_field_klasses);
   lb.build_layout();
+  if (is_inline_type()) {
+    _alignment = lb.get_alignment();
+    _first_field_offset = lb.get_first_field_offset();
+    _payload_size_in_bytes = lb.get_payload_size_in_byte();
+    _internal_null_marker_offset = lb.get_internal_null_marker_offset();
+  }
+  _has_inline_type_fields = _field_info->_has_inline_fields;
 
   int injected_fields_count = _temp_field_info->length() - _java_fields_count;
   _fieldinfo_stream =
     FieldInfoStream::create_FieldInfoStream(_temp_field_info, _java_fields_count,
                                             injected_fields_count, loader_data(), CHECK);
+
   _fields_status =
     MetadataFactory::new_array<FieldStatus>(_loader_data, _temp_field_info->length(),
                                             FieldStatus(0), CHECK);
+  if (_field_info->_has_null_marker_offsets) {
+    int idx = 0;
+    _null_marker_offsets = MetadataFactory::new_array<int>(_loader_data, _temp_field_info->length(), 0, CHECK);
+    for (GrowableArrayIterator<FieldInfo> it = _temp_field_info->begin(); it != _temp_field_info->end(); ++it, ++idx) {
+      FieldInfo fieldinfo = *it;
+      if (fieldinfo.field_flags().has_null_marker()) {
+        assert(fieldinfo.null_marker_offset() != 0, "Invalid value");
+        _null_marker_offsets->at_put(idx, fieldinfo.null_marker_offset());
+      }
+    }
+  }
 }
 
 void ClassFileParser::set_klass(InstanceKlass* klass) {
 
 #ifdef ASSERT
diff a/src/hotspot/share/classfile/classFileParser.hpp b/src/hotspot/share/classfile/classFileParser.hpp
--- a/src/hotspot/share/classfile/classFileParser.hpp
+++ b/src/hotspot/share/classfile/classFileParser.hpp
@@ -72,11 +72,14 @@
  public:
   OopMapBlocksBuilder* oop_map_blocks;
   int _instance_size;
   int _nonstatic_field_size;
   int _static_field_size;
-  bool  _has_nonstatic_fields;
+  bool _has_nonstatic_fields;
+  bool _is_naturally_atomic;
+  bool _has_inline_fields;
+  bool _has_null_marker_offsets;
 };
 
 // Parser for for .class files
 //
 // The bytes describing the class file structure is read from a Stream object
@@ -127,12 +130,14 @@
   Array<Method*>* _methods;
   Array<u2>* _inner_classes;
   Array<u2>* _nest_members;
   u2 _nest_host;
   Array<u2>* _permitted_subclasses;
+  Array<u2>* _preload_classes;
   Array<RecordComponent*>* _record_components;
   Array<InstanceKlass*>* _local_interfaces;
+  GrowableArray<u2>* _local_interface_indexes;
   Array<InstanceKlass*>* _transitive_interfaces;
   Annotations* _combined_annotations;
   AnnotationArray* _class_annotations;
   AnnotationArray* _class_type_annotations;
   Array<AnnotationArray*>* _fields_annotations;
@@ -141,10 +146,12 @@
   InstanceKlass* _klass_to_deallocate; // an InstanceKlass* to be destroyed
 
   ClassAnnotationCollector* _parsed_annotations;
   FieldAllocationCount* _fac;
   FieldLayoutInfo* _field_info;
+  Array<InlineKlass*>* _inline_type_field_klasses;
+  Array<int>* _null_marker_offsets;
   GrowableArray<FieldInfo>* _temp_field_info;
   const intArray* _method_ordering;
   GrowableArray<Method*>* _all_mirandas;
 
   enum { fixed_buffer_size = 128 };
@@ -154,10 +161,15 @@
   int _vtable_size;
   int _itable_size;
 
   int _num_miranda_methods;
 
+  int _alignment;
+  int _first_field_offset;
+  int _payload_size_in_bytes;
+  int _internal_null_marker_offset;
+
   Handle _protection_domain;
   AccessFlags _access_flags;
 
   // for tracing and notifications
   Publicity _pub_level;
@@ -192,10 +204,20 @@
   bool _declares_nonstatic_concrete_methods;
   bool _has_localvariable_table;
   bool _has_final_method;
   bool _has_contended_fields;
 
+  bool _has_inline_type_fields;
+  bool _has_null_marker_offsets;
+  bool _has_nonstatic_fields;
+  bool _is_empty_inline_type;
+  bool _is_naturally_atomic;
+  bool _must_be_atomic;
+  bool _is_implicitly_constructible;
+  bool _has_loosely_consistent_annotation;
+  bool _has_implicitly_constructible_annotation;
+
   // precomputed flags
   bool _has_finalizer;
   bool _has_empty_finalizer;
   bool _has_vanilla_constructor;
   int _max_bootstrap_specifier_index;  // detects BSS values
@@ -258,26 +280,30 @@
                               u2* const generic_signature_index_addr,
                               FieldAnnotationCollector* parsed_annotations,
                               TRAPS);
 
   void parse_fields(const ClassFileStream* const cfs,
-                    bool is_interface,
+                    AccessFlags class_access_flags,
                     FieldAllocationCount* const fac,
                     ConstantPool* cp,
                     const int cp_size,
                     u2* const java_fields_count_ptr,
                     TRAPS);
 
   // Method parsing
   Method* parse_method(const ClassFileStream* const cfs,
                        bool is_interface,
+                       bool is_value_class,
+                       bool is_abstract_class,
                        const ConstantPool* cp,
                        bool* const has_localvariable_table,
                        TRAPS);
 
   void parse_methods(const ClassFileStream* const cfs,
                      bool is_interface,
+                     bool is_value_class,
+                     bool is_abstract_class,
                      bool* const has_localvariable_table,
                      bool* const has_final_method,
                      bool* const declares_nonstatic_concrete_methods,
                      TRAPS);
 
@@ -328,10 +354,14 @@
 
   u2 parse_classfile_permitted_subclasses_attribute(const ClassFileStream* const cfs,
                                                     const u1* const permitted_subclasses_attribute_start,
                                                     TRAPS);
 
+  u2 parse_classfile_preload_attribute(const ClassFileStream* const cfs,
+                                                    const u1* const preload_attribute_start,
+                                                    TRAPS);
+
   u4 parse_classfile_record_attribute(const ClassFileStream* const cfs,
                                       const ConstantPool* cp,
                                       const u1* const record_attribute_start,
                                       TRAPS);
 
@@ -446,10 +476,15 @@
   void throwIllegalSignature(const char* type,
                              const Symbol* name,
                              const Symbol* sig,
                              TRAPS) const;
 
+  void throwInlineTypeLimitation(THREAD_AND_LOCATION_DECL,
+                                 const char* msg,
+                                 const Symbol* name = nullptr,
+                                 const Symbol* sig  = nullptr) const;
+
   void verify_constantvalue(const ConstantPool* const cp,
                             int constantvalue_index,
                             int signature_index,
                             TRAPS) const;
 
@@ -468,14 +503,16 @@
                                         const Symbol* signature,
                                         TRAPS) const;
 
   void verify_class_version(u2 major, u2 minor, Symbol* class_name, TRAPS);
 
-  void verify_legal_class_modifiers(jint flags, TRAPS) const;
-  void verify_legal_field_modifiers(jint flags, bool is_interface, TRAPS) const;
+  void verify_legal_class_modifiers(jint flags, const char* name, bool is_Object, TRAPS) const;
+  void verify_legal_field_modifiers(jint flags,
+                                    AccessFlags class_access_flags,
+                                    TRAPS) const;
   void verify_legal_method_modifiers(jint flags,
-                                     bool is_interface,
+                                     AccessFlags class_access_flags,
                                      const Symbol* name,
                                      TRAPS) const;
 
   void check_super_class_access(const InstanceKlass* this_klass,
                                 TRAPS);
@@ -529,10 +566,13 @@
                                int annotation_default_length,
                                TRAPS);
 
   void update_class_name(Symbol* new_name);
 
+  // Check if the class file supports inline types
+  bool supports_inline_types() const;
+
  public:
   ClassFileParser(ClassFileStream* stream,
                   Symbol* name,
                   ClassLoaderData* loader_data,
                   const ClassLoadInfo* cl_info,
@@ -556,10 +596,16 @@
 
   u2 this_class_index() const { return _this_class_index; }
 
   bool is_hidden() const { return _is_hidden; }
   bool is_interface() const { return _access_flags.is_interface(); }
+  bool is_inline_type() const { return !_access_flags.is_identity_class() && !_access_flags.is_interface() && !_access_flags.is_abstract(); }
+  bool is_abstract_class() const { return _access_flags.is_abstract(); }
+  bool is_identity_class() const { return _access_flags.is_identity_class(); }
+  bool has_inline_fields() const { return _has_inline_type_fields; }
+
+  u2 java_fields_count() const { return _java_fields_count; }
 
   ClassLoaderData* loader_data() const { return _loader_data; }
   const Symbol* class_name() const { return _class_name; }
   const InstanceKlass* super_klass() const { return _super_klass; }
 
@@ -569,10 +615,12 @@
 
   AccessFlags access_flags() const { return _access_flags; }
 
   bool is_internal() const { return INTERNAL == _pub_level; }
 
+  bool is_class_in_preload_attribute(Symbol *klass);
+
   static bool verify_unqualified_name(const char* name, unsigned int length, int type);
 
 #ifdef ASSERT
   static bool is_internal_format(Symbol* class_name);
 #endif
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
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
+ * 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.
@@ -1099,10 +1099,11 @@
 
   // Lookup stream for parsing .class file
   ClassFileStream* stream = nullptr;
   s2 classpath_index = 0;
   ClassPathEntry* e = nullptr;
+  bool is_patched = false;
 
   // If search_append_only is true, boot loader visibility boundaries are
   // set to be _first_append_entry to the end. This includes:
   //   [-Xbootclasspath/a]; [jvmti appended entries]
   //
@@ -1123,16 +1124,20 @@
     // At CDS dump time, the --patch-module entries are ignored. That means a
     // class is still loaded from the runtime image even if it might
     // appear in the _patch_mod_entries. The runtime shared class visibility
     // check will determine if a shared class is visible based on the runtime
     // environment, including the runtime --patch-module setting.
-    //
-    // Dynamic dumping requires UseSharedSpaces to be enabled. Since --patch-module
-    // is not supported with UseSharedSpaces, we can never come here during dynamic dumping.
-    assert(!CDSConfig::is_dumping_dynamic_archive(), "sanity");
-    if (!CDSConfig::is_dumping_static_archive()) {
+    if (!CDSConfig::is_valhalla_preview()) {
+      // Dynamic dumping requires UseSharedSpaces to be enabled. Since --patch-module
+      // is not supported with UseSharedSpaces, we can never come here during dynamic dumping.
+      assert(!CDSConfig::is_dumping_dynamic_archive(), "sanity");
+    }
+    if (CDSConfig::is_valhalla_preview() || !CDSConfig::is_dumping_static_archive()) {
       stream = search_module_entries(THREAD, _patch_mod_entries, class_name, file_name);
+      if (stream != nullptr) {
+        is_patched = true;
+      }
     }
   }
 
   // Load Attempt #2: [jimage | exploded build]
   if (!search_append_only && (nullptr == stream)) {
@@ -1179,10 +1184,14 @@
                                                            name,
                                                            loader_data,
                                                            cl_info,
                                                            CHECK_NULL);
   result->set_classpath_index(classpath_index);
+  if (is_patched) {
+    result->set_shared_classpath_index(0);
+    result->set_shared_class_loader_type(ClassLoader::BOOT_LOADER);
+  }
   return result;
 }
 
 #if INCLUDE_CDS
 char* ClassLoader::skip_uri_protocol(char* source) {
@@ -1214,10 +1223,14 @@
   if (ik->is_hidden()) {
     // We do not archive hidden classes.
     return;
   }
 
+  if (ik->shared_classpath_index() == 0 && ik->is_shared_boot_class()) {
+    return;
+  }
+
   oop loader = ik->class_loader();
   char* src = (char*)stream->source();
   if (src == nullptr) {
     if (loader == nullptr) {
       // JFR classes
@@ -1301,11 +1314,13 @@
     }
   } else {
     // The shared path table is set up after module system initialization.
     // The path table contains no entry before that. Any classes loaded prior
     // to the setup of the shared path table must be from the modules image.
-    assert(stream->from_boot_loader_modules_image(), "stream must be loaded by boot loader from modules image");
+    if (!CDSConfig::is_valhalla_preview()) {
+      assert(stream->from_boot_loader_modules_image(), "stream must be loaded by boot loader from modules image");
+    }
     assert(FileMapInfo::get_number_of_shared_paths() == 0, "shared path table must not have been setup");
     classpath_index = 0;
   }
 
   const char* const class_name = ik->name()->as_C_string();
diff a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp
--- a/src/hotspot/share/classfile/classLoaderData.cpp
+++ b/src/hotspot/share/classfile/classLoaderData.cpp
@@ -64,10 +64,11 @@
 #include "memory/metadataFactory.hpp"
 #include "memory/metaspace.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
 #include "oops/access.inline.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/oopHandle.inline.hpp"
 #include "oops/verifyOopClosure.hpp"
 #include "oops/weakHandle.inline.hpp"
@@ -445,10 +446,20 @@
     }
     assert(k != k->next_link(), "no loops!");
   }
 }
 
+void ClassLoaderData::inline_classes_do(void f(InlineKlass*)) {
+  // Lock-free access requires load_acquire
+  for (Klass* k = Atomic::load_acquire(&_klasses); k != nullptr; k = k->next_link()) {
+    if (k->is_inline_klass()) {
+      f(InlineKlass::cast(k));
+    }
+    assert(k != k->next_link(), "no loops!");
+  }
+}
+
 void ClassLoaderData::modules_do(void f(ModuleEntry*)) {
   assert_locked_or_safepoint(Module_lock);
   if (_unnamed_module != nullptr) {
     f(_unnamed_module);
   }
@@ -596,10 +607,12 @@
 
   // Some items on the _deallocate_list need to free their C heap structures
   // if they are not already on the _klasses list.
   free_deallocate_list_C_heap_structures();
 
+  inline_classes_do(InlineKlass::cleanup);
+
   // Clean up class dependencies and tell serviceability tools
   // these classes are unloading.  This must be called
   // after erroneous classes are released.
   classes_do(InstanceKlass::unload_class);
 
@@ -890,11 +903,15 @@
       if (m->is_method()) {
         MetadataFactory::free_metadata(this, (Method*)m);
       } else if (m->is_constantPool()) {
         MetadataFactory::free_metadata(this, (ConstantPool*)m);
       } else if (m->is_klass()) {
-        MetadataFactory::free_metadata(this, (InstanceKlass*)m);
+        if (!((Klass*)m)->is_inline_klass()) {
+          MetadataFactory::free_metadata(this, (InstanceKlass*)m);
+        } else {
+          MetadataFactory::free_metadata(this, (InlineKlass*)m);
+        }
       } else {
         ShouldNotReachHere();
       }
     } else {
       // Metadata is alive.
diff a/src/hotspot/share/classfile/classLoaderData.hpp b/src/hotspot/share/classfile/classLoaderData.hpp
--- a/src/hotspot/share/classfile/classLoaderData.hpp
+++ b/src/hotspot/share/classfile/classLoaderData.hpp
@@ -212,10 +212,11 @@
  private:
   bool keep_alive() const       { return _keep_alive > 0; }
 
   void loaded_classes_do(KlassClosure* klass_closure);
   void classes_do(void f(InstanceKlass*));
+  void inline_classes_do(void f(InlineKlass*));
   void methods_do(void f(Method*));
   void modules_do(void f(ModuleEntry*));
   void packages_do(void f(PackageEntry*));
 
   // Deallocate free list during class unloading.
diff a/src/hotspot/share/classfile/fieldLayoutBuilder.cpp b/src/hotspot/share/classfile/fieldLayoutBuilder.cpp
--- a/src/hotspot/share/classfile/fieldLayoutBuilder.cpp
+++ b/src/hotspot/share/classfile/fieldLayoutBuilder.cpp
@@ -23,45 +23,53 @@
  */
 
 #include "precompiled.hpp"
 #include "classfile/classFileParser.hpp"
 #include "classfile/fieldLayoutBuilder.hpp"
+#include "classfile/systemDictionary.hpp"
+#include "classfile/vmSymbols.hpp"
 #include "jvm.h"
 #include "memory/resourceArea.hpp"
 #include "oops/array.hpp"
 #include "oops/fieldStreams.inline.hpp"
 #include "oops/instanceMirrorKlass.hpp"
 #include "oops/instanceKlass.inline.hpp"
 #include "oops/klass.inline.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "runtime/fieldDescriptor.inline.hpp"
 
-
 LayoutRawBlock::LayoutRawBlock(Kind kind, int size) :
   _next_block(nullptr),
   _prev_block(nullptr),
+  _inline_klass(nullptr),
   _kind(kind),
   _offset(-1),
   _alignment(1),
   _size(size),
   _field_index(-1),
-  _is_reference(false) {
-  assert(kind == EMPTY || kind == RESERVED || kind == PADDING || kind == INHERITED,
+  _null_marker_offset(-1),
+  _is_reference(false),
+  _needs_null_marker(false) {
+  assert(kind == EMPTY || kind == RESERVED || kind == PADDING || kind == INHERITED || kind == NULL_MARKER,
          "Otherwise, should use the constructor with a field index argument");
   assert(size > 0, "Sanity check");
 }
 
 
 LayoutRawBlock::LayoutRawBlock(int index, Kind kind, int size, int alignment, bool is_reference) :
  _next_block(nullptr),
  _prev_block(nullptr),
+ _inline_klass(nullptr),
  _kind(kind),
  _offset(-1),
  _alignment(alignment),
  _size(size),
  _field_index(index),
- _is_reference(is_reference) {
-  assert(kind == REGULAR || kind == FLATTENED || kind == INHERITED,
+ _null_marker_offset(-1),
+ _is_reference(is_reference),
+ _needs_null_marker(false) {
+  assert(kind == REGULAR || kind == FLAT || kind == INHERITED || kind == INHERITED_NULL_MARKER,
          "Other kind do not have a field index");
   assert(size > 0, "Sanity check");
   assert(alignment > 0, "Sanity check");
 }
 
@@ -73,22 +81,24 @@
   return _size >= size + adjustment;
 }
 
 FieldGroup::FieldGroup(int contended_group) :
   _next(nullptr),
-  _primitive_fields(nullptr),
+  _small_primitive_fields(nullptr),
+  _big_primitive_fields(nullptr),
   _oop_fields(nullptr),
   _contended_group(contended_group),  // -1 means no contended group, 0 means default contended group
   _oop_count(0) {}
 
 void FieldGroup::add_primitive_field(int idx, BasicType type) {
   int size = type2aelembytes(type);
   LayoutRawBlock* block = new LayoutRawBlock(idx, LayoutRawBlock::REGULAR, size, size /* alignment == size for primitive types */, false);
-  if (_primitive_fields == nullptr) {
-    _primitive_fields = new GrowableArray<LayoutRawBlock*>(INITIAL_LIST_SIZE);
+  if (size >= oopSize) {
+    add_to_big_primitive_list(block);
+  } else {
+    add_to_small_primitive_list(block);
   }
-  _primitive_fields->append(block);
 }
 
 void FieldGroup::add_oop_field(int idx) {
   int size = type2aelembytes(T_OBJECT);
   LayoutRawBlock* block = new LayoutRawBlock(idx, LayoutRawBlock::REGULAR, size, size /* alignment == size for oops */, true);
@@ -97,22 +107,51 @@
   }
   _oop_fields->append(block);
   _oop_count++;
 }
 
+void FieldGroup::add_flat_field(int idx, InlineKlass* vk, bool needs_null_marker) {
+  LayoutRawBlock* block = new LayoutRawBlock(idx, LayoutRawBlock::FLAT, vk->get_payload_size_in_bytes(), vk->get_alignment(), false);
+  block->set_inline_klass(vk);
+  if (needs_null_marker) block->set_needs_null_marker();
+  if (block->size() >= oopSize) {
+    add_to_big_primitive_list(block);
+  } else {
+    add_to_small_primitive_list(block);
+  }
+}
+
 void FieldGroup::sort_by_size() {
-  if (_primitive_fields != nullptr) {
-    _primitive_fields->sort(LayoutRawBlock::compare_size_inverted);
+  if (_small_primitive_fields != nullptr) {
+    _small_primitive_fields->sort(LayoutRawBlock::compare_size_inverted);
+  }
+  if (_big_primitive_fields != nullptr) {
+    _big_primitive_fields->sort(LayoutRawBlock::compare_size_inverted);
+  }
+}
+
+void FieldGroup::add_to_small_primitive_list(LayoutRawBlock* block) {
+  if (_small_primitive_fields == nullptr) {
+    _small_primitive_fields = new GrowableArray<LayoutRawBlock*>(INITIAL_LIST_SIZE);
   }
+  _small_primitive_fields->append(block);
+}
+
+void FieldGroup::add_to_big_primitive_list(LayoutRawBlock* block) {
+  if (_big_primitive_fields == nullptr) {
+    _big_primitive_fields = new GrowableArray<LayoutRawBlock*>(INITIAL_LIST_SIZE);
+  }
+  _big_primitive_fields->append(block);
 }
 
 FieldLayout::FieldLayout(GrowableArray<FieldInfo>* field_info, ConstantPool* cp) :
   _field_info(field_info),
   _cp(cp),
   _blocks(nullptr),
   _start(_blocks),
-  _last(_blocks) {}
+  _last(_blocks),
+  _has_missing_null_markers(false) {}
 
 void FieldLayout::initialize_static_layout() {
   _blocks = new LayoutRawBlock(LayoutRawBlock::EMPTY, INT_MAX);
   _blocks->set_offset(0);
   _last = _blocks;
@@ -136,29 +175,31 @@
     insert(first_empty_block(), new LayoutRawBlock(LayoutRawBlock::RESERVED, instanceOopDesc::base_offset_in_bytes()));
   } else {
     bool has_fields = reconstruct_layout(super_klass);
     fill_holes(super_klass);
     if ((UseEmptySlotsInSupers && !super_klass->has_contended_annotations()) || !has_fields) {
-      _start = _blocks;  // start allocating fields from the first empty block
+      _start = _blocks; // Setting _start to _blocks instead of _last would allow subclasses
+      // to allocate fields in empty slots of their super classes
     } else {
       _start = _last;    // append fields at the end of the reconstructed layout
     }
   }
 }
 
 LayoutRawBlock* FieldLayout::first_field_block() {
-  LayoutRawBlock* block = _start;
-  while (block->kind() != LayoutRawBlock::INHERITED && block->kind() != LayoutRawBlock::REGULAR
-      && block->kind() != LayoutRawBlock::FLATTENED && block->kind() != LayoutRawBlock::PADDING) {
+  LayoutRawBlock* block = _blocks;
+  while (block != nullptr
+         && block->kind() != LayoutRawBlock::INHERITED
+         && block->kind() != LayoutRawBlock::REGULAR
+         && block->kind() != LayoutRawBlock::FLAT) {
     block = block->next_block();
   }
   return block;
 }
 
-
-// Insert a set of fields into a layout using a best-fit strategy.
-// For each field, search for the smallest empty slot able to fit the field
+// Insert a set of fields into a layout.
+// For each field, search for an empty slot able to fit the field
 // (satisfying both size and alignment requirements), if none is found,
 // add the field at the end of the layout.
 // Fields cannot be inserted before the block specified in the "start" argument
 void FieldLayout::add(GrowableArray<LayoutRawBlock*>* list, LayoutRawBlock* start) {
   if (list == nullptr) return;
@@ -168,11 +209,10 @@
   int last_alignment = 0;
   for (int i = 0; i < list->length(); i ++) {
     LayoutRawBlock* b = list->at(i);
     LayoutRawBlock* cursor = nullptr;
     LayoutRawBlock* candidate = nullptr;
-
     // if start is the last block, just append the field
     if (start == last_block()) {
       candidate = last_block();
     }
     // Before iterating over the layout to find an empty slot fitting the field's requirements,
@@ -186,10 +226,11 @@
       last_size = b->size();
       last_alignment = b->alignment();
       cursor = last_block()->prev_block();
       assert(cursor != nullptr, "Sanity check");
       last_search_success = true;
+
       while (cursor != start) {
         if (cursor->kind() == LayoutRawBlock::EMPTY && cursor->fit(b->size(), b->alignment())) {
           if (candidate == nullptr || cursor->size() < candidate->size()) {
             candidate = cursor;
           }
@@ -202,11 +243,10 @@
       }
       assert(candidate != nullptr, "Candidate must not be null");
       assert(candidate->kind() == LayoutRawBlock::EMPTY, "Candidate must be an empty block");
       assert(candidate->fit(b->size(), b->alignment()), "Candidate must be able to store the block");
     }
-
     insert_field_block(candidate, b);
   }
 }
 
 // Used for classes with hard coded field offsets, insert a field at the specified offset */
@@ -285,14 +325,21 @@
     int adjustment = block->alignment() - (slot->offset() % block->alignment());
     LayoutRawBlock* adj = new LayoutRawBlock(LayoutRawBlock::EMPTY, adjustment);
     insert(slot, adj);
   }
   insert(slot, block);
+  if (block->needs_null_marker()) {
+    _has_missing_null_markers = true;
+  }
   if (slot->size() == 0) {
     remove(slot);
   }
-  _field_info->adr_at(block->field_index())->set_offset(block->offset());
+  // NULL_MARKER blocks have a field index pointing to the field that needs a null marker,
+  // so the field_info at this index must not be updated with the null marker's offset
+  if (block->kind() != LayoutRawBlock::NULL_MARKER) {
+    _field_info->adr_at(block->field_index())->set_offset(block->offset());
+  }
   return block;
 }
 
 bool FieldLayout::reconstruct_layout(const InstanceKlass* ik) {
   bool has_instance_fields = false;
@@ -301,24 +348,36 @@
     for (AllFieldStream fs(ik->fieldinfo_stream(), ik->constants()); !fs.done(); fs.next()) {
       BasicType type = Signature::basic_type(fs.signature());
       // distinction between static and non-static fields is missing
       if (fs.access_flags().is_static()) continue;
       has_instance_fields = true;
-      int size = type2aelembytes(type);
-      // INHERITED blocks are marked as non-reference because oop_maps are handled by their holder class
-      LayoutRawBlock* block = new LayoutRawBlock(fs.index(), LayoutRawBlock::INHERITED, size, size, false);
+      LayoutRawBlock* block;
+      if (fs.is_flat()) {
+        InlineKlass* vk = InlineKlass::cast(ik->get_inline_type_field_klass(fs.index()));
+        block = new LayoutRawBlock(fs.index(), LayoutRawBlock::INHERITED, vk->get_payload_size_in_bytes(),
+                                   vk->get_alignment(), false);
+        if (!fs.field_flags().is_null_free_inline_type()) {
+          assert(fs.field_flags().has_null_marker(), "Nullable flat fields must have a null marker");
+          LayoutRawBlock* marker = new LayoutRawBlock(fs.index(), LayoutRawBlock::INHERITED_NULL_MARKER, 1 /* current NULL_MARKER block are one byte */,
+                                    1, false);
+          marker->set_offset(fs.null_marker_offset());
+          all_fields->append(marker);
+        }
+      } else {
+        int size = type2aelembytes(type);
+        // INHERITED blocks are marked as non-reference because oop_maps are handled by their holder class
+        block = new LayoutRawBlock(fs.index(), LayoutRawBlock::INHERITED, size, size, false);
+      }
       block->set_offset(fs.offset());
       all_fields->append(block);
     }
     ik = ik->super() == nullptr ? nullptr : InstanceKlass::cast(ik->super());
   }
-
   all_fields->sort(LayoutRawBlock::compare_offset);
   _blocks = new LayoutRawBlock(LayoutRawBlock::RESERVED, instanceOopDesc::base_offset_in_bytes());
   _blocks->set_offset(0);
   _last = _blocks;
-
   for(int i = 0; i < all_fields->length(); i++) {
     LayoutRawBlock* b = all_fields->at(i);
     _last->set_next_block(b);
     b->set_prev_block(_last);
     _last = b;
@@ -350,22 +409,20 @@
     }
     b = b->next_block();
   }
   assert(b->next_block() == nullptr, "Invariant at this point");
   assert(b->kind() != LayoutRawBlock::EMPTY, "Sanity check");
-
   // If the super class has @Contended annotation, a padding block is
   // inserted at the end to ensure that fields from the subclasses won't share
   // the cache line of the last field of the contended class
   if (super_klass->has_contended_annotations() && ContendedPaddingWidth > 0) {
     LayoutRawBlock* p = new LayoutRawBlock(LayoutRawBlock::PADDING, ContendedPaddingWidth);
     p->set_offset(b->offset() + b->size());
     b->set_next_block(p);
     p->set_prev_block(b);
     b = p;
   }
-
   if (!UseEmptySlotsInSupers) {
     // Add an empty slots to align fields of the subclass on a heapOopSize boundary
     // in order to emulate the behavior of the previous algorithm
     int align = (b->offset() + b->size()) % heapOopSize;
     if (align != 0) {
@@ -375,11 +432,10 @@
       b->set_next_block(p);
       p->set_prev_block(b);
       b = p;
     }
   }
-
   LayoutRawBlock* last = new LayoutRawBlock(LayoutRawBlock::EMPTY, INT_MAX);
   last->set_offset(b->offset() + b->size());
   assert(last->offset() > 0, "Sanity check");
   b->set_next_block(last);
   last->set_prev_block(b);
@@ -401,10 +457,13 @@
     block->prev_block()->set_next_block(block);
   }
   if (_blocks == slot) {
     _blocks = block;
   }
+  if (_start == slot) {
+    _start = block;
+  }
   return block;
 }
 
 void FieldLayout::remove(LayoutRawBlock* block) {
   assert(block != nullptr, "Sanity check");
@@ -422,101 +481,142 @@
   if (block == _start) {
     _start = block->prev_block();
   }
 }
 
-void FieldLayout::print(outputStream* output, bool is_static, const InstanceKlass* super) {
+void FieldLayout::print(outputStream* output, bool is_static, const InstanceKlass* super, Array<InlineKlass*>* inline_fields) {
   ResourceMark rm;
   LayoutRawBlock* b = _blocks;
   while(b != _last) {
     switch(b->kind()) {
       case LayoutRawBlock::REGULAR: {
         FieldInfo* fi = _field_info->adr_at(b->field_index());
-        output->print_cr(" @%d \"%s\" %s %d/%d %s",
+        output->print_cr(" @%d %s %d/%d \"%s\" %s",
                          b->offset(),
-                         fi->name(_cp)->as_C_string(),
-                         fi->signature(_cp)->as_C_string(),
+                         "REGULAR",
                          b->size(),
                          b->alignment(),
-                         "REGULAR");
+                         fi->name(_cp)->as_C_string(),
+                         fi->signature(_cp)->as_C_string());
         break;
       }
-      case LayoutRawBlock::FLATTENED: {
+      case LayoutRawBlock::FLAT: {
         FieldInfo* fi = _field_info->adr_at(b->field_index());
-        output->print_cr(" @%d \"%s\" %s %d/%d %s",
+        InlineKlass* ik = inline_fields->at(fi->index());
+        assert(ik != nullptr, "");
+        output->print(" @%d %s %d/%d \"%s\" %s %s@%p",
                          b->offset(),
-                         fi->name(_cp)->as_C_string(),
-                         fi->signature(_cp)->as_C_string(),
+                         "FLAT",
                          b->size(),
                          b->alignment(),
-                         "FLATTENED");
+                         fi->name(_cp)->as_C_string(),
+                         fi->signature(_cp)->as_C_string(),
+                         ik->name()->as_C_string(),
+                         ik->class_loader_data());
+        if (fi->field_flags().has_null_marker()) {
+          output->print_cr(" null marker offset %d %s", fi->null_marker_offset(),
+                           fi->field_flags().is_null_marker_internal() ? "(internal)" : "");
+        } else {
+          output->print_cr("");
+        }
         break;
       }
       case LayoutRawBlock::RESERVED: {
-        output->print_cr(" @%d %d/- %s",
+        output->print_cr(" @%d %s %d/-",
                          b->offset(),
-                         b->size(),
-                         "RESERVED");
+                         "RESERVED",
+                         b->size());
         break;
       }
       case LayoutRawBlock::INHERITED: {
         assert(!is_static, "Static fields are not inherited in layouts");
         assert(super != nullptr, "super klass must be provided to retrieve inherited fields info");
         bool found = false;
         const InstanceKlass* ik = super;
         while (!found && ik != nullptr) {
           for (AllFieldStream fs(ik->fieldinfo_stream(), ik->constants()); !fs.done(); fs.next()) {
-            if (fs.offset() == b->offset()) {
-              output->print_cr(" @%d \"%s\" %s %d/%d %s",
+            if (fs.offset() == b->offset() && fs.access_flags().is_static() == is_static) {
+              output->print_cr(" @%d %s %d/%d \"%s\" %s",
                   b->offset(),
-                  fs.name()->as_C_string(),
-                  fs.signature()->as_C_string(),
+                  "INHERITED",
                   b->size(),
-                  b->size(), // so far, alignment constraint == size, will change with Valhalla
-                  "INHERITED");
+                  b->size(), // so far, alignment constraint == size, will change with Valhalla => FIXME
+                  fs.name()->as_C_string(),
+                  fs.signature()->as_C_string());
               found = true;
               break;
             }
-          }
-          ik = ik->java_super();
         }
-        break;
+        ik = ik->java_super();
       }
-      case LayoutRawBlock::EMPTY:
-        output->print_cr(" @%d %d/1 %s",
-                         b->offset(),
-                         b->size(),
-                        "EMPTY");
-        break;
-      case LayoutRawBlock::PADDING:
-        output->print_cr(" @%d %d/1 %s",
-                         b->offset(),
-                         b->size(),
-                        "PADDING");
-        break;
+      break;
+    }
+    case LayoutRawBlock::INHERITED_NULL_MARKER :
+      output->print_cr(" @%d %s %d/1",
+                       b->offset(),
+                      "INHERITED_NULL_MARKER",
+                       b->size());
+      break;
+    case LayoutRawBlock::EMPTY:
+      output->print_cr(" @%d %s %d/1",
+                       b->offset(),
+                      "EMPTY",
+                       b->size());
+      break;
+    case LayoutRawBlock::PADDING:
+      output->print_cr(" @%d %s %d/1",
+                      b->offset(),
+                      "PADDING",
+                      b->size());
+      break;
+    case LayoutRawBlock::NULL_MARKER:
+    {
+      FieldInfo* fi = _field_info->adr_at(b->field_index());
+      output->print_cr(" @%d %s %d/1 null marker for field at offset %d",
+                      b->offset(),
+                      "NULL_MARKER",
+                      b->size(),
+                      fi->offset());
+      break;
+    }
+    default:
+      fatal("Unknown block type");
     }
     b = b->next_block();
   }
 }
 
-FieldLayoutBuilder::FieldLayoutBuilder(const Symbol* classname, const InstanceKlass* super_klass, ConstantPool* constant_pool,
-      GrowableArray<FieldInfo>* field_info, bool is_contended, FieldLayoutInfo* info) :
+FieldLayoutBuilder::FieldLayoutBuilder(const Symbol* classname, ClassLoaderData* loader_data, const InstanceKlass* super_klass, ConstantPool* constant_pool,
+                                       GrowableArray<FieldInfo>* field_info, bool is_contended, bool is_inline_type,
+                                       FieldLayoutInfo* info, Array<InlineKlass*>* inline_type_field_klasses) :
   _classname(classname),
+  _loader_data(loader_data),
   _super_klass(super_klass),
   _constant_pool(constant_pool),
   _field_info(field_info),
   _info(info),
+  _inline_type_field_klasses(inline_type_field_klasses),
   _root_group(nullptr),
   _contended_groups(GrowableArray<FieldGroup*>(8)),
   _static_fields(nullptr),
   _layout(nullptr),
   _static_layout(nullptr),
   _nonstatic_oopmap_count(0),
   _alignment(-1),
+  _first_field_offset(-1),
+  _internal_null_marker_offset(-1),
+  _payload_size_in_bytes(-1),
+  _atomic_field_count(0),
+  _fields_size_sum(0),
   _has_nonstatic_fields(false),
-  _is_contended(is_contended) {}
-
+  _has_inline_type_fields(false),
+  _is_contended(is_contended),
+  _is_inline_type(is_inline_type),
+  _has_flattening_information(is_inline_type),
+  _has_nonatomic_values(false),
+  _nullable_atomic_flat_candidate(false),
+  _has_null_markers(false) {}
 
 FieldGroup* FieldLayoutBuilder::get_or_create_contended_group(int g) {
   assert(g > 0, "must only be called for named contended groups");
   FieldGroup* fg = nullptr;
   for (int i = 0; i < _contended_groups.length(); i++) {
@@ -539,25 +639,26 @@
   _static_layout->initialize_static_layout();
   _static_fields = new FieldGroup();
   _root_group = new FieldGroup();
 }
 
-// Field sorting for regular classes:
+// Field sorting for regular (non-inline) classes:
 //   - fields are sorted in static and non-static fields
 //   - non-static fields are also sorted according to their contention group
 //     (support of the @Contended annotation)
 //   - @Contended annotation is ignored for static fields
+//   - field flattening decisions are taken in this method
 void FieldLayoutBuilder::regular_field_sorting() {
   int idx = 0;
   for (GrowableArrayIterator<FieldInfo> it = _field_info->begin(); it != _field_info->end(); ++it, ++idx) {
-    FieldInfo ctrl = _field_info->at(0);
     FieldGroup* group = nullptr;
     FieldInfo fieldinfo = *it;
     if (fieldinfo.access_flags().is_static()) {
       group = _static_fields;
     } else {
       _has_nonstatic_fields = true;
+      _atomic_field_count++;  // we might decrement this
       if (fieldinfo.field_flags().is_contended()) {
         int g = fieldinfo.contended_group();
         if (g == 0) {
           group = new FieldGroup(true);
           _contended_groups.append(group);
@@ -569,27 +670,76 @@
       }
     }
     assert(group != nullptr, "invariant");
     BasicType type = Signature::basic_type(fieldinfo.signature(_constant_pool));
     switch(type) {
-      case T_BYTE:
-      case T_CHAR:
-      case T_DOUBLE:
-      case T_FLOAT:
-      case T_INT:
-      case T_LONG:
-      case T_SHORT:
-      case T_BOOLEAN:
-        group->add_primitive_field(idx, type);
-        break;
-      case T_OBJECT:
-      case T_ARRAY:
+    case T_BYTE:
+    case T_CHAR:
+    case T_DOUBLE:
+    case T_FLOAT:
+    case T_INT:
+    case T_LONG:
+    case T_SHORT:
+    case T_BOOLEAN:
+      group->add_primitive_field(idx, type);
+      break;
+    case T_OBJECT:
+    case T_ARRAY:
+    {
+      bool field_is_known_value_class =  !fieldinfo.field_flags().is_injected() && _inline_type_field_klasses != nullptr && _inline_type_field_klasses->at(fieldinfo.index()) != nullptr;
+      bool value_has_oops = field_is_known_value_class ? _inline_type_field_klasses->at(fieldinfo.index())->nonstatic_oop_count() > 0 : true;
+      bool is_candidate_for_flattening = fieldinfo.field_flags().is_null_free_inline_type() || (EnableNullableFieldFlattening && field_is_known_value_class && !value_has_oops);
+      // if (!fieldinfo.field_flags().is_null_free_inline_type()) {
+      if (!is_candidate_for_flattening) {
         if (group != _static_fields) _nonstatic_oopmap_count++;
         group->add_oop_field(idx);
-        break;
-      default:
-        fatal("Something wrong?");
+      } else {
+        assert(type != T_ARRAY, "null free ptr to array not supported");
+        _has_inline_type_fields = true;
+        if (group == _static_fields) {
+          // static fields are never flat
+          group->add_oop_field(idx);
+        } else {
+          // Check below is performed for non-static fields, it should be performed for static fields too but at this stage,
+          // it is not guaranteed that the klass of the static field has been loaded, so the test for static fields is delayed
+          // until the linking phase
+          Klass* klass =  _inline_type_field_klasses->at(idx);
+          assert(klass != nullptr, "Sanity check");
+          InlineKlass* vk = InlineKlass::cast(klass);
+          assert(!fieldinfo.field_flags().is_null_free_inline_type() || vk->is_implicitly_constructible(), "must be, should have been checked in post_process_parsed_stream()");
+          _has_flattening_information = true;
+          // Flattening decision to be taken here
+          // This code assumes all verification already have been performed
+          // (field's type has been loaded and it is an inline klass)
+          bool too_big_to_flatten = (InlineFieldMaxFlatSize >= 0 &&
+                                    (vk->size_helper() * HeapWordSize) > InlineFieldMaxFlatSize);
+          bool too_atomic_to_flatten = vk->must_be_atomic() || AlwaysAtomicAccesses;
+          bool too_volatile_to_flatten = fieldinfo.access_flags().is_volatile();
+          if (vk->is_naturally_atomic()) {
+            too_atomic_to_flatten = false;
+            //too_volatile_to_flatten = false; //FIXME
+            // Currently, volatile fields are never flat, this could change in the future
+          }
+          if (!(too_big_to_flatten | too_atomic_to_flatten | too_volatile_to_flatten)) {
+            group->add_flat_field(idx, vk, !fieldinfo.field_flags().is_null_free_inline_type());
+            _nonstatic_oopmap_count += vk->nonstatic_oop_map_count();
+            _field_info->adr_at(idx)->field_flags_addr()->update_flat(true);
+            if (!vk->is_atomic()) {  // flat and non-atomic: take note
+              _has_nonatomic_values = true;
+              _atomic_field_count--;  // every other field is atomic but this one
+            }
+            if (!fieldinfo.field_flags().is_null_free_inline_type()) _has_null_markers = true;
+          } else {
+            _nonstatic_oopmap_count++;
+            group->add_oop_field(idx);
+          }
+        }
+      }
+      break;
+    }
+    default:
+      fatal("Something wrong?");
     }
   }
   _root_group->sort_by_size();
   _static_fields->sort_by_size();
   if (!_contended_groups.is_empty()) {
@@ -597,87 +747,332 @@
       _contended_groups.at(i)->sort_by_size();
     }
   }
 }
 
+/* Field sorting for inline classes:
+ *   - because inline classes are immutable, the @Contended annotation is ignored
+ *     when computing their layout (with only read operation, there's no false
+ *     sharing issue)
+ *   - this method also records the alignment of the field with the most
+ *     constraining alignment, this value is then used as the alignment
+ *     constraint when flattening this inline type into another container
+ *   - field flattening decisions are taken in this method (those decisions are
+ *     currently only based in the size of the fields to be flattened, the size
+ *     of the resulting instance is not considered)
+ */
+void FieldLayoutBuilder::inline_class_field_sorting() {
+  assert(_is_inline_type, "Should only be used for inline classes");
+  int alignment = 1;
+  for (GrowableArrayIterator<FieldInfo> it = _field_info->begin(); it != _field_info->end(); ++it) {
+    FieldGroup* group = nullptr;
+    FieldInfo fieldinfo = *it;
+    int field_alignment = 1;
+    if (fieldinfo.access_flags().is_static()) {
+      group = _static_fields;
+    } else {
+      _has_nonstatic_fields = true;
+      _atomic_field_count++;  // we might decrement this
+      group = _root_group;
+    }
+    assert(group != nullptr, "invariant");
+    BasicType type = Signature::basic_type(fieldinfo.signature(_constant_pool));
+    switch(type) {
+    case T_BYTE:
+    case T_CHAR:
+    case T_DOUBLE:
+    case T_FLOAT:
+    case T_INT:
+    case T_LONG:
+    case T_SHORT:
+    case T_BOOLEAN:
+      if (group != _static_fields) {
+        field_alignment = type2aelembytes(type); // alignment == size for primitive types
+      }
+      group->add_primitive_field(fieldinfo.index(), type);
+      break;
+    case T_OBJECT:
+    case T_ARRAY:
+    {
+      bool field_is_known_value_class =  !fieldinfo.field_flags().is_injected() && _inline_type_field_klasses != nullptr && _inline_type_field_klasses->at(fieldinfo.index()) != nullptr;
+      bool value_has_oops = field_is_known_value_class ? _inline_type_field_klasses->at(fieldinfo.index())->nonstatic_oop_count() > 0 : true;
+      bool is_candidate_for_flattening = fieldinfo.field_flags().is_null_free_inline_type() || (EnableNullableFieldFlattening && field_is_known_value_class && !value_has_oops);
+      // if (!fieldinfo.field_flags().is_null_free_inline_type()) {
+      if (!is_candidate_for_flattening) {
+        if (group != _static_fields) {
+          _nonstatic_oopmap_count++;
+          field_alignment = type2aelembytes(type); // alignment == size for oops
+        }
+        group->add_oop_field(fieldinfo.index());
+      } else {
+        assert(type != T_ARRAY, "null free ptr to array not supported");
+        _has_inline_type_fields = true;
+        if (group == _static_fields) {
+          // static fields are never flat
+          group->add_oop_field(fieldinfo.index());
+        } else {
+          // Check below is performed for non-static fields, it should be performed for static fields too but at this stage,
+          // it is not guaranteed that the klass of the static field has been loaded, so the test for static fields is delayed
+          // until the linking phase
+          Klass* klass =  _inline_type_field_klasses->at(fieldinfo.index());
+          assert(klass != nullptr, "Sanity check");
+          InlineKlass* vk = InlineKlass::cast(klass);
+          assert(vk->is_implicitly_constructible(), "must be, should have been checked in post_process_parsed_stream()");
+          // Flattening decision to be taken here
+          // This code assumes all verifications have already been performed
+          // (field's type has been loaded and it is an inline klass)
+          bool too_big_to_flatten = (InlineFieldMaxFlatSize >= 0 &&
+                                    (vk->size_helper() * HeapWordSize) > InlineFieldMaxFlatSize);
+          bool too_atomic_to_flatten = vk->must_be_atomic() || AlwaysAtomicAccesses;
+          bool too_volatile_to_flatten = fieldinfo.access_flags().is_volatile();
+          if (vk->is_naturally_atomic()) {
+            too_atomic_to_flatten = false;
+            //too_volatile_to_flatten = false; //FIXME
+            // Currently, volatile fields are never flat, this could change in the future
+          }
+          if (!(too_big_to_flatten | too_atomic_to_flatten | too_volatile_to_flatten)) {
+            group->add_flat_field(fieldinfo.index(), vk, !fieldinfo.field_flags().is_null_free_inline_type());
+            _nonstatic_oopmap_count += vk->nonstatic_oop_map_count();
+            field_alignment = vk->get_alignment();
+            _field_info->adr_at(fieldinfo.index())->field_flags_addr()->update_flat(true);
+            if (!vk->is_atomic()) {  // flat and non-atomic: take note
+              _has_nonatomic_values = true;
+              _atomic_field_count--;  // every other field is atomic but this one
+            }
+            if (!fieldinfo.field_flags().is_null_free_inline_type()) _has_null_markers = true;
+          } else {
+            _nonstatic_oopmap_count++;
+            field_alignment = type2aelembytes(T_OBJECT);
+            group->add_oop_field(fieldinfo.index());
+          }
+        }
+      }
+      break;
+    }
+    default:
+      fatal("Unexpected BasicType");
+    }
+    if (!fieldinfo.access_flags().is_static() && field_alignment > alignment) alignment = field_alignment;
+  }
+  _alignment = alignment;
+  assert(_has_nonstatic_fields, "Empty value instances should have an injected field to have a non-zero size");
+}
+
 void FieldLayoutBuilder::insert_contended_padding(LayoutRawBlock* slot) {
   if (ContendedPaddingWidth > 0) {
     LayoutRawBlock* padding = new LayoutRawBlock(LayoutRawBlock::PADDING, ContendedPaddingWidth);
     _layout->insert(slot, padding);
   }
 }
 
-// Computation of regular classes layout is an evolution of the previous default layout
-// (FieldAllocationStyle 1):
-//   - primitive fields are allocated first (from the biggest to the smallest)
-//   - then oop fields are allocated, either in existing gaps or at the end of
-//     the layout
+/* Computation of regular classes layout is an evolution of the previous default layout
+ * (FieldAllocationStyle 1):
+ *   - primitive fields (both primitive types and flat inline types) are allocated
+ *     first, from the biggest to the smallest
+ *   - then oop fields are allocated (to increase chances to have contiguous oops and
+ *     a simpler oopmap).
+ */
 void FieldLayoutBuilder::compute_regular_layout() {
   bool need_tail_padding = false;
   prologue();
   regular_field_sorting();
-
   if (_is_contended) {
     _layout->set_start(_layout->last_block());
     // insertion is currently easy because the current strategy doesn't try to fill holes
     // in super classes layouts => the _start block is by consequence the _last_block
     insert_contended_padding(_layout->start());
     need_tail_padding = true;
   }
-  _layout->add(_root_group->primitive_fields());
+  _layout->add(_root_group->big_primitive_fields());
+  _layout->add(_root_group->small_primitive_fields());
   _layout->add(_root_group->oop_fields());
 
   if (!_contended_groups.is_empty()) {
     for (int i = 0; i < _contended_groups.length(); i++) {
       FieldGroup* cg = _contended_groups.at(i);
       LayoutRawBlock* start = _layout->last_block();
       insert_contended_padding(start);
-      _layout->add(cg->primitive_fields(), start);
+      _layout->add(cg->big_primitive_fields());
+      _layout->add(cg->small_primitive_fields(), start);
       _layout->add(cg->oop_fields(), start);
       need_tail_padding = true;
     }
   }
 
   if (need_tail_padding) {
     insert_contended_padding(_layout->last_block());
   }
 
-  _static_layout->add_contiguously(this->_static_fields->oop_fields());
-  _static_layout->add(this->_static_fields->primitive_fields());
+  if (EnableNullableFieldFlattening && _layout->has_missing_null_markers()) {
+    insert_null_markers();
+  }
+
+  // Warning: IntanceMirrorKlass expects static oops to be allocated first
+  _static_layout->add_contiguously(_static_fields->oop_fields());
+  _static_layout->add(_static_fields->big_primitive_fields());
+  _static_layout->add(_static_fields->small_primitive_fields());
 
   epilogue();
 }
 
+void FieldLayoutBuilder::insert_null_markers() {
+  if (!EnableNullableFieldFlattening || !_layout->has_missing_null_markers()) return;
+  GrowableArray<LayoutRawBlock*>* list = new GrowableArray<LayoutRawBlock*>(10);
+  for (LayoutRawBlock* block = _layout->first_field_block(); block != _layout->last_block(); block = block->next_block()) {
+    if (block->needs_null_marker()) {
+      assert(block->kind() == LayoutRawBlock::FLAT, "Only flat fields might need null markers");
+      if (block->inline_klass()->has_internal_null_marker_offset()) {
+        // The inline klass has an internal null marker slot, let's use it
+        // The inline klass has the internal null marker offset from the begining of the object,
+        // compute the offset relative to begining of payload
+        int internal_null_marker_offset = block->inline_klass()->get_internal_null_marker_offset() - block->inline_klass()->first_field_offset();
+        block->set_null_marker_offset(block->offset() + internal_null_marker_offset);
+        _field_info->adr_at(block->field_index())->set_null_marker_offset(block->null_marker_offset());
+        _field_info->adr_at(block->field_index())->field_flags_addr()->update_null_marker(true);
+        _field_info->adr_at(block->field_index())->field_flags_addr()->update_internal_null_marker(true);
+      } else {
+        // No internal null marker, need a external slot in the container
+        LayoutRawBlock* marker = new LayoutRawBlock(LayoutRawBlock::NULL_MARKER, 1);
+        marker->set_field_index(block->field_index());
+        list->append(marker);
+      }
+    }
+  }
+  _layout->add(list);
+  for (GrowableArrayIterator<LayoutRawBlock*> it = list->begin(); it != list->end(); ++it) {
+    LayoutRawBlock* block = *it;
+    assert(block->offset() != -1, "Must be set");
+    assert(!block->needs_null_marker(), "Must have been set");
+    _field_info->adr_at(block->field_index())->set_null_marker_offset(block->offset());
+    _field_info->adr_at(block->field_index())->field_flags_addr()->update_null_marker(true);
+  }
+}
+
+/* Computation of inline classes has a slightly different strategy than for
+ * regular classes. Regular classes have their oop fields allocated at the end
+ * of the layout to increase GC performances. Unfortunately, this strategy
+ * increases the number of empty slots inside an instance. Because the purpose
+ * of inline classes is to be embedded into other containers, it is critical
+ * to keep their size as small as possible. For this reason, the allocation
+ * strategy is:
+ *   - big primitive fields (primitive types and flat inline type smaller
+ *     than an oop) are allocated first (from the biggest to the smallest)
+ *   - then oop fields
+ *   - then small primitive fields (from the biggest to the smallest)
+ */
+void FieldLayoutBuilder::compute_inline_class_layout() {
+  prologue();
+  inline_class_field_sorting();
+  // Inline types are not polymorphic, so they cannot inherit fields.
+  // By consequence, at this stage, the layout must be composed of a RESERVED
+  // block, followed by an EMPTY block.
+  assert(_layout->start()->kind() == LayoutRawBlock::RESERVED, "Unexpected");
+  assert(_layout->start()->next_block()->kind() == LayoutRawBlock::EMPTY, "Unexpected");
+  LayoutRawBlock* first_empty = _layout->start()->next_block();
+  if (first_empty->offset() % _alignment != 0) {
+    LayoutRawBlock* padding = new LayoutRawBlock(LayoutRawBlock::PADDING, _alignment - (first_empty->offset() % _alignment));
+    _layout->insert(first_empty, padding);
+    _layout->set_start(padding->next_block());
+  }
+
+  _layout->add(_root_group->big_primitive_fields());
+  _layout->add(_root_group->oop_fields());
+  _layout->add(_root_group->small_primitive_fields());
+
+  if (EnableNullableFieldFlattening && _layout->has_missing_null_markers()) {
+    insert_null_markers();
+  }
+
+  LayoutRawBlock* first_field = _layout->first_field_block();
+   if (first_field != nullptr) {
+     _first_field_offset = _layout->first_field_block()->offset();
+     _payload_size_in_bytes = _layout->last_block()->offset() - _layout->first_field_block()->offset();
+   } else {
+     // special case for empty value types
+     _first_field_offset = _layout->blocks()->size();
+     _payload_size_in_bytes = 0;
+   }
+  _payload_size_in_bytes = _layout->last_block()->offset() - _layout->first_field_block()->offset();
+
+  // Looking if there's an empty slot inside the layout that could be used to store a null marker
+  LayoutRawBlock* b = _layout->first_field_block();
+  while (b != _layout->last_block()) {
+    if (b->kind() == LayoutRawBlock::EMPTY) {
+      break;
+    }
+    b = b->next_block();
+  }
+  if (b != _layout->last_block()) {
+    // found an empty slot, register its offset from the beginning of the payload
+    _internal_null_marker_offset = b->offset();
+  }
+
+  // Warning:: InstanceMirrorKlass expects static oops to be allocated first
+  _static_layout->add_contiguously(_static_fields->oop_fields());
+  _static_layout->add(_static_fields->big_primitive_fields());
+  _static_layout->add(_static_fields->small_primitive_fields());
+
+  epilogue();
+}
+
+void FieldLayoutBuilder::add_flat_field_oopmap(OopMapBlocksBuilder* nonstatic_oop_maps,
+                InlineKlass* vklass, int offset) {
+  int diff = offset - vklass->first_field_offset();
+  const OopMapBlock* map = vklass->start_of_nonstatic_oop_maps();
+  const OopMapBlock* last_map = map + vklass->nonstatic_oop_map_count();
+  while (map < last_map) {
+    nonstatic_oop_maps->add(map->offset() + diff, map->count());
+    map++;
+  }
+}
+
+void FieldLayoutBuilder::register_embedded_oops_from_list(OopMapBlocksBuilder* nonstatic_oop_maps, GrowableArray<LayoutRawBlock*>* list) {
+  if (list != nullptr) {
+    for (int i = 0; i < list->length(); i++) {
+      LayoutRawBlock* f = list->at(i);
+      if (f->kind() == LayoutRawBlock::FLAT) {
+        InlineKlass* vk = f->inline_klass();
+        assert(vk != nullptr, "Should have been initialized");
+        if (vk->contains_oops()) {
+          add_flat_field_oopmap(nonstatic_oop_maps, vk, f->offset());
+        }
+      }
+    }
+  }
+}
+
+void FieldLayoutBuilder::register_embedded_oops(OopMapBlocksBuilder* nonstatic_oop_maps, FieldGroup* group) {
+  if (group->oop_fields() != nullptr) {
+    for (int i = 0; i < group->oop_fields()->length(); i++) {
+      LayoutRawBlock* b = group->oop_fields()->at(i);
+      nonstatic_oop_maps->add(b->offset(), 1);
+    }
+  }
+  register_embedded_oops_from_list(nonstatic_oop_maps, group->big_primitive_fields());
+  register_embedded_oops_from_list(nonstatic_oop_maps, group->small_primitive_fields());
+}
+
 void FieldLayoutBuilder::epilogue() {
   // Computing oopmaps
   int super_oop_map_count = (_super_klass == nullptr) ? 0 :_super_klass->nonstatic_oop_map_count();
   int max_oop_map_count = super_oop_map_count + _nonstatic_oopmap_count;
-
   OopMapBlocksBuilder* nonstatic_oop_maps =
       new OopMapBlocksBuilder(max_oop_map_count);
   if (super_oop_map_count > 0) {
     nonstatic_oop_maps->initialize_inherited_blocks(_super_klass->start_of_nonstatic_oop_maps(),
     _super_klass->nonstatic_oop_map_count());
   }
-
-  if (_root_group->oop_fields() != nullptr) {
-    for (int i = 0; i < _root_group->oop_fields()->length(); i++) {
-      LayoutRawBlock* b = _root_group->oop_fields()->at(i);
-      nonstatic_oop_maps->add(b->offset(), 1);
-    }
-  }
-
+  register_embedded_oops(nonstatic_oop_maps, _root_group);
   if (!_contended_groups.is_empty()) {
     for (int i = 0; i < _contended_groups.length(); i++) {
       FieldGroup* cg = _contended_groups.at(i);
       if (cg->oop_count() > 0) {
         assert(cg->oop_fields() != nullptr && cg->oop_fields()->at(0) != nullptr, "oop_count > 0 but no oop fields found");
-        nonstatic_oop_maps->add(cg->oop_fields()->at(0)->offset(), cg->oop_count());
+        register_embedded_oops(nonstatic_oop_maps, cg);
       }
     }
   }
-
   nonstatic_oop_maps->compact();
 
   int instance_end = align_up(_layout->last_block()->offset(), wordSize);
   int static_fields_end = align_up(_static_layout->last_block()->offset(), wordSize);
   int static_fields_size = (static_fields_end -
@@ -689,21 +1084,75 @@
   _info->oop_map_blocks = nonstatic_oop_maps;
   _info->_instance_size = align_object_size(instance_end / wordSize);
   _info->_static_field_size = static_fields_size;
   _info->_nonstatic_field_size = (nonstatic_field_end - instanceOopDesc::base_offset_in_bytes()) / heapOopSize;
   _info->_has_nonstatic_fields = _has_nonstatic_fields;
+  _info->_has_inline_fields = _has_inline_type_fields;
+  _info->_has_null_marker_offsets = _has_null_markers;
+
+  // An inline type is naturally atomic if it has just one field, and
+  // that field is simple enough.
+  _info->_is_naturally_atomic = (_is_inline_type &&
+                                 (_atomic_field_count <= 1) &&
+                                 !_has_nonatomic_values &&
+                                 _contended_groups.is_empty());
+  // This may be too restrictive, since if all the fields fit in 64
+  // bits we could make the decision to align instances of this class
+  // to 64-bit boundaries, and load and store them as single words.
+  // And on machines which supported larger atomics we could similarly
+  // allow larger values to be atomic, if properly aligned.
+
+#ifdef ASSERT
+  // Tests verifying integrity of field layouts are using the output of -XX:+PrintFieldLayout
+  // which prints the details of LayoutRawBlocks used to compute the layout.
+  // The code below checks that offsets in the _field_info meta-data match offsets
+  // in the LayoutRawBlocks
+  LayoutRawBlock* b = _layout->blocks();
+  while(b != _layout->last_block()) {
+    if (b->kind() == LayoutRawBlock::REGULAR || b->kind() == LayoutRawBlock::FLAT) {
+      assert(_field_info->adr_at(b->field_index())->offset() == (u4)b->offset()," Must match");
+    }
+    b = b->next_block();
+  }
+  b = _static_layout->blocks();
+  while(b != _static_layout->last_block()) {
+    if (b->kind() == LayoutRawBlock::REGULAR || b->kind() == LayoutRawBlock::FLAT) {
+      assert(_field_info->adr_at(b->field_index())->offset() == (u4)b->offset()," Must match");
+    }
+    b = b->next_block();
+  }
+#endif // ASSERT
+
 
-  if (PrintFieldLayout) {
+  if (PrintFieldLayout || (PrintInlineLayout && _has_flattening_information)) {
     ResourceMark rm;
-    tty->print_cr("Layout of class %s", _classname->as_C_string());
+    ttyLocker ttl;
+    if (_super_klass != nullptr) {
+      tty->print_cr("Layout of class %s@%p extends %s@%p", _classname->as_C_string(),
+                    _loader_data, _super_klass->name()->as_C_string(), _super_klass->class_loader_data());
+    } else {
+      tty->print_cr("Layout of class %s@%p", _classname->as_C_string(), _loader_data);
+    }
     tty->print_cr("Instance fields:");
-    _layout->print(tty, false, _super_klass);
+    _layout->print(tty, false, _super_klass, _inline_type_field_klasses);
     tty->print_cr("Static fields:");
-    _static_layout->print(tty, true, nullptr);
+    _static_layout->print(tty, true, nullptr, _inline_type_field_klasses);
     tty->print_cr("Instance size = %d bytes", _info->_instance_size * wordSize);
+    if (_is_inline_type) {
+      tty->print_cr("First field offset = %d", _first_field_offset);
+      tty->print_cr("Alignment = %d bytes", _alignment);
+      tty->print_cr("Exact size = %d bytes", _payload_size_in_bytes);
+      if (_internal_null_marker_offset != -1) {
+        tty->print_cr("Null marker offset = %d", _internal_null_marker_offset);
+      }
+    }
     tty->print_cr("---");
   }
 }
 
 void FieldLayoutBuilder::build_layout() {
-  compute_regular_layout();
+  if (_is_inline_type) {
+    compute_inline_class_layout();
+  } else {
+    compute_regular_layout();
+  }
 }
diff a/src/hotspot/share/classfile/fieldLayoutBuilder.hpp b/src/hotspot/share/classfile/fieldLayoutBuilder.hpp
--- a/src/hotspot/share/classfile/fieldLayoutBuilder.hpp
+++ b/src/hotspot/share/classfile/fieldLayoutBuilder.hpp
@@ -31,11 +31,10 @@
 #include "oops/fieldStreams.hpp"
 #include "utilities/growableArray.hpp"
 
 // Classes below are used to compute the field layout of classes.
 
-
 // A LayoutRawBlock describes an element of a layout.
 // Each field is represented by a LayoutRawBlock.
 // LayoutRawBlocks can also represent elements injected by the JVM:
 // padding, empty blocks, inherited fields, etc.
 // All LayoutRawBlocks must have a size and an alignment. The size is the
@@ -52,30 +51,36 @@
 //
 class LayoutRawBlock : public ResourceObj {
  public:
   // Some code relies on the order of values below.
   enum Kind {
-    EMPTY,         // empty slot, space is taken from this to allocate fields
-    RESERVED,      // reserved for JVM usage (for instance object header)
-    PADDING,       // padding (because of alignment constraints or @Contended)
-    REGULAR,       // primitive or oop field (including non-flattened inline fields)
-    FLATTENED,     // flattened field
-    INHERITED      // field(s) inherited from super classes
+    EMPTY,                 // empty slot, space is taken from this to allocate fields
+    RESERVED,              // reserved for JVM usage (for instance object header)
+    PADDING,               // padding (because of alignment constraints or @Contended)
+    REGULAR,               // primitive or oop field (including not flat inline type fields)
+    FLAT,                  // flat field
+    INHERITED,             // field(s) inherited from super classes
+    NULL_MARKER,           // stores the null marker for a flat field
+    INHERITED_NULL_MARKER  // a super-class used this slot to store a null marker
   };
 
  private:
   LayoutRawBlock* _next_block;
   LayoutRawBlock* _prev_block;
+  InlineKlass* _inline_klass;
   Kind _kind;
   int _offset;
   int _alignment;
   int _size;
   int _field_index;
+  int _null_marker_offset;
   bool _is_reference;
+  bool _needs_null_marker;
 
  public:
   LayoutRawBlock(Kind kind, int size);
+
   LayoutRawBlock(int index, Kind kind, int size, int alignment, bool is_reference = false);
   LayoutRawBlock* next_block() const { return _next_block; }
   void set_next_block(LayoutRawBlock* next) { _next_block = next; }
   LayoutRawBlock* prev_block() const { return _prev_block; }
   void set_prev_block(LayoutRawBlock* prev) { _prev_block = prev; }
@@ -90,11 +95,28 @@
   void set_size(int size) { _size = size; }
   int field_index() const {
     assert(_field_index != -1, "Must be initialized");
     return _field_index;
   }
+  void set_field_index(int field_index) {
+    assert(_field_index == -1, "Must not be initialized");
+    _field_index = field_index;
+  }
   bool is_reference() const { return _is_reference; }
+  InlineKlass* inline_klass() const {
+    assert(_inline_klass != nullptr, "Must be initialized");
+    return _inline_klass;
+  }
+  void set_inline_klass(InlineKlass* inline_klass) { _inline_klass = inline_klass; }
+  void set_needs_null_marker() { _needs_null_marker = true; }
+  bool needs_null_marker() const { return _needs_null_marker; }
+  void set_null_marker_offset(int offset) {
+    assert(_needs_null_marker, "");
+    _null_marker_offset = offset;
+    _needs_null_marker = false;
+  }
+  int null_marker_offset() const { return _null_marker_offset; }
 
   bool fit(int size, int alignment);
 
   static int compare_offset(LayoutRawBlock** x, LayoutRawBlock** y)  { return (*x)->offset() - (*y)->offset(); }
   // compare_size_inverted() returns the opposite of a regular compare method in order to
@@ -111,41 +133,48 @@
     return diff;
 #else
     return (*y)->size() - (*x)->size();
 #endif // _WINDOWS
   }
-
 };
 
 // A Field group represents a set of fields that have to be allocated together,
 // this is the way the @Contended annotation is supported.
 // Inside a FieldGroup, fields are sorted based on their kind: primitive,
-// oop, or flattened.
+// oop, or flat.
 //
 class FieldGroup : public ResourceObj {
 
  private:
   FieldGroup* _next;
-  GrowableArray<LayoutRawBlock*>* _primitive_fields;
+
+  GrowableArray<LayoutRawBlock*>* _small_primitive_fields;
+  GrowableArray<LayoutRawBlock*>* _big_primitive_fields;
   GrowableArray<LayoutRawBlock*>* _oop_fields;
   int _contended_group;
   int _oop_count;
   static const int INITIAL_LIST_SIZE = 16;
 
  public:
   FieldGroup(int contended_group = -1);
 
   FieldGroup* next() const { return _next; }
   void set_next(FieldGroup* next) { _next = next; }
-  GrowableArray<LayoutRawBlock*>* primitive_fields() const { return _primitive_fields; }
+  GrowableArray<LayoutRawBlock*>* small_primitive_fields() const { return _small_primitive_fields; }
+  GrowableArray<LayoutRawBlock*>* big_primitive_fields() const { return _big_primitive_fields; }
   GrowableArray<LayoutRawBlock*>* oop_fields() const { return _oop_fields; }
   int contended_group() const { return _contended_group; }
   int oop_count() const { return _oop_count; }
 
   void add_primitive_field(int idx, BasicType type);
   void add_oop_field(int idx);
+  void add_flat_field(int idx, InlineKlass* vk, bool needs_null_marker);
+  void add_block(LayoutRawBlock** list, LayoutRawBlock* block);
   void sort_by_size();
+ private:
+  void add_to_small_primitive_list(LayoutRawBlock* block);
+  void add_to_big_primitive_list(LayoutRawBlock* block);
 };
 
 // The FieldLayout class represents a set of fields organized
 // in a layout.
 // An instance of FieldLayout can either represent the layout
@@ -167,10 +196,11 @@
   GrowableArray<FieldInfo>* _field_info;
   ConstantPool* _cp;
   LayoutRawBlock* _blocks;  // the layout being computed
   LayoutRawBlock* _start;   // points to the first block where a field can be inserted
   LayoutRawBlock* _last;    // points to the last block of the layout (big empty block)
+  bool _has_missing_null_markers;
 
  public:
   FieldLayout(GrowableArray<FieldInfo>* field_info, ConstantPool* cp);
   void initialize_static_layout();
   void initialize_instance_layout(const InstanceKlass* ik);
@@ -181,33 +211,35 @@
       block = block->next_block();
     }
     return block;
   }
 
-  LayoutRawBlock* start() { return _start; }
+  LayoutRawBlock* blocks() const { return _blocks; }
+
+  LayoutRawBlock* start() const { return _start; }
   void set_start(LayoutRawBlock* start) { _start = start; }
-  LayoutRawBlock* last_block() { return _last; }
+  LayoutRawBlock* last_block() const { return _last; }
+  bool has_missing_null_markers() const { return _has_missing_null_markers; }
 
   LayoutRawBlock* first_field_block();
   void add(GrowableArray<LayoutRawBlock*>* list, LayoutRawBlock* start = nullptr);
   void add_field_at_offset(LayoutRawBlock* blocks, int offset, LayoutRawBlock* start = nullptr);
   void add_contiguously(GrowableArray<LayoutRawBlock*>* list, LayoutRawBlock* start = nullptr);
   LayoutRawBlock* insert_field_block(LayoutRawBlock* slot, LayoutRawBlock* block);
   bool reconstruct_layout(const InstanceKlass* ik);
   void fill_holes(const InstanceKlass* ik);
   LayoutRawBlock* insert(LayoutRawBlock* slot, LayoutRawBlock* block);
   void remove(LayoutRawBlock* block);
-  void print(outputStream* output, bool is_static, const InstanceKlass* super);
+  void print(outputStream* output, bool is_static, const InstanceKlass* super, Array<InlineKlass*>* inline_fields);
 };
 
 
 // FieldLayoutBuilder is the main entry point for layout computation.
-// This class has three methods to generate layout: one for regular classes
-// and two for classes with hard coded offsets (java,lang.ref.Reference
-// and the boxing classes). The rationale for having multiple methods
-// is that each kind of class has a different set goals regarding
-// its layout, so instead of mixing several layout strategies into a
+// This class has two methods to generate layout: one for identity classes
+// and one for inline classes. The rational for having two methods
+// is that each kind of classes has a different set goals regarding
+// its layout, so instead of mixing two layout strategies into a
 // single method, each kind has its own method (see comments below
 // for more details about the allocation strategies).
 //
 // Computing the layout of a class always goes through 4 steps:
 //   1 - Prologue: preparation of data structure and gathering of
@@ -220,46 +252,80 @@
 //   4 - Epilogue: oopmaps are generated, layout information is
 //       prepared so other VM components can use it (instance size,
 //       static field size, non-static field size, etc.)
 //
 //  Steps 1 and 4 are common to all layout computations. Step 2 and 3
-//  can vary with the allocation strategy.
+//  differ for inline classes and identity classes.
 //
 class FieldLayoutBuilder : public ResourceObj {
  private:
-
   const Symbol* _classname;
+  ClassLoaderData* _loader_data;
   const InstanceKlass* _super_klass;
   ConstantPool* _constant_pool;
   GrowableArray<FieldInfo>* _field_info;
   FieldLayoutInfo* _info;
+  Array<InlineKlass*>* _inline_type_field_klasses;
   FieldGroup* _root_group;
   GrowableArray<FieldGroup*> _contended_groups;
   FieldGroup* _static_fields;
   FieldLayout* _layout;
   FieldLayout* _static_layout;
   int _nonstatic_oopmap_count;
   int _alignment;
+  int _first_field_offset;
+  int _internal_null_marker_offset; // if any, -1 means no internal null marker
+  int _payload_size_in_bytes;
+  int _atomic_field_count;
+  int _fields_size_sum;
   bool _has_nonstatic_fields;
-  bool _is_contended; // is a contended class?
+  bool _has_inline_type_fields;
+  bool _is_contended;
+  bool _is_inline_type;
+  bool _has_flattening_information;
+  bool _has_nonatomic_values;
+  bool _nullable_atomic_flat_candidate;
+  bool _has_null_markers;
+
+  FieldGroup* get_or_create_contended_group(int g);
 
  public:
-  FieldLayoutBuilder(const Symbol* classname, const InstanceKlass* super_klass, ConstantPool* constant_pool,
-                     GrowableArray<FieldInfo>* field_info, bool is_contended, FieldLayoutInfo* info);
+  FieldLayoutBuilder(const Symbol* classname, ClassLoaderData* loader_data, const InstanceKlass* super_klass, ConstantPool* constant_pool,
+                     GrowableArray<FieldInfo>* field_info, bool is_contended, bool is_inline_type, FieldLayoutInfo* info,
+                     Array<InlineKlass*>* inline_type_field_klasses);
 
   int get_alignment() {
     assert(_alignment != -1, "Uninitialized");
     return _alignment;
   }
 
+  int get_first_field_offset() {
+    assert(_first_field_offset != -1, "Uninitialized");
+    return _first_field_offset;
+  }
+
+  int get_payload_size_in_byte() {
+    assert(_payload_size_in_bytes != -1, "Uninitialized");
+    return _payload_size_in_bytes;
+  }
+
+  int get_internal_null_marker_offset() {
+    return _internal_null_marker_offset;
+  }
+
   void build_layout();
   void compute_regular_layout();
+  void compute_inline_class_layout();
   void insert_contended_padding(LayoutRawBlock* slot);
+  void insert_null_markers();
 
- private:
+ protected:
   void prologue();
   void epilogue();
   void regular_field_sorting();
-  FieldGroup* get_or_create_contended_group(int g);
+  void inline_class_field_sorting();
+  void add_flat_field_oopmap(OopMapBlocksBuilder* nonstatic_oop_map, InlineKlass* vk, int offset);
+  void register_embedded_oops_from_list(OopMapBlocksBuilder* nonstatic_oop_maps, GrowableArray<LayoutRawBlock*>* list);
+  void register_embedded_oops(OopMapBlocksBuilder* nonstatic_oop_maps, FieldGroup* group);
 };
 
 #endif // SHARE_CLASSFILE_FIELDLAYOUTBUILDER_HPP
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
@@ -51,12 +51,14 @@
 #include "memory/oopFactory.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
 #include "oops/fieldInfo.hpp"
 #include "oops/fieldStreams.inline.hpp"
+#include "oops/flatArrayKlass.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "oops/instanceKlass.inline.hpp"
-#include "oops/instanceMirrorKlass.hpp"
+#include "oops/instanceMirrorKlass.inline.hpp"
 #include "oops/klass.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/method.inline.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "oops/objArrayOop.inline.hpp"
@@ -786,10 +788,11 @@
 int java_lang_Class::_static_oop_field_count_offset;
 int java_lang_Class::_class_loader_offset;
 int java_lang_Class::_module_offset;
 int java_lang_Class::_protection_domain_offset;
 int java_lang_Class::_component_mirror_offset;
+
 int java_lang_Class::_signers_offset;
 int java_lang_Class::_name_offset;
 int java_lang_Class::_source_file_offset;
 int java_lang_Class::_classData_offset;
 int java_lang_Class::_classRedefinedCount_offset;
@@ -985,25 +988,38 @@
 
   set_static_oop_field_count(mirror(), mk->compute_static_oop_field_count(mirror()));
 
   // It might also have a component mirror.  This mirror must already exist.
   if (k->is_array_klass()) {
-    if (k->is_typeArray_klass()) {
+    if (k->is_flatArray_klass()) {
+      Klass* element_klass = (Klass*) FlatArrayKlass::cast(k)->element_klass();
+      assert(element_klass->is_inline_klass(), "Must be inline type component");
+      if (is_scratch) {
+        comp_mirror = Handle(THREAD, HeapShared::scratch_java_mirror(element_klass));
+      } else {
+        comp_mirror = Handle(THREAD, element_klass->java_mirror());
+      }
+    } else if (k->is_typeArray_klass()) {
       BasicType type = TypeArrayKlass::cast(k)->element_type();
       if (is_scratch) {
         comp_mirror = Handle(THREAD, HeapShared::scratch_java_mirror(type));
       } else {
         comp_mirror = Handle(THREAD, Universe::java_mirror(type));
       }
     } else {
       assert(k->is_objArray_klass(), "Must be");
       Klass* element_klass = ObjArrayKlass::cast(k)->element_klass();
       assert(element_klass != nullptr, "Must have an element klass");
+      oop comp_oop = element_klass->java_mirror();
+      if (element_klass->is_inline_klass()) {
+        InlineKlass* ik = InlineKlass::cast(element_klass);
+        comp_oop = ik->java_mirror();
+      }
       if (is_scratch) {
         comp_mirror = Handle(THREAD, HeapShared::scratch_java_mirror(element_klass));
       } else {
-        comp_mirror = Handle(THREAD, element_klass->java_mirror());
+        comp_mirror = Handle(THREAD, comp_oop);
       }
     }
     assert(comp_mirror() != nullptr, "must have a mirror");
 
     // Two-way link between the array klass and its component mirror:
@@ -1061,10 +1077,11 @@
     if (comp_mirror() != nullptr) {
       // 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);
     }
   } else {
     assert(fixup_mirror_list() != nullptr, "fixup_mirror_list not initialized");
@@ -1083,13 +1100,18 @@
 //
 // 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) {
-  if (k->class_loader() != nullptr &&
-      k->class_loader() != SystemDictionary::java_platform_loader() &&
-      k->class_loader() != SystemDictionary::java_system_loader()) {
+  // Inline classes encapsulate two mirror objects, a value mirror (primitive value mirror)
+  // and a reference mirror (primitive class mirror), skip over scratch mirror allocation
+  // for inline classes, they will not be part of shared archive and will be created while
+  // restoring unshared fileds. Refer Klass::restore_unshareable_info() for more details.
+  if (k->is_inline_klass() ||
+      (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
@@ -1289,11 +1311,13 @@
   }
   if (name == nullptr) {
     st->print("<null>");
     return;
   }
-  if (is_instance)  st->print("L");
+  if (is_instance)  {
+    st->print("L");
+  }
   st->write((char*) name->base(), (int) name->utf8_length());
   if (is_instance)  st->print(";");
 }
 
 Symbol* java_lang_Class::as_signature(oop java_class, bool intern_if_not_found) {
@@ -1348,10 +1372,14 @@
 }
 
 
 void java_lang_Class::release_set_array_klass(oop java_class, Klass* klass) {
   assert(klass->is_klass() && klass->is_array_klass(), "should be array klass");
+  if (klass->is_flatArray_klass() || (klass->is_objArray_klass() && ObjArrayKlass::cast(klass)->is_null_free_array_klass())) {
+    // TODO 8325106 Ignore flat / null-free arrays
+    return;
+  }
   java_class->release_metadata_field_put(_array_klass_offset, klass);
 }
 
 
 BasicType java_lang_Class::primitive_type(oop java_class) {
@@ -2623,11 +2651,11 @@
     if (!skip_throwableInit_check) {
       assert(skip_fillInStackTrace_check, "logic error in backtrace filtering");
 
       // skip <init> methods of the exception class and superclasses
       // This is similar to classic VM.
-      if (method->name() == vmSymbols::object_initializer_name() &&
+      if (method->is_object_constructor() &&
           throwable->is_a(method->method_holder())) {
         continue;
       } else {
         // there are none or we've seen them all - either way stop checking
         skip_throwableInit_check = true;
@@ -2982,12 +3010,12 @@
 }
 #endif
 
 static int get_flags(const methodHandle& m) {
   int flags = (jushort)( m->access_flags().as_short() & JVM_RECOGNIZED_METHOD_MODIFIERS );
-  if (m->is_initializer()) {
-    flags |= java_lang_invoke_MemberName::MN_IS_CONSTRUCTOR;
+  if (m->is_object_constructor()) {
+    flags |= java_lang_invoke_MemberName::MN_IS_OBJECT_CONSTRUCTOR;
   } else {
     flags |= java_lang_invoke_MemberName::MN_IS_METHOD;
   }
   if (m->caller_sensitive()) {
     flags |= java_lang_invoke_MemberName::MN_CALLER_SENSITIVE;
@@ -3373,21 +3401,21 @@
 int java_lang_reflect_Field::_clazz_offset;
 int java_lang_reflect_Field::_name_offset;
 int java_lang_reflect_Field::_type_offset;
 int java_lang_reflect_Field::_slot_offset;
 int java_lang_reflect_Field::_modifiers_offset;
-int java_lang_reflect_Field::_trusted_final_offset;
+int java_lang_reflect_Field::_flags_offset;
 int java_lang_reflect_Field::_signature_offset;
 int java_lang_reflect_Field::_annotations_offset;
 
 #define FIELD_FIELDS_DO(macro) \
   macro(_clazz_offset,     k, vmSymbols::clazz_name(),     class_signature,  false); \
   macro(_name_offset,      k, vmSymbols::name_name(),      string_signature, false); \
   macro(_type_offset,      k, vmSymbols::type_name(),      class_signature,  false); \
   macro(_slot_offset,      k, vmSymbols::slot_name(),      int_signature,    false); \
   macro(_modifiers_offset, k, vmSymbols::modifiers_name(), int_signature,    false); \
-  macro(_trusted_final_offset,    k, vmSymbols::trusted_final_name(),    bool_signature,       false); \
+  macro(_flags_offset,     k, vmSymbols::flags_name(),     int_signature,    false); \
   macro(_signature_offset,        k, vmSymbols::signature_name(),        string_signature,     false); \
   macro(_annotations_offset,      k, vmSymbols::annotations_name(),      byte_array_signature, false);
 
 void java_lang_reflect_Field::compute_offsets() {
   InstanceKlass* k = vmClasses::reflect_Field_klass();
@@ -3448,12 +3476,12 @@
 
 void java_lang_reflect_Field::set_modifiers(oop field, int value) {
   field->int_field_put(_modifiers_offset, value);
 }
 
-void java_lang_reflect_Field::set_trusted_final(oop field) {
-  field->bool_field_put(_trusted_final_offset, true);
+void java_lang_reflect_Field::set_flags(oop field, int value) {
+  field->int_field_put(_flags_offset, value);
 }
 
 void java_lang_reflect_Field::set_signature(oop field, oop value) {
   field->obj_field_put(_signature_offset, value);
 }
@@ -4291,11 +4319,11 @@
   return method == nullptr ? nullptr : java_lang_invoke_ResolvedMethodName::vmtarget(method);
 }
 
 bool java_lang_invoke_MemberName::is_method(oop mname) {
   assert(is_instance(mname), "must be MemberName");
-  return (flags(mname) & (MN_IS_METHOD | MN_IS_CONSTRUCTOR)) > 0;
+  return (flags(mname) & (MN_IS_METHOD | MN_IS_OBJECT_CONSTRUCTOR)) > 0;
 }
 
 void java_lang_invoke_MemberName::set_method(oop mname, oop resolved_method) {
   assert(is_instance(mname), "wrong type");
   mname->obj_field_put(_method_offset, resolved_method);
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
@@ -228,10 +228,11 @@
   static int _protection_domain_offset;
   static int _signers_offset;
   static int _class_loader_offset;
   static int _module_offset;
   static int _component_mirror_offset;
+
   static int _name_offset;
   static int _source_file_offset;
   static int _classData_offset;
   static int _classRedefinedCount_offset;
 
@@ -241,10 +242,11 @@
   static GrowableArray<Klass*>* _fixup_module_field_list;
 
   static void set_protection_domain(oop java_class, oop protection_domain);
   static void set_class_loader(oop java_class, oop class_loader);
   static void set_component_mirror(oop java_class, oop comp_mirror);
+
   static void initialize_mirror_fields(Klass* k, Handle mirror, Handle protection_domain,
                                        Handle classData, TRAPS);
   static void set_mirror_module_field(JavaThread* current, Klass* K, Handle mirror, Handle module);
  public:
   static void allocate_fixup_lists();
@@ -284,24 +286,25 @@
   static Klass* array_klass_acquire(oop java_class);
   static void release_set_array_klass(oop java_class, Klass* klass);
   // compiler support for class operations
   static int klass_offset()                { CHECK_INIT(_klass_offset); }
   static int array_klass_offset()          { CHECK_INIT(_array_klass_offset); }
+  static int component_mirror_offset()     { CHECK_INIT(_component_mirror_offset); }
+
   // Support for classRedefinedCount field
   static int classRedefinedCount(oop the_class_mirror);
   static void set_classRedefinedCount(oop the_class_mirror, int value);
 
   // Support for embedded per-class oops
   static oop  protection_domain(oop java_class);
   static oop  component_mirror(oop java_class);
+
   static objArrayOop  signers(oop java_class);
   static void set_signers(oop java_class, objArrayOop signers);
   static oop  class_data(oop java_class);
   static void set_class_data(oop java_class, oop classData);
 
-  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);
   static oop module(oop java_class);
 
   static oop name(Handle java_class, TRAPS);
@@ -757,11 +760,11 @@
   static int _clazz_offset;
   static int _name_offset;
   static int _type_offset;
   static int _slot_offset;
   static int _modifiers_offset;
-  static int _trusted_final_offset;
+  static int _flags_offset;
   static int _signature_offset;
   static int _annotations_offset;
 
   static void compute_offsets();
 
@@ -785,11 +788,11 @@
   static void set_slot(oop reflect, int value);
 
   static int modifiers(oop field);
   static void set_modifiers(oop field, int value);
 
-  static void set_trusted_final(oop field);
+  static void set_flags(oop field, int value);
 
   static void set_signature(oop constructor, oop value);
   static void set_annotations(oop constructor, oop value);
 
   // Debugging
@@ -1284,18 +1287,20 @@
   static bool is_method(oop obj);
 
   // Relevant integer codes (keep these in synch. with MethodHandleNatives.Constants):
   enum {
     MN_IS_METHOD             = 0x00010000, // method (not constructor)
-    MN_IS_CONSTRUCTOR        = 0x00020000, // constructor
+    MN_IS_OBJECT_CONSTRUCTOR = 0x00020000, // constructor
     MN_IS_FIELD              = 0x00040000, // field
     MN_IS_TYPE               = 0x00080000, // nested type
     MN_CALLER_SENSITIVE      = 0x00100000, // @CallerSensitive annotation detected
     MN_TRUSTED_FINAL         = 0x00200000, // trusted final field
     MN_HIDDEN_MEMBER         = 0x00400000, // @Hidden annotation detected
-    MN_REFERENCE_KIND_SHIFT  = 24, // refKind
-    MN_REFERENCE_KIND_MASK   = 0x0F000000 >> MN_REFERENCE_KIND_SHIFT,
+    MN_FLAT_FIELD            = 0x00800000, // flat field
+    MN_NULL_RESTRICTED_FIELD = 0x01000000, // null-restricted field
+    MN_REFERENCE_KIND_SHIFT  = 26, // refKind
+    MN_REFERENCE_KIND_MASK   = 0x3C000000 >> MN_REFERENCE_KIND_SHIFT,
     MN_NESTMATE_CLASS        = 0x00000001,
     MN_HIDDEN_CLASS          = 0x00000002,
     MN_STRONG_LOADER_LINK    = 0x00000004,
     MN_ACCESS_VM_ANNOTATIONS = 0x00000008,
     // Lookup modes
@@ -1855,11 +1860,10 @@
   static void compute_offsets(InstanceKlass* k);
   static objArrayOop  cache(InstanceKlass *k);
   static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN;
 };
 
-
 // Interface to java.lang.InternalError objects
 
 #define INTERNALERROR_INJECTED_FIELDS(macro)                      \
   macro(java_lang_InternalError, during_unsafe_access, bool_signature, false)
 
diff a/src/hotspot/share/classfile/placeholders.cpp b/src/hotspot/share/classfile/placeholders.cpp
--- a/src/hotspot/share/classfile/placeholders.cpp
+++ b/src/hotspot/share/classfile/placeholders.cpp
@@ -60,10 +60,12 @@
 // For bootclasssearchpath, set before calling load_instance_class.
 // Defining must be single threaded on a class/classloader basis
 // For DEFINE_CLASS, the head of the queue owns the
 // define token and the rest of the threads wait to return the
 // result the first thread gets.
+// For INLINE_FIELD, set when loading inline type fields for
+// class circularity checking.
 class SeenThread: public CHeapObj<mtInternal> {
 private:
    JavaThread* _thread;
    SeenThread* _stnext;
    SeenThread* _stprev;
@@ -93,12 +95,12 @@
   SeenThread* queuehead = nullptr;
   switch (action) {
     case PlaceholderTable::LOAD_INSTANCE:
        queuehead = _loadInstanceThreadQ;
        break;
-    case PlaceholderTable::LOAD_SUPER:
-       queuehead = _superThreadQ;
+    case PlaceholderTable::DETECT_CIRCULARITY:
+       queuehead = _circularityThreadQ;
        break;
     case PlaceholderTable::DEFINE_CLASS:
        queuehead = _defineThreadQ;
        break;
     default: Unimplemented();
@@ -109,12 +111,12 @@
 void PlaceholderEntry::set_threadQ(SeenThread* seenthread, PlaceholderTable::classloadAction action) {
   switch (action) {
     case PlaceholderTable::LOAD_INSTANCE:
        _loadInstanceThreadQ = seenthread;
        break;
-    case PlaceholderTable::LOAD_SUPER:
-       _superThreadQ = seenthread;
+    case PlaceholderTable::DETECT_CIRCULARITY:
+       _circularityThreadQ = seenthread;
        break;
     case PlaceholderTable::DEFINE_CLASS:
        _defineThreadQ = seenthread;
        break;
     default: Unimplemented();
@@ -186,27 +188,27 @@
   }
   return (actionToQueue(action) == nullptr);
 }
 
 
-void PlaceholderEntry::set_supername(Symbol* supername) {
+void PlaceholderEntry::set_next_klass_name(Symbol* next_klass_name) {
   assert_locked_or_safepoint(SystemDictionary_lock);
-  assert(_supername == nullptr || _supername->refcount() > 1, "must be referenced also by the loader");
-  _supername = supername;
+  assert(_next_klass_name == nullptr || _next_klass_name->refcount() > 1, "must be referenced also by the loader");
+  _next_klass_name = next_klass_name;
 }
 
 // Placeholder objects represent classes currently being loaded.
 // All threads examining the placeholder table must hold the
 // SystemDictionary_lock, so we don't need special precautions
 // on store ordering here.
 PlaceholderEntry* add_entry(Symbol* class_name, ClassLoaderData* loader_data,
-                            Symbol* supername){
+                            Symbol* next_klass_name){
   assert_locked_or_safepoint(SystemDictionary_lock);
   assert(class_name != nullptr, "adding nullptr obj");
 
   PlaceholderEntry entry;
-  entry.set_supername(supername);
+  entry.set_next_klass_name(next_klass_name);
   PlaceholderKey key(class_name, loader_data);
   bool created;
   PlaceholderEntry* table_copy = _placeholders->put_if_absent(key, entry, &created);
   assert(created, "better be absent");
   return table_copy;
@@ -228,11 +230,11 @@
 }
 
 static const char* action_to_string(PlaceholderTable::classloadAction action) {
   switch (action) {
   case PlaceholderTable::LOAD_INSTANCE: return "LOAD_INSTANCE";
-  case PlaceholderTable::LOAD_SUPER:    return "LOAD_SUPER";
+  case PlaceholderTable::DETECT_CIRCULARITY:    return "DETECT_CIRCULARITY";
   case PlaceholderTable::DEFINE_CLASS:  return "DEFINE_CLASS";
  }
  return "";
 }
 
@@ -248,24 +250,24 @@
 
 // find_and_add returns probe pointer - old or new
 // If no entry exists, add a placeholder entry
 // If entry exists, reuse entry
 // For both, push SeenThread for classloadAction
-// If LOAD_SUPER, this is used for circularity detection for instanceklass loading.
+// If DETECT_CIRCULARITY, this is used for circularity detection for instanceklass loading.
 PlaceholderEntry* PlaceholderTable::find_and_add(Symbol* name,
                                                  ClassLoaderData* loader_data,
                                                  classloadAction action,
-                                                 Symbol* supername,
+                                                 Symbol* next_klass_name,
                                                  JavaThread* thread) {
-  assert(action != LOAD_SUPER || supername != nullptr, "must have a super class name");
+  assert(action != DETECT_CIRCULARITY || next_klass_name != nullptr, "must have a super class name");
   PlaceholderEntry* probe = get_entry(name, loader_data);
   if (probe == nullptr) {
     // Nothing found, add place holder
-    probe = add_entry(name, loader_data, supername);
+    probe = add_entry(name, loader_data, next_klass_name);
   } else {
-    if (action == LOAD_SUPER) {
-      probe->set_supername(supername);
+    if (action == DETECT_CIRCULARITY) {
+      probe->set_next_klass_name(next_klass_name);
     }
   }
   probe->add_seen_thread(thread, action);
   log(name, probe, "find_and_add", action);
   return probe;
@@ -294,11 +296,11 @@
   PlaceholderEntry* probe = get_entry(name, loader_data);
   assert(probe != nullptr, "must find an entry");
   log(name, probe, "find_and_remove", action);
   probe->remove_seen_thread(thread, action);
   if (probe->superThreadQ() == nullptr) {
-    probe->set_supername(nullptr);
+    probe->set_next_klass_name(nullptr);
   }
   // If no other threads using this entry, and this thread is not using this entry for other states
   if ((probe->superThreadQ() == nullptr) && (probe->loadInstanceThreadQ() == nullptr)
       && (probe->defineThreadQ() == nullptr) && (probe->definer() == nullptr)) {
     remove_entry(name, loader_data);
@@ -310,13 +312,13 @@
   st->print(", loader ");
   _loader_data->print_value_on(st);
 }
 
 void PlaceholderEntry::print_on(outputStream* st) const {
-  if (supername() != nullptr) {
-    st->print(", supername ");
-    supername()->print_value_on(st);
+  if (next_klass_name() != nullptr) {
+    st->print(", next_klass_name ");
+    next_klass_name()->print_value_on(st);
   }
   if (definer() != nullptr) {
     st->print(", definer ");
     definer()->print_value_on(st);
   }
diff a/src/hotspot/share/classfile/placeholders.hpp b/src/hotspot/share/classfile/placeholders.hpp
--- a/src/hotspot/share/classfile/placeholders.hpp
+++ b/src/hotspot/share/classfile/placeholders.hpp
@@ -39,19 +39,20 @@
  public:
   // caller to create a placeholder entry must enumerate an action
   // caller claims ownership of that action
   // For parallel classloading:
   // multiple LOAD_INSTANCE threads can proceed in parallel
-  // multiple LOAD_SUPER threads can proceed in parallel
-  // LOAD_SUPER needed to check for class circularity
+  // multiple DETECT_CIRCULARITY threads can proceed in parallel
+  // DETECT_CIRCULARITY needed to check for class circularity
   // DEFINE_CLASS: ultimately define class must be single threaded
   // on a class/classloader basis
   // so the head of that queue owns the token
   // and the rest of the threads return the result the first thread gets
+  // VALUE_OBJECT_FIELD: needed to check for inline type fields circularity
   enum classloadAction {
     LOAD_INSTANCE = 1,             // calling load_instance_class
-    LOAD_SUPER = 2,                // loading superclass for this class
+    DETECT_CIRCULARITY = 2,        // loading while detecting class circularity
     DEFINE_CLASS = 3               // find_or_define class
   };
   static void initialize();
   static PlaceholderEntry* get_entry(Symbol* name, ClassLoaderData* loader_data);
 
@@ -79,14 +80,14 @@
 // on store ordering here.
 // The system dictionary is the only user of this class.
 class PlaceholderEntry {
   friend class PlaceholderTable;
  private:
-  SymbolHandle      _supername;
+  SymbolHandle      _next_klass_name;
   JavaThread*       _definer;       // owner of define token
   InstanceKlass*    _instanceKlass; // InstanceKlass from successful define
-  SeenThread*       _superThreadQ;  // doubly-linked queue of Threads loading a superclass for this class
+  SeenThread*       _circularityThreadQ;  // doubly-linked queue of Threads loading with circularity detection
   SeenThread*       _loadInstanceThreadQ;  // loadInstance thread
                                     // This can't be multiple threads since class loading waits for
                                     // this token to be removed.
 
   SeenThread*       _defineThreadQ; // queue of Threads trying to define this class
@@ -97,34 +98,34 @@
   SeenThread* actionToQueue(PlaceholderTable::classloadAction action);
   void set_threadQ(SeenThread* seenthread, PlaceholderTable::classloadAction action);
   void add_seen_thread(JavaThread* thread, PlaceholderTable::classloadAction action);
   bool remove_seen_thread(JavaThread* thread, PlaceholderTable::classloadAction action);
 
-  SeenThread*        superThreadQ()        const { return _superThreadQ; }
-  void               set_superThreadQ(SeenThread* SeenThread) { _superThreadQ = SeenThread; }
+  SeenThread*        superThreadQ()        const { return _circularityThreadQ; }
+  void               set_superThreadQ(SeenThread* SeenThread) { _circularityThreadQ = SeenThread; }
 
   SeenThread*        loadInstanceThreadQ() const { return _loadInstanceThreadQ; }
   void               set_loadInstanceThreadQ(SeenThread* SeenThread) { _loadInstanceThreadQ = SeenThread; }
 
   SeenThread*        defineThreadQ()       const { return _defineThreadQ; }
   void               set_defineThreadQ(SeenThread* SeenThread) { _defineThreadQ = SeenThread; }
  public:
   PlaceholderEntry() :
      _definer(nullptr), _instanceKlass(nullptr),
-     _superThreadQ(nullptr), _loadInstanceThreadQ(nullptr), _defineThreadQ(nullptr) { }
+     _circularityThreadQ(nullptr), _loadInstanceThreadQ(nullptr), _defineThreadQ(nullptr) { }
 
-  Symbol*            supername()           const { return _supername; }
-  void               set_supername(Symbol* supername);
+  Symbol*            next_klass_name()           const { return _next_klass_name; }
+  void               set_next_klass_name(Symbol* next_klass_name);
 
   JavaThread*        definer()             const {return _definer; }
   void               set_definer(JavaThread* definer) { _definer = definer; }
 
   InstanceKlass*     instance_klass()      const {return _instanceKlass; }
   void               set_instance_klass(InstanceKlass* ik) { _instanceKlass = ik; }
 
-  bool super_load_in_progress() {
-     return (_superThreadQ != nullptr);
+  bool circularity_detection_in_progress() {
+     return (_circularityThreadQ != nullptr);
   }
 
   bool instance_load_in_progress() {
     return (_loadInstanceThreadQ != nullptr);
   }
diff a/src/hotspot/share/classfile/stackMapFrame.cpp b/src/hotspot/share/classfile/stackMapFrame.cpp
--- a/src/hotspot/share/classfile/stackMapFrame.cpp
+++ b/src/hotspot/share/classfile/stackMapFrame.cpp
@@ -79,11 +79,11 @@
   SignatureStream ss(m->signature());
   int init_local_num = 0;
   if (!m->is_static()) {
     init_local_num++;
     // add one extra argument for instance method
-    if (m->name() == vmSymbols::object_initializer_name() &&
+    if (m->is_object_constructor() &&
        thisKlass.name() != vmSymbols::java_lang_Object()) {
       _locals[0] = VerificationType::uninitialized_this_type();
       _flags |= FLAG_THIS_UNINIT;
     } else {
       _locals[0] = thisKlass;
diff a/src/hotspot/share/classfile/stackMapTable.cpp b/src/hotspot/share/classfile/stackMapTable.cpp
--- a/src/hotspot/share/classfile/stackMapTable.cpp
+++ b/src/hotspot/share/classfile/stackMapTable.cpp
@@ -187,11 +187,12 @@
         (!_cp->tag_at(class_index).is_klass() &&
          !_cp->tag_at(class_index).is_unresolved_klass())) {
       _stream->stackmap_format_error("bad class index", THREAD);
       return VerificationType::bogus_type();
     }
-    return VerificationType::reference_type(_cp->klass_name_at(class_index));
+    Symbol* klass_name = _cp->klass_name_at(class_index);
+    return VerificationType::reference_type(klass_name);
   }
   if (tag == ITEM_UninitializedThis) {
     if (flags != nullptr) {
       *flags |= FLAG_THIS_UNINIT;
     }
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
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
+ * 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.
@@ -54,10 +54,11 @@
 #include "memory/metaspaceClosure.hpp"
 #include "memory/oopFactory.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
 #include "oops/access.inline.hpp"
+#include "oops/fieldStreams.inline.hpp"
 #include "oops/instanceKlass.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/method.inline.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "oops/objArrayOop.inline.hpp"
@@ -65,18 +66,20 @@
 #include "oops/oop.hpp"
 #include "oops/oopHandle.hpp"
 #include "oops/oopHandle.inline.hpp"
 #include "oops/symbol.hpp"
 #include "oops/typeArrayKlass.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "prims/methodHandles.hpp"
 #include "runtime/arguments.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/java.hpp"
 #include "runtime/javaCalls.hpp"
 #include "runtime/mutexLocker.hpp"
+#include "runtime/os.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/signature.hpp"
 #include "runtime/synchronizer.hpp"
 #include "services/classLoadingService.hpp"
 #include "services/diagnosticCommand.hpp"
@@ -333,11 +336,11 @@
     return resolve_array_class_or_null(class_name, class_loader, protection_domain, THREAD);
   } else {
     assert(class_name != nullptr && !Signature::is_array(class_name), "must be");
     if (Signature::has_envelope(class_name)) {
       ResourceMark rm(THREAD);
-      // Ignore wrapping L and ;.
+      // Ignore wrapping L and ; (and Q and ; for value types).
       TempNewSymbol name = SymbolTable::new_symbol(class_name->as_C_string() + 1,
                                                    class_name->utf8_length() - 2);
       return resolve_instance_class_or_null(name, class_loader, protection_domain, THREAD);
     } else {
       return resolve_instance_class_or_null(class_name, class_loader, protection_domain, THREAD);
@@ -397,29 +400,29 @@
 //      a saved superclass [i.e. a defineClass is currently in progress]
 //      If another thread is trying to resolve the class, it must do
 //      superclass checks on its own thread to catch class circularity and
 //      to avoid deadlock.
 //
-// resolve_super_or_fail adds a LOAD_SUPER placeholder to the placeholder table before calling
-// resolve_instance_class_or_null. ClassCircularityError is detected when a LOAD_SUPER or LOAD_INSTANCE
+// resolve_with_circularity_detection_or_fail adds a DETECT_CIRCULARITY placeholder to the placeholder table before calling
+// resolve_instance_class_or_null. ClassCircularityError is detected when a DETECT_CIRCULARITY or LOAD_INSTANCE
 // placeholder for the same thread, class, classloader is found.
 // This can be seen with logging option: -Xlog:class+load+placeholders=debug.
 //
-InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* class_name,
-                                                       Symbol* super_name,
+InstanceKlass* SystemDictionary::resolve_with_circularity_detection_or_fail(Symbol* class_name,
+                                                       Symbol* next_name,
                                                        Handle class_loader,
                                                        Handle protection_domain,
                                                        bool is_superclass,
                                                        TRAPS) {
 
-  assert(super_name != nullptr, "null superclass for resolving");
-  assert(!Signature::is_array(super_name), "invalid superclass name");
+  assert(next_name != nullptr, "null superclass for resolving");
+  assert(!Signature::is_array(next_name), "invalid superclass name");
 #if INCLUDE_CDS
   if (CDSConfig::is_dumping_static_archive()) {
     // Special processing for handling UNREGISTERED shared classes.
     InstanceKlass* k = SystemDictionaryShared::lookup_super_for_unregistered_class(class_name,
-                           super_name, is_superclass);
+                           next_name, is_superclass);
     if (k) {
       return k;
     }
   }
 #endif // INCLUDE_CDS
@@ -437,57 +440,57 @@
   {
     MutexLocker mu(THREAD, SystemDictionary_lock);
     InstanceKlass* klassk = dictionary->find_class(THREAD, class_name);
     InstanceKlass* quicksuperk;
     // To support parallel loading: if class is done loading, just return the superclass
-    // if the super_name matches class->super()->name() and if the class loaders match.
+    // if the next_name matches class->super()->name() and if the class loaders match.
     // Otherwise, a LinkageError will be thrown later.
     if (klassk != nullptr && is_superclass &&
         ((quicksuperk = klassk->java_super()) != nullptr) &&
-         ((quicksuperk->name() == super_name) &&
+         ((quicksuperk->name() == next_name) &&
             (quicksuperk->class_loader() == class_loader()))) {
            return quicksuperk;
     } else {
       // Must check ClassCircularity before checking if superclass is already loaded.
       PlaceholderEntry* probe = PlaceholderTable::get_entry(class_name, loader_data);
-      if (probe && probe->check_seen_thread(THREAD, PlaceholderTable::LOAD_SUPER)) {
+      if (probe && probe->check_seen_thread(THREAD, PlaceholderTable::DETECT_CIRCULARITY)) {
           log_circularity_error(class_name, probe);
           throw_circularity_error = true;
       }
     }
 
     if (!throw_circularity_error) {
       // Be careful not to exit resolve_super without removing this placeholder.
       PlaceholderEntry* newprobe = PlaceholderTable::find_and_add(class_name,
                                                                   loader_data,
-                                                                  PlaceholderTable::LOAD_SUPER,
-                                                                  super_name, THREAD);
+                                                                  PlaceholderTable::DETECT_CIRCULARITY,
+                                                                  next_name, THREAD);
     }
   }
 
   if (throw_circularity_error) {
       ResourceMark rm(THREAD);
       THROW_MSG_NULL(vmSymbols::java_lang_ClassCircularityError(), class_name->as_C_string());
   }
 
   // Resolve the superclass or superinterface, check results on return
   InstanceKlass* superk =
-    SystemDictionary::resolve_instance_class_or_null(super_name,
+    SystemDictionary::resolve_instance_class_or_null(next_name,
                                                      class_loader,
                                                      protection_domain,
                                                      THREAD);
 
   // Clean up placeholder entry.
   {
     MutexLocker mu(THREAD, SystemDictionary_lock);
-    PlaceholderTable::find_and_remove(class_name, loader_data, PlaceholderTable::LOAD_SUPER, THREAD);
+    PlaceholderTable::find_and_remove(class_name, loader_data, PlaceholderTable::DETECT_CIRCULARITY, THREAD);
     SystemDictionary_lock->notify_all();
   }
 
   // Check for pending exception or null superk, and throw exception
   if (HAS_PENDING_EXCEPTION || superk == nullptr) {
-    handle_resolution_exception(super_name, true, CHECK_NULL);
+    handle_resolution_exception(next_name, true, CHECK_NULL);
   }
 
   return superk;
 }
 
@@ -501,15 +504,15 @@
                                        Symbol* superclassname,
                                        Handle class_loader,
                                        Handle protection_domain, TRAPS) {
 
   // superk is not used; resolve_super_or_fail is called for circularity check only.
-  Klass* superk = SystemDictionary::resolve_super_or_fail(name,
+  Klass* superk = SystemDictionary::resolve_with_circularity_detection_or_fail(name,
                                                           superclassname,
                                                           class_loader,
                                                           protection_domain,
-                                                          true,
+                                                          false,
                                                           CHECK);
 }
 
 // Bootstrap and non-parallel capable class loaders use the LOAD_INSTANCE placeholder to
 // wait for parallel class loading and/or to check for circularity error for Xcomp when loading.
@@ -534,11 +537,11 @@
       return nullptr;
     } else if (must_wait_for_class_loading) {
       // Wait until the first thread has finished loading this class. Also wait until all the
       // threads trying to load its superclass have removed their placeholders.
       while (oldprobe != nullptr &&
-             (oldprobe->instance_load_in_progress() || oldprobe->super_load_in_progress())) {
+             (oldprobe->instance_load_in_progress() || oldprobe->circularity_detection_in_progress())) {
 
         // LOAD_INSTANCE placeholders are used to implement parallel capable class loading
         // for the bootclass loader.
         SystemDictionary_lock->wait();
 
@@ -574,11 +577,11 @@
                                                                 Handle class_loader,
                                                                 Handle protection_domain,
                                                                 TRAPS) {
   // name must be in the form of "java/lang/Object" -- cannot be "Ljava/lang/Object;"
   assert(name != nullptr && !Signature::is_array(name) &&
-         !Signature::has_envelope(name), "invalid class name");
+         !Signature::has_envelope(name), "invalid class name: %s", name == nullptr ? "nullptr" : name->as_C_string());
 
   EventClassLoad class_load_start_event;
 
   HandleMark hm(THREAD);
 
@@ -605,11 +608,11 @@
   // ParallelCapable class loaders and the bootstrap classloader
   // do not acquire lock here.
   Handle lockObject = get_loader_lock_or_null(class_loader);
   ObjectLocker ol(lockObject, THREAD);
 
-  bool super_load_in_progress  = false;
+  bool circularity_detection_in_progress  = false;
   InstanceKlass* loaded_class = nullptr;
   SymbolHandle superclassname; // Keep alive while loading in parallel thread.
 
   guarantee(THREAD->can_call_java(),
          "can not load classes with compiler thread: class=%s, classloader=%s",
@@ -623,21 +626,21 @@
     if (check != nullptr) {
       // InstanceKlass is already loaded, but we still need to check protection domain below.
       loaded_class = check;
     } else {
       PlaceholderEntry* placeholder = PlaceholderTable::get_entry(name, loader_data);
-      if (placeholder != nullptr && placeholder->super_load_in_progress()) {
-         super_load_in_progress = true;
-         superclassname = placeholder->supername();
+      if (placeholder != nullptr && placeholder->circularity_detection_in_progress()) {
+         circularity_detection_in_progress = true;
+         superclassname = placeholder->next_klass_name();
          assert(superclassname != nullptr, "superclass has to have a name");
       }
     }
   }
 
   // If the class is in the placeholder table with super_class set,
   // handle superclass loading in progress.
-  if (super_load_in_progress) {
+  if (circularity_detection_in_progress) {
     handle_parallel_super_load(name, superclassname,
                                class_loader,
                                protection_domain,
                                CHECK_NULL);
   }
@@ -935,12 +938,11 @@
 // Check if a shared class can be loaded by the specific classloader.
 bool SystemDictionary::is_shared_class_visible(Symbol* class_name,
                                                InstanceKlass* ik,
                                                PackageEntry* pkg_entry,
                                                Handle class_loader) {
-  assert(!ModuleEntryTable::javabase_moduleEntry()->is_patched(),
-         "Cannot use sharing if java.base is patched");
+  assert(!CDSConfig::module_patching_disables_cds(), "Cannot use CDS");
 
   // (1) Check if we are loading into the same loader as in dump time.
 
   if (ik->is_shared_boot_class()) {
     if (class_loader() != nullptr) {
@@ -1012,11 +1014,11 @@
   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");
+        assert(!CDSConfig::module_patching_disables_cds(), "Cannot use CDS");
       }
     } else {
       // During dump time, this class was in a named module, but at run time, this class should be
       // in an unnamed module.
       visible = false;
@@ -1050,11 +1052,11 @@
     if (check == super_type) {
       return true;
     }
   }
 
-  Klass *found = resolve_super_or_fail(klass->name(), super_type->name(),
+  Klass *found = resolve_with_circularity_detection_or_fail(klass->name(), super_type->name(),
                                        class_loader, protection_domain, is_superclass, CHECK_0);
   if (found == super_type) {
     return true;
   } else {
     // The dynamically resolved super type is not the same as the one we used during dump time,
@@ -1139,10 +1141,42 @@
 
   if (!check_shared_class_super_types(ik, class_loader, protection_domain, THREAD)) {
     return nullptr;
   }
 
+  if (ik->has_inline_type_fields()) {
+    for (AllFieldStream fs(ik); !fs.done(); fs.next()) {
+      if (fs.access_flags().is_static()) continue;
+      Symbol* sig = fs.signature();
+      if (fs.is_null_free_inline_type()) {
+        // Pre-load inline class
+        TempNewSymbol name = Signature::strip_envelope(sig);
+        Klass* real_k = SystemDictionary::resolve_with_circularity_detection_or_fail(ik->name(), name,
+          class_loader, protection_domain, false, CHECK_NULL);
+        Klass* k = ik->get_inline_type_field_klass_or_null(fs.index());
+        if (real_k != k) {
+          // oops, the app has substituted a different version of k!
+          return nullptr;
+        }
+      } else if (Signature::has_envelope(sig)) {
+        TempNewSymbol name = Signature::strip_envelope(sig);
+        if (name != ik->name() && ik->is_class_in_preload_attribute(name)) {
+          Klass* real_k = SystemDictionary::resolve_with_circularity_detection_or_fail(ik->name(), name,
+            class_loader, protection_domain, false, THREAD);
+          if (HAS_PENDING_EXCEPTION) {
+            CLEAR_PENDING_EXCEPTION;
+          }
+          Klass* k = ik->get_inline_type_field_klass_or_null(fs.index());
+          if (real_k != k) {
+            // oops, the app has substituted a different version of k!
+            return nullptr;
+          }
+        }
+      }
+    }
+  }
+
   InstanceKlass* new_ik = nullptr;
   // CFLH check is skipped for VM hidden classes (see KlassFactory::create_from_stream).
   // It will be skipped for shared VM hidden lambda proxy classes.
   if (!SystemDictionaryShared::is_hidden_lambda_proxy(ik)) {
     new_ik = KlassFactory::check_shared_class_file_load_hook(
@@ -1174,10 +1208,11 @@
     // restore_unshareable_info which calls ik->set_package()
     ik->restore_unshareable_info(loader_data, protection_domain, pkg_entry, CHECK_NULL);
   }
 
   load_shared_class_misc(ik, loader_data);
+
   return ik;
 }
 
 void SystemDictionary::load_shared_class_misc(InstanceKlass* ik, ClassLoaderData* loader_data) {
   ik->print_class_load_logging(loader_data, nullptr, nullptr);
diff a/src/hotspot/share/classfile/systemDictionary.hpp b/src/hotspot/share/classfile/systemDictionary.hpp
--- a/src/hotspot/share/classfile/systemDictionary.hpp
+++ b/src/hotspot/share/classfile/systemDictionary.hpp
@@ -68,10 +68,11 @@
 
 class BootstrapInfo;
 class ClassFileStream;
 class ClassLoadInfo;
 class Dictionary;
+class AllFieldStream;
 class PackageEntry;
 class GCTimer;
 class EventClassLoad;
 class Symbol;
 
@@ -103,11 +104,11 @@
   }
 
   // Resolve a superclass or superinterface. Called from ClassFileParser,
   // parse_interfaces, resolve_instance_class_or_null, load_shared_class
   // "class_name" is the class whose super class or interface is being resolved.
-  static InstanceKlass* resolve_super_or_fail(Symbol* class_name,
+  static InstanceKlass* resolve_with_circularity_detection_or_fail(Symbol* class_name,
                                               Symbol* super_name,
                                               Handle class_loader,
                                               Handle protection_domain,
                                               bool is_superclass,
                                               TRAPS);
@@ -130,10 +131,11 @@
 
   static oop get_system_class_loader_impl(TRAPS);
   static oop get_platform_class_loader_impl(TRAPS);
 
  public:
+
   // Resolve either a hidden or normal class from a stream of bytes, based on ClassLoadInfo
   static InstanceKlass* resolve_from_stream(ClassFileStream* st,
                                             Symbol* class_name,
                                             Handle class_loader,
                                             const ClassLoadInfo& cl_info,
diff a/src/hotspot/share/classfile/verificationType.cpp b/src/hotspot/share/classfile/verificationType.cpp
--- a/src/hotspot/share/classfile/verificationType.cpp
+++ b/src/hotspot/share/classfile/verificationType.cpp
@@ -62,15 +62,22 @@
     if (log_is_enabled(Debug, class, resolve)) {
       Verifier::trace_class_resolution(this_class, klass);
     }
   }
 
+  // Need to do this check when called from CDS.
+  // if (this_class->access_flags().is_primitive_class()) {
+  //   Klass* from_class = SystemDictionary::resolve_or_fail(
+  //     from_name, Handle(THREAD, klass->class_loader()),
+  //     Handle(THREAD, klass->protection_domain()), true, CHECK_false);
+  //   return from_class == this_class;
+  // }
   if (this_class->is_interface() && (!from_field_is_protected ||
       from_name != vmSymbols::java_lang_Object())) {
     // If we are not trying to access a protected field or method in
     // java.lang.Object then, for arrays, we only allow assignability
-    // to interfaces java.lang.Cloneable and java.io.Serializable.
+    // to interfaces java.lang.Cloneable and java.io.Serializable
     // Otherwise, we treat interfaces as java.lang.Object.
     return !from_is_array ||
       this_class == vmClasses::Cloneable_klass() ||
       this_class == vmClasses::Serializable_klass();
   } else if (from_is_object) {
@@ -122,18 +129,86 @@
     return resolve_and_check_assignability(klass, name(), from.name(),
           from_field_is_protected, from.is_array(), from.is_object(), THREAD);
   } else if (is_array() && from.is_array()) {
     VerificationType comp_this = get_component(context);
     VerificationType comp_from = from.get_component(context);
+
+/*
+    // This code implements non-covariance between inline type arrays and both
+    // arrays of objects and arrays of interface types.  If covariance is
+    // supported for inline type arrays then this code should be removed.
+    if (comp_from.is_inline_type() && !comp_this.is_null() && comp_this.is_reference()) {
+      // An array of inline types is not assignable to an array of java.lang.Objects.
+      if (comp_this.name() == vmSymbols::java_lang_Object()) {
+        return false;
+      }
+
+      // Need to load 'comp_this' to see if it is an interface.
+      InstanceKlass* klass = context->current_class();
+      {
+        HandleMark hm(THREAD);
+        Klass* comp_this_class = SystemDictionary::resolve_or_fail(
+            comp_this.name(), Handle(THREAD, klass->class_loader()),
+            Handle(THREAD, klass->protection_domain()), true, CHECK_false);
+        klass->class_loader_data()->record_dependency(comp_this_class);
+        if (log_is_enabled(Debug, class, resolve)) {
+          Verifier::trace_class_resolution(comp_this_class, klass);
+        }
+        // An array of inline types is not assignable to an array of interface types.
+        if (comp_this_class->is_interface()) {
+          return false;
+        }
+      }
+    }
+*/
     if (!comp_this.is_bogus() && !comp_from.is_bogus()) {
       return comp_this.is_component_assignable_from(comp_from, context,
                                                     from_field_is_protected, THREAD);
     }
   }
   return false;
 }
 
+bool VerificationType::is_inline_type_assignable_from(const VerificationType& from) const {
+  // Check that 'from' is not null, is an inline type, and is the same inline type.
+  assert(is_inline_type(), "called with a non-inline type");
+  assert(!is_null(), "inline type is not null");
+  return (!from.is_null() && from.is_inline_type() && name() == from.name());
+}
+
+bool VerificationType::is_ref_assignable_from_inline_type(const VerificationType& from, ClassVerifier* context, TRAPS) const {
+  assert(!from.is_null(), "Inline type should not be null");
+  if (!is_null() && (name()->is_same_fundamental_type(from.name()) ||
+      name() == vmSymbols::java_lang_Object())) {
+    return true;
+  }
+
+  // Need to load 'this' to see if it is an interface or supertype.
+  InstanceKlass* klass = context->current_class();
+  {
+    HandleMark hm(THREAD);
+    Klass* this_class = SystemDictionary::resolve_or_fail(
+        name(), Handle(THREAD, klass->class_loader()),
+        Handle(THREAD, klass->protection_domain()), true, CHECK_false);
+    klass->class_loader_data()->record_dependency(this_class);
+    if (log_is_enabled(Debug, class, resolve)) {
+      Verifier::trace_class_resolution(this_class, klass);
+    }
+    if (this_class->is_interface()) {
+      return true;
+    } else {
+      Klass* from_class = SystemDictionary::resolve_or_fail(
+        from.name(), Handle(THREAD, klass->class_loader()),
+        Handle(THREAD, klass->protection_domain()), true, CHECK_false);
+      if (log_is_enabled(Debug, class, resolve)) {
+        Verifier::trace_class_resolution(from_class, klass);
+      }
+      return from_class->is_subclass_of(this_class);
+    }
+  }
+}
+
 VerificationType VerificationType::get_component(ClassVerifier *context) const {
   assert(is_array() && name()->utf8_length() >= 2, "Must be a valid array");
   SignatureStream ss(name(), false);
   ss.skip_array_prefix(1);
   switch (ss.type()) {
@@ -176,10 +251,12 @@
     case Double:           st->print("double"); break;
     case Long_2nd:         st->print("long_2nd"); break;
     case Double_2nd:       st->print("double_2nd"); break;
     case Null:             st->print("null"); break;
     case ReferenceQuery:   st->print("reference type"); break;
+    case InlineTypeQuery:  st->print("inline type"); break;
+    case NonScalarQuery:   st->print("reference or inline type"); break;
     case Category1Query:   st->print("category1 type"); break;
     case Category2Query:   st->print("category2 type"); break;
     case Category2_2ndQuery: st->print("category2_2nd type"); break;
     default:
       if (is_uninitialized_this()) {
diff a/src/hotspot/share/classfile/verificationType.hpp b/src/hotspot/share/classfile/verificationType.hpp
--- a/src/hotspot/share/classfile/verificationType.hpp
+++ b/src/hotspot/share/classfile/verificationType.hpp
@@ -66,25 +66,28 @@
       ITEM_Long_2nd, ITEM_Double_2nd
     };
 
     // Enum for the _data field
     enum : uint {
-      // Bottom two bits determine if the type is a reference, primitive,
-      // uninitialized or a query-type.
-      TypeMask           = 0x00000003,
+      // Bottom three bits determine if the type is a reference, inline type,
+      // primitive, uninitialized or a query-type.
+      TypeMask           = 0x00000007,
 
       // Topmost types encoding
-      Reference          = 0x0,        // _sym contains the name
+      Reference          = 0x0,        // _sym contains the name of an object
       Primitive          = 0x1,        // see below for primitive list
       Uninitialized      = 0x2,        // 0x00ffff00 contains bci
       TypeQuery          = 0x3,        // Meta-types used for category testing
+      InlineType         = 0x4,        // _sym contains the name of an inline type
 
       // Utility flags
       ReferenceFlag      = 0x00,       // For reference query types
       Category1Flag      = 0x01,       // One-word values
       Category2Flag      = 0x02,       // First word of a two-word value
       Category2_2ndFlag  = 0x04,       // Second word of a two-word value
+      InlineTypeFlag     = 0x08,       // For inline type query types
+      NonScalarFlag      = 0x10,       // For either inline type or reference queries
 
       // special reference values
       Null               = 0x00000000, // A reference with a 0 sym is null
 
       // Primitives categories (the second byte determines the category)
@@ -112,11 +115,13 @@
 
       // Query values
       ReferenceQuery     = (ReferenceFlag     << 1 * BitsPerByte) | TypeQuery,
       Category1Query     = (Category1Flag     << 1 * BitsPerByte) | TypeQuery,
       Category2Query     = (Category2Flag     << 1 * BitsPerByte) | TypeQuery,
-      Category2_2ndQuery = (Category2_2ndFlag << 1 * BitsPerByte) | TypeQuery
+      Category2_2ndQuery = (Category2_2ndFlag << 1 * BitsPerByte) | TypeQuery,
+      InlineTypeQuery    = (InlineTypeFlag    << 1 * BitsPerByte) | TypeQuery,
+      NonScalarQuery     = (NonScalarFlag     << 1 * BitsPerByte) | TypeQuery
     };
 
   VerificationType(uintptr_t raw_data) {
     _u._data = raw_data;
   }
@@ -145,30 +150,45 @@
   // to anything, but the specified types are assignable to a "check".  For
   // example, any category1 primitive is assignable to category1_check and
   // any reference is assignable to reference_check.
   static VerificationType reference_check()
     { return VerificationType(ReferenceQuery); }
+  static VerificationType inline_type_check()
+    { return VerificationType(InlineTypeQuery); }
   static VerificationType category1_check()
     { return VerificationType(Category1Query); }
   static VerificationType category2_check()
     { return VerificationType(Category2Query); }
   static VerificationType category2_2nd_check()
     { return VerificationType(Category2_2ndQuery); }
+  static VerificationType nonscalar_check()
+    { return VerificationType(NonScalarQuery); }
 
   // For reference types, store the actual Symbol
   static VerificationType reference_type(Symbol* sh) {
-      assert(((uintptr_t)sh & 0x3) == 0, "Symbols must be aligned");
+      assert(((uintptr_t)sh & TypeMask) == 0, "Symbols must be aligned");
       // If the above assert fails in the future because oop* isn't aligned,
       // then this type encoding system will have to change to have a tag value
       // to discriminate between oops and primitives.
       return VerificationType((uintptr_t)sh);
   }
   static VerificationType uninitialized_type(u2 bci)
     { return VerificationType(bci << 1 * BitsPerByte | Uninitialized); }
   static VerificationType uninitialized_this_type()
     { return uninitialized_type(BciForThis); }
 
+  // For inline types, store the actual Symbol* and set the 3rd bit.
+  // Provides a way for an inline type to be distinguished from a reference type.
+  static VerificationType inline_type(Symbol* sh) {
+      assert(((uintptr_t)sh & TypeMask) == 0, "Symbols must be aligned");
+      assert((uintptr_t)sh != 0, "Null is not a valid inline type");
+      // If the above assert fails in the future because oop* isn't aligned,
+      // then this type encoding system will have to change to have a tag value
+      // to discriminate between oops and primitives.
+      return VerificationType((uintptr_t)sh | InlineType);
+  }
+
   // Create based on u1 read from classfile
   static VerificationType from_tag(u1 tag);
 
   bool is_bogus() const     { return (_u._data == Bogus); }
   bool is_null() const      { return (_u._data == Null); }
@@ -180,15 +200,16 @@
   bool is_long() const      { return (_u._data == Long); }
   bool is_float() const     { return (_u._data == Float); }
   bool is_double() const    { return (_u._data == Double); }
   bool is_long2() const     { return (_u._data == Long_2nd); }
   bool is_double2() const   { return (_u._data == Double_2nd); }
-  bool is_reference() const { return ((_u._data & TypeMask) == Reference); }
+  bool is_reference() const { return (((_u._data & TypeMask) == Reference) && !is_inline_type_check()); }
+  bool is_inline_type() const { return ((_u._data & TypeMask) == InlineType); }
   bool is_category1() const {
     // This should return true for all one-word types, which are category1
-    // primitives, and references (including uninitialized refs).  Though
-    // the 'query' types should technically return 'false' here, if we
+    // primitives, references (including uninitialized refs) and inline types.
+    // Though the 'query' types should technically return 'false' here, if we
     // allow this to return true, we can perform the test using only
     // 2 operations rather than 8 (3 masks, 3 compares and 2 logical 'ands').
     // Since no one should call this on a query type anyway, this is ok.
     assert(!is_check(), "Must not be a check type (wrong value returned)");
     return ((_u._data & Category1) != Primitive);
@@ -198,10 +219,12 @@
   bool is_category2() const { return ((_u._data & Category2) == Category2); }
   bool is_category2_2nd() const {
     return ((_u._data & Category2_2nd) == Category2_2nd);
   }
   bool is_reference_check() const { return _u._data == ReferenceQuery; }
+  bool is_inline_type_check() const { return _u._data == InlineTypeQuery; }
+  bool is_nonscalar_check() const { return _u._data == NonScalarQuery; }
   bool is_category1_check() const { return _u._data == Category1Query; }
   bool is_category2_check() const { return _u._data == Category2Query; }
   bool is_category2_2nd_check() const { return _u._data == Category2_2ndQuery; }
   bool is_check() const { return (_u._data & TypeQuery) == TypeQuery; }
 
@@ -218,10 +241,12 @@
   bool is_double_array() const { return is_x_array(JVM_SIGNATURE_DOUBLE); }
   bool is_object_array() const { return is_x_array(JVM_SIGNATURE_CLASS); }
   bool is_array_array() const { return is_x_array(JVM_SIGNATURE_ARRAY); }
   bool is_reference_array() const
     { return is_object_array() || is_array_array(); }
+  bool is_nonscalar_array() const
+    { return is_object_array() || is_array_array(); }
   bool is_object() const
     { return (is_reference() && !is_null() && name()->utf8_length() >= 1 &&
               name()->char_at(0) != JVM_SIGNATURE_ARRAY); }
   bool is_array() const
     { return (is_reference() && !is_null() && name()->utf8_length() >= 2 &&
@@ -234,24 +259,32 @@
   VerificationType to_category2_2nd() const {
     assert(is_category2(), "Must be a double word");
     return VerificationType(is_long() ? Long_2nd : Double_2nd);
   }
 
+  static VerificationType change_ref_to_inline_type(VerificationType ref) {
+    assert(ref.is_reference(), "Bad arg");
+    assert(!ref.is_null(), "Unexpected nullptr");
+    return inline_type(ref.name());
+  }
+
   u2 bci() const {
     assert(is_uninitialized(), "Must be uninitialized type");
     return ((_u._data & BciMask) >> 1 * BitsPerByte);
   }
 
   Symbol* name() const {
-    assert(is_reference() && !is_null(), "Must be a non-null reference");
-    return _u._sym;
+    assert(!is_null() && (is_reference() || is_inline_type()), "Must be a non-null reference or an inline type");
+    return (is_reference() ? _u._sym : ((Symbol*)(_u._data & ~(uintptr_t)InlineType)));
   }
 
   bool equals(const VerificationType& t) const {
     return (_u._data == t._u._data ||
-      (is_reference() && t.is_reference() && !is_null() && !t.is_null() &&
-       name() == t.name()));
+            (((is_reference() && t.is_reference()) ||
+             (is_inline_type() && t.is_inline_type())) &&
+              !is_null() && !t.is_null() && name() == t.name()));
+
   }
 
   bool operator ==(const VerificationType& t) const {
     return equals(t);
   }
@@ -276,18 +309,27 @@
           return from.is_category2();
         case Category2_2ndQuery:
           return from.is_category2_2nd();
         case ReferenceQuery:
           return from.is_reference() || from.is_uninitialized();
+        case NonScalarQuery:
+          return from.is_reference() || from.is_uninitialized() ||
+                 from.is_inline_type();
+        case InlineTypeQuery:
+          return from.is_inline_type();
         case Boolean:
         case Byte:
         case Char:
         case Short:
           // An int can be assigned to boolean, byte, char or short values.
           return from.is_integer();
         default:
-          if (is_reference() && from.is_reference()) {
+          if (is_inline_type()) {
+            return is_inline_type_assignable_from(from);
+          } else if (is_reference() && from.is_inline_type()) {
+            return is_ref_assignable_from_inline_type(from, context, THREAD);
+          } else if (is_reference() && from.is_reference()) {
             return is_reference_assignable_from(from, context,
                                                 from_field_is_protected,
                                                 THREAD);
           } else {
             return false;
@@ -331,10 +373,15 @@
 
   bool is_reference_assignable_from(
     const VerificationType&, ClassVerifier*, bool from_field_is_protected,
     TRAPS) const;
 
+  bool is_inline_type_assignable_from(const VerificationType& from) const;
+
+  bool is_ref_assignable_from_inline_type(const VerificationType& from, ClassVerifier* context, TRAPS) const;
+
+
  public:
   static bool resolve_and_check_assignability(InstanceKlass* klass, Symbol* name,
                                               Symbol* from_name, bool from_field_is_protected,
                                               bool from_is_array, bool from_is_object,
                                               TRAPS);
diff a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp
--- a/src/hotspot/share/classfile/verifier.cpp
+++ b/src/hotspot/share/classfile/verifier.cpp
@@ -62,10 +62,11 @@
 #include "utilities/bytes.hpp"
 
 #define NOFAILOVER_MAJOR_VERSION                       51
 #define NONZERO_PADDING_BYTES_IN_SWITCH_MAJOR_VERSION  51
 #define STATIC_METHOD_IN_INTERFACE_MAJOR_VERSION       52
+#define INLINE_TYPE_MAJOR_VERSION                       56
 #define MAX_ARRAY_DIMENSIONS 255
 
 // Access to external entry for VerifyClassForMajorVersion - old byte code verifier
 
 extern "C" {
@@ -275,11 +276,11 @@
                                 klass->is_subtype_of(refl_serialization_ctor_klass);
 
   return (should_verify_for(klass->class_loader(), should_verify_class) &&
     // return if the class is a bootstrapping class
     // or defineClass specified not to verify by default (flags override passed arg)
-    // We need to skip the following four for bootstraping
+    // We need to skip the following four for bootstrapping
     name != vmSymbols::java_lang_Object() &&
     name != vmSymbols::java_lang_Class() &&
     name != vmSymbols::java_lang_String() &&
     name != vmSymbols::java_lang_Throwable() &&
 
@@ -494,10 +495,17 @@
       ss->print("Expected stackmap frame at this location.");
       break;
     case BAD_STACKMAP:
       ss->print("Invalid stackmap specification.");
       break;
+    case WRONG_INLINE_TYPE:
+      ss->print("Type ");
+      _type.details(ss);
+      ss->print(" and type ");
+      _expected.details(ss);
+      ss->print(" must be identical inline types.");
+      break;
     case UNKNOWN:
     default:
       ShouldNotReachHere();
       ss->print_cr("Unknown");
   }
@@ -588,14 +596,23 @@
   }
 }
 
 // Methods in ClassVerifier
 
+VerificationType reference_or_inline_type(InstanceKlass* klass) {
+  // if (klass->is_inline_klass()) {
+  //   return VerificationType::inline_type(klass->name());
+  // } else {
+  //   return VerificationType::reference_type(klass->name());
+  // }  // LW401 CR required: verifier update/cleanup
+  return VerificationType::reference_type(klass->name());
+}
+
 ClassVerifier::ClassVerifier(JavaThread* current, InstanceKlass* klass)
     : _thread(current), _previous_symbol(nullptr), _symbols(nullptr), _exception_type(nullptr),
       _message(nullptr), _klass(klass) {
-  _this_type = VerificationType::reference_type(klass->name());
+  _this_type = reference_or_inline_type(klass);
 }
 
 ClassVerifier::~ClassVerifier() {
   // Decrement the reference count for any symbols created.
   if (_symbols != nullptr) {
@@ -1035,11 +1052,11 @@
         case Bytecodes::_aaload : {
           type = current_frame.pop_stack(
             VerificationType::integer_type(), CHECK_VERIFY(this));
           atype = current_frame.pop_stack(
             VerificationType::reference_check(), CHECK_VERIFY(this));
-          if (!atype.is_reference_array()) {
+          if (!atype.is_nonscalar_array()) {
             verify_error(ErrorContext::bad_type(bci,
                 current_frame.stack_top_ctx(),
                 TypeOrigin::implicit(VerificationType::reference_check())),
                 bad_type_msg, "aaload");
             return;
@@ -1213,11 +1230,11 @@
           type2 = current_frame.pop_stack(
             VerificationType::integer_type(), CHECK_VERIFY(this));
           atype = current_frame.pop_stack(
             VerificationType::reference_check(), CHECK_VERIFY(this));
           // more type-checking is done at runtime
-          if (!atype.is_reference_array()) {
+          if (!atype.is_nonscalar_array()) {
             verify_error(ErrorContext::bad_type(bci,
                 current_frame.stack_top_ctx(),
                 TypeOrigin::implicit(VerificationType::reference_check())),
                 bad_type_msg, "aastore");
             return;
@@ -1613,16 +1630,16 @@
             &current_frame, target, CHECK_VERIFY(this));
           no_control_flow = false; break;
         case Bytecodes::_if_acmpeq :
         case Bytecodes::_if_acmpne :
           current_frame.pop_stack(
-            VerificationType::reference_check(), CHECK_VERIFY(this));
+            VerificationType::nonscalar_check(), CHECK_VERIFY(this));
           // fall through
         case Bytecodes::_ifnull :
         case Bytecodes::_ifnonnull :
           current_frame.pop_stack(
-            VerificationType::reference_check(), CHECK_VERIFY(this));
+            VerificationType::nonscalar_check(), CHECK_VERIFY(this));
           target = bcs.dest();
           stackmap_table.check_jump_target
             (&current_frame, target, CHECK_VERIFY(this));
           no_control_flow = false; break;
         case Bytecodes::_goto :
@@ -1669,11 +1686,11 @@
           verify_return_value(return_type, type, bci,
                               &current_frame, CHECK_VERIFY(this));
           no_control_flow = true; break;
         case Bytecodes::_areturn :
           type = current_frame.pop_stack(
-            VerificationType::reference_check(), CHECK_VERIFY(this));
+            VerificationType::nonscalar_check(), CHECK_VERIFY(this));
           verify_return_value(return_type, type, bci,
                               &current_frame, CHECK_VERIFY(this));
           no_control_flow = true; break;
         case Bytecodes::_return :
           if (return_type != VerificationType::bogus_type()) {
@@ -1681,11 +1698,11 @@
                          "Method expects a return value");
             return;
           }
           // Make sure "this" has been initialized if current method is an
           // <init>.
-          if (_method->name() == vmSymbols::object_initializer_name() &&
+          if (_method->is_object_constructor() &&
               current_frame.flag_this_uninit()) {
             verify_error(ErrorContext::bad_code(bci),
                          "Constructor must call super() or this() "
                          "before return");
             return;
@@ -1704,19 +1721,15 @@
             &bcs, &current_frame, cp, false, CHECK_VERIFY(this));
           no_control_flow = false; break;
         case Bytecodes::_invokevirtual :
         case Bytecodes::_invokespecial :
         case Bytecodes::_invokestatic :
-          verify_invoke_instructions(
-            &bcs, code_length, &current_frame, (bci >= ex_min && bci < ex_max),
-            &this_uninit, return_type, cp, &stackmap_table, CHECK_VERIFY(this));
-          no_control_flow = false; break;
         case Bytecodes::_invokeinterface :
         case Bytecodes::_invokedynamic :
           verify_invoke_instructions(
             &bcs, code_length, &current_frame, (bci >= ex_min && bci < ex_max),
-            &this_uninit, return_type, cp, &stackmap_table, CHECK_VERIFY(this));
+            &this_uninit, cp, &stackmap_table, CHECK_VERIFY(this));
           no_control_flow = false; break;
         case Bytecodes::_new :
         {
           u2 index = bcs.get_index_u2();
           verify_cp_class_type(bci, index, cp, CHECK_VERIFY(this));
@@ -1770,14 +1783,15 @@
           current_frame.push_stack(
             VerificationType::integer_type(), CHECK_VERIFY(this));
           no_control_flow = false; break;
         }
         case Bytecodes::_monitorenter :
-        case Bytecodes::_monitorexit :
-          current_frame.pop_stack(
-            VerificationType::reference_check(), CHECK_VERIFY(this));
+        case Bytecodes::_monitorexit : {
+          VerificationType ref = current_frame.pop_stack(
+            VerificationType::nonscalar_check(), CHECK_VERIFY(this));
           no_control_flow = false; break;
+        }
         case Bytecodes::_multianewarray :
         {
           u2 index = bcs.get_index_u2();
           u2 dim = *(bcs.bcp()+3);
           verify_cp_class_type(bci, index, cp, CHECK_VERIFY(this));
@@ -2038,10 +2052,11 @@
   // We must check was_recursively_verified() before we get here.
   guarantee(cp->cache() == nullptr, "not rewritten yet");
 
   verify_cp_index(bci, cp, index, CHECK_VERIFY(this));
   unsigned int tag = cp->tag_at(index).value();
+
   if ((types & (1 << tag)) == 0) {
     verify_error(ErrorContext::bad_cp_index(bci, index),
       "Illegal type at constant pool entry %d in class %s",
       index, cp->pool_holder()->external_name());
     return;
@@ -2152,11 +2167,11 @@
   constantTag tag = cp->tag_at(index);
   unsigned int types = 0;
   if (opcode == Bytecodes::_ldc || opcode == Bytecodes::_ldc_w) {
     if (!tag.is_unresolved_klass()) {
       types = (1 << JVM_CONSTANT_Integer) | (1 << JVM_CONSTANT_Float)
-            | (1 << JVM_CONSTANT_String)  | (1 << JVM_CONSTANT_Class)
+            | (1 << JVM_CONSTANT_String) | (1 << JVM_CONSTANT_Class)
             | (1 << JVM_CONSTANT_MethodHandle) | (1 << JVM_CONSTANT_MethodType)
             | (1 << JVM_CONSTANT_Dynamic);
       // Note:  The class file parser already verified the legality of
       // MethodHandle and MethodType constants.
       verify_cp_type(bci, index, cp, types, CHECK_VERIFY(this));
@@ -2328,17 +2343,18 @@
 
   // Get referenced class type
   VerificationType ref_class_type = cp_ref_index_to_type(
     index, cp, CHECK_VERIFY(this));
   if (!ref_class_type.is_object() &&
-    (!allow_arrays || !ref_class_type.is_array())) {
+      (!allow_arrays || !ref_class_type.is_array())) {
     verify_error(ErrorContext::bad_type(bcs->bci(),
         TypeOrigin::cp(index, ref_class_type)),
         "Expecting reference to class in class %s at constant pool index %d",
         _klass->external_name(), index);
     return;
   }
+
   VerificationType target_class_type = ref_class_type;
 
   assert(sizeof(VerificationType) == sizeof(uintptr_t),
         "buffer type must match VerificationType size");
   uintptr_t field_type_buffer[2];
@@ -2787,11 +2803,11 @@
   return false;
 }
 
 void ClassVerifier::verify_invoke_instructions(
     RawBytecodeStream* bcs, u4 code_length, StackMapFrame* current_frame,
-    bool in_try_block, bool *this_uninit, VerificationType return_type,
+    bool in_try_block, bool *this_uninit,
     const constantPoolHandle& cp, StackMapTable* stackmap_table, TRAPS) {
   // Make sure the constant pool item is the right type
   u2 index = bcs->get_index_u2();
   Bytecodes::Code opcode = bcs->raw_code();
   unsigned int types = 0;
@@ -2819,11 +2835,11 @@
 
   // Method signature was checked in ClassFileParser.
   assert(SignatureVerifier::is_valid_method_signature(method_sig),
          "Invalid method signature");
 
-  // Get referenced class type
+  // Get referenced class
   VerificationType ref_class_type;
   if (opcode == Bytecodes::_invokedynamic) {
     if (_klass->major_version() < Verifier::INVOKEDYNAMIC_MAJOR_VERSION) {
       class_format_error(
         "invokedynamic instructions not supported by this class file version (%d), class %s",
@@ -2885,21 +2901,22 @@
       return;
     }
   }
 
   if (method_name->char_at(0) == JVM_SIGNATURE_SPECIAL) {
-    // Make sure <init> can only be invoked by invokespecial
+    // Make sure:
+    //   <init> can only be invoked by invokespecial.
     if (opcode != Bytecodes::_invokespecial ||
-        method_name != vmSymbols::object_initializer_name()) {
+          method_name != vmSymbols::object_initializer_name()) {
       verify_error(ErrorContext::bad_code(bci),
           "Illegal call to internal method");
       return;
     }
   } else if (opcode == Bytecodes::_invokespecial
              && !is_same_or_direct_interface(current_class(), current_type(), ref_class_type)
              && !ref_class_type.equals(VerificationType::reference_type(
-                  current_class()->super()->name()))) {
+                  current_class()->super()->name()))) { // super() can never be an inline_type.
     bool subtype = false;
     bool have_imr_indirect = cp->tag_at(index).value() == JVM_CONSTANT_InterfaceMethodref;
     subtype = ref_class_type.is_assignable_from(
                current_type(), this, false, CHECK_VERIFY(this));
     if (!subtype) {
@@ -2980,13 +2997,11 @@
   }
   // Push the result type.
   int sig_verif_types_len = sig_verif_types->length();
   if (sig_verif_types_len > nargs) {  // There's a return type
     if (method_name == vmSymbols::object_initializer_name()) {
-      // <init> method must have a void return type
-      /* Unreachable?  Class file parser verifies that methods with '<' have
-       * void return */
+      // an <init> method must have a void return type
       verify_error(ErrorContext::bad_code(bci),
           "Return type must be void in <init> method");
       return;
     }
 
@@ -3088,11 +3103,11 @@
     VerificationType::double2_type(), CHECK_VERIFY(this));
 }
 
 void ClassVerifier::verify_aload(int index, StackMapFrame* current_frame, TRAPS) {
   VerificationType type = current_frame->get_local(
-    index, VerificationType::reference_check(), CHECK_VERIFY(this));
+    index, VerificationType::nonscalar_check(), CHECK_VERIFY(this));
   current_frame->push_stack(type, CHECK_VERIFY(this));
 }
 
 void ClassVerifier::verify_istore(int index, StackMapFrame* current_frame, TRAPS) {
   current_frame->pop_stack(
@@ -3125,11 +3140,11 @@
     VerificationType::double2_type(), CHECK_VERIFY(this));
 }
 
 void ClassVerifier::verify_astore(int index, StackMapFrame* current_frame, TRAPS) {
   VerificationType type = current_frame->pop_stack(
-    VerificationType::reference_check(), CHECK_VERIFY(this));
+    VerificationType::nonscalar_check(), CHECK_VERIFY(this));
   current_frame->set_local(index, type, CHECK_VERIFY(this));
 }
 
 void ClassVerifier::verify_iinc(int index, StackMapFrame* current_frame, TRAPS) {
   VerificationType type = current_frame->get_local(
diff a/src/hotspot/share/classfile/verifier.hpp b/src/hotspot/share/classfile/verifier.hpp
--- a/src/hotspot/share/classfile/verifier.hpp
+++ b/src/hotspot/share/classfile/verifier.hpp
@@ -38,11 +38,11 @@
  public:
   enum {
     STACKMAP_ATTRIBUTE_MAJOR_VERSION    = 50,
     INVOKEDYNAMIC_MAJOR_VERSION         = 51,
     NO_RELAX_ACCESS_CTRL_CHECK_VERSION  = 52,
-    DYNAMICCONSTANT_MAJOR_VERSION       = 55
+    DYNAMICCONSTANT_MAJOR_VERSION       = 55,
   };
 
   // Verify the bytecodes for a class.
   static bool verify(InstanceKlass* klass, bool should_verify_class, TRAPS);
 
@@ -153,10 +153,11 @@
     STACK_SIZE_MISMATCH,  // Frames have different stack sizes
     STACK_OVERFLOW,       // Attempt to push onto a full expression stack
     STACK_UNDERFLOW,      // Attempt to pop and empty expression stack
     MISSING_STACKMAP,     // No stackmap for this location and there should be
     BAD_STACKMAP,         // Format error in stackmap
+    WRONG_INLINE_TYPE,    // Mismatched inline type
     NO_FAULT,             // No error
     UNKNOWN
   } FaultType;
 
   int _bci;
@@ -216,10 +217,13 @@
     return ErrorContext(bci, MISSING_STACKMAP);
   }
   static ErrorContext bad_stackmap(int index, StackMapFrame* frame) {
     return ErrorContext(0, BAD_STACKMAP, TypeOrigin::frame(frame));
   }
+  static ErrorContext bad_inline_type(int bci, TypeOrigin type, TypeOrigin exp) {
+    return ErrorContext(bci, WRONG_INLINE_TYPE, type, exp);
+  }
 
   bool is_valid() const { return _fault != NO_FAULT; }
   int bci() const { return _bci; }
 
   void reset_frames() {
@@ -346,11 +350,11 @@
   // bytecode or loop.
   bool ends_in_athrow(u4 start_bc_offset);
 
   void verify_invoke_instructions(
     RawBytecodeStream* bcs, u4 code_length, StackMapFrame* current_frame,
-    bool in_try_block, bool* this_uninit, VerificationType return_type,
+    bool in_try_block, bool* this_uninit,
     const constantPoolHandle& cp, StackMapTable* stackmap_table, TRAPS);
 
   VerificationType get_newarray_type(u2 index, int bci, TRAPS);
   void verify_anewarray(int bci, u2 index, const constantPoolHandle& cp,
       StackMapFrame* current_frame, TRAPS);
@@ -443,11 +447,12 @@
 
   int change_sig_to_verificationType(
     SignatureStream* sig_type, VerificationType* inference_type);
 
   VerificationType cp_index_to_type(int index, const constantPoolHandle& cp, TRAPS) {
-    return VerificationType::reference_type(cp->klass_name_at(index));
+    Symbol* name = cp->klass_name_at(index);
+    return VerificationType::reference_type(name);
   }
 
   // Keep a list of temporary symbols created during verification because
   // their reference counts need to be decremented when the verifier object
   // goes out of scope.  Since these symbols escape the scope in which they're
@@ -481,12 +486,11 @@
       {
         Symbol* name = sig_type->as_symbol();
         // Create another symbol to save as signature stream unreferences this symbol.
         Symbol* name_copy = create_temporary_symbol(name);
         assert(name_copy == name, "symbols don't match");
-        *inference_type =
-          VerificationType::reference_type(name_copy);
+        *inference_type = VerificationType::reference_type(name_copy);
         return 1;
       }
     case T_LONG:
       *inference_type = VerificationType::long_type();
       *++inference_type = VerificationType::long2_type();
diff a/src/hotspot/share/classfile/vmClassMacros.hpp b/src/hotspot/share/classfile/vmClassMacros.hpp
--- a/src/hotspot/share/classfile/vmClassMacros.hpp
+++ b/src/hotspot/share/classfile/vmClassMacros.hpp
@@ -128,10 +128,11 @@
   do_klass(VMStorage_klass,                             jdk_internal_foreign_abi_VMStorage                    ) \
   do_klass(CallConv_klass,                              jdk_internal_foreign_abi_CallConv                     ) \
   do_klass(Context_klass,                               java_lang_invoke_MethodHandleNatives_CallSiteContext  ) \
   do_klass(ConstantCallSite_klass,                      java_lang_invoke_ConstantCallSite                     ) \
   do_klass(MutableCallSite_klass,                       java_lang_invoke_MutableCallSite                      ) \
+  do_klass(ValueObjectMethods_klass,                    java_lang_runtime_ValueObjectMethods                  ) \
   do_klass(VolatileCallSite_klass,                      java_lang_invoke_VolatileCallSite                     ) \
                                                                                                                 \
   do_klass(AssertionStatusDirectives_klass,             java_lang_AssertionStatusDirectives                   ) \
   do_klass(StringBuffer_klass,                          java_lang_StringBuffer                                ) \
   do_klass(StringBuilder_klass,                         java_lang_StringBuilder                               ) \
diff a/src/hotspot/share/classfile/vmIntrinsics.cpp b/src/hotspot/share/classfile/vmIntrinsics.cpp
--- a/src/hotspot/share/classfile/vmIntrinsics.cpp
+++ b/src/hotspot/share/classfile/vmIntrinsics.cpp
@@ -259,10 +259,11 @@
   case vmIntrinsics::_isHidden:
   case vmIntrinsics::_getSuperclass:
   case vmIntrinsics::_Class_cast:
   case vmIntrinsics::_getLength:
   case vmIntrinsics::_newArray:
+  case vmIntrinsics::_newNullRestrictedArray:
   case vmIntrinsics::_getClass:
     if (!InlineClassNatives) return true;
     break;
   case vmIntrinsics::_currentCarrierThread:
   case vmIntrinsics::_currentThread:
@@ -322,28 +323,32 @@
   case vmIntrinsics::_updateCRC32:
   case vmIntrinsics::_updateBytesCRC32:
   case vmIntrinsics::_updateByteBufferCRC32:
     if (!UseCRC32Intrinsics) return true;
     break;
+  case vmIntrinsics::_makePrivateBuffer:
+  case vmIntrinsics::_finishPrivateBuffer:
   case vmIntrinsics::_getReference:
   case vmIntrinsics::_getBoolean:
   case vmIntrinsics::_getByte:
   case vmIntrinsics::_getShort:
   case vmIntrinsics::_getChar:
   case vmIntrinsics::_getInt:
   case vmIntrinsics::_getLong:
   case vmIntrinsics::_getFloat:
   case vmIntrinsics::_getDouble:
+  case vmIntrinsics::_getValue:
   case vmIntrinsics::_putReference:
   case vmIntrinsics::_putBoolean:
   case vmIntrinsics::_putByte:
   case vmIntrinsics::_putShort:
   case vmIntrinsics::_putChar:
   case vmIntrinsics::_putInt:
   case vmIntrinsics::_putLong:
   case vmIntrinsics::_putFloat:
   case vmIntrinsics::_putDouble:
+  case vmIntrinsics::_putValue:
   case vmIntrinsics::_getReferenceVolatile:
   case vmIntrinsics::_getBooleanVolatile:
   case vmIntrinsics::_getByteVolatile:
   case vmIntrinsics::_getShortVolatile:
   case vmIntrinsics::_getCharVolatile:
diff a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp
--- a/src/hotspot/share/classfile/vmIntrinsics.hpp
+++ b/src/hotspot/share/classfile/vmIntrinsics.hpp
@@ -327,10 +327,13 @@
    do_name(     getCallerClass_name,                             "getCallerClass")                                      \
                                                                                                                         \
   do_intrinsic(_newArray,                 java_lang_reflect_Array, newArray_name, newArray_signature,            F_SN)  \
    do_name(     newArray_name,                                    "newArray")                                           \
    do_signature(newArray_signature,                               "(Ljava/lang/Class;I)Ljava/lang/Object;")             \
+  do_intrinsic(_newNullRestrictedArray,   jdk_internal_value_ValueClass, newNullRestrictedArray_name, newNullRestrictedArray_signature, F_SN) \
+   do_signature(newNullRestrictedArray_signature,                 "(Ljava/lang/Class;I)[Ljava/lang/Object;")            \
+   do_name(     newNullRestrictedArray_name,                      "newNullRestrictedArray")                             \
                                                                                                                         \
   do_intrinsic(_onSpinWait,               java_lang_Thread,       onSpinWait_name, onSpinWait_signature,         F_S)   \
    do_name(     onSpinWait_name,                                  "onSpinWait")                                         \
    do_alias(    onSpinWait_signature,                             void_method_signature)                                \
                                                                                                                         \
@@ -619,10 +622,12 @@
   do_intrinsic(_allocateUninitializedArray, jdk_internal_misc_Unsafe,   allocateUninitializedArray_name, newArray_signature,  F_R) \
    do_name(     allocateUninitializedArray_name,                        "allocateUninitializedArray0")                           \
   do_intrinsic(_copyMemory,               jdk_internal_misc_Unsafe,     copyMemory_name, copyMemory_signature,         F_RN)     \
    do_name(     copyMemory_name,                                        "copyMemory0")                                           \
    do_signature(copyMemory_signature,                                   "(Ljava/lang/Object;JLjava/lang/Object;JJ)V")            \
+  do_intrinsic(_isFlatArray,              jdk_internal_misc_Unsafe,     isFlatArray_name, class_boolean_signature, F_RN)         \
+   do_name(     isFlatArray_name,                                       "isFlatArray")                                           \
   do_intrinsic(_loadFence,                jdk_internal_misc_Unsafe,     loadFence_name, loadFence_signature,           F_R)      \
    do_name(     loadFence_name,                                         "loadFence")                                             \
    do_alias(    loadFence_signature,                                    void_method_signature)                                   \
   do_intrinsic(_storeFence,               jdk_internal_misc_Unsafe,     storeFence_name, storeFence_signature,         F_R)      \
    do_name(     storeFence_name,                                        "storeFence")                                            \
@@ -667,39 +672,49 @@
   do_signature(putLong_signature,         "(Ljava/lang/Object;JJ)V")                                                    \
   do_signature(getFloat_signature,        "(Ljava/lang/Object;J)F")                                                     \
   do_signature(putFloat_signature,        "(Ljava/lang/Object;JF)V")                                                    \
   do_signature(getDouble_signature,       "(Ljava/lang/Object;J)D")                                                     \
   do_signature(putDouble_signature,       "(Ljava/lang/Object;JD)V")                                                    \
+  do_signature(getValue_signature,        "(Ljava/lang/Object;JLjava/lang/Class;)Ljava/lang/Object;")                   \
+  do_signature(putValue_signature,        "(Ljava/lang/Object;JLjava/lang/Class;Ljava/lang/Object;)V")                  \
                                                                                                                         \
   do_name(getReference_name,"getReference")     do_name(putReference_name,"putReference")                               \
   do_name(getBoolean_name,"getBoolean")         do_name(putBoolean_name,"putBoolean")                                   \
   do_name(getByte_name,"getByte")               do_name(putByte_name,"putByte")                                         \
   do_name(getShort_name,"getShort")             do_name(putShort_name,"putShort")                                       \
   do_name(getChar_name,"getChar")               do_name(putChar_name,"putChar")                                         \
   do_name(getInt_name,"getInt")                 do_name(putInt_name,"putInt")                                           \
   do_name(getLong_name,"getLong")               do_name(putLong_name,"putLong")                                         \
   do_name(getFloat_name,"getFloat")             do_name(putFloat_name,"putFloat")                                       \
   do_name(getDouble_name,"getDouble")           do_name(putDouble_name,"putDouble")                                     \
+  do_name(getValue_name,"getValue")             do_name(putValue_name,"putValue")                                       \
+  do_name(makePrivateBuffer_name,"makePrivateBuffer")                                                                   \
+  do_name(finishPrivateBuffer_name,"finishPrivateBuffer")                                                               \
                                                                                                                         \
   do_intrinsic(_getReference,       jdk_internal_misc_Unsafe,     getReference_name, getReference_signature,     F_RN)  \
   do_intrinsic(_getBoolean,         jdk_internal_misc_Unsafe,     getBoolean_name, getBoolean_signature,         F_RN)  \
   do_intrinsic(_getByte,            jdk_internal_misc_Unsafe,     getByte_name, getByte_signature,               F_RN)  \
   do_intrinsic(_getShort,           jdk_internal_misc_Unsafe,     getShort_name, getShort_signature,             F_RN)  \
   do_intrinsic(_getChar,            jdk_internal_misc_Unsafe,     getChar_name, getChar_signature,               F_RN)  \
   do_intrinsic(_getInt,             jdk_internal_misc_Unsafe,     getInt_name, getInt_signature,                 F_RN)  \
   do_intrinsic(_getLong,            jdk_internal_misc_Unsafe,     getLong_name, getLong_signature,               F_RN)  \
   do_intrinsic(_getFloat,           jdk_internal_misc_Unsafe,     getFloat_name, getFloat_signature,             F_RN)  \
   do_intrinsic(_getDouble,          jdk_internal_misc_Unsafe,     getDouble_name, getDouble_signature,           F_RN)  \
+  do_intrinsic(_getValue,           jdk_internal_misc_Unsafe,     getValue_name, getValue_signature,             F_RN)  \
   do_intrinsic(_putReference,       jdk_internal_misc_Unsafe,     putReference_name, putReference_signature,     F_RN)  \
   do_intrinsic(_putBoolean,         jdk_internal_misc_Unsafe,     putBoolean_name, putBoolean_signature,         F_RN)  \
   do_intrinsic(_putByte,            jdk_internal_misc_Unsafe,     putByte_name, putByte_signature,               F_RN)  \
   do_intrinsic(_putShort,           jdk_internal_misc_Unsafe,     putShort_name, putShort_signature,             F_RN)  \
   do_intrinsic(_putChar,            jdk_internal_misc_Unsafe,     putChar_name, putChar_signature,               F_RN)  \
   do_intrinsic(_putInt,             jdk_internal_misc_Unsafe,     putInt_name, putInt_signature,                 F_RN)  \
   do_intrinsic(_putLong,            jdk_internal_misc_Unsafe,     putLong_name, putLong_signature,               F_RN)  \
   do_intrinsic(_putFloat,           jdk_internal_misc_Unsafe,     putFloat_name, putFloat_signature,             F_RN)  \
   do_intrinsic(_putDouble,          jdk_internal_misc_Unsafe,     putDouble_name, putDouble_signature,           F_RN)  \
+  do_intrinsic(_putValue,           jdk_internal_misc_Unsafe,     putValue_name, putValue_signature,             F_RN)  \
+                                                                                                                        \
+  do_intrinsic(_makePrivateBuffer,  jdk_internal_misc_Unsafe,     makePrivateBuffer_name, object_object_signature, F_RN)   \
+  do_intrinsic(_finishPrivateBuffer,  jdk_internal_misc_Unsafe,   finishPrivateBuffer_name, object_object_signature, F_RN) \
                                                                                                                         \
   do_name(getReferenceVolatile_name,"getReferenceVolatile")   do_name(putReferenceVolatile_name,"putReferenceVolatile") \
   do_name(getBooleanVolatile_name,"getBooleanVolatile")       do_name(putBooleanVolatile_name,"putBooleanVolatile")     \
   do_name(getByteVolatile_name,"getByteVolatile")             do_name(putByteVolatile_name,"putByteVolatile")           \
   do_name(getShortVolatile_name,"getShortVolatile")           do_name(putShortVolatile_name,"putShortVolatile")         \
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
@@ -182,10 +182,11 @@
   /* class file format tags */                                                                    \
   template(tag_source_file,                           "SourceFile")                               \
   template(tag_inner_classes,                         "InnerClasses")                             \
   template(tag_nest_members,                          "NestMembers")                              \
   template(tag_nest_host,                             "NestHost")                                 \
+  template(tag_preload,                               "Preload")                                  \
   template(tag_constant_value,                        "ConstantValue")                            \
   template(tag_code,                                  "Code")                                     \
   template(tag_exceptions,                            "Exceptions")                               \
   template(tag_line_number_table,                     "LineNumberTable")                          \
   template(tag_local_variable_table,                  "LocalVariableTable")                       \
@@ -266,10 +267,13 @@
   template(java_util_concurrent_atomic_AtomicLongFieldUpdater_CASUpdater,    "java/util/concurrent/atomic/AtomicLongFieldUpdater$CASUpdater") \
   template(java_util_concurrent_atomic_AtomicLongFieldUpdater_LockedUpdater, "java/util/concurrent/atomic/AtomicLongFieldUpdater$LockedUpdater") \
   template(java_util_concurrent_atomic_AtomicReferenceFieldUpdater_Impl,     "java/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl") \
   template(jdk_internal_vm_annotation_Contended_signature,                   "Ljdk/internal/vm/annotation/Contended;")    \
   template(jdk_internal_vm_annotation_ReservedStackAccess_signature,         "Ljdk/internal/vm/annotation/ReservedStackAccess;") \
+  template(jdk_internal_vm_annotation_ImplicitlyConstructible_signature,     "Ljdk/internal/vm/annotation/ImplicitlyConstructible;") \
+  template(jdk_internal_vm_annotation_LooselyConsistentValue_signature,      "Ljdk/internal/vm/annotation/LooselyConsistentValue;") \
+  template(jdk_internal_vm_annotation_NullRestricted_signature,              "Ljdk/internal/vm/annotation/NullRestricted;") \
   template(jdk_internal_ValueBased_signature,                                "Ljdk/internal/ValueBased;") \
                                                                                                   \
   /* class symbols needed by intrinsics */                                                        \
   VM_INTRINSICS_DO(VM_INTRINSIC_IGNORE, template, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_ALIAS_IGNORE) \
                                                                                                   \
@@ -298,11 +302,10 @@
   template(invoke_name,                               "invoke")                                   \
   template(parameterTypes_name,                       "parameterTypes")                           \
   template(returnType_name,                           "returnType")                               \
   template(signature_name,                            "signature")                                \
   template(slot_name,                                 "slot")                                     \
-  template(trusted_final_name,                        "trustedFinal")                             \
   template(blackhole_name,                            "<blackhole>")  /*fake name*/               \
                                                                                                   \
   /* Support for annotations (JDK 1.5 and above) */                                               \
                                                                                                   \
   template(annotations_name,                          "annotations")                              \
@@ -534,10 +537,12 @@
   template(jvmti_VTMS_transition_disable_count_name,  "jvmti_VTMS_transition_disable_count")      \
   template(jvmti_is_in_VTMS_transition_name,          "jvmti_is_in_VTMS_transition")              \
   template(module_entry_name,                         "module_entry")                             \
   template(resolved_references_name,                  "<resolved_references>")                    \
   template(init_lock_name,                            "<init_lock>")                              \
+  template(default_value_name,                        ".default")                                 \
+  template(empty_marker_name,                         ".empty")                                   \
   template(address_size_name,                         "ADDRESS_SIZE0")                            \
   template(page_size_name,                            "PAGE_SIZE")                                \
   template(big_endian_name,                           "BIG_ENDIAN")                               \
   template(use_unaligned_access_name,                 "UNALIGNED_ACCESS")                         \
   template(data_cache_line_flush_size_name,           "DATA_CACHE_LINE_FLUSH_SIZE")               \
@@ -610,10 +615,11 @@
   template(void_throwable_signature,                  "()Ljava/lang/Throwable;")                  \
   template(class_void_signature,                      "(Ljava/lang/Class;)V")                     \
   template(class_int_signature,                       "(Ljava/lang/Class;)I")                     \
   template(class_long_signature,                      "(Ljava/lang/Class;)J")                     \
   template(class_boolean_signature,                   "(Ljava/lang/Class;)Z")                     \
+  template(class_class_signature,                     "(Ljava/lang/Class;)Ljava/lang/Class;")     \
   template(throwable_throwable_signature,             "(Ljava/lang/Throwable;)Ljava/lang/Throwable;")             \
   template(throwable_string_void_signature,           "(Ljava/lang/Throwable;Ljava/lang/String;)V")               \
   template(string_array_void_signature,               "([Ljava/lang/String;)V")                                   \
   template(string_array_string_array_void_signature,  "([Ljava/lang/String;[Ljava/lang/String;)V")                \
   template(thread_throwable_void_signature,           "(Ljava/lang/Thread;Ljava/lang/Throwable;)V")               \
@@ -623,10 +629,11 @@
   template(threadgroup_string_void_signature,         "(Ljava/lang/ThreadGroup;Ljava/lang/String;)V")             \
   template(void_threadgroup_array_signature,          "()[Ljava/lang/ThreadGroup;")                               \
   template(string_class_signature,                    "(Ljava/lang/String;)Ljava/lang/Class;")                    \
   template(string_boolean_class_signature,            "(Ljava/lang/String;Z)Ljava/lang/Class;")                   \
   template(object_object_object_signature,            "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;") \
+  template(object_object_boolean_signature,           "(Ljava/lang/Object;Ljava/lang/Object;)Z") \
   template(string_string_string_signature,            "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;") \
   template(string_string_signature,                   "(Ljava/lang/String;)Ljava/lang/String;")                   \
   template(classloader_string_long_signature,         "(Ljava/lang/ClassLoader;Ljava/lang/String;)J")             \
   template(byte_array_void_signature,                 "([B)V")                                                    \
   template(char_array_void_signature,                 "([C)V")                                                    \
@@ -766,10 +773,12 @@
   template(encodeAnnotations_signature,                "([BLjava/lang/Class;Ljdk/internal/reflect/ConstantPool;Z[Ljava/lang/Class;)[B")\
   template(decodeAndThrowThrowable_signature,          "(IJZ)V")                                                  \
   template(classRedefinedCount_name,                   "classRedefinedCount")                                     \
   template(classLoader_name,                           "classLoader")                                             \
   template(componentType_name,                         "componentType")                                           \
+  template(primaryType_name,                           "primaryType")                                             \
+  template(secondaryType_name,                         "secondaryType")                                           \
                                                                                                                   \
   /* forEachRemaining support */                                                                                  \
   template(java_util_stream_StreamsRangeIntSpliterator,          "java/util/stream/Streams$RangeIntSpliterator")  \
                                                                                                                   \
   /* jfr signatures */                                                                                            \
@@ -792,10 +801,16 @@
   template(toFileURL_name,                                  "toFileURL")                                          \
   template(toFileURL_signature,                             "(Ljava/lang/String;)Ljava/net/URL;")                 \
   template(url_void_signature,                              "(Ljava/net/URL;)V")                                  \
   template(url_array_classloader_void_signature,            "([Ljava/net/URL;Ljava/lang/ClassLoader;)V")          \
                                                                                                                   \
+  template(java_lang_runtime_ValueObjectMethods,            "java/lang/runtime/ValueObjectMethods")               \
+  template(isSubstitutable_name,                            "isSubstitutable")                                    \
+  template(valueObjectHashCode_name,                        "valueObjectHashCode")                                \
+  template(jdk_internal_value_PrimitiveClass,               "jdk/internal/value/PrimitiveClass")                  \
+  template(jdk_internal_value_ValueClass,                   "jdk/internal/value/ValueClass")                      \
+                                                                                                                  \
   /* Thread.dump_to_file jcmd */                                                                                  \
   template(jdk_internal_vm_ThreadDumper,           "jdk/internal/vm/ThreadDumper")                                \
   template(dumpThreads_name,                       "dumpThreads")                                                 \
   template(dumpThreadsToJson_name,                 "dumpThreadsToJson")                                           \
 
diff a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp
--- a/src/hotspot/share/code/codeBlob.cpp
+++ b/src/hotspot/share/code/codeBlob.cpp
@@ -267,23 +267,23 @@
 
   return blob;
 }
 
 
-BufferBlob::BufferBlob(const char* name, int size, CodeBuffer* cb)
-  : RuntimeBlob(name, cb, sizeof(BufferBlob), size, CodeOffsets::frame_never_safe, 0, nullptr)
+BufferBlob::BufferBlob(const char* name, int header_size, int size, CodeBuffer* cb)
+  : RuntimeBlob(name, cb, header_size, size, CodeOffsets::frame_never_safe, 0, nullptr)
 {}
 
 BufferBlob* BufferBlob::create(const char* name, CodeBuffer* cb) {
   ThreadInVMfromUnknown __tiv;  // get to VM state in case we block on CodeCache_lock
 
   BufferBlob* blob = nullptr;
   unsigned int size = CodeBlob::allocation_size(cb, sizeof(BufferBlob));
   assert(name != nullptr, "must provide a name");
   {
     MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
-    blob = new (size) BufferBlob(name, size, cb);
+    blob = new (size) BufferBlob(name, sizeof(BufferBlob), size, cb);
   }
   // Track memory usage statistic after releasing CodeCache_lock
   MemoryService::track_code_cache_memory_usage();
 
   return blob;
@@ -295,29 +295,33 @@
 
 void BufferBlob::free(BufferBlob *blob) {
   RuntimeBlob::free(blob);
 }
 
+BufferBlob::BufferBlob(const char* name, int size, CodeBuffer* cb, int frame_complete, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments)
+  : RuntimeBlob(name, cb, sizeof(BufferBlob), size, frame_complete, frame_size, oop_maps, caller_must_gc_arguments)
+{}
+
 
 //----------------------------------------------------------------------------------------------------
 // Implementation of AdapterBlob
 
-AdapterBlob::AdapterBlob(int size, CodeBuffer* cb) :
-  BufferBlob("I2C/C2I adapters", size, cb) {
+AdapterBlob::AdapterBlob(int size, CodeBuffer* cb, int frame_complete, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments) :
+  BufferBlob("I2C/C2I adapters", size, cb, frame_complete, frame_size, oop_maps, caller_must_gc_arguments) {
   CodeCache::commit(this);
 }
 
-AdapterBlob* AdapterBlob::create(CodeBuffer* cb) {
+AdapterBlob* AdapterBlob::create(CodeBuffer* cb, int frame_complete, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments) {
   ThreadInVMfromUnknown __tiv;  // get to VM state in case we block on CodeCache_lock
 
   CodeCache::gc_on_allocation();
 
   AdapterBlob* blob = nullptr;
   unsigned int size = CodeBlob::allocation_size(cb, sizeof(AdapterBlob));
   {
     MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
-    blob = new (size) AdapterBlob(size, cb);
+    blob = new (size) AdapterBlob(size, cb, frame_complete, frame_size, oop_maps, caller_must_gc_arguments);
   }
   // Track memory usage statistic after releasing CodeCache_lock
   MemoryService::track_code_cache_memory_usage();
 
   return blob;
@@ -391,10 +395,35 @@
   MemoryService::track_code_cache_memory_usage();
 
   return blob;
 }
 
+//----------------------------------------------------------------------------------------------------
+// Implementation of BufferedInlineTypeBlob
+BufferedInlineTypeBlob::BufferedInlineTypeBlob(int size, CodeBuffer* cb, int pack_fields_off, int pack_fields_jobject_off, int unpack_fields_off) :
+  BufferBlob("buffered inline type", sizeof(BufferedInlineTypeBlob), size, cb),
+  _pack_fields_off(pack_fields_off),
+  _pack_fields_jobject_off(pack_fields_jobject_off),
+  _unpack_fields_off(unpack_fields_off) {
+  CodeCache::commit(this);
+}
+
+BufferedInlineTypeBlob* BufferedInlineTypeBlob::create(CodeBuffer* cb, int pack_fields_off, int pack_fields_jobject_off, int unpack_fields_off) {
+  ThreadInVMfromUnknown __tiv;  // get to VM state in case we block on CodeCache_lock
+
+  BufferedInlineTypeBlob* blob = nullptr;
+  unsigned int size = CodeBlob::allocation_size(cb, sizeof(BufferedInlineTypeBlob));
+  {
+    MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
+    blob = new (size) BufferedInlineTypeBlob(size, cb, pack_fields_off, pack_fields_jobject_off, unpack_fields_off);
+  }
+  // Track memory usage statistic after releasing CodeCache_lock
+  MemoryService::track_code_cache_memory_usage();
+
+  return blob;
+}
+
 //----------------------------------------------------------------------------------------------------
 // Implementation of RuntimeStub
 
 RuntimeStub::RuntimeStub(
   const char* name,
diff a/src/hotspot/share/code/codeBlob.hpp b/src/hotspot/share/code/codeBlob.hpp
--- a/src/hotspot/share/code/codeBlob.hpp
+++ b/src/hotspot/share/code/codeBlob.hpp
@@ -157,10 +157,11 @@
   virtual bool is_vtable_blob() const                 { return false; }
   virtual bool is_method_handles_adapter_blob() const { return false; }
   virtual bool is_upcall_stub() const                 { return false; }
   bool is_compiled() const                            { return _is_compiled; }
   const bool* is_compiled_addr() const                { return &_is_compiled; }
+  virtual bool is_buffered_inline_type_blob() const   { return false; }
 
   inline bool is_compiled_by_c1() const    { return _type == compiler_c1; };
   inline bool is_compiled_by_c2() const    { return _type == compiler_c2; };
   inline bool is_compiled_by_jvmci() const { return _type == compiler_jvmci; };
   const char* compiler_name() const;
@@ -396,17 +397,19 @@
 class BufferBlob: public RuntimeBlob {
   friend class VMStructs;
   friend class AdapterBlob;
   friend class VtableBlob;
   friend class MethodHandlesAdapterBlob;
+  friend class BufferedInlineTypeBlob;
   friend class UpcallStub;
   friend class WhiteBox;
 
  private:
   // Creation support
   BufferBlob(const char* name, int size);
-  BufferBlob(const char* name, int size, CodeBuffer* cb);
+  BufferBlob(const char* name, int header_size, int size, CodeBuffer* cb);
+  BufferBlob(const char* name, int size, CodeBuffer* cb, int frame_complete, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments = false);
 
   void* operator new(size_t s, unsigned size) throw();
 
  public:
   // Creation
@@ -430,18 +433,24 @@
 //----------------------------------------------------------------------------------------------------
 // AdapterBlob: used to hold C2I/I2C adapters
 
 class AdapterBlob: public BufferBlob {
 private:
-  AdapterBlob(int size, CodeBuffer* cb);
+  AdapterBlob(int size, CodeBuffer* cb, int frame_complete, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments = false);
 
 public:
   // Creation
-  static AdapterBlob* create(CodeBuffer* cb);
+  static AdapterBlob* create(CodeBuffer* cb,
+                             int frame_complete,
+                             int frame_size,
+                             OopMapSet* oop_maps,
+                             bool caller_must_gc_arguments = false);
 
   // Typing
   virtual bool is_adapter_blob() const { return true; }
+
+  bool caller_must_gc_arguments(JavaThread* thread) const { return true; }
 };
 
 //---------------------------------------------------------------------------------------------------
 class VtableBlob: public BufferBlob {
 private:
@@ -470,10 +479,32 @@
 
   // Typing
   virtual bool is_method_handles_adapter_blob() const { return true; }
 };
 
+//----------------------------------------------------------------------------------------------------
+// BufferedInlineTypeBlob : used for pack/unpack handlers
+
+class BufferedInlineTypeBlob: public BufferBlob {
+private:
+  const int _pack_fields_off;
+  const int _pack_fields_jobject_off;
+  const int _unpack_fields_off;
+
+  BufferedInlineTypeBlob(int size, CodeBuffer* cb, int pack_fields_off, int pack_fields_jobject_off, int unpack_fields_off);
+
+public:
+  // Creation
+  static BufferedInlineTypeBlob* create(CodeBuffer* cb, int pack_fields_off, int pack_fields_jobject_off, int unpack_fields_off);
+
+  address pack_fields() const { return code_begin() + _pack_fields_off; }
+  address pack_fields_jobject() const { return code_begin() + _pack_fields_jobject_off; }
+  address unpack_fields() const { return code_begin() + _unpack_fields_off; }
+
+  // Typing
+  virtual bool is_buffered_inline_type_blob() const { return true; }
+};
 
 //----------------------------------------------------------------------------------------------------
 // RuntimeStub: describes stubs used by compiled code to call a (static) C++ runtime routine
 
 class RuntimeStub: public RuntimeBlob {
diff a/src/hotspot/share/code/compiledIC.cpp b/src/hotspot/share/code/compiledIC.cpp
--- a/src/hotspot/share/code/compiledIC.cpp
+++ b/src/hotspot/share/code/compiledIC.cpp
@@ -246,20 +246,20 @@
 // stubs, or due to running out of IC stubs in an attempted transition to a
 // transitional state. The needs_ic_stub_refill value will be set if the failure
 // was due to running out of IC stubs, in which case the caller will refill IC
 // stubs and retry.
 bool CompiledIC::set_to_megamorphic(CallInfo* call_info, Bytecodes::Code bytecode,
-                                    bool& needs_ic_stub_refill, TRAPS) {
+                                    bool& needs_ic_stub_refill, bool caller_is_c1, TRAPS) {
   assert(CompiledICLocker::is_safe(_method), "mt unsafe call");
   assert(!is_optimized(), "cannot set an optimized virtual call to megamorphic");
   assert(is_call_to_compiled() || is_call_to_interpreted(), "going directly to megamorphic?");
 
   address entry;
   if (call_info->call_kind() == CallInfo::itable_call) {
     assert(bytecode == Bytecodes::_invokeinterface, "");
     int itable_index = call_info->itable_index();
-    entry = VtableStubs::find_itable_stub(itable_index);
+    entry = VtableStubs::find_itable_stub(itable_index, caller_is_c1);
     if (entry == nullptr) {
       return false;
     }
 #ifdef ASSERT
     int index = call_info->resolved_method()->itable_index();
@@ -281,11 +281,11 @@
   } else {
     assert(call_info->call_kind() == CallInfo::vtable_call, "either itable or vtable");
     // Can be different than selected_method->vtable_index(), due to package-private etc.
     int vtable_index = call_info->vtable_index();
     assert(call_info->resolved_klass()->verify_vtable_index(vtable_index), "sanity check");
-    entry = VtableStubs::find_vtable_stub(vtable_index);
+    entry = VtableStubs::find_vtable_stub(vtable_index, caller_is_c1);
     if (entry == nullptr) {
       return false;
     }
     if (!InlineCacheBuffer::create_transition_stub(this, nullptr, entry)) {
       needs_ic_stub_refill = true;
@@ -510,10 +510,11 @@
 void CompiledIC::compute_monomorphic_entry(const methodHandle& method,
                                            Klass* receiver_klass,
                                            bool is_optimized,
                                            bool static_bound,
                                            bool caller_is_nmethod,
+                                           bool caller_is_c1,
                                            CompiledICInfo& info,
                                            TRAPS) {
   CompiledMethod* method_code = method->code();
 
   address entry = nullptr;
@@ -535,27 +536,29 @@
     //     null check on a call when the target isn't loaded).
     //     This causes problems when verifying the IC because
     //     it looks vanilla but is optimized. Code in is_call_to_interpreted
     //     is aware of this and weakens its asserts.
     if (is_optimized) {
-      entry      = method_code->verified_entry_point();
+      entry      = caller_is_c1 ? method_code->verified_inline_entry_point() : method_code->verified_entry_point();
     } else {
-      entry      = method_code->entry_point();
+      entry      = caller_is_c1 ? method_code->inline_entry_point() : method_code->entry_point();
     }
   }
   if (entry != nullptr) {
     // Call to near compiled code.
     info.set_compiled_entry(entry, is_optimized ? nullptr : receiver_klass, is_optimized);
   } else {
     if (is_optimized) {
       // Use stub entry
-      info.set_interpreter_entry(method()->get_c2i_entry(), method());
+      address entry = caller_is_c1 ? method()->get_c2i_inline_entry() : method()->get_c2i_entry();
+      info.set_interpreter_entry(entry, method());
     } else {
       // Use icholder entry
       assert(method_code == nullptr || method_code->is_compiled(), "must be compiled");
       CompiledICHolder* holder = new CompiledICHolder(method(), receiver_klass);
-      info.set_icholder_entry(method()->get_c2i_unverified_entry(), holder);
+      entry = (caller_is_c1)? method()->get_c2i_unverified_inline_entry() : method()->get_c2i_unverified_entry();
+      info.set_icholder_entry(entry, holder);
     }
   }
   assert(info.is_optimized() == is_optimized, "must agree");
 }
 
@@ -637,22 +640,34 @@
   }
 }
 
 // Compute settings for a CompiledStaticCall. Since we might have to set
 // the stub when calling to the interpreter, we need to return arguments.
-void CompiledStaticCall::compute_entry(const methodHandle& m, bool caller_is_nmethod, StaticCallInfo& info) {
+void CompiledStaticCall::compute_entry(const methodHandle& m, CompiledMethod* caller_nm, StaticCallInfo& info) {
+  assert(!m->mismatch(), "Mismatch for static call");
+  bool caller_is_nmethod = caller_nm->is_nmethod();
   CompiledMethod* m_code = m->code();
   info._callee = m;
   if (m_code != nullptr && m_code->is_in_use() && !m_code->is_unloading()) {
     info._to_interpreter = false;
-    info._entry  = m_code->verified_entry_point();
+    if (caller_nm->is_compiled_by_c1()) {
+      info._entry = m_code->verified_inline_entry_point();
+    } else {
+      info._entry = m_code->verified_entry_point();
+    }
   } else {
     // Callee is interpreted code.  In any case entering the interpreter
     // puts a converter-frame on the stack to save arguments.
     assert(!m->is_method_handle_intrinsic(), "Compiled code should never call interpreter MH intrinsics");
     info._to_interpreter = true;
-    info._entry      = m()->get_c2i_entry();
+    if (caller_nm->is_compiled_by_c1()) {
+      // C1 -> interp: values passed as oops
+      info._entry = m()->get_c2i_inline_entry();
+    } else {
+      // C2 -> interp: values passed as fields
+      info._entry = m()->get_c2i_entry();
+    }
   }
 }
 
 void CompiledStaticCall::compute_entry_for_continuation_entry(const methodHandle& m, StaticCallInfo& info) {
   if (ContinuationEntry::is_interpreted_call(instruction_address())) {
diff a/src/hotspot/share/code/compiledIC.hpp b/src/hotspot/share/code/compiledIC.hpp
--- a/src/hotspot/share/code/compiledIC.hpp
+++ b/src/hotspot/share/code/compiledIC.hpp
@@ -259,15 +259,15 @@
   bool set_to_monomorphic(CompiledICInfo& info);
   void clear_ic_stub();
 
   // Returns true if successful and false otherwise. The call can fail if memory
   // allocation in the code cache fails, or ic stub refill is required.
-  bool set_to_megamorphic(CallInfo* call_info, Bytecodes::Code bytecode, bool& needs_ic_stub_refill, TRAPS);
+  bool set_to_megamorphic(CallInfo* call_info, Bytecodes::Code bytecode, bool& needs_ic_stub_refill, bool caller_is_c1, TRAPS);
 
   static void compute_monomorphic_entry(const methodHandle& method, Klass* receiver_klass,
                                         bool is_optimized, bool static_bound, bool caller_is_nmethod,
-                                        CompiledICInfo& info, TRAPS);
+                                        bool caller_is_c1, CompiledICInfo& info, TRAPS);
 
   // Location
   address instruction_address() const { return _call->instruction_address(); }
 
   // Misc
@@ -311,11 +311,11 @@
 //
 //
 //           -----<----- Clean ----->-----
 //          /                             \
 //         /                               \
-//    compilled code <------------> interpreted code
+//    compiled code <------------> interpreted code
 //
 //  Clean:            Calls directly to runtime method for fixup
 //  Compiled code:    Calls directly to compiled code
 //  Interpreted code: Calls to stub that set Method* reference
 //
@@ -344,11 +344,11 @@
   static int to_interp_stub_size();
   static int to_trampoline_stub_size();
   static int reloc_to_interp_stub();
 
   // Compute entry point given a method
-  static void compute_entry(const methodHandle& m, bool caller_is_nmethod, StaticCallInfo& info);
+  static void compute_entry(const methodHandle& m, CompiledMethod* caller_nm, StaticCallInfo& info);
   void compute_entry_for_continuation_entry(const methodHandle& m, StaticCallInfo& info);
 
 public:
   // Clean static call (will force resolving on next use)
   virtual address destination() const = 0;
diff a/src/hotspot/share/code/compiledMethod.cpp b/src/hotspot/share/code/compiledMethod.cpp
--- a/src/hotspot/share/code/compiledMethod.cpp
+++ b/src/hotspot/share/code/compiledMethod.cpp
@@ -379,11 +379,22 @@
     // Bytecode can be inaccurate in such case.
     Method* callee = attached_method_before_pc(pc);
     if (callee != nullptr) {
       has_receiver = !(callee->access_flags().is_static());
       has_appendix = false;
-      signature    = callee->signature();
+      signature = callee->signature();
+
+      // If inline types are passed as fields, use the extended signature
+      // which contains the types of all (oop) fields of the inline type.
+      if (is_compiled_by_c2() && callee->has_scalarized_args()) {
+        const GrowableArray<SigEntry>* sig = callee->adapter()->get_sig_cc();
+        assert(sig != nullptr, "sig should never be null");
+        TempNewSymbol tmp_sig = SigEntry::create_symbol(sig);
+        has_receiver = false; // The extended signature contains the receiver type
+        fr.oops_compiled_arguments_do(tmp_sig, has_receiver, has_appendix, reg_map, f);
+        return;
+      }
     } else {
       SimpleScopeDesc ssd(this, pc);
 
       Bytecode_invoke call(methodHandle(Thread::current(), ssd.method()), ssd.bci());
       has_receiver = call.has_receiver();
diff a/src/hotspot/share/code/compiledMethod.hpp b/src/hotspot/share/code/compiledMethod.hpp
--- a/src/hotspot/share/code/compiledMethod.hpp
+++ b/src/hotspot/share/code/compiledMethod.hpp
@@ -204,10 +204,20 @@
   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  needs_stack_repair() const {
+    if (is_compiled_by_c1()) {
+      return method()->c1_needs_stack_repair();
+    } else if (is_compiled_by_c2()) {
+      return method()->c2_needs_stack_repair();
+    } else {
+      return false;
+    }
+  }
+
   enum : signed char { not_installed = -1, // in construction, only the owner doing the construction is
                                            // allowed to advance state
                        in_use        = 0,  // executable nmethod
                        not_used      = 1,  // not entrant, but revivable
                        not_entrant   = 2,  // marked for deoptimization but activations may still exist
@@ -216,16 +226,19 @@
   virtual bool  is_in_use() const = 0;
   virtual int   comp_level() const = 0;
   virtual int   compile_id() const = 0;
 
   virtual address verified_entry_point() const = 0;
+  virtual address verified_inline_entry_point() const = 0;
+  virtual address verified_inline_ro_entry_point() const = 0;
   virtual void log_identity(xmlStream* log) const = 0;
   virtual void log_state_change() const = 0;
   virtual bool make_not_used() = 0;
   virtual bool make_not_entrant() = 0;
   virtual bool make_entrant() = 0;
   virtual address entry_point() const = 0;
+  virtual address inline_entry_point() const = 0;
   virtual bool is_osr_method() const = 0;
   virtual int osr_entry_bci() const = 0;
   Method* method() const                          { return _method; }
   virtual void print_pcs() = 0;
   bool is_native_method() const { return _method != nullptr && _method->is_native(); }
diff a/src/hotspot/share/code/debugInfo.cpp b/src/hotspot/share/code/debugInfo.cpp
--- a/src/hotspot/share/code/debugInfo.cpp
+++ b/src/hotspot/share/code/debugInfo.cpp
@@ -166,10 +166,11 @@
 }
 
 void ObjectValue::read_object(DebugInfoReadStream* stream) {
   _is_root = stream->read_bool();
   _klass = read_from(stream);
+  _is_init = read_from(stream);
   assert(_klass->is_constant_oop(), "should be constant java mirror oop");
   int length = stream->read_int();
   for (int i = 0; i < length; i++) {
     ScopeValue* val = read_from(stream);
     _field_values.append(val);
@@ -184,10 +185,15 @@
     set_visited(true);
     stream->write_int(is_auto_box() ? AUTO_BOX_OBJECT_CODE : OBJECT_CODE);
     stream->write_int(_id);
     stream->write_bool(_is_root);
     _klass->write_on(stream);
+    if (_is_init == nullptr) {
+      // MarkerValue is used for null-free objects
+      _is_init = new MarkerValue();
+    }
+    _is_init->write_on(stream);
     int length = _field_values.length();
     stream->write_int(length);
     for (int i = 0; i < length; i++) {
       _field_values.at(i)->write_on(stream);
     }
diff a/src/hotspot/share/code/debugInfo.hpp b/src/hotspot/share/code/debugInfo.hpp
--- a/src/hotspot/share/code/debugInfo.hpp
+++ b/src/hotspot/share/code/debugInfo.hpp
@@ -128,46 +128,51 @@
 
 class ObjectValue: public ScopeValue {
  protected:
   int                        _id;
   ScopeValue*                _klass;
+  ScopeValue*                _is_init;
   GrowableArray<ScopeValue*> _field_values;
   Handle                     _value;
   bool                       _visited;
   bool                       _is_root;   // Will be true if this object is referred to
                                          // as a local/expression/monitor in the JVMs.
                                          // Otherwise false, meaning it's just a candidate
                                          // in an object allocation merge.
  public:
-  ObjectValue(int id, ScopeValue* klass)
+  ObjectValue(int id, ScopeValue* klass, ScopeValue* is_init = nullptr)
      : _id(id)
      , _klass(klass)
+     , _is_init(is_init)
      , _field_values()
      , _value()
      , _visited(false)
      , _is_root(true) {
     assert(klass->is_constant_oop(), "should be constant java mirror oop");
   }
 
   ObjectValue(int id)
      : _id(id)
      , _klass(nullptr)
+     , _is_init(new MarkerValue())
      , _field_values()
      , _value()
      , _visited(false)
      , _is_root(true) {}
 
   // Accessors
   bool                        is_object() const           { return true; }
   int                         id() const                  { return _id; }
   virtual ScopeValue*         klass() const               { return _klass; }
+  virtual ScopeValue*         is_init() const             { return _is_init; }
   virtual GrowableArray<ScopeValue*>* field_values()      { return &_field_values; }
   virtual ScopeValue*         field_at(int i) const       { return _field_values.at(i); }
   virtual int                 field_size()                { return _field_values.length(); }
   virtual Handle              value() const               { return _value; }
   bool                        is_visited() const          { return _visited; }
   bool                        is_root() const             { return _is_root; }
+  bool                        maybe_null() const          { return !_is_init->is_marker(); }
 
   void                        set_id(int id)              { _id = id; }
   virtual void                set_value(oop value);
   void                        set_visited(bool visited)   { _visited = visited; }
   void                        set_root(bool root)         { _is_root = root; }
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
@@ -287,10 +287,11 @@
                                               int         bci,
                                               bool        reexecute,
                                               bool        rethrow_exception,
                                               bool        is_method_handle_invoke,
                                               bool        return_oop,
+                                              bool        return_scalarized,
                                               bool        has_ea_local_in_scope,
                                               bool        arg_escape,
                                               DebugToken* locals,
                                               DebugToken* expressions,
                                               DebugToken* monitors) {
@@ -305,10 +306,11 @@
   // Record flags into pcDesc.
   last_pd->set_should_reexecute(reexecute);
   last_pd->set_rethrow_exception(rethrow_exception);
   last_pd->set_is_method_handle_invoke(is_method_handle_invoke);
   last_pd->set_return_oop(return_oop);
+  last_pd->set_return_scalarized(return_scalarized);
   last_pd->set_has_ea_local_in_scope(has_ea_local_in_scope);
   last_pd->set_arg_escape(arg_escape);
 
   // serialize sender stream offset
   stream()->write_int(sender_stream_offset);
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
@@ -105,10 +105,11 @@
                       int         bci,
                       bool        reexecute,
                       bool        rethrow_exception = false,
                       bool        is_method_handle_invoke = false,
                       bool        return_oop = false,
+                      bool        return_scalarized = false,
                       bool        has_ea_local_in_scope = false,
                       bool        arg_escape = false,
                       DebugToken* locals      = nullptr,
                       DebugToken* expressions = nullptr,
                       DebugToken* monitors    = nullptr);
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
@@ -1702,11 +1702,12 @@
   assert(must_be_in_vm(), "raw oops here");
   // Did somebody do a JVMTI RedefineClasses while our backs were turned?
   // Or is there a now a breakpoint?
   // (Assumes compiled code cannot handle bkpts; change if UseFastBreakpoints.)
   if (m->is_old()
-      || m->number_of_breakpoints() > 0) {
+      || m->number_of_breakpoints() > 0
+      || m->mismatch()) {
     return m->method_holder();
   } else {
     return nullptr;
   }
 }
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
@@ -680,10 +680,16 @@
     _nmethod_end_offset      = _nul_chk_table_offset;
 #endif
     _compile_id              = compile_id;
     _entry_point             = code_begin()          + offsets->value(CodeOffsets::Entry);
     _verified_entry_point    = code_begin()          + offsets->value(CodeOffsets::Verified_Entry);
+
+    assert(!method->has_scalarized_args(), "scalarized native wrappers not supported yet"); // for the next 3 fields
+    _inline_entry_point       = _entry_point;
+    _verified_inline_entry_point = _verified_entry_point;
+    _verified_inline_ro_entry_point = _verified_entry_point;
+
     _osr_entry_point         = nullptr;
     _exception_cache         = nullptr;
     _pc_desc_container.reset_to(nullptr);
 
     _exception_offset        = code_offset()         + offsets->value(CodeOffsets::Exceptions);
@@ -865,10 +871,13 @@
 #else
     _nmethod_end_offset      = _nul_chk_table_offset + align_up(nul_chk_table->size_in_bytes(), oopSize);
 #endif
     _entry_point             = code_begin()          + offsets->value(CodeOffsets::Entry);
     _verified_entry_point    = code_begin()          + offsets->value(CodeOffsets::Verified_Entry);
+    _inline_entry_point       = code_begin()         + offsets->value(CodeOffsets::Inline_Entry);
+    _verified_inline_entry_point = code_begin()      + offsets->value(CodeOffsets::Verified_Inline_Entry);
+    _verified_inline_ro_entry_point = code_begin()   + offsets->value(CodeOffsets::Verified_Inline_Entry_RO);
     _osr_entry_point         = code_begin()          + offsets->value(CodeOffsets::OSR_Entry);
     _exception_cache         = nullptr;
     _scopes_data_begin       = (address) this + scopes_data_offset;
 
     _pc_desc_container.reset_to(scopes_pcs_begin());
@@ -2967,119 +2976,148 @@
 
 const char* nmethod::nmethod_section_label(address pos) const {
   const char* label = nullptr;
   if (pos == code_begin())                                              label = "[Instructions begin]";
   if (pos == entry_point())                                             label = "[Entry Point]";
+  if (pos == inline_entry_point())                                      label = "[Inline Entry Point]";
   if (pos == verified_entry_point())                                    label = "[Verified Entry Point]";
+  if (pos == verified_inline_entry_point())                             label = "[Verified Inline Entry Point]";
+  if (pos == verified_inline_ro_entry_point())                          label = "[Verified Inline Entry Point (RO)]";
   if (has_method_handle_invokes() && (pos == deopt_mh_handler_begin())) label = "[Deopt MH Handler Code]";
   if (pos == consts_begin() && pos != insts_begin())                    label = "[Constants]";
   // Check stub_code before checking exception_handler or deopt_handler.
   if (pos == this->stub_begin())                                        label = "[Stub Code]";
   if (JVMCI_ONLY(_exception_offset >= 0 &&) pos == exception_begin())           label = "[Exception Handler]";
   if (JVMCI_ONLY(_deopt_handler_begin != nullptr &&) pos == deopt_handler_begin()) label = "[Deopt Handler Code]";
   return label;
 }
 
+static int maybe_print_entry_label(outputStream* stream, address pos, address entry, const char* label) {
+  if (pos == entry) {
+    stream->bol();
+    stream->print_cr("%s", label);
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
 void nmethod::print_nmethod_labels(outputStream* stream, address block_begin, bool print_section_labels) const {
   if (print_section_labels) {
-    const char* label = nmethod_section_label(block_begin);
-    if (label != nullptr) {
-      stream->bol();
-      stream->print_cr("%s", label);
-    }
-  }
-
-  if (block_begin == entry_point()) {
-    Method* m = method();
-    if (m != nullptr) {
-      stream->print("  # ");
-      m->print_value_on(stream);
-      stream->cr();
-    }
-    if (m != nullptr && !is_osr_method()) {
-      ResourceMark rm;
-      int sizeargs = m->size_of_parameters();
-      BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, sizeargs);
-      VMRegPair* regs   = NEW_RESOURCE_ARRAY(VMRegPair, sizeargs);
-      {
-        int sig_index = 0;
-        if (!m->is_static())
-          sig_bt[sig_index++] = T_OBJECT; // 'this'
-        for (SignatureStream ss(m->signature()); !ss.at_return_type(); ss.next()) {
-          BasicType t = ss.type();
-          sig_bt[sig_index++] = t;
-          if (type2size[t] == 2) {
-            sig_bt[sig_index++] = T_VOID;
-          } else {
-            assert(type2size[t] == 1, "size is 1 or 2");
-          }
-        }
-        assert(sig_index == sizeargs, "");
+    int n = 0;
+    // Multiple entry points may be at the same position. Print them all.
+    n += maybe_print_entry_label(stream, block_begin, entry_point(),                    "[Entry Point]");
+    n += maybe_print_entry_label(stream, block_begin, inline_entry_point(),             "[Inline Entry Point]");
+    n += maybe_print_entry_label(stream, block_begin, verified_entry_point(),           "[Verified Entry Point]");
+    n += maybe_print_entry_label(stream, block_begin, verified_inline_entry_point(),    "[Verified Inline Entry Point]");
+    n += maybe_print_entry_label(stream, block_begin, verified_inline_ro_entry_point(), "[Verified Inline Entry Point (RO)]");
+    if (n == 0) {
+      const char* label = nmethod_section_label(block_begin);
+      if (label != nullptr) {
+        stream->bol();
+        stream->print_cr("%s", label);
       }
-      const char* spname = "sp"; // make arch-specific?
-      SharedRuntime::java_calling_convention(sig_bt, regs, sizeargs);
-      int stack_slot_offset = this->frame_size() * wordSize;
-      int tab1 = 14, tab2 = 24;
-      int sig_index = 0;
-      int arg_index = (m->is_static() ? 0 : -1);
-      bool did_old_sp = false;
-      for (SignatureStream ss(m->signature()); !ss.at_return_type(); ) {
-        bool at_this = (arg_index == -1);
-        bool at_old_sp = false;
-        BasicType t = (at_this ? T_OBJECT : ss.type());
-        assert(t == sig_bt[sig_index], "sigs in sync");
-        if (at_this)
-          stream->print("  # this: ");
-        else
-          stream->print("  # parm%d: ", arg_index);
-        stream->move_to(tab1);
-        VMReg fst = regs[sig_index].first();
-        VMReg snd = regs[sig_index].second();
-        if (fst->is_reg()) {
-          stream->print("%s", fst->name());
-          if (snd->is_valid())  {
-            stream->print(":%s", snd->name());
-          }
-        } else if (fst->is_stack()) {
-          int offset = fst->reg2stack() * VMRegImpl::stack_slot_size + stack_slot_offset;
-          if (offset == stack_slot_offset)  at_old_sp = true;
-          stream->print("[%s+0x%x]", spname, offset);
-        } else {
-          stream->print("reg%d:%d??", (int)(intptr_t)fst, (int)(intptr_t)snd);
-        }
-        stream->print(" ");
-        stream->move_to(tab2);
-        stream->print("= ");
-        if (at_this) {
-          m->method_holder()->print_value_on(stream);
-        } else {
-          bool did_name = false;
-          if (!at_this && ss.is_reference()) {
-            Symbol* name = ss.as_symbol();
-            name->print_value_on(stream);
-            did_name = true;
-          }
-          if (!did_name)
-            stream->print("%s", type2name(t));
-        }
-        if (at_old_sp) {
-          stream->print("  (%s of caller)", spname);
-          did_old_sp = true;
-        }
-        stream->cr();
-        sig_index += type2size[t];
-        arg_index += 1;
-        if (!at_this)  ss.next();
+    }
+  }
+
+  Method* m = method();
+  if (m == nullptr || is_osr_method()) {
+    return;
+  }
+
+  // Print the name of the method (only once)
+  address low = MIN4(entry_point(), verified_entry_point(), verified_inline_entry_point(), verified_inline_ro_entry_point());
+  low = MIN2(low, inline_entry_point());
+  assert(low != 0, "sanity");
+  if (block_begin == low) {
+    stream->print("  # ");
+    m->print_value_on(stream);
+    stream->cr();
+  }
+
+  // Print the arguments for the 3 types of verified entry points
+  CompiledEntrySignature ces(m);
+  ces.compute_calling_conventions(false);
+  const GrowableArray<SigEntry>* sig_cc;
+  const VMRegPair* regs;
+  if (block_begin == verified_entry_point()) {
+    sig_cc = ces.sig_cc();
+    regs = ces.regs_cc();
+  } else if (block_begin == verified_inline_entry_point()) {
+    sig_cc = ces.sig();
+    regs = ces.regs();
+  } else if (block_begin == verified_inline_ro_entry_point()) {
+    sig_cc = ces.sig_cc_ro();
+    regs = ces.regs_cc_ro();
+  } else {
+    return;
+  }
+
+  bool has_this = !m->is_static();
+  if (ces.has_inline_recv() && block_begin == verified_entry_point()) {
+    // <this> argument is scalarized for verified_entry_point()
+    has_this = false;
+  }
+  const char* spname = "sp"; // make arch-specific?
+  int stack_slot_offset = this->frame_size() * wordSize;
+  int tab1 = 14, tab2 = 24;
+  int sig_index = 0;
+  int arg_index = has_this ? -1 : 0;
+  bool did_old_sp = false;
+  for (ExtendedSignature sig = ExtendedSignature(sig_cc, SigEntryFilter()); !sig.at_end(); ++sig) {
+    bool at_this = (arg_index == -1);
+    bool at_old_sp = false;
+    BasicType t = (*sig)._bt;
+    if (at_this) {
+      stream->print("  # this: ");
+    } else {
+      stream->print("  # parm%d: ", arg_index);
+    }
+    stream->move_to(tab1);
+    VMReg fst = regs[sig_index].first();
+    VMReg snd = regs[sig_index].second();
+    if (fst->is_reg()) {
+      stream->print("%s", fst->name());
+      if (snd->is_valid())  {
+        stream->print(":%s", snd->name());
       }
-      if (!did_old_sp) {
-        stream->print("  # ");
-        stream->move_to(tab1);
-        stream->print("[%s+0x%x]", spname, stack_slot_offset);
-        stream->print("  (%s of caller)", spname);
-        stream->cr();
+    } else if (fst->is_stack()) {
+      int offset = fst->reg2stack() * VMRegImpl::stack_slot_size + stack_slot_offset;
+      if (offset == stack_slot_offset)  at_old_sp = true;
+      stream->print("[%s+0x%x]", spname, offset);
+    } else {
+      stream->print("reg%d:%d??", (int)(intptr_t)fst, (int)(intptr_t)snd);
+    }
+    stream->print(" ");
+    stream->move_to(tab2);
+    stream->print("= ");
+    if (at_this) {
+      m->method_holder()->print_value_on(stream);
+    } else {
+      bool did_name = false;
+      if (is_reference_type(t)) {
+        Symbol* name = (*sig)._symbol;
+        name->print_value_on(stream);
+        did_name = true;
       }
+      if (!did_name)
+        stream->print("%s", type2name(t));
     }
+    if (at_old_sp) {
+      stream->print("  (%s of caller)", spname);
+      did_old_sp = true;
+    }
+    stream->cr();
+    sig_index += type2size[t];
+    arg_index += 1;
+  }
+  if (!did_old_sp) {
+    stream->print("  # ");
+    stream->move_to(tab1);
+    stream->print("[%s+0x%x]", spname, stack_slot_offset);
+    stream->print("  (%s of caller)", spname);
+    stream->cr();
   }
 }
 
 // Returns whether this nmethod has code comments.
 bool nmethod::has_code_comment(address begin, address end) {
@@ -3199,11 +3237,11 @@
           }
         default:
           break;
         }
       }
-      st->print(" {reexecute=%d rethrow=%d return_oop=%d}", sd->should_reexecute(), sd->rethrow_exception(), sd->return_oop());
+      st->print(" {reexecute=%d rethrow=%d return_oop=%d return_scalarized=%d}", sd->should_reexecute(), sd->rethrow_exception(), sd->return_oop(), sd->return_scalarized());
     }
 
     // Print all scopes
     for (;sd != nullptr; sd = sd->sender()) {
       st->move_to(column, 6, 0);
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
@@ -24,10 +24,11 @@
 
 #ifndef SHARE_CODE_NMETHOD_HPP
 #define SHARE_CODE_NMETHOD_HPP
 
 #include "code/compiledMethod.hpp"
+#include "compiler/compilerDefinitions.hpp"
 
 class CompileTask;
 class DepChange;
 class DirectiveSet;
 class DebugInformationRecorder;
@@ -192,10 +193,13 @@
   oops_do_mark_link* volatile _oops_do_mark_link;
 
   // offsets for entry points
   address _entry_point;                      // entry point with class check
   address _verified_entry_point;             // entry point without class check
+  address _inline_entry_point;               // inline type entry point (unpack all inline type args) with class check
+  address _verified_inline_entry_point;      // inline type entry point (unpack all inline type args) without class check
+  address _verified_inline_ro_entry_point;   // inline type entry point (unpack receiver only) without class check
   address _osr_entry_point;                  // entry point for on stack replacement
 
   bool _is_unlinked;
 
   // Shared fields for all nmethod's
@@ -425,12 +429,15 @@
   bool metadata_contains     (Metadata** addr) const   { return metadata_begin     () <= addr && addr < metadata_end     (); }
   bool scopes_data_contains  (address addr) const { return scopes_data_begin  () <= addr && addr < scopes_data_end  (); }
   bool scopes_pcs_contains   (PcDesc* addr) const { return scopes_pcs_begin   () <= addr && addr < scopes_pcs_end   (); }
 
   // entry points
-  address entry_point() const                     { return _entry_point;             } // normal entry point
-  address verified_entry_point() const            { return _verified_entry_point;    } // if klass is correct
+  address entry_point() const                     { return _entry_point;             }        // normal entry point
+  address verified_entry_point() const            { return _verified_entry_point;    }        // normal entry point without class check
+  address inline_entry_point() const              { return _inline_entry_point; }             // inline type entry point (unpack all inline type args)
+  address verified_inline_entry_point() const     { return _verified_inline_entry_point; }    // inline type entry point (unpack all inline type args) without class check
+  address verified_inline_ro_entry_point() const  { return _verified_inline_ro_entry_point; } // inline type entry point (only unpack receiver) without class check
 
   // flag accessing and manipulation
   bool  is_not_installed() const                  { return _state == not_installed; }
   bool  is_in_use() const                         { return _state <= in_use; }
   bool  is_not_entrant() const                    { return _state == not_entrant; }
diff a/src/hotspot/share/code/pcDesc.hpp b/src/hotspot/share/code/pcDesc.hpp
--- a/src/hotspot/share/code/pcDesc.hpp
+++ b/src/hotspot/share/code/pcDesc.hpp
@@ -42,11 +42,12 @@
     PCDESC_reexecute                 = 1 << 0,
     PCDESC_is_method_handle_invoke   = 1 << 1,
     PCDESC_return_oop                = 1 << 2,
     PCDESC_rethrow_exception         = 1 << 3,
     PCDESC_has_ea_local_in_scope     = 1 << 4,
-    PCDESC_arg_escape                = 1 << 5
+    PCDESC_arg_escape                = 1 << 5,
+    PCDESC_return_scalarized         = 1 << 6
   };
 
   int _flags;
 
   void set_flag(int mask, bool z) {
@@ -89,10 +90,12 @@
   void set_is_method_handle_invoke(bool z)       { set_flag(PCDESC_is_method_handle_invoke, z); }
 
   bool     return_oop()                    const { return (_flags & PCDESC_return_oop) != 0;     }
   void set_return_oop(bool z)                    { set_flag(PCDESC_return_oop, z); }
 
+  bool     return_scalarized()             const { return (_flags & PCDESC_return_scalarized) != 0; }
+  void set_return_scalarized(bool z)             { set_flag(PCDESC_return_scalarized, z); }
   // Indicates if there are objects in scope that, based on escape analysis, are local to the
   // compiled method or local to the current thread, i.e. NoEscape or ArgEscape
   bool     has_ea_local_in_scope()         const { return (_flags & PCDESC_has_ea_local_in_scope) != 0; }
   void set_has_ea_local_in_scope(bool z)         { set_flag(PCDESC_has_ea_local_in_scope, z); }
 
diff a/src/hotspot/share/code/scopeDesc.cpp b/src/hotspot/share/code/scopeDesc.cpp
--- a/src/hotspot/share/code/scopeDesc.cpp
+++ b/src/hotspot/share/code/scopeDesc.cpp
@@ -38,10 +38,11 @@
   _decode_offset = pd->scope_decode_offset();
   _objects       = decode_object_values(obj_decode_offset);
   _reexecute     = pd->should_reexecute();
   _rethrow_exception = pd->rethrow_exception();
   _return_oop    = pd->return_oop();
+  _return_scalarized = pd->return_scalarized();
   _has_ea_local_in_scope = ignore_objects ? false : pd->has_ea_local_in_scope();
   _arg_escape    = ignore_objects ? false : pd->arg_escape();
   decode_body();
 }
 
@@ -51,10 +52,11 @@
   _decode_offset = decode_offset;
   _objects       = parent->_objects;
   _reexecute     = false; //reexecute only applies to the first scope
   _rethrow_exception = false;
   _return_oop    = false;
+  _return_scalarized = false;
   _has_ea_local_in_scope = parent->has_ea_local_in_scope();
   _arg_escape    = false;
   decode_body();
 }
 
diff a/src/hotspot/share/code/scopeDesc.hpp b/src/hotspot/share/code/scopeDesc.hpp
--- a/src/hotspot/share/code/scopeDesc.hpp
+++ b/src/hotspot/share/code/scopeDesc.hpp
@@ -70,10 +70,11 @@
   Method* method()      const { return _method; }
   int          bci()      const { return _bci;    }
   bool should_reexecute() const { return _reexecute; }
   bool rethrow_exception() const { return _rethrow_exception; }
   bool return_oop()       const { return _return_oop; }
+  bool return_scalarized() const { return _return_scalarized; }
   // Returns true if one or more NoEscape or ArgEscape objects exist in
   // any of the scopes at compiled pc.
   bool has_ea_local_in_scope() const { return _has_ea_local_in_scope; }
   bool arg_escape()       const { return _arg_escape; }
 
@@ -103,14 +104,14 @@
   Method*       _method;
   int           _bci;
   bool          _reexecute;
   bool          _rethrow_exception;
   bool          _return_oop;
+  bool          _return_scalarized;
   bool          _has_ea_local_in_scope;       // One or more NoEscape or ArgEscape objects exist in
                                               // any of the scopes at compiled pc.
   bool          _arg_escape;                  // Compiled Java call in youngest scope passes ArgEscape
-
   // Decoding offsets
   int _decode_offset;
   int _sender_decode_offset;
   int _locals_decode_offset;
   int _expressions_decode_offset;
diff a/src/hotspot/share/code/vtableStubs.cpp b/src/hotspot/share/code/vtableStubs.cpp
--- a/src/hotspot/share/code/vtableStubs.cpp
+++ b/src/hotspot/share/code/vtableStubs.cpp
@@ -206,80 +206,84 @@
   check_and_set_size_limit(is_vtable_stub, masm->offset(), slop_bytes);
   s->set_exception_points(npe_addr, ame_addr);
 }
 
 
-address VtableStubs::find_stub(bool is_vtable_stub, int vtable_index) {
+address VtableStubs::find_stub(bool is_vtable_stub, int vtable_index, bool caller_is_c1) {
   assert(vtable_index >= 0, "must be positive");
 
   VtableStub* s;
   {
     MutexLocker ml(VtableStubs_lock, Mutex::_no_safepoint_check_flag);
-    s = lookup(is_vtable_stub, vtable_index);
+    s = lookup(is_vtable_stub, vtable_index, caller_is_c1);
     if (s == nullptr) {
       if (is_vtable_stub) {
-        s = create_vtable_stub(vtable_index);
+        s = create_vtable_stub(vtable_index, caller_is_c1);
       } else {
-        s = create_itable_stub(vtable_index);
+        s = create_itable_stub(vtable_index, caller_is_c1);
       }
 
       // Creation of vtable or itable can fail if there is not enough free space in the code cache.
       if (s == nullptr) {
         return nullptr;
       }
 
-      enter(is_vtable_stub, vtable_index, s);
+      enter(is_vtable_stub, vtable_index, caller_is_c1, s);
       if (PrintAdapterHandlers) {
-        tty->print_cr("Decoding VtableStub %s[%d]@" PTR_FORMAT " [" PTR_FORMAT ", " PTR_FORMAT "] (" SIZE_FORMAT " bytes)",
+        tty->print_cr("Decoding VtableStub (%s) %s[%d]@" PTR_FORMAT " [" PTR_FORMAT ", " PTR_FORMAT "] (" SIZE_FORMAT " bytes)",
+                      caller_is_c1 ? "c1" : "full opt",
                       is_vtable_stub? "vtbl": "itbl", vtable_index, p2i(VtableStub::receiver_location()),
                       p2i(s->code_begin()), p2i(s->code_end()), pointer_delta(s->code_end(), s->code_begin(), 1));
         Disassembler::decode(s->code_begin(), s->code_end());
       }
       // Notify JVMTI about this stub. The event will be recorded by the enclosing
       // JvmtiDynamicCodeEventCollector and posted when this thread has released
       // all locks. Only post this event if a new state is not required. Creating a new state would
       // cause a safepoint and the caller of this code has a NoSafepointVerifier.
       if (JvmtiExport::should_post_dynamic_code_generated()) {
-        JvmtiExport::post_dynamic_code_generated_while_holding_locks(is_vtable_stub? "vtable stub": "itable stub",
+        JvmtiExport::post_dynamic_code_generated_while_holding_locks(is_vtable_stub? "vtable stub": "itable stub",  // FIXME: need to pass caller_is_c1??
                                                                      s->code_begin(), s->code_end());
       }
     }
   }
   return s->entry_point();
 }
 
 
-inline uint VtableStubs::hash(bool is_vtable_stub, int vtable_index){
+inline uint VtableStubs::hash(bool is_vtable_stub, int vtable_index, bool caller_is_c1) {
   // Assumption: receiver_location < 4 in most cases.
   int hash = ((vtable_index << 2) ^ VtableStub::receiver_location()->value()) + vtable_index;
+  if (caller_is_c1) {
+    hash = 7 - hash;
+  }
   return (is_vtable_stub ? ~hash : hash)  & mask;
 }
 
 
-VtableStub* VtableStubs::lookup(bool is_vtable_stub, int vtable_index) {
+VtableStub* VtableStubs::lookup(bool is_vtable_stub, int vtable_index, bool caller_is_c1) {
   assert_lock_strong(VtableStubs_lock);
-  unsigned hash = VtableStubs::hash(is_vtable_stub, vtable_index);
+  unsigned hash = VtableStubs::hash(is_vtable_stub, vtable_index, caller_is_c1);
   VtableStub* s = Atomic::load(&_table[hash]);
-  while( s && !s->matches(is_vtable_stub, vtable_index)) s = s->next();
+  while( s && !s->matches(is_vtable_stub, vtable_index, caller_is_c1)) s = s->next();
   return s;
 }
 
 
-void VtableStubs::enter(bool is_vtable_stub, int vtable_index, VtableStub* s) {
+void VtableStubs::enter(bool is_vtable_stub, int vtable_index, bool caller_is_c1, VtableStub* s) {
   assert_lock_strong(VtableStubs_lock);
-  assert(s->matches(is_vtable_stub, vtable_index), "bad vtable stub");
-  unsigned int h = VtableStubs::hash(is_vtable_stub, vtable_index);
+  assert(s->matches(is_vtable_stub, vtable_index, caller_is_c1), "bad vtable stub");
+  unsigned int h = VtableStubs::hash(is_vtable_stub, vtable_index, caller_is_c1);
   // Insert s at the beginning of the corresponding list.
   s->set_next(Atomic::load(&_table[h]));
   // Make sure that concurrent readers not taking the mutex observe the writing of "next".
   Atomic::release_store(&_table[h], s);
 }
 
 VtableStub* VtableStubs::entry_point(address pc) {
   MutexLocker ml(VtableStubs_lock, Mutex::_no_safepoint_check_flag);
   VtableStub* stub = (VtableStub*)(pc - VtableStub::entry_offset());
-  uint hash = VtableStubs::hash(stub->is_vtable_stub(), stub->index());
+  uint hash = VtableStubs::hash(stub->is_vtable_stub(), stub->index(), stub->caller_is_c1());
   VtableStub* s;
   for (s = Atomic::load(&_table[hash]); s != nullptr && s != stub; s = s->next()) {}
   return (s == stub) ? s : nullptr;
 }
 
diff a/src/hotspot/share/code/vtableStubs.hpp b/src/hotspot/share/code/vtableStubs.hpp
--- a/src/hotspot/share/code/vtableStubs.hpp
+++ b/src/hotspot/share/code/vtableStubs.hpp
@@ -86,27 +86,27 @@
   friend class VtableStub;
   static VtableStub* volatile _table[N];                  // table of existing stubs
   static int         _vtab_stub_size;            // current size estimate for vtable stub (quasi-constant)
   static int         _itab_stub_size;            // current size estimate for itable stub (quasi-constant)
 
-  static VtableStub* create_vtable_stub(int vtable_index);
-  static VtableStub* create_itable_stub(int vtable_index);
-  static VtableStub* lookup            (bool is_vtable_stub, int vtable_index);
-  static void        enter             (bool is_vtable_stub, int vtable_index, VtableStub* s);
-  static inline uint hash              (bool is_vtable_stub, int vtable_index);
-  static address     find_stub         (bool is_vtable_stub, int vtable_index);
+  static VtableStub* create_vtable_stub(int vtable_index, bool caller_is_c1);
+  static VtableStub* create_itable_stub(int vtable_index, bool caller_is_c1);
+  static VtableStub* lookup            (bool is_vtable_stub, int vtable_index, bool caller_is_c1);
+  static void        enter             (bool is_vtable_stub, int vtable_index, bool caller_is_c1, VtableStub* s);
+  static inline uint hash              (bool is_vtable_stub, int vtable_index, bool caller_is_c1);
+  static address     find_stub         (bool is_vtable_stub, int vtable_index, bool caller_is_c1);
   static void        bookkeeping(MacroAssembler* masm, outputStream* out, VtableStub* s,
                                  address npe_addr, address ame_addr,   bool is_vtable_stub,
                                  int     index,    int     slop_bytes, int  index_dependent_slop);
   static int         code_size_limit(bool is_vtable_stub);
   static void        check_and_set_size_limit(bool is_vtable_stub,
                                               int   code_size,
                                               int   padding);
 
  public:
-  static address     find_vtable_stub(int vtable_index) { return find_stub(true,  vtable_index); }
-  static address     find_itable_stub(int itable_index) { return find_stub(false, itable_index); }
+  static address     find_vtable_stub(int vtable_index, bool caller_is_c1) { return find_stub(true,  vtable_index, caller_is_c1); }
+  static address     find_itable_stub(int itable_index, bool caller_is_c1) { return find_stub(false, itable_index, caller_is_c1); }
 
   static VtableStub* entry_point(address pc);                        // vtable stub entry point for a pc
   static bool        is_icholder_entry(address pc);                  // is the blob containing pc (which must be a vtable blob) an icholder?
   static bool        contains(address pc);                           // is pc within any stub?
   static VtableStub* stub_containing(address pc);                    // stub containing pc or nullptr
@@ -126,17 +126,19 @@
   VtableStub*    _next;              // Pointer to next entry in hash table
   const short    _index;             // vtable index
   short          _ame_offset;        // Where an AbstractMethodError might occur
   short          _npe_offset;        // Where a NullPointerException might occur
   bool           _is_vtable_stub;    // True if vtable stub, false, is itable stub
+  bool           _caller_is_c1;      // True if this is for a caller compiled by C1,
+                                     // which doesn't scalarize parameters.
   /* code follows here */            // The vtableStub code
 
   void* operator new(size_t size, int code_size) throw();
 
-  VtableStub(bool is_vtable_stub, short index)
+  VtableStub(bool is_vtable_stub, short index, bool caller_is_c1)
         : _next(nullptr), _index(index), _ame_offset(-1), _npe_offset(-1),
-          _is_vtable_stub(is_vtable_stub) {}
+          _is_vtable_stub(is_vtable_stub), _caller_is_c1(caller_is_c1) {}
   VtableStub* next() const                       { return _next; }
   int index() const                              { return _index; }
   static VMReg receiver_location()               { return _receiver_location; }
   void set_next(VtableStub* n)                   { _next = n; }
 
@@ -144,12 +146,12 @@
   address code_begin() const                     { return (address)(this + 1); }
   address code_end() const                       { return code_begin() + VtableStubs::code_size_limit(_is_vtable_stub); }
   address entry_point() const                    { return code_begin(); }
   static int entry_offset()                      { return sizeof(class VtableStub); }
 
-  bool matches(bool is_vtable_stub, int index) const {
-    return _index == index && _is_vtable_stub == is_vtable_stub;
+  bool matches(bool is_vtable_stub, int index, bool caller_is_c1) const {
+    return _index == index && _is_vtable_stub == is_vtable_stub && _caller_is_c1 == caller_is_c1;
   }
   bool contains(address pc) const                { return code_begin() <= pc && pc < code_end(); }
 
  private:
   void set_exception_points(address npe_addr, address ame_addr) {
@@ -173,10 +175,11 @@
 
  public:
   // Query
   bool is_itable_stub()                          { return !_is_vtable_stub; }
   bool is_vtable_stub()                          { return  _is_vtable_stub; }
+  bool caller_is_c1()                            { return  _caller_is_c1;   }
   bool is_abstract_method_error(address epc)     { return epc == code_begin()+_ame_offset; }
   bool is_null_pointer_exception(address epc)    { return epc == code_begin()+_npe_offset; }
 
   void print_on(outputStream* st) const;
   void print() const;
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
@@ -1240,11 +1240,11 @@
       // In libjvmci, JVMCI initialization should not deadlock with other threads
       if (!UseJVMCINativeLibrary) {
         // Don't allow blocking compiles if inside a class initializer or while performing class loading
         vframeStream vfst(JavaThread::cast(thread));
         for (; !vfst.at_end(); vfst.next()) {
-          if (vfst.method()->is_static_initializer() ||
+        if (vfst.method()->is_class_initializer() ||
               (vfst.method()->method_holder()->is_subclass_of(vmClasses::ClassLoader_klass()) &&
                   vfst.method()->name() == vmSymbols::loadClass_name())) {
             blocking = false;
             break;
           }
diff a/src/hotspot/share/compiler/compilerEvent.cpp b/src/hotspot/share/compiler/compilerEvent.cpp
--- a/src/hotspot/share/compiler/compilerEvent.cpp
+++ b/src/hotspot/share/compiler/compilerEvent.cpp
@@ -55,30 +55,30 @@
 };
 
 Semaphore PhaseTypeGuard::_mutex_semaphore(1);
 
 // Table for mapping compiler phases names to int identifiers.
-static GrowableArray<const char*>* phase_names = nullptr;
+static GrowableArray<const char*>* _phase_names = nullptr;
 
 class CompilerPhaseTypeConstant : public JfrSerializer {
  public:
   void serialize(JfrCheckpointWriter& writer) {
     PhaseTypeGuard guard;
-    assert(phase_names != nullptr, "invariant");
-    assert(phase_names->is_nonempty(), "invariant");
-    const u4 nof_entries = phase_names->length();
+    assert(_phase_names != nullptr, "invariant");
+    assert(_phase_names->is_nonempty(), "invariant");
+    const u4 nof_entries = _phase_names->length();
     writer.write_count(nof_entries);
     for (u4 i = 0; i < nof_entries; i++) {
       writer.write_key(i);
-      writer.write(phase_names->at(i));
+      writer.write(_phase_names->at(i));
     }
   }
 };
 
 static int lookup_phase(const char* phase_name) {
-  for (int i = 0; i < phase_names->length(); i++) {
-    const char* name = phase_names->at(i);
+  for (int i = 0; i < _phase_names->length(); i++) {
+    const char* name = _phase_names->at(i);
     if (strcmp(name, phase_name) == 0) {
       return i;
     }
   }
   return -1;
@@ -87,24 +87,24 @@
 int CompilerEvent::PhaseEvent::get_phase_id(const char* phase_name, bool may_exist, bool use_strdup, bool sync) {
   int index;
   bool register_jfr_serializer = false;
   {
     PhaseTypeGuard guard(sync);
-    if (phase_names == nullptr) {
-      phase_names = new (mtInternal) GrowableArray<const char*>(100, mtCompiler);
+    if (_phase_names == nullptr) {
+      _phase_names = new (mtInternal) GrowableArray<const char*>(100, mtCompiler);
       register_jfr_serializer = true;
     } else if (may_exist) {
       index = lookup_phase(phase_name);
       if (index != -1) {
         return index;
       }
     } else {
       assert((index = lookup_phase(phase_name)) == -1, "phase name \"%s\" already registered: %d", phase_name, index);
     }
 
-    index = phase_names->length();
-    phase_names->append(use_strdup ? os::strdup(phase_name) : phase_name);
+    index = _phase_names->length();
+    _phase_names->append(use_strdup ? os::strdup(phase_name) : phase_name);
   }
   if (register_jfr_serializer) {
     JfrSerializer::register_serializer(TYPE_COMPILERPHASETYPE, false, new CompilerPhaseTypeConstant());
   } else if (Jfr::is_recording()) {
     // serialize new phase.
diff a/src/hotspot/share/compiler/methodLiveness.cpp b/src/hotspot/share/compiler/methodLiveness.cpp
--- a/src/hotspot/share/compiler/methodLiveness.cpp
+++ b/src/hotspot/share/compiler/methodLiveness.cpp
@@ -617,13 +617,16 @@
     case Bytecodes::_lookupswitch:
       // These bytecodes have no effect on the method's locals.
       break;
 
     case Bytecodes::_return:
-      if (instruction->method()->intrinsic_id() == vmIntrinsics::_Object_init) {
+      if (instruction->method()->intrinsic_id() == vmIntrinsics::_Object_init ||
+          (instruction->method()->is_object_constructor() && instruction->method()->holder()->is_inlinetype())) {
         // return from Object.init implicitly registers a finalizer
         // for the receiver if needed, so keep it alive.
+        // Value class constructors update the scalarized receiver. Keep it live so that
+        // we can find it after (chained) constructor calls and propagate updates to the caller.
         load_one(0);
       }
       break;
 
 
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
@@ -302,11 +302,11 @@
 
     if ((strchr(method_name, JVM_SIGNATURE_SPECIAL) != nullptr) ||
         (strchr(method_name, JVM_SIGNATURE_ENDSPECIAL) != nullptr)) {
       if (!vmSymbols::object_initializer_name()->equals(method_name) &&
           !vmSymbols::class_initializer_name()->equals(method_name)) {
-        error_msg = "Chars '<' and '>' only allowed in <init> and <clinit>";
+        error_msg = "Chars '<' and '>' only allowed in <init>, <clinit>";
         return;
       }
     }
 
     if (c_match == MethodMatcher::Unknown || m_match == MethodMatcher::Unknown) {
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
@@ -32,10 +32,11 @@
 #include "logging/log.hpp"
 #include "logging/logStream.hpp"
 #include "memory/allocation.inline.hpp"
 #include "memory/iterator.hpp"
 #include "memory/resourceArea.hpp"
+#include "oops/inlineKlass.hpp"
 #include "oops/compressedOops.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/frame.inline.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/signature.hpp"
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
@@ -708,13 +708,13 @@
 
 bool G1BarrierSetC2::is_gc_pre_barrier_node(Node *node) const {
   return is_g1_pre_val_load(node);
 }
 
-void G1BarrierSetC2::eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const {
+void G1BarrierSetC2::eliminate_gc_barrier(PhaseIterGVN* igvn, Node* node) const {
   if (is_g1_pre_val_load(node)) {
-    macro->replace_node(node, macro->zerocon(node->as_Load()->bottom_type()->basic_type()));
+    igvn->replace_node(node, igvn->zerocon(node->as_Load()->bottom_type()->basic_type()));
   } else {
     assert(node->Opcode() == Op_CastP2X, "ConvP2XNode required");
     assert(node->outcnt() <= 2, "expects 1 or 2 users: Xor and URShift nodes");
     // It could be only one user, URShift node, in Object.clone() intrinsic
     // but the new allocation is passed to arraycopy stub and it could not
@@ -739,36 +739,34 @@
       Node* shift = xorx->unique_out();
       Node* cmpx = shift->unique_out();
       assert(cmpx->is_Cmp() && cmpx->unique_out()->is_Bool() &&
           cmpx->unique_out()->as_Bool()->_test._test == BoolTest::ne,
           "missing region check in G1 post barrier");
-      macro->replace_node(cmpx, macro->makecon(TypeInt::CC_EQ));
+      igvn->replace_node(cmpx, igvn->makecon(TypeInt::CC_EQ));
 
       // Remove G1 pre barrier.
 
       // Search "if (marking != 0)" check and set it to "false".
       // There is no G1 pre barrier if previous stored value is null
       // (for example, after initialization).
       if (this_region->is_Region() && this_region->req() == 3) {
-        int ind = 1;
-        if (!this_region->in(ind)->is_IfFalse()) {
-          ind = 2;
-        }
-        if (this_region->in(ind)->is_IfFalse() &&
-            this_region->in(ind)->in(0)->Opcode() == Op_If) {
-          Node* bol = this_region->in(ind)->in(0)->in(1);
-          assert(bol->is_Bool(), "");
-          cmpx = bol->in(1);
-          if (bol->as_Bool()->_test._test == BoolTest::ne &&
-              cmpx->is_Cmp() && cmpx->in(2) == macro->intcon(0) &&
-              cmpx->in(1)->is_Load()) {
-            Node* adr = cmpx->in(1)->as_Load()->in(MemNode::Address);
-            const int marking_offset = in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset());
-            if (adr->is_AddP() && adr->in(AddPNode::Base) == macro->top() &&
-                adr->in(AddPNode::Address)->Opcode() == Op_ThreadLocal &&
-                adr->in(AddPNode::Offset) == macro->MakeConX(marking_offset)) {
-              macro->replace_node(cmpx, macro->makecon(TypeInt::CC_EQ));
+        for (int i = 1; i < 3; ++i) {
+          if (this_region->in(i)->is_IfFalse() &&
+              this_region->in(i)->in(0)->is_If() &&
+              this_region->in(i)->in(0)->in(1)->is_Bool()) {
+            Node* bol = this_region->in(i)->in(0)->in(1);
+            cmpx = bol->in(1);
+            if (bol->as_Bool()->_test._test == BoolTest::ne &&
+                cmpx->is_Cmp() && cmpx->in(2) == igvn->intcon(0) &&
+                cmpx->in(1)->is_Load()) {
+              Node* adr = cmpx->in(1)->as_Load()->in(MemNode::Address);
+              const int marking_offset = in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset());
+              if (adr->is_AddP() && adr->in(AddPNode::Base) == igvn->C->top() &&
+                  adr->in(AddPNode::Address)->Opcode() == Op_ThreadLocal &&
+                  adr->in(AddPNode::Offset) == igvn->MakeConX(marking_offset)) {
+                igvn->replace_node(cmpx, igvn->makecon(TypeInt::CC_EQ));
+              }
             }
           }
         }
       }
     } else {
@@ -783,17 +781,17 @@
       assert(load != nullptr, "missing G1 post barrier");
       Node* cmpx = load->unique_out();
       assert(cmpx->is_Cmp() && cmpx->unique_out()->is_Bool() &&
           cmpx->unique_out()->as_Bool()->_test._test == BoolTest::ne,
           "missing card value check in G1 post barrier");
-      macro->replace_node(cmpx, macro->makecon(TypeInt::CC_EQ));
+      igvn->replace_node(cmpx, igvn->makecon(TypeInt::CC_EQ));
       // There is no G1 pre barrier in this case
     }
     // Now CastP2X can be removed since it is used only on dead path
     // which currently still alive until igvn optimize it.
     assert(node->outcnt() == 0 || node->unique_out()->Opcode() == Op_URShiftX, "");
-    macro->replace_node(node, macro->top());
+    igvn->replace_node(node, igvn->C->top());
   }
 }
 
 Node* G1BarrierSetC2::step_over_gc_barrier(Node* c) const {
   if (!use_ReduceInitialCardMarks() &&
diff a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.hpp b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.hpp
--- a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.hpp
+++ b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.hpp
@@ -92,11 +92,11 @@
 
   static bool is_g1_pre_val_load(Node* n);
 public:
   virtual bool is_gc_pre_barrier_node(Node* node) const;
   virtual bool is_gc_barrier_node(Node* node) const;
-  virtual void eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const;
+  virtual void eliminate_gc_barrier(PhaseIterGVN* igvn, Node* node) const;
   virtual Node* step_over_gc_barrier(Node* c) const;
 
 #ifdef ASSERT
   virtual void verify_gc_barriers(Compile* compile, CompilePhase phase) const;
 #endif
diff a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp
--- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp
+++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp
@@ -524,11 +524,12 @@
       update_bot_after_copying(obj, word_sz);
     }
 
     // Most objects are not arrays, so do one array check rather than
     // checking for each array category for each object.
-    if (klass->is_array_klass()) {
+    // CMH: Valhalla flat arrays can split this work up, but for now, doesn't
+    if (klass->is_array_klass() && !klass->is_flatArray_klass()) {
       if (klass->is_objArray_klass()) {
         start_partial_objarray(dest_attr, old, obj);
       } else {
         // Nothing needs to be done for typeArrays.  Body doesn't contain
         // any oops to scan, and the type in the klass will already be handled
diff a/src/hotspot/share/gc/parallel/psCompactionManager.cpp b/src/hotspot/share/gc/parallel/psCompactionManager.cpp
--- a/src/hotspot/share/gc/parallel/psCompactionManager.cpp
+++ b/src/hotspot/share/gc/parallel/psCompactionManager.cpp
@@ -32,10 +32,11 @@
 #include "gc/shared/taskqueue.inline.hpp"
 #include "logging/log.hpp"
 #include "memory/iterator.inline.hpp"
 #include "oops/access.inline.hpp"
 #include "oops/compressedOops.inline.hpp"
+#include "oops/flatArrayKlass.inline.hpp"
 #include "oops/instanceKlass.inline.hpp"
 #include "oops/instanceMirrorKlass.inline.hpp"
 #include "oops/objArrayKlass.inline.hpp"
 #include "oops/oop.inline.hpp"
 
diff a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp
--- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp
+++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp
@@ -68,10 +68,11 @@
 #include "memory/metaspaceUtils.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
 #include "nmt/memTracker.hpp"
 #include "oops/access.inline.hpp"
+#include "oops/flatArrayKlass.inline.hpp"
 #include "oops/instanceClassLoaderKlass.inline.hpp"
 #include "oops/instanceKlass.inline.hpp"
 #include "oops/instanceMirrorKlass.inline.hpp"
 #include "oops/methodData.hpp"
 #include "oops/objArrayKlass.inline.hpp"
diff a/src/hotspot/share/gc/parallel/psPromotionManager.cpp b/src/hotspot/share/gc/parallel/psPromotionManager.cpp
--- a/src/hotspot/share/gc/parallel/psPromotionManager.cpp
+++ b/src/hotspot/share/gc/parallel/psPromotionManager.cpp
@@ -40,10 +40,11 @@
 #include "memory/memRegion.hpp"
 #include "memory/padded.inline.hpp"
 #include "memory/resourceArea.hpp"
 #include "oops/access.inline.hpp"
 #include "oops/compressedOops.inline.hpp"
+#include "oops/flatArrayKlass.inline.hpp"
 
 PaddedEnd<PSPromotionManager>* PSPromotionManager::_manager_array = nullptr;
 PSPromotionManager::PSScannerTasksQueueSet* PSPromotionManager::_stack_array_depth = nullptr;
 PreservedMarksSet*             PSPromotionManager::_preserved_marks_set = nullptr;
 PSOldGen*                      PSPromotionManager::_old_gen = nullptr;
diff a/src/hotspot/share/gc/shared/barrierSet.cpp b/src/hotspot/share/gc/shared/barrierSet.cpp
--- a/src/hotspot/share/gc/shared/barrierSet.cpp
+++ b/src/hotspot/share/gc/shared/barrierSet.cpp
@@ -21,14 +21,17 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "classfile/vmSymbols.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/barrierSetAssembler.hpp"
 #include "gc/shared/barrierSetNMethod.hpp"
 #include "gc/shared/barrierSetStackChunk.hpp"
+#include "memory/resourceArea.hpp"
+#include "oops/objArrayKlass.inline.hpp"
 #include "runtime/continuation.hpp"
 #include "runtime/javaThread.hpp"
 #include "utilities/debug.hpp"
 #include "utilities/macros.hpp"
 
@@ -50,10 +53,37 @@
   assert(!JavaThread::current()->on_thread_list(),
          "Main thread already on thread list.");
   _barrier_set->on_thread_create(Thread::current());
 }
 
+void BarrierSet::throw_array_null_pointer_store_exception(arrayOop src, arrayOop dst, TRAPS) {
+  ResourceMark rm(THREAD);
+  Klass* bound = ObjArrayKlass::cast(dst->klass())->element_klass();
+  stringStream ss;
+  ss.print("arraycopy: can not copy null values into %s[]",
+           bound->external_name());
+  THROW_MSG(vmSymbols::java_lang_NullPointerException(), ss.as_string());
+}
+
+void BarrierSet::throw_array_store_exception(arrayOop src, arrayOop dst, TRAPS) {
+  ResourceMark rm(THREAD);
+  Klass* bound = ObjArrayKlass::cast(dst->klass())->element_klass();
+  Klass* stype = ObjArrayKlass::cast(src->klass())->element_klass();
+  stringStream ss;
+  if (!bound->is_subtype_of(stype)) {
+    ss.print("arraycopy: type mismatch: can not copy %s[] into %s[]",
+             stype->external_name(), bound->external_name());
+  } else {
+    // oop_arraycopy should return the index in the source array that
+    // contains the problematic oop.
+    ss.print("arraycopy: element type mismatch: can not cast one of the elements"
+             " of %s[] to the type of the destination array, %s",
+             stype->external_name(), bound->external_name());
+  }
+  THROW_MSG(vmSymbols::java_lang_ArrayStoreException(), ss.as_string());
+}
+
 static BarrierSetNMethod* select_barrier_set_nmethod(BarrierSetNMethod* barrier_set_nmethod) {
   if (barrier_set_nmethod != nullptr) {
     // The GC needs nmethod entry barriers to do concurrent GC
     return barrier_set_nmethod;
   } else {
diff a/src/hotspot/share/gc/shared/barrierSet.hpp b/src/hotspot/share/gc/shared/barrierSet.hpp
--- a/src/hotspot/share/gc/shared/barrierSet.hpp
+++ b/src/hotspot/share/gc/shared/barrierSet.hpp
@@ -28,10 +28,11 @@
 #include "gc/shared/barrierSetConfig.hpp"
 #include "memory/memRegion.hpp"
 #include "oops/access.hpp"
 #include "oops/accessBackend.hpp"
 #include "oops/oopsHierarchy.hpp"
+#include "utilities/exceptions.hpp"
 #include "utilities/fakeRttiSupport.hpp"
 #include "utilities/macros.hpp"
 
 class BarrierSetAssembler;
 class BarrierSetC1;
@@ -117,10 +118,13 @@
   template <class BarrierSetC2T>
   static BarrierSetC2* make_barrier_set_c2() {
     return COMPILER2_PRESENT(new BarrierSetC2T()) NOT_COMPILER2(nullptr);
   }
 
+  static void throw_array_null_pointer_store_exception(arrayOop src, arrayOop dst, TRAPS);
+  static void throw_array_store_exception(arrayOop src, arrayOop dst, TRAPS);
+
 public:
   // Support for optimizing compilers to call the barrier set on slow path allocations
   // that did not enter a TLAB. Used for e.g. ReduceInitialCardMarks.
   // The allocation is safe to use iff it returns true. If not, the slow-path allocation
   // is redone until it succeeds. This can e.g. prevent allocations from the slow path
@@ -280,11 +284,11 @@
     static oop oop_atomic_xchg_in_heap_at(oop base, ptrdiff_t offset, oop new_value) {
       return Raw::oop_atomic_xchg_at(base, offset, new_value);
     }
 
     template <typename T>
-    static bool oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
+    static void oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
                                       arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                                       size_t length);
 
     // Off-heap oop accesses. These accessors get resolved when
     // IN_HEAP is not set (e.g. when using the NativeAccess API), it is
@@ -311,10 +315,15 @@
 
     // Clone barrier support
     static void clone_in_heap(oop src, oop dst, size_t size) {
       Raw::clone(src, dst, size);
     }
+
+    static void value_copy_in_heap(void* src, void* dst, InlineKlass* md) {
+      Raw::value_copy(src, dst, md);
+    }
+
   };
 };
 
 template<typename T>
 inline T* barrier_set_cast(BarrierSet* bs) {
diff a/src/hotspot/share/gc/shared/barrierSet.inline.hpp b/src/hotspot/share/gc/shared/barrierSet.inline.hpp
--- a/src/hotspot/share/gc/shared/barrierSet.inline.hpp
+++ b/src/hotspot/share/gc/shared/barrierSet.inline.hpp
@@ -29,33 +29,41 @@
 #include "oops/accessDecorators.hpp"
 #include "oops/arrayOop.hpp"
 #include "oops/compressedOops.inline.hpp"
 #include "oops/objArrayOop.inline.hpp"
 #include "oops/oop.hpp"
+#include "runtime/javaThread.hpp"
+#include "runtime/thread.hpp"
 
 template <DecoratorSet decorators, typename BarrierSetT>
 template <typename T>
-inline bool BarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
+inline void BarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
                                                                                       arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                                                                                       size_t length) {
   T* src = arrayOopDesc::obj_offset_to_raw(src_obj, src_offset_in_bytes, src_raw);
   T* dst = arrayOopDesc::obj_offset_to_raw(dst_obj, dst_offset_in_bytes, dst_raw);
 
-  if (!HasDecorator<decorators, ARRAYCOPY_CHECKCAST>::value) {
+  if ((!HasDecorator<decorators, ARRAYCOPY_CHECKCAST>::value) &&
+      (!HasDecorator<decorators, ARRAYCOPY_NOTNULL>::value)) {
     // Covariant, copy without checks
-    return Raw::oop_arraycopy(nullptr, 0, src, nullptr, 0, dst, length);
+    Raw::oop_arraycopy(nullptr, 0, src, nullptr, 0, dst, length);
+    return;
   }
 
   // Copy each element with checking casts
   Klass* const dst_klass = objArrayOop(dst_obj)->element_klass();
   for (const T* const end = src + length; src < end; src++, dst++) {
     const T elem = *src;
-    if (!oopDesc::is_instanceof_or_null(CompressedOops::decode(elem), dst_klass)) {
-      return false;
+    if (HasDecorator<decorators, ARRAYCOPY_NOTNULL>::value && CompressedOops::is_null(elem)) {
+      throw_array_null_pointer_store_exception(src_obj, dst_obj, JavaThread::current());
+      return;
+    }
+    if (HasDecorator<decorators, ARRAYCOPY_CHECKCAST>::value &&
+        (!oopDesc::is_instanceof_or_null(CompressedOops::decode(elem), dst_klass))) {
+      throw_array_store_exception(src_obj, dst_obj, JavaThread::current());
+      return;
     }
     *dst = elem;
   }
-
-  return true;
 }
 
 #endif // SHARE_GC_SHARED_BARRIERSET_INLINE_HPP
diff a/src/hotspot/share/gc/shared/barrierSetRuntime.cpp b/src/hotspot/share/gc/shared/barrierSetRuntime.cpp
--- /dev/null
+++ b/src/hotspot/share/gc/shared/barrierSetRuntime.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2019, 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 "gc/shared/barrierSetRuntime.hpp"
+#include "oops/access.inline.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
+#include "utilities/macros.hpp"
+
+JRT_LEAF(void, BarrierSetRuntime::value_copy(void* src, void* dst, InlineKlass* md))
+  assert(md->is_inline_klass(), "invariant");
+  HeapAccess<>::value_copy(src, dst, md);
+JRT_END
+
+JRT_LEAF(void, BarrierSetRuntime::value_copy_is_dest_uninitialized(void* src, void* dst, InlineKlass* md))
+  assert(md->is_inline_klass(), "invariant");
+  HeapAccess<IS_DEST_UNINITIALIZED>::value_copy(src, dst, md);
+JRT_END
diff a/src/hotspot/share/gc/shared/barrierSetRuntime.hpp b/src/hotspot/share/gc/shared/barrierSetRuntime.hpp
--- /dev/null
+++ b/src/hotspot/share/gc/shared/barrierSetRuntime.hpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019, 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_GC_SHARED_BARRIERSETRUNTIME_HPP
+#define SHARE_GC_SHARED_BARRIERSETRUNTIME_HPP
+
+#include "memory/allocation.hpp"
+#include "oops/oopsHierarchy.hpp"
+#include "oops/inlineKlass.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/macros.hpp"
+
+class oopDesc;
+class JavaThread;
+
+class BarrierSetRuntime: public AllStatic {
+public:
+  // Template interpreter...
+  static void value_copy(void* src, void* dst, InlineKlass* md);
+  static void value_copy_is_dest_uninitialized(void* src, void* dst, InlineKlass* md);
+};
+
+#endif // SHARE_GC_SHARED_BARRIERSETRUNTIME_HPP
diff a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp
--- a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp
+++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp
@@ -41,10 +41,14 @@
   return _kit->barrier_set_state();
 }
 
 PhaseGVN& C2ParseAccess::gvn() const { return _kit->gvn(); }
 
+Node* C2ParseAccess::control() const {
+  return _ctl == nullptr ? _kit->control() : _ctl;
+}
+
 bool C2Access::needs_cpu_membar() const {
   bool mismatched   = (_decorators & C2_MISMATCHED) != 0;
   bool is_unordered = (_decorators & MO_UNORDERED) != 0;
 
   bool anonymous = (_decorators & C2_UNSAFE_ACCESS) != 0;
@@ -148,11 +152,11 @@
 
   Node* load;
   if (access.is_parse_access()) {
     C2ParseAccess& parse_access = static_cast<C2ParseAccess&>(access);
     GraphKit* kit = parse_access.kit();
-    Node* control = control_dependent ? kit->control() : nullptr;
+    Node* control = control_dependent ? parse_access.control() : nullptr;
 
     if (immutable) {
       Compile* C = Compile::current();
       Node* mem = kit->immutable_memory();
       load = LoadNode::make(kit->gvn(), control, mem, adr,
@@ -782,9 +786,9 @@
   const TypeFunc* call_type = OptoRuntime::fast_arraycopy_Type();
 
   Node* call = phase->make_leaf_call(ctrl, mem, call_type, copyfunc_addr, copyfunc_name, raw_adr_type, payload_src, payload_dst, length XTOP);
   phase->transform_later(call);
 
-  phase->igvn().replace_node(ac, call);
+  phase->replace_node(ac, call);
 }
 
 #undef XTOP
diff a/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp
--- a/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp
+++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp
@@ -145,22 +145,26 @@
 
 // C2Access for parse time calls to the BarrierSetC2 backend.
 class C2ParseAccess: public C2Access {
 protected:
   GraphKit*         _kit;
+  Node* _ctl;
 
   void* barrier_set_state() const;
 
 public:
   C2ParseAccess(GraphKit* kit, DecoratorSet decorators,
-                BasicType type, Node* base, C2AccessValuePtr& addr) :
+                BasicType type, Node* base, C2AccessValuePtr& addr,
+                Node* ctl = nullptr) :
     C2Access(decorators, type, base, addr),
-    _kit(kit) {
+    _kit(kit),
+    _ctl(ctl) {
     fixup_decorators();
   }
 
   GraphKit* kit() const           { return _kit; }
+  Node* control() const;
 
   virtual PhaseGVN& gvn() const;
   virtual bool is_parse_access() const { return true; }
 };
 
@@ -234,11 +238,11 @@
   virtual Node* atomic_cmpxchg_bool_at(C2AtomicParseAccess& access, Node* expected_val,
                                        Node* new_val, const Type* val_type) const;
   virtual Node* atomic_xchg_at(C2AtomicParseAccess& access, Node* new_val, const Type* value_type) const;
   virtual Node* atomic_add_at(C2AtomicParseAccess& access, Node* new_val, const Type* value_type) const;
 
-  virtual void clone(GraphKit* kit, Node* src, Node* dst, Node* size, bool is_array) const;
+  virtual void clone(GraphKit* kit, Node* src_base, Node* dst_base, Node* size, bool is_array) const;
 
   virtual Node* obj_allocate(PhaseMacroExpand* macro, Node* mem, Node* toobig_false, Node* size_in_bytes,
                              Node*& i_o, Node*& needgc_ctrl,
                              Node*& fast_oop_ctrl, Node*& fast_oop_rawmem,
                              intx prefetch_lines) const;
@@ -262,11 +266,11 @@
   virtual Node* step_over_gc_barrier(Node* c) const { return c; }
 
   // Support for macro expanded GC barriers
   virtual void register_potential_barrier_node(Node* node) const { }
   virtual void unregister_potential_barrier_node(Node* node) const { }
-  virtual void eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const { }
+  virtual void eliminate_gc_barrier(PhaseIterGVN* igvn, Node* node) const { }
   virtual void eliminate_gc_barrier_data(Node* node) const { }
   virtual void enqueue_useful_gc_barrier(PhaseIterGVN* igvn, Node* node) const {}
   virtual void eliminate_useless_gc_barriers(Unique_Node_List &useful, Compile* C) const {}
 
   // Allow barrier sets to have shared state that is preserved across a compilation unit.
diff a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp
--- a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp
+++ b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp
@@ -156,27 +156,31 @@
 
 bool CardTableBarrierSetC2::is_gc_barrier_node(Node* node) const {
   return ModRefBarrierSetC2::is_gc_barrier_node(node) || node->Opcode() == Op_StoreCM;
 }
 
-void CardTableBarrierSetC2::eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const {
+void CardTableBarrierSetC2::eliminate_gc_barrier(PhaseIterGVN* igvn, Node* node) const {
   assert(node->Opcode() == Op_CastP2X, "ConvP2XNode required");
-  Node *shift = node->unique_out();
-  Node *addp = shift->unique_out();
-  for (DUIterator_Last jmin, j = addp->last_outs(jmin); j >= jmin; --j) {
-    Node *mem = addp->last_out(j);
-    if (UseCondCardMark && mem->is_Load()) {
-      assert(mem->Opcode() == Op_LoadB, "unexpected code shape");
-      // The load is checking if the card has been written so
-      // replace it with zero to fold the test.
-      macro->replace_node(mem, macro->intcon(0));
-      continue;
+  for (DUIterator_Last imin, i = node->last_outs(imin); i >= imin; --i) {
+    Node* shift = node->last_out(i);
+    for (DUIterator_Last jmin, j = shift->last_outs(jmin); j >= jmin; --j) {
+      Node* addp = shift->last_out(j);
+      for (DUIterator_Last kmin, k = addp->last_outs(kmin); k >= kmin; --k) {
+        Node* mem = addp->last_out(k);
+        if (UseCondCardMark && mem->is_Load()) {
+          assert(mem->Opcode() == Op_LoadB, "unexpected code shape");
+          // The load is checking if the card has been written so
+          // replace it with zero to fold the test.
+          igvn->replace_node(mem, igvn->intcon(0));
+          continue;
+        }
+        assert(mem->is_Store(), "store required");
+        igvn->replace_node(mem, mem->in(MemNode::Memory));
+      }
     }
-    assert(mem->is_Store(), "store required");
-    macro->replace_node(mem, mem->in(MemNode::Memory));
   }
 }
 
 bool CardTableBarrierSetC2::array_copy_requires_gc_barriers(bool tightly_coupled_alloc, BasicType type, bool is_clone, bool is_clone_instance, ArrayCopyPhase phase) const {
-  bool is_oop = is_reference_type(type);
+  bool is_oop = type == T_OBJECT || type == T_ARRAY;
   return is_oop && (!tightly_coupled_alloc || !use_ReduceInitialCardMarks());
 }
diff a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.hpp b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.hpp
--- a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.hpp
+++ b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.hpp
@@ -42,11 +42,11 @@
   Node* byte_map_base_node(GraphKit* kit) const;
 
 public:
   virtual void clone(GraphKit* kit, Node* src, Node* dst, Node* size, bool is_array) const;
   virtual bool is_gc_barrier_node(Node* node) const;
-  virtual void eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const;
+  virtual void eliminate_gc_barrier(PhaseIterGVN* igvn, Node* node) const;
   virtual bool array_copy_requires_gc_barriers(bool tightly_coupled_alloc, BasicType type, bool is_clone, bool is_clone_instance, ArrayCopyPhase phase) const;
 
   bool use_ReduceInitialCardMarks() const;
 };
 
diff a/src/hotspot/share/gc/shared/collectedHeap.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp
--- a/src/hotspot/share/gc/shared/collectedHeap.hpp
+++ b/src/hotspot/share/gc/shared/collectedHeap.hpp
@@ -282,10 +282,11 @@
 
   void set_gc_cause(GCCause::Cause v);
   GCCause::Cause gc_cause() { return _gc_cause; }
 
   oop obj_allocate(Klass* klass, size_t size, TRAPS);
+  oop obj_buffer_allocate(Klass* klass, size_t size, TRAPS); // doesn't clear memory
   virtual oop array_allocate(Klass* klass, size_t size, int length, bool do_zero, TRAPS);
   oop class_allocate(Klass* klass, size_t size, TRAPS);
 
   // Utilities for turning raw memory into filler objects.
   //
diff a/src/hotspot/share/gc/shared/collectedHeap.inline.hpp b/src/hotspot/share/gc/shared/collectedHeap.inline.hpp
--- a/src/hotspot/share/gc/shared/collectedHeap.inline.hpp
+++ b/src/hotspot/share/gc/shared/collectedHeap.inline.hpp
@@ -34,10 +34,15 @@
 inline oop CollectedHeap::obj_allocate(Klass* klass, size_t size, TRAPS) {
   ObjAllocator allocator(klass, size, THREAD);
   return allocator.allocate();
 }
 
+inline oop CollectedHeap::obj_buffer_allocate(Klass* klass, size_t size, TRAPS) {
+  ObjBufferAllocator allocator(klass, size, THREAD);
+  return allocator.allocate();
+}
+
 inline oop CollectedHeap::array_allocate(Klass* klass, size_t size, int length, bool do_zero, TRAPS) {
   ObjArrayAllocator allocator(klass, size, length, do_zero, THREAD);
   return allocator.allocate();
 }
 
diff a/src/hotspot/share/gc/shared/memAllocator.cpp b/src/hotspot/share/gc/shared/memAllocator.cpp
--- a/src/hotspot/share/gc/shared/memAllocator.cpp
+++ b/src/hotspot/share/gc/shared/memAllocator.cpp
@@ -385,12 +385,11 @@
   Copy::fill_to_aligned_words(mem + hs, _word_size - hs);
 }
 
 oop MemAllocator::finish(HeapWord* mem) const {
   assert(mem != nullptr, "null object pointer");
-  // May be bootstrapping
-  oopDesc::set_mark(mem, markWord::prototype());
+  oopDesc::set_mark(mem, Klass::default_prototype_header(_klass));
   // Need a release store to ensure array/class length, mark word, and
   // object zeroing are visible before setting the klass non-null, for
   // concurrent collectors.
   oopDesc::release_set_klass(mem, _klass);
   return cast_to_oop(mem);
@@ -399,10 +398,16 @@
 oop ObjAllocator::initialize(HeapWord* mem) const {
   mem_clear(mem);
   return finish(mem);
 }
 
+oop ObjBufferAllocator::initialize(HeapWord* mem) const {
+  oopDesc::set_klass_gap(mem, 0);
+  return finish(mem);
+}
+
+
 oop ObjArrayAllocator::initialize(HeapWord* mem) const {
   // Set array length before setting the _klass field because a
   // non-null klass field indicates that the object is parsable by
   // concurrent GC.
   assert(_length >= 0, "length should be non-negative");
diff a/src/hotspot/share/gc/shared/memAllocator.hpp b/src/hotspot/share/gc/shared/memAllocator.hpp
--- a/src/hotspot/share/gc/shared/memAllocator.hpp
+++ b/src/hotspot/share/gc/shared/memAllocator.hpp
@@ -91,10 +91,18 @@
     : MemAllocator(klass, word_size, thread) {}
 
   virtual oop initialize(HeapWord* mem) const;
 };
 
+class ObjBufferAllocator: public MemAllocator {
+public:
+  ObjBufferAllocator(Klass* klass, size_t word_size, Thread* thread = Thread::current())
+    : MemAllocator(klass, word_size, thread) {}
+  virtual oop initialize(HeapWord* mem) const;
+};
+
+
 class ObjArrayAllocator: public MemAllocator {
 protected:
   const int  _length;
   const bool _do_zero;
 
diff a/src/hotspot/share/gc/shared/modRefBarrierSet.hpp b/src/hotspot/share/gc/shared/modRefBarrierSet.hpp
--- a/src/hotspot/share/gc/shared/modRefBarrierSet.hpp
+++ b/src/hotspot/share/gc/shared/modRefBarrierSet.hpp
@@ -68,11 +68,10 @@
   // at the address "start", which may not necessarily be HeapWord-aligned
   inline void write_ref_array(HeapWord* start, size_t count);
 
  protected:
   virtual void write_ref_array_work(MemRegion mr) = 0;
-
  public:
   // The ModRef abstraction introduces pre and post barriers
   template <DecoratorSet decorators, typename BarrierSetT>
   class AccessBarrier: public BarrierSet::AccessBarrier<decorators, BarrierSetT> {
     typedef BarrierSet::AccessBarrier<decorators, BarrierSetT> Raw;
@@ -84,14 +83,18 @@
     static oop oop_atomic_cmpxchg_in_heap(T* addr, oop compare_value, oop new_value);
     template <typename T>
     static oop oop_atomic_xchg_in_heap(T* addr, oop new_value);
 
     template <typename T>
-    static bool oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
+    static void oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
                                       arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                                       size_t length);
-
+  private:
+    // Failing checkcast or check null during copy, still needs barrier
+    template <typename T>
+    static inline void oop_arraycopy_partial_barrier(BarrierSetT *bs, T* dst_raw, T* p);
+  public:
     static void clone_in_heap(oop src, oop dst, size_t size);
 
     static void oop_store_in_heap_at(oop base, ptrdiff_t offset, oop value) {
       oop_store_in_heap(AccessInternal::oop_field_addr<decorators>(base, offset), value);
     }
@@ -101,10 +104,12 @@
     }
 
     static oop oop_atomic_cmpxchg_in_heap_at(oop base, ptrdiff_t offset, oop compare_value, oop new_value) {
       return oop_atomic_cmpxchg_in_heap(AccessInternal::oop_field_addr<decorators>(base, offset), compare_value, new_value);
     }
+
+    static void value_copy_in_heap(void* src, void* dst, InlineKlass* md);
   };
 };
 
 template<>
 struct BarrierSet::GetName<ModRefBarrierSet> {
diff a/src/hotspot/share/gc/shared/modRefBarrierSet.inline.hpp b/src/hotspot/share/gc/shared/modRefBarrierSet.inline.hpp
--- a/src/hotspot/share/gc/shared/modRefBarrierSet.inline.hpp
+++ b/src/hotspot/share/gc/shared/modRefBarrierSet.inline.hpp
@@ -29,10 +29,11 @@
 
 #include "gc/shared/barrierSet.hpp"
 #include "oops/compressedOops.inline.hpp"
 #include "oops/objArrayOop.hpp"
 #include "oops/oop.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "runtime/thread.hpp"
 
 class Klass;
 
 // count is number of array elements being written
@@ -91,20 +92,32 @@
   return result;
 }
 
 template <DecoratorSet decorators, typename BarrierSetT>
 template <typename T>
-inline bool ModRefBarrierSet::AccessBarrier<decorators, BarrierSetT>::
+inline void ModRefBarrierSet::AccessBarrier<decorators, BarrierSetT>::
+oop_arraycopy_partial_barrier(BarrierSetT *bs, T* dst_raw, T* p) {
+  const size_t pd = pointer_delta(p, dst_raw, (size_t)heapOopSize);
+  // pointer delta is scaled to number of elements (length field in
+  // objArrayOop) which we assume is 32 bit.
+  assert(pd == (size_t)(int)pd, "length field overflow");
+  bs->write_ref_array((HeapWord*)dst_raw, pd);
+}
+
+template <DecoratorSet decorators, typename BarrierSetT>
+template <typename T>
+inline void ModRefBarrierSet::AccessBarrier<decorators, BarrierSetT>::
 oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
                       arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                       size_t length) {
   BarrierSetT *bs = barrier_set_cast<BarrierSetT>(barrier_set());
 
   src_raw = arrayOopDesc::obj_offset_to_raw(src_obj, src_offset_in_bytes, src_raw);
   dst_raw = arrayOopDesc::obj_offset_to_raw(dst_obj, dst_offset_in_bytes, dst_raw);
 
-  if (!HasDecorator<decorators, ARRAYCOPY_CHECKCAST>::value) {
+  if ((!HasDecorator<decorators, ARRAYCOPY_CHECKCAST>::value) &&
+      (!HasDecorator<decorators, ARRAYCOPY_NOTNULL>::value)) {
     // Optimized covariant case
     bs->write_ref_array_pre(dst_raw, length,
                             HasDecorator<decorators, IS_DEST_UNINITIALIZED>::value);
     Raw::oop_arraycopy(nullptr, 0, src_raw, nullptr, 0, dst_raw, length);
     bs->write_ref_array((HeapWord*)dst_raw, length);
@@ -113,32 +126,66 @@
     Klass* bound = objArrayOop(dst_obj)->element_klass();
     T* from = const_cast<T*>(src_raw);
     T* end = from + length;
     for (T* p = dst_raw; from < end; from++, p++) {
       T element = *from;
-      if (oopDesc::is_instanceof_or_null(CompressedOops::decode(element), bound)) {
-        bs->template write_ref_field_pre<decorators>(p);
-        *p = element;
-      } else {
-        // We must do a barrier to cover the partial copy.
-        const size_t pd = pointer_delta(p, dst_raw, (size_t)heapOopSize);
-        // pointer delta is scaled to number of elements (length field in
-        // objArrayOop) which we assume is 32 bit.
-        assert(pd == (size_t)(int)pd, "length field overflow");
-        bs->write_ref_array((HeapWord*)dst_raw, pd);
-        return false;
+      // Apply any required checks
+      if (HasDecorator<decorators, ARRAYCOPY_NOTNULL>::value && CompressedOops::is_null(element)) {
+        oop_arraycopy_partial_barrier(bs, dst_raw, p);
+        throw_array_null_pointer_store_exception(src_obj, dst_obj, JavaThread::current());
+        return;
+      }
+      if (HasDecorator<decorators, ARRAYCOPY_CHECKCAST>::value &&
+          (!oopDesc::is_instanceof_or_null(CompressedOops::decode(element), bound))) {
+        oop_arraycopy_partial_barrier(bs, dst_raw, p);
+        throw_array_store_exception(src_obj, dst_obj, JavaThread::current());
+        return;
       }
+      // write
+      bs->template write_ref_field_pre<decorators>(p);
+      *p = element;
     }
     bs->write_ref_array((HeapWord*)dst_raw, length);
   }
-  return true;
 }
 
 template <DecoratorSet decorators, typename BarrierSetT>
 inline void ModRefBarrierSet::AccessBarrier<decorators, BarrierSetT>::
 clone_in_heap(oop src, oop dst, size_t size) {
   Raw::clone(src, dst, size);
   BarrierSetT *bs = barrier_set_cast<BarrierSetT>(barrier_set());
   bs->invalidate(MemRegion((HeapWord*)(void*)dst, size));
 }
 
+template <DecoratorSet decorators, typename BarrierSetT>
+inline void ModRefBarrierSet::AccessBarrier<decorators, BarrierSetT>::
+value_copy_in_heap(void* src, void* dst, InlineKlass* md) {
+  if (HasDecorator<decorators, IS_DEST_UNINITIALIZED>::value || (!md->contains_oops())) {
+    Raw::value_copy(src, dst, md);
+  } else {
+    BarrierSetT* bs = barrier_set_cast<BarrierSetT>(BarrierSet::barrier_set());
+    // src/dst aren't oops, need offset to adjust oop map offset
+    const address dst_oop_addr_offset = ((address) dst) - md->first_field_offset();
+    typedef typename ValueOopType<decorators>::type OopType;
+
+    // Pre-barriers...
+    OopMapBlock* map = md->start_of_nonstatic_oop_maps();
+    OopMapBlock* const end = map + md->nonstatic_oop_map_count();
+    while (map != end) {
+      address doop_address = dst_oop_addr_offset + map->offset();
+      bs->write_ref_array_pre((OopType*) doop_address, map->count(), false);
+      map++;
+    }
+
+    Raw::value_copy(src, dst, md);
+
+    // Post-barriers...
+    map = md->start_of_nonstatic_oop_maps();
+    while (map != end) {
+      address doop_address = dst_oop_addr_offset + map->offset();
+      bs->write_ref_array((HeapWord*) doop_address, map->count());
+      map++;
+    }
+  }
+}
+
 #endif // SHARE_GC_SHARED_MODREFBARRIERSET_INLINE_HPP
diff a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp
--- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp
+++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp
@@ -826,11 +826,11 @@
     enum { _heap_stable = 1, _heap_unstable, PATH_LIMIT };
     Node* region = new RegionNode(PATH_LIMIT);
     Node* mem_phi = new PhiNode(region, Type::MEMORY, TypeRawPtr::BOTTOM);
 
     Node* thread = phase->transform_later(new ThreadLocalNode());
-    Node* offset = phase->igvn().MakeConX(in_bytes(ShenandoahThreadLocalData::gc_state_offset()));
+    Node* offset = phase->MakeConX(in_bytes(ShenandoahThreadLocalData::gc_state_offset()));
     Node* gc_state_addr = phase->transform_later(new AddPNode(phase->C->top(), thread, offset));
 
     uint gc_state_idx = Compile::AliasIdxRaw;
     const TypePtr* gc_state_adr_type = nullptr; // debug-mode-only argument
     debug_only(gc_state_adr_type = phase->C->get_adr_type(gc_state_idx));
@@ -878,11 +878,11 @@
                                  src, dest, length
                                  LP64_ONLY(COMMA phase->top()));
     call = phase->transform_later(call);
 
     // Hook up the whole thing into the graph
-    phase->igvn().replace_node(ac, call);
+    phase->replace_node(ac, call);
   } else {
     BarrierSetC2::clone_at_expansion(phase, ac);
   }
 }
 
@@ -904,13 +904,13 @@
   if (node->Opcode() == Op_ShenandoahLoadReferenceBarrier) {
     state()->remove_load_reference_barrier((ShenandoahLoadReferenceBarrierNode*) node);
   }
 }
 
-void ShenandoahBarrierSetC2::eliminate_gc_barrier(PhaseMacroExpand* macro, Node* n) const {
+void ShenandoahBarrierSetC2::eliminate_gc_barrier(PhaseIterGVN* igvn, Node* n) const {
   if (is_shenandoah_wb_pre_call(n)) {
-    shenandoah_eliminate_wb_pre(n, &macro->igvn());
+    shenandoah_eliminate_wb_pre(n, igvn);
   }
 }
 
 void ShenandoahBarrierSetC2::shenandoah_eliminate_wb_pre(Node* call, PhaseIterGVN* igvn) const {
   assert(UseShenandoahGC && is_shenandoah_wb_pre_call(call), "");
@@ -1039,11 +1039,11 @@
 }
 #endif
 
 Node* ShenandoahBarrierSetC2::ideal_node(PhaseGVN* phase, Node* n, bool can_reshape) const {
   if (is_shenandoah_wb_pre_call(n)) {
-    uint cnt = ShenandoahBarrierSetC2::write_ref_field_pre_entry_Type()->domain()->cnt();
+    uint cnt = ShenandoahBarrierSetC2::write_ref_field_pre_entry_Type()->domain_sig()->cnt();
     if (n->req() > cnt) {
       Node* addp = n->in(cnt);
       if (has_only_shenandoah_wb_pre_uses(addp)) {
         n->del_req(cnt);
         if (can_reshape) {
@@ -1125,11 +1125,11 @@
     case Op_CallLeaf:
     case Op_CallLeafNoFP: {
       assert (n->is_Call(), "");
       CallNode *call = n->as_Call();
       if (ShenandoahBarrierSetC2::is_shenandoah_wb_pre_call(call)) {
-        uint cnt = ShenandoahBarrierSetC2::write_ref_field_pre_entry_Type()->domain()->cnt();
+        uint cnt = ShenandoahBarrierSetC2::write_ref_field_pre_entry_Type()->domain_sig()->cnt();
         if (call->req() > cnt) {
           assert(call->req() == cnt + 1, "only one extra input");
           Node *addp = call->in(cnt);
           assert(!ShenandoahBarrierSetC2::has_only_shenandoah_wb_pre_uses(addp), "useless address computation?");
           call->del_req(cnt);
diff a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp
--- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp
+++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp
@@ -121,11 +121,11 @@
   virtual bool is_gc_specific_loop_opts_pass(LoopOptsMode mode) const { return mode == LoopOptsShenandoahExpand || mode == LoopOptsShenandoahPostExpand; }
 
   // Support for macro expanded GC barriers
   virtual void register_potential_barrier_node(Node* node) const;
   virtual void unregister_potential_barrier_node(Node* node) const;
-  virtual void eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const;
+  virtual void eliminate_gc_barrier(PhaseIterGVN* igvn, Node* node) const;
   virtual void enqueue_useful_gc_barrier(PhaseIterGVN* igvn, Node* node) const;
   virtual void eliminate_useless_gc_barriers(Unique_Node_List &useful, Compile* C) const;
 
   // Allow barrier sets to have shared state that is preserved across a compilation unit.
   // This could for example comprise macro nodes to be expanded during macro expansion.
diff a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp
--- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp
+++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp
@@ -465,11 +465,11 @@
           { -1,  ShenandoahNone},                 { -1,  ShenandoahNone},                 { -1,  ShenandoahNone} },
       };
 
       if (call->is_call_to_arraycopystub()) {
         Node* dest = nullptr;
-        const TypeTuple* args = n->as_Call()->_tf->domain();
+        const TypeTuple* args = n->as_Call()->_tf->domain_sig();
         for (uint i = TypeFunc::Parms, j = 0; i < args->cnt(); i++) {
           if (args->field_at(i)->isa_ptr()) {
             j++;
             if (j == 2) {
               dest = n->in(i);
@@ -586,11 +586,11 @@
       for (; i < others_len; i++) {
         if (others[i].opcode == n->Opcode()) {
           break;
         }
       }
-      uint stop = n->is_Call() ? n->as_Call()->tf()->domain()->cnt() : n->req();
+      uint stop = n->is_Call() ? n->as_Call()->tf()->domain_sig()->cnt() : n->req();
       if (i != others_len) {
         const uint inputs_len = sizeof(others[0].inputs) / sizeof(others[0].inputs[0]);
         for (uint j = 0; j < inputs_len; j++) {
           int pos = others[i].inputs[j].pos;
           if (pos == -1) {
@@ -806,22 +806,21 @@
           }
         }
       }
     } else {
       if (c->is_Call() && c->as_Call()->adr_type() != nullptr) {
-        CallProjections projs;
-        c->as_Call()->extract_projections(&projs, true, false);
-        if (projs.fallthrough_memproj != nullptr) {
-          if (projs.fallthrough_memproj->adr_type() == TypePtr::BOTTOM) {
-            if (projs.catchall_memproj == nullptr) {
-              mem = projs.fallthrough_memproj;
+        CallProjections* projs = c->as_Call()->extract_projections(true, false);
+        if (projs->fallthrough_memproj != nullptr) {
+          if (projs->fallthrough_memproj->adr_type() == TypePtr::BOTTOM) {
+            if (projs->catchall_memproj == nullptr) {
+              mem = projs->fallthrough_memproj;
             } else {
-              if (phase->is_dominator(projs.fallthrough_catchproj, ctrl)) {
-                mem = projs.fallthrough_memproj;
+              if (phase->is_dominator(projs->fallthrough_catchproj, ctrl)) {
+                mem = projs->fallthrough_memproj;
               } else {
-                assert(phase->is_dominator(projs.catchall_catchproj, ctrl), "one proj must dominate barrier");
-                mem = projs.catchall_memproj;
+                assert(phase->is_dominator(projs->catchall_catchproj, ctrl), "one proj must dominate barrier");
+                mem = projs->catchall_memproj;
               }
             }
           }
         } else {
           Node* proj = c->as_Call()->proj_out(TypeFunc::Memory);
@@ -1077,11 +1076,11 @@
       }
     }
   }
 }
 
-static Node* create_phis_on_call_return(Node* ctrl, Node* c, Node* n, Node* n_clone, const CallProjections& projs, PhaseIdealLoop* phase) {
+static Node* create_phis_on_call_return(Node* ctrl, Node* c, Node* n, Node* n_clone, const CallProjections* projs, PhaseIdealLoop* phase) {
   Node* region = nullptr;
   while (c != ctrl) {
     if (c->is_Region()) {
       region = c;
     }
@@ -1089,13 +1088,13 @@
   }
   assert(region != nullptr, "");
   Node* phi = new PhiNode(region, n->bottom_type());
   for (uint j = 1; j < region->req(); j++) {
     Node* in = region->in(j);
-    if (phase->is_dominator(projs.fallthrough_catchproj, in)) {
+    if (phase->is_dominator(projs->fallthrough_catchproj, in)) {
       phi->init_req(j, n);
-    } else if (phase->is_dominator(projs.catchall_catchproj, in)) {
+    } else if (phase->is_dominator(projs->catchall_catchproj, in)) {
       phi->init_req(j, n_clone);
     } else {
       phi->init_req(j, create_phis_on_call_return(ctrl, in, n, n_clone, projs, phase));
     }
   }
@@ -1207,19 +1206,17 @@
             stack.pop();
           }
         } while(stack.size() > 0);
         continue;
       }
-      CallProjections projs;
-      call->extract_projections(&projs, false, false);
-
+      CallProjections* projs = call->extract_projections(false, false);
 #ifdef ASSERT
       VectorSet cloned;
 #endif
       Node* lrb_clone = lrb->clone();
-      phase->register_new_node(lrb_clone, projs.catchall_catchproj);
-      phase->set_ctrl(lrb, projs.fallthrough_catchproj);
+      phase->register_new_node(lrb_clone, projs->catchall_catchproj);
+      phase->set_ctrl(lrb, projs->fallthrough_catchproj);
 
       stack.push(lrb, 0);
       clones.push(lrb_clone);
 
       do {
@@ -1237,42 +1234,42 @@
         uint idx = stack.index();
         Node* n_clone = clones.at(clones.size()-1);
         if (idx < n->outcnt()) {
           Node* u = n->raw_out(idx);
           Node* c = phase->ctrl_or_self(u);
-          if (phase->is_dominator(call, c) && phase->is_dominator(c, projs.fallthrough_proj)) {
+          if (phase->is_dominator(call, c) && phase->is_dominator(c, projs->fallthrough_proj)) {
             stack.set_index(idx+1);
             assert(!u->is_CFG(), "");
             stack.push(u, 0);
             assert(!cloned.test_set(u->_idx), "only one clone");
             Node* u_clone = u->clone();
             int nb = u_clone->replace_edge(n, n_clone, &phase->igvn());
             assert(nb > 0, "should have replaced some uses");
-            phase->register_new_node(u_clone, projs.catchall_catchproj);
+            phase->register_new_node(u_clone, projs->catchall_catchproj);
             clones.push(u_clone);
-            phase->set_ctrl(u, projs.fallthrough_catchproj);
+            phase->set_ctrl(u, projs->fallthrough_catchproj);
           } else {
             bool replaced = false;
             if (u->is_Phi()) {
               for (uint k = 1; k < u->req(); k++) {
                 if (u->in(k) == n) {
-                  if (phase->is_dominator(projs.catchall_catchproj, u->in(0)->in(k))) {
+                  if (phase->is_dominator(projs->catchall_catchproj, u->in(0)->in(k))) {
                     phase->igvn().replace_input_of(u, k, n_clone);
                     replaced = true;
-                  } else if (!phase->is_dominator(projs.fallthrough_catchproj, u->in(0)->in(k))) {
+                  } else if (!phase->is_dominator(projs->fallthrough_catchproj, u->in(0)->in(k))) {
                     phase->igvn().replace_input_of(u, k, create_phis_on_call_return(ctrl, u->in(0)->in(k), n, n_clone, projs, phase));
                     replaced = true;
                   }
                 }
               }
             } else {
-              if (phase->is_dominator(projs.catchall_catchproj, c)) {
+              if (phase->is_dominator(projs->catchall_catchproj, c)) {
                 phase->igvn().rehash_node_delayed(u);
                 int nb = u->replace_edge(n, n_clone, &phase->igvn());
                 assert(nb > 0, "should have replaced some uses");
                 replaced = true;
-              } else if (!phase->is_dominator(projs.fallthrough_catchproj, c)) {
+              } else if (!phase->is_dominator(projs->fallthrough_catchproj, c)) {
                 if (u->is_If()) {
                   // Can't break If/Bool/Cmp chain
                   assert(n->is_Bool(), "unexpected If shape");
                   assert(stack.node_at(stack.size()-2)->is_Cmp(), "unexpected If shape");
                   assert(n_clone->is_Bool(), "unexpected clone");
@@ -2339,18 +2336,17 @@
 Node* MemoryGraphFixer::get_ctrl(Node* n) const {
   Node* c = _phase->get_ctrl(n);
   if (n->is_Proj() && n->in(0) != nullptr && n->in(0)->is_Call()) {
     assert(c == n->in(0), "");
     CallNode* call = c->as_Call();
-    CallProjections projs;
-    call->extract_projections(&projs, true, false);
-    if (projs.catchall_memproj != nullptr) {
-      if (projs.fallthrough_memproj == n) {
-        c = projs.fallthrough_catchproj;
+    CallProjections* projs = call->extract_projections(true, false);
+    if (projs->catchall_memproj != nullptr) {
+      if (projs->fallthrough_memproj == n) {
+        c = projs->fallthrough_catchproj;
       } else {
-        assert(projs.catchall_memproj == n, "");
-        c = projs.catchall_catchproj;
+        assert(projs->catchall_memproj == n, "");
+        c = projs->catchall_catchproj;
       }
     }
   }
   return c;
 }
diff a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp
--- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp
@@ -157,11 +157,11 @@
     template <typename T>
     static oop oop_atomic_xchg_in_heap(T* addr, oop new_value);
     static oop oop_atomic_xchg_in_heap_at(oop base, ptrdiff_t offset, oop new_value);
 
     template <typename T>
-    static bool oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
+    static void oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
                                       arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                                       size_t length);
 
     // Clone barrier support
     static void clone_in_heap(oop src, oop dst, size_t size);
diff a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp
--- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp
@@ -327,18 +327,18 @@
   Raw::clone(src, dst, size);
 }
 
 template <DecoratorSet decorators, typename BarrierSetT>
 template <typename T>
-bool ShenandoahBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
+void ShenandoahBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
                                                                                          arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                                                                                          size_t length) {
   ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set();
   bs->arraycopy_barrier(arrayOopDesc::obj_offset_to_raw(src_obj, src_offset_in_bytes, src_raw),
                         arrayOopDesc::obj_offset_to_raw(dst_obj, dst_offset_in_bytes, dst_raw),
                         length);
-  return Raw::oop_arraycopy_in_heap(src_obj, src_offset_in_bytes, src_raw, dst_obj, dst_offset_in_bytes, dst_raw, length);
+  Raw::oop_arraycopy_in_heap(src_obj, src_offset_in_bytes, src_raw, dst_obj, dst_offset_in_bytes, dst_raw, length);
 }
 
 template <class T, bool HAS_FWD, bool EVAC, bool ENQUEUE>
 void ShenandoahBarrierSet::arraycopy_work(T* src, size_t count) {
   assert(HAS_FWD == _heap->has_forwarded_objects(), "Forwarded object status is sane");
diff a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp
--- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp
@@ -54,11 +54,11 @@
     // the entry barrier, which acquires the per nmethod lock.
     nm->unlink_from_method();
 
     // We can end up calling nmethods that are unloading
     // since we clear compiled ICs lazily. Returning false
-    // will re-resovle the call and update the compiled IC.
+    // will re-resolve the call and update the compiled IC.
     return false;
   }
 
   // Heal oops
   ShenandoahNMethod::heal_nmethod(nm);
diff a/src/hotspot/share/gc/x/c2/xBarrierSetC2.cpp b/src/hotspot/share/gc/x/c2/xBarrierSetC2.cpp
--- a/src/hotspot/share/gc/x/c2/xBarrierSetC2.cpp
+++ b/src/hotspot/share/gc/x/c2/xBarrierSetC2.cpp
@@ -272,11 +272,11 @@
   Node* const src = ac->in(ArrayCopyNode::Src);
   const TypeAryPtr* ary_ptr = src->get_ptr_type()->isa_aryptr();
 
   if (ac->is_clone_array() && ary_ptr != nullptr) {
     BasicType bt = ary_ptr->elem()->array_element_basic_type();
-    if (is_reference_type(bt)) {
+    if (is_reference_type(bt) && !ary_ptr->is_flat()) {
       // Clone object array
       bt = T_OBJECT;
     } else {
       // Clone primitive array
       bt = T_LONG;
diff a/src/hotspot/share/gc/x/xBarrierSet.hpp b/src/hotspot/share/gc/x/xBarrierSet.hpp
--- a/src/hotspot/share/gc/x/xBarrierSet.hpp
+++ b/src/hotspot/share/gc/x/xBarrierSet.hpp
@@ -76,16 +76,18 @@
     template <typename T>
     static oop oop_atomic_xchg_in_heap(T* addr, oop new_value);
     static oop oop_atomic_xchg_in_heap_at(oop base, ptrdiff_t offset, oop new_value);
 
     template <typename T>
-    static bool oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
+    static void oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
                                       arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                                       size_t length);
 
     static void clone_in_heap(oop src, oop dst, size_t size);
 
+    static void value_copy_in_heap(void* src, void* dst, InlineKlass* md);
+
     //
     // Not in heap
     //
     template <typename T>
     static oop oop_load_not_in_heap(T* addr);
diff a/src/hotspot/share/gc/x/xBarrierSet.inline.hpp b/src/hotspot/share/gc/x/xBarrierSet.inline.hpp
--- a/src/hotspot/share/gc/x/xBarrierSet.inline.hpp
+++ b/src/hotspot/share/gc/x/xBarrierSet.inline.hpp
@@ -26,10 +26,11 @@
 
 #include "gc/x/xBarrierSet.hpp"
 
 #include "gc/shared/accessBarrierSupport.inline.hpp"
 #include "gc/x/xBarrier.inline.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "utilities/debug.hpp"
 
 template <DecoratorSet decorators, typename BarrierSetT>
 template <DecoratorSet expected>
 inline void XBarrierSet::AccessBarrier<decorators, BarrierSetT>::verify_decorators_present() {
@@ -173,44 +174,67 @@
   return XBarrier::load_barrier_on_oop(o);
 }
 
 template <DecoratorSet decorators, typename BarrierSetT>
 template <typename T>
-inline bool XBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
+inline void XBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
                                                                                        arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                                                                                        size_t length) {
   T* src = arrayOopDesc::obj_offset_to_raw(src_obj, src_offset_in_bytes, src_raw);
   T* dst = arrayOopDesc::obj_offset_to_raw(dst_obj, dst_offset_in_bytes, dst_raw);
 
-  if (!HasDecorator<decorators, ARRAYCOPY_CHECKCAST>::value) {
+  if ((!HasDecorator<decorators, ARRAYCOPY_CHECKCAST>::value) &&
+      (!HasDecorator<decorators, ARRAYCOPY_NOTNULL>::value)) {
     // No check cast, bulk barrier and bulk copy
     XBarrier::load_barrier_on_oop_array(src, length);
-    return Raw::oop_arraycopy_in_heap(nullptr, 0, src, nullptr, 0, dst, length);
+    Raw::oop_arraycopy_in_heap(nullptr, 0, src, nullptr, 0, dst, length);
+    return;
   }
 
   // Check cast and copy each elements
   Klass* const dst_klass = objArrayOop(dst_obj)->element_klass();
   for (const T* const end = src + length; src < end; src++, dst++) {
     const oop elem = XBarrier::load_barrier_on_oop_field(src);
-    if (!oopDesc::is_instanceof_or_null(elem, dst_klass)) {
+    if (HasDecorator<decorators, ARRAYCOPY_NOTNULL>::value && elem == nullptr) {
+      throw_array_null_pointer_store_exception(src_obj, dst_obj, JavaThread::current());
+      return;
+    }
+    if (HasDecorator<decorators, ARRAYCOPY_CHECKCAST>::value &&
+        (!oopDesc::is_instanceof_or_null(elem, dst_klass))) {
       // Check cast failed
-      return false;
+      throw_array_store_exception(src_obj, dst_obj, JavaThread::current());
+      return;
     }
 
     // Cast is safe, since we know it's never a narrowOop
     *(oop*)dst = elem;
   }
-
-  return true;
 }
 
 template <DecoratorSet decorators, typename BarrierSetT>
 inline void XBarrierSet::AccessBarrier<decorators, BarrierSetT>::clone_in_heap(oop src, oop dst, size_t size) {
   XBarrier::load_barrier_on_oop_fields(src);
   Raw::clone_in_heap(src, dst, size);
 }
 
+template <DecoratorSet decorators, typename BarrierSetT>
+inline void XBarrierSet::AccessBarrier<decorators, BarrierSetT>::value_copy_in_heap(void* src, void* dst, InlineKlass* md) {
+  if (md->contains_oops()) {
+    // src/dst aren't oops, need offset to adjust oop map offset
+    const address src_oop_addr_offset = ((address) src) - md->first_field_offset();
+
+    OopMapBlock* map = md->start_of_nonstatic_oop_maps();
+    OopMapBlock* const end = map + md->nonstatic_oop_map_count();
+    while (map != end) {
+      address soop_address = src_oop_addr_offset + map->offset();
+      XBarrier::load_barrier_on_oop_array((oop*) soop_address, map->count());
+      map++;
+    }
+  }
+  Raw::value_copy_in_heap(src, dst, md);
+}
+
 //
 // Not in heap
 //
 template <DecoratorSet decorators, typename BarrierSetT>
 template <typename T>
diff a/src/hotspot/share/gc/x/xObjArrayAllocator.cpp b/src/hotspot/share/gc/x/xObjArrayAllocator.cpp
--- a/src/hotspot/share/gc/x/xObjArrayAllocator.cpp
+++ b/src/hotspot/share/gc/x/xObjArrayAllocator.cpp
@@ -61,11 +61,11 @@
   // Segmented clearing
 
   // The array is going to be exposed before it has been completely
   // cleared, therefore we can't expose the header at the end of this
   // function. Instead explicitly initialize it according to our needs.
-  arrayOopDesc::set_mark(mem, markWord::prototype());
+  arrayOopDesc::set_mark(mem, Klass::default_prototype_header(_klass));
   arrayOopDesc::release_set_klass(mem, _klass);
   assert(_length >= 0, "length should be non-negative");
   arrayOopDesc::set_length(mem, _length);
 
   // Keep the array alive across safepoints through an invisible
diff a/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp b/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp
--- a/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp
+++ b/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp
@@ -471,11 +471,11 @@
   Node* const src = ac->in(ArrayCopyNode::Src);
   const TypeAryPtr* const ary_ptr = src->get_ptr_type()->isa_aryptr();
 
   if (ac->is_clone_array() && ary_ptr != nullptr) {
     BasicType bt = ary_ptr->elem()->array_element_basic_type();
-    if (is_reference_type(bt)) {
+    if (is_reference_type(bt) && !ary_ptr->is_flat()) {
       // Clone object array
       bt = T_OBJECT;
     } else {
       // Clone primitive array
       bt = T_LONG;
@@ -497,11 +497,11 @@
       assert(src_offset == dest_offset, "should be equal");
       const jlong offset = src_offset->get_long();
       if (offset != arrayOopDesc::base_offset_in_bytes(T_OBJECT)) {
         assert(!UseCompressedClassPointers, "should only happen without compressed class pointers");
         assert((arrayOopDesc::base_offset_in_bytes(T_OBJECT) - offset) == BytesPerLong, "unexpected offset");
-        length = phase->transform_later(new SubLNode(length, phase->longcon(1))); // Size is in longs
+        length = phase->transform_later(new SubXNode(length, phase->longcon(1))); // Size is in longs
         src_offset = phase->longcon(arrayOopDesc::base_offset_in_bytes(T_OBJECT));
         dest_offset = src_offset;
       }
     }
     Node* const payload_src = phase->basic_plus_adr(src, src_offset);
@@ -542,11 +542,11 @@
                                            src,
                                            dst,
                                            full_size,
                                            phase->top());
   phase->transform_later(call);
-  phase->igvn().replace_node(ac, call);
+  phase->replace_node(ac, call);
 }
 
 #undef XTOP
 
 // == Dominating barrier elision ==
@@ -957,11 +957,11 @@
       }
     }
   }
 }
 
-void ZBarrierSetC2::eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const {
+void ZBarrierSetC2::eliminate_gc_barrier(PhaseIterGVN* igvn, Node* node) const {
   eliminate_gc_barrier_data(node);
 }
 
 void ZBarrierSetC2::eliminate_gc_barrier_data(Node* node) const {
   if (node->is_LoadStore()) {
diff a/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp b/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp
--- a/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp
+++ b/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp
@@ -139,11 +139,11 @@
                                   ArrayCopyNode* ac) const;
 
   virtual void late_barrier_analysis() const;
   virtual int estimate_stub_size() const;
   virtual void emit_stubs(CodeBuffer& cb) const;
-  virtual void eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const;
+  virtual void eliminate_gc_barrier(PhaseIterGVN* igvn, Node* node) const;
   virtual void eliminate_gc_barrier_data(Node* node) const;
 
 #ifndef PRODUCT
   virtual void dump_barrier_data(const MachNode* mach, outputStream* st) const;
 #endif
diff a/src/hotspot/share/gc/z/zBarrier.hpp b/src/hotspot/share/gc/z/zBarrier.hpp
--- a/src/hotspot/share/gc/z/zBarrier.hpp
+++ b/src/hotspot/share/gc/z/zBarrier.hpp
@@ -25,10 +25,11 @@
 #define SHARE_GC_Z_ZBARRIER_HPP
 
 #include "gc/z/zAddress.hpp"
 #include "memory/allStatic.hpp"
 #include "memory/iterator.hpp"
+#include "oops/inlineKlass.hpp"
 
 // == Shift based load barrier ==
 //
 // The load barriers of ZGC check if a loaded value is safe to expose or not, and
 // then shifts the pointer to remove metadata bits, such that it points to mapped
diff a/src/hotspot/share/gc/z/zBarrierSet.hpp b/src/hotspot/share/gc/z/zBarrierSet.hpp
--- a/src/hotspot/share/gc/z/zBarrierSet.hpp
+++ b/src/hotspot/share/gc/z/zBarrierSet.hpp
@@ -48,10 +48,16 @@
 
   virtual void on_slowpath_allocation_exit(JavaThread* thread, oop new_obj);
 
   virtual void print_on(outputStream* st) const;
 
+  enum OopCopyCheckStatus {
+    oop_copy_check_ok = 0,         // oop array copy sucessful
+    oop_copy_check_class_cast = 1, // oop array copy failed subtype check (ARRAYCOPY_CHECKCAST)
+    oop_copy_check_null = 2        // oop array copy failed null check (ARRAYCOPY_NOTNULL)
+  };
+
   template <DecoratorSet decorators, typename BarrierSetT = ZBarrierSet>
   class AccessBarrier : public BarrierSet::AccessBarrier<decorators, BarrierSetT> {
   private:
     typedef BarrierSet::AccessBarrier<decorators, BarrierSetT> Raw;
 
@@ -81,15 +87,15 @@
     static void no_keep_alive_store_barrier_heap(narrowOop* p)  { unsupported(); }
     static void store_barrier_native_with_healing(narrowOop* p)  { unsupported(); }
     static void store_barrier_native_without_healing(narrowOop* p)  { unsupported(); }
 
     static zaddress oop_copy_one_barriers(zpointer* dst, zpointer* src);
-    static bool oop_copy_one_check_cast(zpointer* dst, zpointer* src, Klass* dst_klass);
-    static void oop_copy_one(zpointer* dst, zpointer* src);
+    static OopCopyCheckStatus oop_copy_one_check_cast(zpointer* dst, zpointer* src, Klass* dst_klass);
+    static OopCopyCheckStatus oop_copy_one(zpointer* dst, zpointer* src);
 
-    static bool oop_arraycopy_in_heap_check_cast(zpointer* dst, zpointer* src, size_t length, Klass* dst_klass);
-    static bool oop_arraycopy_in_heap_no_check_cast(zpointer* dst, zpointer* src, size_t length);
+    static OopCopyCheckStatus oop_arraycopy_in_heap_check_cast(zpointer* dst, zpointer* src, size_t length, Klass* dst_klass);
+    static OopCopyCheckStatus oop_arraycopy_in_heap_no_check_cast(zpointer* dst, zpointer* src, size_t length);
 
   public:
     //
     // In heap
     //
@@ -117,26 +123,28 @@
     static oop oop_atomic_xchg_in_heap(zpointer* p, oop new_value);
     static oop oop_atomic_xchg_in_heap(oop* p, oop new_value)       { return oop_atomic_xchg_in_heap((zpointer*)p, new_value); }
     static oop oop_atomic_xchg_in_heap(narrowOop* p, oop new_value) { unsupported(); return nullptr; }
     static oop oop_atomic_xchg_in_heap_at(oop base, ptrdiff_t offset, oop new_value);
 
-    static bool oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, zpointer* src_raw,
+    static void oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, zpointer* src_raw,
                                       arrayOop dst_obj, size_t dst_offset_in_bytes, zpointer* dst_raw,
                                       size_t length);
-    static bool oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, oop* src_raw,
+    static void oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, oop* src_raw,
                                       arrayOop dst_obj, size_t dst_offset_in_bytes, oop* dst_raw,
                                       size_t length) {
-      return oop_arraycopy_in_heap(src_obj, src_offset_in_bytes, (zpointer*)src_raw,
-                                   dst_obj, dst_offset_in_bytes, (zpointer*)dst_raw,
-                                   length);
+      oop_arraycopy_in_heap(src_obj, src_offset_in_bytes, (zpointer*)src_raw,
+                            dst_obj, dst_offset_in_bytes, (zpointer*)dst_raw,
+                            length);
     }
-    static bool oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, narrowOop* src_raw,
+    static void oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, narrowOop* src_raw,
                                       arrayOop dst_obj, size_t dst_offset_in_bytes, narrowOop* dst_raw,
-                                      size_t length) { unsupported(); return false; }
+                                      size_t length) { unsupported(); }
 
     static void clone_in_heap(oop src, oop dst, size_t size);
 
+    static void value_copy_in_heap(void* src, void* dst, InlineKlass* md);
+
     //
     // Not in heap
     //
     static oop oop_load_not_in_heap(zpointer* p);
     static oop oop_load_not_in_heap(oop* p);
diff a/src/hotspot/share/gc/z/zBarrierSet.inline.hpp b/src/hotspot/share/gc/z/zBarrierSet.inline.hpp
--- a/src/hotspot/share/gc/z/zBarrierSet.inline.hpp
+++ b/src/hotspot/share/gc/z/zBarrierSet.inline.hpp
@@ -30,10 +30,11 @@
 #include "gc/z/zAddress.inline.hpp"
 #include "gc/z/zBarrier.inline.hpp"
 #include "gc/z/zIterator.inline.hpp"
 #include "gc/z/zNMethod.hpp"
 #include "memory/iterator.inline.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "utilities/debug.hpp"
 
 template <DecoratorSet decorators, typename BarrierSetT>
 template <DecoratorSet expected>
 inline void ZBarrierSet::AccessBarrier<decorators, BarrierSetT>::verify_decorators_present() {
@@ -327,82 +328,103 @@
 
   return ZBarrier::load_barrier_on_oop_field(src);
 }
 
 template <DecoratorSet decorators, typename BarrierSetT>
-inline void ZBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_copy_one(zpointer* dst, zpointer* src) {
+inline ZBarrierSet::OopCopyCheckStatus ZBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_copy_one(zpointer* dst, zpointer* src) {
   const zaddress obj = oop_copy_one_barriers(dst, src);
 
+  if (HasDecorator<decorators, ARRAYCOPY_NOTNULL>::value && is_null(obj)) {
+    return oop_copy_check_null;
+  }
+
   Atomic::store(dst, ZAddress::store_good(obj));
+  return oop_copy_check_ok;
 }
 
 template <DecoratorSet decorators, typename BarrierSetT>
-inline bool ZBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_copy_one_check_cast(zpointer* dst, zpointer* src, Klass* dst_klass) {
+inline ZBarrierSet::OopCopyCheckStatus ZBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_copy_one_check_cast(zpointer* dst, zpointer* src, Klass* dst_klass) {
   const zaddress obj = oop_copy_one_barriers(dst, src);
+  const bool null_check = HasDecorator<decorators, ARRAYCOPY_NOTNULL>::value;
 
-  if (!oopDesc::is_instanceof_or_null(to_oop(obj), dst_klass)) {
+  if (null_check && is_null(obj)) {
+    return oop_copy_check_null;
+  }
+  else if (!oopDesc::is_instanceof_or_null(to_oop(obj), dst_klass)) {
     // Check cast failed
-    return false;
+    return oop_copy_check_class_cast;
   }
 
   Atomic::store(dst, ZAddress::store_good(obj));
 
-  return true;
+  return oop_copy_check_ok;
 }
 
 
 template <DecoratorSet decorators, typename BarrierSetT>
-inline bool ZBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_arraycopy_in_heap_check_cast(zpointer* dst, zpointer* src, size_t length, Klass* dst_klass) {
+inline ZBarrierSet::OopCopyCheckStatus ZBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_arraycopy_in_heap_check_cast(zpointer* dst, zpointer* src, size_t length, Klass* dst_klass) {
   // Check cast and copy each elements
-  for (const zpointer* const end = src + length; src < end; src++, dst++) {
-    if (!oop_copy_one_check_cast(dst, src, dst_klass)) {
-      // Check cast failed
-      return false;
-    }
+  OopCopyCheckStatus check_status = oop_copy_check_ok;
+  for (const zpointer* const end = src + length; (check_status == oop_copy_check_ok) && (src < end); src++, dst++) {
+    check_status = oop_copy_one_check_cast(dst, src, dst_klass);
   }
-
-  return true;
+  return check_status;
 }
 
 template <DecoratorSet decorators, typename BarrierSetT>
-inline bool ZBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_arraycopy_in_heap_no_check_cast(zpointer* dst, zpointer* src, size_t length) {
+inline ZBarrierSet::OopCopyCheckStatus ZBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_arraycopy_in_heap_no_check_cast(zpointer* dst, zpointer* src, size_t length) {
   const bool is_disjoint = HasDecorator<decorators, ARRAYCOPY_DISJOINT>::value;
-
+  OopCopyCheckStatus check_status = oop_copy_check_ok;
   if (is_disjoint || src > dst) {
-    for (const zpointer* const end = src + length; src < end; src++, dst++) {
-      oop_copy_one(dst, src);
+    for (const zpointer* const end = src + length; (check_status == oop_copy_check_ok) && (src < end); src++, dst++) {
+      check_status = oop_copy_one(dst, src);
     }
-    return true;
+    return check_status;
   }
 
   if (src < dst) {
     const zpointer* const end = src;
     src += length - 1;
     dst += length - 1;
-    for ( ; src >= end; src--, dst--) {
-      oop_copy_one(dst, src);
+    for ( ; (check_status == oop_copy_check_ok) && (src >= end); src--, dst--) {
+      check_status = oop_copy_one(dst, src);
     }
-    return true;
+    return check_status;
   }
 
   // src and dst are the same; nothing to do
-  return true;
+  return check_status;
 }
 
 template <DecoratorSet decorators, typename BarrierSetT>
-inline bool ZBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, zpointer* src_raw,
+inline void ZBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, zpointer* src_raw,
                                                                                        arrayOop dst_obj, size_t dst_offset_in_bytes, zpointer* dst_raw,
                                                                                        size_t length) {
   zpointer* const src = arrayOopDesc::obj_offset_to_raw(src_obj, src_offset_in_bytes, src_raw);
   zpointer* const dst = arrayOopDesc::obj_offset_to_raw(dst_obj, dst_offset_in_bytes, dst_raw);
+  OopCopyCheckStatus check_status;
 
   if (HasDecorator<decorators, ARRAYCOPY_CHECKCAST>::value) {
     Klass* const dst_klass = objArrayOop(dst_obj)->element_klass();
-    return oop_arraycopy_in_heap_check_cast(dst, src, length, dst_klass);
+    check_status = oop_arraycopy_in_heap_check_cast(dst, src, length, dst_klass);
+  } else {
+    check_status = oop_arraycopy_in_heap_no_check_cast(dst, src, length);
   }
 
-  return oop_arraycopy_in_heap_no_check_cast(dst, src, length);
+  switch (check_status) {
+  case oop_copy_check_ok:
+    return;
+  case oop_copy_check_class_cast:
+    throw_array_store_exception(src_obj, dst_obj, JavaThread::current());
+    break;
+  case oop_copy_check_null:
+    throw_array_null_pointer_store_exception(src_obj, dst_obj, JavaThread::current());
+    break;
+  default:
+    ShouldNotReachHere();
+    return;
+  }
 }
 
 class ZColorStoreGoodOopClosure : public BasicOopIterateClosure {
 public:
   virtual void do_oop(oop* p_) {
@@ -455,10 +477,30 @@
   // Color store good before handing out
   ZColorStoreGoodOopClosure cl_sg;
   ZIterator::oop_iterate(dst, &cl_sg);
 }
 
+template <DecoratorSet decorators, typename BarrierSetT>
+inline void ZBarrierSet::AccessBarrier<decorators, BarrierSetT>::value_copy_in_heap(void* src, void* dst, InlineKlass* md) {
+  if (md->contains_oops()) {
+    // src/dst aren't oops, need offset to adjust oop map offset
+    const address src_oop_addr_offset = ((address) src) - md->first_field_offset();
+
+    OopMapBlock* map = md->start_of_nonstatic_oop_maps();
+    const OopMapBlock* const end = map + md->nonstatic_oop_map_count();
+    while (map != end) {
+      const address soop_address = src_oop_addr_offset + map->offset();
+      zpointer *p = (zpointer*) soop_address;
+      for (const zpointer* const end = p + map->count(); p < end; p++) {
+        ZBarrier::load_barrier_on_oop_field(p);
+      }
+      map++;
+    }
+  }
+  Raw::value_copy_in_heap(src, dst, md);
+}
+
 //
 // Not in heap
 //
 template <DecoratorSet decorators, typename BarrierSetT>
 inline oop ZBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_load_not_in_heap(zpointer* p) {
diff a/src/hotspot/share/gc/z/zObjArrayAllocator.cpp b/src/hotspot/share/gc/z/zObjArrayAllocator.cpp
--- a/src/hotspot/share/gc/z/zObjArrayAllocator.cpp
+++ b/src/hotspot/share/gc/z/zObjArrayAllocator.cpp
@@ -26,10 +26,11 @@
 #include "gc/z/zObjArrayAllocator.hpp"
 #include "gc/z/zUtils.inline.hpp"
 #include "oops/arrayKlass.hpp"
 #include "runtime/interfaceSupport.inline.hpp"
 #include "utilities/debug.hpp"
+#include "utilities/globalDefinitions.hpp"
 
 ZObjArrayAllocator::ZObjArrayAllocator(Klass* klass, size_t word_size, int length, bool do_zero, Thread* thread)
   : ObjArrayAllocator(klass, word_size, length, do_zero, thread) {}
 
 void ZObjArrayAllocator::yield_for_safepoint() const {
@@ -51,11 +52,11 @@
   const size_t segment_max = ZUtils::bytes_to_words(64 * K);
   const BasicType element_type = ArrayKlass::cast(_klass)->element_type();
   const size_t header = arrayOopDesc::header_size(element_type);
   const size_t payload_size = _word_size - header;
 
-  if (payload_size <= segment_max) {
+  if (payload_size <= segment_max || ArrayKlass::cast(_klass)->is_flatArray_klass()) {
     // To small to use segmented clearing
     return ObjArrayAllocator::initialize(mem);
   }
 
   // Segmented clearing
@@ -64,11 +65,11 @@
   // cleared, therefore we can't expose the header at the end of this
   // function. Instead explicitly initialize it according to our needs.
 
   // Signal to the ZIterator that this is an invisible root, by setting
   // the mark word to "marked". Reset to prototype() after the clearing.
-  arrayOopDesc::set_mark(mem, markWord::prototype().set_marked());
+  arrayOopDesc::set_mark(mem, Klass::default_prototype_header(_klass).set_marked());
   arrayOopDesc::release_set_klass(mem, _klass);
   assert(_length >= 0, "length should be non-negative");
   arrayOopDesc::set_length(mem, _length);
 
   // Keep the array alive across safepoints through an invisible
@@ -133,9 +134,9 @@
   }
 
   ZThreadLocalData::clear_invisible_root(_thread);
 
   // Signal to the ZIterator that this is no longer an invisible root
-  oopDesc::release_set_mark(mem, markWord::prototype());
+  oopDesc::release_set_mark(mem, Klass::default_prototype_header(_klass));
 
   return cast_to_oop(mem);
 }
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
@@ -169,10 +169,13 @@
 JVM_GetVmArguments(JNIEnv *env);
 
 JNIEXPORT jboolean JNICALL
 JVM_IsPreviewEnabled(void);
 
+JNIEXPORT jboolean JNICALL
+JVM_IsValhallaEnabled(void);
+
 JNIEXPORT jboolean JNICALL
 JVM_IsContinuationsSupported(void);
 
 JNIEXPORT jboolean JNICALL
 JVM_IsForeignLinkerSupported(void);
@@ -420,11 +423,11 @@
 JNIEXPORT jclass JNICALL
 JVM_FindPrimitiveClass(JNIEnv *env, const char *utf);
 
 
 /*
- * Find a class from a boot class loader. Returns NULL if class not found.
+ * Find a class from a boot class loader. Returns nullptr if class not found.
  */
 JNIEXPORT jclass JNICALL
 JVM_FindClassFromBootLoader(JNIEnv *env, const char *name);
 
 /*
@@ -571,10 +574,16 @@
 JVM_IsPrimitiveClass(JNIEnv *env, jclass cls);
 
 JNIEXPORT jboolean JNICALL
 JVM_IsHiddenClass(JNIEnv *env, jclass cls);
 
+JNIEXPORT jboolean JNICALL
+JVM_IsIdentityClass(JNIEnv *env, jclass cls);
+
+JNIEXPORT jboolean JNICALL
+JVM_IsImplicitlyConstructibleClass(JNIEnv *env, jclass cls);
+
 JNIEXPORT jint JNICALL
 JVM_GetClassModifiers(JNIEnv *env, jclass cls);
 
 JNIEXPORT jobjectArray JNICALL
 JVM_GetDeclaredClasses(JNIEnv *env, jclass ofClass);
@@ -1120,10 +1129,16 @@
 JVM_InitAgentProperties(JNIEnv *env, jobject agent_props);
 
 JNIEXPORT jstring JNICALL
 JVM_GetTemporaryDirectory(JNIEnv *env);
 
+JNIEXPORT jarray JNICALL
+JVM_NewNullRestrictedArray(JNIEnv *env, jclass elmClass, jint len);
+
+JNIEXPORT jboolean JNICALL
+JVM_IsNullRestrictedArray(JNIEnv *env, jobject obj);
+
 /* Generics reflection support.
  *
  * Returns information about the given class's EnclosingMethod
  * attribute, if present, or null if the class had no enclosing
  * method.
diff a/src/hotspot/share/include/jvm_constants.h b/src/hotspot/share/include/jvm_constants.h
--- a/src/hotspot/share/include/jvm_constants.h
+++ b/src/hotspot/share/include/jvm_constants.h
@@ -28,11 +28,11 @@
 
 #include "classfile_constants.h"
 
 #define JVM_RECOGNIZED_CLASS_MODIFIERS (JVM_ACC_PUBLIC | \
                                         JVM_ACC_FINAL | \
-                                        JVM_ACC_SUPER | \
+                                        JVM_ACC_IDENTITY | \
                                         JVM_ACC_INTERFACE | \
                                         JVM_ACC_ABSTRACT | \
                                         JVM_ACC_ANNOTATION | \
                                         JVM_ACC_ENUM | \
                                         JVM_ACC_SYNTHETIC)
@@ -43,10 +43,11 @@
                                         JVM_ACC_STATIC | \
                                         JVM_ACC_FINAL | \
                                         JVM_ACC_VOLATILE | \
                                         JVM_ACC_TRANSIENT | \
                                         JVM_ACC_ENUM | \
+                                        JVM_ACC_STRICT | \
                                         JVM_ACC_SYNTHETIC)
 
 #define JVM_RECOGNIZED_METHOD_MODIFIERS (JVM_ACC_PUBLIC | \
                                          JVM_ACC_PRIVATE | \
                                          JVM_ACC_PROTECTED | \
diff a/src/hotspot/share/interpreter/bytecodeUtils.cpp b/src/hotspot/share/interpreter/bytecodeUtils.cpp
--- a/src/hotspot/share/interpreter/bytecodeUtils.cpp
+++ b/src/hotspot/share/interpreter/bytecodeUtils.cpp
@@ -1109,10 +1109,11 @@
     case Bytecodes::_getfield:
     case Bytecodes::_arraylength:
     case Bytecodes::_athrow:
     case Bytecodes::_monitorenter:
     case Bytecodes::_monitorexit:
+    case Bytecodes::_checkcast:
       return 0;
     case Bytecodes::_iaload:
     case Bytecodes::_faload:
     case Bytecodes::_aaload:
     case Bytecodes::_baload:
@@ -1169,11 +1170,17 @@
   return INVALID_BYTECODE_ENCOUNTERED;
 }
 
 bool ExceptionMessageBuilder::print_NPE_cause(outputStream* os, int bci, int slot) {
   if (print_NPE_cause0(os, bci, slot, _max_cause_detail, false, " because \"")) {
-    os->print("\" is null");
+    address code_base = _method->constMethod()->code_base();
+    Bytecodes::Code code = Bytecodes::java_code_at(_method, code_base + bci);
+    if (code == Bytecodes::_aastore) {
+      os->print("\" is null or is a null-free array and there's an attempt to store null in it");
+    } else {
+      os->print("\" is null");
+    }
     return true;
   }
   return false;
 }
 
@@ -1431,10 +1438,15 @@
         int cp_index = Bytes::get_native_u2(code_base+ pos);
         os->print("Cannot invoke \"");
         print_method_name(os, _method, cp_index, code);
         os->print("\"");
       } break;
+    case Bytecodes::_checkcast: {
+        int cp_index = Bytes::get_Java_u2(code_base + pos);
+        ConstantPool* cp = _method->constants();
+        os->print("Cannot cast to null-free type \"%s\"", cp->klass_at_noresolve(cp_index)->as_C_string());
+      } break;
 
     default:
       assert(0, "We should have checked this bytecode in get_NPE_null_slot().");
       break;
   }
diff a/src/hotspot/share/interpreter/bytecodes.cpp b/src/hotspot/share/interpreter/bytecodes.cpp
--- a/src/hotspot/share/interpreter/bytecodes.cpp
+++ b/src/hotspot/share/interpreter/bytecodes.cpp
@@ -29,19 +29,21 @@
 #include "utilities/align.hpp"
 #include "utilities/bytes.hpp"
 
 #define JVM_BYTECODES_DO(def)                                                                                                     \
   def(_fast_agetfield            , "fast_agetfield"            , "bJJ"  , nullptr    , T_OBJECT ,  0, true , _getfield          ) \
+  def(_fast_vgetfield            , "fast_qgetfield"            , "bJJ"  , nullptr    , T_OBJECT ,  0, true , _getfield          ) \
   def(_fast_bgetfield            , "fast_bgetfield"            , "bJJ"  , nullptr    , T_INT    ,  0, true , _getfield          ) \
   def(_fast_cgetfield            , "fast_cgetfield"            , "bJJ"  , nullptr    , T_CHAR   ,  0, true , _getfield          ) \
   def(_fast_dgetfield            , "fast_dgetfield"            , "bJJ"  , nullptr    , T_DOUBLE ,  0, true , _getfield          ) \
   def(_fast_fgetfield            , "fast_fgetfield"            , "bJJ"  , nullptr    , T_FLOAT  ,  0, true , _getfield          ) \
   def(_fast_igetfield            , "fast_igetfield"            , "bJJ"  , nullptr    , T_INT    ,  0, true , _getfield          ) \
   def(_fast_lgetfield            , "fast_lgetfield"            , "bJJ"  , nullptr    , T_LONG   ,  0, true , _getfield          ) \
   def(_fast_sgetfield            , "fast_sgetfield"            , "bJJ"  , nullptr    , T_SHORT  ,  0, true , _getfield          ) \
                                                                                                                                   \
   def(_fast_aputfield            , "fast_aputfield"            , "bJJ"  , nullptr    , T_OBJECT ,  0, true , _putfield          ) \
+  def(_fast_vputfield            , "fast_qputfield"            , "bJJ"  , nullptr    , T_OBJECT ,  0, true , _putfield          ) \
   def(_fast_bputfield            , "fast_bputfield"            , "bJJ"  , nullptr    , T_INT    ,  0, true , _putfield          ) \
   def(_fast_zputfield            , "fast_zputfield"            , "bJJ"  , nullptr    , T_INT    ,  0, true , _putfield          ) \
   def(_fast_cputfield            , "fast_cputfield"            , "bJJ"  , nullptr    , T_CHAR   ,  0, true , _putfield          ) \
   def(_fast_dputfield            , "fast_dputfield"            , "bJJ"  , nullptr    , T_DOUBLE ,  0, true , _putfield          ) \
   def(_fast_fputfield            , "fast_fputfield"            , "bJJ"  , nullptr    , T_FLOAT  ,  0, true , _putfield          ) \
diff a/src/hotspot/share/interpreter/bytecodes.hpp b/src/hotspot/share/interpreter/bytecodes.hpp
--- a/src/hotspot/share/interpreter/bytecodes.hpp
+++ b/src/hotspot/share/interpreter/bytecodes.hpp
@@ -24,10 +24,11 @@
 
 #ifndef SHARE_INTERPRETER_BYTECODES_HPP
 #define SHARE_INTERPRETER_BYTECODES_HPP
 
 #include "memory/allStatic.hpp"
+#include "runtime/globals.hpp"
 #include "utilities/globalDefinitions.hpp"
 
 // Bytecodes specifies all bytecodes used in the VM and
 // provides utility functions to get bytecode attributes.
 
@@ -246,19 +247,21 @@
 
     number_of_java_codes,
 
     // JVM bytecodes
     _fast_agetfield       = number_of_java_codes,
+    _fast_vgetfield       ,
     _fast_bgetfield       ,
     _fast_cgetfield       ,
     _fast_dgetfield       ,
     _fast_fgetfield       ,
     _fast_igetfield       ,
     _fast_lgetfield       ,
     _fast_sgetfield       ,
 
     _fast_aputfield       ,
+    _fast_vputfield       ,
     _fast_bputfield       ,
     _fast_zputfield       ,
     _fast_cputfield       ,
     _fast_dputfield       ,
     _fast_fputfield       ,
@@ -417,11 +420,13 @@
   static bool        is_const       (Code code)    { return (_aconst_null <= code && code <= _ldc2_w); }
   static bool        is_zero_const  (Code code)    { return (code == _aconst_null || code == _iconst_0
                                                            || code == _fconst_0 || code == _dconst_0); }
   static bool        is_return      (Code code)    { return (_ireturn <= code && code <= _return); }
   static bool        is_invoke      (Code code)    { return (_invokevirtual <= code && code <= _invokedynamic); }
-  static bool        is_field_code  (Code code)    { return (_getstatic <= java_code(code) && java_code(code) <= _putfield); }
+  static bool        is_field_code  (Code code)    {
+    return (_getstatic <= java_code(code) && java_code(code) <= _putfield);
+  }
   static bool        has_receiver   (Code code)    { assert(is_invoke(code), "");  return code == _invokevirtual ||
                                                                                           code == _invokespecial ||
                                                                                           code == _invokeinterface; }
   static bool        has_optional_appendix(Code code) { return code == _invokedynamic || code == _invokehandle; }
 
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
@@ -23,10 +23,11 @@
  */
 
 #include "precompiled.hpp"
 #include "classfile/javaClasses.inline.hpp"
 #include "classfile/symbolTable.hpp"
+#include "classfile/systemDictionary.hpp"
 #include "classfile/vmClasses.hpp"
 #include "classfile/vmSymbols.hpp"
 #include "code/codeCache.hpp"
 #include "compiler/compilationPolicy.hpp"
 #include "compiler/compileBroker.hpp"
@@ -43,10 +44,13 @@
 #include "memory/oopFactory.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
 #include "oops/constantPool.inline.hpp"
 #include "oops/cpCache.inline.hpp"
+#include "oops/flatArrayKlass.hpp"
+#include "oops/flatArrayOop.inline.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "oops/instanceKlass.inline.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/methodData.hpp"
 #include "oops/method.inline.hpp"
 #include "oops/objArrayKlass.hpp"
@@ -75,10 +79,11 @@
 #include "runtime/threadCritical.hpp"
 #include "utilities/align.hpp"
 #include "utilities/checkedCast.hpp"
 #include "utilities/copy.hpp"
 #include "utilities/events.hpp"
+#include "utilities/globalDefinitions.hpp"
 #ifdef COMPILER2
 #include "opto/runtime.hpp"
 #endif
 
 // Helper class to access current interpreter state
@@ -239,30 +244,164 @@
   //       because the _breakpoint bytecode would be lost.
   oop obj = klass->allocate_instance(CHECK);
   current->set_vm_result(obj);
 JRT_END
 
+JRT_ENTRY(void, InterpreterRuntime::uninitialized_static_inline_type_field(JavaThread* current, oopDesc* mirror, ResolvedFieldEntry* entry))
+  // The interpreter tries to access an inline static field that has not been initialized.
+  // This situation can happen in different scenarios:
+  //   1 - if the load or initialization of the field failed during step 8 of
+  //       the initialization of the holder of the field, in this case the access to the field
+  //       must fail
+  //   2 - it can also happen when the initialization of the holder class triggered the initialization of
+  //       another class which accesses this field in its static initializer, in this case the
+  //       access must succeed to allow circularity
+  // The code below tries to load and initialize the field's class again before returning the default value.
+  // If the field was not initialized because of an error, an exception should be thrown.
+  // If the class is being initialized, the default value is returned.
+  assert(entry->is_valid(), "Invalid ResolvedFieldEntry");
+  instanceHandle mirror_h(THREAD, (instanceOop)mirror);
+  InstanceKlass* klass = entry->field_holder();
+  u2 index = entry->field_index();
+  assert(klass == java_lang_Class::as_Klass(mirror), "Not the field holder klass");
+  assert(klass->field_is_null_free_inline_type(index), "Sanity check");
+  if (klass->is_being_initialized() && klass->is_init_thread(THREAD)) {
+    int offset = klass->field_offset(index);
+    Klass* field_k = klass->get_inline_type_field_klass_or_null(index);
+    if (field_k == nullptr) {
+      field_k = SystemDictionary::resolve_or_fail(klass->field_signature(index)->fundamental_name(THREAD),
+          Handle(THREAD, klass->class_loader()),
+          Handle(THREAD, klass->protection_domain()),
+          true, CHECK);
+      assert(field_k != nullptr, "Should have been loaded or an exception thrown above");
+      klass->set_inline_type_field_klass(index, InlineKlass::cast(field_k));
+    }
+    field_k->initialize(CHECK);
+    oop defaultvalue = InlineKlass::cast(field_k)->default_value();
+    // It is safe to initialize the static field because 1) the current thread is the initializing thread
+    // and is the only one that can access it, and 2) the field is actually not initialized (i.e. null)
+    // otherwise the JVM should not be executing this code.
+    mirror_h()->obj_field_put(offset, defaultvalue);
+    current->set_vm_result(defaultvalue);
+  } else {
+    assert(klass->is_in_error_state(), "If not initializing, initialization must have failed to get there");
+    ResourceMark rm(THREAD);
+    const char* desc = "Could not initialize class ";
+    const char* className = klass->external_name();
+    size_t msglen = strlen(desc) + strlen(className) + 1;
+    char* message = NEW_RESOURCE_ARRAY(char, msglen);
+    if (nullptr == message) {
+      // Out of memory: can't create detailed error message
+      THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), className);
+    } else {
+      jio_snprintf(message, msglen, "%s%s", desc, className);
+      THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), message);
+    }
+  }
+JRT_END
+
+JRT_ENTRY(void, InterpreterRuntime::read_flat_field(JavaThread* current, oopDesc* obj, int index, Klass* field_holder))
+  Handle obj_h(THREAD, obj);
+
+  assert(oopDesc::is_oop(obj), "Sanity check");
+
+  assert(field_holder->is_instance_klass(), "Sanity check");
+  InstanceKlass* klass = InstanceKlass::cast(field_holder);
+
+  assert(klass->field_is_flat(index), "Sanity check");
+
+  InlineKlass* field_vklass = InlineKlass::cast(klass->get_inline_type_field_klass(index));
+
+  oop res = field_vklass->read_flat_field(obj_h(), klass->field_offset(index), CHECK);
+  current->set_vm_result(res);
+JRT_END
+
+// The protocol to read a nullable flat field is:
+// Step 1: read the null marker with an load_acquire barrier to ensure that
+//         reordered loads won't try to load the value before the null marker is read
+// Step 2: if the null marker value is zero, the field's value is null
+//         otherwise the flat field value can be read like a regular flat field
+JRT_ENTRY(void, InterpreterRuntime::read_nullable_flat_field(JavaThread* current, oopDesc* obj, ResolvedFieldEntry* entry))
+  assert(oopDesc::is_oop(obj), "Sanity check");
+  assert(entry->has_null_marker(), "Otherwise should not get there");
+  Handle obj_h(THREAD, obj);
+
+  InstanceKlass* ik = InstanceKlass::cast(obj_h()->klass());
+  int field_index = entry->field_index();
+  int nm_offset = ik->null_marker_offsets_array()->at(field_index);
+  if (obj_h()->byte_field_acquire(nm_offset) == 0) {
+    current->set_vm_result(nullptr);
+  } else {
+    InlineKlass* field_vklass = InlineKlass::cast(ik->get_inline_type_field_klass(field_index));
+    oop res = field_vklass->read_flat_field(obj_h(), ik->field_offset(field_index), CHECK);
+    current->set_vm_result(res);
+  }
+JRT_END
+
+// The protocol to write a nullable flat field is:
+// If the new field value is null, just write zero to the null marker
+// Otherwise:
+// Step 1: write the field value like a regular flat field
+// Step 2: have a memory barrier to ensure that the whole value content is visible
+// Step 3: update the null marker to a non zero value
+JRT_ENTRY(void, InterpreterRuntime::write_nullable_flat_field(JavaThread* current, oopDesc* obj, oopDesc* value, ResolvedFieldEntry* entry))
+  assert(oopDesc::is_oop(obj), "Sanity check");
+  Handle obj_h(THREAD, obj);
+  assert(value == nullptr || oopDesc::is_oop(value), "Sanity check");
+  Handle val_h(THREAD, value);
+
+  InstanceKlass* ik = InstanceKlass::cast(obj_h()->klass());
+  int nm_offset = ik->null_marker_offsets_array()->at(entry->field_index());
+  if (val_h() == nullptr) {
+    obj_h()->byte_field_put(nm_offset, (jbyte)0);
+    return;
+  }
+  InlineKlass* vk = InlineKlass::cast(val_h()->klass());
+  if (entry->has_internal_null_marker()) {
+    // The interpreter copies values with a bulk operation
+    // To avoid accidently setting the null marker to "null" during
+    // the copying, the null marker is set to non zero in the source object
+    if (val_h()->byte_field(vk->get_internal_null_marker_offset()) == 0) {
+      val_h()->byte_field_put(vk->get_internal_null_marker_offset(), (jbyte)1);
+    }
+    vk->write_non_null_flat_field(obj_h(), entry->field_offset(), val_h());
+  } else {
+    vk->write_non_null_flat_field(obj_h(), entry->field_offset(), val_h());
+    OrderAccess::release();
+    obj_h()->byte_field_put(nm_offset, (jbyte)1);
+  }
+JRT_END
 
 JRT_ENTRY(void, 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))
   Klass*    klass = pool->klass_at(index, CHECK);
-  objArrayOop obj = oopFactory::new_objArray(klass, size, CHECK);
+  arrayOop obj = oopFactory::new_objArray(klass, size, CHECK);
   current->set_vm_result(obj);
 JRT_END
 
+JRT_ENTRY(void, InterpreterRuntime::value_array_load(JavaThread* current, arrayOopDesc* array, int index))
+  flatArrayHandle vah(current, (flatArrayOop)array);
+  oop value_holder = flatArrayOopDesc::value_alloc_copy_from_index(vah, index, CHECK);
+  current->set_vm_result(value_holder);
+JRT_END
+
+JRT_ENTRY(void, InterpreterRuntime::value_array_store(JavaThread* current, void* val, arrayOopDesc* array, int index))
+  assert(val != nullptr, "can't store null into flat array");
+  ((flatArrayOop)array)->value_copy_to_index(cast_to_oop(val), index);
+JRT_END
 
 JRT_ENTRY(void, 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);
+  int i = last_frame.get_index_u2(Bytecodes::_multianewarray);
+  Klass* klass = constants->klass_at(i, CHECK);
   int   nof_dims = last_frame.number_of_dimensions();
   assert(klass->is_klass(), "not a class");
   assert(nof_dims >= 1, "multianewarray rank must be nonzero");
 
   // We must create an array of jints to pass to multi_allocate.
@@ -287,10 +426,33 @@
   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(jboolean, InterpreterRuntime::is_substitutable(JavaThread* current, oopDesc* aobj, oopDesc* bobj))
+  assert(oopDesc::is_oop(aobj) && oopDesc::is_oop(bobj), "must be valid oops");
+
+  Handle ha(THREAD, aobj);
+  Handle hb(THREAD, bobj);
+  JavaValue result(T_BOOLEAN);
+  JavaCallArguments args;
+  args.push_oop(ha);
+  args.push_oop(hb);
+  methodHandle method(current, Universe::is_substitutable_method());
+  JavaCalls::call(&result, method, &args, THREAD);
+  if (HAS_PENDING_EXCEPTION) {
+    // Something really bad happened because isSubstitutable() should not throw exceptions
+    // If it is an error, just let it propagate
+    // If it is an exception, wrap it into an InternalError
+    if (!PENDING_EXCEPTION->is_a(vmClasses::Error_klass())) {
+      Handle e(THREAD, PENDING_EXCEPTION);
+      CLEAR_PENDING_EXCEPTION;
+      THROW_MSG_CAUSE_(vmSymbols::java_lang_InternalError(), "Internal error in substitutability test", e, false);
+    }
+  }
+  return result.get_jboolean();
+JRT_END
 
 // Quicken instance-of and check-cast bytecodes
 JRT_ENTRY(void, InterpreterRuntime::quicken_io_cc(JavaThread* current))
   // Force resolving; quicken the bytecode
   LastFrameAccessor last_frame(current);
@@ -625,10 +787,14 @@
   ResourceMark rm(current);
   methodHandle mh = methodHandle(current, missingMethod);
   LinkResolver::throw_abstract_method_error(mh, recvKlass, THREAD);
 JRT_END
 
+JRT_ENTRY(void, InterpreterRuntime::throw_InstantiationError(JavaThread* current))
+  THROW(vmSymbols::java_lang_InstantiationError());
+JRT_END
+
 
 JRT_ENTRY(void, InterpreterRuntime::throw_IncompatibleClassChangeError(JavaThread* current))
   THROW(vmSymbols::java_lang_IncompatibleClassChangeError());
 JRT_END
 
@@ -705,18 +871,25 @@
   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) {
-    get_code = ((is_static) ? Bytecodes::_getstatic : Bytecodes::_getfield);
+    if (is_static) {
+      get_code = Bytecodes::_getstatic;
+    } else {
+      get_code = Bytecodes::_getfield;
+    }
     if ((is_put && !has_initialized_final_update) || !info.access_flags().is_final()) {
-      put_code = ((is_static) ? Bytecodes::_putstatic : Bytecodes::_putfield);
+        put_code = ((is_static) ? Bytecodes::_putstatic : Bytecodes::_putfield);
     }
   }
 
   ResolvedFieldEntry* entry = pool->resolved_field_entry_at(field_index);
-  entry->set_flags(info.access_flags().is_final(), info.access_flags().is_volatile());
+  entry->set_flags(info.access_flags().is_final(), info.access_flags().is_volatile(),
+                   info.is_flat(), info.is_null_free_inline_type(),
+                   info.has_null_marker(), info.has_internal_null_marker());
+
   entry->fill_in(info.field_holder(), info.offset(),
                  checked_cast<u2>(info.index()), checked_cast<u1>(state),
                  static_cast<u1>(get_code), static_cast<u1>(put_code));
 }
 
@@ -1156,33 +1329,36 @@
 JRT_END
 
 JRT_ENTRY(void, InterpreterRuntime::post_field_access(JavaThread* current, oopDesc* obj,
                                                       ResolvedFieldEntry *entry))
 
+  assert(entry->is_valid(), "Invalid ResolvedFieldEntry");
   // check the access_flags for the field in the klass
 
   InstanceKlass* ik = entry->field_holder();
   int index = entry->field_index();
   if (!ik->field_status(index).is_access_watched()) return;
 
   bool is_static = (obj == nullptr);
+  bool is_flat = entry->is_flat();
   HandleMark hm(current);
 
   Handle h_obj;
   if (!is_static) {
     // non-static field accessors have an object, but we need a handle
     h_obj = Handle(current, obj);
   }
   InstanceKlass* field_holder = entry->field_holder(); // HERE
-  jfieldID fid = jfieldIDWorkaround::to_jfieldID(field_holder, entry->field_offset(), is_static);
+  jfieldID fid = jfieldIDWorkaround::to_jfieldID(field_holder, entry->field_offset(), is_static, is_flat);
   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))
 
+  assert(entry->is_valid(), "Invalid ResolvedFieldEntry");
   InstanceKlass* ik = entry->field_holder();
 
   // check the access_flags for the field in the klass
   int index = entry->field_index();
   // bail out if field modifications are not watched
@@ -1200,14 +1376,16 @@
     case atos: sig_type = JVM_SIGNATURE_CLASS;   break;
     case ltos: sig_type = JVM_SIGNATURE_LONG;    break;
     case dtos: sig_type = JVM_SIGNATURE_DOUBLE;  break;
     default:  ShouldNotReachHere(); return;
   }
+
   bool is_static = (obj == nullptr);
+  bool is_flat = entry->is_flat();
 
   HandleMark hm(current);
-  jfieldID fid = jfieldIDWorkaround::to_jfieldID(ik, entry->field_offset(), is_static);
+  jfieldID fid = jfieldIDWorkaround::to_jfieldID(ik, entry->field_offset(), is_static, is_flat);
   jvalue fvalue;
 #ifdef _LP64
   fvalue = *value;
 #else
   // Long/double values are stored unaligned and also noncontiguously with
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
@@ -56,13 +56,23 @@
   static void    resolve_ldc   (JavaThread* current, Bytecodes::Code bytecode);
 
   // Allocation
   static void    _new          (JavaThread* current, ConstantPool* pool, int index);
   static void    newarray      (JavaThread* current, BasicType type, jint size);
-  static void    anewarray     (JavaThread* current, ConstantPool* pool, int index, jint size);
+  static void    anewarray     (JavaThread* threcurrentad, ConstantPool* pool, int index, jint size);
   static void    multianewarray(JavaThread* current, jint* first_size_address);
   static void    register_finalizer(JavaThread* current, oopDesc* obj);
+  static void    uninitialized_static_inline_type_field(JavaThread* current, oopDesc* mirror, ResolvedFieldEntry* entry);
+  static void    write_heap_copy (JavaThread* current, oopDesc* value, int offset, oopDesc* rcv);
+  static void    read_flat_field(JavaThread* current, oopDesc* value, int index, Klass* field_holder);
+  static void    read_nullable_flat_field(JavaThread* current, oopDesc* object, ResolvedFieldEntry* entry);
+  static void    write_nullable_flat_field(JavaThread* current, oopDesc* object, oopDesc* value, ResolvedFieldEntry* entry);
+
+  static void value_array_load(JavaThread* current, arrayOopDesc* array, int index);
+  static void value_array_store(JavaThread* current, void* val, arrayOopDesc* array, int index);
+
+  static jboolean is_substitutable(JavaThread* current, oopDesc* aobj, oopDesc* bobj);
 
   // Quicken instance-of and check-cast bytecodes
   static void    quicken_io_cc(JavaThread* current);
 
   // Exceptions thrown by the interpreter
@@ -70,10 +80,11 @@
   static void    throw_AbstractMethodErrorWithMethod(JavaThread* current, Method* oop);
   static void    throw_AbstractMethodErrorVerbose(JavaThread* current,
                                                   Klass* recvKlass,
                                                   Method* missingMethod);
 
+  static void    throw_InstantiationError(JavaThread* current);
   static void    throw_IncompatibleClassChangeError(JavaThread* current);
   static void    throw_IncompatibleClassChangeErrorVerbose(JavaThread* current,
                                                            Klass* resc,
                                                            Klass* interfaceKlass);
   static void    throw_StackOverflowError(JavaThread* current);
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
@@ -963,11 +963,12 @@
          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");
 
   bool is_static = (byte == Bytecodes::_getstatic || byte == Bytecodes::_putstatic);
-  bool is_put    = (byte == Bytecodes::_putfield  || byte == Bytecodes::_putstatic || byte == Bytecodes::_nofast_putfield);
+  bool is_put    = (byte == Bytecodes::_putfield  || byte == Bytecodes::_putstatic ||
+                    byte == Bytecodes::_nofast_putfield);
   // Check if there's a resolved klass containing the field
   Klass* resolved_klass = link_info.resolved_klass();
   Symbol* field = link_info.name();
   Symbol* sig = link_info.signature();
 
@@ -1006,24 +1007,24 @@
 
       if (sel_klass != current_klass) {
         ResourceMark rm(THREAD);
         stringStream ss;
         ss.print("Update to %s final field %s.%s attempted from a different class (%s) than the field's declaring class",
-                 is_static ? "static" : "non-static", resolved_klass->external_name(), fd.name()->as_C_string(),
-                current_klass->external_name());
+                  is_static ? "static" : "non-static", resolved_klass->external_name(), fd.name()->as_C_string(),
+                  current_klass->external_name());
         THROW_MSG(vmSymbols::java_lang_IllegalAccessError(), ss.as_string());
       }
 
       if (fd.constants()->pool_holder()->major_version() >= 53) {
         Method* m = link_info.current_method();
         assert(m != nullptr, "information about the current method must be available for 'put' bytecodes");
         bool is_initialized_static_final_update = (byte == Bytecodes::_putstatic &&
                                                    fd.is_static() &&
-                                                   !m->is_static_initializer());
+                                                   !m->is_class_initializer());
         bool is_initialized_instance_final_update = ((byte == Bytecodes::_putfield || byte == Bytecodes::_nofast_putfield) &&
                                                      !fd.is_static() &&
-                                                     !m->is_object_initializer());
+                                                     !m->is_object_constructor());
 
         if (is_initialized_static_final_update || is_initialized_instance_final_update) {
           ResourceMark rm(THREAD);
           stringStream ss;
           ss.print("Update to %s final field %s.%s attempted from a different method (%s) than the initializer method %s ",
@@ -1147,10 +1148,12 @@
   } else {
     resolved_method = resolve_interface_method(link_info, Bytecodes::_invokespecial, CHECK_NULL);
   }
 
   // check if method name is <init>, that it is found in same klass as static type
+  // Since this method is never inherited from a super, any appearance here under
+  // the wrong class would be an error.
   if (resolved_method->name() == vmSymbols::object_initializer_name() &&
       resolved_method->method_holder() != resolved_klass) {
     ResourceMark rm(THREAD);
     stringStream ss;
     ss.print("%s: method '", resolved_klass->external_name());
@@ -1218,11 +1221,11 @@
   // Invokespecial for a superinterface, resolved method is selected method,
   // no checks for shadowing
   methodHandle sel_method(THREAD, resolved_method());
 
   if (link_info.check_access() &&
-      // check if the method is not <init>
+      // check if the method is not <init>, which is never inherited
       resolved_method->name() != vmSymbols::object_initializer_name()) {
 
     Klass* current_klass = link_info.current_klass();
 
     // Check if the class of the resolved_klass is a superclass
@@ -1640,24 +1643,25 @@
   }
   return;
 }
 
 void LinkResolver::resolve_invoke(CallInfo& result, Handle& recv,
-                             const methodHandle& attached_method,
-                             Bytecodes::Code byte, TRAPS) {
+                                  const methodHandle& attached_method,
+                                  Bytecodes::Code byte, bool check_null_and_abstract, TRAPS) {
   Klass* defc = attached_method->method_holder();
   Symbol* name = attached_method->name();
   Symbol* type = attached_method->signature();
   LinkInfo link_info(defc, name, type);
+  Klass* recv_klass = recv.is_null() ? defc : recv->klass();
   switch(byte) {
     case Bytecodes::_invokevirtual:
-      resolve_virtual_call(result, recv, recv->klass(), link_info,
-                           /*check_null_and_abstract=*/true, CHECK);
+      resolve_virtual_call(result, recv, recv_klass, link_info,
+                           check_null_and_abstract, CHECK);
       break;
     case Bytecodes::_invokeinterface:
-      resolve_interface_call(result, recv, recv->klass(), link_info,
-                             /*check_null_and_abstract=*/true, CHECK);
+      resolve_interface_call(result, recv, recv_klass, link_info,
+                             check_null_and_abstract, CHECK);
       break;
     case Bytecodes::_invokestatic:
       resolve_static_call(result, link_info, /*initialize_class=*/false, CHECK);
       break;
     case Bytecodes::_invokespecial:
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
@@ -341,11 +341,11 @@
                              Bytecodes::Code byte, TRAPS);
 
   // runtime resolving from attached method
   static void resolve_invoke(CallInfo& result, Handle& recv,
                              const methodHandle& attached_method,
-                             Bytecodes::Code byte, TRAPS);
+                             Bytecodes::Code byte, bool check_null_and_abstract, TRAPS);
 
   // Only resolved method known.
   static void throw_abstract_method_error(const methodHandle& resolved_method, TRAPS) {
     throw_abstract_method_error(resolved_method, methodHandle(), nullptr, CHECK);
   }
diff a/src/hotspot/share/interpreter/oopMapCache.cpp b/src/hotspot/share/interpreter/oopMapCache.cpp
--- a/src/hotspot/share/interpreter/oopMapCache.cpp
+++ b/src/hotspot/share/interpreter/oopMapCache.cpp
@@ -285,20 +285,20 @@
   LogStream st(Log(interpreter, oopmap)::trace());
 
   if (log) st.print("Locals (%d): ", max_locals);
   for(int i = 0; i < max_locals; i++) {
     bool v1 = is_oop(i)               ? true : false;
-    bool v2 = vars[i].is_reference()  ? true : false;
+    bool v2 = vars[i].is_reference();
     assert(v1 == v2, "locals oop mask generation error");
     if (log) st.print("%d", v1 ? 1 : 0);
   }
   if (log) st.cr();
 
   if (log) st.print("Stack (%d): ", stack_top);
   for(int j = 0; j < stack_top; j++) {
     bool v1 = is_oop(max_locals + j)  ? true : false;
-    bool v2 = stack[j].is_reference() ? true : false;
+    bool v2 = stack[j].is_reference();
     assert(v1 == v2, "stack oop mask generation error");
     if (log) st.print("%d", v1 ? 1 : 0);
   }
   if (log) st.cr();
   return true;
@@ -377,11 +377,11 @@
     if (entry_index == max_locals) {
       cell = stack;
     }
 
     // set oop bit
-    if ( cell->is_reference()) {
+    if (cell->is_reference()) {
       value |= (mask << oop_bit_number );
       _num_oops++;
     }
 
     // set dead bit
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
@@ -454,15 +454,15 @@
 
             fieldDescriptor fd;
             if (klass->find_field(field_name, field_sig, &fd) != nullptr) {
               if (fd.access_flags().is_final()) {
                 if (fd.access_flags().is_static()) {
-                  if (!method->is_static_initializer()) {
+                  if (!method->is_class_initializer()) {
                     fd.set_has_initialized_final_update(true);
                   }
                 } else {
-                  if (!method->is_object_initializer()) {
+                  if (!method->is_object_constructor()) {
                     fd.set_has_initialized_final_update(true);
                   }
                 }
               }
             }
diff a/src/hotspot/share/interpreter/templateTable.cpp b/src/hotspot/share/interpreter/templateTable.cpp
--- a/src/hotspot/share/interpreter/templateTable.cpp
+++ b/src/hotspot/share/interpreter/templateTable.cpp
@@ -157,11 +157,12 @@
 
 //----------------------------------------------------------------------------------------------------
 // Implementation of TemplateTable: Debugging
 
 void TemplateTable::transition(TosState tos_in, TosState tos_out) {
-  assert(_desc->tos_in()  == tos_in , "inconsistent tos_in  information");
+  assert(_desc->tos_in()  == tos_in,
+         "inconsistent tos_in  information");
   assert(_desc->tos_out() == tos_out, "inconsistent tos_out information");
 }
 
 
 //----------------------------------------------------------------------------------------------------
@@ -228,10 +229,11 @@
   const int  ____ = 0;
   const int  ubcp = 1 << Template::uses_bcp_bit;
   const int  disp = 1 << Template::does_dispatch_bit;
   const int  clvm = 1 << Template::calls_vm_bit;
   const int  iswd = 1 << Template::wide_bit;
+
   //                                    interpr. templates
   // Java spec bytecodes                ubcp|disp|clvm|iswd  in    out   generator             argument
   def(Bytecodes::_nop                 , ____|____|____|____, vtos, vtos, nop                 ,  _           );
   def(Bytecodes::_aconst_null         , ____|____|____|____, vtos, atos, aconst_null         ,  _           );
   def(Bytecodes::_iconst_m1           , ____|____|____|____, vtos, itos, iconst              , -1           );
@@ -280,11 +282,11 @@
   def(Bytecodes::_aload_3             , ____|____|____|____, vtos, atos, aload               ,  3           );
   def(Bytecodes::_iaload              , ____|____|____|____, itos, itos, iaload              ,  _           );
   def(Bytecodes::_laload              , ____|____|____|____, itos, ltos, laload              ,  _           );
   def(Bytecodes::_faload              , ____|____|____|____, itos, ftos, faload              ,  _           );
   def(Bytecodes::_daload              , ____|____|____|____, itos, dtos, daload              ,  _           );
-  def(Bytecodes::_aaload              , ____|____|____|____, itos, atos, aaload              ,  _           );
+  def(Bytecodes::_aaload              , ____|____|clvm|____, itos, atos, aaload              ,  _           );
   def(Bytecodes::_baload              , ____|____|____|____, itos, itos, baload              ,  _           );
   def(Bytecodes::_caload              , ____|____|____|____, itos, itos, caload              ,  _           );
   def(Bytecodes::_saload              , ____|____|____|____, itos, itos, saload              ,  _           );
   def(Bytecodes::_istore              , ubcp|____|clvm|____, itos, vtos, istore              ,  _           );
   def(Bytecodes::_lstore              , ubcp|____|____|____, ltos, vtos, lstore              ,  _           );
@@ -432,10 +434,11 @@
   def(Bytecodes::_multianewarray      , ubcp|____|clvm|____, vtos, atos, multianewarray      ,  _           );
   def(Bytecodes::_ifnull              , ubcp|____|clvm|____, atos, vtos, if_nullcmp          , equal        );
   def(Bytecodes::_ifnonnull           , ubcp|____|clvm|____, atos, vtos, if_nullcmp          , not_equal    );
   def(Bytecodes::_goto_w              , ubcp|____|clvm|____, vtos, vtos, goto_w              ,  _           );
   def(Bytecodes::_jsr_w               , ubcp|____|____|____, vtos, vtos, jsr_w               ,  _           );
+  def(Bytecodes::_breakpoint          , ubcp|disp|clvm|____, vtos, vtos, _breakpoint         ,  _           );
 
   // wide Java spec bytecodes
   def(Bytecodes::_iload               , ubcp|____|____|iswd, vtos, itos, wide_iload          ,  _           );
   def(Bytecodes::_lload               , ubcp|____|____|iswd, vtos, ltos, wide_lload          ,  _           );
   def(Bytecodes::_fload               , ubcp|____|____|iswd, vtos, ftos, wide_fload          ,  _           );
@@ -450,19 +453,21 @@
   def(Bytecodes::_ret                 , ubcp|disp|____|iswd, vtos, vtos, wide_ret            ,  _           );
   def(Bytecodes::_breakpoint          , ubcp|disp|clvm|____, vtos, vtos, _breakpoint         ,  _           );
 
   // JVM bytecodes
   def(Bytecodes::_fast_agetfield      , ubcp|____|____|____, atos, atos, fast_accessfield    ,  atos        );
+  def(Bytecodes::_fast_vgetfield      , ubcp|____|clvm|____, atos, atos, fast_accessfield    ,  atos        );
   def(Bytecodes::_fast_bgetfield      , ubcp|____|____|____, atos, itos, fast_accessfield    ,  itos        );
   def(Bytecodes::_fast_cgetfield      , ubcp|____|____|____, atos, itos, fast_accessfield    ,  itos        );
   def(Bytecodes::_fast_dgetfield      , ubcp|____|____|____, atos, dtos, fast_accessfield    ,  dtos        );
   def(Bytecodes::_fast_fgetfield      , ubcp|____|____|____, atos, ftos, fast_accessfield    ,  ftos        );
   def(Bytecodes::_fast_igetfield      , ubcp|____|____|____, atos, itos, fast_accessfield    ,  itos        );
   def(Bytecodes::_fast_lgetfield      , ubcp|____|____|____, atos, ltos, fast_accessfield    ,  ltos        );
   def(Bytecodes::_fast_sgetfield      , ubcp|____|____|____, atos, itos, fast_accessfield    ,  itos        );
 
   def(Bytecodes::_fast_aputfield      , ubcp|____|____|____, atos, vtos, fast_storefield ,   atos        );
+  def(Bytecodes::_fast_vputfield      , ubcp|____|clvm|____, atos, vtos, fast_storefield ,   atos        );
   def(Bytecodes::_fast_bputfield      , ubcp|____|____|____, itos, vtos, fast_storefield ,   itos        );
   def(Bytecodes::_fast_zputfield      , ubcp|____|____|____, itos, vtos, fast_storefield ,   itos        );
   def(Bytecodes::_fast_cputfield      , ubcp|____|____|____, itos, vtos, fast_storefield  ,  itos        );
   def(Bytecodes::_fast_dputfield      , ubcp|____|____|____, dtos, vtos, fast_storefield  ,  dtos        );
   def(Bytecodes::_fast_fputfield      , ubcp|____|____|____, ftos, vtos, fast_storefield  ,  ftos        );
@@ -495,10 +500,11 @@
   def(Bytecodes::_nofast_putfield     , ubcp|____|clvm|____, vtos, vtos, nofast_putfield     , f2_byte      );
 
   def(Bytecodes::_nofast_aload_0      , ____|____|clvm|____, vtos, atos, nofast_aload_0      ,  _           );
   def(Bytecodes::_nofast_iload        , ubcp|____|clvm|____, vtos, itos, nofast_iload        ,  _           );
 
+
   def(Bytecodes::_shouldnotreachhere   , ____|____|____|____, vtos, vtos, shouldnotreachhere ,  _           );
 }
 
 void TemplateTable::unimplemented_bc() {
   _masm->unimplemented( Bytecodes::name(_desc->bytecode()));
diff a/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp b/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp
--- a/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp
+++ b/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp
@@ -1161,14 +1161,15 @@
         stack_token = _debug_recorder->create_scope_values(stack);
         monitors_token = _debug_recorder->create_monitor_values(monitors);
       }
 
       // has_ea_local_in_scope and arg_escape should be added to JVMCI
+      const bool return_scalarized     = false;
       const bool has_ea_local_in_scope = false;
       const bool arg_escape            = false;
       _debug_recorder->describe_scope(pc_offset, method, nullptr, bci, reexecute, rethrow_exception, is_mh_invoke, return_oop,
-                                      has_ea_local_in_scope, arg_escape,
+                                      return_scalarized, has_ea_local_in_scope, arg_escape,
                                       locals_token, stack_token, monitors_token);
     }
   }
   if (full_info) {
     // Clear the virtual objects as they are specific to one DebugInfo
@@ -1303,10 +1304,12 @@
     case UNVERIFIED_ENTRY:
       _offsets.set_value(CodeOffsets::Entry, pc_offset);
       break;
     case VERIFIED_ENTRY:
       _offsets.set_value(CodeOffsets::Verified_Entry, pc_offset);
+      _offsets.set_value(CodeOffsets::Verified_Inline_Entry, pc_offset);
+      _offsets.set_value(CodeOffsets::Verified_Inline_Entry_RO, pc_offset);
       break;
     case OSR_ENTRY:
       _offsets.set_value(CodeOffsets::OSR_Entry, pc_offset);
       break;
     case EXCEPTION_HANDLER_ENTRY:
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
@@ -85,11 +85,14 @@
   Array<Method*>* objectMethods = vmClasses::Object_klass()->methods();
   // Initialize compile queue with a selected set of methods.
   int len = objectMethods->length();
   for (int i = 0; i < len; i++) {
     methodHandle mh(THREAD, objectMethods->at(i));
-    if (!mh->is_native() && !mh->is_static() && !mh->is_initializer()) {
+    if (!mh->is_native() &&
+        !mh->is_static() &&
+        !mh->is_object_constructor() &&
+        !mh->is_class_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);
     }
   }
diff a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp
--- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp
+++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp
@@ -1543,11 +1543,11 @@
             }
 
             if (objects != nullptr) {
               RegisterMap reg_map(vf->register_map());
               bool realloc_failures = Deoptimization::realloc_objects(thread, vf->frame_pointer(), &reg_map, objects, CHECK_NULL);
-              Deoptimization::reassign_fields(vf->frame_pointer(), &reg_map, objects, realloc_failures, false);
+              Deoptimization::reassign_fields(vf->frame_pointer(), &reg_map, objects, realloc_failures, false, CHECK_NULL);
               realloc_called = true;
             }
 
             GrowableArray<ScopeValue*>* local_values = scope->locals();
             for (int i = 0; i < local_values->length(); i++) {
@@ -1800,11 +1800,11 @@
     // no objects to materialize
     return;
   }
 
   bool realloc_failures = Deoptimization::realloc_objects(thread, fstAfterDeopt.current(), fstAfterDeopt.register_map(), objects, CHECK);
-  Deoptimization::reassign_fields(fstAfterDeopt.current(), fstAfterDeopt.register_map(), objects, realloc_failures, false);
+  Deoptimization::reassign_fields(fstAfterDeopt.current(), fstAfterDeopt.register_map(), objects, realloc_failures, false, THREAD);
 
   for (int frame_index = 0; frame_index < virtualFrames->length(); frame_index++) {
     compiledVFrame* cvf = virtualFrames->at(frame_index);
 
     GrowableArray<ScopeValue*>* scopedValues = cvf->scope()->locals();
@@ -2125,11 +2125,11 @@
 
   InstanceKlass* iklass = InstanceKlass::cast(klass);
   GrowableArray<Method*> constructors_array;
   for (int i = 0; i < iklass->methods()->length(); i++) {
     Method* m = iklass->methods()->at(i);
-    if (m->is_initializer() && !m->is_static()) {
+    if (m->is_object_constructor()) {
       constructors_array.append(m);
     }
   }
   JVMCIObjectArray methods = JVMCIENV->new_ResolvedJavaMethod_array(constructors_array.length(), JVMCI_CHECK_NULL);
   for (int i = 0; i < constructors_array.length(); i++) {
@@ -2152,11 +2152,11 @@
 
   InstanceKlass* iklass = InstanceKlass::cast(klass);
   GrowableArray<Method*> methods_array;
   for (int i = 0; i < iklass->methods()->length(); i++) {
     Method* m = iklass->methods()->at(i);
-    if (!m->is_initializer() && !m->is_overpass()) {
+    if (!(m->is_object_constructor() || m->is_class_initializer()) && !m->is_overpass()) {
       methods_array.append(m);
     }
   }
   JVMCIObjectArray methods = JVMCIENV->new_ResolvedJavaMethod_array(methods_array.length(), JVMCI_CHECK_NULL);
   for (int i = 0; i < methods_array.length(); i++) {
@@ -2865,15 +2865,15 @@
 
 C2V_VMENTRY_NULL(jobject, asReflectionExecutable, (JNIEnv* env, jobject, ARGUMENT_PAIR(method)))
   requireInHotSpot("asReflectionExecutable", JVMCI_CHECK_NULL);
   methodHandle m(THREAD, UNPACK_PAIR(Method, method));
   oop executable;
-  if (m->is_initializer()) {
-    if (m->is_static_initializer()) {
+  if (m->is_class_initializer()) {
       JVMCI_THROW_MSG_NULL(IllegalArgumentException,
           "Cannot create java.lang.reflect.Method for class initializer");
-    }
+  }
+  else if (m->is_object_constructor()) {
     executable = Reflection::new_constructor(m, CHECK_NULL);
   } else {
     executable = Reflection::new_method(m, false, CHECK_NULL);
   }
   return JNIHandles::make_local(THREAD, executable);
diff a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
--- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
+++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
@@ -181,11 +181,11 @@
                                                                                                                                      \
   nonstatic_field(InstanceKlass,               _fieldinfo_stream,                             Array<u1>*)                            \
   nonstatic_field(InstanceKlass,               _constants,                                    ConstantPool*)                         \
   volatile_nonstatic_field(InstanceKlass,      _init_state,                                   InstanceKlass::ClassState)             \
   volatile_nonstatic_field(InstanceKlass,      _init_thread,                                  JavaThread*)                           \
-  nonstatic_field(InstanceKlass,               _misc_flags._flags,                            u2)                                    \
+  nonstatic_field(InstanceKlass,               _misc_flags._flags,                            u4)                                    \
   nonstatic_field(InstanceKlass,               _annotations,                                  Annotations*)                          \
                                                                                                                                      \
   volatile_nonstatic_field(JavaFrameAnchor,    _last_Java_sp,                                 intptr_t*)                             \
   volatile_nonstatic_field(JavaFrameAnchor,    _last_Java_pc,                                 address)                               \
                                                                                                                                      \
@@ -643,10 +643,13 @@
   declare_constant(DataLayout::arg_info_data_tag)                         \
   declare_constant(DataLayout::call_type_data_tag)                        \
   declare_constant(DataLayout::virtual_call_type_data_tag)                \
   declare_constant(DataLayout::parameters_type_data_tag)                  \
   declare_constant(DataLayout::speculative_trap_data_tag)                 \
+  declare_constant(DataLayout::array_store_data_tag)                      \
+  declare_constant(DataLayout::array_load_data_tag)                       \
+  declare_constant(DataLayout::acmp_data_tag)                             \
                                                                           \
   declare_constant(Deoptimization::Unpack_deopt)                          \
   declare_constant(Deoptimization::Unpack_exception)                      \
   declare_constant(Deoptimization::Unpack_uncommon_trap)                  \
   declare_constant(Deoptimization::Unpack_reexecute)                      \
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
@@ -152,10 +152,11 @@
   LOG_TAG(periodic) \
   LOG_TAG(phases) \
   LOG_TAG(plab) \
   LOG_TAG(placeholders) \
   LOG_TAG(preempt) \
+  LOG_TAG(preload)   /* Trace successfull class preloading */ \
   LOG_TAG(preorder)  /* Trace all classes loaded in order referenced (not loaded) */ \
   LOG_TAG(preview)   /* Trace loading of preview feature types */ \
   LOG_TAG(promotion) \
   LOG_TAG(protectiondomain) /* "Trace protection domain verification" */ \
   LOG_TAG(ptrqueue) \
@@ -206,10 +207,11 @@
   LOG_TAG(unmap) \
   LOG_TAG(unshareable) \
   NOT_PRODUCT(LOG_TAG(upcall)) \
   LOG_TAG(update) \
   LOG_TAG(valuebasedclasses) \
+  LOG_TAG(valuetypes) \
   LOG_TAG(verification) \
   LOG_TAG(verify) \
   LOG_TAG(vmmutex) \
   LOG_TAG(vmoperation) \
   LOG_TAG(vmthread) \
diff a/src/hotspot/share/memory/heapInspection.cpp b/src/hotspot/share/memory/heapInspection.cpp
--- a/src/hotspot/share/memory/heapInspection.cpp
+++ b/src/hotspot/share/memory/heapInspection.cpp
@@ -32,13 +32,17 @@
 #include "logging/logTag.hpp"
 #include "memory/heapInspection.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
 #include "nmt/memTracker.hpp"
+#include "oops/fieldInfo.hpp"
 #include "oops/oop.inline.hpp"
+#include "oops/inlineKlass.inline.hpp"
+#include "runtime/reflectionUtils.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/os.hpp"
+#include "runtime/fieldDescriptor.inline.hpp"
 #include "utilities/globalDefinitions.hpp"
 #include "utilities/macros.hpp"
 #include "utilities/stack.inline.hpp"
 
 // HeapInspection
@@ -507,10 +511,134 @@
   void do_cinfo(KlassInfoEntry* cie) {
     _cih->add(cie);
   }
 };
 
+
+class FindClassByNameClosure : public KlassInfoClosure {
+ private:
+  GrowableArray<Klass*>* _klasses;
+  Symbol* _classname;
+ public:
+  FindClassByNameClosure(GrowableArray<Klass*>* klasses, Symbol* classname) :
+    _klasses(klasses), _classname(classname) { }
+
+  void do_cinfo(KlassInfoEntry* cie) {
+    if (cie->klass()->name() == _classname) {
+      _klasses->append(cie->klass());
+    }
+  }
+};
+
+class FieldDesc {
+private:
+  Symbol* _name;
+  Symbol* _signature;
+  int _offset;
+  int _index;
+  InstanceKlass* _holder;
+  AccessFlags _access_flags;
+  FieldInfo::FieldFlags _field_flags;
+ public:
+  FieldDesc() : _name(nullptr), _signature(nullptr), _offset(-1), _index(-1), _holder(nullptr),
+                _access_flags(AccessFlags()), _field_flags(FieldInfo::FieldFlags((u4)0)) { }
+
+  FieldDesc(fieldDescriptor& fd) : _name(fd.name()), _signature(fd.signature()), _offset(fd.offset()),
+                                   _index(fd.index()), _holder(fd.field_holder()),
+                                   _access_flags(fd.access_flags()), _field_flags(fd.field_flags()) { }
+
+  const Symbol* name() { return _name;}
+  const Symbol* signature() { return _signature; }
+  int offset() const { return _offset; }
+  int index() const { return _index; }
+  const InstanceKlass* holder() { return _holder; }
+  const AccessFlags& access_flags() { return _access_flags; }
+  bool is_null_free_inline_type() const { return _field_flags.is_null_free_inline_type(); }
+};
+
+static int compare_offset(FieldDesc* f1, FieldDesc* f2) {
+   return f1->offset() > f2->offset() ? 1 : -1;
+}
+
+static void print_field(outputStream* st, int level, int offset, FieldDesc& fd, bool is_inline_type, bool is_flat ) {
+  const char* flat_field_msg = "";
+  if (is_flat) {
+    flat_field_msg = is_flat ? "flat" : "not flat";
+  }
+  st->print_cr("  @ %d %*s \"%s\" %s %s %s",
+      offset, level * 3, "",
+      fd.name()->as_C_string(),
+      fd.signature()->as_C_string(),
+      is_inline_type ? " // inline type " : "",
+      flat_field_msg);
+}
+
+static void print_flat_field(outputStream* st, int level, int offset, InstanceKlass* klass) {
+  assert(klass->is_inline_klass(), "Only inline types can be flat");
+  InlineKlass* vklass = InlineKlass::cast(klass);
+  GrowableArray<FieldDesc>* fields = new (mtServiceability) GrowableArray<FieldDesc>(100, mtServiceability);
+  for (FieldStream fd(klass, false, false); !fd.eos(); fd.next()) {
+    if (!fd.access_flags().is_static()) {
+      fields->append(FieldDesc(fd.field_descriptor()));
+    }
+  }
+  fields->sort(compare_offset);
+  for(int i = 0; i < fields->length(); i++) {
+    FieldDesc fd = fields->at(i);
+    int offset2 = offset + fd.offset() - vklass->first_field_offset();
+    print_field(st, level, offset2, fd,
+        fd.is_null_free_inline_type(), fd.holder()->field_is_flat(fd.index()));
+    if (fd.holder()->field_is_flat(fd.index())) {
+      print_flat_field(st, level + 1, offset2 ,
+          InstanceKlass::cast(fd.holder()->get_inline_type_field_klass(fd.index())));
+    }
+  }
+}
+
+void PrintClassLayout::print_class_layout(outputStream* st, char* class_name) {
+  KlassInfoTable cit(true);
+  if (cit.allocation_failed()) {
+    st->print_cr("ERROR: Ran out of C-heap; hierarchy not generated");
+    return;
+  }
+
+  Thread* THREAD = Thread::current();
+
+  Symbol* classname = SymbolTable::probe(class_name, (int)strlen(class_name));
+
+  GrowableArray<Klass*>* klasses = new (mtServiceability) GrowableArray<Klass*>(100, mtServiceability);
+
+  FindClassByNameClosure fbnc(klasses, classname);
+  cit.iterate(&fbnc);
+
+  for(int i = 0; i < klasses->length(); i++) {
+    Klass* klass = klasses->at(i);
+    if (!klass->is_instance_klass()) continue;  // Skip
+    InstanceKlass* ik = InstanceKlass::cast(klass);
+    int tab = 1;
+    st->print_cr("Class %s [@%s]:", klass->name()->as_C_string(),
+        klass->class_loader_data()->loader_name());
+    ResourceMark rm;
+    GrowableArray<FieldDesc>* fields = new (mtServiceability) GrowableArray<FieldDesc>(100, mtServiceability);
+    for (FieldStream fd(ik, false, false); !fd.eos(); fd.next()) {
+      if (!fd.access_flags().is_static()) {
+        fields->append(FieldDesc(fd.field_descriptor()));
+      }
+    }
+    fields->sort(compare_offset);
+    for(int i = 0; i < fields->length(); i++) {
+      FieldDesc fd = fields->at(i);
+      print_field(st, 0, fd.offset(), fd, fd.is_null_free_inline_type(), fd.holder()->field_is_flat(fd.index()));
+      if (fd.holder()->field_is_flat(fd.index())) {
+        print_flat_field(st, 1, fd.offset(),
+            InstanceKlass::cast(fd.holder()->get_inline_type_field_klass(fd.index())));
+      }
+    }
+  }
+  st->cr();
+}
+
 class RecordInstanceClosure : public ObjectClosure {
  private:
   KlassInfoTable* _cit;
   uintx _missed_count;
   BoolObjectClosure* _filter;
diff a/src/hotspot/share/memory/heapInspection.hpp b/src/hotspot/share/memory/heapInspection.hpp
--- a/src/hotspot/share/memory/heapInspection.hpp
+++ b/src/hotspot/share/memory/heapInspection.hpp
@@ -189,10 +189,15 @@
   void add(KlassInfoEntry* cie);
   void print_histo_on(outputStream* st);
   void sort();
 };
 
+class PrintClassLayout : AllStatic {
+ public:
+  static void print_class_layout(outputStream* st, char* classname);
+};
+
 #endif // INCLUDE_SERVICES
 
 // These declarations are needed since the declaration of KlassInfoTable and
 // KlassInfoClosure are guarded by #if INLCUDE_SERVICES
 class KlassInfoTable;
diff a/src/hotspot/share/memory/iterator.hpp b/src/hotspot/share/memory/iterator.hpp
--- a/src/hotspot/share/memory/iterator.hpp
+++ b/src/hotspot/share/memory/iterator.hpp
@@ -52,10 +52,12 @@
 // OopClosure is used for iterating through references to Java objects.
 class OopClosure : public Closure {
  public:
   virtual void do_oop(oop* o) = 0;
   virtual void do_oop(narrowOop* o) = 0;
+  virtual void do_oop_no_buffering(oop* o) { do_oop(o); }
+  virtual void do_oop_no_buffering(narrowOop* o) { do_oop(o); }
 };
 
 class DoNothingClosure : public OopClosure {
  public:
   virtual void do_oop(oop* p)       {}
@@ -135,10 +137,15 @@
  public:
   enum { SkipNull = true };
   virtual void do_derived_oop(derived_base* base, derived_pointer* derived) = 0;
 };
 
+class BufferedValueClosure : public Closure {
+public:
+  virtual void do_buffered_value(oop* p) = 0;
+};
+
 class KlassClosure : public Closure {
  public:
   virtual void do_klass(Klass* k) = 0;
 };
 
diff a/src/hotspot/share/memory/iterator.inline.hpp b/src/hotspot/share/memory/iterator.inline.hpp
--- a/src/hotspot/share/memory/iterator.inline.hpp
+++ b/src/hotspot/share/memory/iterator.inline.hpp
@@ -30,10 +30,11 @@
 #include "classfile/classLoaderData.hpp"
 #include "code/nmethod.hpp"
 #include "oops/access.inline.hpp"
 #include "oops/compressedOops.inline.hpp"
 #include "oops/klass.hpp"
+#include "oops/flatArrayKlass.inline.hpp"
 #include "oops/instanceKlass.inline.hpp"
 #include "oops/instanceMirrorKlass.inline.hpp"
 #include "oops/instanceClassLoaderKlass.inline.hpp"
 #include "oops/instanceRefKlass.inline.hpp"
 #include "oops/instanceStackChunkKlass.inline.hpp"
@@ -141,16 +142,18 @@
   public:
     FunctionType _function[Klass::KLASS_KIND_COUNT];
 
     Table(){
       set_init_function<InstanceKlass>();
+      set_init_function<InlineKlass>();
       set_init_function<InstanceRefKlass>();
       set_init_function<InstanceMirrorKlass>();
       set_init_function<InstanceClassLoaderKlass>();
       set_init_function<InstanceStackChunkKlass>();
       set_init_function<ObjArrayKlass>();
       set_init_function<TypeArrayKlass>();
+      set_init_function<FlatArrayKlass>();
     }
   };
 
   static Table _table;
 public:
@@ -204,16 +207,18 @@
   public:
     FunctionType _function[Klass::KLASS_KIND_COUNT];
 
     Table(){
       set_init_function<InstanceKlass>();
+      set_init_function<InlineKlass>();
       set_init_function<InstanceRefKlass>();
       set_init_function<InstanceMirrorKlass>();
       set_init_function<InstanceClassLoaderKlass>();
       set_init_function<InstanceStackChunkKlass>();
       set_init_function<ObjArrayKlass>();
       set_init_function<TypeArrayKlass>();
+      set_init_function<FlatArrayKlass>();
     }
   };
 
   static Table _table;
 public:
@@ -267,16 +272,18 @@
   public:
     FunctionType _function[Klass::KLASS_KIND_COUNT];
 
     Table(){
       set_init_function<InstanceKlass>();
+      set_init_function<InlineKlass>();
       set_init_function<InstanceRefKlass>();
       set_init_function<InstanceMirrorKlass>();
       set_init_function<InstanceClassLoaderKlass>();
       set_init_function<InstanceStackChunkKlass>();
       set_init_function<ObjArrayKlass>();
       set_init_function<TypeArrayKlass>();
+      set_init_function<FlatArrayKlass>();
     }
   };
 
   static Table _table;
 public:
diff a/src/hotspot/share/memory/oopFactory.cpp b/src/hotspot/share/memory/oopFactory.cpp
--- a/src/hotspot/share/memory/oopFactory.cpp
+++ b/src/hotspot/share/memory/oopFactory.cpp
@@ -28,13 +28,17 @@
 #include "classfile/vmSymbols.hpp"
 #include "gc/shared/collectedHeap.inline.hpp"
 #include "memory/oopFactory.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
+#include "oops/flatArrayKlass.hpp"
+#include "oops/flatArrayOop.inline.hpp"
+#include "oops/flatArrayOop.hpp"
 #include "oops/instanceKlass.hpp"
 #include "oops/instanceOop.hpp"
 #include "oops/objArrayKlass.hpp"
+#include "oops/objArrayOop.inline.hpp"
 #include "oops/objArrayOop.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/typeArrayKlass.hpp"
 #include "oops/typeArrayOop.inline.hpp"
 #include "runtime/handles.inline.hpp"
@@ -121,9 +125,45 @@
   } else {
     return InstanceKlass::cast(klass)->allocate_objArray(1, length, THREAD);
   }
 }
 
+arrayOop oopFactory::new_valueArray(Klass* k, int length, TRAPS) {
+  InlineKlass* klass = InlineKlass::cast(k);
+  // Request a flat array, but we might not actually get it...either way "null-free" are the aaload/aastore semantics
+  Klass* array_klass = klass->value_array_klass(CHECK_NULL);
+  assert(array_klass->is_null_free_array_klass(), "Expect a null-free array class here");
+
+  arrayOop oop;
+  if (array_klass->is_flatArray_klass()) {
+    oop = (arrayOop) FlatArrayKlass::cast(array_klass)->allocate(length, CHECK_NULL);
+    assert(oop == nullptr || oop->is_flatArray(), "sanity");
+    assert(oop == nullptr || oop->klass()->is_flatArray_klass(), "sanity");
+  } else {
+    oop = (arrayOop) ObjArrayKlass::cast(array_klass)->allocate(length, CHECK_NULL);
+  }
+  assert(oop == nullptr || oop->klass()->is_null_free_array_klass(), "sanity");
+  assert(oop == nullptr || oop->is_null_free_array(), "sanity");
+  return oop;
+}
+
+objArrayHandle oopFactory::copy_flatArray_to_objArray(flatArrayHandle array, TRAPS) {
+  int len = array->length();
+  FlatArrayKlass* vak = FlatArrayKlass::cast(array->klass());
+  objArrayOop oarray = new_objectArray(array->length(), CHECK_(objArrayHandle()));
+  objArrayHandle oarrayh(THREAD, oarray);
+  vak->copy_array(array(), 0, oarrayh(), 0, len, CHECK_(objArrayHandle()));
+  return oarrayh;
+}
+
+objArrayHandle  oopFactory::ensure_objArray(oop array, TRAPS) {
+  if (array != nullptr && array->is_flatArray()) {
+    return copy_flatArray_to_objArray(flatArrayHandle(THREAD, flatArrayOop(array)), THREAD);
+  } else {
+    return objArrayHandle(THREAD, objArrayOop(array));
+  }
+}
+
 objArrayHandle oopFactory::new_objArray_handle(Klass* klass, int length, TRAPS) {
   objArrayOop obj = new_objArray(klass, length, CHECK_(objArrayHandle()));
   return objArrayHandle(THREAD, obj);
 }
diff a/src/hotspot/share/memory/oopFactory.hpp b/src/hotspot/share/memory/oopFactory.hpp
--- a/src/hotspot/share/memory/oopFactory.hpp
+++ b/src/hotspot/share/memory/oopFactory.hpp
@@ -54,10 +54,23 @@
   static typeArrayOop    new_symbolArray(int length, TRAPS);
 
   // Regular object arrays
   static objArrayOop     new_objArray(Klass* klass, int length, TRAPS);
 
+  // Value arrays...
+  // LWorld:
+  //    - Q-type signature allocation should use this path.
+  //    - L-type signature allocation should use new_objArray
+  //
+  // Method specifically null free and possibly flat if possible
+  // i.e. flatArrayOop if flattening can be done, else "null free" objArrayOop
+  static arrayOop        new_valueArray(Klass* klass, int length, TRAPS);
+
+  // Helper conversions from value to obj array...
+  static objArrayHandle  copy_flatArray_to_objArray(flatArrayHandle array, TRAPS);
+  static objArrayHandle  ensure_objArray(oop array, TRAPS); // copy into new objArray if not already an objArray
+
   // Helper that returns a Handle
   static objArrayHandle  new_objArray_handle(Klass* klass, int length, TRAPS);
 };
 
 #endif // SHARE_MEMORY_OOPFACTORY_HPP
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
@@ -136,10 +136,12 @@
 LatestMethodCache* Universe::_finalizer_register_cache = nullptr;
 LatestMethodCache* Universe::_loader_addClass_cache    = nullptr;
 LatestMethodCache* Universe::_throw_illegal_access_error_cache = nullptr;
 LatestMethodCache* Universe::_throw_no_such_method_error_cache = nullptr;
 LatestMethodCache* Universe::_do_stack_walk_cache     = nullptr;
+LatestMethodCache* Universe::_is_substitutable_cache  = nullptr;
+LatestMethodCache* Universe::_value_object_hash_code_cache = nullptr;
 
 long Universe::verify_flags                           = Universe::Verify_All;
 
 Array<int>* Universe::_the_empty_int_array            = nullptr;
 Array<u2>* Universe::_the_empty_short_array           = nullptr;
@@ -235,10 +237,12 @@
   _finalizer_register_cache->metaspace_pointers_do(it);
   _loader_addClass_cache->metaspace_pointers_do(it);
   _throw_illegal_access_error_cache->metaspace_pointers_do(it);
   _throw_no_such_method_error_cache->metaspace_pointers_do(it);
   _do_stack_walk_cache->metaspace_pointers_do(it);
+  _is_substitutable_cache->metaspace_pointers_do(it);
+  _value_object_hash_code_cache->metaspace_pointers_do(it);
 }
 
 #if INCLUDE_CDS_JAVA_HEAP
 void Universe::set_archived_basic_type_mirror_index(BasicType t, int index) {
   assert(CDSConfig::is_dumping_heap(), "sanity");
@@ -286,10 +290,12 @@
   _finalizer_register_cache->serialize(f);
   _loader_addClass_cache->serialize(f);
   _throw_illegal_access_error_cache->serialize(f);
   _throw_no_such_method_error_cache->serialize(f);
   _do_stack_walk_cache->serialize(f);
+  _is_substitutable_cache->serialize(f);
+  _value_object_hash_code_cache->serialize(f);
 }
 
 
 void Universe::check_alignment(uintx size, uintx alignment, const char* name) {
   if (size < alignment || size % alignment != 0) {
@@ -369,10 +375,11 @@
       // Verify shared interfaces array.
       assert(_the_array_interfaces_array->at(0) ==
              vmClasses::Cloneable_klass(), "u3");
       assert(_the_array_interfaces_array->at(1) ==
              vmClasses::Serializable_klass(), "u3");
+
     } else
 #endif
     {
       // Set up shared interfaces array.  (Do this before supers are set up.)
       _the_array_interfaces_array->at_put(0, vmClasses::Cloneable_klass());
@@ -797,11 +804,10 @@
   }
 
   Universe::initialize_tlab();
 
   Metaspace::global_initialize();
-
   // Initialize performance counters for metaspaces
   MetaspaceCounters::initialize_performance_counters();
 
   // Checks 'AfterMemoryInit' constraints.
   if (!JVMFlagLimit::check_all_constraints(JVMFlagConstraintPhase::AfterMemoryInit)) {
@@ -815,10 +821,12 @@
   Universe::_finalizer_register_cache = new LatestMethodCache();
   Universe::_loader_addClass_cache    = new LatestMethodCache();
   Universe::_throw_illegal_access_error_cache = new LatestMethodCache();
   Universe::_throw_no_such_method_error_cache = new LatestMethodCache();
   Universe::_do_stack_walk_cache = new LatestMethodCache();
+  Universe::_is_substitutable_cache = new LatestMethodCache();
+  Universe::_value_object_hash_code_cache = new LatestMethodCache();
 
 #if INCLUDE_CDS
   DynamicArchive::check_for_dynamic_dump();
   if (UseSharedSpaces) {
     // Read the data structures supporting the shared spaces (shared
@@ -971,10 +979,21 @@
   // Set up method for stack walking
   initialize_known_method(_do_stack_walk_cache,
                           vmClasses::AbstractStackWalker_klass(),
                           "doStackWalk",
                           vmSymbols::doStackWalk_signature(), false, CHECK);
+
+  // Set up substitutability testing
+  ResourceMark rm;
+  initialize_known_method(_is_substitutable_cache,
+                          vmClasses::ValueObjectMethods_klass(),
+                          vmSymbols::isSubstitutable_name()->as_C_string(),
+                          vmSymbols::object_object_boolean_signature(), true, CHECK);
+  initialize_known_method(_value_object_hash_code_cache,
+                          vmClasses::ValueObjectMethods_klass(),
+                          vmSymbols::valueObjectHashCode_name()->as_C_string(),
+                          vmSymbols::object_int_signature(), true, CHECK);
 }
 
 void universe2_init() {
   EXCEPTION_MARK;
   Universe::genesis(CATCH);
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
@@ -118,10 +118,12 @@
   static LatestMethodCache* _finalizer_register_cache; // static method for registering finalizable objects
   static LatestMethodCache* _loader_addClass_cache;    // method for registering loaded classes in class loader vector
   static LatestMethodCache* _throw_illegal_access_error_cache; // Unsafe.throwIllegalAccessError() method
   static LatestMethodCache* _throw_no_such_method_error_cache; // Unsafe.throwNoSuchMethodError() method
   static LatestMethodCache* _do_stack_walk_cache;      // method for stack walker callback
+  static LatestMethodCache* _is_substitutable_cache;   // ValueObjectMethods.isSubstitutable() method
+  static LatestMethodCache* _value_object_hash_code_cache;  // ValueObjectMethods.valueObjectHashCode() method
 
   static Array<int>*            _the_empty_int_array;            // Canonicalized int array
   static Array<u2>*             _the_empty_short_array;          // Canonicalized short array
   static Array<Klass*>*         _the_empty_klass_array;          // Canonicalized klass array
   static Array<InstanceKlass*>* _the_empty_instance_klass_array; // Canonicalized instance klass array
@@ -149,10 +151,11 @@
   // The particular choice of collected heap.
   static CollectedHeap* _collectedHeap;
 
   static intptr_t _non_oop_bits;
 
+
   // array of dummy objects used with +FullGCAlot
   debug_only(static OopHandle   _fullgc_alot_dummy_array;)
   debug_only(static int         _fullgc_alot_dummy_next;)
 
   // Compiler/dispatch support
@@ -272,10 +275,13 @@
   static Method*      throw_illegal_access_error()    { return _throw_illegal_access_error_cache->get_method(); }
   static Method*      throw_no_such_method_error()    { return _throw_no_such_method_error_cache->get_method(); }
 
   static Method*      do_stack_walk_method()          { return _do_stack_walk_cache->get_method(); }
 
+  static Method*      is_substitutable_method()       { return _is_substitutable_cache->get_method(); }
+  static Method*      value_object_hash_code_method() { return _value_object_hash_code_cache->get_method(); }
+
   static oop          the_null_sentinel();
   static address      the_null_sentinel_addr()        { return (address) &_the_null_sentinel;  }
 
   // Function to initialize these
   static void initialize_known_methods(TRAPS);
diff a/src/hotspot/share/oops/access.hpp b/src/hotspot/share/oops/access.hpp
--- a/src/hotspot/share/oops/access.hpp
+++ b/src/hotspot/share/oops/access.hpp
@@ -55,10 +55,11 @@
 // * atomic_cmpxchg_at: Atomically compare-and-swap a new value at an internal pointer address if previous value matched the compared value.
 // * atomic_xchg: Atomically swap a new value at an address without checking the previous value.
 // * atomic_xchg_at: Atomically swap a new value at an internal pointer address without checking the previous value.
 // * arraycopy: Copy data from one heap array to another heap array. The ArrayAccess class has convenience functions for this.
 // * clone: Clone the contents of an object to a newly allocated object.
+// * value_copy: Copy the contents of a value type from one heap address to another
 //
 // == IMPLEMENTATION ==
 // Each access goes through the following steps in a template pipeline.
 // There are essentially 5 steps for each access:
 // * Step 1:   Set default decorators and decay types. This step gets rid of CV qualifiers
@@ -87,10 +88,12 @@
 // accesses to be accessible from only access.hpp, as opposed to access.inline.hpp.
 // Steps 5.a and 5.b require knowledge about the GC backends, and therefore needs to
 // include the various GC backend .inline.hpp headers. Their implementation resides in
 // access.inline.hpp.
 
+class InlineKlass;
+
 template <DecoratorSet decorators = DECORATORS_NONE>
 class Access: public AllStatic {
   // This function asserts that if an access gets passed in a decorator outside
   // of the expected_decorators, then something is wrong. It additionally checks
   // the consistency of the decorators so that supposedly disjoint decorators are indeed
@@ -119,25 +122,31 @@
     const DecoratorSet heap_oop_decorators = AS_DECORATOR_MASK | ON_DECORATOR_MASK |
                                              IN_HEAP | IS_ARRAY | IS_NOT_NULL | IS_DEST_UNINITIALIZED;
     verify_decorators<expected_mo_decorators | heap_oop_decorators>();
   }
 
+  template <DecoratorSet expected_mo_decorators>
+  static void verify_heap_value_decorators() {
+    const DecoratorSet heap_value_decorators = IN_HEAP | IS_DEST_UNINITIALIZED;
+    verify_decorators<expected_mo_decorators | heap_value_decorators>();
+  }
+
   static const DecoratorSet load_mo_decorators = MO_UNORDERED | MO_RELAXED | MO_ACQUIRE | MO_SEQ_CST;
   static const DecoratorSet store_mo_decorators = MO_UNORDERED | MO_RELAXED | MO_RELEASE | MO_SEQ_CST;
   static const DecoratorSet atomic_xchg_mo_decorators = MO_SEQ_CST;
   static const DecoratorSet atomic_cmpxchg_mo_decorators = MO_RELAXED | MO_SEQ_CST;
 
 protected:
   template <typename T>
-  static inline bool oop_arraycopy(arrayOop src_obj, size_t src_offset_in_bytes, const T* src_raw,
+  static inline void oop_arraycopy(arrayOop src_obj, size_t src_offset_in_bytes, const T* src_raw,
                                    arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                                    size_t length) {
     verify_decorators<ARRAYCOPY_DECORATOR_MASK | IN_HEAP |
                       AS_DECORATOR_MASK | IS_ARRAY | IS_DEST_UNINITIALIZED>();
-    return AccessInternal::arraycopy<decorators | INTERNAL_VALUE_IS_OOP>(src_obj, src_offset_in_bytes, src_raw,
-                                                                         dst_obj, dst_offset_in_bytes, dst_raw,
-                                                                         length);
+    AccessInternal::arraycopy<decorators | INTERNAL_VALUE_IS_OOP>(src_obj, src_offset_in_bytes, src_raw,
+                                                                  dst_obj, dst_offset_in_bytes, dst_raw,
+                                                                  length);
   }
 
   template <typename T>
   static inline void arraycopy(arrayOop src_obj, size_t src_offset_in_bytes, const T* src_raw,
                                arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
@@ -209,10 +218,18 @@
   static inline void clone(oop src, oop dst, size_t size) {
     verify_decorators<IN_HEAP>();
     AccessInternal::clone<decorators>(src, dst, size);
   }
 
+  // inline type heap access (when flat)...
+
+  // Copy value type data from src to dst
+  static inline void value_copy(void* src, void* dst, InlineKlass* md) {
+    verify_heap_value_decorators<IN_HEAP>();
+    AccessInternal::value_copy<decorators>(src, dst, md);
+  }
+
   // Primitive accesses
   template <typename P>
   static inline P load(P* addr) {
     verify_primitive_decorators<load_mo_decorators>();
     return AccessInternal::load<decorators, P, P>(addr);
@@ -319,23 +336,23 @@
     AccessT::arraycopy(nullptr, 0, src,
                        dst_obj, dst_offset_in_bytes, static_cast<T*>(nullptr),
                        length);
   }
 
-  static inline bool oop_arraycopy(arrayOop src_obj, size_t src_offset_in_bytes,
+  static inline void oop_arraycopy(arrayOop src_obj, size_t src_offset_in_bytes,
                                    arrayOop dst_obj, size_t dst_offset_in_bytes,
                                    size_t length) {
-    return AccessT::oop_arraycopy(src_obj, src_offset_in_bytes, static_cast<const HeapWord*>(nullptr),
-                                  dst_obj, dst_offset_in_bytes, static_cast<HeapWord*>(nullptr),
-                                  length);
+    AccessT::oop_arraycopy(src_obj, src_offset_in_bytes, static_cast<const HeapWord*>(nullptr),
+                           dst_obj, dst_offset_in_bytes, static_cast<HeapWord*>(nullptr),
+                           length);
   }
 
   template <typename T>
-  static inline bool oop_arraycopy_raw(T* src, T* dst, size_t length) {
-    return AccessT::oop_arraycopy(nullptr, 0, src,
-                                  nullptr, 0, dst,
-                                  length);
+  static inline void oop_arraycopy_raw(T* src, T* dst, size_t length) {
+    AccessT::oop_arraycopy(nullptr, 0, src,
+                           nullptr, 0, dst,
+                           length);
   }
 
 };
 
 template <DecoratorSet decorators>
diff a/src/hotspot/share/oops/access.inline.hpp b/src/hotspot/share/oops/access.inline.hpp
--- a/src/hotspot/share/oops/access.inline.hpp
+++ b/src/hotspot/share/oops/access.inline.hpp
@@ -123,27 +123,26 @@
   };
 
   template <class GCBarrierType, DecoratorSet decorators>
   struct PostRuntimeDispatch<GCBarrierType, BARRIER_ARRAYCOPY, decorators>: public AllStatic {
     template <typename T>
-    static bool access_barrier(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
+    static void access_barrier(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
                                arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                                size_t length) {
       GCBarrierType::arraycopy_in_heap(src_obj, src_offset_in_bytes, src_raw,
                                        dst_obj, dst_offset_in_bytes, dst_raw,
                                        length);
-      return true;
     }
 
     template <typename T>
-    static bool oop_access_barrier(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
+    static void oop_access_barrier(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
                                    arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                                    size_t length) {
       typedef typename HeapOopType<decorators>::type OopType;
-      return GCBarrierType::oop_arraycopy_in_heap(src_obj, src_offset_in_bytes, reinterpret_cast<OopType*>(src_raw),
-                                                  dst_obj, dst_offset_in_bytes, reinterpret_cast<OopType*>(dst_raw),
-                                                  length);
+      GCBarrierType::oop_arraycopy_in_heap(src_obj, src_offset_in_bytes, reinterpret_cast<OopType*>(src_raw),
+                                           dst_obj, dst_offset_in_bytes, reinterpret_cast<OopType*>(dst_raw),
+                                           length);
     }
   };
 
   template <class GCBarrierType, DecoratorSet decorators>
   struct PostRuntimeDispatch<GCBarrierType, BARRIER_STORE_AT, decorators>: public AllStatic {
@@ -198,10 +197,17 @@
     static void access_barrier(oop src, oop dst, size_t size) {
       GCBarrierType::clone_in_heap(src, dst, size);
     }
   };
 
+  template <class GCBarrierType, DecoratorSet decorators>
+  struct PostRuntimeDispatch<GCBarrierType, BARRIER_VALUE_COPY, decorators>: public AllStatic {
+    static void access_barrier(void* src, void* dst, InlineKlass* md) {
+      GCBarrierType::value_copy_in_heap(src, dst, md);
+    }
+  };
+
   // Resolving accessors with barriers from the barrier set happens in two steps.
   // 1. Expand paths with runtime-decorators, e.g. is UseCompressedOops on or off.
   // 2. Expand paths for each BarrierSet available in the system.
   template <DecoratorSet decorators, typename FunctionPointerT, BarrierType barrier_type>
   struct BarrierResolver: public AllStatic {
@@ -329,24 +335,31 @@
     _atomic_xchg_at_func = function;
     return function(base, offset, new_value);
   }
 
   template <DecoratorSet decorators, typename T>
-  bool RuntimeDispatch<decorators, T, BARRIER_ARRAYCOPY>::arraycopy_init(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
+  void RuntimeDispatch<decorators, T, BARRIER_ARRAYCOPY>::arraycopy_init(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
                                                                          arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                                                                          size_t length) {
     func_t function = BarrierResolver<decorators, func_t, BARRIER_ARRAYCOPY>::resolve_barrier();
     _arraycopy_func = function;
-    return function(src_obj, src_offset_in_bytes, src_raw,
-                    dst_obj, dst_offset_in_bytes, dst_raw,
-                    length);
+    function(src_obj, src_offset_in_bytes, src_raw,
+             dst_obj, dst_offset_in_bytes, dst_raw,
+             length);
   }
 
   template <DecoratorSet decorators, typename T>
   void RuntimeDispatch<decorators, T, BARRIER_CLONE>::clone_init(oop src, oop dst, size_t size) {
     func_t function = BarrierResolver<decorators, func_t, BARRIER_CLONE>::resolve_barrier();
     _clone_func = function;
     function(src, dst, size);
   }
+
+  template <DecoratorSet decorators, typename T>
+  void RuntimeDispatch<decorators, T, BARRIER_VALUE_COPY>::value_copy_init(void* src, void* dst, InlineKlass* md) {
+    func_t function = BarrierResolver<decorators, func_t, BARRIER_VALUE_COPY>::resolve_barrier();
+    _value_copy_func = function;
+    function(src, dst, md);
+  }
 }
 
 #endif // SHARE_OOPS_ACCESS_INLINE_HPP
diff a/src/hotspot/share/oops/accessBackend.hpp b/src/hotspot/share/oops/accessBackend.hpp
--- a/src/hotspot/share/oops/accessBackend.hpp
+++ b/src/hotspot/share/oops/accessBackend.hpp
@@ -43,10 +43,18 @@
   static const bool needs_oop_compress = HasDecorator<decorators, INTERNAL_CONVERT_COMPRESSED_OOP>::value &&
                                          HasDecorator<decorators, INTERNAL_RT_USE_COMPRESSED_OOPS>::value;
   using type = std::conditional_t<needs_oop_compress, narrowOop, oop>;
 };
 
+// This meta-function returns either oop or narrowOop depending on whether
+// a back-end needs to consider compressed oops types or not.
+template <DecoratorSet decorators>
+struct ValueOopType: AllStatic {
+  static const bool needs_oop_compress = HasDecorator<decorators, INTERNAL_RT_USE_COMPRESSED_OOPS>::value;
+  using type = std::conditional_t<needs_oop_compress, narrowOop, oop>;
+};
+
 namespace AccessInternal {
   enum BarrierType {
     BARRIER_STORE,
     BARRIER_STORE_AT,
     BARRIER_LOAD,
@@ -54,11 +62,12 @@
     BARRIER_ATOMIC_CMPXCHG,
     BARRIER_ATOMIC_CMPXCHG_AT,
     BARRIER_ATOMIC_XCHG,
     BARRIER_ATOMIC_XCHG_AT,
     BARRIER_ARRAYCOPY,
-    BARRIER_CLONE
+    BARRIER_CLONE,
+    BARRIER_VALUE_COPY
   };
 
   template <DecoratorSet decorators, typename T>
   struct MustConvertCompressedOop: public std::integral_constant<bool,
     HasDecorator<decorators, INTERNAL_VALUE_IS_OOP>::value &&
@@ -91,19 +100,20 @@
     typedef T (*load_func_t)(void* addr);
     typedef void (*store_func_t)(void* addr, T value);
     typedef T (*atomic_cmpxchg_func_t)(void* addr, T compare_value, T new_value);
     typedef T (*atomic_xchg_func_t)(void* addr, T new_value);
 
-    typedef bool (*arraycopy_func_t)(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
+    typedef void (*arraycopy_func_t)(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
                                      arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                                      size_t length);
     typedef void (*clone_func_t)(oop src, oop dst, size_t size);
+    typedef void (*value_copy_func_t)(void* src, void* dst, InlineKlass* md);
   };
 
   template <DecoratorSet decorators>
   struct AccessFunctionTypes<decorators, void> {
-    typedef bool (*arraycopy_func_t)(arrayOop src_obj, size_t src_offset_in_bytes, void* src,
+    typedef void (*arraycopy_func_t)(arrayOop src_obj, size_t src_offset_in_bytes, void* src,
                                      arrayOop dst_obj, size_t dst_offset_in_bytes, void* dst,
                                      size_t length);
   };
 
   template <DecoratorSet decorators, typename T, BarrierType barrier> struct AccessFunction {};
@@ -121,10 +131,11 @@
   ACCESS_GENERATE_ACCESS_FUNCTION(BARRIER_ATOMIC_CMPXCHG_AT, atomic_cmpxchg_at_func_t);
   ACCESS_GENERATE_ACCESS_FUNCTION(BARRIER_ATOMIC_XCHG, atomic_xchg_func_t);
   ACCESS_GENERATE_ACCESS_FUNCTION(BARRIER_ATOMIC_XCHG_AT, atomic_xchg_at_func_t);
   ACCESS_GENERATE_ACCESS_FUNCTION(BARRIER_ARRAYCOPY, arraycopy_func_t);
   ACCESS_GENERATE_ACCESS_FUNCTION(BARRIER_CLONE, clone_func_t);
+  ACCESS_GENERATE_ACCESS_FUNCTION(BARRIER_VALUE_COPY, value_copy_func_t);
 #undef ACCESS_GENERATE_ACCESS_FUNCTION
 
   template <DecoratorSet decorators, typename T, BarrierType barrier_type>
   typename AccessFunction<decorators, T, barrier_type>::type resolve_barrier();
 
@@ -285,11 +296,11 @@
   static inline T atomic_xchg(void* addr, T new_value) {
     return atomic_xchg_internal<decorators>(addr, new_value);
   }
 
   template <typename T>
-  static bool arraycopy(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
+  static void arraycopy(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
                         arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                         size_t length);
 
   template <typename T>
   static void oop_store(void* addr, T value);
@@ -330,15 +341,17 @@
   static T atomic_xchg_at(oop base, ptrdiff_t offset, T new_value) {
     return atomic_xchg(field_addr(base, offset), new_value);
   }
 
   template <typename T>
-  static bool oop_arraycopy(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
+  static void oop_arraycopy(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
                             arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                             size_t length);
 
   static void clone(oop src, oop dst, size_t size);
+  static void value_copy(void* src, void* dst, InlineKlass* md);
+
 };
 
 namespace AccessInternal {
   DEBUG_ONLY(void check_access_thread_state());
 #define assert_access_thread_state() DEBUG_ONLY(check_access_thread_state())
@@ -504,15 +517,15 @@
   template <DecoratorSet decorators, typename T>
   struct RuntimeDispatch<decorators, T, BARRIER_ARRAYCOPY>: AllStatic {
     typedef typename AccessFunction<decorators, T, BARRIER_ARRAYCOPY>::type func_t;
     static func_t _arraycopy_func;
 
-    static bool arraycopy_init(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
+    static void arraycopy_init(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
                                arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                                size_t length);
 
-    static inline bool arraycopy(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
+    static inline void arraycopy(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
                                  arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                                  size_t length) {
       assert_access_thread_state();
       return _arraycopy_func(src_obj, src_offset_in_bytes, src_raw,
                              dst_obj, dst_offset_in_bytes, dst_raw,
@@ -531,10 +544,22 @@
       assert_access_thread_state();
       _clone_func(src, dst, size);
     }
   };
 
+  template <DecoratorSet decorators, typename T>
+  struct RuntimeDispatch<decorators, T, BARRIER_VALUE_COPY>: AllStatic {
+    typedef typename AccessFunction<decorators, T, BARRIER_VALUE_COPY>::type func_t;
+    static func_t _value_copy_func;
+
+    static void value_copy_init(void* src, void* dst, InlineKlass* md);
+
+    static inline void value_copy(void* src, void* dst, InlineKlass* md) {
+      _value_copy_func(src, dst, md);
+    }
+  };
+
   // Initialize the function pointers to point to the resolving function.
   template <DecoratorSet decorators, typename T>
   typename AccessFunction<decorators, T, BARRIER_STORE>::type
   RuntimeDispatch<decorators, T, BARRIER_STORE>::_store_func = &store_init;
 
@@ -572,10 +597,14 @@
 
   template <DecoratorSet decorators, typename T>
   typename AccessFunction<decorators, T, BARRIER_CLONE>::type
   RuntimeDispatch<decorators, T, BARRIER_CLONE>::_clone_func = &clone_init;
 
+  template <DecoratorSet decorators, typename T>
+  typename AccessFunction<decorators, T, BARRIER_VALUE_COPY>::type
+  RuntimeDispatch<decorators, T, BARRIER_VALUE_COPY>::_value_copy_func = &value_copy_init;
+
   // Step 3: Pre-runtime dispatching.
   // The PreRuntimeDispatch class is responsible for filtering the barrier strength
   // decorators. That is, for AS_RAW, it hardwires the accesses without a runtime
   // dispatch point. Otherwise it goes through a runtime check if hardwiring was
   // not possible.
@@ -819,60 +848,60 @@
       }
     }
 
     template <DecoratorSet decorators, typename T>
     inline static typename EnableIf<
-      HasDecorator<decorators, AS_RAW>::value && CanHardwireRaw<decorators>::value, bool>::type
+      HasDecorator<decorators, AS_RAW>::value && CanHardwireRaw<decorators>::value, void>::type
     arraycopy(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
               arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
               size_t length) {
       typedef RawAccessBarrier<decorators & RAW_DECORATOR_MASK> Raw;
       if (HasDecorator<decorators, INTERNAL_VALUE_IS_OOP>::value) {
-        return Raw::oop_arraycopy(src_obj, src_offset_in_bytes, src_raw,
-                                  dst_obj, dst_offset_in_bytes, dst_raw,
-                                  length);
+        Raw::oop_arraycopy(src_obj, src_offset_in_bytes, src_raw,
+                           dst_obj, dst_offset_in_bytes, dst_raw,
+                           length);
       } else {
-        return Raw::arraycopy(src_obj, src_offset_in_bytes, src_raw,
-                              dst_obj, dst_offset_in_bytes, dst_raw,
-                              length);
+        Raw::arraycopy(src_obj, src_offset_in_bytes, src_raw,
+                       dst_obj, dst_offset_in_bytes, dst_raw,
+                       length);
       }
     }
 
     template <DecoratorSet decorators, typename T>
     inline static typename EnableIf<
-      HasDecorator<decorators, AS_RAW>::value && !CanHardwireRaw<decorators>::value, bool>::type
+      HasDecorator<decorators, AS_RAW>::value && !CanHardwireRaw<decorators>::value, void>::type
     arraycopy(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
               arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
               size_t length) {
       if (UseCompressedOops) {
         const DecoratorSet expanded_decorators = decorators | convert_compressed_oops;
-        return PreRuntimeDispatch::arraycopy<expanded_decorators>(src_obj, src_offset_in_bytes, src_raw,
-                                                                  dst_obj, dst_offset_in_bytes, dst_raw,
-                                                                  length);
+        PreRuntimeDispatch::arraycopy<expanded_decorators>(src_obj, src_offset_in_bytes, src_raw,
+                                                           dst_obj, dst_offset_in_bytes, dst_raw,
+                                                           length);
       } else {
         const DecoratorSet expanded_decorators = decorators & ~convert_compressed_oops;
-        return PreRuntimeDispatch::arraycopy<expanded_decorators>(src_obj, src_offset_in_bytes, src_raw,
-                                                                  dst_obj, dst_offset_in_bytes, dst_raw,
-                                                                  length);
+        PreRuntimeDispatch::arraycopy<expanded_decorators>(src_obj, src_offset_in_bytes, src_raw,
+                                                           dst_obj, dst_offset_in_bytes, dst_raw,
+                                                           length);
       }
     }
 
     template <DecoratorSet decorators, typename T>
     inline static typename EnableIf<
-      !HasDecorator<decorators, AS_RAW>::value, bool>::type
+      !HasDecorator<decorators, AS_RAW>::value, void>::type
     arraycopy(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
               arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
               size_t length) {
       if (is_hardwired_primitive<decorators>()) {
         const DecoratorSet expanded_decorators = decorators | AS_RAW;
-        return PreRuntimeDispatch::arraycopy<expanded_decorators>(src_obj, src_offset_in_bytes, src_raw,
-                                                                  dst_obj, dst_offset_in_bytes, dst_raw,
-                                                                  length);
+        PreRuntimeDispatch::arraycopy<expanded_decorators>(src_obj, src_offset_in_bytes, src_raw,
+                                                           dst_obj, dst_offset_in_bytes, dst_raw,
+                                                           length);
       } else {
-        return RuntimeDispatch<decorators, T, BARRIER_ARRAYCOPY>::arraycopy(src_obj, src_offset_in_bytes, src_raw,
-                                                                            dst_obj, dst_offset_in_bytes, dst_raw,
-                                                                            length);
+        RuntimeDispatch<decorators, T, BARRIER_ARRAYCOPY>::arraycopy(src_obj, src_offset_in_bytes, src_raw,
+                                                                     dst_obj, dst_offset_in_bytes, dst_raw,
+                                                                     length);
       }
     }
 
     template <DecoratorSet decorators>
     inline static typename EnableIf<
@@ -886,10 +915,26 @@
     inline static typename EnableIf<
       !HasDecorator<decorators, AS_RAW>::value>::type
     clone(oop src, oop dst, size_t size) {
       RuntimeDispatch<decorators, oop, BARRIER_CLONE>::clone(src, dst, size);
     }
+
+    template <DecoratorSet decorators>
+    inline static typename EnableIf<
+      HasDecorator<decorators, AS_RAW>::value>::type
+    value_copy(void* src, void* dst, InlineKlass* md) {
+      typedef RawAccessBarrier<decorators & RAW_DECORATOR_MASK> Raw;
+      Raw::value_copy(src, dst, md);
+    }
+
+    template <DecoratorSet decorators>
+    inline static typename EnableIf<
+      !HasDecorator<decorators, AS_RAW>::value>::type
+      value_copy(void* src, void* dst, InlineKlass* md) {
+      const DecoratorSet expanded_decorators = decorators;
+      RuntimeDispatch<expanded_decorators, void*, BARRIER_VALUE_COPY>::value_copy(src, dst, md);
+    }
   };
 
   // Step 2: Reduce types.
   // Enforce that for non-oop types, T and P have to be strictly the same.
   // P is the type of the address and T is the type of the values.
@@ -1000,37 +1045,37 @@
     const DecoratorSet expanded_decorators = decorators | INTERNAL_CONVERT_COMPRESSED_OOP;
     return PreRuntimeDispatch::load<expanded_decorators, oop>(addr);
   }
 
   template <DecoratorSet decorators, typename T>
-  inline bool arraycopy_reduce_types(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
+  inline void arraycopy_reduce_types(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
                                      arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                                      size_t length) {
-    return PreRuntimeDispatch::arraycopy<decorators>(src_obj, src_offset_in_bytes, src_raw,
-                                                     dst_obj, dst_offset_in_bytes, dst_raw,
-                                                     length);
+    PreRuntimeDispatch::arraycopy<decorators>(src_obj, src_offset_in_bytes, src_raw,
+                                              dst_obj, dst_offset_in_bytes, dst_raw,
+                                              length);
   }
 
   template <DecoratorSet decorators>
-  inline bool arraycopy_reduce_types(arrayOop src_obj, size_t src_offset_in_bytes, HeapWord* src_raw,
+  inline void arraycopy_reduce_types(arrayOop src_obj, size_t src_offset_in_bytes, HeapWord* src_raw,
                                      arrayOop dst_obj, size_t dst_offset_in_bytes, HeapWord* dst_raw,
                                      size_t length) {
     const DecoratorSet expanded_decorators = decorators | INTERNAL_CONVERT_COMPRESSED_OOP;
-    return PreRuntimeDispatch::arraycopy<expanded_decorators>(src_obj, src_offset_in_bytes, src_raw,
-                                                              dst_obj, dst_offset_in_bytes, dst_raw,
-                                                              length);
+    PreRuntimeDispatch::arraycopy<expanded_decorators>(src_obj, src_offset_in_bytes, src_raw,
+                                                       dst_obj, dst_offset_in_bytes, dst_raw,
+                                                       length);
   }
 
   template <DecoratorSet decorators>
-  inline bool arraycopy_reduce_types(arrayOop src_obj, size_t src_offset_in_bytes, narrowOop* src_raw,
+  inline void arraycopy_reduce_types(arrayOop src_obj, size_t src_offset_in_bytes, narrowOop* src_raw,
                                      arrayOop dst_obj, size_t dst_offset_in_bytes, narrowOop* dst_raw,
                                      size_t length) {
     const DecoratorSet expanded_decorators = decorators | INTERNAL_CONVERT_COMPRESSED_OOP |
                                              INTERNAL_RT_USE_COMPRESSED_OOPS;
-    return PreRuntimeDispatch::arraycopy<expanded_decorators>(src_obj, src_offset_in_bytes, src_raw,
-                                                              dst_obj, dst_offset_in_bytes, dst_raw,
-                                                              length);
+    PreRuntimeDispatch::arraycopy<expanded_decorators>(src_obj, src_offset_in_bytes, src_raw,
+                                                       dst_obj, dst_offset_in_bytes, dst_raw,
+                                                       length);
   }
 
   // Step 1: Set default decorators. This step remembers if a type was volatile
   // and then sets the MO_RELAXED decorator by default. Otherwise, a default
   // memory ordering is set for the access, and the implied decorator rules
@@ -1159,29 +1204,35 @@
                                               INTERNAL_CONVERT_COMPRESSED_OOP : DECORATORS_NONE)>::value;
     return PreRuntimeDispatch::atomic_xchg_at<expanded_decorators>(base, offset, new_decayed_value);
   }
 
   template <DecoratorSet decorators, typename T>
-  inline bool arraycopy(arrayOop src_obj, size_t src_offset_in_bytes, const T* src_raw,
+  inline void arraycopy(arrayOop src_obj, size_t src_offset_in_bytes, const T* src_raw,
                         arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                         size_t length) {
     STATIC_ASSERT((HasDecorator<decorators, INTERNAL_VALUE_IS_OOP>::value ||
                    (std::is_same<T, void>::value || std::is_integral<T>::value) ||
                     std::is_floating_point<T>::value)); // arraycopy allows type erased void elements
     using DecayedT = std::decay_t<T>;
     const DecoratorSet expanded_decorators = DecoratorFixup<decorators | IS_ARRAY | IN_HEAP>::value;
-    return arraycopy_reduce_types<expanded_decorators>(src_obj, src_offset_in_bytes, const_cast<DecayedT*>(src_raw),
-                                                       dst_obj, dst_offset_in_bytes, const_cast<DecayedT*>(dst_raw),
-                                                       length);
+    arraycopy_reduce_types<expanded_decorators>(src_obj, src_offset_in_bytes, const_cast<DecayedT*>(src_raw),
+                                                dst_obj, dst_offset_in_bytes, const_cast<DecayedT*>(dst_raw),
+                                                length);
   }
 
   template <DecoratorSet decorators>
   inline void clone(oop src, oop dst, size_t size) {
     const DecoratorSet expanded_decorators = DecoratorFixup<decorators>::value;
     PreRuntimeDispatch::clone<expanded_decorators>(src, dst, size);
   }
 
+  template <DecoratorSet decorators>
+  inline void value_copy(void* src, void* dst, InlineKlass* md) {
+    const DecoratorSet expanded_decorators = DecoratorFixup<decorators>::value;
+    PreRuntimeDispatch::value_copy<expanded_decorators>(src, dst, md);
+  }
+
   // Infer the type that should be returned from an Access::oop_load.
   template <typename P, DecoratorSet decorators>
   class OopLoadProxy: public StackObj {
   private:
     P *const _addr;
diff a/src/hotspot/share/oops/accessBackend.inline.hpp b/src/hotspot/share/oops/accessBackend.inline.hpp
--- a/src/hotspot/share/oops/accessBackend.inline.hpp
+++ b/src/hotspot/share/oops/accessBackend.inline.hpp
@@ -31,10 +31,11 @@
 #include "oops/arrayOop.hpp"
 #include "oops/compressedOops.inline.hpp"
 #include "oops/oopsHierarchy.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/orderAccess.hpp"
+#include "oops/inlineKlass.hpp"
 
 #include <type_traits>
 
 template <DecoratorSet decorators>
 template <DecoratorSet idecorators, typename T>
@@ -122,16 +123,16 @@
   return oop_atomic_xchg(field_addr(base, offset), new_value);
 }
 
 template <DecoratorSet decorators>
 template <typename T>
-inline bool RawAccessBarrier<decorators>::oop_arraycopy(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
+inline void RawAccessBarrier<decorators>::oop_arraycopy(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
                                                         arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                                                         size_t length) {
-  return arraycopy(src_obj, src_offset_in_bytes, src_raw,
-                   dst_obj, dst_offset_in_bytes, dst_raw,
-                   length);
+  arraycopy(src_obj, src_offset_in_bytes, src_raw,
+            dst_obj, dst_offset_in_bytes, dst_raw,
+            length);
 }
 
 template <DecoratorSet decorators>
 template <DecoratorSet ds, typename T>
 inline typename EnableIf<
@@ -300,17 +301,16 @@
 
 template<> struct RawAccessBarrierArrayCopy::IsHeapWordSized<void>: public std::false_type { };
 
 template <DecoratorSet decorators>
 template <typename T>
-inline bool RawAccessBarrier<decorators>::arraycopy(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
+inline void RawAccessBarrier<decorators>::arraycopy(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
                                                     arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
                                                     size_t length) {
   RawAccessBarrierArrayCopy::arraycopy<decorators>(src_obj, src_offset_in_bytes, src_raw,
                                                    dst_obj, dst_offset_in_bytes, dst_raw,
                                                    length);
-  return true;
 }
 
 template <DecoratorSet decorators>
 inline void RawAccessBarrier<decorators>::clone(oop src, oop dst, size_t size) {
   // 4839641 (4840070): We must do an oop-atomic copy, because if another thread
@@ -329,6 +329,11 @@
                                             align_object_size(size) / HeapWordsPerLong);
   // Clear the header
   dst->init_mark();
 }
 
+template <DecoratorSet decorators>
+inline void RawAccessBarrier<decorators>::value_copy(void* src, void* dst, InlineKlass* md) {
+  assert(is_aligned(src, md->get_alignment()) && is_aligned(dst, md->get_alignment()), "Unalign value_copy");
+  AccessInternal::arraycopy_conjoint_atomic(src, dst, static_cast<size_t>(md->get_payload_size_in_bytes()));
+}
 #endif // SHARE_OOPS_ACCESSBACKEND_INLINE_HPP
diff a/src/hotspot/share/oops/accessDecorators.hpp b/src/hotspot/share/oops/accessDecorators.hpp
--- a/src/hotspot/share/oops/accessDecorators.hpp
+++ b/src/hotspot/share/oops/accessDecorators.hpp
@@ -192,33 +192,36 @@
 // == Arraycopy Decorators ==
 // * ARRAYCOPY_CHECKCAST: This property means that the class of the objects in source
 //   are not guaranteed to be subclasses of the class of the destination array. This requires
 //   a check-cast barrier during the copying operation. If this is not set, it is assumed
 //   that the array is covariant: (the source array type is-a destination array type)
+// * ARRAYCOPY_NOTNULL: This property means that the source array may contain null elements
+//   but the destination does not allow null elements (i.e. throw NPE)
 // * ARRAYCOPY_DISJOINT: This property means that it is known that the two array ranges
 //   are disjoint.
 // * ARRAYCOPY_ARRAYOF: The copy is in the arrayof form.
 // * ARRAYCOPY_ATOMIC: The accesses have to be atomic over the size of its elements.
 // * ARRAYCOPY_ALIGNED: The accesses have to be aligned on a HeapWord.
 const DecoratorSet ARRAYCOPY_CHECKCAST            = UCONST64(1) << 24;
-const DecoratorSet ARRAYCOPY_DISJOINT             = UCONST64(1) << 25;
-const DecoratorSet ARRAYCOPY_ARRAYOF              = UCONST64(1) << 26;
-const DecoratorSet ARRAYCOPY_ATOMIC               = UCONST64(1) << 27;
-const DecoratorSet ARRAYCOPY_ALIGNED              = UCONST64(1) << 28;
-const DecoratorSet ARRAYCOPY_DECORATOR_MASK       = ARRAYCOPY_CHECKCAST | ARRAYCOPY_DISJOINT |
+const DecoratorSet ARRAYCOPY_NOTNULL              = UCONST64(1) << 25;
+const DecoratorSet ARRAYCOPY_DISJOINT             = UCONST64(1) << 26;
+const DecoratorSet ARRAYCOPY_ARRAYOF              = UCONST64(1) << 27;
+const DecoratorSet ARRAYCOPY_ATOMIC               = UCONST64(1) << 28;
+const DecoratorSet ARRAYCOPY_ALIGNED              = UCONST64(1) << 29;
+const DecoratorSet ARRAYCOPY_DECORATOR_MASK       = ARRAYCOPY_CHECKCAST | ARRAYCOPY_NOTNULL |
                                                     ARRAYCOPY_DISJOINT | ARRAYCOPY_ARRAYOF |
                                                     ARRAYCOPY_ATOMIC | ARRAYCOPY_ALIGNED;
 
 // == Resolve barrier decorators ==
 // * ACCESS_READ: Indicate that the resolved object is accessed read-only. This allows the GC
 //   backend to use weaker and more efficient barriers.
 // * ACCESS_WRITE: Indicate that the resolved object is used for write access.
-const DecoratorSet ACCESS_READ                    = UCONST64(1) << 29;
-const DecoratorSet ACCESS_WRITE                   = UCONST64(1) << 30;
+const DecoratorSet ACCESS_READ                    = UCONST64(1) << 30;
+const DecoratorSet ACCESS_WRITE                   = UCONST64(1) << 31;
 
 // Keep track of the last decorator.
-const DecoratorSet DECORATOR_LAST = UCONST64(1) << 30;
+const DecoratorSet DECORATOR_LAST = UCONST64(1) << 31;
 
 namespace AccessInternal {
   // This class adds implied decorators that follow according to decorator rules.
   // For example adding default reference strength and default memory ordering
   // semantics.
diff a/src/hotspot/share/oops/arrayKlass.cpp b/src/hotspot/share/oops/arrayKlass.cpp
--- a/src/hotspot/share/oops/arrayKlass.cpp
+++ b/src/hotspot/share/oops/arrayKlass.cpp
@@ -25,10 +25,11 @@
 #include "precompiled.hpp"
 #include "cds/cdsConfig.hpp"
 #include "cds/metaspaceShared.hpp"
 #include "classfile/javaClasses.hpp"
 #include "classfile/moduleEntry.hpp"
+#include "classfile/symbolTable.hpp"
 #include "classfile/vmClasses.hpp"
 #include "classfile/vmSymbols.hpp"
 #include "gc/shared/collectedHeap.inline.hpp"
 #include "jvmtifiles/jvmti.h"
 #include "memory/metaspaceClosure.hpp"
@@ -36,10 +37,11 @@
 #include "memory/universe.hpp"
 #include "oops/arrayKlass.inline.hpp"
 #include "oops/arrayOop.hpp"
 #include "oops/instanceKlass.hpp"
 #include "oops/klass.inline.hpp"
+#include "oops/objArrayKlass.hpp"
 #include "oops/objArrayOop.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/handles.inline.hpp"
 
 ArrayKlass::ArrayKlass() {
@@ -103,10 +105,29 @@
   set_is_cloneable(); // All arrays are considered to be cloneable (See JLS 20.1.5)
   JFR_ONLY(INIT_ID(this);)
   log_array_class_load(this);
 }
 
+Symbol* ArrayKlass::create_element_klass_array_name(Klass* element_klass, TRAPS) {
+  ResourceMark rm(THREAD);
+  Symbol* name = nullptr;
+  char *name_str = element_klass->name()->as_C_string();
+  int len = element_klass->name()->utf8_length();
+  char *new_str = NEW_RESOURCE_ARRAY(char, len + 4);
+  int idx = 0;
+  new_str[idx++] = JVM_SIGNATURE_ARRAY;
+  if (element_klass->is_instance_klass()) { // it could be an array or simple type
+    new_str[idx++] = JVM_SIGNATURE_CLASS;
+  }
+  memcpy(&new_str[idx], name_str, len * sizeof(char));
+  idx += len;
+  if (element_klass->is_instance_klass()) {
+    new_str[idx++] = JVM_SIGNATURE_ENDCLASS;
+  }
+  new_str[idx++] = '\0';
+  return SymbolTable::new_symbol(new_str);
+}
 
 // Initialization of vtables and mirror object is done separately from base_create_array_klass,
 // since a GC can happen. At this point all instance variables of the ArrayKlass must be setup.
 void ArrayKlass::complete_create_array_klass(ArrayKlass* k, Klass* super_klass, ModuleEntry* module_entry, TRAPS) {
   k->initialize_supers(super_klass, nullptr, CHECK);
@@ -138,11 +159,12 @@
       // Check if another thread beat us
       if (higher_dimension() == nullptr) {
 
         // Create multi-dim klass object and link them together
         ObjArrayKlass* ak =
-          ObjArrayKlass::allocate_objArray_klass(class_loader_data(), dim + 1, this, CHECK_NULL);
+          ObjArrayKlass::allocate_objArray_klass(class_loader_data(), dim + 1, this,
+                                                 false, CHECK_NULL);
         ak->set_lower_dimension(this);
         // use 'release' to pair with lock-free load
         release_set_higher_dimension(ak);
         assert(ak->is_objArray_klass(), "incorrect initialization of ObjArrayKlass");
       }
@@ -196,10 +218,14 @@
                                                                 /* do_zero */ true, CHECK_NULL);
   // initialization to null not necessary, area already cleared
   return o;
 }
 
+oop ArrayKlass::component_mirror() const {
+  return java_lang_Class::component_mirror(java_mirror());
+}
+
 jint ArrayKlass::compute_modifier_flags() const {
   return JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC;
 }
 
 // JVMTI support
diff a/src/hotspot/share/oops/arrayKlass.hpp b/src/hotspot/share/oops/arrayKlass.hpp
--- a/src/hotspot/share/oops/arrayKlass.hpp
+++ b/src/hotspot/share/oops/arrayKlass.hpp
@@ -41,17 +41,38 @@
   int      _dimension;         // This is n'th-dimensional array.
   ObjArrayKlass* volatile _higher_dimension;  // Refers the (n+1)'th-dimensional array (if present).
   ArrayKlass* volatile    _lower_dimension;   // Refers the (n-1)'th-dimensional array (if present).
 
  protected:
+  Klass* _element_klass;            // The klass of the elements of this array type
+                                    // The element type must be registered for both object arrays
+                                    // (incl. object arrays with value type elements) and value type
+                                    // arrays containing flat value types. However, the element
+                                    // type must not be registered for arrays of primitive types.
+                                    // TODO: Update the class hierarchy so that element klass appears
+                                    // only in array that contain non-primitive types.
   // Constructors
   // The constructor with the Symbol argument does the real array
   // initialization, the other is a dummy
   ArrayKlass(Symbol* name, KlassKind kind);
   ArrayKlass();
 
+  // Create array_name for element klass
+  static Symbol* create_element_klass_array_name(Klass* element_klass, TRAPS);
+
  public:
+  // Instance variables
+  virtual Klass* element_klass() const      { return _element_klass; }
+  virtual void set_element_klass(Klass* k)  { _element_klass = k; }
+
+  // Compiler/Interpreter offset
+  static ByteSize element_klass_offset() { return in_ByteSize(offset_of(ArrayKlass, _element_klass)); }
+
+  // Are loads and stores to this concrete array type atomic?
+  // Note that Object[] is naturally atomic, but its subtypes may not be.
+  virtual bool element_access_is_atomic() { return true; }
+
   // Testing operation
   DEBUG_ONLY(bool is_array_klass_slow() const { return true; })
 
   // Returns the ObjArrayKlass for n'th dimension.
   ArrayKlass* array_klass(int n, TRAPS);
@@ -106,10 +127,12 @@
   }
 
   GrowableArray<Klass*>* compute_secondary_supers(int num_extra_slots,
                                                   Array<InstanceKlass*>* transitive_interfaces);
 
+  oop component_mirror() const;
+
   // Sizing
   static int static_size(int header_size);
 
   virtual void metaspace_pointers_do(MetaspaceClosure* iter);
 
diff a/src/hotspot/share/oops/arrayOop.hpp b/src/hotspot/share/oops/arrayOop.hpp
--- a/src/hotspot/share/oops/arrayOop.hpp
+++ b/src/hotspot/share/oops/arrayOop.hpp
@@ -42,11 +42,11 @@
 class arrayOopDesc : public oopDesc {
   friend class VMStructs;
   friend class arrayOopDescTest;
 
   // Interpreter/Compiler offsets
-
+protected:
   // Header size computation.
   // The header is considered the oop part of this type plus the length.
   // Returns the aligned header_size_in_bytes.  This is not equivalent to
   // sizeof(arrayOopDesc) which should not appear in the code.
   static int header_size_in_bytes() {
@@ -69,11 +69,11 @@
 
   // Check whether an element of a typeArrayOop with the given type must be
   // aligned 0 mod 8.  The typeArrayOop itself must be aligned at least this
   // strongly.
   static bool element_type_should_be_aligned(BasicType type) {
-    return type == T_DOUBLE || type == T_LONG;
+    return type == T_DOUBLE || type == T_LONG || type == T_PRIMITIVE_OBJECT;
   }
 
  public:
   // The _length field is not declared in C++.  It is allocated after the
   // declared nonstatic fields in arrayOopDesc if not compressed, otherwise
diff a/src/hotspot/share/oops/constMethodFlags.hpp b/src/hotspot/share/oops/constMethodFlags.hpp
--- a/src/hotspot/share/oops/constMethodFlags.hpp
+++ b/src/hotspot/share/oops/constMethodFlags.hpp
@@ -58,10 +58,15 @@
    flag(is_scoped                 , 1 << 16) \
    flag(changes_current_thread    , 1 << 17) \
    flag(jvmti_mount_transition    , 1 << 18) \
    flag(deprecated                , 1 << 19) \
    flag(deprecated_for_removal    , 1 << 20) \
+   flag(has_scalarized_args       , 1 << 21) \
+   flag(has_scalarized_return     , 1 << 22) \
+   flag(c1_needs_stack_repair     , 1 << 23) \
+   flag(c2_needs_stack_repair     , 1 << 24) \
+   flag(mismatch                  , 1 << 25) \
    /* end of list */
 
 #define CM_FLAGS_ENUM_NAME(name, value)    _misc_##name = value,
   enum {
     CM_FLAGS_DO(CM_FLAGS_ENUM_NAME)
@@ -82,10 +87,14 @@
     _flags |= _misc_##name;  \
   }
   CM_FLAGS_DO(CM_FLAGS_GET_SET)
 #undef CM_FLAGS_GET_SET
 
+  static u4 has_scalarized_return_flag() {
+    return _misc_has_scalarized_return;
+  }
+
   int as_int() const { return _flags; }
   void print_on(outputStream* st) const;
 };
 
 #endif // SHARE_OOPS_CONSTMETHODFLAGS_HPP
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
@@ -49,10 +49,11 @@
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
 #include "oops/array.hpp"
 #include "oops/constantPool.inline.hpp"
 #include "oops/cpCache.inline.hpp"
+#include "oops/flatArrayKlass.hpp"
 #include "oops/instanceKlass.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "oops/objArrayOop.inline.hpp"
 #include "oops/oop.inline.hpp"
@@ -256,11 +257,11 @@
       break;
 #ifndef PRODUCT
     case JVM_CONSTANT_Class:
     case JVM_CONSTANT_UnresolvedClass:
     case JVM_CONSTANT_UnresolvedClassInError:
-      // All of these should have been reverted back to ClassIndex before calling
+      // All of these should have been reverted back to Unresolved before calling
       // this function.
       ShouldNotReachHere();
 #endif
     }
   }
@@ -500,10 +501,16 @@
                  k->external_name());
     }
   }
 }
 
+void check_is_inline_type(Klass* k, TRAPS) {
+  if (!k->is_inline_klass()) {
+    THROW(vmSymbols::java_lang_IncompatibleClassChangeError());
+  }
+}
+
 Klass* ConstantPool::klass_at_impl(const constantPoolHandle& this_cp, int cp_index,
                                    TRAPS) {
   JavaThread* javaThread = THREAD;
 
   // A resolved constantPool entry will contain a Klass*, otherwise a Symbol*.
@@ -538,27 +545,47 @@
   }
 
   HandleMark hm(THREAD);
   Handle mirror_handle;
   Symbol* name = this_cp->symbol_at(name_index);
+  bool inline_type_signature = false;
   Handle loader (THREAD, this_cp->pool_holder()->class_loader());
   Handle protection_domain (THREAD, this_cp->pool_holder()->protection_domain());
 
   Klass* k;
   {
     // Turn off the single stepping while doing class resolution
     JvmtiHideSingleStepping jhss(javaThread);
     k = SystemDictionary::resolve_or_fail(name, loader, protection_domain, true, THREAD);
   } //  JvmtiHideSingleStepping jhss(javaThread);
+  if (inline_type_signature) {
+    name->decrement_refcount();
+  }
 
   if (!HAS_PENDING_EXCEPTION) {
     // preserve the resolved klass from unloading
     mirror_handle = Handle(THREAD, k->java_mirror());
     // Do access check for klasses
     verify_constant_pool_resolve(this_cp, k, THREAD);
   }
 
+  if (!HAS_PENDING_EXCEPTION && inline_type_signature) {
+    check_is_inline_type(k, THREAD);
+  }
+
+  if (!HAS_PENDING_EXCEPTION) {
+    Klass* bottom_klass = nullptr;
+    if (k->is_objArray_klass()) {
+      bottom_klass = ObjArrayKlass::cast(k)->bottom_klass();
+      assert(bottom_klass != nullptr, "Should be set");
+      assert(bottom_klass->is_instance_klass() || bottom_klass->is_typeArray_klass(), "Sanity check");
+    } else if (k->is_flatArray_klass()) {
+      bottom_klass = FlatArrayKlass::cast(k)->element_klass();
+      assert(bottom_klass != nullptr, "Should be set");
+    }
+  }
+
   // Failed to resolve class. We must record the errors so that subsequent attempts
   // to resolve this constant pool entry fail with the same error (JVMS 5.4.3).
   if (HAS_PENDING_EXCEPTION) {
     save_and_throw_exception(this_cp, cp_index, constantTag(JVM_CONSTANT_UnresolvedClass), CHECK_NULL);
     // If CHECK_NULL above doesn't return the exception, that means that
diff a/src/hotspot/share/oops/constantPool.hpp b/src/hotspot/share/oops/constantPool.hpp
--- a/src/hotspot/share/oops/constantPool.hpp
+++ b/src/hotspot/share/oops/constantPool.hpp
@@ -267,11 +267,11 @@
   static ByteSize pool_holder_offset()  { return byte_offset_of(ConstantPool, _pool_holder); }
   static ByteSize resolved_klasses_offset()    { return byte_offset_of(ConstantPool, _resolved_klasses); }
 
   // Storing constants
 
-  // For temporary use while constructing constant pool
+  // For temporary use while constructing constant pool. Used during a retransform/class redefinition as well.
   void klass_index_at_put(int cp_index, int name_index) {
     tag_at_put(cp_index, JVM_CONSTANT_ClassIndex);
     *int_at_addr(cp_index) = name_index;
   }
 
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
@@ -206,10 +206,11 @@
       // case, the method gets reresolved with caller for each interface call
       // because the actual selected method may not be public.
       //
       // We set bytecode_2() to _invokevirtual.
       // See also interpreterRuntime.cpp. (8/25/2000)
+      invoke_code = Bytecodes::_invokevirtual;
     } else {
       assert(invoke_code == Bytecodes::_invokevirtual ||
              (invoke_code == Bytecodes::_invokeinterface &&
               ((method->is_private() ||
                 (method->is_final() && method->method_holder() == vmClasses::Object_klass())))),
@@ -221,11 +222,11 @@
         // We set bytecode_2() to _invokevirtual.
         method_entry->set_bytecode1(invoke_code);
       }
     }
     // set up for invokevirtual, even if linking for invokeinterface also:
-    method_entry->set_bytecode2(Bytecodes::_invokevirtual);
+    method_entry->set_bytecode2(invoke_code);
   } else {
     ShouldNotReachHere();
   }
 }
 
diff a/src/hotspot/share/oops/fieldInfo.cpp b/src/hotspot/share/oops/fieldInfo.cpp
--- a/src/hotspot/share/oops/fieldInfo.cpp
+++ b/src/hotspot/share/oops/fieldInfo.cpp
@@ -106,10 +106,13 @@
       assert(fi_ref->generic_signature_index() == fi.generic_signature_index(), "Must be");
     }
     if (fi_ref->field_flags().is_contended()) {
       assert(fi_ref->contended_group() == fi.contended_group(), "Must be");
     }
+    if (fi_ref->field_flags().has_null_marker()) {
+      assert(fi_ref->null_marker_offset() == fi.null_marker_offset(), "Must be");
+    }
   }
 #endif // ASSERT
 
   return fis;
 }
diff a/src/hotspot/share/oops/fieldInfo.hpp b/src/hotspot/share/oops/fieldInfo.hpp
--- a/src/hotspot/share/oops/fieldInfo.hpp
+++ b/src/hotspot/share/oops/fieldInfo.hpp
@@ -64,28 +64,34 @@
  public:
 
   class FieldFlags {
     friend class VMStructs;
     friend class JVMCIVMStructs;
+    friend class FieldDesc;
 
     // The ordering of this enum is totally internal.  More frequent
     // flags should come earlier than less frequent ones, because
     // earlier ones compress better.
     enum FieldFlagBitPosition {
+      _ff_null_free_inline_type,  // field's type is an inline type and the field is null free
+      _ff_flat,         // field is a flat field
       _ff_initialized,  // has ConstantValue initializer attribute
       _ff_injected,     // internal field injected by the JVM
       _ff_generic,      // has a generic signature
       _ff_stable,       // trust as stable b/c declared as @Stable
       _ff_contended,    // is contended, may have contention-group
+      _ff_null_marker,  // field has a null marker, optional section include the null marker offset
+      _ff_internal_null_marker // null marker is internal (inside the layout of a flat field)
     };
 
     // Some but not all of the flag bits signal the presence of an
     // additional 32-bit item in the field record.
     static const u4 _optional_item_bit_mask =
       flag_mask((int)_ff_initialized) |
       flag_mask((int)_ff_generic)     |
-      flag_mask((int)_ff_contended);
+      flag_mask((int)_ff_contended)   |
+      flag_mask((int)_ff_null_marker);
 
     // boilerplate:
     u4 _flags;
 
     bool test_flag(FieldFlagBitPosition pos) const {
@@ -104,20 +110,28 @@
     bool has_any_optionals() const {
       return (_flags & _optional_item_bit_mask) != 0;
     }
 
     bool is_initialized() const     { return test_flag(_ff_initialized); }
+    bool is_null_free_inline_type() const { return test_flag(_ff_null_free_inline_type); }
+    bool is_flat() const            { return test_flag(_ff_flat); }
     bool is_injected() const        { return test_flag(_ff_injected); }
     bool is_generic() const         { return test_flag(_ff_generic); }
     bool is_stable() const          { return test_flag(_ff_stable); }
     bool is_contended() const       { return test_flag(_ff_contended); }
+    bool has_null_marker() const    { return test_flag(_ff_null_marker); }
+    bool is_null_marker_internal() const {return test_flag(_ff_internal_null_marker); }
 
     void update_initialized(bool z) { update_flag(_ff_initialized, z); }
+    void update_null_free_inline_type(bool z) { update_flag(_ff_null_free_inline_type, z); }
+    void update_flat(bool z)        { update_flag(_ff_flat, z); }
     void update_injected(bool z)    { update_flag(_ff_injected, z); }
     void update_generic(bool z)     { update_flag(_ff_generic, z); }
     void update_stable(bool z)      { update_flag(_ff_stable, z); }
     void update_contended(bool z)   { update_flag(_ff_contended, z); }
+    void update_null_marker(bool z) { update_flag(_ff_null_marker, z); }
+    void update_internal_null_marker(bool z) { update_flag(_ff_internal_null_marker, z); }
   };
 
  private:
   // The following items are the unpacked bitwise information content
   // of a field record.  Per-field metadata extracted from the class
@@ -129,10 +143,11 @@
   u2 _name_index;               // index in CP of name
   u2 _signature_index;          // index in CP of descriptor
   u4 _offset;                   // offset in object layout
   AccessFlags _access_flags;    // access flags (JVM spec)
   FieldFlags _field_flags;      // VM defined flags (not JVM spec)
+  u4 _null_marker_offset;       // null marker offset for this field in the object layout
   u2 _initializer_index;        // index from ConstantValue attr (or 0)
   u2 _generic_signature_index;  // index from GenericSignature attr (or 0)
   u2 _contention_group;         // index from @Contended group item (or 0)
 
  public:
@@ -140,20 +155,22 @@
   FieldInfo() : _name_index(0),
                 _signature_index(0),
                 _offset(0),
                 _access_flags(AccessFlags(0)),
                 _field_flags(FieldFlags(0)),
+                _null_marker_offset(0),
                 _initializer_index(0),
                 _generic_signature_index(0),
                 _contention_group(0) { }
 
   FieldInfo(AccessFlags access_flags, u2 name_index, u2 signature_index, u2 initval_index, FieldInfo::FieldFlags fflags) :
             _name_index(name_index),
             _signature_index(signature_index),
             _offset(0),
             _access_flags(access_flags),
             _field_flags(fflags),
+            _null_marker_offset(0),
             _initializer_index(initval_index),
             _generic_signature_index(0),
             _contention_group(0) {
               if (initval_index != 0) {
                 _field_flags.update_initialized(true);
@@ -169,10 +186,12 @@
   u4 offset() const                          { return _offset; }
   void set_offset(u4 offset)                 { _offset = offset; }
   AccessFlags access_flags() const           { return _access_flags; }
   FieldFlags field_flags() const             { return _field_flags; }
   FieldFlags* field_flags_addr()             { return &_field_flags; }
+  u4 null_marker_offset() const              { return _null_marker_offset; }
+  void set_null_marker_offset(u4 offset)     { _null_marker_offset = offset; }
   u2 initializer_index() const               { return _initializer_index; }
   void set_initializer_index(u2 index)       { _initializer_index = index; }
   u2 generic_signature_index() const         { return _generic_signature_index; }
   void set_generic_signature_index(u2 index) { _generic_signature_index = index; }
   u2 contention_group() const                { return _contention_group; }
diff a/src/hotspot/share/oops/fieldInfo.inline.hpp b/src/hotspot/share/oops/fieldInfo.inline.hpp
--- a/src/hotspot/share/oops/fieldInfo.inline.hpp
+++ b/src/hotspot/share/oops/fieldInfo.inline.hpp
@@ -83,14 +83,18 @@
       _consumer->accept_uint(fi.generic_signature_index());
     }
     if (fi.field_flags().is_contended()) {
       _consumer->accept_uint(fi.contention_group());
     }
+    if (fi.field_flags().has_null_marker()) {
+      _consumer->accept_uint(fi.null_marker_offset());
+    }
   } else {
     assert(fi.initializer_index() == 0, "");
     assert(fi.generic_signature_index() == 0, "");
     assert(fi.contention_group() == 0, "");
+    assert(fi.null_marker_offset() == 0, "");
   }
 }
 
 
 inline FieldInfoReader::FieldInfoReader(const Array<u1>* fi)
@@ -117,21 +121,27 @@
   if (fi._field_flags.is_contended()) {
     fi._contention_group = checked_cast<u2>(next_uint());
   } else {
     fi._contention_group = 0;
   }
+  if (fi._field_flags.has_null_marker()) {
+    fi._null_marker_offset = next_uint();
+  } else {
+    fi._null_marker_offset = 0;
+  }
 }
 
 inline FieldInfoReader&  FieldInfoReader::skip_field_info() {
   _next_index++;
   const int name_sig_af_off = 4;  // four items
   skip(name_sig_af_off);
   FieldInfo::FieldFlags ff(next_uint());
   if (ff.has_any_optionals()) {
     const int init_gen_cont = (ff.is_initialized() +
                                 ff.is_generic() +
-                                ff.is_contended());
+                                ff.is_contended() +
+                                ff.has_null_marker());
     skip(init_gen_cont);  // up to three items
   }
   return *this;
 }
 
diff a/src/hotspot/share/oops/fieldStreams.hpp b/src/hotspot/share/oops/fieldStreams.hpp
--- a/src/hotspot/share/oops/fieldStreams.hpp
+++ b/src/hotspot/share/oops/fieldStreams.hpp
@@ -37,10 +37,11 @@
 // iterates over fields that have been injected by the JVM.
 // AllFieldStream exposes all fields and should only be used in rare
 // cases.
 // HierarchicalFieldStream allows to also iterate over fields of supertypes.
 class FieldStreamBase : public StackObj {
+
  protected:
   const Array<u1>*    _fieldinfo_stream;
   FieldInfoReader     _reader;
   constantPoolHandle  _constants;
   int                 _index;
@@ -108,18 +109,30 @@
 
   int offset() const {
     return field()->offset();
   }
 
+  bool is_null_free_inline_type() {
+    return field()->field_flags().is_null_free_inline_type();
+  }
+
+  bool is_flat() {
+    return field()->field_flags().is_flat();
+  }
+
   bool is_contended() const {
     return field()->is_contended();
   }
 
   int contended_group() const {
     return field()->contended_group();
   }
 
+  int null_marker_offset() const {
+    return field()->null_marker_offset();
+  }
+
   // Convenient methods
 
   FieldInfo to_FieldInfo() {
     return _fi_buf;
   }
diff a/src/hotspot/share/oops/flatArrayKlass.cpp b/src/hotspot/share/oops/flatArrayKlass.cpp
--- /dev/null
+++ b/src/hotspot/share/oops/flatArrayKlass.cpp
@@ -0,0 +1,531 @@
+/*
+ * Copyright (c) 2017, 2020, 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 "classfile/moduleEntry.hpp"
+#include "classfile/packageEntry.hpp"
+#include "classfile/symbolTable.hpp"
+#include "classfile/systemDictionary.hpp"
+#include "classfile/vmSymbols.hpp"
+#include "gc/shared/collectedHeap.inline.hpp"
+#include "memory/iterator.inline.hpp"
+#include "memory/metadataFactory.hpp"
+#include "memory/metaspaceClosure.hpp"
+#include "memory/oopFactory.hpp"
+#include "memory/resourceArea.hpp"
+#include "memory/universe.hpp"
+#include "oops/arrayKlass.inline.hpp"
+#include "oops/arrayOop.hpp"
+#include "oops/flatArrayOop.hpp"
+#include "oops/flatArrayOop.inline.hpp"
+#include "oops/inlineKlass.hpp"
+#include "oops/instanceKlass.hpp"
+#include "oops/klass.inline.hpp"
+#include "oops/objArrayKlass.hpp"
+#include "oops/objArrayOop.inline.hpp"
+#include "oops/oop.inline.hpp"
+#include "oops/verifyOopClosure.hpp"
+#include "runtime/handles.inline.hpp"
+#include "runtime/mutexLocker.hpp"
+#include "utilities/copy.hpp"
+#include "utilities/macros.hpp"
+
+#include "oops/flatArrayKlass.hpp"
+
+// Allocation...
+
+FlatArrayKlass::FlatArrayKlass(Klass* element_klass, Symbol* name) : ArrayKlass(name, Kind) {
+  assert(element_klass->is_inline_klass(), "Expected Inline");
+
+  set_element_klass(InlineKlass::cast(element_klass));
+  set_class_loader_data(element_klass->class_loader_data());
+
+  set_layout_helper(array_layout_helper(InlineKlass::cast(element_klass)));
+  assert(is_array_klass(), "sanity");
+  assert(is_flatArray_klass(), "sanity");
+  assert(is_null_free_array_klass(), "sanity");
+
+#ifdef _LP64
+  set_prototype_header(markWord::flat_array_prototype());
+  assert(prototype_header().is_flat_array(), "sanity");
+#else
+  set_prototype_header(markWord::inline_type_prototype());
+#endif
+
+#ifndef PRODUCT
+  if (PrintFlatArrayLayout) {
+    print();
+  }
+#endif
+}
+
+InlineKlass* FlatArrayKlass::element_klass() const {
+  return InlineKlass::cast(_element_klass);
+}
+
+void FlatArrayKlass::set_element_klass(Klass* k) {
+  _element_klass = k;
+}
+
+FlatArrayKlass* FlatArrayKlass::allocate_klass(Klass* eklass, TRAPS) {
+  guarantee((!Universe::is_bootstrapping() || vmClasses::Object_klass_loaded()), "Really ?!");
+  assert(UseFlatArray, "Flatten array required");
+
+  InlineKlass* element_klass = InlineKlass::cast(eklass);
+  assert(element_klass->is_naturally_atomic() || (!InlineArrayAtomicAccess), "Atomic by-default");
+
+  /*
+   *  MVT->LWorld, now need to allocate secondaries array types, just like objArrayKlass...
+   *  ...so now we are trying out covariant array types, just copy objArrayKlass
+   *  TODO refactor any remaining commonality
+   *
+   */
+  // Eagerly allocate the direct array supertype.
+  Klass* super_klass = nullptr;
+  Klass* element_super = element_klass->super();
+  if (element_super != nullptr) {
+    // The element type has a direct super.  E.g., String[] has direct super of Object[].
+    super_klass = element_klass->array_klass_or_null();
+    bool supers_exist = super_klass != nullptr;
+    // Also, see if the element has secondary supertypes.
+    // We need an array type for each.
+    const Array<Klass*>* element_supers = element_klass->secondary_supers();
+    for( int i = element_supers->length()-1; i >= 0; i-- ) {
+      Klass* elem_super = element_supers->at(i);
+      if (elem_super->array_klass_or_null() == nullptr) {
+        supers_exist = false;
+        break;
+      }
+    }
+    if (!supers_exist) {
+      // Oops.  Not allocated yet.  Back out, allocate it, and retry.
+      Klass* ek = nullptr;
+      {
+        MutexUnlocker mu(MultiArray_lock);
+        super_klass = element_klass->array_klass(CHECK_NULL);
+        for( int i = element_supers->length()-1; i >= 0; i-- ) {
+          Klass* elem_super = element_supers->at(i);
+          elem_super->array_klass(CHECK_NULL);
+        }
+        // Now retry from the beginning
+        ek = element_klass->value_array_klass(CHECK_NULL);
+      }  // re-lock
+      return FlatArrayKlass::cast(ek);
+    }
+  }
+
+  Symbol* name = ArrayKlass::create_element_klass_array_name(element_klass, CHECK_NULL);
+  ClassLoaderData* loader_data = element_klass->class_loader_data();
+  int size = ArrayKlass::static_size(FlatArrayKlass::header_size());
+  FlatArrayKlass* vak = new (loader_data, size, THREAD) FlatArrayKlass(element_klass, name);
+
+  ModuleEntry* module = vak->module();
+  assert(module != nullptr, "No module entry for array");
+  complete_create_array_klass(vak, super_klass, module, CHECK_NULL);
+
+  loader_data->add_class(vak);
+
+  return vak;
+}
+
+void FlatArrayKlass::initialize(TRAPS) {
+  element_klass()->initialize(THREAD);
+}
+
+void FlatArrayKlass::metaspace_pointers_do(MetaspaceClosure* it) {
+  ArrayKlass::metaspace_pointers_do(it);
+  it->push(&_element_klass);
+}
+
+// Oops allocation...
+flatArrayOop FlatArrayKlass::allocate(int length, TRAPS) {
+  check_array_allocation_length(length, max_elements(), CHECK_NULL);
+  int size = flatArrayOopDesc::object_size(layout_helper(), length);
+  return (flatArrayOop) Universe::heap()->array_allocate(this, size, length, true, THREAD);
+}
+
+
+oop FlatArrayKlass::multi_allocate(int rank, jint* last_size, TRAPS) {
+  // For flatArrays this is only called for the last dimension
+  assert(rank == 1, "just checking");
+  int length = *last_size;
+  return allocate(length, THREAD);
+}
+
+jint FlatArrayKlass::array_layout_helper(InlineKlass* vk) {
+  BasicType etype = T_PRIMITIVE_OBJECT;
+  int esize = log2i_exact(round_up_power_of_2(vk->get_payload_size_in_bytes()));
+  int hsize = arrayOopDesc::base_offset_in_bytes(etype);
+
+  int lh = Klass::array_layout_helper(_lh_array_tag_vt_value, true, hsize, etype, esize);
+
+  assert(lh < (int)_lh_neutral_value, "must look like an array layout");
+  assert(layout_helper_is_array(lh), "correct kind");
+  assert(layout_helper_is_flatArray(lh), "correct kind");
+  assert(!layout_helper_is_typeArray(lh), "correct kind");
+  assert(!layout_helper_is_objArray(lh), "correct kind");
+  assert(layout_helper_is_null_free(lh), "correct kind");
+  assert(layout_helper_header_size(lh) == hsize, "correct decode");
+  assert(layout_helper_element_type(lh) == etype, "correct decode");
+  assert(layout_helper_log2_element_size(lh) == esize, "correct decode");
+  assert((1 << esize) < BytesPerLong || is_aligned(hsize, HeapWordsPerLong), "unaligned base");
+
+  return lh;
+}
+
+size_t FlatArrayKlass::oop_size(oop obj) const {
+  assert(obj->klass()->is_flatArray_klass(),"must be an flat array");
+  flatArrayOop array = flatArrayOop(obj);
+  return array->object_size();
+}
+
+// For now return the maximum number of array elements that will not exceed:
+// nof bytes = "max_jint * HeapWord" since the "oopDesc::oop_iterate_size"
+// returns "int" HeapWords, need fix for JDK-4718400 and JDK-8233189
+jint FlatArrayKlass::max_elements() const {
+  // Check the max number of heap words limit first (because of int32_t in oopDesc_oop_size() etc)
+  size_t max_size = max_jint;
+  max_size -= arrayOopDesc::header_size(T_PRIMITIVE_OBJECT);
+  max_size = align_down(max_size, MinObjAlignment);
+  max_size <<= LogHeapWordSize;                                  // convert to max payload size in bytes
+  max_size >>= layout_helper_log2_element_size(_layout_helper);  // divide by element size (in bytes) = max elements
+  // Within int32_t heap words, still can't exceed Java array element limit
+  if (max_size > max_jint) {
+    max_size = max_jint;
+  }
+  assert((max_size >> LogHeapWordSize) <= max_jint, "Overflow");
+  return (jint) max_size;
+}
+
+oop FlatArrayKlass::protection_domain() const {
+  return element_klass()->protection_domain();
+}
+
+// Temp hack having this here: need to move towards Access API
+static bool needs_backwards_copy(arrayOop s, int src_pos,
+                                 arrayOop d, int dst_pos, int length) {
+  return (s == d) && (dst_pos > src_pos) && (dst_pos - src_pos) < length;
+}
+
+void FlatArrayKlass::copy_array(arrayOop s, int src_pos,
+                                arrayOop d, int dst_pos, int length, TRAPS) {
+
+  assert(s->is_objArray() || s->is_flatArray(), "must be obj or flat array");
+
+   // Check destination
+   if ((!d->is_flatArray()) && (!d->is_objArray())) {
+     THROW(vmSymbols::java_lang_ArrayStoreException());
+   }
+
+   // Check if all offsets and lengths are non negative
+   if (src_pos < 0 || dst_pos < 0 || length < 0) {
+     THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException());
+   }
+   // Check if the ranges are valid
+   if  ( (((unsigned int) length + (unsigned int) src_pos) > (unsigned int) s->length())
+      || (((unsigned int) length + (unsigned int) dst_pos) > (unsigned int) d->length()) ) {
+     THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException());
+   }
+   // Check zero copy
+   if (length == 0)
+     return;
+
+   ArrayKlass* sk = ArrayKlass::cast(s->klass());
+   ArrayKlass* dk = ArrayKlass::cast(d->klass());
+   Klass* d_elem_klass = dk->element_klass();
+   Klass* s_elem_klass = sk->element_klass();
+   /**** CMH: compare and contrast impl, re-factor once we find edge cases... ****/
+
+   if (sk->is_flatArray_klass()) {
+     assert(sk == this, "Unexpected call to copy_array");
+     // Check subtype, all src homogeneous, so just once
+     if (!s_elem_klass->is_subtype_of(d_elem_klass)) {
+       THROW(vmSymbols::java_lang_ArrayStoreException());
+     }
+
+     flatArrayOop sa = flatArrayOop(s);
+     InlineKlass* s_elem_vklass = element_klass();
+
+     // flatArray-to-flatArray
+     if (dk->is_flatArray_klass()) {
+       // element types MUST be exact, subtype check would be dangerous
+       if (dk != this) {
+         THROW(vmSymbols::java_lang_ArrayStoreException());
+       }
+
+       flatArrayOop da = flatArrayOop(d);
+       address dst = (address) da->value_at_addr(dst_pos, layout_helper());
+       address src = (address) sa->value_at_addr(src_pos, layout_helper());
+       if (contains_oops()) {
+         int elem_incr = 1 << log2_element_size();
+         address src_end = src + (length << log2_element_size());
+         if (needs_backwards_copy(s, src_pos, d, dst_pos, length)) {
+           swap(src, src_end);
+           dst = dst + (length << log2_element_size());
+           do {
+             src -= elem_incr;
+             dst -= elem_incr;
+             HeapAccess<>::value_copy(src, dst, s_elem_vklass);
+           } while (src > src_end);
+         } else {
+           address src_end = src + (length << log2_element_size());
+           while (src < src_end) {
+             HeapAccess<>::value_copy(src, dst, s_elem_vklass);
+             src += elem_incr;
+             dst += elem_incr;
+           }
+         }
+       } else {
+         // we are basically a type array...don't bother limiting element copy
+         // it would have to be a lot wasted space to be worth value_store() calls, need a setting here ?
+         Copy::conjoint_memory_atomic(src, dst, (size_t)length << log2_element_size());
+       }
+     }
+     else { // flatArray-to-objArray
+       assert(dk->is_objArray_klass(), "Expected objArray here");
+       // Need to allocate each new src elem payload -> dst oop
+       objArrayHandle dh(THREAD, (objArrayOop)d);
+       flatArrayHandle sh(THREAD, sa);
+       int dst_end = dst_pos + length;
+       while (dst_pos < dst_end) {
+         oop o = flatArrayOopDesc::value_alloc_copy_from_index(sh, src_pos, CHECK);
+         dh->obj_at_put(dst_pos, o);
+         dst_pos++;
+         src_pos++;
+       }
+     }
+   } else {
+     assert(s->is_objArray(), "Expected objArray");
+     objArrayOop sa = objArrayOop(s);
+     assert(d->is_flatArray(), "Excepted flatArray");  // objArray-to-flatArray
+     InlineKlass* d_elem_vklass = InlineKlass::cast(d_elem_klass);
+     flatArrayOop da = flatArrayOop(d);
+
+     int src_end = src_pos + length;
+     int delem_incr = 1 << dk->log2_element_size();
+     address dst = (address) da->value_at_addr(dst_pos, layout_helper());
+     while (src_pos < src_end) {
+       oop se = sa->obj_at(src_pos);
+       if (se == nullptr) {
+         THROW(vmSymbols::java_lang_NullPointerException());
+       }
+       // Check exact type per element
+       if (se->klass() != d_elem_klass) {
+         THROW(vmSymbols::java_lang_ArrayStoreException());
+       }
+       d_elem_vklass->inline_copy_oop_to_payload(se, dst);
+       dst += delem_incr;
+       src_pos++;
+     }
+   }
+}
+
+
+ArrayKlass* FlatArrayKlass::array_klass(int n, TRAPS) {
+  assert(dimension() <= n, "check order of chain");
+  int dim = dimension();
+  if (dim == n) return this;
+
+  // lock-free read needs acquire semantics
+  if (higher_dimension_acquire() == nullptr) {
+
+    ResourceMark rm(THREAD);
+    {
+      // Ensure atomic creation of higher dimensions
+      MutexLocker mu(THREAD, MultiArray_lock);
+
+      // Check if another thread beat us
+      if (higher_dimension() == nullptr) {
+
+        // Create multi-dim klass object and link them together
+        ObjArrayKlass* ak = ObjArrayKlass::allocate_objArray_klass(class_loader_data(), dim + 1, this, false, CHECK_NULL);
+        ak->set_lower_dimension(this);
+        // use 'release' to pair with lock-free load
+        release_set_higher_dimension(ak);
+        assert(ak->is_objArray_klass(), "incorrect initialization of ObjArrayKlass");
+      }
+    }
+  }
+
+  ObjArrayKlass *ak = ObjArrayKlass::cast(higher_dimension());
+  JavaThread::cast(THREAD)->check_possible_safepoint();
+  return ak->array_klass(n, THREAD);
+}
+
+ArrayKlass* FlatArrayKlass::array_klass_or_null(int n) {
+
+  assert(dimension() <= n, "check order of chain");
+  int dim = dimension();
+  if (dim == n) return this;
+
+  // lock-free read needs acquire semantics
+  if (higher_dimension_acquire() == nullptr) {
+    return nullptr;
+  }
+
+  ObjArrayKlass *ak = ObjArrayKlass::cast(higher_dimension());
+  return ak->array_klass_or_null(n);
+}
+
+ArrayKlass* FlatArrayKlass::array_klass(TRAPS) {
+  return array_klass(dimension() +  1, THREAD);
+}
+
+ArrayKlass* FlatArrayKlass::array_klass_or_null() {
+  return array_klass_or_null(dimension() +  1);
+}
+
+
+ModuleEntry* FlatArrayKlass::module() const {
+  assert(element_klass() != nullptr, "FlatArrayKlass returned unexpected nullptr bottom_klass");
+  // The array is defined in the module of its bottom class
+  return element_klass()->module();
+}
+
+PackageEntry* FlatArrayKlass::package() const {
+  assert(element_klass() != nullptr, "FlatArrayKlass returned unexpected nullptr bottom_klass");
+  return element_klass()->package();
+}
+
+bool FlatArrayKlass::can_be_primary_super_slow() const {
+    return true;
+}
+
+GrowableArray<Klass*>* FlatArrayKlass::compute_secondary_supers(int num_extra_slots,
+                                                                Array<InstanceKlass*>* transitive_interfaces) {
+  assert(transitive_interfaces == nullptr, "sanity");
+  // interfaces = { cloneable_klass, serializable_klass, elemSuper[], ... };
+  Array<Klass*>* elem_supers = element_klass()->secondary_supers();
+  int num_elem_supers = elem_supers == nullptr ? 0 : elem_supers->length();
+  int num_secondaries = num_extra_slots + 2 + num_elem_supers;
+  GrowableArray<Klass*>* secondaries = new GrowableArray<Klass*>(num_elem_supers+2);
+
+  secondaries->push(vmClasses::Cloneable_klass());
+  secondaries->push(vmClasses::Serializable_klass());
+  for (int i = 0; i < num_elem_supers; i++) {
+    Klass* elem_super = (Klass*) elem_supers->at(i);
+    Klass* array_super = elem_super->array_klass_or_null();
+    assert(array_super != nullptr, "must already have been created");
+    secondaries->push(array_super);
+  }
+  return secondaries;
+}
+
+jint FlatArrayKlass::compute_modifier_flags() const {
+  // The modifier for an flatArray is the same as its element
+  jint element_flags = element_klass()->compute_modifier_flags();
+
+  return (element_flags & (JVM_ACC_PUBLIC | JVM_ACC_PRIVATE | JVM_ACC_PROTECTED))
+                        | (JVM_ACC_ABSTRACT | JVM_ACC_FINAL);
+}
+
+void FlatArrayKlass::print_on(outputStream* st) const {
+#ifndef PRODUCT
+  assert(!is_objArray_klass(), "Unimplemented");
+
+  st->print("Flat Type Array: ");
+  Klass::print_on(st);
+
+  st->print(" - element klass: ");
+  element_klass()->print_value_on(st);
+  st->cr();
+
+  int elem_size = element_byte_size();
+  st->print(" - element size %i ", elem_size);
+  st->print("aligned layout size %i", 1 << layout_helper_log2_element_size(layout_helper()));
+  st->cr();
+#endif //PRODUCT
+}
+
+void FlatArrayKlass::print_value_on(outputStream* st) const {
+  assert(is_klass(), "must be klass");
+
+  element_klass()->print_value_on(st);
+  st->print("[]");
+}
+
+
+#ifndef PRODUCT
+void FlatArrayKlass::oop_print_on(oop obj, outputStream* st) {
+  ArrayKlass::oop_print_on(obj, st);
+  flatArrayOop va = flatArrayOop(obj);
+  InlineKlass* vk = element_klass();
+  int print_len = MIN2(va->length(), MaxElementPrintSize);
+  for(int index = 0; index < print_len; index++) {
+    int off = (address) va->value_at_addr(index, layout_helper()) - cast_from_oop<address>(obj);
+    st->print_cr(" - Index %3d offset %3d: ", index, off);
+    oop obj = cast_to_oop((address)va->value_at_addr(index, layout_helper()) - vk->first_field_offset());
+    FieldPrinter print_field(st, obj);
+    vk->do_nonstatic_fields(&print_field);
+    st->cr();
+  }
+  int remaining = va->length() - print_len;
+  if (remaining > 0) {
+    st->print_cr(" - <%d more elements, increase MaxElementPrintSize to print>", remaining);
+  }
+}
+#endif //PRODUCT
+
+void FlatArrayKlass::oop_print_value_on(oop obj, outputStream* st) {
+  assert(obj->is_flatArray(), "must be flatArray");
+  st->print("a ");
+  element_klass()->print_value_on(st);
+  int len = flatArrayOop(obj)->length();
+  st->print("[%d] ", len);
+  obj->print_address_on(st);
+  if (PrintMiscellaneous && (WizardMode || Verbose)) {
+    int lh = layout_helper();
+    st->print("{");
+    for (int i = 0; i < len; i++) {
+      if (i > 4) {
+        st->print("..."); break;
+      }
+      st->print(" " INTPTR_FORMAT, (intptr_t)(void*)flatArrayOop(obj)->value_at_addr(i , lh));
+    }
+    st->print(" }");
+  }
+}
+
+// Verification
+class VerifyElementClosure: public BasicOopIterateClosure {
+ public:
+  virtual void do_oop(oop* p)       { VerifyOopClosure::verify_oop.do_oop(p); }
+  virtual void do_oop(narrowOop* p) { VerifyOopClosure::verify_oop.do_oop(p); }
+};
+
+void FlatArrayKlass::oop_verify_on(oop obj, outputStream* st) {
+  ArrayKlass::oop_verify_on(obj, st);
+  guarantee(obj->is_flatArray(), "must be flatArray");
+
+  if (contains_oops()) {
+    flatArrayOop va = flatArrayOop(obj);
+    VerifyElementClosure ec;
+    va->oop_iterate(&ec);
+  }
+}
+
+void FlatArrayKlass::verify_on(outputStream* st) {
+  ArrayKlass::verify_on(st);
+  guarantee(element_klass()->is_inline_klass(), "should be inline type klass");
+}
diff a/src/hotspot/share/oops/flatArrayKlass.hpp b/src/hotspot/share/oops/flatArrayKlass.hpp
--- /dev/null
+++ b/src/hotspot/share/oops/flatArrayKlass.hpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2017, 2020, 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_VM_OOPS_FLATARRAYKLASS_HPP
+#define SHARE_VM_OOPS_FLATARRAYKLASS_HPP
+
+#include "classfile/classLoaderData.hpp"
+#include "oops/arrayKlass.hpp"
+#include "oops/inlineKlass.hpp"
+#include "utilities/macros.hpp"
+
+/**
+ * Array of inline types, gives a layout of typeArrayOop, but needs oops iterators
+ */
+class FlatArrayKlass : public ArrayKlass {
+  friend class VMStructs;
+
+ public:
+  static const KlassKind Kind = FlatArrayKlassKind;
+
+ private:
+  // Constructor
+  FlatArrayKlass(Klass* element_klass, Symbol* name);
+
+ public:
+
+  FlatArrayKlass() {}
+
+  // Returns the ObjArrayKlass for n'th dimension.
+  virtual ArrayKlass* array_klass(int n, TRAPS);
+  virtual ArrayKlass* array_klass_or_null(int n);
+
+  // Returns the array class with this class as element type.
+  virtual ArrayKlass* array_klass(TRAPS);
+  virtual ArrayKlass* array_klass_or_null();
+
+  virtual InlineKlass* element_klass() const;
+  virtual void set_element_klass(Klass* k);
+
+  // Casting from Klass*
+  static FlatArrayKlass* cast(Klass* k) {
+    assert(k->is_flatArray_klass(), "cast to FlatArrayKlass");
+    return (FlatArrayKlass*) k;
+  }
+
+  // klass allocation
+  static FlatArrayKlass* allocate_klass(Klass* element_klass, TRAPS);
+
+  void initialize(TRAPS);
+
+  ModuleEntry* module() const;
+  PackageEntry* package() const;
+
+  bool can_be_primary_super_slow() const;
+  GrowableArray<Klass*>* compute_secondary_supers(int num_extra_slots,
+                                                  Array<InstanceKlass*>* transitive_interfaces);
+
+  int element_byte_size() const { return 1 << layout_helper_log2_element_size(_layout_helper); }
+
+  bool is_flatArray_klass_slow() const { return true; }
+
+  bool contains_oops() {
+    return element_klass()->contains_oops();
+  }
+
+  // Override.
+  bool element_access_is_atomic() {
+    return element_klass()->is_atomic();
+  }
+
+  oop protection_domain() const;
+
+  virtual void metaspace_pointers_do(MetaspaceClosure* iter);
+
+  static jint array_layout_helper(InlineKlass* vklass); // layout helper for values
+
+  // sizing
+  static int header_size()  { return sizeof(FlatArrayKlass)/HeapWordSize; }
+  int size() const          { return ArrayKlass::static_size(header_size()); }
+
+  jint max_elements() const;
+
+  size_t oop_size(oop obj) const;
+
+  // Oop Allocation
+  flatArrayOop allocate(int length, TRAPS);
+  oop multi_allocate(int rank, jint* sizes, TRAPS);
+
+  // Naming
+  const char* internal_name() const { return external_name(); }
+
+  // Copying
+  void copy_array(arrayOop s, int src_pos, arrayOop d, int dst_pos, int length, TRAPS);
+
+  // GC specific object visitors
+  //
+  // Mark Sweep
+  int oop_ms_adjust_pointers(oop obj);
+
+
+  template <typename T, typename OopClosureType>
+  inline void oop_oop_iterate(oop obj, OopClosureType* closure);
+
+  template <typename T, typename OopClosureType>
+  inline void oop_oop_iterate_reverse(oop obj, OopClosureType* closure);
+
+  template <typename T, typename OopClosureType>
+  inline void oop_oop_iterate_bounded(oop obj, OopClosureType* closure, MemRegion mr);
+
+  template <typename T, class OopClosureType>
+  inline void oop_oop_iterate_elements(flatArrayOop a, OopClosureType* closure);
+
+private:
+  template <typename T, class OopClosureType>
+  inline void oop_oop_iterate_elements_specialized(flatArrayOop a, OopClosureType* closure);
+
+  template <typename T, class OopClosureType>
+  inline void oop_oop_iterate_elements_bounded(flatArrayOop a, OopClosureType* closure, MemRegion mr);
+
+  template <typename T, class OopClosureType>
+  inline void oop_oop_iterate_elements_specialized_bounded(flatArrayOop a, OopClosureType* closure, void* low, void* high);
+
+ public:
+  jint compute_modifier_flags() const;
+
+  // Printing
+  void print_on(outputStream* st) const;
+  void print_value_on(outputStream* st) const;
+
+  void oop_print_value_on(oop obj, outputStream* st);
+#ifndef PRODUCT
+  void oop_print_on(oop obj, outputStream* st);
+#endif
+
+  // Verification
+  void verify_on(outputStream* st);
+  void oop_verify_on(oop obj, outputStream* st);
+};
+
+#endif
diff a/src/hotspot/share/oops/flatArrayKlass.inline.hpp b/src/hotspot/share/oops/flatArrayKlass.inline.hpp
--- /dev/null
+++ b/src/hotspot/share/oops/flatArrayKlass.inline.hpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2017, 2020, 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_VM_OOPS_FLATARRAYKLASS_INLINE_HPP
+#define SHARE_VM_OOPS_FLATARRAYKLASS_INLINE_HPP
+
+#include "memory/memRegion.hpp"
+#include "memory/iterator.hpp"
+#include "oops/arrayKlass.hpp"
+#include "oops/flatArrayKlass.hpp"
+#include "oops/flatArrayOop.hpp"
+#include "oops/flatArrayOop.inline.hpp"
+#include "oops/inlineKlass.hpp"
+#include "oops/inlineKlass.inline.hpp"
+#include "oops/klass.hpp"
+#include "oops/oop.inline.hpp"
+#include "utilities/devirtualizer.inline.hpp"
+#include "utilities/macros.hpp"
+
+/*
+ * Warning incomplete: requires embedded oops, not yet enabled, so consider this a "sketch-up" of oop iterators
+ */
+
+template <typename T, class OopClosureType>
+void FlatArrayKlass::oop_oop_iterate_elements_specialized(flatArrayOop a,
+                                                          OopClosureType* closure) {
+  assert(contains_oops(), "Nothing to iterate");
+
+  const int shift = Klass::layout_helper_log2_element_size(layout_helper());
+  const int addr_incr = 1 << shift;
+  uintptr_t elem_addr = (uintptr_t) a->base();
+  const uintptr_t stop_addr = elem_addr + ((uintptr_t)a->length() << shift);
+  const int oop_offset = element_klass()->first_field_offset();
+
+  while (elem_addr < stop_addr) {
+    element_klass()->oop_iterate_specialized<T>((address)(elem_addr - oop_offset), closure);
+    elem_addr += addr_incr;
+  }
+}
+
+template <typename T, class OopClosureType>
+void FlatArrayKlass::oop_oop_iterate_elements_specialized_bounded(flatArrayOop a,
+                                                                  OopClosureType* closure,
+                                                                  void* lo, void* hi) {
+  assert(contains_oops(), "Nothing to iterate");
+
+  const int shift = Klass::layout_helper_log2_element_size(layout_helper());
+  const int addr_incr = 1 << shift;
+  uintptr_t elem_addr = (uintptr_t)a->base();
+  uintptr_t stop_addr = elem_addr + ((uintptr_t)a->length() << shift);
+  const int oop_offset = element_klass()->first_field_offset();
+
+  if (elem_addr < (uintptr_t) lo) {
+    uintptr_t diff = ((uintptr_t) lo) - elem_addr;
+    elem_addr += (diff >> shift) << shift;
+  }
+  if (stop_addr > (uintptr_t) hi) {
+    uintptr_t diff = stop_addr - ((uintptr_t) hi);
+    stop_addr -= (diff >> shift) << shift;
+  }
+
+  const uintptr_t end = stop_addr;
+  while (elem_addr < end) {
+    element_klass()->oop_iterate_specialized_bounded<T>((address)(elem_addr - oop_offset), closure, lo, hi);
+    elem_addr += addr_incr;
+  }
+}
+
+template <typename T, class OopClosureType>
+void FlatArrayKlass::oop_oop_iterate_elements(flatArrayOop a, OopClosureType* closure) {
+  if (contains_oops()) {
+    oop_oop_iterate_elements_specialized<T>(a, closure);
+  }
+}
+
+template <typename T, typename OopClosureType>
+void FlatArrayKlass::oop_oop_iterate(oop obj, OopClosureType* closure) {
+  assert(obj->klass()->is_flatArray_klass(),"must be a flat array");
+  flatArrayOop a = flatArrayOop(obj);
+
+  if (Devirtualizer::do_metadata(closure)) {
+    Devirtualizer::do_klass(closure, obj->klass());
+    Devirtualizer::do_klass(closure, FlatArrayKlass::cast(obj->klass())->element_klass());
+  }
+
+  oop_oop_iterate_elements<T>(a, closure);
+}
+
+template <typename T, typename OopClosureType>
+void FlatArrayKlass::oop_oop_iterate_reverse(oop obj, OopClosureType* closure) {
+  // TODO
+  oop_oop_iterate<T>(obj, closure);
+}
+
+template <typename T, class OopClosureType>
+void FlatArrayKlass::oop_oop_iterate_elements_bounded(flatArrayOop a, OopClosureType* closure, MemRegion mr) {
+  if (contains_oops()) {
+    oop_oop_iterate_elements_specialized_bounded<T>(a, closure, mr.start(), mr.end());
+  }
+}
+
+
+template <typename T, typename OopClosureType>
+void FlatArrayKlass::oop_oop_iterate_bounded(oop obj, OopClosureType* closure, MemRegion mr) {
+  flatArrayOop a = flatArrayOop(obj);
+  if (Devirtualizer::do_metadata(closure)) {
+    Devirtualizer::do_klass(closure, a->klass());
+    Devirtualizer::do_klass(closure, FlatArrayKlass::cast(obj->klass())->element_klass());
+  }
+  oop_oop_iterate_elements_bounded<T>(a, closure, mr);
+}
+
+#endif // SHARE_VM_OOPS_FLATARRAYKLASS_INLINE_HPP
diff a/src/hotspot/share/oops/flatArrayOop.cpp b/src/hotspot/share/oops/flatArrayOop.cpp
--- /dev/null
+++ b/src/hotspot/share/oops/flatArrayOop.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015, 2020, 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 "flatArrayOop.hpp"
+#include "flatArrayKlass.inline.hpp"
+
diff a/src/hotspot/share/oops/flatArrayOop.hpp b/src/hotspot/share/oops/flatArrayOop.hpp
--- /dev/null
+++ b/src/hotspot/share/oops/flatArrayOop.hpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2015, 2020, 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_VM_OOPS_FLATARRAYOOP_HPP
+#define SHARE_VM_OOPS_FLATARRAYOOP_HPP
+
+#include "oops/arrayOop.hpp"
+#include "oops/klass.hpp"
+#include "runtime/handles.hpp"
+
+// A flatArrayOop points to a flat array containing inline types (no indirection).
+// It may include embedded oops in its elements.
+
+class flatArrayOopDesc : public arrayOopDesc {
+
+ public:
+  void*  base() const;
+  void* value_at_addr(int index, jint lh) const;
+
+  // Return a buffered element from index
+  static oop value_alloc_copy_from_index(flatArrayHandle vah, int index, TRAPS);
+  void value_copy_from_index(int index, oop dst) const;
+  void value_copy_to_index(oop src, int index) const;
+
+  // Sizing
+  static size_t element_size(int lh, int nof_elements) {
+    size_t sz = (size_t) nof_elements;
+    return sz << Klass::layout_helper_log2_element_size(lh);
+  }
+
+  static int object_size(int lh, int length) {
+    julong size_in_bytes = header_size_in_bytes() + element_size(lh, length);
+    julong size_in_words = ((size_in_bytes + (HeapWordSize-1)) >> LogHeapWordSize);
+    assert(size_in_words <= (julong)max_jint, "no overflow");
+    return align_object_size((intptr_t)size_in_words);
+  }
+
+  int object_size() const;
+
+};
+
+#endif // SHARE_VM_OOPS_FLATARRAYOOP_HPP
diff a/src/hotspot/share/oops/flatArrayOop.inline.hpp b/src/hotspot/share/oops/flatArrayOop.inline.hpp
--- /dev/null
+++ b/src/hotspot/share/oops/flatArrayOop.inline.hpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2018, 2020, 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_VM_OOPS_FLATARRAYOOP_INLINE_HPP
+#define SHARE_VM_OOPS_FLATARRAYOOP_INLINE_HPP
+
+#include "oops/access.inline.hpp"
+#include "oops/flatArrayOop.hpp"
+#include "oops/inlineKlass.inline.hpp"
+#include "oops/oop.inline.hpp"
+#include "runtime/globals.hpp"
+
+inline void* flatArrayOopDesc::base() const { return arrayOopDesc::base(T_PRIMITIVE_OBJECT); }
+
+inline void* flatArrayOopDesc::value_at_addr(int index, jint lh) const {
+  assert(is_within_bounds(index), "index out of bounds");
+
+  address addr = (address) base();
+  addr += (index << Klass::layout_helper_log2_element_size(lh));
+  return (void*) addr;
+}
+
+inline int flatArrayOopDesc::object_size() const {
+  return object_size(klass()->layout_helper(), length());
+}
+
+inline oop flatArrayOopDesc::value_alloc_copy_from_index(flatArrayHandle vah, int index, TRAPS) {
+  FlatArrayKlass* vaklass = FlatArrayKlass::cast(vah->klass());
+  InlineKlass* vklass = vaklass->element_klass();
+  assert(vklass->is_initialized(), "Should be");
+  if (vklass->is_empty_inline_type()) {
+    return vklass->default_value();
+  } else {
+    oop buf = vklass->allocate_instance_buffer(CHECK_NULL);
+    vklass->inline_copy_payload_to_new_oop(vah->value_at_addr(index, vaklass->layout_helper()), buf);
+    return buf;
+  }
+}
+
+inline void flatArrayOopDesc::value_copy_from_index(int index, oop dst) const {
+  FlatArrayKlass* vaklass = FlatArrayKlass::cast(klass());
+  InlineKlass* vklass = vaklass->element_klass();
+  void* src = value_at_addr(index, vaklass->layout_helper());
+  return vklass->inline_copy_payload_to_new_oop(src, dst);
+}
+
+inline void flatArrayOopDesc::value_copy_to_index(oop src, int index) const {
+  FlatArrayKlass* vaklass = FlatArrayKlass::cast(klass());
+  InlineKlass* vklass = vaklass->element_klass();
+  if (vklass->is_empty_inline_type()) {
+    return;
+  }
+  void* dst = value_at_addr(index, vaklass->layout_helper());
+  vklass->inline_copy_oop_to_payload(src, dst);
+}
+
+
+
+#endif // SHARE_VM_OOPS_FLATARRAYOOP_INLINE_HPP
diff a/src/hotspot/share/oops/generateOopMap.cpp b/src/hotspot/share/oops/generateOopMap.cpp
--- a/src/hotspot/share/oops/generateOopMap.cpp
+++ b/src/hotspot/share/oops/generateOopMap.cpp
@@ -137,12 +137,13 @@
   // Compute methods
   int compute_for_parameters(bool is_static, CellTypeState *effect) {
     _idx    = 0;
     _effect = effect;
 
-    if (!is_static)
+    if (!is_static) {
       effect[_idx++] = CellTypeState::ref;
+    }
 
     do_parameters_on(this);
 
     return length();
   };
@@ -1600,15 +1601,15 @@
     case Bytecodes::_getstatic:         do_field(true,   true,  itr->get_index_u2(), itr->bci(), itr->code()); break;
     case Bytecodes::_putstatic:         do_field(false,  true,  itr->get_index_u2(), itr->bci(), itr->code()); break;
     case Bytecodes::_getfield:          do_field(true,   false, itr->get_index_u2(), itr->bci(), itr->code()); break;
     case Bytecodes::_putfield:          do_field(false,  false, itr->get_index_u2(), itr->bci(), itr->code()); break;
 
+    case Bytecodes::_invokeinterface:
     case Bytecodes::_invokevirtual:
-    case Bytecodes::_invokespecial:     do_method(false, false, itr->get_index_u2(), itr->bci(), itr->code()); break;
-    case Bytecodes::_invokestatic:      do_method(true,  false, itr->get_index_u2(), itr->bci(), itr->code()); break;
-    case Bytecodes::_invokedynamic:     do_method(true,  false, itr->get_index_u4(), itr->bci(), itr->code()); break;
-    case Bytecodes::_invokeinterface:   do_method(false, true,  itr->get_index_u2(), itr->bci(), itr->code()); break;
+    case Bytecodes::_invokespecial:     do_method(false, itr->get_index_u2(), itr->bci(), itr->code()); break;
+    case Bytecodes::_invokestatic:      do_method(true , itr->get_index_u2(), itr->bci(), itr->code()); break;
+    case Bytecodes::_invokedynamic:     do_method(true , itr->get_index_u4(), itr->bci(), itr->code()); break;
     case Bytecodes::_newarray:
     case Bytecodes::_anewarray:         pp_new_ref(vCTS, itr->bci()); break;
     case Bytecodes::_checkcast:         do_checkcast(); break;
     case Bytecodes::_arraylength:
     case Bytecodes::_instanceof:        pp(rCTS, vCTS); break;
@@ -1624,10 +1625,11 @@
                                         break;
 
     case Bytecodes::_areturn:           do_return_monitor_check();
                                         ppop1(refCTS);
                                         break;
+
     case Bytecodes::_ifnull:
     case Bytecodes::_ifnonnull:         ppop1(refCTS); break;
     case Bytecodes::_multianewarray:    do_multianewarray(*(itr->bcp()+3), itr->bci()); break;
 
     case Bytecodes::_wide:              fatal("Iterator should skip this bytecode"); break;
@@ -1949,17 +1951,19 @@
     out = eff;
   } else {
     out = epsilonCTS;
     i   = copy_cts(in, eff);
   }
-  if (!is_static) in[i++] = CellTypeState::ref;
+  if (!is_static) {
+    in[i++] = CellTypeState::ref;
+  }
   in[i] = CellTypeState::bottom;
   assert(i<=3, "sanity check");
   pp(in, out);
 }
 
-void GenerateOopMap::do_method(int is_static, int is_interface, int idx, int bci, Bytecodes::Code bc) {
+void GenerateOopMap::do_method(int is_static, int idx, int bci, Bytecodes::Code bc) {
  // Dig up signature for field in constant pool
   ConstantPool* cp  = _method->constants();
   Symbol* signature   = cp->signature_ref_at(idx, bc);
 
   // Parse method signature
diff a/src/hotspot/share/oops/generateOopMap.hpp b/src/hotspot/share/oops/generateOopMap.hpp
--- a/src/hotspot/share/oops/generateOopMap.hpp
+++ b/src/hotspot/share/oops/generateOopMap.hpp
@@ -89,11 +89,11 @@
 class CellTypeState {
  private:
   unsigned int _state;
 
   // Masks for separating the BITS and INFO portions of a CellTypeState
-  enum { info_mask            = right_n_bits(28),
+  enum { info_mask            = right_n_bits(27),
          bits_mask            = (int)(~info_mask) };
 
   // These constant are used for manipulating the BITS portion of a
   // CellTypeState
   enum { uninit_bit           = (int)(nth_bit(31)),
@@ -102,22 +102,27 @@
          addr_bit             = nth_bit(28),
          live_bits_mask       = (int)(bits_mask & ~uninit_bit) };
 
   // These constants are used for manipulating the INFO portion of a
   // CellTypeState
-  enum { top_info_bit         = nth_bit(27),
-         not_bottom_info_bit  = nth_bit(26),
-         info_data_mask       = right_n_bits(26),
+  enum { top_info_bit         = nth_bit(26),
+         not_bottom_info_bit  = nth_bit(25),
+         info_data_mask       = right_n_bits(25),
          info_conflict        = info_mask };
 
   // Within the INFO data, these values are used to distinguish different
   // kinds of references.
-  enum { ref_not_lock_bit     = nth_bit(25),  // 0 if this reference is locked as a monitor
-         ref_slot_bit         = nth_bit(24),  // 1 if this reference is a "slot" reference,
+  enum { ref_not_lock_bit     = nth_bit(24),  // 0 if this reference is locked as a monitor
+         ref_slot_bit         = nth_bit(23),  // 1 if this reference is a "slot" reference,
                                               // 0 if it is a "line" reference.
-         ref_data_mask        = right_n_bits(24) };
+         ref_data_mask        = right_n_bits(23) };
 
+  // Within the INFO data, these values are used to distinguish different
+  // kinds of value types.
+  enum { valuetype_slot_bit   = nth_bit(24),  // 1 if this reference is a "slot" value type,
+    // 0 if it is a "line" value type.
+    valuetype_data_mask  = right_n_bits(24) };
 
   // These values are used to initialize commonly used CellTypeState
   // constants.
   enum { bottom_value         = 0,
          uninit_value         = (int)(uninit_bit | info_conflict),
@@ -395,11 +400,11 @@
   void  ppdupswap                           (int poplen, const char *out);
   void  do_ldc                              (int bci);
   void  do_astore                           (int idx);
   void  do_jsr                              (int delta);
   void  do_field                            (int is_get, int is_static, int idx, int bci, Bytecodes::Code bc);
-  void  do_method                           (int is_static, int is_interface, int idx, int bci, Bytecodes::Code bc);
+  void  do_method                           (int is_static, int idx, int bci, Bytecodes::Code bc);
   void  do_multianewarray                   (int dims, int bci);
   void  do_monitorenter                     (int bci);
   void  do_monitorexit                      (int bci);
   void  do_return_monitor_check             ();
   void  do_checkcast                        ();
diff a/src/hotspot/share/oops/inlineKlass.cpp b/src/hotspot/share/oops/inlineKlass.cpp
--- /dev/null
+++ b/src/hotspot/share/oops/inlineKlass.cpp
@@ -0,0 +1,565 @@
+/*
+ * Copyright (c) 2017, 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/cdsConfig.hpp"
+#include "classfile/vmSymbols.hpp"
+#include "code/codeCache.hpp"
+#include "gc/shared/barrierSet.hpp"
+#include "gc/shared/collectedHeap.inline.hpp"
+#include "gc/shared/gcLocker.inline.hpp"
+#include "interpreter/interpreter.hpp"
+#include "logging/log.hpp"
+#include "memory/metaspaceClosure.hpp"
+#include "memory/metadataFactory.hpp"
+#include "oops/access.hpp"
+#include "oops/compressedOops.inline.hpp"
+#include "oops/fieldStreams.inline.hpp"
+#include "oops/flatArrayKlass.hpp"
+#include "oops/inlineKlass.inline.hpp"
+#include "oops/instanceKlass.inline.hpp"
+#include "oops/method.hpp"
+#include "oops/oop.inline.hpp"
+#include "oops/objArrayKlass.hpp"
+#include "runtime/fieldDescriptor.inline.hpp"
+#include "runtime/handles.inline.hpp"
+#include "runtime/safepointVerifiers.hpp"
+#include "runtime/sharedRuntime.hpp"
+#include "runtime/signature.hpp"
+#include "runtime/thread.inline.hpp"
+#include "utilities/copy.hpp"
+
+  // Constructor
+InlineKlass::InlineKlass(const ClassFileParser& parser)
+    : InstanceKlass(parser, InlineKlass::Kind) {
+  set_prototype_header(markWord::inline_type_prototype());
+  assert(is_inline_klass(), "sanity");
+  assert(prototype_header().is_inline_type(), "sanity");
+}
+
+InlineKlass::InlineKlass() {
+  assert(CDSConfig::is_dumping_archive() || UseSharedSpaces, "only for CDS");
+}
+
+void InlineKlass::init_fixed_block() {
+  _adr_inlineklass_fixed_block = inlineklass_static_block();
+  // Addresses used for inline type calling convention
+  *((Array<SigEntry>**)adr_extended_sig()) = nullptr;
+  *((Array<VMRegPair>**)adr_return_regs()) = nullptr;
+  *((address*)adr_pack_handler()) = nullptr;
+  *((address*)adr_pack_handler_jobject()) = nullptr;
+  *((address*)adr_unpack_handler()) = nullptr;
+  assert(pack_handler() == nullptr, "pack handler not null");
+  *((int*)adr_default_value_offset()) = 0;
+  *((address*)adr_value_array_klasses()) = nullptr;
+}
+
+oop InlineKlass::default_value() {
+  assert(is_initialized() || is_being_initialized() || is_in_error_state(), "default value is set at the beginning of initialization");
+  oop val = java_mirror()->obj_field_acquire(default_value_offset());
+  assert(val != nullptr, "Sanity check");
+  assert(oopDesc::is_oop(val), "Sanity check");
+  assert(val->is_inline_type(), "Sanity check");
+  assert(val->klass() == this, "sanity check");
+  return val;
+}
+
+int InlineKlass::first_field_offset_old() {
+#ifdef ASSERT
+  int first_offset = INT_MAX;
+  for (AllFieldStream fs(this); !fs.done(); fs.next()) {
+    if (fs.offset() < first_offset) first_offset= fs.offset();
+  }
+#endif
+  int base_offset = instanceOopDesc::base_offset_in_bytes();
+  // The first field of line types is aligned on a long boundary
+  base_offset = align_up(base_offset, BytesPerLong);
+  assert(base_offset == first_offset, "inconsistent offsets");
+  return base_offset;
+}
+
+instanceOop InlineKlass::allocate_instance(TRAPS) {
+  int size = size_helper();  // Query before forming handle.
+
+  instanceOop oop = (instanceOop)Universe::heap()->obj_allocate(this, size, CHECK_NULL);
+  assert(oop->mark().is_inline_type(), "Expected inline type");
+  return oop;
+}
+
+instanceOop InlineKlass::allocate_instance_buffer(TRAPS) {
+  int size = size_helper();  // Query before forming handle.
+
+  instanceOop oop = (instanceOop)Universe::heap()->obj_buffer_allocate(this, size, CHECK_NULL);
+  assert(oop->mark().is_inline_type(), "Expected inline type");
+  return oop;
+}
+
+int InlineKlass::nonstatic_oop_count() {
+  int oops = 0;
+  int map_count = nonstatic_oop_map_count();
+  OopMapBlock* block = start_of_nonstatic_oop_maps();
+  OopMapBlock* end = block + map_count;
+  while (block != end) {
+    oops += block->count();
+    block++;
+  }
+  return oops;
+}
+
+oop InlineKlass::read_flat_field(oop obj, int offset, TRAPS) {
+  oop res = nullptr;
+  assert(is_initialized() || is_being_initialized()|| is_in_error_state(),
+        "Must be initialized, initializing or in a corner case of an escaped instance of a class that failed its initialization");
+  if (is_empty_inline_type()) {
+    res = (instanceOop)default_value();
+  } else {
+    Handle obj_h(THREAD, obj);
+    res = allocate_instance_buffer(CHECK_NULL);
+    inline_copy_payload_to_new_oop(((char*)(oopDesc*)obj_h()) + offset, res);
+  }
+  assert(res != nullptr, "Must be set in one of two paths above");
+  return res;
+}
+
+void InlineKlass::write_flat_field(oop obj, int offset, oop value, TRAPS) {
+  if (value == nullptr) {
+    THROW(vmSymbols::java_lang_NullPointerException());
+  }
+  write_non_null_flat_field(obj, offset, value);
+}
+
+void InlineKlass::write_non_null_flat_field(oop obj, int offset, oop value) {
+  assert(value != nullptr, "");
+  if (!is_empty_inline_type()) {
+    inline_copy_oop_to_payload(value, ((char*)(oopDesc*)obj) + offset);
+  }
+}
+
+// Arrays of...
+
+bool InlineKlass::flat_array() {
+  if (!UseFlatArray) {
+    return false;
+  }
+  // Too big
+  int elem_bytes = get_payload_size_in_bytes();
+  if ((FlatArrayElementMaxSize >= 0) && (elem_bytes > FlatArrayElementMaxSize)) {
+    return false;
+  }
+  // Too many embedded oops
+  if ((FlatArrayElementMaxOops >= 0) && (nonstatic_oop_count() > FlatArrayElementMaxOops)) {
+    return false;
+  }
+  // Declared atomic but not naturally atomic.
+  if (must_be_atomic() && !is_naturally_atomic()) {
+    return false;
+  }
+  // VM enforcing InlineArrayAtomicAccess only...
+  if (InlineArrayAtomicAccess && (!is_naturally_atomic())) {
+    return false;
+  }
+  return true;
+}
+
+Klass* InlineKlass::value_array_klass(int n, TRAPS) {
+  if (Atomic::load_acquire(adr_value_array_klasses()) == nullptr) {
+    ResourceMark rm(THREAD);
+    JavaThread *jt = JavaThread::cast(THREAD);
+    {
+      // Atomic creation of array_klasses
+      MutexLocker ma(THREAD, MultiArray_lock);
+
+      // Check if update has already taken place
+      if (value_array_klasses() == nullptr) {
+        ArrayKlass* k;
+        if (flat_array()) {
+          k = FlatArrayKlass::allocate_klass(this, CHECK_NULL);
+        } else {
+          k = ObjArrayKlass::allocate_objArray_klass(class_loader_data(), 1, this, true, CHECK_NULL);
+
+        }
+        // use 'release' to pair with lock-free load
+        Atomic::release_store(adr_value_array_klasses(), k);
+      }
+    }
+  }
+  ArrayKlass* ak = value_array_klasses();
+  return ak->array_klass(n, THREAD);
+}
+
+Klass* InlineKlass::value_array_klass_or_null(int n) {
+  // Need load-acquire for lock-free read
+  ArrayKlass* ak = Atomic::load_acquire(adr_value_array_klasses());
+  if (ak == nullptr) {
+    return nullptr;
+  } else {
+    return ak->array_klass_or_null(n);
+  }
+}
+
+Klass* InlineKlass::value_array_klass(TRAPS) {
+  return value_array_klass(1, THREAD);
+}
+
+Klass* InlineKlass::value_array_klass_or_null() {
+  return value_array_klass_or_null(1);
+}
+
+// Inline type arguments are not passed by reference, instead each
+// field of the inline type is passed as an argument. This helper
+// function collects the flat field (recursively)
+// in a list. Included with the field's type is
+// the offset of each field in the inline type: i2c and c2i adapters
+// need that to load or store fields. Finally, the list of fields is
+// sorted in order of increasing offsets: the adapters and the
+// compiled code need to agree upon the order of fields.
+//
+// The list of basic types that is returned starts with a T_METADATA
+// and ends with an extra T_VOID. T_METADATA/T_VOID pairs are used as
+// delimiters. Every entry between the two is a field of the inline
+// type. If there's an embedded inline type in the list, it also starts
+// with a T_METADATA and ends with a T_VOID. This is so we can
+// generate a unique fingerprint for the method's adapters and we can
+// generate the list of basic types from the interpreter point of view
+// (inline types passed as reference: iterate on the list until a
+// T_METADATA, drop everything until and including the closing
+// T_VOID) or the compiler point of view (each field of the inline
+// types is an argument: drop all T_METADATA/T_VOID from the list).
+int InlineKlass::collect_fields(GrowableArray<SigEntry>* sig, int base_off) {
+  int count = 0;
+  SigEntry::add_entry(sig, T_METADATA, name(), base_off);
+  for (JavaFieldStream fs(this); !fs.done(); fs.next()) {
+    if (fs.access_flags().is_static()) continue;
+    int offset = base_off + fs.offset() - (base_off > 0 ? first_field_offset() : 0);
+    // TODO 8284443 Use different heuristic to decide what should be scalarized in the calling convention
+    if (fs.is_flat()) {
+      // Resolve klass of flat field and recursively collect fields
+      Klass* vk = get_inline_type_field_klass(fs.index());
+      count += InlineKlass::cast(vk)->collect_fields(sig, offset);
+    } else {
+      BasicType bt = Signature::basic_type(fs.signature());
+      SigEntry::add_entry(sig, bt, fs.signature(), offset);
+      count += type2size[bt];
+    }
+  }
+  int offset = base_off + size_helper()*HeapWordSize - (base_off > 0 ? first_field_offset() : 0);
+  SigEntry::add_entry(sig, T_VOID, name(), offset);
+  if (base_off == 0) {
+    sig->sort(SigEntry::compare);
+  }
+  assert(sig->at(0)._bt == T_METADATA && sig->at(sig->length()-1)._bt == T_VOID, "broken structure");
+  return count;
+}
+
+void InlineKlass::initialize_calling_convention(TRAPS) {
+  // Because the pack and unpack handler addresses need to be loadable from generated code,
+  // they are stored at a fixed offset in the klass metadata. Since inline type klasses do
+  // not have a vtable, the vtable offset is used to store these addresses.
+  if (InlineTypeReturnedAsFields || InlineTypePassFieldsAsArgs) {
+    ResourceMark rm;
+    GrowableArray<SigEntry> sig_vk;
+    int nb_fields = collect_fields(&sig_vk);
+    Array<SigEntry>* extended_sig = MetadataFactory::new_array<SigEntry>(class_loader_data(), sig_vk.length(), CHECK);
+    *((Array<SigEntry>**)adr_extended_sig()) = extended_sig;
+    for (int i = 0; i < sig_vk.length(); i++) {
+      extended_sig->at_put(i, sig_vk.at(i));
+    }
+    if (can_be_returned_as_fields(/* init= */ true)) {
+      nb_fields++;
+      BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, nb_fields);
+      sig_bt[0] = T_METADATA;
+      SigEntry::fill_sig_bt(&sig_vk, sig_bt+1);
+      VMRegPair* regs = NEW_RESOURCE_ARRAY(VMRegPair, nb_fields);
+      int total = SharedRuntime::java_return_convention(sig_bt, regs, nb_fields);
+
+      if (total > 0) {
+        Array<VMRegPair>* return_regs = MetadataFactory::new_array<VMRegPair>(class_loader_data(), nb_fields, CHECK);
+        *((Array<VMRegPair>**)adr_return_regs()) = return_regs;
+        for (int i = 0; i < nb_fields; i++) {
+          return_regs->at_put(i, regs[i]);
+        }
+
+        BufferedInlineTypeBlob* buffered_blob = SharedRuntime::generate_buffered_inline_type_adapter(this);
+        *((address*)adr_pack_handler()) = buffered_blob->pack_fields();
+        *((address*)adr_pack_handler_jobject()) = buffered_blob->pack_fields_jobject();
+        *((address*)adr_unpack_handler()) = buffered_blob->unpack_fields();
+        assert(CodeCache::find_blob(pack_handler()) == buffered_blob, "lost track of blob");
+        assert(can_be_returned_as_fields(), "sanity");
+      }
+    }
+    if (!can_be_returned_as_fields() && !can_be_passed_as_fields()) {
+      MetadataFactory::free_array<SigEntry>(class_loader_data(), extended_sig);
+      assert(return_regs() == nullptr, "sanity");
+    }
+  }
+}
+
+void InlineKlass::deallocate_contents(ClassLoaderData* loader_data) {
+  if (extended_sig() != nullptr) {
+    MetadataFactory::free_array<SigEntry>(loader_data, extended_sig());
+  }
+  if (return_regs() != nullptr) {
+    MetadataFactory::free_array<VMRegPair>(loader_data, return_regs());
+  }
+  cleanup_blobs();
+  InstanceKlass::deallocate_contents(loader_data);
+}
+
+void InlineKlass::cleanup(InlineKlass* ik) {
+  ik->cleanup_blobs();
+}
+
+void InlineKlass::cleanup_blobs() {
+  if (pack_handler() != nullptr) {
+    CodeBlob* buffered_blob = CodeCache::find_blob(pack_handler());
+    assert(buffered_blob->is_buffered_inline_type_blob(), "bad blob type");
+    BufferBlob::free((BufferBlob*)buffered_blob);
+    *((address*)adr_pack_handler()) = nullptr;
+    *((address*)adr_pack_handler_jobject()) = nullptr;
+    *((address*)adr_unpack_handler()) = nullptr;
+  }
+}
+
+// Can this inline type be passed as multiple values?
+bool InlineKlass::can_be_passed_as_fields() const {
+  return InlineTypePassFieldsAsArgs;
+}
+
+// Can this inline type be returned as multiple values?
+bool InlineKlass::can_be_returned_as_fields(bool init) const {
+  return InlineTypeReturnedAsFields && (init || return_regs() != nullptr);
+}
+
+// Create handles for all oop fields returned in registers that are going to be live across a safepoint
+void InlineKlass::save_oop_fields(const RegisterMap& reg_map, GrowableArray<Handle>& handles) const {
+  Thread* thread = Thread::current();
+  const Array<SigEntry>* sig_vk = extended_sig();
+  const Array<VMRegPair>* regs = return_regs();
+  int j = 1;
+
+  for (int i = 0; i < sig_vk->length(); i++) {
+    BasicType bt = sig_vk->at(i)._bt;
+    if (bt == T_OBJECT || bt == T_ARRAY) {
+      VMRegPair pair = regs->at(j);
+      address loc = reg_map.location(pair.first(), nullptr);
+      oop v = *(oop*)loc;
+      assert(v == nullptr || oopDesc::is_oop(v), "not an oop?");
+      assert(Universe::heap()->is_in_or_null(v), "must be heap pointer");
+      handles.push(Handle(thread, v));
+    }
+    if (bt == T_METADATA) {
+      continue;
+    }
+    if (bt == T_VOID &&
+        sig_vk->at(i-1)._bt != T_LONG &&
+        sig_vk->at(i-1)._bt != T_DOUBLE) {
+      continue;
+    }
+    j++;
+  }
+  assert(j == regs->length(), "missed a field?");
+}
+
+// Update oop fields in registers from handles after a safepoint
+void InlineKlass::restore_oop_results(RegisterMap& reg_map, GrowableArray<Handle>& handles) const {
+  assert(InlineTypeReturnedAsFields, "Inline types should never be returned as fields");
+  const Array<SigEntry>* sig_vk = extended_sig();
+  const Array<VMRegPair>* regs = return_regs();
+  assert(regs != nullptr, "inconsistent");
+
+  int j = 1;
+  for (int i = 0, k = 0; i < sig_vk->length(); i++) {
+    BasicType bt = sig_vk->at(i)._bt;
+    if (bt == T_OBJECT || bt == T_ARRAY) {
+      VMRegPair pair = regs->at(j);
+      address loc = reg_map.location(pair.first(), nullptr);
+      *(oop*)loc = handles.at(k++)();
+    }
+    if (bt == T_METADATA) {
+      continue;
+    }
+    if (bt == T_VOID &&
+        sig_vk->at(i-1)._bt != T_LONG &&
+        sig_vk->at(i-1)._bt != T_DOUBLE) {
+      continue;
+    }
+    j++;
+  }
+  assert(j == regs->length(), "missed a field?");
+}
+
+// Fields are in registers. Create an instance of the inline type and
+// initialize it with the values of the fields.
+oop InlineKlass::realloc_result(const RegisterMap& reg_map, const GrowableArray<Handle>& handles, TRAPS) {
+  oop new_vt = allocate_instance(CHECK_NULL);
+  const Array<SigEntry>* sig_vk = extended_sig();
+  const Array<VMRegPair>* regs = return_regs();
+
+  int j = 1;
+  int k = 0;
+  for (int i = 0; i < sig_vk->length(); i++) {
+    BasicType bt = sig_vk->at(i)._bt;
+    if (bt == T_METADATA) {
+      continue;
+    }
+    if (bt == T_VOID) {
+      if (sig_vk->at(i-1)._bt == T_LONG ||
+          sig_vk->at(i-1)._bt == T_DOUBLE) {
+        j++;
+      }
+      continue;
+    }
+    int off = sig_vk->at(i)._offset;
+    assert(off > 0, "offset in object should be positive");
+    VMRegPair pair = regs->at(j);
+    address loc = reg_map.location(pair.first(), nullptr);
+    switch(bt) {
+    case T_BOOLEAN: {
+      new_vt->bool_field_put(off, *(jboolean*)loc);
+      break;
+    }
+    case T_CHAR: {
+      new_vt->char_field_put(off, *(jchar*)loc);
+      break;
+    }
+    case T_BYTE: {
+      new_vt->byte_field_put(off, *(jbyte*)loc);
+      break;
+    }
+    case T_SHORT: {
+      new_vt->short_field_put(off, *(jshort*)loc);
+      break;
+    }
+    case T_INT: {
+      new_vt->int_field_put(off, *(jint*)loc);
+      break;
+    }
+    case T_LONG: {
+#ifdef _LP64
+      new_vt->double_field_put(off,  *(jdouble*)loc);
+#else
+      Unimplemented();
+#endif
+      break;
+    }
+    case T_OBJECT:
+    case T_ARRAY: {
+      Handle handle = handles.at(k++);
+      new_vt->obj_field_put(off, handle());
+      break;
+    }
+    case T_FLOAT: {
+      new_vt->float_field_put(off,  *(jfloat*)loc);
+      break;
+    }
+    case T_DOUBLE: {
+      new_vt->double_field_put(off, *(jdouble*)loc);
+      break;
+    }
+    default:
+      ShouldNotReachHere();
+    }
+    *(intptr_t*)loc = 0xDEAD;
+    j++;
+  }
+  assert(j == regs->length(), "missed a field?");
+  assert(k == handles.length(), "missed an oop?");
+  return new_vt;
+}
+
+// Check the return register for an InlineKlass oop
+InlineKlass* InlineKlass::returned_inline_klass(const RegisterMap& map) {
+  BasicType bt = T_METADATA;
+  VMRegPair pair;
+  int nb = SharedRuntime::java_return_convention(&bt, &pair, 1);
+  assert(nb == 1, "broken");
+
+  address loc = map.location(pair.first(), nullptr);
+  intptr_t ptr = *(intptr_t*)loc;
+  if (is_set_nth_bit(ptr, 0)) {
+    // Return value is tagged, must be an InlineKlass pointer
+    clear_nth_bit(ptr, 0);
+    assert(Metaspace::contains((void*)ptr), "should be klass");
+    InlineKlass* vk = (InlineKlass*)ptr;
+    assert(vk->can_be_returned_as_fields(), "must be able to return as fields");
+    return vk;
+  }
+  // Return value is not tagged, must be a valid oop
+  assert(oopDesc::is_oop_or_null(cast_to_oop(ptr), true),
+         "Bad oop return: " PTR_FORMAT, ptr);
+  return nullptr;
+}
+
+// CDS support
+
+void InlineKlass::metaspace_pointers_do(MetaspaceClosure* it) {
+  InstanceKlass::metaspace_pointers_do(it);
+
+  InlineKlass* this_ptr = this;
+  it->push((Klass**)adr_value_array_klasses());
+}
+
+void InlineKlass::remove_unshareable_info() {
+  InstanceKlass::remove_unshareable_info();
+
+  *((Array<SigEntry>**)adr_extended_sig()) = nullptr;
+  *((Array<VMRegPair>**)adr_return_regs()) = nullptr;
+  *((address*)adr_pack_handler()) = nullptr;
+  *((address*)adr_pack_handler_jobject()) = nullptr;
+  *((address*)adr_unpack_handler()) = nullptr;
+  assert(pack_handler() == nullptr, "pack handler not null");
+  if (value_array_klasses() != nullptr) {
+    value_array_klasses()->remove_unshareable_info();
+  }
+}
+
+void InlineKlass::remove_java_mirror() {
+  InstanceKlass::remove_java_mirror();
+  if (value_array_klasses() != nullptr) {
+    value_array_klasses()->remove_java_mirror();
+  }
+}
+
+void InlineKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, PackageEntry* pkg_entry, TRAPS) {
+  // We are no longer bookkeeping pointer to fixed block during serialization, hence reinitializing
+  // fixed block address since its size was already accounted by InstanceKlass::size() and it will
+  // anyways be part of shared archive.
+  _adr_inlineklass_fixed_block = inlineklass_static_block();
+  InstanceKlass::restore_unshareable_info(loader_data, protection_domain, pkg_entry, CHECK);
+  if (value_array_klasses() != nullptr) {
+    value_array_klasses()->restore_unshareable_info(ClassLoaderData::the_null_class_loader_data(), Handle(), CHECK);
+  }
+}
+
+// oop verify
+
+void InlineKlass::verify_on(outputStream* st) {
+  InstanceKlass::verify_on(st);
+  guarantee(prototype_header().is_inline_type(), "Prototype header is not inline type");
+}
+
+void InlineKlass::oop_verify_on(oop obj, outputStream* st) {
+  InstanceKlass::oop_verify_on(obj, st);
+  guarantee(obj->mark().is_inline_type(), "Header is not inline type");
+}
diff a/src/hotspot/share/oops/inlineKlass.hpp b/src/hotspot/share/oops/inlineKlass.hpp
--- /dev/null
+++ b/src/hotspot/share/oops/inlineKlass.hpp
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2017, 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_VM_OOPS_INLINEKLASS_HPP
+#define SHARE_VM_OOPS_INLINEKLASS_HPP
+
+#include "classfile/classFileParser.hpp"
+#include "classfile/javaClasses.hpp"
+#include "oops/instanceKlass.hpp"
+#include "oops/method.hpp"
+#include "runtime/registerMap.hpp"
+//#include "oops/oop.inline.hpp"
+
+// An InlineKlass is a specialized InstanceKlass for inline types.
+
+
+class InlineKlass: public InstanceKlass {
+  friend class VMStructs;
+  friend class InstanceKlass;
+  friend class ClassFileParser;
+
+ public:
+  static const KlassKind Kind = InlineKlassKind;
+
+  InlineKlass();
+
+ private:
+
+  // Constructor
+  InlineKlass(const ClassFileParser& parser);
+
+  void init_fixed_block();
+  inline InlineKlassFixedBlock* inlineklass_static_block() const;
+  inline address adr_return_regs() const;
+
+  address adr_extended_sig() const {
+    assert(_adr_inlineklass_fixed_block != nullptr, "Should have been initialized");
+    return ((address)_adr_inlineklass_fixed_block) + in_bytes(byte_offset_of(InlineKlassFixedBlock, _extended_sig));
+  }
+
+  // pack and unpack handlers for inline types return
+  address adr_pack_handler() const {
+    assert(_adr_inlineklass_fixed_block != nullptr, "Should have been initialized");
+    return ((address)_adr_inlineklass_fixed_block) + in_bytes(byte_offset_of(InlineKlassFixedBlock, _pack_handler));
+  }
+
+  address adr_pack_handler_jobject() const {
+    assert(_adr_inlineklass_fixed_block != nullptr, "Should have been initialized");
+    return ((address)_adr_inlineklass_fixed_block) + in_bytes(byte_offset_of(InlineKlassFixedBlock, _pack_handler_jobject));
+  }
+
+  address adr_unpack_handler() const {
+    assert(_adr_inlineklass_fixed_block != nullptr, "Should have been initialized");
+    return ((address)_adr_inlineklass_fixed_block) + in_bytes(byte_offset_of(InlineKlassFixedBlock, _unpack_handler));
+  }
+
+  address adr_default_value_offset() const {
+    assert(_adr_inlineklass_fixed_block != nullptr, "Should have been initialized");
+    return ((address)_adr_inlineklass_fixed_block) + in_bytes(default_value_offset_offset());
+  }
+
+  ArrayKlass* volatile* adr_value_array_klasses() const {
+    assert(_adr_inlineklass_fixed_block != nullptr, "Should have been initialized");
+    return (ArrayKlass* volatile*) ((address)_adr_inlineklass_fixed_block) + in_bytes(byte_offset_of(InlineKlassFixedBlock, _null_free_inline_array_klasses));
+  }
+
+  ArrayKlass* value_array_klasses() const {
+    return *adr_value_array_klasses();
+  }
+
+  address adr_alignment() const {
+    assert(_adr_inlineklass_fixed_block != nullptr, "Should have been initialized");
+    return ((address)_adr_inlineklass_fixed_block) + in_bytes(byte_offset_of(InlineKlassFixedBlock, _alignment));
+  }
+
+  address adr_first_field_offset() const {
+    assert(_adr_inlineklass_fixed_block != nullptr, "Should have been initialized");
+    return ((address)_adr_inlineklass_fixed_block) + in_bytes(byte_offset_of(InlineKlassFixedBlock, _first_field_offset));
+  }
+
+  address adr_payload_size_in_bytes() const {
+    assert(_adr_inlineklass_fixed_block != nullptr, "Should have been initialized");
+    return ((address)_adr_inlineklass_fixed_block) + in_bytes(byte_offset_of(InlineKlassFixedBlock, _payload_size_in_bytes));
+  }
+
+  address adr_internal_null_marker_offset() const {
+    assert(_adr_inlineklass_fixed_block != nullptr, "Should have been initialized");
+    return ((address)_adr_inlineklass_fixed_block) + in_bytes(byte_offset_of(InlineKlassFixedBlock, _internal_null_marker_offset));
+  }
+
+ public:
+  int get_alignment() const {
+    return *(int*)adr_alignment();
+  }
+
+  void set_alignment(int alignment) {
+    *(int*)adr_alignment() = alignment;
+  }
+
+  int first_field_offset() const {
+    int offset = *(int*)adr_first_field_offset();
+    assert(offset != 0, "Must be initialized before use");
+    return *(int*)adr_first_field_offset();
+  }
+
+  void set_first_field_offset(int offset) {
+    *(int*)adr_first_field_offset() = offset;
+  }
+
+  int get_payload_size_in_bytes() const {
+    return *(int*)adr_payload_size_in_bytes();
+  }
+
+  void set_payload_size_in_bytes(int payload_size) {
+    *(int*)adr_payload_size_in_bytes() = payload_size;
+  }
+
+  void set_internal_null_marker_offset(int offset) {
+    *(int*)adr_internal_null_marker_offset() = offset;
+  }
+
+  bool has_internal_null_marker_offset() const {
+    return *(int*)adr_internal_null_marker_offset() != -1;
+  }
+
+  int get_internal_null_marker_offset() const {
+    assert(has_internal_null_marker_offset(), "Must not be call if value class has no internal null marker");
+    return *(int*)adr_internal_null_marker_offset();
+  }
+
+  int first_field_offset_old();
+
+  virtual void remove_unshareable_info();
+  virtual void remove_java_mirror();
+  virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, PackageEntry* pkg_entry, TRAPS);
+  virtual void metaspace_pointers_do(MetaspaceClosure* it);
+
+ private:
+  int collect_fields(GrowableArray<SigEntry>* sig, int base_off = 0);
+
+  void cleanup_blobs();
+
+ public:
+  // Type testing
+  bool is_inline_klass_slow() const        { return true; }
+
+  // Casting from Klass*
+  static InlineKlass* cast(Klass* k);
+
+  // Use this to return the size of an instance in heap words.
+  // Note that this size only applies to heap allocated stand-alone instances.
+  virtual int size_helper() const {
+    return layout_helper_to_size_helper(layout_helper());
+  }
+
+  // allocate_instance() allocates a stand alone value in the Java heap
+  // initialized to default value (cleared memory)
+  instanceOop allocate_instance(TRAPS);
+  // allocates a stand alone inline buffer in the Java heap
+  // DOES NOT have memory cleared, user MUST initialize payload before
+  // returning to Java (i.e.: inline_copy)
+  instanceOop allocate_instance_buffer(TRAPS);
+
+  address data_for_oop(oop o) const;
+
+  // Query if this class promises atomicity one way or another
+  bool is_atomic() { return is_naturally_atomic() || must_be_atomic(); }
+
+  bool flat_array();
+
+  bool contains_oops() const { return nonstatic_oop_map_count() > 0; }
+  int nonstatic_oop_count();
+
+  // null free inline arrays...
+  //
+
+  // null free inline array klass, akin to InstanceKlass::array_klass()
+  // Returns the array class for the n'th dimension
+  Klass* value_array_klass(int n, TRAPS);
+  Klass* value_array_klass_or_null(int n);
+
+  // Returns the array class with this class as element type
+  Klass* value_array_klass(TRAPS);
+  Klass* value_array_klass_or_null();
+
+
+  // General store methods
+  //
+  // Normally loads and store methods would be found in *Oops classes, but since values can be
+  // "in-lined" (flat layout) into containing oops, these methods reside here in InlineKlass.
+  //
+  // "inline_copy_*_to_new_*" assume new memory (i.e. IS_DEST_UNINITIALIZED for write barriers)
+
+  void inline_copy_payload_to_new_oop(void* src, oop dst);
+  void inline_copy_oop_to_new_oop(oop src, oop dst);
+  void inline_copy_oop_to_new_payload(oop src, void* dst);
+  void inline_copy_oop_to_payload(oop src, void* dst);
+
+  oop read_flat_field(oop obj, int offset, TRAPS);
+  void write_flat_field(oop obj, int offset, oop value, TRAPS);
+  void write_non_null_flat_field(oop obj, int offset, oop value);
+
+  // oop iterate raw inline type data pointer (where oop_addr may not be an oop, but backing/array-element)
+  template <typename T, class OopClosureType>
+  inline void oop_iterate_specialized(const address oop_addr, OopClosureType* closure);
+
+  template <typename T, class OopClosureType>
+  inline void oop_iterate_specialized_bounded(const address oop_addr, OopClosureType* closure, void* lo, void* hi);
+
+  // calling convention support
+  void initialize_calling_convention(TRAPS);
+  Array<SigEntry>* extended_sig() const {
+    return *((Array<SigEntry>**)adr_extended_sig());
+  }
+  inline Array<VMRegPair>* return_regs() const;
+  bool can_be_passed_as_fields() const;
+  bool can_be_returned_as_fields(bool init = false) const;
+  void save_oop_fields(const RegisterMap& map, GrowableArray<Handle>& handles) const;
+  void restore_oop_results(RegisterMap& map, GrowableArray<Handle>& handles) const;
+  oop realloc_result(const RegisterMap& reg_map, const GrowableArray<Handle>& handles, TRAPS);
+  static InlineKlass* returned_inline_klass(const RegisterMap& reg_map);
+
+  address pack_handler() const {
+    return *(address*)adr_pack_handler();
+  }
+
+  address unpack_handler() const {
+    return *(address*)adr_unpack_handler();
+  }
+
+  // pack and unpack handlers. Need to be loadable from generated code
+  // so at a fixed offset from the base of the klass pointer.
+  static ByteSize pack_handler_offset() {
+    return byte_offset_of(InlineKlassFixedBlock, _pack_handler);
+  }
+
+  static ByteSize pack_handler_jobject_offset() {
+    return byte_offset_of(InlineKlassFixedBlock, _pack_handler_jobject);
+  }
+
+  static ByteSize unpack_handler_offset() {
+    return byte_offset_of(InlineKlassFixedBlock, _unpack_handler);
+  }
+
+  static ByteSize default_value_offset_offset() {
+    return byte_offset_of(InlineKlassFixedBlock, _default_value_offset);
+  }
+
+  static ByteSize first_field_offset_offset() {
+    return byte_offset_of(InlineKlassFixedBlock, _first_field_offset);
+  }
+
+  static ByteSize internal_null_marker_offset_offset() {
+    return byte_offset_of(InlineKlassFixedBlock, _internal_null_marker_offset);
+  }
+
+  void set_default_value_offset(int offset) {
+    *((int*)adr_default_value_offset()) = offset;
+  }
+
+  int default_value_offset() {
+    int offset = *((int*)adr_default_value_offset());
+    assert(offset != 0, "must not be called if not initialized");
+    return offset;
+  }
+
+  void set_default_value(oop val) {
+    java_mirror()->obj_field_put(default_value_offset(), val);
+  }
+
+  oop default_value();
+  void deallocate_contents(ClassLoaderData* loader_data);
+  static void cleanup(InlineKlass* ik) ;
+
+  // Verification
+  void verify_on(outputStream* st);
+  void oop_verify_on(oop obj, outputStream* st);
+
+};
+
+#endif /* SHARE_VM_OOPS_INLINEKLASS_HPP */
diff a/src/hotspot/share/oops/inlineKlass.inline.hpp b/src/hotspot/share/oops/inlineKlass.inline.hpp
--- /dev/null
+++ b/src/hotspot/share/oops/inlineKlass.inline.hpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2017, 2020, 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_VM_OOPS_INLINEKLASS_INLINE_HPP
+#define SHARE_VM_OOPS_INLINEKLASS_INLINE_HPP
+
+#include "memory/iterator.hpp"
+#include "oops/flatArrayKlass.hpp"
+#include "oops/inlineKlass.hpp"
+#include "oops/instanceKlass.inline.hpp"
+#include "oops/oop.inline.hpp"
+#include "utilities/devirtualizer.inline.hpp"
+#include "utilities/macros.hpp"
+
+inline InlineKlassFixedBlock* InlineKlass::inlineklass_static_block() const {
+
+  InstanceKlass* volatile* adr_impl = adr_implementor();
+  if (adr_impl != nullptr) {
+    return (InlineKlassFixedBlock*)(adr_impl + 1);
+  }
+
+  return (InlineKlassFixedBlock*)end_of_nonstatic_oop_maps();
+}
+
+inline address InlineKlass::adr_return_regs() const {
+  InlineKlassFixedBlock* vkst = inlineklass_static_block();
+  return ((address)_adr_inlineklass_fixed_block) + in_bytes(byte_offset_of(InlineKlassFixedBlock, _return_regs));
+}
+
+inline Array<VMRegPair>* InlineKlass::return_regs() const {
+  return *((Array<VMRegPair>**)adr_return_regs());
+}
+
+
+inline InlineKlass* InlineKlass::cast(Klass* k) {
+  assert(k->is_inline_klass(), "cast to InlineKlass");
+  return (InlineKlass*) k;
+}
+
+inline address InlineKlass::data_for_oop(oop o) const {
+  return ((address) (void*) o) + first_field_offset();
+}
+
+inline void InlineKlass::inline_copy_payload_to_new_oop(void* src, oop dst) {
+  HeapAccess<IS_DEST_UNINITIALIZED>::value_copy(src, data_for_oop(dst), this);
+}
+
+inline void InlineKlass::inline_copy_oop_to_new_oop(oop src, oop dst) {
+  HeapAccess<IS_DEST_UNINITIALIZED>::value_copy(data_for_oop(src), data_for_oop(dst), this);
+}
+
+inline void InlineKlass::inline_copy_oop_to_new_payload(oop src, void* dst) {
+  HeapAccess<IS_DEST_UNINITIALIZED>::value_copy(data_for_oop(src), dst, this);
+}
+
+inline void InlineKlass::inline_copy_oop_to_payload(oop src, void* dst) {
+  HeapAccess<>::value_copy(data_for_oop(src), dst, this);
+}
+
+
+template <typename T, class OopClosureType>
+void InlineKlass::oop_iterate_specialized(const address oop_addr, OopClosureType* closure) {
+  OopMapBlock* map = start_of_nonstatic_oop_maps();
+  OopMapBlock* const end_map = map + nonstatic_oop_map_count();
+
+  for (; map < end_map; map++) {
+    T* p = (T*) (oop_addr + map->offset());
+    T* const end = p + map->count();
+    for (; p < end; ++p) {
+      Devirtualizer::do_oop(closure, p);
+    }
+  }
+}
+
+template <typename T, class OopClosureType>
+inline void InlineKlass::oop_iterate_specialized_bounded(const address oop_addr, OopClosureType* closure, void* lo, void* hi) {
+  OopMapBlock* map = start_of_nonstatic_oop_maps();
+  OopMapBlock* const end_map = map + nonstatic_oop_map_count();
+
+  T* const l   = (T*) lo;
+  T* const h   = (T*) hi;
+
+  for (; map < end_map; map++) {
+    T* p = (T*) (oop_addr + map->offset());
+    T* end = p + map->count();
+    if (p < l) {
+      p = l;
+    }
+    if (end > h) {
+      end = h;
+    }
+    for (; p < end; ++p) {
+      Devirtualizer::do_oop(closure, p);
+    }
+  }
+}
+
+
+#endif // SHARE_VM_OOPS_INLINEKLASS_INLINE_HPP
diff a/src/hotspot/share/oops/instanceClassLoaderKlass.cpp b/src/hotspot/share/oops/instanceClassLoaderKlass.cpp
--- a/src/hotspot/share/oops/instanceClassLoaderKlass.cpp
+++ b/src/hotspot/share/oops/instanceClassLoaderKlass.cpp
@@ -23,9 +23,10 @@
  */
 
 #include "precompiled.hpp"
 #include "cds/cdsConfig.hpp"
 #include "oops/instanceClassLoaderKlass.hpp"
+#include "oops/instanceKlass.inline.hpp"
 
 InstanceClassLoaderKlass::InstanceClassLoaderKlass() {
   assert(CDSConfig::is_dumping_static_archive() || UseSharedSpaces, "only for CDS");
 }
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, 2023, Oracle and/or its affiliates. All rights reserved.
+ * 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.
@@ -69,10 +69,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/inlineKlass.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "prims/jvmtiRedefineClasses.hpp"
 #include "prims/jvmtiThreadState.hpp"
 #include "prims/methodComparator.hpp"
 #include "runtime/arguments.hpp"
@@ -165,10 +166,23 @@
     }
   }
   return false;
 }
 
+bool InstanceKlass::field_is_null_free_inline_type(int index) const {
+  return field(index).field_flags().is_null_free_inline_type();
+}
+
+bool InstanceKlass::is_class_in_preload_attribute(Symbol* name) const {
+  if (_preload_classes == nullptr) return false;
+  for (int i = 0; i < _preload_classes->length(); i++) {
+        Symbol* class_name = _constants->klass_at_noresolve(_preload_classes->at(i));
+        if (class_name == name) return true;
+  }
+  return false;
+}
+
 static inline bool is_stack_chunk_class(const Symbol* class_name,
                                         const ClassLoaderData* loader_data) {
   return (class_name == vmSymbols::jdk_internal_vm_StackChunk() &&
           loader_data->is_the_null_class_loader_data());
 }
@@ -437,11 +451,12 @@
 
 InstanceKlass* InstanceKlass::allocate_instance_klass(const ClassFileParser& parser, TRAPS) {
   const int size = InstanceKlass::size(parser.vtable_size(),
                                        parser.itable_size(),
                                        nonstatic_oop_map_size(parser.total_oop_map_count()),
-                                       parser.is_interface());
+                                       parser.is_interface(),
+                                       parser.is_inline_type());
 
   const Symbol* const class_name = parser.class_name();
   assert(class_name != nullptr, "invariant");
   ClassLoaderData* loader_data = parser.loader_data();
   assert(loader_data != nullptr, "invariant");
@@ -459,10 +474,13 @@
     // stack chunk
     ik = new (loader_data, size, THREAD) InstanceStackChunkKlass(parser);
   } else if (is_class_loader(class_name, parser)) {
     // class loader - java.lang.ClassLoader
     ik = new (loader_data, size, THREAD) InstanceClassLoaderKlass(parser);
+  } else if (parser.is_inline_type()) {
+    // inline type
+    ik = new (loader_data, size, THREAD) InlineKlass(parser);
   } else {
     // normal
     ik = new (loader_data, size, THREAD) InstanceKlass(parser);
   }
 
@@ -470,13 +488,42 @@
   // class count.  Can get OOM here.
   if (HAS_PENDING_EXCEPTION) {
     return nullptr;
   }
 
+#ifdef ASSERT
+  ik->bounds_check((address) ik->start_of_vtable(), false, size);
+  ik->bounds_check((address) ik->start_of_itable(), false, size);
+  ik->bounds_check((address) ik->end_of_itable(), true, size);
+  ik->bounds_check((address) ik->end_of_nonstatic_oop_maps(), true, size);
+#endif //ASSERT
   return ik;
 }
 
+#ifndef PRODUCT
+bool InstanceKlass::bounds_check(address addr, bool edge_ok, intptr_t size_in_bytes) const {
+  const char* bad = nullptr;
+  address end = nullptr;
+  if (addr < (address)this) {
+    bad = "before";
+  } else if (addr == (address)this) {
+    if (edge_ok)  return true;
+    bad = "just before";
+  } else if (addr == (end = (address)this + sizeof(intptr_t) * (size_in_bytes < 0 ? size() : size_in_bytes))) {
+    if (edge_ok)  return true;
+    bad = "just after";
+  } else if (addr > end) {
+    bad = "after";
+  } else {
+    return true;
+  }
+  tty->print_cr("%s object bounds: " INTPTR_FORMAT " [" INTPTR_FORMAT ".." INTPTR_FORMAT "]",
+      bad, (intptr_t)addr, (intptr_t)this, (intptr_t)end);
+  Verbose = WizardMode = true; this->print(); //@@
+  return false;
+}
+#endif //PRODUCT
 
 // copy method ordering from resource area to Metaspace
 void InstanceKlass::copy_method_ordering(const intArray* m, TRAPS) {
   if (m != nullptr) {
     // allocate a new array and copy contents (memcpy?)
@@ -516,17 +563,24 @@
   _itable_len(parser.itable_size()),
   _nest_host_index(0),
   _init_state(allocated),
   _reference_type(reference_type),
   _init_monitor(create_init_monitor("InstanceKlassInitMonitor_lock")),
-  _init_thread(nullptr)
+  _init_thread(nullptr),
+  _inline_type_field_klasses(nullptr),
+  _null_marker_offsets(nullptr),
+  _preload_classes(nullptr),
+  _adr_inlineklass_fixed_block(nullptr)
 {
   set_vtable_length(parser.vtable_size());
   set_access_flags(parser.access_flags());
   if (parser.is_hidden()) set_is_hidden();
   set_layout_helper(Klass::instance_layout_helper(parser.layout_size(),
                                                     false));
+  if (parser.has_inline_fields()) {
+    set_has_inline_type_fields();
+  }
 
   assert(nullptr == _methods, "underlying memory not zeroed?");
   assert(is_instance_klass(), "is layout incorrect?");
   assert(size_helper() == parser.layout_size(), "incorrect size_helper?");
 }
@@ -662,10 +716,19 @@
   if (fields_status() != nullptr && !fields_status()->is_shared()) {
     MetadataFactory::free_array<FieldStatus>(loader_data, fields_status());
   }
   set_fields_status(nullptr);
 
+  if (inline_type_field_klasses_array() != nullptr) {
+    MetadataFactory::free_array<InlineKlass*>(loader_data, inline_type_field_klasses_array());
+  }
+  set_inline_type_field_klasses_array(nullptr);
+
+  if (null_marker_offsets_array() != nullptr) {
+    MetadataFactory::free_array<int>(loader_data, null_marker_offsets_array());
+  }
+
   // If a method from a redefined class is using this constant pool, don't
   // delete it, yet.  The new class's previous version will point to this.
   if (constants() != nullptr) {
     assert (!constants()->on_stack(), "shouldn't be called if anything is onstack");
     if (!constants()->is_shared()) {
@@ -696,10 +759,16 @@
       !permitted_subclasses()->is_shared()) {
     MetadataFactory::free_array<jushort>(loader_data, permitted_subclasses());
   }
   set_permitted_subclasses(nullptr);
 
+  if (preload_classes() != nullptr &&
+      preload_classes() != Universe::the_empty_short_array() &&
+      !preload_classes()->is_shared()) {
+    MetadataFactory::free_array<jushort>(loader_data, preload_classes());
+  }
+
   // We should deallocate the Annotations instance if it's not in shared spaces.
   if (annotations() != nullptr && !annotations()->is_shared()) {
     MetadataFactory::free_metadata(loader_data, annotations());
   }
   set_annotations(nullptr);
@@ -875,10 +944,102 @@
   for (int index = 0; index < num_interfaces; index++) {
     InstanceKlass* interk = interfaces->at(index);
     interk->link_class_impl(CHECK_false);
   }
 
+
+  // If a class declares a method that uses an inline class as an argument
+  // type or return inline type, this inline class must be loaded during the
+  // linking of this class because size and properties of the inline class
+  // must be known in order to be able to perform inline type optimizations.
+  // The implementation below is an approximation of this rule, the code
+  // iterates over all methods of the current class (including overridden
+  // methods), not only the methods declared by this class. This
+  // approximation makes the code simpler, and doesn't change the semantic
+  // because classes declaring methods overridden by the current class are
+  // linked (and have performed their own pre-loading) before the linking
+  // of the current class.
+
+
+  // Note:
+  // Inline class types are loaded during
+  // the loading phase (see ClassFileParser::post_process_parsed_stream()).
+  // Inline class types used as element types for array creation
+  // are not pre-loaded. Their loading is triggered by either anewarray
+  // or multianewarray bytecodes.
+
+  // Could it be possible to do the following processing only if the
+  // class uses inline types?
+  if (EnableValhalla) {
+    ResourceMark rm(THREAD);
+    for (AllFieldStream fs(this); !fs.done(); fs.next()) {
+      if (fs.is_null_free_inline_type() && fs.access_flags().is_static()) {
+        Symbol* sig = fs.signature();
+        TempNewSymbol s = Signature::strip_envelope(sig);
+        if (s != name()) {
+          log_info(class, preload)("Preloading class %s during linking of class %s. Cause: a null-free static field is declared with this type", s->as_C_string(), name()->as_C_string());
+          Klass* klass = SystemDictionary::resolve_or_fail(s,
+                                                          Handle(THREAD, class_loader()), Handle(THREAD, protection_domain()), true,
+                                                          CHECK_false);
+          if (HAS_PENDING_EXCEPTION) {
+            log_warning(class, preload)("Preloading of class %s during linking of class %s (cause: null-free static field) failed: %s",
+                                      s->as_C_string(), name()->as_C_string(), PENDING_EXCEPTION->klass()->name()->as_C_string());
+            return false; // Exception is still pending
+          }
+          log_info(class, preload)("Preloading of class %s during linking of class %s (cause: null-free static field) succeeded",
+                                   s->as_C_string(), name()->as_C_string());
+          assert(klass != nullptr, "Sanity check");
+          if (!klass->is_inline_klass()) {
+            THROW_MSG_(vmSymbols::java_lang_IncompatibleClassChangeError(),
+                       err_msg("class %s expects class %s to be a value class but it is an identity class",
+                       name()->as_C_string(), klass->external_name()), false);
+          }
+          if (klass->is_abstract()) {
+            THROW_MSG_(vmSymbols::java_lang_IncompatibleClassChangeError(),
+                      err_msg("Class %s expects class %s to be concrete value class, but it is an abstract class",
+                      name()->as_C_string(),
+                      InstanceKlass::cast(klass)->external_name()), false);
+          }
+          InstanceKlass* ik = InstanceKlass::cast(klass);
+          if (!ik->is_implicitly_constructible()) {
+             THROW_MSG_(vmSymbols::java_lang_IncompatibleClassChangeError(),
+                        err_msg("class %s is not implicitly constructible and it is used in a null restricted static field (not supported)",
+                        klass->external_name()), false);
+          }
+          // the inline_type_field_klass_array is not updated because of CDS (see verifications in SystemDictionary::load_shared_class())
+        }
+      }
+    }
+
+    // Aggressively preloading all classes from the Preload attribute
+    if (preload_classes() != nullptr) {
+      HandleMark hm(THREAD);
+      for (int i = 0; i < preload_classes()->length(); i++) {
+        if (constants()->tag_at(preload_classes()->at(i)).is_klass()) continue;
+        Symbol* class_name = constants()->klass_at_noresolve(preload_classes()->at(i));
+        if (class_name == name()) continue;
+        log_info(class, preload)("Preloading class %s during linking of class %s because of the class is listed in the Preload attribute", class_name->as_C_string(), name()->as_C_string());
+        oop loader = class_loader();
+        oop protection_domain = this->protection_domain();
+        Klass* klass = SystemDictionary::resolve_or_null(class_name,
+                                                         Handle(THREAD, loader), Handle(THREAD, protection_domain), THREAD);
+        if (HAS_PENDING_EXCEPTION) {
+          CLEAR_PENDING_EXCEPTION;
+        }
+        if (klass != nullptr) {
+          log_info(class, preload)("Preloading of class %s during linking of class %s (cause: Preload attribute) succeeded", class_name->as_C_string(), name()->as_C_string());
+          if (!klass->is_inline_klass()) {
+            // Non value class are allowed by the current spec, but it could be an indication of an issue so let's log a warning
+              log_warning(class, preload)("Preloading class %s during linking of class %s (cause: Preload attribute) but loaded class is not a value class", class_name->as_C_string(), name()->as_C_string());
+          }
+        } else {
+          log_warning(class, preload)("Preloading of class %s during linking of class %s (cause: Preload attribute) failed", class_name->as_C_string(), name()->as_C_string());
+        }
+      }
+    }
+  }
+
   // in case the class is linked in the process of linking its superclasses
   if (is_linked()) {
     return true;
   }
 
@@ -1164,10 +1325,29 @@
       THROW_MSG_CAUSE(vmSymbols::java_lang_NoClassDefFoundError(),
                       ss.as_string(), cause);
     }
   }
 
+  // Pre-allocating an instance of the default value
+  if (is_inline_klass()) {
+      InlineKlass* vk = InlineKlass::cast(this);
+      oop val = vk->allocate_instance(THREAD);
+      if (HAS_PENDING_EXCEPTION) {
+          Handle e(THREAD, PENDING_EXCEPTION);
+          CLEAR_PENDING_EXCEPTION;
+          {
+              EXCEPTION_MARK;
+              add_initialization_error(THREAD, e);
+              // Locks object, set state, and notify all waiting threads
+              set_initialization_state_and_notify(initialization_error, THREAD);
+              CLEAR_PENDING_EXCEPTION;
+          }
+          THROW_OOP(e());
+      }
+      vk->set_default_value(val);
+  }
+
   // Step 7
   // Next, if C is a class rather than an interface, initialize it's super class and super
   // interfaces.
   if (!is_interface()) {
     Klass* super_klass = super();
@@ -1196,12 +1376,53 @@
       DTRACE_CLASSINIT_PROBE_WAIT(super__failed, -1, wait);
       THROW_OOP(e());
     }
   }
 
-
   // Step 8
+  // Initialize classes of inline fields
+  if (EnableValhalla) {
+    for (AllFieldStream fs(this); !fs.done(); fs.next()) {
+      if (fs.is_null_free_inline_type()) {
+        Klass* klass = get_inline_type_field_klass_or_null(fs.index());
+        if (fs.access_flags().is_static() && klass == nullptr) {
+          klass = SystemDictionary::resolve_or_fail(field_signature(fs.index())->fundamental_name(THREAD),
+              Handle(THREAD, class_loader()),
+              Handle(THREAD, protection_domain()),
+              true, THREAD);
+          assert(klass->is_inline_klass(), "Must be");
+          set_inline_type_field_klass(fs.index(), InlineKlass::cast(klass));
+        }
+
+        if (!HAS_PENDING_EXCEPTION) {
+          assert(klass != nullptr, "Must  be");
+          InstanceKlass::cast(klass)->initialize(THREAD);
+          if (fs.access_flags().is_static()) {
+            if (java_mirror()->obj_field(fs.offset()) == nullptr) {
+              java_mirror()->obj_field_put(fs.offset(), InlineKlass::cast(klass)->default_value());
+            }
+          }
+        }
+
+        if (HAS_PENDING_EXCEPTION) {
+          Handle e(THREAD, PENDING_EXCEPTION);
+          CLEAR_PENDING_EXCEPTION;
+          {
+            EXCEPTION_MARK;
+            add_initialization_error(THREAD, e);
+            // Locks object, set state, and notify all waiting threads
+            set_initialization_state_and_notify(initialization_error, THREAD);
+            CLEAR_PENDING_EXCEPTION;
+          }
+          THROW_OOP(e());
+        }
+      }
+    }
+  }
+
+
+  // Step 9
   {
     DTRACE_CLASSINIT_PROBE_WAIT(clinit, -1, wait);
     if (class_initializer() != nullptr) {
       // Timer includes any side effects of class initialization (resolution,
       // etc), but not recursive entry into call_class_initializer().
@@ -1219,17 +1440,17 @@
       }
       call_class_initializer(THREAD);
     }
   }
 
-  // Step 9
+  // Step 10
   if (!HAS_PENDING_EXCEPTION) {
     set_initialization_state_and_notify(fully_initialized, THREAD);
     debug_only(vtable().verify(tty, true);)
   }
   else {
-    // Step 10 and 11
+    // Step 11 and 12
     Handle e(THREAD, PENDING_EXCEPTION);
     CLEAR_PENDING_EXCEPTION;
     // JVMTI has already reported the pending exception
     // JVMTI internal flag reset is needed in order to report ExceptionInInitializerError
     JvmtiExport::clear_detected_exception(jt);
@@ -1551,28 +1772,29 @@
       // Atomic creation of array_klasses
       MutexLocker ma(THREAD, MultiArray_lock);
 
       // Check if update has already taken place
       if (array_klasses() == nullptr) {
-        ObjArrayKlass* k = ObjArrayKlass::allocate_objArray_klass(class_loader_data(), 1, this, CHECK_NULL);
+        ObjArrayKlass* k = ObjArrayKlass::allocate_objArray_klass(class_loader_data(), 1, this,
+                                                                  false, CHECK_NULL);
         // use 'release' to pair with lock-free load
         release_set_array_klasses(k);
       }
     }
   }
   // array_klasses() will always be set at this point
-  ObjArrayKlass* oak = array_klasses();
-  return oak->array_klass(n, THREAD);
+  ArrayKlass* ak = array_klasses();
+  return ak->array_klass(n, THREAD);
 }
 
 ArrayKlass* InstanceKlass::array_klass_or_null(int n) {
   // Need load-acquire for lock-free read
-  ObjArrayKlass* oak = array_klasses_acquire();
-  if (oak == nullptr) {
+  ArrayKlass* ak = array_klasses_acquire();
+  if (ak == nullptr) {
     return nullptr;
   } else {
-    return oak->array_klass_or_null(n);
+    return ak->array_klass_or_null(n);
   }
 }
 
 ArrayKlass* InstanceKlass::array_klass(TRAPS) {
   return array_klass(1, THREAD);
@@ -1585,11 +1807,11 @@
 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()) {
+  if (clinit != nullptr && clinit->is_class_initializer()) {
     return clinit;
   }
   return nullptr;
 }
 
@@ -1636,11 +1858,11 @@
   InterpreterOopMap* entry_for) {
   // Lazily create the _oop_map_cache at first request
   // Lock-free access requires load_acquire.
   OopMapCache* oop_map_cache = Atomic::load_acquire(&_oop_map_cache);
   if (oop_map_cache == nullptr) {
-    MutexLocker x(OopMapCacheAlloc_lock);
+    MutexLocker x(OopMapCacheAlloc_lock,  Mutex::_no_safepoint_check_flag);
     // Check if _oop_map_cache was allocated while we were waiting for this lock
     if ((oop_map_cache = _oop_map_cache) == nullptr) {
       oop_map_cache = new OopMapCache();
       // Ensure _oop_map_cache is stable, since it is examined without a lock
       Atomic::release_store(&_oop_map_cache, oop_map_cache);
@@ -1648,14 +1870,10 @@
   }
   // _oop_map_cache is constant after init; lookup below does its own locking.
   oop_map_cache->lookup(method, bci, entry_for);
 }
 
-bool InstanceKlass::contains_field_offset(int offset) {
-  fieldDescriptor fd;
-  return find_field_from_offset(offset, false, &fd);
-}
 
 FieldInfo InstanceKlass::field(int index) const {
   for (AllFieldStream fs(this); !fs.done(); fs.next()) {
     if (fs.index() == index) {
       return fs.to_FieldInfo();
@@ -1733,10 +1951,19 @@
   }
   // 4) otherwise field lookup fails
   return nullptr;
 }
 
+bool InstanceKlass::contains_field_offset(int offset) {
+  if (this->is_inline_klass()) {
+    InlineKlass* vk = InlineKlass::cast(this);
+    return offset >= vk->first_field_offset() && offset < (vk->first_field_offset() + vk->get_payload_size_in_bytes());
+  } else {
+    fieldDescriptor fd;
+    return find_field_from_offset(offset, false, &fd);
+  }
+}
 
 bool InstanceKlass::find_local_field_from_offset(int offset, bool is_static, fieldDescriptor* fd) const {
   for (JavaFieldStream fs(this); !fs.done(); fs.next()) {
     if (fs.offset() == offset) {
       fd->reinitialize(const_cast<InstanceKlass*>(this), fs.index());
@@ -2124,10 +2351,13 @@
                                                                         StaticLookupMode::find,
                                                                         private_mode);
     if (method != nullptr) {
       return method;
     }
+    if (name == vmSymbols::object_initializer_name()) {
+      break;  // <init> is never inherited
+    }
     klass = klass->super();
     overpass_local_mode = OverpassLookupMode::skip;   // Always ignore overpass methods in superclasses
   }
   return nullptr;
 }
@@ -2605,11 +2835,15 @@
     }
   }
 
   it->push(&_nest_members);
   it->push(&_permitted_subclasses);
+  it->push(&_preload_classes);
   it->push(&_record_components);
+
+  it->push(&_inline_type_field_klasses, MetaspaceClosure::_writable);
+  it->push(&_null_marker_offsets);
 }
 
 #if INCLUDE_CDS
 void InstanceKlass::remove_unshareable_info() {
 
@@ -2649,11 +2883,11 @@
   // do array classes also.
   if (array_klasses() != nullptr) {
     array_klasses()->remove_unshareable_info();
   }
 
-  // These are not allocated from metaspace. They are safe to set to null.
+  // These are not allocated from metaspace. They are safe to set to nullptr.
   _source_debug_extension = nullptr;
   _dep_context = nullptr;
   _osr_nmethods_head = nullptr;
 #if INCLUDE_JVMTI
   _breakpoints = nullptr;
@@ -2734,10 +2968,14 @@
   assert(!is_loaded(), "invalid init state");
   assert(!shared_loading_failed(), "Must not try to load failed class again");
   set_package(loader_data, pkg_entry, CHECK);
   Klass::restore_unshareable_info(loader_data, protection_domain, CHECK);
 
+  if (is_inline_klass()) {
+    InlineKlass::cast(this)->initialize_calling_convention(CHECK);
+  }
+
   Array<Method*>* methods = this->methods();
   int num_methods = methods->length();
   for (int index = 0; index < num_methods; ++index) {
     methods->at(index)->restore_unshareable_info(CHECK);
   }
@@ -2762,11 +3000,11 @@
 
   if (array_klasses() != nullptr) {
     // To get a consistent list of classes we need MultiArray_lock to ensure
     // array classes aren't observed while they are being restored.
     MutexLocker ml(MultiArray_lock);
-    assert(this == array_klasses()->bottom_klass(), "sanity");
+    assert(this == ObjArrayKlass::cast(array_klasses())->bottom_klass(), "sanity");
     // Array classes have null protection domain.
     // --> see ArrayKlass::complete_create_array_klass()
     array_klasses()->restore_unshareable_info(class_loader_data(), Handle(), CHECK);
   }
 
@@ -2961,20 +3199,23 @@
 Symbol* InstanceKlass::generic_signature() const                   { return _constants->generic_signature(); }
 u2 InstanceKlass::generic_signature_index() const                  { return _constants->generic_signature_index(); }
 void InstanceKlass::set_generic_signature_index(u2 sig_index)      { _constants->set_generic_signature_index(sig_index); }
 
 const char* InstanceKlass::signature_name() const {
+  return signature_name_of_carrier(JVM_SIGNATURE_CLASS);
+}
 
+const char* InstanceKlass::signature_name_of_carrier(char c) const {
   // Get the internal name as a c string
   const char* src = (const char*) (name()->as_C_string());
   const int src_length = (int)strlen(src);
 
   char* dest = NEW_RESOURCE_ARRAY(char, src_length + 3);
 
-  // Add L as type indicator
+  // Add L or Q as type indicator
   int dest_index = 0;
-  dest[dest_index++] = JVM_SIGNATURE_CLASS;
+  dest[dest_index++] = c;
 
   // Add the actual class name
   for (int src_index = 0; src_index < src_length; ) {
     dest[dest_index++] = src[src_index++];
   }
@@ -3312,12 +3553,11 @@
       // This is really a member class.
       access = iter.inner_access_flags();
       break;
     }
   }
-  // Remember to strip ACC_SUPER bit
-  return (access & (~JVM_ACC_SUPER)) & JVM_ACC_WRITTEN_FLAGS;
+  return (access & JVM_ACC_WRITTEN_FLAGS);
 }
 
 jint InstanceKlass::jvmti_class_status() const {
   jint result = 0;
 
@@ -3567,24 +3807,59 @@
 
 static const char* state_names[] = {
   "allocated", "loaded", "being_linked", "linked", "being_initialized", "fully_initialized", "initialization_error"
 };
 
-static void print_vtable(intptr_t* start, int len, outputStream* st) {
+static void print_vtable(address self, intptr_t* start, int len, outputStream* st) {
+  ResourceMark rm;
+  int* forward_refs = NEW_RESOURCE_ARRAY(int, len);
+  for (int i = 0; i < len; i++)  forward_refs[i] = 0;
   for (int i = 0; i < len; i++) {
     intptr_t e = start[i];
     st->print("%d : " INTPTR_FORMAT, i, e);
+    if (forward_refs[i] != 0) {
+      int from = forward_refs[i];
+      int off = (int) start[from];
+      st->print(" (offset %d <= [%d])", off, from);
+    }
     if (MetaspaceObj::is_valid((Metadata*)e)) {
       st->print(" ");
       ((Metadata*)e)->print_value_on(st);
+    } else if (self != nullptr && e > 0 && e < 0x10000) {
+      address location = self + e;
+      int index = (int)((intptr_t*)location - start);
+      st->print(" (offset %d => [%d])", (int)e, index);
+      if (index >= 0 && index < len)
+        forward_refs[index] = i;
     }
     st->cr();
   }
 }
 
 static void print_vtable(vtableEntry* start, int len, outputStream* st) {
-  return print_vtable(reinterpret_cast<intptr_t*>(start), len, st);
+  return print_vtable(nullptr, reinterpret_cast<intptr_t*>(start), len, st);
+}
+
+template<typename T>
+ static void print_array_on(outputStream* st, Array<T>* array) {
+   if (array == nullptr) { st->print_cr("nullptr"); return; }
+   array->print_value_on(st); st->cr();
+   if (Verbose || WizardMode) {
+     for (int i = 0; i < array->length(); i++) {
+       st->print("%d : ", i); array->at(i)->print_value_on(st); st->cr();
+     }
+   }
+ }
+
+static void print_array_on(outputStream* st, Array<int>* array) {
+  if (array == nullptr) { st->print_cr("nullptr"); return; }
+  array->print_value_on(st); st->cr();
+  if (Verbose || WizardMode) {
+    for (int i = 0; i < array->length(); i++) {
+      st->print("%d : %d", i, array->at(i)); st->cr();
+    }
+  }
 }
 
 const char* InstanceKlass::init_state_name() const {
   return state_names[init_state()];
 }
@@ -3621,30 +3896,18 @@
       st->cr();
     }
   }
 
   st->print(BULLET"arrays:            "); Metadata::print_value_on_maybe_null(st, array_klasses()); st->cr();
-  st->print(BULLET"methods:           "); methods()->print_value_on(st);                  st->cr();
-  if (Verbose || WizardMode) {
-    Array<Method*>* method_array = methods();
-    for (int i = 0; i < method_array->length(); i++) {
-      st->print("%d : ", i); method_array->at(i)->print_value(); st->cr();
-    }
-  }
-  st->print(BULLET"method ordering:   "); method_ordering()->print_value_on(st);      st->cr();
-  st->print(BULLET"default_methods:   "); default_methods()->print_value_on(st);      st->cr();
-  if (Verbose && default_methods() != nullptr) {
-    Array<Method*>* method_array = default_methods();
-    for (int i = 0; i < method_array->length(); i++) {
-      st->print("%d : ", i); method_array->at(i)->print_value(); st->cr();
-    }
-  }
+  st->print(BULLET"methods:           "); print_array_on(st, methods());
+  st->print(BULLET"method ordering:   "); print_array_on(st, method_ordering());
+  st->print(BULLET"default_methods:   "); print_array_on(st, default_methods());
   if (default_vtable_indices() != nullptr) {
-    st->print(BULLET"default vtable indices:   "); default_vtable_indices()->print_value_on(st);       st->cr();
+    st->print(BULLET"default vtable indices:   "); print_array_on(st, default_vtable_indices());
   }
-  st->print(BULLET"local interfaces:  "); local_interfaces()->print_value_on(st);      st->cr();
-  st->print(BULLET"trans. interfaces: "); transitive_interfaces()->print_value_on(st); st->cr();
+  st->print(BULLET"local interfaces:  "); print_array_on(st, local_interfaces());
+  st->print(BULLET"trans. interfaces: "); print_array_on(st, transitive_interfaces());
   st->print(BULLET"constants:         "); constants()->print_value_on(st);         st->cr();
   if (class_loader_data() != nullptr) {
     st->print(BULLET"class loader data:  ");
     class_loader_data()->print_value_on(st);
     st->cr();
@@ -3686,21 +3949,22 @@
   st->print(BULLET"nest members:     "); nest_members()->print_value_on(st);     st->cr();
   if (record_components() != nullptr) {
     st->print(BULLET"record components:     "); record_components()->print_value_on(st);     st->cr();
   }
   st->print(BULLET"permitted subclasses:     "); permitted_subclasses()->print_value_on(st);     st->cr();
+  st->print(BULLET"preload classes:     "); preload_classes()->print_value_on(st); st->cr();
   if (java_mirror() != nullptr) {
     st->print(BULLET"java mirror:       ");
     java_mirror()->print_value_on(st);
     st->cr();
   } else {
     st->print_cr(BULLET"java mirror:       null");
   }
   st->print(BULLET"vtable length      %d  (start addr: " PTR_FORMAT ")", vtable_length(), p2i(start_of_vtable())); st->cr();
   if (vtable_length() > 0 && (Verbose || WizardMode))  print_vtable(start_of_vtable(), vtable_length(), st);
   st->print(BULLET"itable length      %d (start addr: " PTR_FORMAT ")", itable_length(), p2i(start_of_itable())); st->cr();
-  if (itable_length() > 0 && (Verbose || WizardMode))  print_vtable(start_of_itable(), itable_length(), st);
+  if (itable_length() > 0 && (Verbose || WizardMode))  print_vtable(nullptr, start_of_itable(), itable_length(), st);
   st->print_cr(BULLET"---- static fields (%d words):", static_field_size());
   FieldPrinter print_static_field(st);
   ((InstanceKlass*)this)->do_local_static_fields(&print_static_field);
   st->print_cr(BULLET"---- non-static fields (%d words):", nonstatic_field_size());
   FieldPrinter print_nonstatic_field(st);
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
@@ -23,10 +23,11 @@
  */
 
 #ifndef SHARE_OOPS_INSTANCEKLASS_HPP
 #define SHARE_OOPS_INSTANCEKLASS_HPP
 
+#include "code/vmreg.hpp"
 #include "memory/referenceType.hpp"
 #include "oops/annotations.hpp"
 #include "oops/constMethod.hpp"
 #include "oops/fieldInfo.hpp"
 #include "oops/instanceKlassFlags.hpp"
@@ -54,10 +55,11 @@
 //    [EMBEDDED Java vtable             ] size in words = vtable_len
 //    [EMBEDDED nonstatic oop-map blocks] size in words = nonstatic_oop_map_size
 //      The embedded nonstatic oop-map blocks are short pairs (offset, length)
 //      indicating where oops are located in instances of this klass.
 //    [EMBEDDED implementor of the interface] only exist for interface
+//    [EMBEDDED InlineKlassFixedBlock] only if is an InlineKlass instance
 
 
 // forward declaration for class -- see below for definition
 #if INCLUDE_JVMTI
 class BreakpointInfo;
@@ -70,10 +72,11 @@
 class jniIdMapBase;
 class JNIid;
 class JvmtiCachedClassFieldMap;
 class nmethodBucket;
 class OopMapCache;
+class BufferedInlineTypeBlob;
 class InterpreterOopMap;
 class PackageEntry;
 class ModuleEntry;
 
 // This is used in iterators below.
@@ -130,15 +133,34 @@
   uint _count;
 };
 
 struct JvmtiCachedClassFileData;
 
+class SigEntry;
+
+class InlineKlassFixedBlock {
+  Array<SigEntry>** _extended_sig;
+  Array<VMRegPair>** _return_regs;
+  address* _pack_handler;
+  address* _pack_handler_jobject;
+  address* _unpack_handler;
+  int* _default_value_offset;
+  ArrayKlass** _null_free_inline_array_klasses;
+  int _alignment;
+  int _first_field_offset;
+  int _payload_size_in_bytes;
+  int _internal_null_marker_offset; // -1 if none
+
+  friend class InlineKlass;
+};
+
 class InstanceKlass: public Klass {
   friend class VMStructs;
   friend class JVMCIVMStructs;
   friend class ClassFileParser;
   friend class CompileReplay;
+  friend class TemplateTable;
 
  public:
   static const KlassKind Kind = InstanceKlassKind;
 
  protected:
@@ -169,11 +191,11 @@
   // Annotations for this class
   Annotations*    _annotations;
   // Package this class is defined in
   PackageEntry*   _package_entry;
   // Array classes holding elements of this class.
-  ObjArrayKlass* volatile _array_klasses;
+  ArrayKlass* volatile _array_klasses;
   // Constant pool for this class.
   ConstantPool* _constants;
   // The InnerClasses attribute and EnclosingMethod attribute. The
   // _inner_classes is an array of shorts. If the class has InnerClasses
   // attribute, then the _inner_classes array begins with 4-tuples of shorts
@@ -274,10 +296,15 @@
 
   // Fields information is stored in an UNSIGNED5 encoded stream (see fieldInfo.hpp)
   Array<u1>*          _fieldinfo_stream;
   Array<FieldStatus>* _fields_status;
 
+  Array<InlineKlass*>* _inline_type_field_klasses; // For "inline class" fields, null if none present
+  Array<int>* _null_marker_offsets; // for flat fields with a null marker
+  Array<u2>* _preload_classes;
+  const InlineKlassFixedBlock* _adr_inlineklass_fixed_block;
+
   // embedded Java vtable follows here
   // embedded Java itables follows here
   // embedded static fields follows here
   // embedded nonstatic oop-map blocks follows here
   // embedded implementor of this interface follows here
@@ -327,10 +354,35 @@
   void set_has_nonstatic_fields(bool b)    { _misc_flags.set_has_nonstatic_fields(b); }
 
   bool has_localvariable_table() const     { return _misc_flags.has_localvariable_table(); }
   void set_has_localvariable_table(bool b) { _misc_flags.set_has_localvariable_table(b); }
 
+  bool has_inline_type_fields() const { return _misc_flags.has_inline_type_fields(); }
+  void set_has_inline_type_fields()   { _misc_flags.set_has_inline_type_fields(true); }
+
+  bool is_empty_inline_type() const   { return _misc_flags.is_empty_inline_type(); }
+  void set_is_empty_inline_type()     { _misc_flags.set_is_empty_inline_type(true); }
+
+  // Note:  The naturally_atomic property only applies to
+  // inline classes; it is never true on identity classes.
+  // The bit is placed on instanceKlass for convenience.
+
+  // Query if h/w provides atomic load/store for instances.
+  bool is_naturally_atomic() const  { return _misc_flags.is_naturally_atomic(); }
+  void set_is_naturally_atomic()    { _misc_flags.set_is_naturally_atomic(true); }
+
+  // Query if this class implements jl.NonTearable or was
+  // mentioned in the JVM option ForceNonTearable.
+  // This bit can occur anywhere, but is only significant
+  // for inline classes *and* their super types.
+  // It inherits from supers along with NonTearable.
+  bool must_be_atomic() const { return _misc_flags.must_be_atomic(); }
+  void set_must_be_atomic()   { _misc_flags.set_must_be_atomic(true); }
+
+  bool is_implicitly_constructible() const { return _misc_flags.is_implicitly_constructible(); }
+  void set_is_implicitly_constructible()   { _misc_flags.set_is_implicitly_constructible(true); }
+
   // field sizes
   int nonstatic_field_size() const         { return _nonstatic_field_size; }
   void set_nonstatic_field_size(int size)  { _nonstatic_field_size = size; }
 
   int static_field_size() const            { return _static_field_size; }
@@ -342,14 +394,14 @@
   // Java itable
   int  itable_length() const               { return _itable_len; }
   void set_itable_length(int len)          { _itable_len = len; }
 
   // array klasses
-  ObjArrayKlass* array_klasses() const     { return _array_klasses; }
-  inline ObjArrayKlass* array_klasses_acquire() const; // load with acquire semantics
-  inline void release_set_array_klasses(ObjArrayKlass* k); // store with release semantics
-  void set_array_klasses(ObjArrayKlass* k) { _array_klasses = k; }
+  ArrayKlass* array_klasses() const     { return _array_klasses; }
+  inline ArrayKlass* array_klasses_acquire() const; // load with acquire semantics
+  inline void release_set_array_klasses(ArrayKlass* k); // store with release semantics
+  void set_array_klasses(ArrayKlass* k) { _array_klasses = k; }
 
   // methods
   Array<Method*>* methods() const          { return _methods; }
   void set_methods(Array<Method*>* a)      { _methods = a; }
   Method* method_with_idnum(int idnum);
@@ -385,16 +437,20 @@
  private:
   friend class fieldDescriptor;
   FieldInfo field(int index) const;
 
  public:
-  int     field_offset      (int index) const { return field(index).offset(); }
-  int     field_access_flags(int index) const { return field(index).access_flags().as_int(); }
+  int field_offset      (int index) const { return field(index).offset(); }
+  int field_access_flags(int index) const { return field(index).access_flags().as_int(); }
   FieldInfo::FieldFlags field_flags(int index) const { return field(index).field_flags(); }
   FieldStatus field_status(int index)   const { return fields_status()->at(index); }
   inline Symbol* field_name        (int index) const;
   inline Symbol* field_signature   (int index) const;
+  bool field_is_flat(int index) const { return field_flags(index).is_flat(); }
+  bool field_has_null_marker(int index) const { return field_flags(index).has_null_marker(); }
+  bool field_is_null_free_inline_type(int index) const;
+  bool is_class_in_preload_attribute(Symbol* name) const;
 
   // Number of Java declared fields
   int java_fields_count() const;
   int total_fields_count() const;
 
@@ -402,10 +458,13 @@
   void set_fieldinfo_stream(Array<u1>* fis) { _fieldinfo_stream = fis; }
 
   Array<FieldStatus>* fields_status() const {return _fields_status; }
   void set_fields_status(Array<FieldStatus>* array) { _fields_status = array; }
 
+  Array<u2>* preload_classes() const { return _preload_classes; }
+  void set_preload_classes(Array<u2>* c) { _preload_classes = c; }
+
   // inner classes
   Array<u2>* inner_classes() const       { return _inner_classes; }
   void set_inner_classes(Array<u2>* f)   { _inner_classes = f; }
 
   // nest members
@@ -541,10 +600,13 @@
 
   // marking
   bool is_marked_dependent() const         { return _misc_flags.is_marked_dependent(); }
   void set_is_marked_dependent(bool value) { _misc_flags.set_is_marked_dependent(value); }
 
+  static ByteSize kind_offset() { return in_ByteSize(offset_of(InstanceKlass, _kind)); }
+  static ByteSize misc_flags_offset() { return in_ByteSize(offset_of(InstanceKlass, _misc_flags)); }
+
   // initialization (virtuals from Klass)
   bool should_be_initialized() const;  // means that initialize should be called
   void initialize(TRAPS);
   void link_class(TRAPS);
   bool link_class_or_fail(TRAPS); // returns false on failure
@@ -873,10 +935,14 @@
   // support for stub routines
   static ByteSize init_state_offset()  { return byte_offset_of(InstanceKlass, _init_state); }
   JFR_ONLY(DEFINE_KLASS_TRACE_ID_OFFSET;)
   static ByteSize init_thread_offset() { return byte_offset_of(InstanceKlass, _init_thread); }
 
+  static ByteSize inline_type_field_klasses_offset() { return in_ByteSize(offset_of(InstanceKlass, _inline_type_field_klasses)); }
+  static ByteSize null_marker_array_offset() { return in_ByteSize(offset_of(InstanceKlass, _null_marker_offsets)); }
+  static ByteSize adr_inlineklass_fixed_block_offset() { return in_ByteSize(offset_of(InstanceKlass, _adr_inlineklass_fixed_block)); }
+
   // subclass/subinterface checks
   bool implements_interface(Klass* k) const;
   bool is_same_or_direct_interface(Klass* k) const;
 
 #ifdef ASSERT
@@ -930,36 +996,51 @@
   // Sizing (in words)
   static int header_size()            { return sizeof(InstanceKlass)/wordSize; }
 
   static int size(int vtable_length, int itable_length,
                   int nonstatic_oop_map_size,
-                  bool is_interface) {
+                  bool is_interface,
+                  bool is_inline_type) {
     return align_metadata_size(header_size() +
            vtable_length +
            itable_length +
            nonstatic_oop_map_size +
-           (is_interface ? (int)sizeof(Klass*)/wordSize : 0));
+           (is_interface ? (int)sizeof(Klass*)/wordSize : 0) +
+           (is_inline_type ? (int)sizeof(InlineKlassFixedBlock) : 0));
   }
 
   int size() const                    { return size(vtable_length(),
                                                itable_length(),
                                                nonstatic_oop_map_size(),
-                                               is_interface());
+                                               is_interface(),
+                                               is_inline_klass());
   }
 
 
   inline intptr_t* start_of_itable() const;
   inline intptr_t* end_of_itable() const;
   inline oop static_field_base_raw();
+  bool bounds_check(address addr, bool edge_ok = false, intptr_t size_in_bytes = -1) const PRODUCT_RETURN0;
 
   inline OopMapBlock* start_of_nonstatic_oop_maps() const;
   inline Klass** end_of_nonstatic_oop_maps() const;
 
   inline InstanceKlass* volatile* adr_implementor() const;
 
+  Array<InlineKlass*>* inline_type_field_klasses_array() const { return _inline_type_field_klasses; }
+  void set_inline_type_field_klasses_array(Array<InlineKlass*>* array) { _inline_type_field_klasses = array; }
+
+  Array<int>* null_marker_offsets_array() const { return _null_marker_offsets; }
+  void set_null_marker_offsets_array(Array<int>* array) { _null_marker_offsets = array; }
+
+  inline InlineKlass* get_inline_type_field_klass(int idx) const;
+  inline InlineKlass* get_inline_type_field_klass_or_null(int idx) const;
+  inline void set_inline_type_field_klass(int idx, InlineKlass* k);
+  inline void reset_inline_type_field_klass(int idx);
+
   // Use this to return the size of an instance in heap words:
-  int size_helper() const {
+  virtual int size_helper() const {
     return layout_helper_to_size_helper(layout_helper());
   }
 
   // This bit is initialized in classFileParser.cpp.
   // It is false under any of the following conditions:
@@ -1007,10 +1088,11 @@
 
   virtual void release_C_heap_structures(bool release_sub_metadata = true);
 
   // Naming
   const char* signature_name() const;
+  const char* signature_name_of_carrier(char c) const;
 
   // Oop fields (and metadata) iterators
   //
   // The InstanceKlass iterators also visits the Object's klass.
 
@@ -1132,11 +1214,11 @@
 #if INCLUDE_CDS
   // CDS support - remove and restore oops from metadata. Oops are not shared.
   virtual void remove_unshareable_info();
   void remove_unshareable_flags();
   virtual void remove_java_mirror();
-  void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, PackageEntry* pkg_entry, TRAPS);
+  virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, PackageEntry* pkg_entry, TRAPS);
   void init_shared_package_entry();
   bool can_be_verified_at_dumptime() const;
   bool methods_contain_jsr_bytecode() const;
   void compute_has_loops_flag_for_methods();
 #endif
diff a/src/hotspot/share/oops/instanceKlass.inline.hpp b/src/hotspot/share/oops/instanceKlass.inline.hpp
--- a/src/hotspot/share/oops/instanceKlass.inline.hpp
+++ b/src/hotspot/share/oops/instanceKlass.inline.hpp
@@ -61,15 +61,46 @@
   } else {
     return nullptr;
   }
 }
 
-inline ObjArrayKlass* InstanceKlass::array_klasses_acquire() const {
+inline InlineKlass* InstanceKlass::get_inline_type_field_klass(int idx) const {
+  assert(has_inline_type_fields(), "Sanity checking");
+  assert(idx < java_fields_count(), "IOOB");
+  InlineKlass* k = inline_type_field_klasses_array()->at(idx);
+  assert(k != nullptr, "Should always be set before being read");
+  return k;
+}
+
+inline InlineKlass* InstanceKlass::get_inline_type_field_klass_or_null(int idx) const {
+  assert(has_inline_type_fields(), "Sanity checking");
+  assert(idx < java_fields_count(), "IOOB");
+  InlineKlass* k = inline_type_field_klasses_array()->at(idx);
+  return k;
+}
+
+inline void InstanceKlass::set_inline_type_field_klass(int idx, InlineKlass* k) {
+  assert(has_inline_type_fields(), "Sanity checking");
+  assert(idx < java_fields_count(), "IOOB");
+  assert(k != nullptr, "Should not be set to nullptr");
+  assert(inline_type_field_klasses_array() != nullptr, "array must have been created");
+  assert(inline_type_field_klasses_array()->at(idx) == nullptr, "Should not be set twice");
+  inline_type_field_klasses_array()->at_put(idx, k);
+}
+
+inline void InstanceKlass::reset_inline_type_field_klass(int idx) {
+  assert(has_inline_type_fields(), "Sanity checking");
+  assert(idx < java_fields_count(), "IOOB");
+  inline_type_field_klasses_array()->at_put(idx, nullptr);
+}
+
+
+inline ArrayKlass* InstanceKlass::array_klasses_acquire() const {
   return Atomic::load_acquire(&_array_klasses);
 }
 
-inline void InstanceKlass::release_set_array_klasses(ObjArrayKlass* k) {
+inline void InstanceKlass::release_set_array_klasses(ArrayKlass* k) {
   Atomic::release_store(&_array_klasses, k);
 }
 
 inline jmethodID* InstanceKlass::methods_jmethod_ids_acquire() const {
   return Atomic::load_acquire(&_methods_jmethod_ids);
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
@@ -53,12 +53,23 @@
     flag(has_contended_annotations          , 1 << 10) /* has @Contended annotation */ \
     flag(has_localvariable_table            , 1 << 11) /* has localvariable information */ \
     flag(has_miranda_methods                , 1 << 12) /* True if this class has miranda methods in it's vtable */ \
     flag(has_vanilla_constructor            , 1 << 13) /* True if klass has a vanilla default constructor */ \
     flag(has_final_method                   , 1 << 14) /* True if klass has final method */ \
+    flag(has_inline_type_fields             , 1 << 15) /* has inline fields and related embedded section is not empty */ \
+    flag(is_empty_inline_type               , 1 << 16) /* empty inline type (*) */ \
+    flag(is_naturally_atomic                , 1 << 17) /* loaded/stored in one instruction */ \
+    flag(must_be_atomic                     , 1 << 18) /* doesn't allow tearing */ \
+    flag(has_loosely_consistent_annotation  , 1 << 19) /* the class has the LooselyConsistentValue annotation WARNING: it doesn't automatically mean that the class allows tearing */ \
+    flag(is_implicitly_constructible        , 1 << 20) /* the class has the ImplicitlyConstrutible annotation */
     /* end of list */
 
+  /* (*) An inline type is considered empty if it contains no non-static fields or
+     if it contains only empty inline fields. Note that JITs have a slightly different
+     definition: empty inline fields must be flat otherwise the container won't
+     be considered empty */
+
 #define IK_FLAGS_ENUM_NAME(name, value)    _misc_##name = value,
   enum {
     IK_FLAGS_DO(IK_FLAGS_ENUM_NAME)
   };
 #undef IK_FLAGS_ENUM_NAME
@@ -80,11 +91,11 @@
   u2 shared_loader_type_bits() const {
     return _misc_is_shared_boot_class|_misc_is_shared_platform_class|_misc_is_shared_app_class;
   }
 
   // These flags are write-once before the class is published and then read-only so don't require atomic updates.
-  u2 _flags;
+  u4 _flags;
 
   // These flags are written during execution so require atomic stores
   u1 _status;
 
  public:
@@ -106,10 +117,17 @@
   }
 
   void set_shared_class_loader_type(s2 loader_type);
 
   void assign_class_loader_type(const ClassLoaderData* cld);
+
+  u4 flags() const { return _flags; }
+
+  static u4 is_empty_inline_type_value() {
+    return _misc_is_empty_inline_type;
+  }
+
   void assert_is_safe(bool set) NOT_DEBUG_RETURN;
 
   // Create getters and setters for the status values.
 #define IK_STATUS_GET_SET(name, ignore)          \
   bool name() const { return (_status & _misc_##name) != 0; } \
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
@@ -198,11 +198,12 @@
 // "Normal" instantiation is preceded by a MetaspaceObj allocation
 // which zeros out memory - calloc equivalent.
 // The constructor is also used from CppVtableCloner,
 // which doesn't zero out the memory before calling the constructor.
 Klass::Klass(KlassKind kind) : _kind(kind),
-                           _shared_class_path_index(-1) {
+                               _prototype_header(markWord::prototype()),
+                               _shared_class_path_index(-1) {
   CDS_ONLY(_shared_class_flags = 0;)
   CDS_JAVA_HEAP_ONLY(_archived_mirror_index = -1;)
   _primary_supers[0] = this;
   set_super_check_offset(in_bytes(primary_supers_offset()));
 }
@@ -212,11 +213,11 @@
   // Note that T_ARRAY is not allowed here.
   int  hsize = arrayOopDesc::base_offset_in_bytes(etype);
   int  esize = type2aelembytes(etype);
   bool isobj = (etype == T_OBJECT);
   int  tag   =  isobj ? _lh_array_tag_obj_value : _lh_array_tag_type_value;
-  int lh = array_layout_helper(tag, hsize, etype, exact_log2(esize));
+  int lh = array_layout_helper(tag, false, hsize, etype, exact_log2(esize));
 
   assert(lh < (int)_lh_neutral_value, "must look like an array layout");
   assert(layout_helper_is_array(lh), "correct kind");
   assert(layout_helper_is_objArray(lh) == isobj, "correct kind");
   assert(layout_helper_is_typeArray(lh) == !isobj, "correct kind");
@@ -744,10 +745,12 @@
 
   if (WizardMode) {
      // print header
      obj->mark().print_on(st);
      st->cr();
+     st->print(BULLET"prototype_header: " INTPTR_FORMAT, _prototype_header.value());
+     st->cr();
   }
 
   // print class
   st->print(BULLET"klass: ");
   obj->klass()->print_value_on(st);
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
@@ -68,15 +68,17 @@
   friend class JVMCIVMStructs;
  public:
   // Klass Kinds for all subclasses of Klass
   enum KlassKind {
     InstanceKlassKind,
+    InlineKlassKind,
     InstanceRefKlassKind,
     InstanceMirrorKlassKind,
     InstanceClassLoaderKlassKind,
     InstanceStackChunkKlassKind,
     TypeArrayKlassKind,
+    FlatArrayKlassKind,
     ObjArrayKlassKind,
     UnknownKlassKind
   };
 
   static const uint KLASS_KIND_COUNT = ObjArrayKlassKind + 1;
@@ -99,11 +101,11 @@
   //
   // For arrays, layout helper is a negative number, containing four
   // distinct bytes, as follows:
   //    MSB:[tag, hsz, ebt, log2(esz)]:LSB
   // where:
-  //    tag is 0x80 if the elements are oops, 0xC0 if non-oops
+  //    tag is 0x80 if the elements are oops, 0xC0 if non-oops, 0xA0 if value types
   //    hsz is array header size in bytes (i.e., offset of first element)
   //    ebt is the BasicType of the elements
   //    esz is the element size in bytes
   // This packed word is arranged so as to be quickly unpacked by the
   // various fast paths that use the various subfields.
@@ -165,10 +167,11 @@
                                 // contention that may happen when a nearby object is modified.
   AccessFlags _access_flags;    // Access flags. The class/interface distinction is stored here.
 
   JFR_ONLY(DEFINE_TRACE_ID_FIELD;)
 
+  markWord _prototype_header;  // inline type and inline array mark patterns
 private:
   // This is an index into FileMapHeader::_shared_path_table[], to
   // associate this class with the JAR file where it's loaded from during
   // dump time. If a class is not loaded from the shared archive, this field is
   // -1.
@@ -407,15 +410,23 @@
   static const int _lh_log2_element_size_mask  = BitsPerLong-1;
   static const int _lh_element_type_shift      = BitsPerByte*1;
   static const int _lh_element_type_mask       = right_n_bits(BitsPerByte);  // shifted mask
   static const int _lh_header_size_shift       = BitsPerByte*2;
   static const int _lh_header_size_mask        = right_n_bits(BitsPerByte);  // shifted mask
-  static const int _lh_array_tag_bits          = 2;
+  static const int _lh_array_tag_bits          = 3;
   static const int _lh_array_tag_shift         = BitsPerInt - _lh_array_tag_bits;
-  static const int _lh_array_tag_obj_value     = ~0x01;   // 0x80000000 >> 30
 
-  static const unsigned int _lh_array_tag_type_value = 0Xffffffff; // ~0x00,  // 0xC0000000 >> 30
+  static const unsigned int _lh_array_tag_type_value = 0Xfffffffc;
+  static const unsigned int _lh_array_tag_vt_value   = 0Xfffffffd;
+  static const unsigned int _lh_array_tag_obj_value  = 0Xfffffffe;
+
+  // null-free array flag bit under the array tag bits, shift one more to get array tag value
+  static const int _lh_null_free_shift = _lh_array_tag_shift - 1;
+  static const int _lh_null_free_mask  = 1;
+
+  static const jint _lh_array_tag_flat_value_bit_inplace = (jint) (1 << _lh_array_tag_shift);
+  static const jint _lh_null_free_array_bit_inplace = (jint) (_lh_null_free_mask << _lh_null_free_shift);
 
   static int layout_helper_size_in_bytes(jint lh) {
     assert(lh > (jint)_lh_neutral_value, "must be instance");
     return (int) lh & ~_lh_instance_slow_path_bit;
   }
@@ -428,27 +439,37 @@
   }
   static bool layout_helper_is_array(jint lh) {
     return (jint)lh < (jint)_lh_neutral_value;
   }
   static bool layout_helper_is_typeArray(jint lh) {
-    // _lh_array_tag_type_value == (lh >> _lh_array_tag_shift);
-    return (juint)lh >= (juint)(_lh_array_tag_type_value << _lh_array_tag_shift);
+    return (juint) _lh_array_tag_type_value == (juint)(lh >> _lh_array_tag_shift);
   }
   static bool layout_helper_is_objArray(jint lh) {
-    // _lh_array_tag_obj_value == (lh >> _lh_array_tag_shift);
-    return (jint)lh < (jint)(_lh_array_tag_type_value << _lh_array_tag_shift);
+    return (juint)_lh_array_tag_obj_value == (juint)(lh >> _lh_array_tag_shift);
+  }
+  static bool layout_helper_is_flatArray(jint lh) {
+    return (juint)_lh_array_tag_vt_value == (juint)(lh >> _lh_array_tag_shift);
+  }
+  static bool layout_helper_is_null_free(jint lh) {
+    assert(layout_helper_is_flatArray(lh) || layout_helper_is_objArray(lh), "must be array of inline types");
+    return ((lh >> _lh_null_free_shift) & _lh_null_free_mask);
+  }
+  static jint layout_helper_set_null_free(jint lh) {
+    lh |= (_lh_null_free_mask << _lh_null_free_shift);
+    assert(layout_helper_is_null_free(lh), "Bad encoding");
+    return lh;
   }
   static int layout_helper_header_size(jint lh) {
     assert(lh < (jint)_lh_neutral_value, "must be array");
     int hsize = (lh >> _lh_header_size_shift) & _lh_header_size_mask;
     assert(hsize > 0 && hsize < (int)sizeof(oopDesc)*3, "sanity");
     return hsize;
   }
   static BasicType layout_helper_element_type(jint lh) {
     assert(lh < (jint)_lh_neutral_value, "must be array");
     int btvalue = (lh >> _lh_element_type_shift) & _lh_element_type_mask;
-    assert(btvalue >= T_BOOLEAN && btvalue <= T_OBJECT, "sanity");
+    assert((btvalue >= T_BOOLEAN && btvalue <= T_OBJECT) || btvalue == T_PRIMITIVE_OBJECT, "sanity");
     return (BasicType) btvalue;
   }
 
   // Want a pattern to quickly diff against layout header in register
   // find something less clever!
@@ -465,16 +486,17 @@
   }
 
   static int layout_helper_log2_element_size(jint lh) {
     assert(lh < (jint)_lh_neutral_value, "must be array");
     int l2esz = (lh >> _lh_log2_element_size_shift) & _lh_log2_element_size_mask;
-    assert(l2esz <= LogBytesPerLong,
+    assert(layout_helper_element_type(lh) == T_PRIMITIVE_OBJECT || l2esz <= LogBytesPerLong,
            "sanity. l2esz: 0x%x for lh: 0x%x", (uint)l2esz, (uint)lh);
     return l2esz;
   }
-  static jint array_layout_helper(jint tag, int hsize, BasicType etype, int log2_esize) {
+  static jint array_layout_helper(jint tag, bool null_free, int hsize, BasicType etype, int log2_esize) {
     return (tag        << _lh_array_tag_shift)
+      |    ((null_free ? 1 : 0) <<  _lh_null_free_shift)
       |    (hsize      << _lh_header_size_shift)
       |    ((int)etype << _lh_element_type_shift)
       |    (log2_esize << _lh_log2_element_size_shift);
   }
   static jint instance_layout_helper(jint size, bool slow_path_flag) {
@@ -623,11 +645,14 @@
  protected:
   virtual bool is_instance_klass_slow()     const { return false; }
   virtual bool is_array_klass_slow()        const { return false; }
   virtual bool is_objArray_klass_slow()     const { return false; }
   virtual bool is_typeArray_klass_slow()    const { return false; }
+  virtual bool is_flatArray_klass_slow()    const { return false; }
 #endif // ASSERT
+  // current implementation uses this method even in non debug builds
+  virtual bool is_inline_klass_slow()       const { return false; }
  public:
 
   // Fast non-virtual versions
   #ifndef ASSERT
   #define assert_same_query(xval, xcheck) xval
@@ -639,31 +664,35 @@
   }
  public:
   #endif
 
   bool is_instance_klass()              const { return assert_same_query(_kind <= InstanceStackChunkKlassKind, is_instance_klass_slow()); }
+  bool is_inline_klass()                const { return is_inline_klass_slow(); } //temporary hack
   // Other is anything that is not one of the more specialized kinds of InstanceKlass.
-  bool is_other_instance_klass()        const { return _kind == InstanceKlassKind; }
+  bool is_other_instance_klass()        const { return _kind <= InlineKlassKind; }
   bool is_reference_instance_klass()    const { return _kind == InstanceRefKlassKind; }
   bool is_mirror_instance_klass()       const { return _kind == InstanceMirrorKlassKind; }
   bool is_class_loader_instance_klass() const { return _kind == InstanceClassLoaderKlassKind; }
   bool is_array_klass()                 const { return assert_same_query( _kind >= TypeArrayKlassKind, is_array_klass_slow()); }
   bool is_stack_chunk_instance_klass()  const { return _kind == InstanceStackChunkKlassKind; }
+  bool is_flatArray_klass()             const { return assert_same_query( _kind == FlatArrayKlassKind, is_flatArray_klass_slow()); }
   bool is_objArray_klass()              const { return assert_same_query( _kind == ObjArrayKlassKind,  is_objArray_klass_slow()); }
   bool is_typeArray_klass()             const { return assert_same_query( _kind == TypeArrayKlassKind, is_typeArray_klass_slow()); }
   #undef assert_same_query
 
+  inline bool is_null_free_array_klass()      const { return layout_helper_is_null_free(layout_helper()); }
+
   // Access flags
   AccessFlags access_flags() const         { return _access_flags;  }
   void set_access_flags(AccessFlags flags) { _access_flags = flags; }
 
   bool is_public() const                { return _access_flags.is_public(); }
   bool is_final() const                 { return _access_flags.is_final(); }
   bool is_interface() const             { return _access_flags.is_interface(); }
   bool is_abstract() const              { return _access_flags.is_abstract(); }
-  bool is_super() const                 { return _access_flags.is_super(); }
   bool is_synthetic() const             { return _access_flags.is_synthetic(); }
+  bool is_identity_class() const        { return _access_flags.is_identity_class(); }
   void set_is_synthetic()               { _access_flags.set_is_synthetic(); }
   bool has_finalizer() const            { return _access_flags.has_finalizer(); }
   void set_has_finalizer()              { _access_flags.set_has_finalizer(); }
   bool is_hidden() const                { return access_flags().is_hidden_class(); }
   void set_is_hidden()                  { _access_flags.set_is_hidden_class(); }
@@ -673,10 +702,21 @@
   inline bool is_non_strong_hidden() const;
 
   bool is_cloneable() const;
   void set_is_cloneable();
 
+  // inline types and inline type array patterns
+  markWord prototype_header() const {
+    return _prototype_header;
+  }
+  static inline markWord default_prototype_header(Klass* k) {
+    return (k == nullptr) ? markWord::prototype() : k->prototype_header();
+  }
+
+  inline void set_prototype_header(markWord header);
+  static ByteSize prototype_header_offset() { return in_ByteSize(offset_of(Klass, _prototype_header)); }
+
   JFR_ONLY(DEFINE_TRACE_ID_METHODS;)
 
   virtual void metaspace_pointers_do(MetaspaceClosure* iter);
   virtual MetaspaceObj::Type type() const { return ClassType; }
 
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
@@ -50,10 +50,28 @@
 // of this function must ensure that a safepoint doesn't happen while interpreting the return value.
 inline bool Klass::is_loader_alive() const {
   return class_loader_data()->is_alive();
 }
 
+inline void Klass::set_prototype_header(markWord header) {
+  assert(!is_inline_klass() || header.is_inline_type(), "Unexpected prototype");
+  assert(_prototype_header.value() == 0 || _prototype_header == markWord::prototype(),
+         "Prototype already set");
+#ifdef _LP64
+    assert(header == markWord::prototype() ||
+           header.is_inline_type() ||
+           header.is_flat_array() ||
+           header.is_null_free_array(),
+           "unknown prototype header");
+#else
+    assert(header == markWord::prototype() ||
+           header.is_inline_type(),
+           "unknown prototype header");
+#endif
+  _prototype_header = header;
+}
+
 inline oop Klass::java_mirror() const {
   return _java_mirror.resolve();
 }
 
 inline oop Klass::java_mirror_no_keepalive() const {
diff a/src/hotspot/share/oops/klassVtable.cpp b/src/hotspot/share/oops/klassVtable.cpp
--- a/src/hotspot/share/oops/klassVtable.cpp
+++ b/src/hotspot/share/oops/klassVtable.cpp
@@ -1228,13 +1228,14 @@
   initialize_itable(supers);
   check_constraints(supers, CHECK);
 }
 
 inline bool interface_method_needs_itable_index(Method* m) {
-  if (m->is_static())           return false;   // e.g., Stream.empty
-  if (m->is_initializer())      return false;   // <init> or <clinit>
-  if (m->is_private())          return false;   // uses direct call
+  if (m->is_static())             return false;   // e.g., Stream.empty
+  if (m->is_private())            return false;   // uses direct call
+  if (m->is_object_constructor()) return false;   // <init>(...)V
+  if (m->is_class_initializer())  return false;   // <clinit>()V
   // If an interface redeclares a method from java.lang.Object,
   // it should already have a vtable index, don't touch it.
   // e.g., CharSequence.toString (from initialize_vtable)
   // if (m->has_vtable_index())  return false; // NO!
   return true;
@@ -1439,10 +1440,22 @@
 class InterfaceVisiterClosure : public StackObj {
  public:
   virtual void doit(InstanceKlass* intf, int method_count) = 0;
 };
 
+int count_interface_methods_needing_itable_index(Array<Method*>* methods) {
+  int method_count = 0;
+  if (methods->length() > 0) {
+    for (int i = methods->length(); --i >= 0; ) {
+      if (interface_method_needs_itable_index(methods->at(i))) {
+        method_count++;
+      }
+    }
+  }
+  return method_count;
+}
+
 // Visit all interfaces with at least one itable method
 void visit_all_interfaces(Array<InstanceKlass*>* transitive_intf, InterfaceVisiterClosure *blk) {
   // Handle array argument
   for(int i = 0; i < transitive_intf->length(); i++) {
     InstanceKlass* intf = transitive_intf->at(i);
diff a/src/hotspot/share/oops/klassVtable.hpp b/src/hotspot/share/oops/klassVtable.hpp
--- a/src/hotspot/share/oops/klassVtable.hpp
+++ b/src/hotspot/share/oops/klassVtable.hpp
@@ -293,11 +293,14 @@
                                            return &((itableOffsetEntry*)vtable_start())[i]; }
 
   itableMethodEntry* method_entry(int i) { assert(0 <= i && i <= _size_method_table, "index out of bounds");
                                            return &((itableMethodEntry*)method_start())[i]; }
 
-  int size_offset_table()                { return _size_offset_table; }
+  InstanceKlass* klass() const          { return _klass; }
+  int table_offset() const              { return _table_offset; }
+  int size_offset_table() const         { return _size_offset_table; }
+  int size_method_table() const         { return _size_method_table; }
 
   // Initialization
   void initialize_itable_and_check_constraints(TRAPS);
   void initialize_itable(GrowableArray<Method*>* supers = nullptr);
 
diff a/src/hotspot/share/oops/markWord.hpp b/src/hotspot/share/oops/markWord.hpp
--- a/src/hotspot/share/oops/markWord.hpp
+++ b/src/hotspot/share/oops/markWord.hpp
@@ -57,13 +57,78 @@
 //    [ptr             | 11]  marked             used to mark an object
 //    [0 ............ 0| 00]  inflating          inflation in progress (stack-locking in use)
 //
 //    We assume that stack/thread pointers have the lowest two bits cleared.
 //
+//
 //  - INFLATING() is a distinguished markword value of all zeros that is
 //    used when inflating an existing stack-lock into an ObjectMonitor.
 //    See below for is_being_inflated() and INFLATING().
+//
+//
+//
+//  Valhalla
+//
+//  <CMH: merge this doc into the text above>
+//
+//  Project Valhalla has mark word encoding requirements for the following oops:
+//
+//  * inline types: have alternative bytecode behavior, e.g. can not be locked
+//    - "larval state": mutable state, but only during object init, observable
+//      by only by a single thread (generally do not mutate markWord)
+//
+//  * flat arrays: load/decode of klass layout helper is expensive for aaload
+//
+//  * "null free" arrays: load/decode of klass layout helper again for aaload
+//
+//  EnableValhalla
+//
+//  Formerly known as "biased lock bit", "unused_gap" is free to use: using this
+//  bit to indicate inline type, combined with "unlocked" lock bits, means we
+//  will not interfere with lock encodings (displaced, inflating, and monitor),
+//  since inline types can't be locked.
+//
+//  Further state encoding
+//
+//  32 bit plaforms currently have no further room for encoding. No room for
+//  "denormalized layout helper bits", these fast mark word tests can only be made on
+//  64 bit platforms. 32-bit platforms need to load the klass->_layout_helper. This
+//  said, the larval state bit is still required for operation, stealing from the hash
+//  code is simplest mechanism.
+//
+//  Valhalla specific encodings
+//
+//  Revised Bit-format of an object header (most significant first, big endian layout below):
+//
+//  32 bits:
+//  --------
+//  hash:24 ------------>| larval:1 age:4 inline_type:1 lock:2
+//
+//  64 bits:
+//  --------
+//  unused:1 | <-- hash:31 -->| unused:22 larval:1 age:4 flat_array:1 null_free_array:1 inline_type:1 lock:2
+//
+//  The "fast" static type bits (flat_array, null_free_array, and inline_type)
+//  are placed lowest next to lock bits to more easily decode forwarding pointers.
+//  G1 for example, implicitly clears age bits ("G1FullGCCompactionPoint::forward()")
+//  using "oopDesc->forwardee()", so it necessary for "markWord::decode_pointer()"
+//  to return a non-nullptr for this case, but not confuse the static type bits for
+//  a pointer.
+//
+//  Static types bits are recorded in the "klass->prototype_header()", displaced
+//  mark should simply use the prototype header as "slow path", rather chasing
+//  monitor or stack lock races.
+//
+//  Lock patterns (note inline types can't be locked/monitor/inflating)...
+//
+//  [ptr            | 000]  locked             ptr points to real header on stack
+//  [header         | ?01]  unlocked           regular object header
+//  [ptr            | 010]  monitor            inflated lock (header is wapped out)
+//  [ptr            | ?11]  marked             used to mark an object
+//  [0 ............ | 000]  inflating          inflation in progress
+//
+//
 
 class BasicLock;
 class ObjectMonitor;
 class JavaThread;
 class outputStream;
@@ -98,55 +163,98 @@
   }
 
   // Conversion
   uintptr_t value() const { return _value; }
 
-  // Constants
-  static const int age_bits                       = 4;
+  // Constants, in least significant bit order
   static const int lock_bits                      = 2;
-  static const int first_unused_gap_bits          = 1;
-  static const int max_hash_bits                  = BitsPerWord - age_bits - lock_bits - first_unused_gap_bits;
+  static const int first_unused_gap_bits          = 1; // When !EnableValhalla
+  // EnableValhalla: static prototype header bits (fast path instead of klass layout_helper)
+  static const int inline_type_bits               = 1;
+  static const int null_free_array_bits           = LP64_ONLY(1) NOT_LP64(0);
+  static const int flat_array_bits                = LP64_ONLY(1) NOT_LP64(0);
+  // instance state
+  static const int age_bits                       = 4;
+  static const int larval_bits                    = 1;
+  static const int max_hash_bits                  = BitsPerWord - age_bits - lock_bits - inline_type_bits - larval_bits - flat_array_bits - null_free_array_bits;
   static const int hash_bits                      = max_hash_bits > 31 ? 31 : max_hash_bits;
-  static const int second_unused_gap_bits         = LP64_ONLY(1) NOT_LP64(0);
+  static const int second_unused_gap_bits         = LP64_ONLY(1) NOT_LP64(0); // !EnableValhalla: unused
 
   static const int lock_shift                     = 0;
-  static const int age_shift                      = lock_bits + first_unused_gap_bits;
-  static const int hash_shift                     = age_shift + age_bits + second_unused_gap_bits;
+  static const int inline_type_shift              = lock_bits;
+  static const int null_free_array_shift          = inline_type_shift + inline_type_bits;
+  static const int flat_array_shift               = null_free_array_shift + null_free_array_bits;
+  static const int age_shift                      = flat_array_shift + flat_array_bits;
+  static const int unused_gap_shift               = age_shift + age_bits; // !EnableValhalla: unused
+  static const int larval_shift                   = age_shift + age_bits;
+  static const int hash_shift                     = LP64_ONLY(32) NOT_LP64(larval_shift + larval_bits);
 
   static const uintptr_t lock_mask                = right_n_bits(lock_bits);
   static const uintptr_t lock_mask_in_place       = lock_mask << lock_shift;
+  static const uintptr_t inline_type_mask         = right_n_bits(lock_bits + inline_type_bits);
+  static const uintptr_t inline_type_mask_in_place = inline_type_mask << lock_shift;
+  static const uintptr_t inline_type_bit_in_place = 1 << inline_type_shift;
+  static const uintptr_t null_free_array_mask     = right_n_bits(null_free_array_bits);
+  static const uintptr_t null_free_array_mask_in_place = (null_free_array_mask << null_free_array_shift) | lock_mask_in_place;
+  static const uintptr_t null_free_array_bit_in_place  = (1 << null_free_array_shift);
+  static const uintptr_t flat_array_mask          = right_n_bits(flat_array_bits);
+  static const uintptr_t flat_array_mask_in_place = (flat_array_mask << flat_array_shift) | null_free_array_mask_in_place | lock_mask_in_place;
+  static const uintptr_t flat_array_bit_in_place  = (1 << flat_array_shift);
+
   static const uintptr_t age_mask                 = right_n_bits(age_bits);
   static const uintptr_t age_mask_in_place        = age_mask << age_shift;
+
+  static const uintptr_t larval_mask              = right_n_bits(larval_bits);
+  static const uintptr_t larval_mask_in_place     = (larval_mask << larval_shift) | inline_type_mask_in_place;
+  static const uintptr_t larval_bit_in_place      = (1 << larval_shift);
+
   static const uintptr_t hash_mask                = right_n_bits(hash_bits);
   static const uintptr_t hash_mask_in_place       = hash_mask << hash_shift;
 
   static const uintptr_t locked_value             = 0;
   static const uintptr_t unlocked_value           = 1;
   static const uintptr_t monitor_value            = 2;
   static const uintptr_t marked_value             = 3;
 
+  static const uintptr_t inline_type_pattern      = inline_type_bit_in_place | unlocked_value;
+  static const uintptr_t null_free_array_pattern  = null_free_array_bit_in_place | unlocked_value;
+  static const uintptr_t flat_array_pattern       = flat_array_bit_in_place | null_free_array_pattern;
+  // Has static klass prototype, used for decode/encode pointer
+  static const uintptr_t static_prototype_mask    = LP64_ONLY(right_n_bits(inline_type_bits + flat_array_bits + null_free_array_bits)) NOT_LP64(right_n_bits(inline_type_bits));
+  static const uintptr_t static_prototype_mask_in_place = static_prototype_mask << lock_bits;
+  static const uintptr_t static_prototype_value_max = (1 << age_shift) - 1;
+
+  static const uintptr_t larval_pattern           = larval_bit_in_place | inline_type_pattern;
+
   static const uintptr_t no_hash                  = 0 ;  // no hash value assigned
   static const uintptr_t no_hash_in_place         = (uintptr_t)no_hash << hash_shift;
   static const uintptr_t no_lock_in_place         = unlocked_value;
 
   static const uint max_age                       = age_mask;
 
   // Creates a markWord with all bits set to zero.
   static markWord zero() { return markWord(uintptr_t(0)); }
 
+  bool is_inline_type() const {
+    return (mask_bits(value(), inline_type_mask_in_place) == inline_type_pattern);
+  }
+
   // lock accessors (note that these assume lock_shift == 0)
   bool is_locked()   const {
     return (mask_bits(value(), lock_mask_in_place) != unlocked_value);
   }
   bool is_unlocked() const {
     return (mask_bits(value(), lock_mask_in_place) == unlocked_value);
   }
   bool is_marked()   const {
     return (mask_bits(value(), lock_mask_in_place) == marked_value);
   }
+
+  // is unlocked and not an inline type (which cannot be involved in locking, displacement or inflation)
+  // i.e. test both lock bits and the inline type bit together
   bool is_neutral()  const {
-    return (mask_bits(value(), lock_mask_in_place) == unlocked_value);
+    return (mask_bits(value(), inline_type_mask_in_place) == unlocked_value);
   }
 
   // Special temporary state of the markWord while being inflated.
   // Code that looks at mark outside a lock need to take this into account.
   bool is_being_inflated() const { return (value() == 0); }
@@ -160,11 +268,11 @@
   // Fast-locking does not use INFLATING.
   static markWord INFLATING() { return zero(); }    // inflate-in-progress
 
   // Should this header be preserved during GC?
   bool must_be_preserved(const oopDesc* obj) const {
-    return (!is_unlocked() || !has_no_hash());
+    return (!is_unlocked() || !has_no_hash() || (EnableValhalla && is_larval_state()));
   }
 
   // WARNING: The following routines are used EXCLUSIVELY by
   // synchronization functions. They are not really gc safe.
   // They must get updated if markWord layout get changed.
@@ -245,23 +353,70 @@
 
   bool has_no_hash() const {
     return hash() == no_hash;
   }
 
+  // private buffered value operations
+  markWord enter_larval_state() const {
+    return markWord(value() | larval_bit_in_place);
+  }
+  markWord exit_larval_state() const {
+    return markWord(value() & ~larval_bit_in_place);
+  }
+  bool is_larval_state() const {
+    return (mask_bits(value(), larval_mask_in_place) == larval_pattern);
+  }
+
+#ifdef _LP64 // 64 bit encodings only
+  bool is_flat_array() const {
+    return (mask_bits(value(), flat_array_mask_in_place) == flat_array_pattern);
+  }
+
+  bool is_null_free_array() const {
+    return (mask_bits(value(), null_free_array_mask_in_place) == null_free_array_pattern);
+  }
+#else
+  bool is_flat_array() const {
+    fatal("Should not ask this for mark word, ask oopDesc");
+    return false;
+  }
+
+  bool is_null_free_array() const {
+    fatal("Should not ask this for mark word, ask oopDesc");
+    return false;
+  }
+#endif
   // Prototype mark for initialization
   static markWord prototype() {
     return markWord( no_hash_in_place | no_lock_in_place );
   }
 
+  static markWord inline_type_prototype() {
+    return markWord(inline_type_pattern);
+  }
+
+#ifdef _LP64 // 64 bit encodings only
+  static markWord flat_array_prototype() {
+    return markWord(flat_array_pattern);
+  }
+
+  static markWord null_free_array_prototype() {
+    return markWord(null_free_array_pattern);
+  }
+#endif
+
   // Debugging
   void print_on(outputStream* st, bool print_monitor_info = true) const;
 
   // Prepare address of oop for placement into mark
   inline static markWord encode_pointer_as_mark(void* p) { return from_pointer(p).set_marked(); }
 
   // Recover address of oop from encoded form used in mark
-  inline void* decode_pointer() { return (void*)clear_lock_bits().value(); }
+  inline void* decode_pointer() {
+    return (EnableValhalla && _value < static_prototype_value_max) ? nullptr :
+      (void*) (clear_lock_bits().value());
+  }
 };
 
 // Support atomic operations.
 template<>
 struct PrimitiveConversions::Translate<markWord> : public std::true_type {
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
@@ -57,10 +57,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/inlineKlass.inline.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "prims/methodHandles.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/continuationEntry.hpp"
 #include "runtime/frame.inline.hpp"
@@ -114,11 +115,10 @@
 
   if (access_flags.is_native()) {
     clear_native_function();
     set_signature_handler(nullptr);
   }
-
   NOT_PRODUCT(set_compiled_invocation_count(0);)
   // Name is very useful for debugging.
   NOT_PRODUCT(_name = name;)
 }
 
@@ -152,15 +152,25 @@
 address Method::get_c2i_entry() {
   assert(adapter() != nullptr, "must have");
   return adapter()->get_c2i_entry();
 }
 
+address Method::get_c2i_inline_entry() {
+  assert(adapter() != nullptr, "must have");
+  return adapter()->get_c2i_inline_entry();
+}
+
 address Method::get_c2i_unverified_entry() {
   assert(adapter() != nullptr, "must have");
   return adapter()->get_c2i_unverified_entry();
 }
 
+address Method::get_c2i_unverified_inline_entry() {
+  assert(adapter() != nullptr, "must have");
+  return adapter()->get_c2i_unverified_inline_entry();
+}
+
 address Method::get_c2i_no_clinit_check_entry() {
   assert(VM_Version::supports_fast_class_init_checks(), "");
   assert(adapter() != nullptr, "must have");
   return adapter()->get_c2i_no_clinit_check_entry();
 }
@@ -384,11 +394,11 @@
 }
 
 void Method::metaspace_pointers_do(MetaspaceClosure* it) {
   log_trace(cds)("Iter(Method): %p", this);
 
-  if (!method_holder()->is_rewritten()) {
+  if (!method_holder()->is_rewritten() || CDSConfig::is_valhalla_preview()) {
     it->push(&_constMethod, MetaspaceClosure::_writable);
   } else {
     it->push(&_constMethod);
   }
   it->push(&_method_data);
@@ -661,16 +671,29 @@
 int Method::extra_stack_words() {
   // not an inline function, to avoid a header dependency on Interpreter
   return extra_stack_entries() * Interpreter::stackElementSize;
 }
 
+// InlineKlass the method is declared to return. This must not
+// safepoint as it is called with references live on the stack at
+// locations the GC is unaware of.
+InlineKlass* Method::returns_inline_type(Thread* thread) const {
+  assert(InlineTypeReturnedAsFields, "Inline types should never be returned as fields");
+  NoSafepointVerifier nsv;
+  SignatureStream ss(signature());
+  while (!ss.at_return_type()) {
+    ss.next();
+  }
+  return ss.as_inline_klass(method_holder());
+}
+
 bool Method::is_vanilla_constructor() const {
   // Returns true if this method is a vanilla constructor, i.e. an "<init>" "()V" method
   // which only calls the superclass vanilla constructor and possibly does stores of
   // zero constants to local fields:
   //
-  //   aload_0
+  //   aload_0, _fast_aload_0, or _nofast_aload_0
   //   invokespecial
   //   indexbyte1
   //   indexbyte2
   //
   // followed by an (optional) sequence of:
@@ -690,11 +713,12 @@
   int size = code_size();
   // Check if size match
   if (size == 0 || size % 5 != 0) return false;
   address cb = code_base();
   int last = size - 1;
-  if (cb[0] != Bytecodes::_aload_0 || cb[1] != Bytecodes::_invokespecial || cb[last] != Bytecodes::_return) {
+  if ((cb[0] != Bytecodes::_aload_0 && cb[0] != Bytecodes::_fast_aload_0 && cb[0] != Bytecodes::_nofast_aload_0) ||
+       cb[1] != Bytecodes::_invokespecial || cb[last] != Bytecodes::_return) {
     // Does not call superclass default constructor
     return false;
   }
   // Check optional sequence
   for (int i = 4; i < last; i += 5) {
@@ -852,10 +876,15 @@
     case Bytecodes::_areturn:
       break;
     default:
       return false;
   }
+  if (has_scalarized_return()) {
+    // Don't treat this as (trivial) getter method because the
+    // inline type should be returned in a scalarized form.
+    return false;
+  }
   return true;
 }
 
 bool Method::is_setter() const {
   if (code_size() != 6) return false;
@@ -873,42 +902,41 @@
     default:
       return false;
   }
   if (java_code_at(2) != Bytecodes::_putfield) return false;
   if (java_code_at(5) != Bytecodes::_return)   return false;
+  if (has_scalarized_args()) {
+    // Don't treat this as (trivial) setter method because the
+    // inline type argument should be passed in a scalarized form.
+    return false;
+  }
   return true;
 }
 
 bool Method::is_constant_getter() const {
   int last_index = code_size() - 1;
   // Check if the first 1-3 bytecodes are a constant push
   // and the last bytecode is a return.
   return (2 <= code_size() && code_size() <= 4 &&
           Bytecodes::is_const(java_code_at(0)) &&
           Bytecodes::length_for(java_code_at(0)) == last_index &&
-          Bytecodes::is_return(java_code_at(last_index)));
+          Bytecodes::is_return(java_code_at(last_index)) &&
+          !has_scalarized_args());
 }
 
-bool Method::is_initializer() const {
-  return is_object_initializer() || is_static_initializer();
-}
-
-bool Method::has_valid_initializer_flags() const {
-  return (is_static() ||
-          method_holder()->major_version() < 51);
-}
-
-bool Method::is_static_initializer() const {
+bool Method::is_class_initializer() const {
   // For classfiles version 51 or greater, ensure that the clinit method is
   // static.  Non-static methods with the name "<clinit>" are not static
   // initializers. (older classfiles exempted for backward compatibility)
-  return name() == vmSymbols::class_initializer_name() &&
-         has_valid_initializer_flags();
+  return (name() == vmSymbols::class_initializer_name() &&
+          (is_static() ||
+           method_holder()->major_version() < 51));
 }
 
-bool Method::is_object_initializer() const {
-   return name() == vmSymbols::object_initializer_name();
+// A method named <init>, is a classic object constructor.
+bool Method::is_object_constructor() const {
+  return name() == vmSymbols::object_initializer_name();
 }
 
 bool Method::needs_clinit_barrier() const {
   return is_static() && !method_holder()->is_initialized();
 }
@@ -962,11 +990,11 @@
   return best_line;
 }
 
 
 bool Method::is_klass_loaded_by_klass_index(int klass_index) const {
-  if( constants()->tag_at(klass_index).is_unresolved_klass() ) {
+  if( constants()->tag_at(klass_index).is_unresolved_klass()) {
     Thread *thread = Thread::current();
     Symbol* klass_name = constants()->klass_name_at(klass_index);
     Handle loader(thread, method_holder()->class_loader());
     Handle prot  (thread, method_holder()->protection_domain());
     return SystemDictionary::find_instance_klass(thread, klass_name, loader, prot) != nullptr;
@@ -978,11 +1006,13 @@
 
 bool Method::is_klass_loaded(int refinfo_index, Bytecodes::Code bc, bool must_be_resolved) const {
   int klass_index = constants()->klass_ref_index_at(refinfo_index, bc);
   if (must_be_resolved) {
     // Make sure klass is resolved in constantpool.
-    if (constants()->tag_at(klass_index).is_unresolved_klass()) return false;
+    if (constants()->tag_at(klass_index).is_unresolved_klass()) {
+      return false;
+    }
   }
   return is_klass_loaded_by_klass_index(klass_index);
 }
 
 
@@ -1147,12 +1177,16 @@
 void Method::clear_code() {
   // this may be null if c2i adapters have not been made yet
   // Only should happen at allocate time.
   if (adapter() == nullptr) {
     _from_compiled_entry    = nullptr;
+    _from_compiled_inline_entry = nullptr;
+    _from_compiled_inline_ro_entry = nullptr;
   } else {
     _from_compiled_entry    = adapter()->get_c2i_entry();
+    _from_compiled_inline_entry = adapter()->get_c2i_inline_entry();
+    _from_compiled_inline_ro_entry = adapter()->get_c2i_inline_ro_entry();
   }
   OrderAccess::storestore();
   _from_interpreted_entry = _i2i_entry;
   OrderAccess::storestore();
   _code = nullptr;
@@ -1180,10 +1214,12 @@
   assert(CDSConfig::is_dumping_archive(), "sanity");
   _code = nullptr;
   _adapter = nullptr;
   _i2i_entry = nullptr;
   _from_compiled_entry = nullptr;
+  _from_compiled_inline_entry = nullptr;
+  _from_compiled_inline_ro_entry = nullptr;
   _from_interpreted_entry = nullptr;
 
   if (is_native()) {
     *native_function_addr() = nullptr;
     set_signature_handler(nullptr);
@@ -1233,10 +1269,13 @@
   if (is_native() && !has_native_function()) {
     set_native_function(
       SharedRuntime::native_method_throw_unsatisfied_link_error_entry(),
       !native_bind_event_is_interesting);
   }
+  if (InlineTypeReturnedAsFields && returns_inline_type(THREAD) && !has_scalarized_return()) {
+    set_has_scalarized_return();
+  }
 
   // Setup compiler entrypoint.  This is made eagerly, so we do not need
   // special handling of vtables.  An alternative is to make adapters more
   // lazily by calling make_adapter() from from_compiled_entry() for the
   // normal calls.  For vtable calls life gets more complicated.  When a
@@ -1272,10 +1311,12 @@
     }
   }
 
   mh->set_adapter_entry(adapter);
   mh->_from_compiled_entry = adapter->get_c2i_entry();
+  mh->_from_compiled_inline_entry = adapter->get_c2i_inline_entry();
+  mh->_from_compiled_inline_ro_entry = adapter->get_c2i_inline_ro_entry();
   return adapter->get_c2i_entry();
 }
 
 // The verified_code_entry() must be called when a invoke is resolved
 // on this method.
@@ -1288,10 +1329,22 @@
   debug_only(NoSafepointVerifier nsv;)
   assert(_from_compiled_entry != nullptr, "must be set");
   return _from_compiled_entry;
 }
 
+address Method::verified_inline_code_entry() {
+  debug_only(NoSafepointVerifier nsv;)
+  assert(_from_compiled_inline_entry != nullptr, "must be set");
+  return _from_compiled_inline_entry;
+}
+
+address Method::verified_inline_ro_code_entry() {
+  debug_only(NoSafepointVerifier nsv;)
+  assert(_from_compiled_inline_ro_entry != nullptr, "must be set");
+  return _from_compiled_inline_ro_entry;
+}
+
 // Check that if an nmethod ref exists, it has a backlink to this or no backlink at all
 // (could be racing a deopt).
 // Not inline to avoid circular ref.
 bool Method::check_code() const {
   // cached in a register or local.  There's a race on the value of the field.
@@ -1319,10 +1372,12 @@
     mh->set_highest_comp_level(comp_level);
   }
 
   OrderAccess::storestore();
   mh->_from_compiled_entry = code->verified_entry_point();
+  mh->_from_compiled_inline_entry = code->verified_inline_entry_point();
+  mh->_from_compiled_inline_ro_entry = code->verified_inline_ro_entry_point();
   OrderAccess::storestore();
 
   if (mh->is_continuation_native_intrinsic()) {
     assert(mh->_from_interpreted_entry == nullptr, "initialized incorrectly"); // see link_method
 
@@ -2309,10 +2364,35 @@
   } else {
     return false;
   }
 }
 
+bool Method::is_scalarized_arg(int idx) const {
+  if (!has_scalarized_args()) {
+    return false;
+  }
+  // Search through signature and check if argument is wrapped in T_METADATA/T_VOID
+  int depth = 0;
+  const GrowableArray<SigEntry>* sig = adapter()->get_sig_cc();
+  for (int i = 0; i < sig->length(); i++) {
+    BasicType bt = sig->at(i)._bt;
+    if (bt == T_METADATA) {
+      depth++;
+    }
+    if (idx == 0) {
+      break; // Argument found
+    }
+    if (bt == T_VOID && (sig->at(i-1)._bt != T_LONG && sig->at(i-1)._bt != T_DOUBLE)) {
+      depth--;
+    }
+    if (depth == 0 && bt != T_LONG && bt != T_DOUBLE) {
+      idx--; // Advance to next argument
+    }
+  }
+  return depth != 0;
+}
+
 #ifndef PRODUCT
 void Method::print_jmethod_ids_count(const ClassLoaderData* loader_data, outputStream* out) {
   out->print("%d", loader_data->jmethod_ids()->count_methods());
 }
 #endif // PRODUCT
@@ -2341,18 +2421,24 @@
   if (intrinsic_id() != vmIntrinsics::_none)
     st->print_cr(" - intrinsic id:      %d %s", vmIntrinsics::as_int(intrinsic_id()), vmIntrinsics::name_at(intrinsic_id()));
   if (highest_comp_level() != CompLevel_none)
     st->print_cr(" - highest level:     %d", highest_comp_level());
   st->print_cr(" - vtable index:      %d",   _vtable_index);
+#ifdef ASSERT
+  if (valid_itable_index())
+    st->print_cr(" - itable index:      %d",   itable_index());
+#endif
   st->print_cr(" - i2i entry:         " PTR_FORMAT, p2i(interpreter_entry()));
   st->print(   " - adapters:          ");
   AdapterHandlerEntry* a = ((Method*)this)->adapter();
   if (a == nullptr)
     st->print_cr(PTR_FORMAT, p2i(a));
   else
     a->print_adapter_on(st);
-  st->print_cr(" - compiled entry     " PTR_FORMAT, p2i(from_compiled_entry()));
+  st->print_cr(" - compiled entry           " PTR_FORMAT, p2i(from_compiled_entry()));
+  st->print_cr(" - compiled inline entry    " PTR_FORMAT, p2i(from_compiled_inline_entry()));
+  st->print_cr(" - compiled inline ro entry " PTR_FORMAT, p2i(from_compiled_inline_ro_entry()));
   st->print_cr(" - code size:         %d",   code_size());
   if (code_size() != 0) {
     st->print_cr(" - code start:        " PTR_FORMAT, p2i(code_base()));
     st->print_cr(" - code end (excl):   " PTR_FORMAT, p2i(code_base() + code_size()));
   }
@@ -2418,10 +2504,11 @@
 void Method::print_value_on(outputStream* st) const {
   assert(is_method(), "must be method");
   st->print("%s", internal_name());
   print_address_on(st);
   st->print(" ");
+  if (WizardMode) access_flags().print_on(st);
   name()->print_value_on(st);
   st->print(" ");
   signature()->print_value_on(st);
   st->print(" in ");
   method_holder()->print_value_on(st);
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
@@ -91,11 +91,13 @@
 #endif
   // Entry point for calling both from and to the interpreter.
   address _i2i_entry;           // All-args-on-stack calling convention
   // Entry point for calling from compiled code, to compiled code if it exists
   // or else the interpreter.
-  volatile address _from_compiled_entry;        // Cache of: _code ? _code->entry_point() : _adapter->c2i_entry()
+  volatile address _from_compiled_entry;           // Cache of: _code ? _code->verified_entry_point()           : _adapter->c2i_entry()
+  volatile address _from_compiled_inline_ro_entry; // Cache of: _code ? _code->verified_inline_ro_entry_point() : _adapter->c2i_inline_ro_entry()
+  volatile address _from_compiled_inline_entry;    // Cache of: _code ? _code->verified_inline_entry_point()    : _adapter->c2i_inline_entry()
   // The entry point for calling both from and to compiled code is
   // "_code->entry_point()".  Because of tiered compilation and de-opt, this
   // field can come and go.  It can transition from null to not-null at any
   // time (whenever a compile completes).  It can transition from not-null to
   // null only at safepoints (because of a de-opt).
@@ -130,10 +132,12 @@
   void set_constMethod(ConstMethod* xconst)    { _constMethod = xconst; }
 
 
   static address make_adapters(const methodHandle& mh, TRAPS);
   address from_compiled_entry() const;
+  address from_compiled_inline_ro_entry() const;
+  address from_compiled_inline_entry() const;
   address from_interpreted_entry() const;
 
   // access flag
   AccessFlags access_flags() const               { return _access_flags;  }
   void set_access_flags(AccessFlags flags)       { _access_flags = flags; }
@@ -354,10 +358,12 @@
   int64_t  compiled_invocation_count() const    { return 0; }
 #endif // not PRODUCT
 
   // nmethod/verified compiler entry
   address verified_code_entry();
+  address verified_inline_code_entry();
+  address verified_inline_ro_code_entry();
   bool check_code() const;      // Not inline to avoid circular ref
   CompiledMethod* code() const;
 
   // Locks CompiledMethod_lock if not held.
   void unlink_code(CompiledMethod *compare);
@@ -376,16 +382,24 @@
   static void set_code(const methodHandle& mh, CompiledMethod* code);
   void set_adapter_entry(AdapterHandlerEntry* adapter) {
     _adapter = adapter;
   }
   void set_from_compiled_entry(address entry) {
-    _from_compiled_entry =  entry;
+    _from_compiled_entry = entry;
+  }
+  void set_from_compiled_inline_ro_entry(address entry) {
+    _from_compiled_inline_ro_entry = entry;
+  }
+  void set_from_compiled_inline_entry(address entry) {
+    _from_compiled_inline_entry = entry;
   }
 
   address get_i2c_entry();
   address get_c2i_entry();
+  address get_c2i_inline_entry();
   address get_c2i_unverified_entry();
+  address get_c2i_unverified_inline_entry();
   address get_c2i_no_clinit_check_entry();
   AdapterHandlerEntry* adapter() const {
     return _adapter;
   }
   // setup entry points
@@ -497,11 +511,11 @@
   InstanceKlass* method_holder() const         { return constants()->pool_holder(); }
 
   Symbol* klass_name() const;                    // returns the name of the method holder
   BasicType result_type() const                  { return constMethod()->result_type(); }
   bool is_returning_oop() const                  { BasicType r = result_type(); return is_reference_type(r); }
-  bool is_returning_fp() const                   { BasicType r = result_type(); return (r == T_FLOAT || r == T_DOUBLE); }
+  InlineKlass* returns_inline_type(Thread* thread) const;
 
   // Checked exceptions thrown by this method (resolved to mirrors)
   objArrayHandle resolved_checked_exceptions(TRAPS) { return resolved_checked_exceptions_impl(this, THREAD); }
 
   // Access flags
@@ -576,22 +590,16 @@
   bool is_setter() const;
 
   // returns true if the method does nothing but return a constant of primitive type
   bool is_constant_getter() const;
 
-  // returns true if the method is an initializer (<init> or <clinit>).
-  bool is_initializer() const;
-
-  // returns true if the method is static OR if the classfile version < 51
-  bool has_valid_initializer_flags() const;
-
   // returns true if the method name is <clinit> and the method has
   // valid static initializer flags.
-  bool is_static_initializer() const;
+  bool is_class_initializer() const;
 
   // returns true if the method name is <init>
-  bool is_object_initializer() const;
+  bool is_object_constructor() const;
 
   // compiled code support
   // NOTE: code() is inherently racy as deopt can be clearing code
   // simultaneously. Use with caution.
   bool has_compiled_code() const;
@@ -609,11 +617,14 @@
 
   // interpreter support
   static ByteSize const_offset()                 { return byte_offset_of(Method, _constMethod       ); }
   static ByteSize access_flags_offset()          { return byte_offset_of(Method, _access_flags      ); }
   static ByteSize from_compiled_offset()         { return byte_offset_of(Method, _from_compiled_entry); }
+  static ByteSize from_compiled_inline_offset()  { return byte_offset_of(Method, _from_compiled_inline_entry); }
+  static ByteSize from_compiled_inline_ro_offset(){ return byte_offset_of(Method, _from_compiled_inline_ro_entry); }
   static ByteSize code_offset()                  { return byte_offset_of(Method, _code); }
+  static ByteSize flags_offset()                 { return byte_offset_of(Method, _flags); }
 
   static ByteSize method_counters_offset()       {
     return byte_offset_of(Method, _method_counters);
   }
 #ifndef PRODUCT
@@ -768,10 +779,27 @@
   void set_has_injected_profile() { constMethod()->set_has_injected_profile(); }
 
   bool has_reserved_stack_access() const { return constMethod()->reserved_stack_access(); }
   void set_has_reserved_stack_access() { constMethod()->set_reserved_stack_access(); }
 
+  bool has_scalarized_args() const { return constMethod()->has_scalarized_args(); }
+  void set_has_scalarized_args() { constMethod()->set_has_scalarized_args(); }
+
+  bool has_scalarized_return() const { return constMethod()->has_scalarized_return(); }
+  void set_has_scalarized_return() { constMethod()->set_has_scalarized_return(); }
+
+  bool is_scalarized_arg(int idx) const;
+
+  bool c1_needs_stack_repair() const { return constMethod()->c1_needs_stack_repair(); }
+  void set_c1_needs_stack_repair() { constMethod()->set_c1_needs_stack_repair(); }
+
+  bool c2_needs_stack_repair() const { return constMethod()->c2_needs_stack_repair(); }
+  void set_c2_needs_stack_repair() { constMethod()->set_c2_needs_stack_repair(); }
+
+  bool mismatch() const { return constMethod()->mismatch(); }
+  void set_mismatch() { constMethod()->set_mismatch(); }
+
   JFR_ONLY(DEFINE_TRACE_FLAG_ACCESSOR;)
 
   ConstMethod::MethodType method_type() const {
       return _constMethod->method_type();
   }
diff a/src/hotspot/share/oops/method.inline.hpp b/src/hotspot/share/oops/method.inline.hpp
--- a/src/hotspot/share/oops/method.inline.hpp
+++ b/src/hotspot/share/oops/method.inline.hpp
@@ -33,10 +33,18 @@
 
 inline address Method::from_compiled_entry() const {
   return Atomic::load_acquire(&_from_compiled_entry);
 }
 
+inline address Method::from_compiled_inline_ro_entry() const {
+  return Atomic::load_acquire(&_from_compiled_inline_ro_entry);
+}
+
+inline address Method::from_compiled_inline_entry() const {
+  return Atomic::load_acquire(&_from_compiled_inline_entry);
+}
+
 inline address Method::from_interpreted_entry() const {
   return Atomic::load_acquire(&_from_interpreted_entry);
 }
 
 inline CompiledMethod* Method::code() const {
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
@@ -139,11 +139,11 @@
   if (extra != nullptr) {
     st->print("%s", extra);
   }
   int flags = data()->flags();
   if (flags != 0) {
-    st->print("flags(%d) ", flags);
+    st->print("flags(%d) %p/%d", flags, data(), in_bytes(DataLayout::flags_offset()));
   }
 }
 
 void ProfileData::tab(outputStream* st, bool first) const {
   st->fill_to(first ? tab_width_one : tab_width_two);
@@ -209,21 +209,21 @@
   return args_count * per_arg_cell_count;
 }
 
 int TypeEntriesAtCall::compute_cell_count(BytecodeStream* stream) {
   assert(Bytecodes::is_invoke(stream->code()), "should be invoke");
-  assert(TypeStackSlotEntries::per_arg_count() > ReturnTypeEntry::static_cell_count(), "code to test for arguments/results broken");
+  assert(TypeStackSlotEntries::per_arg_count() > SingleTypeEntry::static_cell_count(), "code to test for arguments/results broken");
   const methodHandle m = stream->method();
   int bci = stream->bci();
   Bytecode_invoke inv(m, bci);
   int args_cell = 0;
   if (MethodData::profile_arguments_for_invoke(m, bci)) {
     args_cell = TypeStackSlotEntries::compute_cell_count(inv.signature(), false, TypeProfileArgsLimit);
   }
   int ret_cell = 0;
   if (MethodData::profile_return_for_invoke(m, bci) && is_reference_type(inv.result_type())) {
-    ret_cell = ReturnTypeEntry::static_cell_count();
+    ret_cell = SingleTypeEntry::static_cell_count();
   }
   int header_cell = 0;
   if (args_cell + ret_cell > 0) {
     header_cell = header_cell_count();
   }
@@ -322,11 +322,11 @@
       set_type(i, with_status((Klass*)nullptr, p));
     }
   }
 }
 
-void ReturnTypeEntry::clean_weak_klass_links(bool always_clean) {
+void SingleTypeEntry::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));
   }
@@ -360,11 +360,11 @@
     print_klass(st, type(i));
     st->cr();
   }
 }
 
-void ReturnTypeEntry::print_data_on(outputStream* st) const {
+void SingleTypeEntry::print_data_on(outputStream* st) const {
   _pd->tab(st);
   print_klass(st, type());
   st->cr();
 }
 
@@ -521,10 +521,14 @@
   set_displacement(offset);
 }
 
 void BranchData::print_data_on(outputStream* st, const char* extra) const {
   print_shared(st, "BranchData", extra);
+  if (data()->flags()) {
+    tty->cr();
+    tab(st);
+  }
   st->print_cr("taken(%u) displacement(%d)",
                taken(), displacement());
   tab(st);
   st->print_cr("not taken(%u)", not_taken());
 }
@@ -646,10 +650,46 @@
   tab(st);
   method()->print_short_name(st);
   st->cr();
 }
 
+void ArrayStoreData::print_data_on(outputStream* st, const char* extra) const {
+  print_shared(st, "ArrayStore", extra);
+  st->cr();
+  tab(st, true);
+  st->print("array");
+  _array.print_data_on(st);
+  tab(st, true);
+  st->print("element");
+  if (null_seen()) {
+    st->print(" (null seen)");
+  }
+  tab(st);
+  print_receiver_data_on(st);
+}
+
+void ArrayLoadData::print_data_on(outputStream* st, const char* extra) const {
+  print_shared(st, "ArrayLoad", extra);
+  st->cr();
+  tab(st, true);
+  st->print("array");
+  _array.print_data_on(st);
+  tab(st, true);
+  st->print("element");
+  _element.print_data_on(st);
+}
+
+void ACmpData::print_data_on(outputStream* st, const char* extra) const {
+  BranchData::print_data_on(st, extra);
+  tab(st, true);
+  st->print("left");
+  _left.print_data_on(st);
+  tab(st, true);
+  st->print("right");
+  _right.print_data_on(st);
+}
+
 // ==================================================================
 // MethodData*
 //
 // A MethodData* holds information which has been collected about
 // a method.
@@ -664,16 +704,19 @@
 
 int MethodData::bytecode_cell_count(Bytecodes::Code code) {
   switch (code) {
   case Bytecodes::_checkcast:
   case Bytecodes::_instanceof:
-  case Bytecodes::_aastore:
     if (TypeProfileCasts) {
       return ReceiverTypeData::static_cell_count();
     } else {
       return BitData::static_cell_count();
     }
+  case Bytecodes::_aaload:
+    return ArrayLoadData::static_cell_count();
+  case Bytecodes::_aastore:
+    return ArrayStoreData::static_cell_count();
   case Bytecodes::_invokespecial:
   case Bytecodes::_invokestatic:
     if (MethodData::profile_arguments() || MethodData::profile_return()) {
       return variable_cell_count;
     } else {
@@ -709,15 +752,16 @@
   case Bytecodes::_if_icmpne:
   case Bytecodes::_if_icmplt:
   case Bytecodes::_if_icmpge:
   case Bytecodes::_if_icmpgt:
   case Bytecodes::_if_icmple:
-  case Bytecodes::_if_acmpeq:
-  case Bytecodes::_if_acmpne:
   case Bytecodes::_ifnull:
   case Bytecodes::_ifnonnull:
     return BranchData::static_cell_count();
+  case Bytecodes::_if_acmpne:
+  case Bytecodes::_if_acmpeq:
+    return ACmpData::static_cell_count();
   case Bytecodes::_lookupswitch:
   case Bytecodes::_tableswitch:
     return variable_cell_count;
   default:
     return no_profile_data;
@@ -772,10 +816,11 @@
 bool MethodData::is_speculative_trap_bytecode(Bytecodes::Code code) {
   // Bytecodes for which we may use speculation
   switch (code) {
   case Bytecodes::_checkcast:
   case Bytecodes::_instanceof:
+  case Bytecodes::_aaload:
   case Bytecodes::_aastore:
   case Bytecodes::_invokevirtual:
   case Bytecodes::_invokeinterface:
   case Bytecodes::_if_acmpeq:
   case Bytecodes::_if_acmpne:
@@ -988,19 +1033,26 @@
   DataLayout* data_layout = data_layout_at(data_index);
   Bytecodes::Code c = stream->code();
   switch (c) {
   case Bytecodes::_checkcast:
   case Bytecodes::_instanceof:
-  case Bytecodes::_aastore:
     if (TypeProfileCasts) {
       cell_count = ReceiverTypeData::static_cell_count();
       tag = DataLayout::receiver_type_data_tag;
     } else {
       cell_count = BitData::static_cell_count();
       tag = DataLayout::bit_data_tag;
     }
     break;
+  case Bytecodes::_aaload:
+    cell_count = ArrayLoadData::static_cell_count();
+    tag = DataLayout::array_load_data_tag;
+    break;
+  case Bytecodes::_aastore:
+    cell_count = ArrayStoreData::static_cell_count();
+    tag = DataLayout::array_store_data_tag;
+    break;
   case Bytecodes::_invokespecial:
   case Bytecodes::_invokestatic: {
     int counter_data_cell_count = CounterData::static_cell_count();
     if (profile_arguments_for_invoke(stream->method(), stream->bci()) ||
         profile_return_for_invoke(stream->method(), stream->bci())) {
@@ -1068,17 +1120,20 @@
   case Bytecodes::_if_icmpne:
   case Bytecodes::_if_icmplt:
   case Bytecodes::_if_icmpge:
   case Bytecodes::_if_icmpgt:
   case Bytecodes::_if_icmple:
-  case Bytecodes::_if_acmpeq:
-  case Bytecodes::_if_acmpne:
   case Bytecodes::_ifnull:
   case Bytecodes::_ifnonnull:
     cell_count = BranchData::static_cell_count();
     tag = DataLayout::branch_data_tag;
     break;
+  case Bytecodes::_if_acmpeq:
+  case Bytecodes::_if_acmpne:
+    cell_count = ACmpData::static_cell_count();
+    tag = DataLayout::acmp_data_tag;
+    break;
   case Bytecodes::_lookupswitch:
   case Bytecodes::_tableswitch:
     cell_count = MultiBranchData::compute_cell_count(stream);
     tag = DataLayout::multi_branch_data_tag;
     break;
@@ -1142,10 +1197,16 @@
     return ((new VirtualCallTypeData(this))->cell_count());
   case DataLayout::parameters_type_data_tag:
     return ((new ParametersTypeData(this))->cell_count());
   case DataLayout::speculative_trap_data_tag:
     return SpeculativeTrapData::static_cell_count();
+  case DataLayout::array_store_data_tag:
+    return ((new ArrayStoreData(this))->cell_count());
+  case DataLayout::array_load_data_tag:
+    return ((new ArrayLoadData(this))->cell_count());
+  case DataLayout::acmp_data_tag:
+    return ((new ACmpData(this))->cell_count());
   }
 }
 ProfileData* DataLayout::data_in() {
   switch (tag()) {
   case DataLayout::no_tag:
@@ -1176,10 +1237,16 @@
     return new VirtualCallTypeData(this);
   case DataLayout::parameters_type_data_tag:
     return new ParametersTypeData(this);
   case DataLayout::speculative_trap_data_tag:
     return new SpeculativeTrapData(this);
+  case DataLayout::array_store_data_tag:
+    return new ArrayStoreData(this);
+  case DataLayout::array_load_data_tag:
+    return new ArrayLoadData(this);
+  case DataLayout::acmp_data_tag:
+    return new ACmpData(this);
   }
 }
 
 // Iteration over data.
 ProfileData* MethodData::next_data(ProfileData* current) const {
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
@@ -127,11 +127,14 @@
     multi_branch_data_tag,
     arg_info_data_tag,
     call_type_data_tag,
     virtual_call_type_data_tag,
     parameters_type_data_tag,
-    speculative_trap_data_tag
+    speculative_trap_data_tag,
+    array_store_data_tag,
+    array_load_data_tag,
+    acmp_data_tag
   };
 
   enum {
     // The trap state breaks down as [recompile:1 | reason:31].
     // This further breakdown is defined in deoptimization.cpp.
@@ -284,27 +287,30 @@
 class   BitData;
 class     CounterData;
 class       ReceiverTypeData;
 class         VirtualCallData;
 class           VirtualCallTypeData;
+class         ArrayStoreData;
 class       RetData;
 class       CallTypeData;
 class   JumpData;
 class     BranchData;
+class       ACmpData;
 class   ArrayData;
 class     MultiBranchData;
 class     ArgInfoData;
 class     ParametersTypeData;
 class   SpeculativeTrapData;
+class   ArrayLoadData;
 
 // ProfileData
 //
 // A ProfileData object is created to refer to a section of profiling
 // data in a structured way.
 class ProfileData : public ResourceObj {
   friend class TypeEntries;
-  friend class ReturnTypeEntry;
+  friend class SingleTypeEntry;
   friend class TypeStackSlotEntries;
 private:
   enum {
     tab_width_one = 16,
     tab_width_two = 36
@@ -421,10 +427,13 @@
   virtual bool is_ArgInfoData()     const { return false; }
   virtual bool is_CallTypeData()    const { return false; }
   virtual bool is_VirtualCallTypeData()const { return false; }
   virtual bool is_ParametersTypeData() const { return false; }
   virtual bool is_SpeculativeTrapData()const { return false; }
+  virtual bool is_ArrayStoreData() const { return false; }
+  virtual bool is_ArrayLoadData() const { return false; }
+  virtual bool is_ACmpData()           const { return false; }
 
 
   BitData* as_BitData() const {
     assert(is_BitData(), "wrong type");
     return is_BitData()         ? (BitData*)        this : nullptr;
@@ -479,10 +488,22 @@
   }
   SpeculativeTrapData* as_SpeculativeTrapData() const {
     assert(is_SpeculativeTrapData(), "wrong type");
     return is_SpeculativeTrapData() ? (SpeculativeTrapData*)this : nullptr;
   }
+  ArrayStoreData* as_ArrayStoreData() const {
+    assert(is_ArrayStoreData(), "wrong type");
+    return is_ArrayStoreData() ? (ArrayStoreData*)this : nullptr;
+  }
+  ArrayLoadData* as_ArrayLoadData() const {
+    assert(is_ArrayLoadData(), "wrong type");
+    return is_ArrayLoadData() ? (ArrayLoadData*)this : nullptr;
+  }
+  ACmpData* as_ACmpData() const {
+    assert(is_ACmpData(), "wrong type");
+    return is_ACmpData() ? (ACmpData*)this : nullptr;
+  }
 
 
   // Subclass specific initialization
   virtual void post_initialize(BytecodeStream* stream, MethodData* mdo) {}
 
@@ -521,10 +542,11 @@
       deprecated_method_callsite_flag = exception_handler_entered_flag + 1
 #if INCLUDE_JVMCI
     // bytecode threw any exception
     , exception_seen_flag             = deprecated_method_callsite_flag + 1
 #endif
+    , last_bit_data_flag
   };
   enum { bit_cell_count = 0 };  // no additional data fields needed.
 public:
   BitData(DataLayout* layout) : ProfileData(layout) {
   }
@@ -541,11 +563,11 @@
 
   // Accessor
 
   // The null_seen flag bit is specially known to the interpreter.
   // Consulting it allows the compiler to avoid setting up null_check traps.
-  bool null_seen()     { return flag_at(null_seen_flag); }
+  bool null_seen() const  { return flag_at(null_seen_flag); }
   void set_null_seen()    { set_flag_at(null_seen_flag); }
   bool deprecated_method_call_site() const { return flag_at(deprecated_method_callsite_flag); }
   bool set_deprecated_method_call_site() { return data()->set_flag_at(deprecated_method_callsite_flag); }
   bool clear_deprecated_method_call_site() { return data()->clear_flag_at(deprecated_method_callsite_flag); }
 
@@ -642,11 +664,12 @@
   }
 
 public:
   JumpData(DataLayout* layout) : ProfileData(layout) {
     assert(layout->tag() == DataLayout::jump_data_tag ||
-      layout->tag() == DataLayout::branch_data_tag, "wrong type");
+      layout->tag() == DataLayout::branch_data_tag ||
+      layout->tag() == DataLayout::acmp_data_tag, "wrong type");
   }
 
   virtual bool is_JumpData() const { return true; }
 
   static int static_cell_count() {
@@ -878,19 +901,19 @@
   void print_data_on(outputStream* st) const;
 };
 
 // Type entry used for return from a call. A single cell to record the
 // type.
-class ReturnTypeEntry : public TypeEntries {
+class SingleTypeEntry : public TypeEntries {
 
 private:
   enum {
     cell_count = 1
   };
 
 public:
-  ReturnTypeEntry(int base_off)
+  SingleTypeEntry(int base_off)
     : TypeEntries(base_off) {}
 
   void post_initialize() {
     set_type(type_none());
   }
@@ -920,11 +943,11 @@
 
   void print_data_on(outputStream* st) const;
 };
 
 // Entries to collect type information at a call: contains arguments
-// (TypeStackSlotEntries), a return type (ReturnTypeEntry) and a
+// (TypeStackSlotEntries), a return type (SingleTypeEntry) and a
 // number of cells. Because the number of cells for the return type is
 // smaller than the number of cells for the type of an arguments, the
 // number of cells is used to tell how many arguments are profiled and
 // whether a return value is profiled. See has_arguments() and
 // has_return().
@@ -974,11 +997,11 @@
   static ByteSize argument_type_offset(int i) {
     return in_ByteSize(argument_type_local_offset(i) * DataLayout::cell_size);
   }
 
   static ByteSize return_only_size() {
-    return ReturnTypeEntry::size() + in_ByteSize(header_cell_count() * DataLayout::cell_size);
+    return SingleTypeEntry::size() + in_ByteSize(header_cell_count() * DataLayout::cell_size);
   }
 
 };
 
 // CallTypeData
@@ -989,11 +1012,11 @@
 class CallTypeData : public CounterData {
 private:
   // entries for arguments if any
   TypeStackSlotEntries _args;
   // entry for return type if any
-  ReturnTypeEntry _ret;
+  SingleTypeEntry _ret;
 
   int cell_count_global_offset() const {
     return CounterData::static_cell_count() + TypeEntriesAtCall::cell_count_local_offset();
   }
 
@@ -1008,11 +1031,11 @@
 
 public:
   CallTypeData(DataLayout* layout) :
     CounterData(layout),
     _args(CounterData::static_cell_count()+TypeEntriesAtCall::header_cell_count(), number_of_arguments()),
-    _ret(cell_count() - ReturnTypeEntry::static_cell_count())
+    _ret(cell_count() - SingleTypeEntry::static_cell_count())
   {
     assert(layout->tag() == DataLayout::call_type_data_tag, "wrong type");
     // Some compilers (VC++) don't want this passed in member initialization list
     _args.set_profile_data(this);
     _ret.set_profile_data(this);
@@ -1021,11 +1044,11 @@
   const TypeStackSlotEntries* args() const {
     assert(has_arguments(), "no profiling of arguments");
     return &_args;
   }
 
-  const ReturnTypeEntry* ret() const {
+  const SingleTypeEntry* ret() const {
     assert(has_return(), "no profiling of return value");
     return &_ret;
   }
 
   virtual bool is_CallTypeData() const { return true; }
@@ -1133,11 +1156,12 @@
 
 public:
   ReceiverTypeData(DataLayout* layout) : CounterData(layout) {
     assert(layout->tag() == DataLayout::receiver_type_data_tag ||
            layout->tag() == DataLayout::virtual_call_data_tag ||
-           layout->tag() == DataLayout::virtual_call_type_data_tag, "wrong type");
+           layout->tag() == DataLayout::virtual_call_type_data_tag ||
+           layout->tag() == DataLayout::array_store_data_tag, "wrong type");
   }
 
   virtual bool is_ReceiverTypeData() const { return true; }
 
   static int static_cell_count() {
@@ -1263,11 +1287,11 @@
 class VirtualCallTypeData : public VirtualCallData {
 private:
   // entries for arguments if any
   TypeStackSlotEntries _args;
   // entry for return type if any
-  ReturnTypeEntry _ret;
+  SingleTypeEntry _ret;
 
   int cell_count_global_offset() const {
     return VirtualCallData::static_cell_count() + TypeEntriesAtCall::cell_count_local_offset();
   }
 
@@ -1282,11 +1306,11 @@
 
 public:
   VirtualCallTypeData(DataLayout* layout) :
     VirtualCallData(layout),
     _args(VirtualCallData::static_cell_count()+TypeEntriesAtCall::header_cell_count(), number_of_arguments()),
-    _ret(cell_count() - ReturnTypeEntry::static_cell_count())
+    _ret(cell_count() - SingleTypeEntry::static_cell_count())
   {
     assert(layout->tag() == DataLayout::virtual_call_type_data_tag, "wrong type");
     // Some compilers (VC++) don't want this passed in member initialization list
     _args.set_profile_data(this);
     _ret.set_profile_data(this);
@@ -1295,11 +1319,11 @@
   const TypeStackSlotEntries* args() const {
     assert(has_arguments(), "no profiling of arguments");
     return &_args;
   }
 
-  const ReturnTypeEntry* ret() const {
+  const SingleTypeEntry* ret() const {
     assert(has_return(), "no profiling of return value");
     return &_ret;
   }
 
   virtual bool is_VirtualCallTypeData() const { return true; }
@@ -1497,11 +1521,11 @@
     set_int_at(displacement_off_set, displacement);
   }
 
 public:
   BranchData(DataLayout* layout) : JumpData(layout) {
-    assert(layout->tag() == DataLayout::branch_data_tag, "wrong type");
+    assert(layout->tag() == DataLayout::branch_data_tag || layout->tag() == DataLayout::acmp_data_tag, "wrong type");
   }
 
   virtual bool is_BranchData() const { return true; }
 
   static int static_cell_count() {
@@ -1854,10 +1878,218 @@
   }
 
   virtual void print_data_on(outputStream* st, const char* extra = nullptr) const;
 };
 
+class ArrayStoreData : public ReceiverTypeData {
+private:
+  enum {
+    flat_array_flag = BitData::last_bit_data_flag,
+    null_free_array_flag = flat_array_flag + 1,
+  };
+
+  SingleTypeEntry _array;
+
+public:
+  ArrayStoreData(DataLayout* layout) :
+    ReceiverTypeData(layout),
+    _array(ReceiverTypeData::static_cell_count()) {
+    assert(layout->tag() == DataLayout::array_store_data_tag, "wrong type");
+    _array.set_profile_data(this);
+  }
+
+  const SingleTypeEntry* array() const {
+    return &_array;
+  }
+
+  virtual bool is_ArrayStoreData() const { return true; }
+
+  static int static_cell_count() {
+    return ReceiverTypeData::static_cell_count() + SingleTypeEntry::static_cell_count();
+  }
+
+  virtual int cell_count() const {
+    return static_cell_count();
+  }
+
+  void set_flat_array() { set_flag_at(flat_array_flag); }
+  bool flat_array() const { return flag_at(flat_array_flag); }
+
+  void set_null_free_array() { set_flag_at(null_free_array_flag); }
+  bool null_free_array() const { return flag_at(null_free_array_flag); }
+
+  // Code generation support
+  static int flat_array_byte_constant() {
+    return flag_number_to_constant(flat_array_flag);
+  }
+
+  static int null_free_array_byte_constant() {
+    return flag_number_to_constant(null_free_array_flag);
+  }
+
+  static ByteSize array_offset() {
+    return cell_offset(ReceiverTypeData::static_cell_count());
+  }
+
+  virtual void clean_weak_klass_links(bool always_clean) {
+    ReceiverTypeData::clean_weak_klass_links(always_clean);
+    _array.clean_weak_klass_links(always_clean);
+  }
+
+  static ByteSize array_store_data_size() {
+    return cell_offset(static_cell_count());
+  }
+
+  virtual void print_data_on(outputStream* st, const char* extra = nullptr) const;
+};
+
+class ArrayLoadData : public ProfileData {
+private:
+  enum {
+    flat_array_flag = DataLayout::first_flag,
+    null_free_array_flag = flat_array_flag + 1,
+  };
+
+  SingleTypeEntry _array;
+  SingleTypeEntry _element;
+
+public:
+  ArrayLoadData(DataLayout* layout) :
+    ProfileData(layout),
+    _array(0),
+    _element(SingleTypeEntry::static_cell_count()) {
+    assert(layout->tag() == DataLayout::array_load_data_tag, "wrong type");
+    _array.set_profile_data(this);
+    _element.set_profile_data(this);
+  }
+
+  const SingleTypeEntry* array() const {
+    return &_array;
+  }
+
+  const SingleTypeEntry* element() const {
+    return &_element;
+  }
+
+  virtual bool is_ArrayLoadData() const { return true; }
+
+  static int static_cell_count() {
+    return SingleTypeEntry::static_cell_count() * 2;
+  }
+
+  virtual int cell_count() const {
+    return static_cell_count();
+  }
+
+  void set_flat_array() { set_flag_at(flat_array_flag); }
+  bool flat_array() const { return flag_at(flat_array_flag); }
+
+  void set_null_free_array() { set_flag_at(null_free_array_flag); }
+  bool null_free_array() const { return flag_at(null_free_array_flag); }
+
+  // Code generation support
+  static int flat_array_byte_constant() {
+    return flag_number_to_constant(flat_array_flag);
+  }
+
+  static int null_free_array_byte_constant() {
+    return flag_number_to_constant(null_free_array_flag);
+  }
+
+  static ByteSize array_offset() {
+    return cell_offset(0);
+  }
+
+  static ByteSize element_offset() {
+    return cell_offset(SingleTypeEntry::static_cell_count());
+  }
+
+  virtual void clean_weak_klass_links(bool always_clean) {
+    _array.clean_weak_klass_links(always_clean);
+    _element.clean_weak_klass_links(always_clean);
+  }
+
+  static ByteSize array_load_data_size() {
+    return cell_offset(static_cell_count());
+  }
+
+  virtual void print_data_on(outputStream* st, const char* extra = nullptr) const;
+};
+
+class ACmpData : public BranchData {
+private:
+  enum {
+    left_inline_type_flag = DataLayout::first_flag,
+    right_inline_type_flag
+  };
+
+  SingleTypeEntry _left;
+  SingleTypeEntry _right;
+
+public:
+  ACmpData(DataLayout* layout) :
+    BranchData(layout),
+    _left(BranchData::static_cell_count()),
+    _right(BranchData::static_cell_count() + SingleTypeEntry::static_cell_count()) {
+    assert(layout->tag() == DataLayout::acmp_data_tag, "wrong type");
+    _left.set_profile_data(this);
+    _right.set_profile_data(this);
+  }
+
+  const SingleTypeEntry* left() const {
+    return &_left;
+  }
+
+  const SingleTypeEntry* right() const {
+    return &_right;
+  }
+
+  virtual bool is_ACmpData() const { return true; }
+
+  static int static_cell_count() {
+    return BranchData::static_cell_count() + SingleTypeEntry::static_cell_count() * 2;
+  }
+
+  virtual int cell_count() const {
+    return static_cell_count();
+  }
+
+  void set_left_inline_type() { set_flag_at(left_inline_type_flag); }
+  bool left_inline_type() const { return flag_at(left_inline_type_flag); }
+
+  void set_right_inline_type() { set_flag_at(right_inline_type_flag); }
+  bool right_inline_type() const { return flag_at(right_inline_type_flag); }
+
+  // Code generation support
+  static int left_inline_type_byte_constant() {
+    return flag_number_to_constant(left_inline_type_flag);
+  }
+
+  static int right_inline_type_byte_constant() {
+    return flag_number_to_constant(right_inline_type_flag);
+  }
+
+  static ByteSize left_offset() {
+    return cell_offset(BranchData::static_cell_count());
+  }
+
+  static ByteSize right_offset() {
+    return cell_offset(BranchData::static_cell_count() + SingleTypeEntry::static_cell_count());
+  }
+
+  virtual void clean_weak_klass_links(bool always_clean) {
+    _left.clean_weak_klass_links(always_clean);
+    _right.clean_weak_klass_links(always_clean);
+  }
+
+  static ByteSize acmp_data_size() {
+    return cell_offset(static_cell_count());
+  }
+
+  virtual void print_data_on(outputStream* st, const char* extra = nullptr) const;
+};
+
 // MethodData*
 //
 // A MethodData* holds information which has been collected about
 // a method.  Its layout looks like this:
 //
diff a/src/hotspot/share/oops/objArrayKlass.cpp b/src/hotspot/share/oops/objArrayKlass.cpp
--- a/src/hotspot/share/oops/objArrayKlass.cpp
+++ b/src/hotspot/share/oops/objArrayKlass.cpp
@@ -30,10 +30,11 @@
 #include "classfile/vmSymbols.hpp"
 #include "gc/shared/collectedHeap.inline.hpp"
 #include "memory/iterator.inline.hpp"
 #include "memory/metadataFactory.hpp"
 #include "memory/metaspaceClosure.hpp"
+#include "memory/oopFactory.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
 #include "oops/arrayKlass.hpp"
 #include "oops/instanceKlass.hpp"
 #include "oops/klass.inline.hpp"
@@ -43,29 +44,37 @@
 #include "oops/symbol.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/mutexLocker.hpp"
 #include "utilities/macros.hpp"
 
-ObjArrayKlass* ObjArrayKlass::allocate(ClassLoaderData* loader_data, int n, Klass* k, Symbol* name, TRAPS) {
+ObjArrayKlass* ObjArrayKlass::allocate(ClassLoaderData* loader_data, int n,
+                                       Klass* k, Symbol* name, bool null_free,
+                                       TRAPS) {
   assert(ObjArrayKlass::header_size() <= InstanceKlass::header_size(),
       "array klasses must be same size as InstanceKlass");
 
   int size = ArrayKlass::static_size(ObjArrayKlass::header_size());
 
-  return new (loader_data, size, THREAD) ObjArrayKlass(n, k, name);
+  return new (loader_data, size, THREAD) ObjArrayKlass(n, k, name, null_free);
 }
 
 ObjArrayKlass* ObjArrayKlass::allocate_objArray_klass(ClassLoaderData* loader_data,
-                                                      int n, Klass* element_klass, TRAPS) {
+                                                      int n, Klass* element_klass,
+                                                      bool null_free, TRAPS) {
+  assert(!null_free || (n == 1 && element_klass->is_inline_klass()), "null-free unsupported");
 
   // Eagerly allocate the direct array supertype.
   Klass* super_klass = nullptr;
   if (!Universe::is_bootstrapping() || vmClasses::Object_klass_loaded()) {
     Klass* element_super = element_klass->super();
     if (element_super != nullptr) {
       // The element type has a direct super.  E.g., String[] has direct super of Object[].
-      super_klass = element_super->array_klass_or_null();
+      if (null_free) {
+        super_klass = element_klass->array_klass_or_null();
+      } else {
+        super_klass = element_super->array_klass_or_null();
+      }
       bool supers_exist = super_klass != nullptr;
       // Also, see if the element has secondary supertypes.
       // We need an array type for each.
       const Array<Klass*>* element_supers = element_klass->secondary_supers();
       for( int i = element_supers->length()-1; i >= 0; i-- ) {
@@ -73,54 +82,49 @@
         if (elem_super->array_klass_or_null() == nullptr) {
           supers_exist = false;
           break;
         }
       }
+      if (null_free) {
+        if (element_klass->array_klass_or_null() == nullptr) {
+          supers_exist = false;
+        }
+      }
       if (!supers_exist) {
         // Oops.  Not allocated yet.  Back out, allocate it, and retry.
         Klass* ek = nullptr;
         {
           MutexUnlocker mu(MultiArray_lock);
-          super_klass = element_super->array_klass(CHECK_NULL);
+          if (null_free) {
+            element_klass->array_klass(CHECK_NULL);
+          } else {
+            element_super->array_klass(CHECK_NULL);
+          }
           for( int i = element_supers->length()-1; i >= 0; i-- ) {
             Klass* elem_super = element_supers->at(i);
             elem_super->array_klass(CHECK_NULL);
           }
           // Now retry from the beginning
-          ek = element_klass->array_klass(n, CHECK_NULL);
+          if (null_free) {
+            ek = InlineKlass::cast(element_klass)->value_array_klass(CHECK_NULL);
+          } else {
+            ek = element_klass->array_klass(n, CHECK_NULL);
+          }
         }  // re-lock
         return ObjArrayKlass::cast(ek);
       }
     } else {
       // The element type is already Object.  Object[] has direct super of Object.
       super_klass = vmClasses::Object_klass();
     }
   }
 
   // Create type name for klass.
-  Symbol* name = nullptr;
-  {
-    ResourceMark rm(THREAD);
-    char *name_str = element_klass->name()->as_C_string();
-    int len = element_klass->name()->utf8_length();
-    char *new_str = NEW_RESOURCE_ARRAY(char, len + 4);
-    int idx = 0;
-    new_str[idx++] = JVM_SIGNATURE_ARRAY;
-    if (element_klass->is_instance_klass()) { // it could be an array or simple type
-      new_str[idx++] = JVM_SIGNATURE_CLASS;
-    }
-    memcpy(&new_str[idx], name_str, len * sizeof(char));
-    idx += len;
-    if (element_klass->is_instance_klass()) {
-      new_str[idx++] = JVM_SIGNATURE_ENDCLASS;
-    }
-    new_str[idx++] = '\0';
-    name = SymbolTable::new_symbol(new_str);
-  }
+  Symbol* name = ArrayKlass::create_element_klass_array_name(element_klass, CHECK_NULL);
 
   // Initialize instance variables
-  ObjArrayKlass* oak = ObjArrayKlass::allocate(loader_data, n, element_klass, name, CHECK_NULL);
+  ObjArrayKlass* oak = ObjArrayKlass::allocate(loader_data, n, element_klass, name, null_free, CHECK_NULL);
 
   ModuleEntry* module = oak->module();
   assert(module != nullptr, "No module entry for array");
 
   // Call complete_create_array_klass after all instance variables has been initialized.
@@ -134,25 +138,38 @@
   loader_data->add_class(oak);
 
   return oak;
 }
 
-ObjArrayKlass::ObjArrayKlass(int n, Klass* element_klass, Symbol* name) : ArrayKlass(name, Kind) {
+ObjArrayKlass::ObjArrayKlass(int n, Klass* element_klass, Symbol* name, bool null_free) : ArrayKlass(name, Kind) {
   set_dimension(n);
   set_element_klass(element_klass);
 
   Klass* bk;
   if (element_klass->is_objArray_klass()) {
     bk = ObjArrayKlass::cast(element_klass)->bottom_klass();
+  } else if (element_klass->is_flatArray_klass()) {
+    bk = FlatArrayKlass::cast(element_klass)->element_klass();
   } else {
     bk = element_klass;
   }
   assert(bk != nullptr && (bk->is_instance_klass() || bk->is_typeArray_klass()), "invalid bottom klass");
   set_bottom_klass(bk);
   set_class_loader_data(bk->class_loader_data());
 
-  set_layout_helper(array_layout_helper(T_OBJECT));
+  int lh = array_layout_helper(T_OBJECT);
+  if (null_free) {
+    assert(n == 1, "Bytecode does not support null-free multi-dim");
+    lh = layout_helper_set_null_free(lh);
+#ifdef _LP64
+    set_prototype_header(markWord::null_free_array_prototype());
+    assert(prototype_header().is_null_free_array(), "sanity");
+#else
+    set_prototype_header(markWord::inline_type_prototype());
+#endif
+  }
+  set_layout_helper(lh);
   assert(is_array_klass(), "sanity");
   assert(is_objArray_klass(), "sanity");
 }
 
 size_t ObjArrayKlass::oop_size(oop obj) const {
@@ -161,12 +178,27 @@
 }
 
 objArrayOop ObjArrayKlass::allocate(int length, TRAPS) {
   check_array_allocation_length(length, arrayOopDesc::max_array_length(T_OBJECT), CHECK_NULL);
   size_t size = objArrayOopDesc::object_size(length);
-  return (objArrayOop)Universe::heap()->array_allocate(this, size, length,
-                                                       /* do_zero */ true, THREAD);
+  bool populate_null_free = is_null_free_array_klass();
+  objArrayOop array =  (objArrayOop)Universe::heap()->array_allocate(this, size, length,
+                                                       /* do_zero */ true, CHECK_NULL);
+  objArrayHandle array_h(THREAD, array);
+  if (populate_null_free) {
+    assert(dimension() == 1, "Can only populate the final dimension");
+    assert(element_klass()->is_inline_klass(), "Unexpected");
+    assert(!element_klass()->is_array_klass(), "ArrayKlass unexpected here");
+    assert(!InlineKlass::cast(element_klass())->flat_array(), "Expected flatArrayOop allocation");
+    element_klass()->initialize(CHECK_NULL);
+    // Populate default values...
+    instanceOop value = (instanceOop) InlineKlass::cast(element_klass())->default_value();
+    for (int i = 0; i < length; i++) {
+      array_h->obj_at_put(i, value);
+    }
+  }
+  return array_h();
 }
 
 oop ObjArrayKlass::multi_allocate(int rank, jint* sizes, TRAPS) {
   int length = *sizes;
   ArrayKlass* ld_klass = lower_dimension();
@@ -174,11 +206,11 @@
   objArrayOop array = allocate(length, CHECK_NULL);
   objArrayHandle h_array (THREAD, array);
   if (rank > 1) {
     if (length != 0) {
       for (int index = 0; index < length; index++) {
-        oop sub_array = ld_klass->multi_allocate(rank - 1, &sizes[1], CHECK_NULL);
+        oop sub_array = ld_klass->multi_allocate(rank-1, &sizes[1], CHECK_NULL);
         h_array->obj_at_put(index, sub_array);
       }
     } else {
       // Since this array dimension has zero length, nothing will be
       // allocated, however the lower dimension values must be checked
@@ -203,39 +235,40 @@
     ArrayAccess<>::oop_arraycopy(s, src_offset, d, dst_offset, length);
   } else {
     // We have to make sure all elements conform to the destination array
     Klass* bound = ObjArrayKlass::cast(d->klass())->element_klass();
     Klass* stype = ObjArrayKlass::cast(s->klass())->element_klass();
+    // Perform null check if dst is null-free but src has no such guarantee
+    bool null_check = ((!s->klass()->is_null_free_array_klass()) &&
+        d->klass()->is_null_free_array_klass());
     if (stype == bound || stype->is_subtype_of(bound)) {
-      // elements are guaranteed to be subtypes, so no check necessary
-      ArrayAccess<ARRAYCOPY_DISJOINT>::oop_arraycopy(s, src_offset, d, dst_offset, length);
+      if (null_check) {
+        ArrayAccess<ARRAYCOPY_DISJOINT | ARRAYCOPY_NOTNULL>::oop_arraycopy(s, src_offset, d, dst_offset, length);
+      } else {
+        ArrayAccess<ARRAYCOPY_DISJOINT>::oop_arraycopy(s, src_offset, d, dst_offset, length);
+      }
     } else {
-      // slow case: need individual subtype checks
-      // note: don't use obj_at_put below because it includes a redundant store check
-      if (!ArrayAccess<ARRAYCOPY_DISJOINT | ARRAYCOPY_CHECKCAST>::oop_arraycopy(s, src_offset, d, dst_offset, length)) {
-        ResourceMark rm(THREAD);
-        stringStream ss;
-        if (!bound->is_subtype_of(stype)) {
-          ss.print("arraycopy: type mismatch: can not copy %s[] into %s[]",
-                   stype->external_name(), bound->external_name());
-        } else {
-          // oop_arraycopy should return the index in the source array that
-          // contains the problematic oop.
-          ss.print("arraycopy: element type mismatch: can not cast one of the elements"
-                   " of %s[] to the type of the destination array, %s",
-                   stype->external_name(), bound->external_name());
-        }
-        THROW_MSG(vmSymbols::java_lang_ArrayStoreException(), ss.as_string());
+      if (null_check) {
+        ArrayAccess<ARRAYCOPY_DISJOINT | ARRAYCOPY_CHECKCAST | ARRAYCOPY_NOTNULL>::oop_arraycopy(s, src_offset, d, dst_offset, length);
+      } else {
+        ArrayAccess<ARRAYCOPY_DISJOINT | ARRAYCOPY_CHECKCAST>::oop_arraycopy(s, src_offset, d, dst_offset, length);
       }
     }
   }
 }
 
 void ObjArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d,
                                int dst_pos, int length, TRAPS) {
   assert(s->is_objArray(), "must be obj array");
 
+  if (UseFlatArray) {
+    if (d->is_flatArray()) {
+      FlatArrayKlass::cast(d->klass())->copy_array(s, src_pos, d, dst_pos, length, THREAD);
+      return;
+    }
+  }
+
   if (!d->is_objArray()) {
     ResourceMark rm(THREAD);
     stringStream ss;
     if (d->is_typeArray()) {
       ss.print("arraycopy: type mismatch: can not copy object array[] into %s[]",
@@ -374,11 +407,11 @@
 // Printing
 
 void ObjArrayKlass::print_on(outputStream* st) const {
 #ifndef PRODUCT
   Klass::print_on(st);
-  st->print(" - instance klass: ");
+  st->print(" - element klass: ");
   element_klass()->print_value_on(st);
   st->cr();
 #endif //PRODUCT
 }
 
@@ -436,16 +469,18 @@
 void ObjArrayKlass::verify_on(outputStream* st) {
   ArrayKlass::verify_on(st);
   guarantee(element_klass()->is_klass(), "should be klass");
   guarantee(bottom_klass()->is_klass(), "should be klass");
   Klass* bk = bottom_klass();
-  guarantee(bk->is_instance_klass() || bk->is_typeArray_klass(),  "invalid bottom klass");
+  guarantee(bk->is_instance_klass() || bk->is_typeArray_klass() || bk->is_flatArray_klass(),
+            "invalid bottom klass");
 }
 
 void ObjArrayKlass::oop_verify_on(oop obj, outputStream* st) {
   ArrayKlass::oop_verify_on(obj, st);
   guarantee(obj->is_objArray(), "must be objArray");
+  guarantee(obj->is_null_free_array() || (!is_null_free_array_klass()), "null-free klass but not object");
   objArrayOop oa = objArrayOop(obj);
   for(int index = 0; index < oa->length(); index++) {
     guarantee(oopDesc::is_oop_or_null(oa->obj_at(index)), "should be oop");
   }
 }
diff a/src/hotspot/share/oops/objArrayKlass.hpp b/src/hotspot/share/oops/objArrayKlass.hpp
--- a/src/hotspot/share/oops/objArrayKlass.hpp
+++ b/src/hotspot/share/oops/objArrayKlass.hpp
@@ -40,45 +40,37 @@
   static const KlassKind Kind = ObjArrayKlassKind;
 
  private:
   // If you add a new field that points to any metaspace object, you
   // must add this field to ObjArrayKlass::metaspace_pointers_do().
-  Klass* _element_klass;            // The klass of the elements of this array type
   Klass* _bottom_klass;             // The one-dimensional type (InstanceKlass or TypeArrayKlass)
 
   // Constructor
-  ObjArrayKlass(int n, Klass* element_klass, Symbol* name);
-  static ObjArrayKlass* allocate(ClassLoaderData* loader_data, int n, Klass* k, Symbol* name, TRAPS);
+  ObjArrayKlass(int n, Klass* element_klass, Symbol* name, bool null_free);
+  static ObjArrayKlass* allocate(ClassLoaderData* loader_data, int n, Klass* k, Symbol* name, bool null_free, TRAPS);
  public:
   // For dummy objects
   ObjArrayKlass() {}
 
-  // Instance variables
-  Klass* element_klass() const      { return _element_klass; }
-  void set_element_klass(Klass* k)  { _element_klass = k; }
-  Klass** element_klass_addr()      { return &_element_klass; }
-
   Klass* bottom_klass() const       { return _bottom_klass; }
   void set_bottom_klass(Klass* k)   { _bottom_klass = k; }
   Klass** bottom_klass_addr()       { return &_bottom_klass; }
 
   ModuleEntry* module() const;
   PackageEntry* package() const;
 
-  // Compiler/Interpreter offset
-  static ByteSize element_klass_offset() { return byte_offset_of(ObjArrayKlass, _element_klass); }
-
   // Dispatched operation
   bool can_be_primary_super_slow() const;
   GrowableArray<Klass*>* compute_secondary_supers(int num_extra_slots,
                                                   Array<InstanceKlass*>* transitive_interfaces);
   DEBUG_ONLY(bool is_objArray_klass_slow()  const  { return true; })
   size_t oop_size(oop obj) const;
 
   // Allocation
   static ObjArrayKlass* allocate_objArray_klass(ClassLoaderData* loader_data,
-                                                int n, Klass* element_klass, TRAPS);
+                                                int n, Klass* element_klass,
+                                                bool null_free, TRAPS);
 
   objArrayOop allocate(int length, TRAPS);
   oop multi_allocate(int rank, jint* sizes, TRAPS);
 
   // Copying
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
@@ -147,16 +147,18 @@
 
 void VerifyOopClosure::do_oop(oop* p)       { VerifyOopClosure::do_oop_work(p); }
 void VerifyOopClosure::do_oop(narrowOop* p) { VerifyOopClosure::do_oop_work(p); }
 
 // type test operations that doesn't require inclusion of oop.inline.hpp.
-bool oopDesc::is_instance_noinline()    const { return is_instance();    }
-bool oopDesc::is_instanceRef_noinline() const { return is_instanceRef(); }
-bool oopDesc::is_stackChunk_noinline()  const { return is_stackChunk();  }
-bool oopDesc::is_array_noinline()       const { return is_array();       }
-bool oopDesc::is_objArray_noinline()    const { return is_objArray();    }
-bool oopDesc::is_typeArray_noinline()   const { return is_typeArray();   }
+bool oopDesc::is_instance_noinline()        const { return is_instance();         }
+bool oopDesc::is_instanceRef_noinline()     const { return is_instanceRef();      }
+bool oopDesc::is_stackChunk_noinline()      const { return is_stackChunk();       }
+bool oopDesc::is_array_noinline()           const { return is_array();            }
+bool oopDesc::is_objArray_noinline()        const { return is_objArray();         }
+bool oopDesc::is_typeArray_noinline()       const { return is_typeArray();        }
+bool oopDesc::is_flatArray_noinline()       const { return is_flatArray();        }
+bool oopDesc::is_null_free_array_noinline() const { return is_null_free_array();  }
 
 bool oopDesc::has_klass_gap() {
   // Only has a klass gap when compressed class pointers are used.
   return UseCompressedClassPointers;
 }
diff a/src/hotspot/share/oops/oop.hpp b/src/hotspot/share/oops/oop.hpp
--- a/src/hotspot/share/oops/oop.hpp
+++ b/src/hotspot/share/oops/oop.hpp
@@ -40,10 +40,20 @@
 // the format of Java objects so the fields can be accessed from C++.
 // oopDesc is abstract.
 // (see oopHierarchy for complete oop class hierarchy)
 //
 // no virtual functions allowed
+//
+// oopDesc::_mark - the "oop mark word" encoding to be found separately in markWord.hpp
+//
+// oopDesc::_metadata - encodes the object's klass pointer, as a raw pointer in "_klass"
+//                      or compressed pointer in "_compressed_klass"
+//
+// The overall size of the _metadata field is dependent on "UseCompressedClassPointers",
+// hence the terms "narrow" (32 bits) vs "wide" (64 bits).
+//
+
 
 // Forward declarations.
 class OopClosure;
 class PSPromotionManager;
 class ParCompactionManager;
@@ -107,24 +117,29 @@
   // Sometimes (for complicated concurrency-related reasons), it is useful
   // to be able to figure out the size of an object knowing its klass.
   inline size_t size_given_klass(Klass* klass);
 
   // type test operations (inlined in oop.inline.hpp)
-  inline bool is_instance()    const;
-  inline bool is_instanceRef() const;
-  inline bool is_stackChunk()  const;
-  inline bool is_array()       const;
-  inline bool is_objArray()    const;
-  inline bool is_typeArray()   const;
+  inline bool is_instance()         const;
+  inline bool is_inline_type()      const;
+  inline bool is_instanceRef()      const;
+  inline bool is_stackChunk()       const;
+  inline bool is_array()            const;
+  inline bool is_objArray()         const;
+  inline bool is_typeArray()        const;
+  inline bool is_flatArray()        const;
+  inline bool is_null_free_array()  const;
 
   // type test operations that don't require inclusion of oop.inline.hpp.
-  bool is_instance_noinline()    const;
-  bool is_instanceRef_noinline() const;
-  bool is_stackChunk_noinline()  const;
-  bool is_array_noinline()       const;
-  bool is_objArray_noinline()    const;
-  bool is_typeArray_noinline()   const;
+  bool is_instance_noinline()         const;
+  bool is_instanceRef_noinline()      const;
+  bool is_stackChunk_noinline()       const;
+  bool is_array_noinline()            const;
+  bool is_objArray_noinline()         const;
+  bool is_typeArray_noinline()        const;
+  bool is_flatArray_noinline()        const;
+  bool is_null_free_array_noinline()  const;
 
  protected:
   inline oop        as_oop() const { return const_cast<oopDesc*>(this); }
 
  public:
diff a/src/hotspot/share/oops/oop.inline.hpp b/src/hotspot/share/oops/oop.inline.hpp
--- a/src/hotspot/share/oops/oop.inline.hpp
+++ b/src/hotspot/share/oops/oop.inline.hpp
@@ -81,11 +81,11 @@
 markWord oopDesc::cas_set_mark(markWord new_mark, markWord old_mark, atomic_memory_order order) {
   return Atomic::cmpxchg(&_mark, old_mark, new_mark, order);
 }
 
 void oopDesc::init_mark() {
-  set_mark(markWord::prototype());
+  set_mark(Klass::default_prototype_header(klass()));
 }
 
 Klass* oopDesc::klass() const {
   if (UseCompressedClassPointers) {
     return CompressedKlassPointers::decode_not_null(_metadata._compressed_klass);
@@ -207,10 +207,25 @@
 bool oopDesc::is_stackChunk()  const { return klass()->is_stack_chunk_instance_klass(); }
 bool oopDesc::is_array()       const { return klass()->is_array_klass();                }
 bool oopDesc::is_objArray()    const { return klass()->is_objArray_klass();             }
 bool oopDesc::is_typeArray()   const { return klass()->is_typeArray_klass();            }
 
+bool oopDesc::is_inline_type() const { return mark().is_inline_type(); }
+#ifdef _LP64
+bool oopDesc::is_flatArray() const {
+  markWord mrk = mark();
+  return (mrk.is_unlocked()) ? mrk.is_flat_array() : klass()->is_flatArray_klass();
+}
+bool oopDesc::is_null_free_array() const {
+  markWord mrk = mark();
+  return (mrk.is_unlocked()) ? mrk.is_null_free_array() : klass()->is_null_free_array_klass();
+}
+#else
+bool oopDesc::is_flatArray()       const { return klass()->is_flatArray_klass(); }
+bool oopDesc::is_null_free_array() const { return klass()->is_null_free_array_klass(); }
+#endif
+
 template<typename T>
 T*       oopDesc::field_addr(int offset)     const { return reinterpret_cast<T*>(cast_from_oop<intptr_t>(as_oop()) + offset); }
 
 template <typename T>
 size_t   oopDesc::field_offset(T* p) const { return pointer_delta((void*)p, (void*)this, 1); }
diff a/src/hotspot/share/oops/oopsHierarchy.hpp b/src/hotspot/share/oops/oopsHierarchy.hpp
--- a/src/hotspot/share/oops/oopsHierarchy.hpp
+++ b/src/hotspot/share/oops/oopsHierarchy.hpp
@@ -45,10 +45,11 @@
 typedef class   instanceOopDesc*            instanceOop;
 typedef class     stackChunkOopDesc*          stackChunkOop;
 typedef class   arrayOopDesc*               arrayOop;
 typedef class     objArrayOopDesc*            objArrayOop;
 typedef class     typeArrayOopDesc*           typeArrayOop;
+typedef class     flatArrayOopDesc*           flatArrayOop;
 
 #else
 
 // When CHECK_UNHANDLED_OOPS is defined, an "oop" is a class with a
 // carefully chosen set of constructors and conversion operators to go
@@ -153,10 +154,11 @@
 DEF_OOP(instance);
 DEF_OOP(stackChunk);
 DEF_OOP(array);
 DEF_OOP(objArray);
 DEF_OOP(typeArray);
+DEF_OOP(flatArray);
 
 #endif // CHECK_UNHANDLED_OOPS
 
 // Cast functions to convert to and from oops.
 template <typename T> inline oop cast_to_oop(T value) {
@@ -185,14 +187,16 @@
 
 // The klass hierarchy is separate from the oop hierarchy.
 
 class Klass;
 class   InstanceKlass;
+class     InlineKlass;
 class     InstanceMirrorKlass;
 class     InstanceClassLoaderKlass;
 class     InstanceRefKlass;
 class     InstanceStackChunkKlass;
 class   ArrayKlass;
 class     ObjArrayKlass;
 class     TypeArrayKlass;
+class     FlatArrayKlass;
 
 #endif // SHARE_OOPS_OOPSHIERARCHY_HPP
diff a/src/hotspot/share/oops/resolvedFieldEntry.cpp b/src/hotspot/share/oops/resolvedFieldEntry.cpp
--- a/src/hotspot/share/oops/resolvedFieldEntry.cpp
+++ b/src/hotspot/share/oops/resolvedFieldEntry.cpp
@@ -22,10 +22,13 @@
  *
  */
 
 #include "precompiled.hpp"
 #include "resolvedFieldEntry.hpp"
+#include "interpreter/bytecodes.hpp"
+#include "oops/instanceOop.hpp"
+#include "utilities/globalDefinitions.hpp"
 
 void ResolvedFieldEntry::print_on(outputStream* st) const {
   st->print_cr("Field Entry:");
 
   if (field_holder() != nullptr) {
@@ -37,14 +40,25 @@
   st->print_cr(" - Field Index: %d", field_index());
   st->print_cr(" - CP Index: %d", constant_pool_index());
   st->print_cr(" - TOS: %s", type2name(as_BasicType((TosState)tos_state())));
   st->print_cr(" - Is Final: %d", is_final());
   st->print_cr(" - Is Volatile: %d", is_volatile());
+  st->print_cr(" - Is Flat: %d", is_flat());
+  st->print_cr(" - Is Null Free Inline Type: %d", is_null_free_inline_type());
   st->print_cr(" - Get Bytecode: %s", Bytecodes::name((Bytecodes::Code)get_code()));
   st->print_cr(" - Put Bytecode: %s", Bytecodes::name((Bytecodes::Code)put_code()));
 }
 
+bool ResolvedFieldEntry::is_valid() const {
+  return field_holder()->is_instance_klass() &&
+    field_offset() >= instanceOopDesc::base_offset_in_bytes() && field_offset() < 0x7fffffff &&
+    as_BasicType((TosState)tos_state()) != T_ILLEGAL &&
+    _flags < (1 << (max_flag_shift + 1)) &&
+    (get_code() == 0 || get_code() == Bytecodes::_getstatic || get_code() == Bytecodes::_getfield) &&
+    (put_code() == 0 || put_code() == Bytecodes::_putstatic || put_code() == Bytecodes::_putfield);
+}
+
 void ResolvedFieldEntry::remove_unshareable_info() {
   u2 saved_cpool_index = _cpool_index;
   memset(this, 0, sizeof(*this));
   _cpool_index = saved_cpool_index;
 }
diff a/src/hotspot/share/oops/resolvedFieldEntry.hpp b/src/hotspot/share/oops/resolvedFieldEntry.hpp
--- a/src/hotspot/share/oops/resolvedFieldEntry.hpp
+++ b/src/hotspot/share/oops/resolvedFieldEntry.hpp
@@ -50,11 +50,11 @@
   InstanceKlass* _field_holder; // Field holder klass
   int _field_offset;            // Field offset in bytes
   u2 _field_index;              // Index into field information in holder InstanceKlass
   u2 _cpool_index;              // Constant pool index
   u1 _tos_state;                // TOS state
-  u1 _flags;                    // Flags: [0000|00|is_final|is_volatile]
+  u1 _flags;                    // Flags: [00|has_internal_null_marker|has_null_marker|is_null_free_inline_type|is_flat|is_final|is_volatile]
   u1 _get_code, _put_code;      // Get and Put bytecodes of the field
 
 public:
   ResolvedFieldEntry(u2 cpi) :
     _field_holder(nullptr),
@@ -71,10 +71,15 @@
   // Bit shift to get flags
   // Note: Only two flags exists at the moment but more could be added
   enum {
       is_volatile_shift     = 0,
       is_final_shift        = 1, // unused
+      is_flat_shift         = 2,
+      is_null_free_inline_type_shift = 3,
+      has_null_marker_shift = 4,
+      has_internal_null_marker_shift = 5,
+      max_flag_shift = has_internal_null_marker_shift
   };
 
   // Getters
   InstanceKlass* field_holder() const { return _field_holder; }
   int field_offset()            const { return _field_offset; }
@@ -83,10 +88,14 @@
   u1 tos_state()                const { return _tos_state;    }
   u1 get_code()                 const { return Atomic::load_acquire(&_get_code);      }
   u1 put_code()                 const { return Atomic::load_acquire(&_put_code);      }
   bool is_final()               const { return (_flags & (1 << is_final_shift))    != 0; }
   bool is_volatile ()           const { return (_flags & (1 << is_volatile_shift)) != 0; }
+  bool is_flat()                const { return (_flags & (1 << is_flat_shift))     != 0; }
+  bool is_null_free_inline_type() const { return (_flags & (1 << is_null_free_inline_type_shift)) != 0; }
+  bool has_null_marker()        const { return (_flags & (1 << has_null_marker_shift)) != 0; }
+  bool has_internal_null_marker() const { return (_flags & (1 << has_internal_null_marker_shift)) != 0; }
   bool is_resolved(Bytecodes::Code code) const {
     switch(code) {
     case Bytecodes::_getstatic:
     case Bytecodes::_getfield:
       return (get_code() == code);
@@ -100,15 +109,24 @@
   }
 
   // Printing
   void print_on(outputStream* st) const;
 
-  void set_flags(bool is_final_flag, bool is_volatile_flag) {
-    int new_flags = (is_final_flag << is_final_shift) | static_cast<int>(is_volatile_flag);
+  void set_flags(bool is_final_flag, bool is_volatile_flag, bool is_flat_flag, bool is_null_free_inline_type_flag,
+                 bool has_null_marker_flag, bool has_internal_null_marker_flag) {
+    u1 new_flags = ((is_final_flag ? 1 : 0) << is_final_shift) | static_cast<int>(is_volatile_flag) |
+      ((is_flat_flag ? 1 : 0) << is_flat_shift) |
+      ((is_null_free_inline_type_flag ? 1 : 0) << is_null_free_inline_type_shift) |
+      ((has_null_marker_flag ? 1 : 0) << has_null_marker_shift) |
+      ((has_internal_null_marker_flag ? 1 : 0) << has_internal_null_marker_shift);
     _flags = checked_cast<u1>(new_flags);
     assert(is_final() == is_final_flag, "Must be");
     assert(is_volatile() == is_volatile_flag, "Must be");
+    assert(is_flat() == is_flat_flag, "Must be");
+    assert(is_null_free_inline_type() == is_null_free_inline_type_flag, "Must be");
+    assert(has_null_marker() == has_null_marker_flag, "Must be");
+    assert(has_internal_null_marker() == has_internal_null_marker_flag, "Must be");
   }
 
   inline void set_bytecode(u1* code, u1 new_code) {
   #ifdef ASSERT
     // Read once.
@@ -116,20 +134,21 @@
     assert(c == 0 || c == new_code || new_code == 0, "update must be consistent");
   #endif
     Atomic::release_store(code, new_code);
   }
 
-  // Populate the strucutre with resolution information
+  // Populate the structure with resolution information
   void fill_in(InstanceKlass* klass, int offset, u2 index, u1 tos_state, u1 b1, u1 b2) {
     _field_holder = klass;
     _field_offset = offset;
     _field_index = index;
     _tos_state = tos_state;
 
     // These must be set after the other fields
     set_bytecode(&_get_code, b1);
     set_bytecode(&_put_code, b2);
+    assert(is_valid(), "invalid");
   }
 
   // CDS
   void remove_unshareable_info();
 
@@ -140,8 +159,10 @@
   static ByteSize get_code_offset()     { return byte_offset_of(ResolvedFieldEntry, _get_code);     }
   static ByteSize put_code_offset()     { return byte_offset_of(ResolvedFieldEntry, _put_code);     }
   static ByteSize type_offset()         { return byte_offset_of(ResolvedFieldEntry, _tos_state);    }
   static ByteSize flags_offset()        { return byte_offset_of(ResolvedFieldEntry, _flags);        }
 
+  // Debug help
+  bool is_valid() const;
 };
 
 #endif //SHARE_OOPS_RESOLVEDFIELDENTRY_HPP
diff a/src/hotspot/share/oops/symbol.cpp b/src/hotspot/share/oops/symbol.cpp
--- a/src/hotspot/share/oops/symbol.cpp
+++ b/src/hotspot/share/oops/symbol.cpp
@@ -85,10 +85,50 @@
   assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint");
   _hash_and_refcount =  pack_hash_and_refcount(extract_hash(_hash_and_refcount), PERM_REFCOUNT);
 }
 #endif
 
+Symbol* Symbol::fundamental_name(TRAPS) {
+  if (char_at(0) == JVM_SIGNATURE_CLASS && ends_with(JVM_SIGNATURE_ENDCLASS)) {
+    return SymbolTable::new_symbol(this, 1, utf8_length() - 1);
+  } else {
+    // reference count is incremented to be consistent with the behavior with
+    // the SymbolTable::new_symbol() call above
+    this->increment_refcount();
+    return this;
+  }
+}
+
+bool Symbol::is_same_fundamental_type(Symbol* s) const {
+  if (this == s) return true;
+  if (utf8_length() < 3) return false;
+  int offset1, offset2, len;
+  if (ends_with(JVM_SIGNATURE_ENDCLASS)) {
+    if (char_at(0) != JVM_SIGNATURE_CLASS) return false;
+    offset1 = 1;
+    len = utf8_length() - 2;
+  } else {
+    offset1 = 0;
+    len = utf8_length();
+  }
+  if (ends_with(JVM_SIGNATURE_ENDCLASS)) {
+    if (s->char_at(0) != JVM_SIGNATURE_CLASS) return false;
+    offset2 = 1;
+  } else {
+    offset2 = 0;
+  }
+  if ((offset2 + len) > s->utf8_length()) return false;
+  if ((utf8_length() - offset1 * 2) != (s->utf8_length() - offset2 * 2))
+    return false;
+  int l = len;
+  while (l-- > 0) {
+    if (char_at(offset1 + l) != s->char_at(offset2 + l))
+      return false;
+  }
+  return true;
+}
+
 // ------------------------------------------------------------------
 // Symbol::index_of
 //
 // Test if we have the give substring at or after the i-th char of this
 // symbol's utf8 bytes.
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
@@ -239,10 +239,19 @@
     if (position < 0)  return false;  // can happen with ends_with
     if (position >= utf8_length()) return false;
     return code_byte == char_at(position);
   }
 
+  // True if this is a descriptor for a method with void return.
+  // (Assumes it is a valid descriptor.)
+  bool is_void_method_signature() const {
+    return starts_with('(') && ends_with('V');
+  }
+
+  Symbol* fundamental_name(TRAPS);
+  bool is_same_fundamental_type(Symbol*) const;
+
   // Test if the symbol has the give substring at or after the i-th char.
   int index_of_at(int i, const char* substr, int substr_len) const;
 
   // Three-way compare for sorting; returns -1/0/1 if receiver is </==/> than arg
   // note that the ordering is not alfabetical
diff a/src/hotspot/share/oops/typeArrayKlass.cpp b/src/hotspot/share/oops/typeArrayKlass.cpp
--- a/src/hotspot/share/oops/typeArrayKlass.cpp
+++ b/src/hotspot/share/oops/typeArrayKlass.cpp
@@ -93,11 +93,10 @@
   return (typeArrayOop)Universe::heap()->array_allocate(this, size, length,
                                                         do_zero, CHECK_NULL);
 }
 
 oop TypeArrayKlass::multi_allocate(int rank, jint* last_size, TRAPS) {
-  // For typeArrays this is only called for the last dimension
   assert(rank == 1, "just checking");
   int length = *last_size;
   return allocate(length, THREAD);
 }
 
diff a/src/hotspot/share/oops/typeArrayOop.hpp b/src/hotspot/share/oops/typeArrayOop.hpp
--- a/src/hotspot/share/oops/typeArrayOop.hpp
+++ b/src/hotspot/share/oops/typeArrayOop.hpp
@@ -113,11 +113,10 @@
 
   // Sizing
 
   // Returns the number of words necessary to hold an array of "len"
   // elements each of the given "byte_size".
- private:
   static size_t object_size(int lh, int length) {
     int instance_header_size = Klass::layout_helper_header_size(lh);
     int element_shift = Klass::layout_helper_log2_element_size(lh);
     DEBUG_ONLY(BasicType etype = Klass::layout_helper_element_type(lh));
     assert(length <= arrayOopDesc::max_array_length(etype), "no overflow");
diff a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp
--- a/src/hotspot/share/opto/addnode.cpp
+++ b/src/hotspot/share/opto/addnode.cpp
@@ -674,10 +674,16 @@
   const TypeX *tx = t->is_intptr_t();
   intptr_t txoffset = Type::OffsetBot;
   if (tx->is_con()) {   // Left input is an add of a constant?
     txoffset = tx->get_con();
   }
+  if (tp->isa_aryptr()) {
+    // In the case of a flat inline type array, each field has its
+    // own slice so we need to extract the field being accessed from
+    // the address computation
+    return tp->is_aryptr()->add_field_offset_and_offset(txoffset);
+  }
   return tp->add_offset(txoffset);
 }
 
 //------------------------------Value------------------------------------------
 const Type* AddPNode::Value(PhaseGVN* phase) const {
@@ -694,10 +700,16 @@
   // Add 'em
   intptr_t p2offset = Type::OffsetBot;
   if (p2->is_con()) {   // Left input is an add of a constant?
     p2offset = p2->get_con();
   }
+  if (p1->isa_aryptr()) {
+    // In the case of a flat inline type array, each field has its
+    // own slice so we need to extract the field being accessed from
+    // the address computation
+    return p1->is_aryptr()->add_field_offset_and_offset(p2offset);
+  }
   return p1->add_offset(p2offset);
 }
 
 //------------------------Ideal_base_and_offset--------------------------------
 // Split an oop pointer into a base and offset.
diff a/src/hotspot/share/opto/arraycopynode.cpp b/src/hotspot/share/opto/arraycopynode.cpp
--- a/src/hotspot/share/opto/arraycopynode.cpp
+++ b/src/hotspot/share/opto/arraycopynode.cpp
@@ -21,16 +21,18 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "ci/ciFlatArrayKlass.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/c2/barrierSetC2.hpp"
 #include "gc/shared/c2/cardTableBarrierSetC2.hpp"
 #include "gc/shared/gc_globals.hpp"
 #include "opto/arraycopynode.hpp"
 #include "opto/graphKit.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "utilities/macros.hpp"
 #include "utilities/powerOfTwo.hpp"
 
 ArrayCopyNode::ArrayCopyNode(Compile* C, bool alloc_tightly_coupled, bool has_negative_length_guard)
@@ -112,14 +114,18 @@
 
   return is_clonebasic() ? length->find_intptr_t_con(-1) : length->find_int_con(-1);
 }
 
 int ArrayCopyNode::get_count(PhaseGVN *phase) const {
-  Node* src = in(ArrayCopyNode::Src);
-  const Type* src_type = phase->type(src);
-
   if (is_clonebasic()) {
+    Node* src = in(ArrayCopyNode::Src);
+    const Type* src_type = phase->type(src);
+
+    if (src_type == Type::TOP) {
+      return -1;
+    }
+
     if (src_type->isa_instptr()) {
       const TypeInstPtr* inst_src = src_type->is_instptr();
       ciInstanceKlass* ik = inst_src->instance_klass();
       // ciInstanceKlass::nof_nonstatic_fields() doesn't take injected
       // fields into account. They are rare anyway so easier to simply
@@ -138,10 +144,11 @@
       // must be too. Note that the opposite does not need to hold,
       // because different input array lengths (e.g. int arrays with
       // 3 or 4 elements) might lead to the same length input
       // (e.g. 2 double-words).
       assert(!ary_src->size()->is_con() || (get_length_if_constant(phase) >= 0) ||
+             (UseFlatArray && ary_src->elem()->make_oopptr() != nullptr && ary_src->elem()->make_oopptr()->can_be_inline_type()) ||
              phase->is_IterGVN() || phase->C->inlining_incrementally() || StressReflectiveCode, "inconsistent");
       if (ary_src->size()->is_con()) {
         return ary_src->size()->get_con();
       }
       return -1;
@@ -188,10 +195,11 @@
   if (inst_src == nullptr) {
     return nullptr;
   }
 
   MergeMemNode* mem = phase->transform(MergeMemNode::make(in_mem))->as_MergeMem();
+  phase->record_for_igvn(mem);
   if (can_reshape) {
     phase->is_IterGVN()->_worklist.push(mem);
   }
 
 
@@ -274,25 +282,29 @@
     BasicType src_elem = ary_src->elem()->array_element_basic_type();
     BasicType dest_elem = ary_dest->elem()->array_element_basic_type();
     if (is_reference_type(src_elem, true)) src_elem = T_OBJECT;
     if (is_reference_type(dest_elem, true)) dest_elem = T_OBJECT;
 
-    if (src_elem != dest_elem || dest_elem == T_VOID) {
+    if (src_elem != dest_elem || ary_src->is_flat() != ary_dest->is_flat() || dest_elem == T_VOID) {
       // We don't know if arguments are arrays of the same type
       return false;
     }
 
     BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
-    if (bs->array_copy_requires_gc_barriers(is_alloc_tightly_coupled(), dest_elem, false, false, BarrierSetC2::Optimization)) {
-      // It's an object array copy but we can't emit the card marking
-      // that is needed
+    if ((!ary_dest->is_flat() && bs->array_copy_requires_gc_barriers(is_alloc_tightly_coupled(), dest_elem, false, false, BarrierSetC2::Optimization)) ||
+        (ary_dest->is_flat() && ary_src->elem()->inline_klass()->contains_oops() &&
+         bs->array_copy_requires_gc_barriers(is_alloc_tightly_coupled(), T_OBJECT, false, false, BarrierSetC2::Optimization))) {
+      // It's an object array copy but we can't emit the card marking that is needed
       return false;
     }
 
     value_type = ary_src->elem();
 
     uint shift  = exact_log2(type2aelembytes(dest_elem));
+    if (ary_dest->is_flat()) {
+      shift = ary_src->flat_log_elem_size();
+    }
     uint header = arrayOopDesc::base_offset_in_bytes(dest_elem);
 
     src_offset = Compile::conv_I2X_index(phase, src_offset, ary_src->size());
     if (src_offset->is_top()) {
       // Offset is out of bounds (the ArrayCopyNode will be removed)
@@ -328,17 +340,25 @@
     assert(ary_src != nullptr, "should be a clone");
     assert(is_clonebasic(), "should be");
 
     disjoint_bases = true;
 
+    if (ary_src->elem()->make_oopptr() != nullptr &&
+        ary_src->elem()->make_oopptr()->can_be_inline_type()) {
+      return false;
+    }
+
     BasicType elem = ary_src->isa_aryptr()->elem()->array_element_basic_type();
     if (is_reference_type(elem, true)) {
       elem = T_OBJECT;
     }
 
     BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
-    if (bs->array_copy_requires_gc_barriers(true, elem, true, is_clone_inst(), BarrierSetC2::Optimization)) {
+    if ((!ary_src->is_flat() && bs->array_copy_requires_gc_barriers(true, elem, true, is_clone_inst(), BarrierSetC2::Optimization)) ||
+        (ary_src->is_flat() && ary_src->elem()->inline_klass()->contains_oops() &&
+         bs->array_copy_requires_gc_barriers(true, T_OBJECT, true, is_clone_inst(), BarrierSetC2::Optimization))) {
+      // It's an object array copy but we can't emit the card marking that is needed
       return false;
     }
 
     adr_src  = phase->transform(new AddPNode(base_src, base_src, src_offset));
     adr_dest = phase->transform(new AddPNode(base_dest, base_dest, dest_offset));
@@ -358,113 +378,128 @@
     value_type = ary_src->elem();
   }
   return true;
 }
 
-const TypePtr* ArrayCopyNode::get_address_type(PhaseGVN* phase, const TypePtr* atp, Node* n) {
+const TypeAryPtr* ArrayCopyNode::get_address_type(PhaseGVN* phase, const TypePtr* atp, Node* n) {
   if (atp == TypeOopPtr::BOTTOM) {
     atp = phase->type(n)->isa_ptr();
   }
   // adjust atp to be the correct array element address type
-  return atp->add_offset(Type::OffsetBot);
+  return atp->add_offset(Type::OffsetBot)->is_aryptr();
 }
 
-void ArrayCopyNode::array_copy_test_overlap(PhaseGVN *phase, bool can_reshape, bool disjoint_bases, int count, Node*& forward_ctl, Node*& backward_ctl) {
-  Node* ctl = in(TypeFunc::Control);
+void ArrayCopyNode::array_copy_test_overlap(GraphKit& kit, bool disjoint_bases, int count, Node*& backward_ctl) {
+  Node* ctl = kit.control();
   if (!disjoint_bases && count > 1) {
+    PhaseGVN& gvn = kit.gvn();
     Node* src_offset = in(ArrayCopyNode::SrcPos);
     Node* dest_offset = in(ArrayCopyNode::DestPos);
     assert(src_offset != nullptr && dest_offset != nullptr, "should be");
-    Node* cmp = phase->transform(new CmpINode(src_offset, dest_offset));
-    Node *bol = phase->transform(new BoolNode(cmp, BoolTest::lt));
+    Node* cmp = gvn.transform(new CmpINode(src_offset, dest_offset));
+    Node *bol = gvn.transform(new BoolNode(cmp, BoolTest::lt));
     IfNode *iff = new IfNode(ctl, bol, PROB_FAIR, COUNT_UNKNOWN);
 
-    phase->transform(iff);
+    gvn.transform(iff);
+
+    kit.set_control(gvn.transform(new IfFalseNode(iff)));
+    backward_ctl = gvn.transform(new IfTrueNode(iff));
+  }
+}
 
-    forward_ctl = phase->transform(new IfFalseNode(iff));
-    backward_ctl = phase->transform(new IfTrueNode(iff));
+void ArrayCopyNode::copy(GraphKit& kit,
+                         const TypeAryPtr* atp_src,
+                         const TypeAryPtr* atp_dest,
+                         int i,
+                         Node* base_src,
+                         Node* base_dest,
+                         Node* adr_src,
+                         Node* adr_dest,
+                         BasicType copy_type,
+                         const Type* value_type) {
+  BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
+  Node* ctl = kit.control();
+  if (atp_dest->is_flat()) {
+    ciInlineKlass* vk = atp_src->elem()->inline_klass();
+    for (int j = 0; j < vk->nof_nonstatic_fields(); j++) {
+      ciField* field = vk->nonstatic_field_at(j);
+      int off_in_vt = field->offset_in_bytes() - vk->first_field_offset();
+      Node* off  = kit.MakeConX(off_in_vt + i * atp_src->flat_elem_size());
+      ciType* ft = field->type();
+      BasicType bt = type2field[ft->basic_type()];
+      assert(!field->is_flat(), "flat field encountered");
+      const Type* rt = Type::get_const_type(ft);
+      const TypePtr* adr_type = atp_src->with_field_offset(off_in_vt)->add_offset(Type::OffsetBot);
+      assert(!bs->array_copy_requires_gc_barriers(is_alloc_tightly_coupled(), bt, false, false, BarrierSetC2::Optimization), "GC barriers required");
+      Node* next_src = kit.gvn().transform(new AddPNode(base_src, adr_src, off));
+      Node* next_dest = kit.gvn().transform(new AddPNode(base_dest, adr_dest, off));
+      Node* v = load(bs, &kit.gvn(), ctl, kit.merged_memory(), next_src, adr_type, rt, bt);
+      store(bs, &kit.gvn(), ctl, kit.merged_memory(), next_dest, adr_type, v, rt, bt);
+    }
   } else {
-    forward_ctl = ctl;
+    Node* off = kit.MakeConX(type2aelembytes(copy_type) * i);
+    Node* next_src = kit.gvn().transform(new AddPNode(base_src, adr_src, off));
+    Node* next_dest = kit.gvn().transform(new AddPNode(base_dest, adr_dest, off));
+    Node* v = load(bs, &kit.gvn(), ctl, kit.merged_memory(), next_src, atp_src, value_type, copy_type);
+    store(bs, &kit.gvn(), ctl, kit.merged_memory(), next_dest, atp_dest, v, value_type, copy_type);
   }
+  kit.set_control(ctl);
 }
 
-Node* ArrayCopyNode::array_copy_forward(PhaseGVN *phase,
-                                        bool can_reshape,
-                                        Node*& forward_ctl,
-                                        Node* mem,
-                                        const TypePtr* atp_src,
-                                        const TypePtr* atp_dest,
-                                        Node* adr_src,
-                                        Node* base_src,
-                                        Node* adr_dest,
-                                        Node* base_dest,
-                                        BasicType copy_type,
-                                        const Type* value_type,
-                                        int count) {
-  if (!forward_ctl->is_top()) {
-    // copy forward
-    MergeMemNode* mm = MergeMemNode::make(mem);
 
+void ArrayCopyNode::array_copy_forward(GraphKit& kit,
+                                       bool can_reshape,
+                                       const TypeAryPtr* atp_src,
+                                       const TypeAryPtr* atp_dest,
+                                       Node* adr_src,
+                                       Node* base_src,
+                                       Node* adr_dest,
+                                       Node* base_dest,
+                                       BasicType copy_type,
+                                       const Type* value_type,
+                                       int count) {
+  if (!kit.stopped()) {
+    // copy forward
     if (count > 0) {
-      BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
-      Node* v = load(bs, phase, forward_ctl, mm, adr_src, atp_src, value_type, copy_type);
-      store(bs, phase, forward_ctl, mm, adr_dest, atp_dest, v, value_type, copy_type);
-      for (int i = 1; i < count; i++) {
-        Node* off  = phase->MakeConX(type2aelembytes(copy_type) * i);
-        Node* next_src = phase->transform(new AddPNode(base_src,adr_src,off));
-        Node* next_dest = phase->transform(new AddPNode(base_dest,adr_dest,off));
-        v = load(bs, phase, forward_ctl, mm, next_src, atp_src, value_type, copy_type);
-        store(bs, phase, forward_ctl, mm, next_dest, atp_dest, v, value_type, copy_type);
+      for (int i = 0; i < count; i++) {
+        copy(kit, atp_src, atp_dest, i, base_src, base_dest, adr_src, adr_dest, copy_type, value_type);
       }
     } else if (can_reshape) {
-      PhaseIterGVN* igvn = phase->is_IterGVN();
-      igvn->_worklist.push(adr_src);
-      igvn->_worklist.push(adr_dest);
+      PhaseGVN& gvn = kit.gvn();
+      assert(gvn.is_IterGVN(), "");
+      gvn.record_for_igvn(adr_src);
+      gvn.record_for_igvn(adr_dest);
     }
-    return mm;
   }
-  return phase->C->top();
 }
 
-Node* ArrayCopyNode::array_copy_backward(PhaseGVN *phase,
-                                         bool can_reshape,
-                                         Node*& backward_ctl,
-                                         Node* mem,
-                                         const TypePtr* atp_src,
-                                         const TypePtr* atp_dest,
-                                         Node* adr_src,
-                                         Node* base_src,
-                                         Node* adr_dest,
-                                         Node* base_dest,
-                                         BasicType copy_type,
-                                         const Type* value_type,
-                                         int count) {
-  if (!backward_ctl->is_top()) {
+void ArrayCopyNode::array_copy_backward(GraphKit& kit,
+                                        bool can_reshape,
+                                        const TypeAryPtr* atp_src,
+                                        const TypeAryPtr* atp_dest,
+                                        Node* adr_src,
+                                        Node* base_src,
+                                        Node* adr_dest,
+                                        Node* base_dest,
+                                        BasicType copy_type,
+                                        const Type* value_type,
+                                        int count) {
+  if (!kit.stopped()) {
     // copy backward
-    MergeMemNode* mm = MergeMemNode::make(mem);
-
-    BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
-    assert(copy_type != T_OBJECT || !bs->array_copy_requires_gc_barriers(false, T_OBJECT, false, false, BarrierSetC2::Optimization), "only tightly coupled allocations for object arrays");
+    PhaseGVN& gvn = kit.gvn();
 
     if (count > 0) {
-      for (int i = count-1; i >= 1; i--) {
-        Node* off  = phase->MakeConX(type2aelembytes(copy_type) * i);
-        Node* next_src = phase->transform(new AddPNode(base_src,adr_src,off));
-        Node* next_dest = phase->transform(new AddPNode(base_dest,adr_dest,off));
-        Node* v = load(bs, phase, backward_ctl, mm, next_src, atp_src, value_type, copy_type);
-        store(bs, phase, backward_ctl, mm, next_dest, atp_dest, v, value_type, copy_type);
+      for (int i = count-1; i >= 0; i--) {
+        copy(kit, atp_src, atp_dest, i, base_src, base_dest, adr_src, adr_dest, copy_type, value_type);
       }
-      Node* v = load(bs, phase, backward_ctl, mm, adr_src, atp_src, value_type, copy_type);
-      store(bs, phase, backward_ctl, mm, adr_dest, atp_dest, v, value_type, copy_type);
-    } else if (can_reshape) {
-      PhaseIterGVN* igvn = phase->is_IterGVN();
-      igvn->_worklist.push(adr_src);
-      igvn->_worklist.push(adr_dest);
+    } else if(can_reshape) {
+      PhaseGVN& gvn = kit.gvn();
+      assert(gvn.is_IterGVN(), "");
+      gvn.record_for_igvn(adr_src);
+      gvn.record_for_igvn(adr_dest);
     }
-    return phase->transform(mm);
   }
-  return phase->C->top();
 }
 
 bool ArrayCopyNode::finish_transform(PhaseGVN *phase, bool can_reshape,
                                      Node* ctl, Node *mem) {
   if (can_reshape) {
@@ -485,21 +520,20 @@
       Node* out_ctl = proj_out(TypeFunc::Control);
       igvn->replace_node(out_ctl, ctl);
     } else {
       // replace fallthrough projections of the ArrayCopyNode by the
       // new memory, control and the input IO.
-      CallProjections callprojs;
-      extract_projections(&callprojs, true, false);
+      CallProjections* callprojs = extract_projections(true, false);
 
-      if (callprojs.fallthrough_ioproj != nullptr) {
-        igvn->replace_node(callprojs.fallthrough_ioproj, in(TypeFunc::I_O));
+      if (callprojs->fallthrough_ioproj != nullptr) {
+        igvn->replace_node(callprojs->fallthrough_ioproj, in(TypeFunc::I_O));
       }
-      if (callprojs.fallthrough_memproj != nullptr) {
-        igvn->replace_node(callprojs.fallthrough_memproj, mem);
+      if (callprojs->fallthrough_memproj != nullptr) {
+        igvn->replace_node(callprojs->fallthrough_memproj, mem);
       }
-      if (callprojs.fallthrough_catchproj != nullptr) {
-        igvn->replace_node(callprojs.fallthrough_catchproj, ctl);
+      if (callprojs->fallthrough_catchproj != nullptr) {
+        igvn->replace_node(callprojs->fallthrough_catchproj, ctl);
       }
 
       // The ArrayCopyNode is not disconnected. It still has the
       // projections for the exception case. Replace current
       // ArrayCopyNode with a dummy new one with a top() control so
@@ -520,11 +554,15 @@
   return true;
 }
 
 
 Node *ArrayCopyNode::Ideal(PhaseGVN *phase, bool can_reshape) {
-  if (remove_dead_region(phase, can_reshape))  return this;
+  // Perform any generic optimizations first
+  Node* result = SafePointNode::Ideal(phase, can_reshape);
+  if (result != nullptr) {
+    return result;
+  }
 
   if (StressArrayCopyMacroNode && !can_reshape) {
     phase->record_for_igvn(this);
     return nullptr;
   }
@@ -562,10 +600,21 @@
 
   if (count < 0 || count > ArrayCopyLoadStoreMaxElem) {
     return nullptr;
   }
 
+  Node* src = in(ArrayCopyNode::Src);
+  Node* dest = in(ArrayCopyNode::Dest);
+  const Type* src_type = phase->type(src);
+  const Type* dest_type = phase->type(dest);
+
+  if (src_type->isa_aryptr() && dest_type->isa_instptr()) {
+    // clone used for load of unknown inline type can't be optimized at
+    // this point
+    return nullptr;
+  }
+
   Node* mem = try_clone_instance(phase, can_reshape, count);
   if (mem != nullptr) {
     return (mem == NodeSentinel) ? nullptr : mem;
   }
 
@@ -583,71 +632,93 @@
     assert(adr_src == nullptr, "no node can be left behind");
     assert(adr_dest == nullptr, "no node can be left behind");
     return nullptr;
   }
 
-  Node* src = in(ArrayCopyNode::Src);
-  Node* dest = in(ArrayCopyNode::Dest);
-  const TypePtr* atp_src = get_address_type(phase, _src_type, src);
-  const TypePtr* atp_dest = get_address_type(phase, _dest_type, dest);
-  Node* in_mem = in(TypeFunc::Memory);
+  JVMState* new_jvms = nullptr;
+  SafePointNode* new_map = nullptr;
+  if (!is_clonebasic()) {
+    new_jvms = jvms()->clone_shallow(phase->C);
+    new_map = new SafePointNode(req(), new_jvms);
+    for (uint i = TypeFunc::FramePtr; i < req(); i++) {
+      new_map->init_req(i, in(i));
+    }
+    new_jvms->set_map(new_map);
+  } else {
+    new_jvms = new (phase->C) JVMState(0);
+    new_map = new SafePointNode(TypeFunc::Parms, new_jvms);
+    new_jvms->set_map(new_map);
+  }
+  new_map->set_control(in(TypeFunc::Control));
+  new_map->set_memory(MergeMemNode::make(in(TypeFunc::Memory)));
+  new_map->set_i_o(in(TypeFunc::I_O));
+  phase->record_for_igvn(new_map);
+
+  const TypeAryPtr* atp_src = get_address_type(phase, _src_type, src);
+  const TypeAryPtr* atp_dest = get_address_type(phase, _dest_type, dest);
 
   if (can_reshape) {
     assert(!phase->is_IterGVN()->delay_transform(), "cannot delay transforms");
     phase->is_IterGVN()->set_delay_transform(true);
   }
 
+  GraphKit kit(new_jvms, phase);
+
+  SafePointNode* backward_map = nullptr;
+  SafePointNode* forward_map = nullptr;
   Node* backward_ctl = phase->C->top();
-  Node* forward_ctl = phase->C->top();
-  array_copy_test_overlap(phase, can_reshape, disjoint_bases, count, forward_ctl, backward_ctl);
-
-  Node* forward_mem = array_copy_forward(phase, can_reshape, forward_ctl,
-                                         in_mem,
-                                         atp_src, atp_dest,
-                                         adr_src, base_src, adr_dest, base_dest,
-                                         copy_type, value_type, count);
-
-  Node* backward_mem = array_copy_backward(phase, can_reshape, backward_ctl,
-                                           in_mem,
-                                           atp_src, atp_dest,
-                                           adr_src, base_src, adr_dest, base_dest,
-                                           copy_type, value_type, count);
-
-  Node* ctl = nullptr;
-  if (!forward_ctl->is_top() && !backward_ctl->is_top()) {
-    ctl = new RegionNode(3);
-    ctl->init_req(1, forward_ctl);
-    ctl->init_req(2, backward_ctl);
-    ctl = phase->transform(ctl);
-    MergeMemNode* forward_mm = forward_mem->as_MergeMem();
-    MergeMemNode* backward_mm = backward_mem->as_MergeMem();
-    for (MergeMemStream mms(forward_mm, backward_mm); mms.next_non_empty2(); ) {
-      if (mms.memory() != mms.memory2()) {
-        Node* phi = new PhiNode(ctl, Type::MEMORY, phase->C->get_adr_type(mms.alias_idx()));
-        phi->init_req(1, mms.memory());
-        phi->init_req(2, mms.memory2());
-        phi = phase->transform(phi);
-        mms.set_memory(phi);
-      }
-    }
-    mem = forward_mem;
-  } else if (!forward_ctl->is_top()) {
-    ctl = forward_ctl;
-    mem = forward_mem;
+
+  array_copy_test_overlap(kit, disjoint_bases, count, backward_ctl);
+
+  {
+    PreserveJVMState pjvms(&kit);
+
+    array_copy_forward(kit, can_reshape,
+                       atp_src, atp_dest,
+                       adr_src, base_src, adr_dest, base_dest,
+                       copy_type, value_type, count);
+
+    forward_map = kit.stop();
+  }
+
+  kit.set_control(backward_ctl);
+  array_copy_backward(kit, can_reshape,
+                      atp_src, atp_dest,
+                      adr_src, base_src, adr_dest, base_dest,
+                      copy_type, value_type, count);
+
+  backward_map = kit.stop();
+
+  if (!forward_map->control()->is_top() && !backward_map->control()->is_top()) {
+    assert(forward_map->i_o() == backward_map->i_o(), "need a phi on IO?");
+    Node* ctl = new RegionNode(3);
+    Node* mem = new PhiNode(ctl, Type::MEMORY, TypePtr::BOTTOM);
+    kit.set_map(forward_map);
+    ctl->init_req(1, kit.control());
+    mem->init_req(1, kit.reset_memory());
+    kit.set_map(backward_map);
+    ctl->init_req(2, kit.control());
+    mem->init_req(2, kit.reset_memory());
+    kit.set_control(phase->transform(ctl));
+    kit.set_all_memory(phase->transform(mem));
+  } else if (!forward_map->control()->is_top()) {
+    kit.set_map(forward_map);
   } else {
-    assert(!backward_ctl->is_top(), "no copy?");
-    ctl = backward_ctl;
-    mem = backward_mem;
+    assert(!backward_map->control()->is_top(), "no copy?");
+    kit.set_map(backward_map);
   }
 
   if (can_reshape) {
     assert(phase->is_IterGVN()->delay_transform(), "should be delaying transforms");
     phase->is_IterGVN()->set_delay_transform(false);
   }
 
-  if (!finish_transform(phase, can_reshape, ctl, mem)) {
-    if (can_reshape) {
+  mem = kit.map()->memory();
+  if (!finish_transform(phase, can_reshape, kit.control(), mem)) {
+    if (!can_reshape) {
+      phase->record_for_igvn(this);
+    } else {
       // put in worklist, so that if it happens to be dead it is removed
       phase->is_IterGVN()->_worklist.push(mem);
     }
     return nullptr;
   }
@@ -742,11 +813,11 @@
 
   BasicType ary_elem = ary_t->isa_aryptr()->elem()->array_element_basic_type();
   if (is_reference_type(ary_elem, true)) ary_elem = T_OBJECT;
 
   uint header = arrayOopDesc::base_offset_in_bytes(ary_elem);
-  uint elemsize = type2aelembytes(ary_elem);
+  uint elemsize = ary_t->is_flat() ? ary_t->flat_elem_size() : type2aelembytes(ary_elem);
 
   jlong dest_pos_plus_len_lo = (((jlong)dest_pos_t->_lo) + len_t->_lo) * elemsize + header;
   jlong dest_pos_plus_len_hi = (((jlong)dest_pos_t->_hi) + len_t->_hi) * elemsize + header;
   jlong dest_pos_lo = ((jlong)dest_pos_t->_lo) * elemsize + header;
   jlong dest_pos_hi = ((jlong)dest_pos_t->_hi) * elemsize + header;
diff a/src/hotspot/share/opto/arraycopynode.hpp b/src/hotspot/share/opto/arraycopynode.hpp
--- a/src/hotspot/share/opto/arraycopynode.hpp
+++ b/src/hotspot/share/opto/arraycopynode.hpp
@@ -88,31 +88,33 @@
 
   ArrayCopyNode(Compile* C, bool alloc_tightly_coupled, bool has_negative_length_guard);
 
   intptr_t get_length_if_constant(PhaseGVN *phase) const;
   int get_count(PhaseGVN *phase) const;
-  static const TypePtr* get_address_type(PhaseGVN* phase, const TypePtr* atp, Node* n);
+  static const TypeAryPtr* get_address_type(PhaseGVN* phase, const TypePtr* atp, Node* n);
 
   Node* try_clone_instance(PhaseGVN *phase, bool can_reshape, int count);
   bool prepare_array_copy(PhaseGVN *phase, bool can_reshape,
                           Node*& adr_src, Node*& base_src, Node*& adr_dest, Node*& base_dest,
                           BasicType& copy_type, const Type*& value_type, bool& disjoint_bases);
-  void array_copy_test_overlap(PhaseGVN *phase, bool can_reshape,
+  void array_copy_test_overlap(GraphKit& kit,
                                bool disjoint_bases, int count,
-                               Node*& forward_ctl, Node*& backward_ctl);
-  Node* array_copy_forward(PhaseGVN *phase, bool can_reshape, Node*& ctl,
-                           Node* mem,
-                           const TypePtr* atp_src, const TypePtr* atp_dest,
+                               Node*& backward_ctl);
+  void array_copy_forward(GraphKit& kit, bool can_reshape,
+                          const TypeAryPtr* atp_src, const TypeAryPtr* atp_dest,
+                          Node* adr_src, Node* base_src, Node* adr_dest, Node* base_dest,
+                          BasicType copy_type, const Type* value_type, int count);
+  void array_copy_backward(GraphKit& kit, bool can_reshape,
+                           const TypeAryPtr* atp_src, const TypeAryPtr* atp_dest,
                            Node* adr_src, Node* base_src, Node* adr_dest, Node* base_dest,
                            BasicType copy_type, const Type* value_type, int count);
-  Node* array_copy_backward(PhaseGVN *phase, bool can_reshape, Node*& ctl,
-                            Node* mem,
-                            const TypePtr* atp_src, const TypePtr* atp_dest,
-                            Node* adr_src, Node* base_src, Node* adr_dest, Node* base_dest,
-                            BasicType copy_type, const Type* value_type, int count);
   bool finish_transform(PhaseGVN *phase, bool can_reshape,
                         Node* ctl, Node *mem);
+  void copy(GraphKit& kit, const TypeAryPtr* atp_src, const TypeAryPtr* atp_dest, int i,
+            Node* base_src, Node* base_dest, Node* adr_src, Node* adr_dest,
+            BasicType copy_type, const Type* value_type);
+
   static bool may_modify_helper(const TypeOopPtr* t_oop, Node* n, PhaseValues* phase, CallNode*& call);
 public:
   static Node* load(BarrierSetC2* bs, PhaseGVN *phase, Node*& ctl, MergeMemNode* mem, Node* addr, const TypePtr* adr_type, const Type *type, BasicType bt);
 private:
   void store(BarrierSetC2* bs, PhaseGVN *phase, Node*& ctl, MergeMemNode* mem, Node* addr, const TypePtr* adr_type, Node* val, const Type *type, BasicType bt);
diff a/src/hotspot/share/opto/buildOopMap.cpp b/src/hotspot/share/opto/buildOopMap.cpp
--- a/src/hotspot/share/opto/buildOopMap.cpp
+++ b/src/hotspot/share/opto/buildOopMap.cpp
@@ -256,16 +256,16 @@
         r->print_on(&ss);
         assert(false, "%s", ss.as_string());
         regalloc->C->record_method_not_compilable(ss.as_string());
         continue;
       }
-      if( t->is_ptr()->_offset == 0 ) { // Not derived?
+      if (t->is_ptr()->offset() == 0) { // Not derived?
         if( mcall ) {
           // Outgoing argument GC mask responsibility belongs to the callee,
           // not the caller.  Inspect the inputs to the call, to see if
           // this live-range is one of them.
-          uint cnt = mcall->tf()->domain()->cnt();
+          uint cnt = mcall->tf()->domain_cc()->cnt();
           uint j;
           for( j = TypeFunc::Parms; j < cnt; j++)
             if( mcall->in(j) == def )
               break;            // reaching def is an argument oop
           if( j < cnt )         // arg oops dont go in GC map
@@ -331,11 +331,11 @@
       }
       if( mcall ) {
           // Outgoing argument GC mask responsibility belongs to the callee,
           // not the caller.  Inspect the inputs to the call, to see if
           // this live-range is one of them.
-        uint cnt = mcall->tf()->domain()->cnt();
+        uint cnt = mcall->tf()->domain_cc()->cnt();
         uint j;
         for( j = TypeFunc::Parms; j < cnt; j++)
           if( mcall->in(j) == def )
             break;            // reaching def is an argument oop
         if( j < cnt )         // arg oops dont go in GC map
diff a/src/hotspot/share/opto/bytecodeInfo.cpp b/src/hotspot/share/opto/bytecodeInfo.cpp
--- a/src/hotspot/share/opto/bytecodeInfo.cpp
+++ b/src/hotspot/share/opto/bytecodeInfo.cpp
@@ -84,14 +84,14 @@
 static bool is_init_with_ea(ciMethod* callee_method,
                             ciMethod* caller_method, Compile* C) {
   if (!C->do_escape_analysis() || !EliminateAllocations) {
     return false; // EA is off
   }
-  if (callee_method->is_initializer()) {
+  if (callee_method->is_object_constructor()) {
     return true; // constructor
   }
-  if (caller_method->is_initializer() &&
+  if ((caller_method->is_object_constructor() || caller_method->is_class_initializer()) &&
       caller_method != C->method() &&
       caller_method->holder()->is_subclass_of(callee_method->holder())) {
     return true; // super constructor is called from inlined constructor
   }
   if (C->eliminate_boxing() && callee_method->is_boxing_method()) {
diff a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp
--- a/src/hotspot/share/opto/c2_globals.hpp
+++ b/src/hotspot/share/opto/c2_globals.hpp
@@ -760,10 +760,16 @@
           range(0, max_juint)                                               \
                                                                             \
   product(bool, UseProfiledLoopPredicate, true,                             \
           "Move predicates out of loops based on profiling data")           \
                                                                             \
+  product(bool, UseArrayLoadStoreProfile, true,                             \
+          "Take advantage of profiling at array load/store")                \
+                                                                            \
+  product(bool, UseACmpProfile, true,                                       \
+          "Take advantage of profiling at acmp")                            \
+                                                                            \
   product(bool, ExpandSubTypeCheckAtParseTime, false, DIAGNOSTIC,           \
           "Do not use subtype check macro node")                            \
                                                                             \
   develop(uintx, StressLongCountedLoop, 0,                                  \
           "if > 0, convert int counted loops to long counted loops"         \
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
@@ -122,11 +122,12 @@
 
   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;
+  // TODO 8328675 Re-enable
+  bool eliminate_boxing = false; // EliminateAutoBox;
   bool do_locks_coarsening = EliminateLocks;
   bool do_superword = UseSuperWord;
 
   while (!env->failing()) {
     ResourceMark rm;
@@ -638,28 +639,32 @@
   case vmIntrinsics::_indexOfL_char:
   case vmIntrinsics::_toBytesStringU:
   case vmIntrinsics::_getCharsStringU:
   case vmIntrinsics::_getCharStringU:
   case vmIntrinsics::_putCharStringU:
+  case vmIntrinsics::_makePrivateBuffer:
+  case vmIntrinsics::_finishPrivateBuffer:
   case vmIntrinsics::_getReference:
   case vmIntrinsics::_getBoolean:
   case vmIntrinsics::_getByte:
   case vmIntrinsics::_getShort:
   case vmIntrinsics::_getChar:
   case vmIntrinsics::_getInt:
   case vmIntrinsics::_getLong:
   case vmIntrinsics::_getFloat:
   case vmIntrinsics::_getDouble:
+  case vmIntrinsics::_getValue:
   case vmIntrinsics::_putReference:
   case vmIntrinsics::_putBoolean:
   case vmIntrinsics::_putByte:
   case vmIntrinsics::_putShort:
   case vmIntrinsics::_putChar:
   case vmIntrinsics::_putInt:
   case vmIntrinsics::_putLong:
   case vmIntrinsics::_putFloat:
   case vmIntrinsics::_putDouble:
+  case vmIntrinsics::_putValue:
   case vmIntrinsics::_getReferenceVolatile:
   case vmIntrinsics::_getBooleanVolatile:
   case vmIntrinsics::_getByteVolatile:
   case vmIntrinsics::_getShortVolatile:
   case vmIntrinsics::_getCharVolatile:
@@ -736,11 +741,13 @@
 #endif
   case vmIntrinsics::_currentTimeMillis:
   case vmIntrinsics::_nanoTime:
   case vmIntrinsics::_allocateInstance:
   case vmIntrinsics::_allocateUninitializedArray:
+  case vmIntrinsics::_isFlatArray:
   case vmIntrinsics::_newArray:
+  case vmIntrinsics::_newNullRestrictedArray:
   case vmIntrinsics::_getLength:
   case vmIntrinsics::_copyOf:
   case vmIntrinsics::_copyOfRange:
   case vmIntrinsics::_clone:
   case vmIntrinsics::_isAssignableFrom:
diff a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp
--- a/src/hotspot/share/opto/callGenerator.cpp
+++ b/src/hotspot/share/opto/callGenerator.cpp
@@ -33,10 +33,11 @@
 #include "opto/addnode.hpp"
 #include "opto/callGenerator.hpp"
 #include "opto/callnode.hpp"
 #include "opto/castnode.hpp"
 #include "opto/cfgnode.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/parse.hpp"
 #include "opto/rootnode.hpp"
 #include "opto/runtime.hpp"
 #include "opto/subnode.hpp"
 #include "runtime/os.inline.hpp"
@@ -118,21 +119,30 @@
 // Internal class which handles all out-of-line calls w/o receiver type checks.
 class DirectCallGenerator : public CallGenerator {
  private:
   CallStaticJavaNode* _call_node;
   // Force separate memory and I/O projections for the exceptional
-  // paths to facilitate late inlinig.
+  // paths to facilitate late inlining.
   bool                _separate_io_proj;
 
 protected:
   void set_call_node(CallStaticJavaNode* call) { _call_node = call; }
 
  public:
   DirectCallGenerator(ciMethod* method, bool separate_io_proj)
     : CallGenerator(method),
+      _call_node(nullptr),
       _separate_io_proj(separate_io_proj)
   {
+    if (InlineTypeReturnedAsFields && method->is_method_handle_intrinsic()) {
+      // If that call has not been optimized by the time optimizations are over,
+      // we'll need to add a call to create an inline type instance from the klass
+      // returned by the call (see PhaseMacroExpand::expand_mh_intrinsic_return).
+      // Separating memory and I/O projections for exceptions is required to
+      // perform that graph transformation.
+      _separate_io_proj = true;
+    }
   }
   virtual JVMState* generate(JVMState* jvms);
 
   virtual CallNode* call_node() const { return _call_node; }
   virtual CallGenerator* with_call_node(CallNode* call) {
@@ -143,10 +153,11 @@
 };
 
 JVMState* DirectCallGenerator::generate(JVMState* jvms) {
   GraphKit kit(jvms);
   kit.C->print_inlining_update(this);
+  PhaseGVN& gvn = kit.gvn();
   bool is_static = method()->is_static();
   address target = is_static ? SharedRuntime::get_resolve_static_call_stub()
                              : SharedRuntime::get_resolve_opt_virtual_call_stub();
 
   if (kit.C->log() != nullptr) {
@@ -175,11 +186,14 @@
     if (method()->is_method_handle_intrinsic() ||
         method()->is_compiled_lambda_form()) {
       call->set_method_handle_invoke(true);
     }
   }
-  kit.set_arguments_for_java_call(call);
+  kit.set_arguments_for_java_call(call, is_late_inline());
+  if (kit.stopped()) {
+    return kit.transfer_exceptions_into_jvms();
+  }
   kit.set_edges_for_java_call(call, false, _separate_io_proj);
   Node* ret = kit.set_results_for_java_call(call, _separate_io_proj);
   kit.push_node(method()->return_type()->basic_type(), ret);
   return kit.transfer_exceptions_into_jvms();
 }
@@ -216,11 +230,10 @@
 };
 
 JVMState* VirtualCallGenerator::generate(JVMState* jvms) {
   GraphKit kit(jvms);
   Node* receiver = kit.argument(0);
-
   kit.C->print_inlining_update(this);
 
   if (kit.C->log() != nullptr) {
     kit.C->log()->elem("virtual_call bci='%d'", jvms->bci());
   }
@@ -276,10 +289,13 @@
     call->set_override_symbolic_info(true);
   }
   _call_node = call;  // Save the call node in case we need it later
 
   kit.set_arguments_for_java_call(call);
+  if (kit.stopped()) {
+    return kit.transfer_exceptions_into_jvms();
+  }
   kit.set_edges_for_java_call(call, false /*must_throw*/, _separate_io_proj);
   Node* ret = kit.set_results_for_java_call(call, _separate_io_proj);
   kit.push_node(method()->return_type()->basic_type(), ret);
 
   // Represent the effect of an implicit receiver null_check
@@ -369,10 +385,14 @@
 
   virtual jlong unique_id() const {
     return _unique_id;
   }
 
+  virtual CallGenerator* inline_cg() {
+    return _inline_cg;
+  }
+
   virtual CallGenerator* with_call_node(CallNode* call) {
     LateInlineCallGenerator* cg = new LateInlineCallGenerator(method(), _inline_cg, _is_pure_call);
     cg->set_call_node(call->as_CallStaticJava());
     return cg;
   }
@@ -430,10 +450,18 @@
   bool input_not_const = true;
   CallGenerator* cg = for_method_handle_inline(jvms, _caller, method(), allow_inline, input_not_const);
   assert(!input_not_const, "sanity"); // shouldn't have been scheduled for inlining in the first place
 
   if (cg != nullptr) {
+    // AlwaysIncrementalInline causes for_method_handle_inline() to
+    // return a LateInlineCallGenerator. Extract the
+    // InlineCallGenerator from it.
+    if (AlwaysIncrementalInline && cg->is_late_inline() && !cg->is_virtual_late_inline()) {
+      cg = cg->inline_cg();
+      assert(cg != nullptr, "inline call generator expected");
+    }
+
     assert(!cg->is_late_inline() || cg->is_mh_late_inline() || AlwaysIncrementalInline || StressIncrementalInlining, "we're doing late inlining");
     _inline_cg = cg;
     C->dec_number_of_mh_late_inlines();
     return true;
   } else {
@@ -592,13 +620,13 @@
   if (call == nullptr || call->outcnt() == 0 ||
       call->in(0) == nullptr || call->in(0)->is_top()) {
     return;
   }
 
-  const TypeTuple *r = call->tf()->domain();
-  for (int i1 = 0; i1 < method()->arg_size(); i1++) {
-    if (call->in(TypeFunc::Parms + i1)->is_top() && r->field_at(TypeFunc::Parms + i1) != Type::HALF) {
+  const TypeTuple* r = call->tf()->domain_cc();
+  for (uint i1 = TypeFunc::Parms; i1 < r->cnt(); i1++) {
+    if (call->in(i1)->is_top() && r->field_at(i1) != Type::HALF) {
       assert(Compile::current()->inlining_incrementally(), "shouldn't happen during parsing");
       return;
     }
   }
 
@@ -612,34 +640,43 @@
       return; // dead path
     }
   }
 
   // check for unreachable loop
-  CallProjections callprojs;
-  call->extract_projections(&callprojs, true);
-  if ((callprojs.fallthrough_catchproj == call->in(0)) ||
-      (callprojs.catchall_catchproj    == call->in(0)) ||
-      (callprojs.fallthrough_memproj   == call->in(TypeFunc::Memory)) ||
-      (callprojs.catchall_memproj      == call->in(TypeFunc::Memory)) ||
-      (callprojs.fallthrough_ioproj    == call->in(TypeFunc::I_O)) ||
-      (callprojs.catchall_ioproj       == call->in(TypeFunc::I_O)) ||
-      (callprojs.resproj != nullptr && call->find_edge(callprojs.resproj) != -1) ||
-      (callprojs.exobj   != nullptr && call->find_edge(callprojs.exobj) != -1)) {
+  CallProjections* callprojs = call->extract_projections(true);
+  if ((callprojs->fallthrough_catchproj == call->in(0)) ||
+      (callprojs->catchall_catchproj    == call->in(0)) ||
+      (callprojs->fallthrough_memproj   == call->in(TypeFunc::Memory)) ||
+      (callprojs->catchall_memproj      == call->in(TypeFunc::Memory)) ||
+      (callprojs->fallthrough_ioproj    == call->in(TypeFunc::I_O)) ||
+      (callprojs->catchall_ioproj       == call->in(TypeFunc::I_O)) ||
+      (callprojs->exobj != nullptr && call->find_edge(callprojs->exobj) != -1)) {
     return;
   }
 
   Compile* C = Compile::current();
   // Remove inlined methods from Compiler's lists.
   if (call->is_macro()) {
     C->remove_macro_node(call);
   }
 
-  // The call is marked as pure (no important side effects), but result isn't used.
-  // It's safe to remove the call.
-  bool result_not_used = (callprojs.resproj == nullptr || callprojs.resproj->outcnt() == 0);
+
+  bool result_not_used = true;
+  for (uint i = 0; i < callprojs->nb_resproj; i++) {
+    if (callprojs->resproj[i] != nullptr) {
+      if (callprojs->resproj[i]->outcnt() != 0) {
+        result_not_used = false;
+      }
+      if (call->find_edge(callprojs->resproj[i]) != -1) {
+        return;
+      }
+    }
+  }
 
   if (is_pure_call() && result_not_used) {
+    // The call is marked as pure (no important side effects), but result isn't used.
+    // It's safe to remove the call.
     GraphKit kit(call->jvms());
     kit.replace_call(call, C->top(), true);
   } else {
     // Make a clone of the JVMState that appropriate to use for driving a parse
     JVMState* old_jvms = call->jvms();
@@ -648,30 +685,48 @@
     SafePointNode* map = new SafePointNode(size, jvms);
     for (uint i1 = 0; i1 < size; i1++) {
       map->init_req(i1, call->in(i1));
     }
 
+    PhaseGVN& gvn = *C->initial_gvn();
     // Make sure the state is a MergeMem for parsing.
     if (!map->in(TypeFunc::Memory)->is_MergeMem()) {
       Node* mem = MergeMemNode::make(map->in(TypeFunc::Memory));
-      C->initial_gvn()->set_type_bottom(mem);
+      gvn.set_type_bottom(mem);
       map->set_req(TypeFunc::Memory, mem);
     }
 
-    uint nargs = method()->arg_size();
     // blow away old call arguments
-    Node* top = C->top();
-    for (uint i1 = 0; i1 < nargs; i1++) {
-      map->set_req(TypeFunc::Parms + i1, top);
+    for (uint i1 = TypeFunc::Parms; i1 < r->cnt(); i1++) {
+      map->set_req(i1, C->top());
     }
     jvms->set_map(map);
 
     // Make enough space in the expression stack to transfer
     // the incoming arguments and return value.
     map->ensure_stack(jvms, jvms->method()->max_stack());
+    const TypeTuple* domain_sig = call->_tf->domain_sig();
+    uint nargs = method()->arg_size();
+    assert(domain_sig->cnt() - TypeFunc::Parms == nargs, "inconsistent signature");
+
+    uint j = TypeFunc::Parms;
+    int arg_num = 0;
     for (uint i1 = 0; i1 < nargs; i1++) {
-      map->set_argument(jvms, i1, call->in(TypeFunc::Parms + i1));
+      const Type* t = domain_sig->field_at(TypeFunc::Parms + i1);
+      if (t->is_inlinetypeptr() && !method()->get_Method()->mismatch() && method()->is_scalarized_arg(arg_num)) {
+        // Inline type arguments are not passed by reference: we get an argument per
+        // field of the inline type. Build InlineTypeNodes from the inline type arguments.
+        GraphKit arg_kit(jvms, &gvn);
+        Node* vt = InlineTypeNode::make_from_multi(&arg_kit, call, t->inline_klass(), j, /* in= */ true, /* null_free= */ !t->maybe_null());
+        map->set_control(arg_kit.control());
+        map->set_argument(jvms, i1, vt);
+      } else {
+        map->set_argument(jvms, i1, call->in(j++));
+      }
+      if (t != Type::HALF) {
+        arg_num++;
+      }
     }
 
     C->print_inlining_assert_ready();
 
     C->print_inlining_move_to(this);
@@ -683,10 +738,30 @@
       map->disconnect_inputs(C);
       C->print_inlining_update_delayed(this);
       return;
     }
 
+    // Check if we are late inlining a method handle call that returns an inline type as fields.
+    Node* buffer_oop = nullptr;
+    ciMethod* inline_method = inline_cg()->method();
+    ciType* return_type = inline_method->return_type();
+    if (!call->tf()->returns_inline_type_as_fields() && is_mh_late_inline() &&
+        return_type->is_inlinetype() && return_type->as_inline_klass()->can_be_returned_as_fields()) {
+      // Allocate a buffer for the inline type returned as fields because the caller expects an oop return.
+      // Do this before the method handle call in case the buffer allocation triggers deoptimization and
+      // we need to "re-execute" the call in the interpreter (to make sure the call is only executed once).
+      GraphKit arg_kit(jvms, &gvn);
+      {
+        PreserveReexecuteState preexecs(&arg_kit);
+        arg_kit.jvms()->set_should_reexecute(true);
+        arg_kit.inc_sp(nargs);
+        Node* klass_node = arg_kit.makecon(TypeKlassPtr::make(return_type->as_inline_klass()));
+        buffer_oop = arg_kit.new_instance(klass_node, nullptr, nullptr, /* deoptimize_on_exception */ true);
+      }
+      jvms = arg_kit.transfer_exceptions_into_jvms();
+    }
+
     // Setup default node notes to be picked up by the inlining
     Node_Notes* old_nn = C->node_notes_at(call->_idx);
     if (old_nn != nullptr) {
       Node_Notes* entry_nn = old_nn->clone(C);
       entry_nn->set_jvms(jvms);
@@ -711,15 +786,67 @@
     if (call->is_CallStaticJava() && call->as_CallStaticJava()->is_boxing_method()) {
       result = kit.must_be_not_null(result, false);
     }
 
     if (inline_cg()->is_inline()) {
-      C->set_has_loops(C->has_loops() || inline_cg()->method()->has_loops());
-      C->env()->notice_inlined_method(inline_cg()->method());
+      C->set_has_loops(C->has_loops() || inline_method->has_loops());
+      C->env()->notice_inlined_method(inline_method);
     }
     C->set_inlining_progress(true);
     C->set_do_cleanup(kit.stopped()); // path is dead; needs cleanup
+
+    // Handle inline type returns
+    InlineTypeNode* vt = result->isa_InlineType();
+    if (vt != nullptr) {
+      if (call->tf()->returns_inline_type_as_fields()) {
+        vt->replace_call_results(&kit, call, C);
+      } else if (vt->is_InlineType()) {
+        // Result might still be allocated (for example, if it has been stored to a non-flat field)
+        if (!vt->is_allocated(&kit.gvn())) {
+          assert(buffer_oop != nullptr, "should have allocated a buffer");
+          RegionNode* region = new RegionNode(3);
+
+          // Check if result is null
+          Node* null_ctl = kit.top();
+          kit.null_check_common(vt->get_is_init(), T_INT, false, &null_ctl);
+          region->init_req(1, null_ctl);
+          PhiNode* oop = PhiNode::make(region, kit.gvn().zerocon(T_OBJECT), TypeInstPtr::make(TypePtr::BotPTR, vt->type()->inline_klass()));
+          Node* init_mem = kit.reset_memory();
+          PhiNode* mem = PhiNode::make(region, init_mem, Type::MEMORY, TypePtr::BOTTOM);
+
+          // Not null, initialize the buffer
+          kit.set_all_memory(init_mem);
+          vt->store(&kit, buffer_oop, buffer_oop, vt->type()->inline_klass());
+          // Do not let stores that initialize this buffer be reordered with a subsequent
+          // store that would make this buffer accessible by other threads.
+          AllocateNode* alloc = AllocateNode::Ideal_allocation(buffer_oop);
+          assert(alloc != nullptr, "must have an allocation node");
+          kit.insert_mem_bar(Op_MemBarStoreStore, alloc->proj_out_or_null(AllocateNode::RawAddress));
+          region->init_req(2, kit.control());
+          oop->init_req(2, buffer_oop);
+          mem->init_req(2, kit.merged_memory());
+
+          // Update oop input to buffer
+          kit.gvn().hash_delete(vt);
+          vt->set_oop(kit.gvn(), kit.gvn().transform(oop));
+          vt->set_is_buffered(kit.gvn());
+          vt = kit.gvn().transform(vt)->as_InlineType();
+
+          kit.set_control(kit.gvn().transform(region));
+          kit.set_all_memory(kit.gvn().transform(mem));
+          kit.record_for_igvn(region);
+          kit.record_for_igvn(oop);
+          kit.record_for_igvn(mem);
+        }
+        result = vt;
+      }
+      DEBUG_ONLY(buffer_oop = nullptr);
+    } else {
+      assert(result->is_top() || !call->tf()->returns_inline_type_as_fields(), "Unexpected return value");
+    }
+    assert(buffer_oop == nullptr, "unused buffer allocation");
+
     kit.replace_call(call, result, true);
   }
 }
 
 class LateInlineStringCallGenerator : public LateInlineCallGenerator {
@@ -938,10 +1065,33 @@
     // Inlined method threw an exception, so it's just the slow path after all.
     kit.set_jvms(slow_jvms);
     return kit.transfer_exceptions_into_jvms();
   }
 
+  // Allocate inline types if they are merged with objects (similar to Parse::merge_common())
+  uint tos = kit.jvms()->stkoff() + kit.sp();
+  uint limit = slow_map->req();
+  for (uint i = TypeFunc::Parms; i < limit; i++) {
+    Node* m = kit.map()->in(i);
+    Node* n = slow_map->in(i);
+    const Type* t = gvn.type(m)->meet_speculative(gvn.type(n));
+    // TODO 8284443 still needed?
+    if (m->is_InlineType() && !t->is_inlinetypeptr()) {
+      // Allocate inline type in fast path
+      m = m->as_InlineType()->buffer(&kit);
+      kit.map()->set_req(i, m);
+    }
+    if (n->is_InlineType() && !t->is_inlinetypeptr()) {
+      // Allocate inline type in slow path
+      PreserveJVMState pjvms(&kit);
+      kit.set_map(slow_map);
+      n = n->as_InlineType()->buffer(&kit);
+      kit.map()->set_req(i, n);
+      slow_map = kit.stop();
+    }
+  }
+
   // There are 2 branches and the replaced nodes are only valid on
   // one: restore the replaced nodes to what they were before the
   // branch.
   kit.map()->set_replaced_nodes(replaced_nodes);
 
@@ -961,12 +1111,10 @@
     Node* phi = mms.memory();
     if (phi->is_Phi() && phi->in(0) == region) {
       mms.set_memory(gvn.transform(phi));
     }
   }
-  uint tos = kit.jvms()->stkoff() + kit.sp();
-  uint limit = slow_map->req();
   for (uint i = TypeFunc::Parms; i < limit; i++) {
     // Skip unused stack slots; fast forward to monoff();
     if (i == tos) {
       i = kit.jvms()->monoff();
       if( i >= limit ) break;
@@ -999,19 +1147,35 @@
   }
   int bci = jvms->bci();
   ciCallProfile profile = caller->call_profile_at_bci(bci);
   int call_site_count = caller->scale_count(profile.count());
 
-  if (IncrementalInlineMH && call_site_count > 0 &&
-      (should_delay || input_not_const || !C->inlining_incrementally() || C->over_inlining_cutoff())) {
+  if (IncrementalInlineMH && (AlwaysIncrementalInline ||
+                            (call_site_count > 0 && (should_delay || input_not_const || !C->inlining_incrementally() || C->over_inlining_cutoff())))) {
     return CallGenerator::for_mh_late_inline(caller, callee, input_not_const);
   } else {
     // Out-of-line call.
     return CallGenerator::for_direct_call(callee);
   }
 }
 
+static void cast_argument(int nargs, int arg_nb, ciType* t, GraphKit& kit) {
+  PhaseGVN& gvn = kit.gvn();
+  Node* arg = kit.argument(arg_nb);
+  const Type* arg_type = arg->bottom_type();
+  const Type* sig_type = TypeOopPtr::make_from_klass(t->as_klass());
+  if (arg_type->isa_oopptr() && !arg_type->higher_equal(sig_type)) {
+    const Type* narrowed_arg_type = arg_type->filter_speculative(sig_type); // keep speculative part
+    arg = gvn.transform(new CheckCastPPNode(kit.control(), arg, narrowed_arg_type));
+    kit.set_argument(arg_nb, arg);
+  }
+  if (sig_type->is_inlinetypeptr()) {
+    arg = InlineTypeNode::make_from_oop(&kit, arg, sig_type->inline_klass(), !kit.gvn().type(arg)->maybe_null());
+    kit.set_argument(arg_nb, arg);
+  }
+}
+
 CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod* caller, ciMethod* callee, bool allow_inline, bool& input_not_const) {
   GraphKit kit(jvms);
   PhaseGVN& gvn = kit.gvn();
   Compile* C = kit.C;
   vmIntrinsics::ID iid = callee->intrinsic_id();
@@ -1059,12 +1223,13 @@
   case vmIntrinsics::_linkToVirtual:
   case vmIntrinsics::_linkToStatic:
   case vmIntrinsics::_linkToSpecial:
   case vmIntrinsics::_linkToInterface:
     {
+      int nargs = callee->arg_size();
       // Get MemberName argument:
-      Node* member_name = kit.argument(callee->arg_size() - 1);
+      Node* member_name = kit.argument(nargs - 1);
       if (member_name->Opcode() == Op_ConP) {
         input_not_const = false;
         const TypeOopPtr* oop_ptr = member_name->bottom_type()->is_oopptr();
         ciMethod* target = oop_ptr->const_oop()->as_member_name()->get_vmtarget();
 
@@ -1080,31 +1245,17 @@
         // actual types.
         ciSignature* signature = target->signature();
         const int receiver_skip = target->is_static() ? 0 : 1;
         // Cast receiver to its type.
         if (!target->is_static()) {
-          Node* arg = kit.argument(0);
-          const TypeOopPtr* arg_type = arg->bottom_type()->isa_oopptr();
-          const Type*       sig_type = TypeOopPtr::make_from_klass(signature->accessing_klass());
-          if (arg_type != nullptr && !arg_type->higher_equal(sig_type)) {
-            const Type* recv_type = arg_type->filter_speculative(sig_type); // keep speculative part
-            Node* cast_obj = gvn.transform(new CheckCastPPNode(kit.control(), arg, recv_type));
-            kit.set_argument(0, cast_obj);
-          }
+          cast_argument(nargs, 0, signature->accessing_klass(), kit);
         }
         // Cast reference arguments to its type.
         for (int i = 0, j = 0; i < signature->count(); i++) {
           ciType* t = signature->type_at(i);
           if (t->is_klass()) {
-            Node* arg = kit.argument(receiver_skip + j);
-            const TypeOopPtr* arg_type = arg->bottom_type()->isa_oopptr();
-            const Type*       sig_type = TypeOopPtr::make_from_klass(t->as_klass());
-            if (arg_type != nullptr && !arg_type->higher_equal(sig_type)) {
-              const Type* narrowed_arg_type = arg_type->filter_speculative(sig_type); // keep speculative part
-              Node* cast_obj = gvn.transform(new CheckCastPPNode(kit.control(), arg, narrowed_arg_type));
-              kit.set_argument(receiver_skip + j, cast_obj);
-            }
+            cast_argument(nargs, receiver_skip + j, t, kit);
           }
           j += t->size();  // long and double take two slots
         }
 
         // Try to get the most accurate receiver type
@@ -1131,11 +1282,12 @@
           speculative_receiver_type = (receiver_type != nullptr) ? receiver_type->speculative_type() : nullptr;
         }
         CallGenerator* cg = C->call_generator(target, vtable_index, call_does_dispatch, jvms,
                                               allow_inline,
                                               PROB_ALWAYS,
-                                              speculative_receiver_type);
+                                              speculative_receiver_type,
+                                              true);
         return cg;
       } else {
         print_inlining_failure(C, callee, jvms->depth() - 1, jvms->bci(),
                                "member_name not constant");
       }
@@ -1206,11 +1358,11 @@
   }
 
   if (!method()->is_static()) {
     // We need an explicit receiver null_check before checking its type in predicate.
     // We share a map with the caller, so his JVMS gets adjusted.
-    Node* receiver = kit.null_check_receiver_before_call(method());
+    kit.null_check_receiver_before_call(method());
     if (kit.stopped()) {
       return kit.transfer_exceptions_into_jvms();
     }
   }
 
diff a/src/hotspot/share/opto/callGenerator.hpp b/src/hotspot/share/opto/callGenerator.hpp
--- a/src/hotspot/share/opto/callGenerator.hpp
+++ b/src/hotspot/share/opto/callGenerator.hpp
@@ -42,13 +42,12 @@
  protected:
   CallGenerator(ciMethod* method) : _method(method) {}
 
   void do_late_inline_helper();
 
-  virtual bool           do_late_inline_check(Compile* C, JVMState* jvms) { ShouldNotReachHere(); return false;  }
-  virtual CallGenerator* inline_cg()    const                             { ShouldNotReachHere(); return nullptr;}
-  virtual bool           is_pure_call() const                             { ShouldNotReachHere(); return false;  }
+  virtual bool           do_late_inline_check(Compile* C, JVMState* jvms) { ShouldNotReachHere(); return false; }
+  virtual bool           is_pure_call() const                             { ShouldNotReachHere(); return false; }
 
  public:
   // Accessors
   ciMethod*          method() const             { return _method; }
 
@@ -84,10 +83,12 @@
   virtual CallGenerator* with_call_node(CallNode* call)  { return this; }
 
   virtual void set_unique_id(jlong id)          { fatal("unique id only for late inlines"); };
   virtual jlong unique_id() const               { fatal("unique id only for late inlines"); return 0; };
 
+  virtual CallGenerator* inline_cg()    const                             { ShouldNotReachHere(); return nullptr;  }
+
   virtual void set_callee_method(ciMethod* callee) { ShouldNotReachHere(); }
 
   // Note:  It is possible for a CG to be both inline and virtual.
   // (The hashCode intrinsic does a vtable check and an inlined fast path.)
 
diff a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp
--- a/src/hotspot/share/opto/callnode.cpp
+++ b/src/hotspot/share/opto/callnode.cpp
@@ -22,29 +22,32 @@
  *
  */
 
 #include "precompiled.hpp"
 #include "compiler/compileLog.hpp"
+#include "ci/ciFlatArrayKlass.hpp"
 #include "ci/bcEscapeAnalyzer.hpp"
 #include "compiler/oopMap.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/c2/barrierSetC2.hpp"
 #include "interpreter/interpreter.hpp"
 #include "opto/callGenerator.hpp"
 #include "opto/callnode.hpp"
 #include "opto/castnode.hpp"
 #include "opto/convertnode.hpp"
 #include "opto/escape.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/locknode.hpp"
 #include "opto/machnode.hpp"
 #include "opto/matcher.hpp"
 #include "opto/parse.hpp"
 #include "opto/regalloc.hpp"
 #include "opto/regmask.hpp"
 #include "opto/rootnode.hpp"
 #include "opto/runtime.hpp"
 #include "runtime/sharedRuntime.hpp"
+#include "runtime/stubRoutines.hpp"
 #include "utilities/powerOfTwo.hpp"
 #include "code/vmreg.hpp"
 
 // Portions of code courtesy of Clifford Click
 
@@ -76,11 +79,11 @@
   return RegMask::Empty;
 }
 
 //------------------------------match------------------------------------------
 // Construct projections for incoming parameters, and their RegMask info
-Node *StartNode::match( const ProjNode *proj, const Matcher *match ) {
+Node *StartNode::match(const ProjNode *proj, const Matcher *match, const RegMask* mask) {
   switch (proj->_con) {
   case TypeFunc::Control:
   case TypeFunc::I_O:
   case TypeFunc::Memory:
     return new MachProjNode(this,proj->_con,RegMask::Empty,MachProjNode::unmatched_proj);
@@ -100,21 +103,10 @@
     }
   }
   return nullptr;
 }
 
-//------------------------------StartOSRNode----------------------------------
-// The method start node for an on stack replacement adapter
-
-//------------------------------osr_domain-----------------------------
-const TypeTuple *StartOSRNode::osr_domain() {
-  const Type **fields = TypeTuple::fields(2);
-  fields[TypeFunc::Parms+0] = TypeRawPtr::BOTTOM;  // address of osr buffer
-
-  return TypeTuple::make(TypeFunc::Parms+1, fields);
-}
-
 //=============================================================================
 const char * const ParmNode::names[TypeFunc::Parms+1] = {
   "Control", "I_O", "Memory", "FramePtr", "ReturnAdr", "Parms"
 };
 
@@ -496,15 +488,30 @@
         st->print("[%d]", spobj->n_fields());
         int ndim = cik->as_array_klass()->dimension() - 1;
         while (ndim-- > 0) {
           st->print("[]");
         }
+      } else if (cik->is_flat_array_klass()) {
+        ciKlass* cie = cik->as_flat_array_klass()->base_element_klass();
+        cie->print_name_on(st);
+        st->print("[%d]", spobj->n_fields());
+        int ndim = cik->as_array_klass()->dimension() - 1;
+        while (ndim-- > 0) {
+          st->print("[]");
+        }
       }
       st->print("={");
       uint nf = spobj->n_fields();
       if (nf > 0) {
         uint first_ind = spobj->first_index(mcall->jvms());
+        if (iklass != nullptr && iklass->is_inlinetype()) {
+          Node* init_node = mcall->in(first_ind++);
+          if (!init_node->is_top()) {
+            st->print(" [is_init");
+            format_helper(regalloc, st, init_node, ":", -1, nullptr);
+          }
+        }
         Node* fld_node = mcall->in(first_ind);
         ciField* cifield;
         if (iklass != nullptr) {
           st->print(" [");
           cifield = iklass->nonstatic_field_at(0);
@@ -714,63 +721,82 @@
   if (_cnt != COUNT_UNKNOWN)  st->print(" C=%f",_cnt);
   if (jvms() != nullptr)  jvms()->dump_spec(st);
 }
 #endif
 
-const Type *CallNode::bottom_type() const { return tf()->range(); }
+const Type *CallNode::bottom_type() const { return tf()->range_cc(); }
 const Type* CallNode::Value(PhaseGVN* phase) const {
-  if (phase->type(in(0)) == Type::TOP)  return Type::TOP;
-  return tf()->range();
+  if (!in(0) || phase->type(in(0)) == Type::TOP) {
+    return Type::TOP;
+  }
+  return tf()->range_cc();
 }
 
 //------------------------------calling_convention-----------------------------
 void CallNode::calling_convention(BasicType* sig_bt, VMRegPair *parm_regs, uint argcnt) const {
+  if (_entry_point == StubRoutines::store_inline_type_fields_to_buf()) {
+    // The call to that stub is a special case: its inputs are
+    // multiple values returned from a call and so it should follow
+    // the return convention.
+    SharedRuntime::java_return_convention(sig_bt, parm_regs, argcnt);
+    return;
+  }
   // Use the standard compiler calling convention
   SharedRuntime::java_calling_convention(sig_bt, parm_regs, argcnt);
 }
 
 
 //------------------------------match------------------------------------------
 // Construct projections for control, I/O, memory-fields, ..., and
 // return result(s) along with their RegMask info
-Node *CallNode::match( const ProjNode *proj, const Matcher *match ) {
-  switch (proj->_con) {
-  case TypeFunc::Control:
-  case TypeFunc::I_O:
-  case TypeFunc::Memory:
-    return new MachProjNode(this,proj->_con,RegMask::Empty,MachProjNode::unmatched_proj);
-
-  case TypeFunc::Parms+1:       // For LONG & DOUBLE returns
-    assert(tf()->range()->field_at(TypeFunc::Parms+1) == Type::HALF, "");
-    // 2nd half of doubles and longs
-    return new MachProjNode(this,proj->_con, RegMask::Empty, (uint)OptoReg::Bad);
-
-  case TypeFunc::Parms: {       // Normal returns
-    uint ideal_reg = tf()->range()->field_at(TypeFunc::Parms)->ideal_reg();
-    OptoRegPair regs = Opcode() == Op_CallLeafVector
-      ? match->vector_return_value(ideal_reg)      // Calls into assembly vector routine
-      : is_CallRuntime()
-        ? match->c_return_value(ideal_reg)  // Calls into C runtime
-        : match->  return_value(ideal_reg); // Calls into compiled Java code
-    RegMask rm = RegMask(regs.first());
-
-    if (Opcode() == Op_CallLeafVector) {
-      // If the return is in vector, compute appropriate regmask taking into account the whole range
-      if(ideal_reg >= Op_VecS && ideal_reg <= Op_VecZ) {
-        if(OptoReg::is_valid(regs.second())) {
-          for (OptoReg::Name r = regs.first(); r <= regs.second(); r = OptoReg::add(r, 1)) {
-            rm.Insert(r);
+Node *CallNode::match(const ProjNode *proj, const Matcher *match, const RegMask* mask) {
+  uint con = proj->_con;
+  const TypeTuple* range_cc = tf()->range_cc();
+  if (con >= TypeFunc::Parms) {
+    if (tf()->returns_inline_type_as_fields()) {
+      // The call returns multiple values (inline type fields): we
+      // create one projection per returned value.
+      assert(con <= TypeFunc::Parms+1 || InlineTypeReturnedAsFields, "only for multi value return");
+      uint ideal_reg = range_cc->field_at(con)->ideal_reg();
+      return new MachProjNode(this, con, mask[con-TypeFunc::Parms], ideal_reg);
+    } else {
+      if (con == TypeFunc::Parms) {
+        uint ideal_reg = range_cc->field_at(TypeFunc::Parms)->ideal_reg();
+        OptoRegPair regs = Opcode() == Op_CallLeafVector
+          ? match->vector_return_value(ideal_reg)      // Calls into assembly vector routine
+          : match->c_return_value(ideal_reg);
+        RegMask rm = RegMask(regs.first());
+
+        if (Opcode() == Op_CallLeafVector) {
+          // If the return is in vector, compute appropriate regmask taking into account the whole range
+          if(ideal_reg >= Op_VecS && ideal_reg <= Op_VecZ) {
+            if(OptoReg::is_valid(regs.second())) {
+              for (OptoReg::Name r = regs.first(); r <= regs.second(); r = OptoReg::add(r, 1)) {
+                rm.Insert(r);
+              }
+            }
           }
         }
+
+        if (OptoReg::is_valid(regs.second())) {
+          rm.Insert(regs.second());
+        }
+        return new MachProjNode(this,con,rm,ideal_reg);
+      } else {
+        assert(con == TypeFunc::Parms+1, "only one return value");
+        assert(range_cc->field_at(TypeFunc::Parms+1) == Type::HALF, "");
+        return new MachProjNode(this,con, RegMask::Empty, (uint)OptoReg::Bad);
       }
     }
-
-    if( OptoReg::is_valid(regs.second()) )
-      rm.Insert( regs.second() );
-    return new MachProjNode(this,proj->_con,rm,ideal_reg);
   }
 
+  switch (con) {
+  case TypeFunc::Control:
+  case TypeFunc::I_O:
+  case TypeFunc::Memory:
+    return new MachProjNode(this,proj->_con,RegMask::Empty,MachProjNode::unmatched_proj);
+
   case TypeFunc::ReturnAdr:
   case TypeFunc::FramePtr:
   default:
     ShouldNotReachHere();
   }
@@ -787,11 +813,11 @@
 // instance at the specified offset.
 //
 bool CallNode::may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) {
   assert((t_oop != nullptr), "sanity");
   if (is_call_to_arraycopystub() && strcmp(_name, "unsafe_arraycopy") != 0) {
-    const TypeTuple* args = _tf->domain();
+    const TypeTuple* args = _tf->domain_sig();
     Node* dest = nullptr;
     // Stubs that can be called once an ArrayCopyNode is expanded have
     // different signatures. Look for the second pointer argument,
     // that is the destination of the copy.
     for (uint i = TypeFunc::Parms, j = 0; i < args->cnt(); i++) {
@@ -836,11 +862,11 @@
         if ((inst_t != nullptr) && (!inst_t->klass_is_exact() ||
                                    (inst_t->instance_klass() == boxing_klass))) {
           return true;
         }
       }
-      const TypeTuple* d = tf()->domain();
+      const TypeTuple* d = tf()->domain_cc();
       for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
         const TypeInstPtr* inst_t = d->field_at(i)->isa_instptr();
         if ((inst_t != nullptr) && (!inst_t->klass_is_exact() ||
                                  (inst_t->instance_klass() == boxing_klass))) {
           return true;
@@ -851,21 +877,31 @@
   }
   return true;
 }
 
 // Does this call have a direct reference to n other than debug information?
-bool CallNode::has_non_debug_use(Node *n) {
-  const TypeTuple * d = tf()->domain();
+bool CallNode::has_non_debug_use(Node* n) {
+  const TypeTuple* d = tf()->domain_cc();
   for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
-    Node *arg = in(i);
-    if (arg == n) {
+    if (in(i) == n) {
       return true;
     }
   }
   return false;
 }
 
+bool CallNode::has_debug_use(Node* n) {
+  if (jvms() != nullptr) {
+    for (uint i = jvms()->debug_start(); i < jvms()->debug_end(); i++) {
+      if (in(i) == n) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 // Returns the unique CheckCastPP of a call
 // or 'this' if there are several CheckCastPP or unexpected uses
 // or returns null if there is no one.
 Node *CallNode::result_cast() {
   Node *cast = nullptr;
@@ -893,20 +929,25 @@
   }
   return cast;
 }
 
 
-void CallNode::extract_projections(CallProjections* projs, bool separate_io_proj, bool do_asserts) {
-  projs->fallthrough_proj      = nullptr;
-  projs->fallthrough_catchproj = nullptr;
-  projs->fallthrough_ioproj    = nullptr;
-  projs->catchall_ioproj       = nullptr;
-  projs->catchall_catchproj    = nullptr;
-  projs->fallthrough_memproj   = nullptr;
-  projs->catchall_memproj      = nullptr;
-  projs->resproj               = nullptr;
-  projs->exobj                 = nullptr;
+CallProjections* CallNode::extract_projections(bool separate_io_proj, bool do_asserts) {
+  uint max_res = TypeFunc::Parms-1;
+  for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) {
+    ProjNode *pn = fast_out(i)->as_Proj();
+    max_res = MAX2(max_res, pn->_con);
+  }
+
+  assert(max_res < _tf->range_cc()->cnt(), "result out of bounds");
+
+  uint projs_size = sizeof(CallProjections);
+  if (max_res > TypeFunc::Parms) {
+    projs_size += (max_res-TypeFunc::Parms)*sizeof(Node*);
+  }
+  char* projs_storage = resource_allocate_bytes(projs_size);
+  CallProjections* projs = new(projs_storage)CallProjections(max_res - TypeFunc::Parms + 1);
 
   for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) {
     ProjNode *pn = fast_out(i)->as_Proj();
     if (pn->outcnt() == 0) continue;
     switch (pn->_con) {
@@ -948,30 +989,33 @@
         projs->catchall_memproj = pn;
       else
         projs->fallthrough_memproj = pn;
       break;
     case TypeFunc::Parms:
-      projs->resproj = pn;
+      projs->resproj[0] = pn;
       break;
     default:
-      assert(false, "unexpected projection from allocation node.");
+      assert(pn->_con <= max_res, "unexpected projection from allocation node.");
+      projs->resproj[pn->_con-TypeFunc::Parms] = pn;
+      break;
     }
   }
 
   // The resproj may not exist because the result could be ignored
   // and the exception object may not exist if an exception handler
   // swallows the exception but all the other must exist and be found.
-  assert(projs->fallthrough_proj      != nullptr, "must be found");
   do_asserts = do_asserts && !Compile::current()->inlining_incrementally();
+  assert(!do_asserts || projs->fallthrough_proj      != nullptr, "must be found");
   assert(!do_asserts || projs->fallthrough_catchproj != nullptr, "must be found");
   assert(!do_asserts || projs->fallthrough_memproj   != nullptr, "must be found");
   assert(!do_asserts || projs->fallthrough_ioproj    != nullptr, "must be found");
   assert(!do_asserts || projs->catchall_catchproj    != nullptr, "must be found");
   if (separate_io_proj) {
     assert(!do_asserts || projs->catchall_memproj    != nullptr, "must be found");
     assert(!do_asserts || projs->catchall_ioproj     != nullptr, "must be found");
   }
+  return projs;
 }
 
 Node* CallNode::Ideal(PhaseGVN* phase, bool can_reshape) {
 #ifdef ASSERT
   // Validate attached generator
@@ -999,12 +1043,12 @@
          _override_symbolic_info == call._override_symbolic_info;
 }
 
 void CallJavaNode::copy_call_debug_info(PhaseIterGVN* phase, SafePointNode* sfpt) {
   // Copy debug information and adjust JVMState information
-  uint old_dbg_start = sfpt->is_Call() ? sfpt->as_Call()->tf()->domain()->cnt() : (uint)TypeFunc::Parms+1;
-  uint new_dbg_start = tf()->domain()->cnt();
+  uint old_dbg_start = sfpt->is_Call() ? sfpt->as_Call()->tf()->domain_sig()->cnt() : (uint)TypeFunc::Parms+1;
+  uint new_dbg_start = tf()->domain_sig()->cnt();
   int jvms_adj  = new_dbg_start - old_dbg_start;
   assert (new_dbg_start == req(), "argument count mismatch");
   Compile* C = phase->C;
 
   // SafePointScalarObject node could be referenced several times in debug info.
@@ -1041,10 +1085,14 @@
 #ifdef ASSERT
 bool CallJavaNode::validate_symbolic_info() const {
   if (method() == nullptr) {
     return true; // call into runtime or uncommon trap
   }
+  Bytecodes::Code bc = jvms()->method()->java_code_at_bci(jvms()->bci());
+  if (EnableValhalla && (bc == Bytecodes::_if_acmpeq || bc == Bytecodes::_if_acmpne)) {
+    return true;
+  }
   ciMethod* symbolic_info = jvms()->method()->get_method_at_bci(jvms()->bci());
   ciMethod* callee = method();
   if (symbolic_info->is_method_handle_intrinsic() && !callee->is_method_handle_intrinsic()) {
     assert(override_symbolic_info(), "should be set");
   }
@@ -1074,10 +1122,20 @@
   CallStaticJavaNode &call = (CallStaticJavaNode&)n;
   return CallJavaNode::cmp(call);
 }
 
 Node* CallStaticJavaNode::Ideal(PhaseGVN* phase, bool can_reshape) {
+  if (can_reshape && uncommon_trap_request() != 0) {
+    if (remove_useless_allocation(phase, in(0), in(TypeFunc::Memory), in(TypeFunc::Parms))) {
+      if (!in(0)->is_Region()) {
+        PhaseIterGVN* igvn = phase->is_IterGVN();
+        igvn->replace_input_of(this, 0, phase->C->top());
+      }
+      return this;
+    }
+  }
+
   CallGenerator* cg = generator();
   if (can_reshape && cg != nullptr) {
     assert(IncrementalInlineMH, "required");
     assert(cg->call_node() == this, "mismatch");
     assert(cg->is_mh_late_inline(), "not virtual");
@@ -1126,10 +1184,134 @@
   }
 #endif
   return call->in(TypeFunc::Parms)->bottom_type()->is_int()->get_con();
 }
 
+bool CallStaticJavaNode::remove_useless_allocation(PhaseGVN *phase, Node* ctl, Node* mem, Node* unc_arg) {
+  // Split if can cause the flat array branch of an array load to
+  // end in an uncommon trap. In that case, the allocation of the
+  // loaded value and its initialization is useless. Eliminate it. use
+  // the jvm state of the allocation to create a new uncommon trap
+  // call at the load.
+  if (ctl == nullptr || ctl->is_top() || mem == nullptr || mem->is_top() || !mem->is_MergeMem()) {
+    return false;
+  }
+  PhaseIterGVN* igvn = phase->is_IterGVN();
+  if (ctl->is_Region()) {
+    bool res = false;
+    for (uint i = 1; i < ctl->req(); i++) {
+      MergeMemNode* mm = mem->clone()->as_MergeMem();
+      for (MergeMemStream mms(mm); mms.next_non_empty(); ) {
+        Node* m = mms.memory();
+        if (m->is_Phi() && m->in(0) == ctl) {
+          mms.set_memory(m->in(i));
+        }
+      }
+      if (remove_useless_allocation(phase, ctl->in(i), mm, unc_arg)) {
+        res = true;
+        if (!ctl->in(i)->is_Region()) {
+          igvn->replace_input_of(ctl, i, phase->C->top());
+        }
+      }
+      igvn->remove_dead_node(mm);
+    }
+    return res;
+  }
+  // verify the control flow is ok
+  Node* call = ctl;
+  MemBarNode* membar = nullptr;
+  for (;;) {
+    if (call == nullptr || call->is_top()) {
+      return false;
+    }
+    if (call->is_Proj() || call->is_Catch() || call->is_MemBar()) {
+      call = call->in(0);
+    } else if (call->Opcode() == Op_CallStaticJava &&
+               call->as_Call()->entry_point() == OptoRuntime::load_unknown_inline_Java()) {
+      assert(call->in(0)->is_Proj() && call->in(0)->in(0)->is_MemBar(), "missing membar");
+      membar = call->in(0)->in(0)->as_MemBar();
+      break;
+    } else {
+      return false;
+    }
+  }
+
+  JVMState* jvms = call->jvms();
+  if (phase->C->too_many_traps(jvms->method(), jvms->bci(), Deoptimization::trap_request_reason(uncommon_trap_request()))) {
+    return false;
+  }
+
+  Node* alloc_mem = call->in(TypeFunc::Memory);
+  if (alloc_mem == nullptr || alloc_mem->is_top()) {
+    return false;
+  }
+  if (!alloc_mem->is_MergeMem()) {
+    alloc_mem = MergeMemNode::make(alloc_mem);
+    igvn->register_new_node_with_optimizer(alloc_mem);
+  }
+
+  // and that there's no unexpected side effect
+  for (MergeMemStream mms2(mem->as_MergeMem(), alloc_mem->as_MergeMem()); mms2.next_non_empty2(); ) {
+    Node* m1 = mms2.is_empty() ? mms2.base_memory() : mms2.memory();
+    Node* m2 = mms2.memory2();
+
+    for (uint i = 0; i < 100; i++) {
+      if (m1 == m2) {
+        break;
+      } else if (m1->is_Proj()) {
+        m1 = m1->in(0);
+      } else if (m1->is_MemBar()) {
+        m1 = m1->in(TypeFunc::Memory);
+      } else if (m1->Opcode() == Op_CallStaticJava &&
+                 m1->as_Call()->entry_point() == OptoRuntime::load_unknown_inline_Java()) {
+        if (m1 != call) {
+          return false;
+        }
+        break;
+      } else if (m1->is_MergeMem()) {
+        MergeMemNode* mm = m1->as_MergeMem();
+        int idx = mms2.alias_idx();
+        if (idx == Compile::AliasIdxBot) {
+          m1 = mm->base_memory();
+        } else {
+          m1 = mm->memory_at(idx);
+        }
+      } else {
+        return false;
+      }
+    }
+  }
+  if (alloc_mem->outcnt() == 0) {
+    igvn->remove_dead_node(alloc_mem);
+  }
+
+  // Remove membar preceding the call
+  membar->remove(igvn);
+
+  address call_addr = SharedRuntime::uncommon_trap_blob()->entry_point();
+  CallNode* unc = new CallStaticJavaNode(OptoRuntime::uncommon_trap_Type(), call_addr, "uncommon_trap", nullptr);
+  unc->init_req(TypeFunc::Control, call->in(0));
+  unc->init_req(TypeFunc::I_O, call->in(TypeFunc::I_O));
+  unc->init_req(TypeFunc::Memory, call->in(TypeFunc::Memory));
+  unc->init_req(TypeFunc::FramePtr,  call->in(TypeFunc::FramePtr));
+  unc->init_req(TypeFunc::ReturnAdr, call->in(TypeFunc::ReturnAdr));
+  unc->init_req(TypeFunc::Parms+0, unc_arg);
+  unc->set_cnt(PROB_UNLIKELY_MAG(4));
+  unc->copy_call_debug_info(igvn, call->as_CallStaticJava());
+
+  igvn->replace_input_of(call, 0, phase->C->top());
+
+  igvn->register_new_node_with_optimizer(unc);
+
+  Node* ctrl = phase->transform(new ProjNode(unc, TypeFunc::Control));
+  Node* halt = phase->transform(new HaltNode(ctrl, call->in(TypeFunc::FramePtr), "uncommon trap returned which should never happen"));
+  igvn->add_input_to(phase->C->root(), halt);
+
+  return true;
+}
+
+
 #ifndef PRODUCT
 void CallStaticJavaNode::dump_spec(outputStream *st) const {
   st->print("# Static ");
   if (_name != nullptr) {
     st->print("%s", _name);
@@ -1230,18 +1412,25 @@
   return CallLeafNode::cmp(call) && _num_bits == call._num_bits;
 }
 
 //------------------------------calling_convention-----------------------------
 void CallRuntimeNode::calling_convention(BasicType* sig_bt, VMRegPair *parm_regs, uint argcnt) const {
+  if (_entry_point == nullptr) {
+    // The call to that stub is a special case: its inputs are
+    // multiple values returned from a call and so it should follow
+    // the return convention.
+    SharedRuntime::java_return_convention(sig_bt, parm_regs, argcnt);
+    return;
+  }
   SharedRuntime::c_calling_convention(sig_bt, parm_regs, argcnt);
 }
 
 void CallLeafVectorNode::calling_convention( BasicType* sig_bt, VMRegPair *parm_regs, uint argcnt ) const {
 #ifdef ASSERT
-  assert(tf()->range()->field_at(TypeFunc::Parms)->is_vect()->length_in_bytes() * BitsPerByte == _num_bits,
+  assert(tf()->range_sig()->field_at(TypeFunc::Parms)->is_vect()->length_in_bytes() * BitsPerByte == _num_bits,
          "return vector size must match");
-  const TypeTuple* d = tf()->domain();
+  const TypeTuple* d = tf()->domain_sig();
   for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
     Node* arg = in(i);
     assert(arg->bottom_type()->is_vect()->length_in_bytes() * BitsPerByte == _num_bits,
            "vector argument size must match");
   }
@@ -1261,10 +1450,16 @@
   st->print("%s", _name);
   CallNode::dump_spec(st);
 }
 #endif
 
+uint CallLeafNoFPNode::match_edge(uint idx) const {
+  // Null entry point is a special case for which the target is in a
+  // register. Need to match that edge.
+  return entry_point() == nullptr && idx == TypeFunc::Parms;
+}
+
 //=============================================================================
 
 void SafePointNode::set_local(JVMState* jvms, uint idx, Node *c) {
   assert(verify_jvms(jvms), "jvms must match");
   int loc = jvms->locoff() + idx;
@@ -1311,11 +1506,24 @@
 
 //------------------------------Ideal------------------------------------------
 // Skip over any collapsed Regions
 Node *SafePointNode::Ideal(PhaseGVN *phase, bool can_reshape) {
   assert(_jvms == nullptr || ((uintptr_t)_jvms->map() & 1) || _jvms->map() == this, "inconsistent JVMState");
-  return remove_dead_region(phase, can_reshape) ? this : nullptr;
+  if (remove_dead_region(phase, can_reshape)) {
+    return this;
+  }
+  // Scalarize inline types in safepoint debug info.
+  // Delay this until all inlining is over to avoid getting inconsistent debug info.
+  if (phase->C->scalarize_in_safepoints() && can_reshape && jvms() != nullptr) {
+    for (uint i = jvms()->debug_start(); i < jvms()->debug_end(); i++) {
+      Node* n = in(i)->uncast();
+      if (n->is_InlineType()) {
+        n->as_InlineType()->make_scalar_in_safepoints(phase->is_IterGVN());
+      }
+    }
+  }
+  return nullptr;
 }
 
 //------------------------------Identity---------------------------------------
 // Remove obviously duplicate safepoints
 Node* SafePointNode::Identity(PhaseGVN* phase) {
@@ -1468,11 +1676,11 @@
   _first_index(first_index),
   _n_fields(n_fields),
   _alloc(alloc)
 {
 #ifdef ASSERT
-  if (!alloc->is_Allocate() && !(alloc->Opcode() == Op_VectorBox)) {
+  if (alloc != nullptr && !alloc->is_Allocate() && !(alloc->Opcode() == Op_VectorBox)) {
     alloc->dump();
     assert(false, "unexpected call node");
   }
 #endif
   init_class_id(Class_SafePointScalarObject);
@@ -1573,18 +1781,21 @@
 //=============================================================================
 uint AllocateNode::size_of() const { return sizeof(*this); }
 
 AllocateNode::AllocateNode(Compile* C, const TypeFunc *atype,
                            Node *ctrl, Node *mem, Node *abio,
-                           Node *size, Node *klass_node, Node *initial_test)
+                           Node *size, Node *klass_node,
+                           Node* initial_test,
+                           InlineTypeNode* inline_type_node)
   : CallNode(atype, nullptr, TypeRawPtr::BOTTOM)
 {
   init_class_id(Class_Allocate);
   init_flags(Flag_is_macro);
   _is_scalar_replaceable = false;
   _is_non_escaping = false;
   _is_allocation_MemBar_redundant = false;
+  _larval = false;
   Node *topnode = C->top();
 
   init_req( TypeFunc::Control  , ctrl );
   init_req( TypeFunc::I_O      , abio );
   init_req( TypeFunc::Memory   , mem );
@@ -1593,34 +1804,44 @@
   init_req( AllocSize          , size);
   init_req( KlassNode          , klass_node);
   init_req( InitialTest        , initial_test);
   init_req( ALength            , topnode);
   init_req( ValidLengthTest    , topnode);
+  init_req( InlineType     , inline_type_node);
+  // DefaultValue defaults to nullptr
+  // RawDefaultValue defaults to nullptr
   C->add_macro_node(this);
 }
 
 void AllocateNode::compute_MemBar_redundancy(ciMethod* initializer)
 {
   assert(initializer != nullptr &&
-         initializer->is_initializer() &&
-         !initializer->is_static(),
-             "unexpected initializer method");
+         (initializer->is_object_constructor() || initializer->is_class_initializer()),
+         "unexpected initializer method");
   BCEscapeAnalyzer* analyzer = initializer->get_bcea();
   if (analyzer == nullptr) {
     return;
   }
 
   // Allocation node is first parameter in its initializer
   if (analyzer->is_arg_stack(0) || analyzer->is_arg_local(0)) {
     _is_allocation_MemBar_redundant = true;
   }
 }
-Node *AllocateNode::make_ideal_mark(PhaseGVN *phase, Node* obj, Node* control, Node* mem) {
+
+Node* AllocateNode::make_ideal_mark(PhaseGVN* phase, Node* control, Node* mem) {
   Node* mark_node = nullptr;
-  // For now only enable fast locking for non-array types
-  mark_node = phase->MakeConX(markWord::prototype().value());
-  return mark_node;
+  if (EnableValhalla) {
+    Node* klass_node = in(AllocateNode::KlassNode);
+    Node* proto_adr = phase->transform(new AddPNode(klass_node, klass_node, phase->MakeConX(in_bytes(Klass::prototype_header_offset()))));
+    mark_node = LoadNode::make(*phase, control, mem, proto_adr, TypeRawPtr::BOTTOM, TypeX_X, TypeX_X->basic_type(), MemNode::unordered);
+  } else {
+    mark_node = phase->MakeConX(markWord::prototype().value());
+  }
+  mark_node = phase->transform(mark_node);
+  // Avoid returning a constant (old node) here because this method is used by LoadNode::Ideal
+  return new OrXNode(mark_node, phase->MakeConX(_larval ? markWord::larval_bit_in_place : 0));
 }
 
 // Retrieve the length from the AllocateArrayNode. Narrow the type with a
 // CastII, if appropriate.  If we are not allowed to create new nodes, and
 // a CastII is appropriate, return null.
@@ -1994,11 +2215,12 @@
   // Now see if we can optimize away this lock.  We don't actually
   // remove the locking here, we simply set the _eliminate flag which
   // prevents macro expansion from expanding the lock.  Since we don't
   // modify the graph, the value returned from this function is the
   // one computed above.
-  if (can_reshape && EliminateLocks && !is_non_esc_obj()) {
+  const Type* obj_type = phase->type(obj_node());
+  if (can_reshape && EliminateLocks && !is_non_esc_obj() && !obj_type->is_inlinetypeptr()) {
     //
     // If we are locking an non-escaped object, the lock/unlock is unnecessary
     //
     ConnectionGraph *cgr = phase->C->congraph();
     if (cgr != nullptr && cgr->not_global_escape(obj_node())) {
@@ -2190,11 +2412,12 @@
   // remove the unlocking here, we simply set the _eliminate flag which
   // prevents macro expansion from expanding the unlock.  Since we don't
   // modify the graph, the value returned from this function is the
   // one computed above.
   // Escape state is defined after Parse phase.
-  if (can_reshape && EliminateLocks && !is_non_esc_obj()) {
+  const Type* obj_type = phase->type(obj_node());
+  if (can_reshape && EliminateLocks && !is_non_esc_obj() && !obj_type->is_inlinetypeptr()) {
     //
     // If we are unlocking an non-escaped object, the lock/unlock is unnecessary.
     //
     ConnectionGraph *cgr = phase->C->congraph();
     if (cgr != nullptr && cgr->not_global_escape(obj_node())) {
@@ -2270,11 +2493,12 @@
     if (elem == Type::BOTTOM) {
       // An array but we don't know what elements are
       return true;
     }
 
-    dest_t = dest_t->add_offset(Type::OffsetBot)->is_oopptr();
+    dest_t = dest_t->is_aryptr()->with_field_offset(Type::OffsetBot)->add_offset(Type::OffsetBot)->is_oopptr();
+    t_oop = t_oop->is_aryptr()->with_field_offset(Type::OffsetBot);
     uint dest_alias = phase->C->get_alias_index(dest_t);
     uint t_oop_alias = phase->C->get_alias_index(t_oop);
 
     return dest_alias == t_oop_alias;
   }
diff a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp
--- a/src/hotspot/share/opto/callnode.hpp
+++ b/src/hotspot/share/opto/callnode.hpp
@@ -74,11 +74,11 @@
   virtual const TypePtr *adr_type() const { return TypePtr::BOTTOM; }
   virtual const Type* Value(PhaseGVN* phase) const;
   virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
   virtual void  calling_convention( BasicType* sig_bt, VMRegPair *parm_reg, uint length ) const;
   virtual const RegMask &in_RegMask(uint) const;
-  virtual Node *match( const ProjNode *proj, const Matcher *m );
+  virtual Node *match(const ProjNode *proj, const Matcher *m, const RegMask* mask);
   virtual uint ideal_reg() const { return 0; }
 #ifndef PRODUCT
   virtual void  dump_spec(outputStream *st) const;
   virtual void  dump_compact_spec(outputStream *st) const;
 #endif
@@ -88,11 +88,10 @@
 // The method start node for on stack replacement code
 class StartOSRNode : public StartNode {
 public:
   StartOSRNode( Node *root, const TypeTuple *domain ) : StartNode(root, domain) {}
   virtual int   Opcode() const;
-  static  const TypeTuple *osr_domain();
 };
 
 
 //------------------------------ParmNode---------------------------------------
 // Incoming parameters
@@ -640,21 +639,39 @@
 #endif
 };
 
 // Simple container for the outgoing projections of a call.  Useful
 // for serious surgery on calls.
-class CallProjections : public StackObj {
+class CallProjections {
 public:
   Node* fallthrough_proj;
   Node* fallthrough_catchproj;
   Node* fallthrough_memproj;
   Node* fallthrough_ioproj;
   Node* catchall_catchproj;
   Node* catchall_memproj;
   Node* catchall_ioproj;
-  Node* resproj;
   Node* exobj;
+  uint nb_resproj;
+  Node* resproj[1]; // at least one projection
+
+  CallProjections(uint nbres) {
+    fallthrough_proj      = nullptr;
+    fallthrough_catchproj = nullptr;
+    fallthrough_memproj   = nullptr;
+    fallthrough_ioproj    = nullptr;
+    catchall_catchproj    = nullptr;
+    catchall_memproj      = nullptr;
+    catchall_ioproj       = nullptr;
+    exobj                 = nullptr;
+    nb_resproj            = nbres;
+    resproj[0]            = nullptr;
+    for (uint i = 1; i < nb_resproj; i++) {
+      resproj[i]          = nullptr;
+    }
+  }
+
 };
 
 class CallGenerator;
 
 //------------------------------CallNode---------------------------------------
@@ -672,11 +689,11 @@
   float           _cnt;         // Estimate of number of times called
   CallGenerator*  _generator;   // corresponding CallGenerator for some late inline calls
   const char*     _name;        // Printable name, if _method is null
 
   CallNode(const TypeFunc* tf, address addr, const TypePtr* adr_type, JVMState* jvms = nullptr)
-    : SafePointNode(tf->domain()->cnt(), jvms, adr_type),
+    : SafePointNode(tf->domain_cc()->cnt(), jvms, adr_type),
       _tf(tf),
       _entry_point(addr),
       _cnt(COUNT_UNKNOWN),
       _generator(nullptr),
       _name(nullptr)
@@ -699,11 +716,11 @@
   virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
   virtual Node* Identity(PhaseGVN* phase) { return this; }
   virtual bool        cmp(const Node &n) const;
   virtual uint        size_of() const = 0;
   virtual void        calling_convention(BasicType* sig_bt, VMRegPair* parm_regs, uint argcnt) const;
-  virtual Node*       match(const ProjNode* proj, const Matcher* m);
+  virtual Node*       match(const ProjNode* proj, const Matcher* m, const RegMask* mask);
   virtual uint        ideal_reg() const { return NotAMachineReg; }
   // Are we guaranteed that this node is a safepoint?  Not true for leaf calls and
   // for some macro nodes whose expansion does not have a safepoint on the fast path.
   virtual bool        guaranteed_safepoint()  { return true; }
   // For macro nodes, the JVMState gets modified during expansion. If calls
@@ -713,25 +730,27 @@
 
   // Returns true if the call may modify n
   virtual bool        may_modify(const TypeOopPtr* t_oop, PhaseValues* phase);
   // Does this node have a use of n other than in debug information?
   bool                has_non_debug_use(Node* n);
+  bool                has_debug_use(Node* n);
   // Returns the unique CheckCastPP of a call
   // or result projection is there are several CheckCastPP
   // or returns null if there is no one.
   Node* result_cast();
   // Does this node returns pointer?
   bool returns_pointer() const {
-    const TypeTuple* r = tf()->range();
-    return (r->cnt() > TypeFunc::Parms &&
+    const TypeTuple* r = tf()->range_sig();
+    return (!tf()->returns_inline_type_as_fields() &&
+            r->cnt() > TypeFunc::Parms &&
             r->field_at(TypeFunc::Parms)->isa_ptr());
   }
 
   // Collect all the interesting edges from a call for use in
   // replacing the call by something else.  Used by macro expansion
   // and the late inlining support.
-  void extract_projections(CallProjections* projs, bool separate_io_proj, bool do_asserts = true);
+  CallProjections* extract_projections(bool separate_io_proj, bool do_asserts = true);
 
   virtual uint match_edge(uint idx) const;
 
   bool is_call_to_arraycopystub() const;
 
@@ -797,18 +816,32 @@
 // calls and optimized virtual calls, plus calls to wrappers for run-time
 // routines); generates static stub.
 class CallStaticJavaNode : public CallJavaNode {
   virtual bool cmp( const Node &n ) const;
   virtual uint size_of() const; // Size is bigger
+
+  bool remove_useless_allocation(PhaseGVN *phase, Node* ctl, Node* mem, Node* unc_arg);
+
 public:
   CallStaticJavaNode(Compile* C, const TypeFunc* tf, address addr, ciMethod* method)
     : CallJavaNode(tf, addr, method) {
     init_class_id(Class_CallStaticJava);
     if (C->eliminate_boxing() && (method != nullptr) && method->is_boxing_method()) {
       init_flags(Flag_is_macro);
       C->add_macro_node(this);
     }
+    const TypeTuple *r = tf->range_sig();
+    if (InlineTypeReturnedAsFields &&
+        method != nullptr &&
+        method->is_method_handle_intrinsic() &&
+        r->cnt() > TypeFunc::Parms &&
+        r->field_at(TypeFunc::Parms)->isa_oopptr() &&
+        r->field_at(TypeFunc::Parms)->is_oopptr()->can_be_inline_type()) {
+      // Make sure this call is processed by PhaseMacroExpand::expand_mh_intrinsic_return
+      init_flags(Flag_is_macro);
+      C->add_macro_node(this);
+    }
   }
   CallStaticJavaNode(const TypeFunc* tf, address addr, const char* name, const TypePtr* adr_type)
     : CallJavaNode(tf, addr, nullptr) {
     init_class_id(Class_CallStaticJava);
     // This node calls a runtime stub, which often has narrow memory effects.
@@ -915,10 +948,11 @@
     : CallLeafNode(tf, addr, name, adr_type)
   {
     init_class_id(Class_CallLeafNoFP);
   }
   virtual int   Opcode() const;
+  virtual uint match_edge(uint idx) const;
 };
 
 //------------------------------CallLeafVectorNode-------------------------------
 // CallLeafNode but calling with vector calling convention instead.
 class CallLeafVectorNode : public CallLeafNode {
@@ -957,20 +991,26 @@
     AllocSize   = TypeFunc::Parms,    // size (in bytes) of the new object
     KlassNode,                        // type (maybe dynamic) of the obj.
     InitialTest,                      // slow-path test (may be constant)
     ALength,                          // array length (or TOP if none)
     ValidLengthTest,
+    InlineType,                       // InlineTypeNode if this is an inline type allocation
+    DefaultValue,                     // default value in case of non-flat inline type array
+    RawDefaultValue,                  // same as above but as raw machine word
     ParmLimit
   };
 
   static const TypeFunc* alloc_type(const Type* t) {
     const Type** fields = TypeTuple::fields(ParmLimit - TypeFunc::Parms);
     fields[AllocSize]   = TypeInt::POS;
     fields[KlassNode]   = TypeInstPtr::NOTNULL;
     fields[InitialTest] = TypeInt::BOOL;
     fields[ALength]     = t;  // length (can be a bad length)
     fields[ValidLengthTest] = TypeInt::BOOL;
+    fields[InlineType] = Type::BOTTOM;
+    fields[DefaultValue] = TypeInstPtr::NOTNULL;
+    fields[RawDefaultValue] = TypeX_X;
 
     const TypeTuple *domain = TypeTuple::make(ParmLimit, fields);
 
     // create result type (range)
     fields = TypeTuple::fields(1);
@@ -984,14 +1024,16 @@
   // Result of Escape Analysis
   bool _is_scalar_replaceable;
   bool _is_non_escaping;
   // True when MemBar for new is redundant with MemBar at initialzer exit
   bool _is_allocation_MemBar_redundant;
+  bool _larval;
 
   virtual uint size_of() const; // Size is bigger
   AllocateNode(Compile* C, const TypeFunc *atype, Node *ctrl, Node *mem, Node *abio,
-               Node *size, Node *klass_node, Node *initial_test);
+               Node *size, Node *klass_node, Node *initial_test,
+               InlineTypeNode* inline_type_node = nullptr);
   // Expansion modifies the JVMState, so we need to deep clone it
   virtual bool needs_deep_clone_jvms(Compile* C) { return true; }
   virtual int Opcode() const;
   virtual uint ideal_reg() const { return Op_RegP; }
   virtual bool        guaranteed_safepoint()  { return false; }
@@ -1052,28 +1094,36 @@
   // Inovke this method when MemBar at exit of initializer and post-dominate
   // allocation node.
   void compute_MemBar_redundancy(ciMethod* initializer);
   bool is_allocation_MemBar_redundant() { return _is_allocation_MemBar_redundant; }
 
-  Node* make_ideal_mark(PhaseGVN *phase, Node* obj, Node* control, Node* mem);
+  Node* make_ideal_mark(PhaseGVN* phase, Node* control, Node* mem);
 };
 
 //------------------------------AllocateArray---------------------------------
 //
 // High-level array allocation
 //
 class AllocateArrayNode : public AllocateNode {
+private:
+  bool _null_free;
+
 public:
   AllocateArrayNode(Compile* C, const TypeFunc* atype, Node* ctrl, Node* mem, Node* abio, Node* size, Node* klass_node,
-                    Node* initial_test, Node* count_val, Node* valid_length_test)
+                    Node* initial_test, Node* count_val, Node* valid_length_test,
+                    Node* default_value, Node* raw_default_value)
     : AllocateNode(C, atype, ctrl, mem, abio, size, klass_node,
                    initial_test)
   {
     init_class_id(Class_AllocateArray);
     set_req(AllocateNode::ALength,        count_val);
     set_req(AllocateNode::ValidLengthTest, valid_length_test);
+    init_req(AllocateNode::DefaultValue,  default_value);
+    init_req(AllocateNode::RawDefaultValue, raw_default_value);
+    _null_free = false;
   }
+  virtual uint size_of() const { return sizeof(*this); }
   virtual int Opcode() const;
 
   // Dig the length operand out of a array allocation site.
   Node* Ideal_length() {
     return in(AllocateNode::ALength);
@@ -1088,10 +1138,13 @@
   static AllocateArrayNode* Ideal_array_allocation(Node* ptr) {
     AllocateNode* allo = Ideal_allocation(ptr);
     return (allo == nullptr || !allo->is_AllocateArray())
            ? nullptr : allo->as_AllocateArray();
   }
+
+  void set_null_free() { _null_free = true; }
+  bool is_null_free() const { return _null_free; }
 };
 
 //------------------------------AbstractLockNode-----------------------------------
 class AbstractLockNode: public CallNode {
 private:
@@ -1188,11 +1241,11 @@
     // create result type (range)
     fields = TypeTuple::fields(0);
 
     const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0,fields);
 
-    return TypeFunc::make(domain,range);
+    return TypeFunc::make(domain, range);
   }
 
   virtual int Opcode() const;
   virtual uint size_of() const; // Size is bigger
   LockNode(Compile* C, const TypeFunc *tf) : AbstractLockNode( tf ) {
diff a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp
--- a/src/hotspot/share/opto/castnode.cpp
+++ b/src/hotspot/share/opto/castnode.cpp
@@ -25,12 +25,15 @@
 #include "precompiled.hpp"
 #include "opto/addnode.hpp"
 #include "opto/callnode.hpp"
 #include "opto/castnode.hpp"
 #include "opto/connode.hpp"
+#include "opto/graphKit.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/matcher.hpp"
 #include "opto/phaseX.hpp"
+#include "opto/rootnode.hpp"
 #include "opto/subnode.hpp"
 #include "opto/type.hpp"
 #include "castnode.hpp"
 #include "utilities/checkedCast.hpp"
 
@@ -96,11 +99,29 @@
 
 //------------------------------Ideal------------------------------------------
 // Return a node which is more "ideal" than the current node.  Strip out
 // control copies
 Node *ConstraintCastNode::Ideal(PhaseGVN *phase, bool can_reshape) {
-  return (in(0) && remove_dead_region(phase, can_reshape)) ? this : nullptr;
+  if (in(0) && remove_dead_region(phase, can_reshape)) {
+    return this;
+  }
+
+  // Push cast through InlineTypeNode
+  InlineTypeNode* vt = in(1)->isa_InlineType();
+  if (vt != nullptr && phase->type(vt)->filter_speculative(_type) != Type::TOP) {
+    // TODO 8325106 Can we avoid cloning?
+    Node* cast = clone();
+    cast->set_req(1, vt->get_oop());
+    vt = vt->clone()->as_InlineType();
+    if (!_type->maybe_null()) {
+      vt->as_InlineType()->set_is_init(*phase);
+    }
+    vt->set_oop(*phase, phase->transform(cast));
+    return vt;
+  }
+
+  return nullptr;
 }
 
 uint ConstraintCastNode::hash() const {
   return TypeNode::hash() + (int)_dependency + (_extra_types != nullptr ? _extra_types->hash() : 0);
 }
@@ -330,10 +351,20 @@
     }
   }
   return optimize_integer_cast(phase, T_LONG);
 }
 
+//=============================================================================
+//------------------------------Identity---------------------------------------
+// If input is already higher or equal to cast type, then this is an identity.
+Node* CheckCastPPNode::Identity(PhaseGVN* phase) {
+  if (in(1)->is_InlineType() && _type->isa_instptr() && phase->type(in(1))->inline_klass()->is_subtype_of(_type->is_instptr()->instance_klass())) {
+    return in(1);
+  }
+  return ConstraintCastNode::Identity(phase);
+}
+
 //------------------------------Value------------------------------------------
 // Take 'join' of input and cast-up type, unless working with an Interface
 const Type* CheckCastPPNode::Value(PhaseGVN* phase) const {
   if( in(0) && phase->type(in(0)) == Type::TOP ) return Type::TOP;
 
@@ -346,15 +377,25 @@
 
   const TypePtr *in_type = inn->isa_ptr();
   const TypePtr *my_type = _type->isa_ptr();
   const Type *result = _type;
   if (in_type != nullptr && my_type != nullptr) {
+    // TODO 8302672
+    if (!StressReflectiveCode && my_type->isa_aryptr() && in_type->isa_aryptr()) {
+      // Propagate array properties (not flat/null-free)
+      // Don't do this when StressReflectiveCode is enabled because it might lead to
+      // a dying data path while the corresponding flat/null-free check is not folded.
+      my_type = my_type->is_aryptr()->update_properties(in_type->is_aryptr());
+      if (my_type == nullptr) {
+        return Type::TOP; // Inconsistent properties
+      }
+    }
     TypePtr::PTR in_ptr = in_type->ptr();
     if (in_ptr == TypePtr::Null) {
       result = in_type;
     } else if (in_ptr != TypePtr::Constant) {
-      result =  my_type->cast_to_ptr_type(my_type->join_ptr(in_ptr));
+      result = my_type->cast_to_ptr_type(my_type->join_ptr(in_ptr));
     }
   }
 
   return result;
 }
@@ -437,10 +478,26 @@
   if (t == Type::TOP) return Type::TOP;
   if (t->base() == Type::RawPtr && t->singleton()) {
     uintptr_t bits = (uintptr_t) t->is_rawptr()->get_con();
     return TypeX::make(bits);
   }
+
+  if (t->is_zero_type() || !t->maybe_null()) {
+    for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) {
+      Node* u = fast_out(i);
+      if (u->Opcode() == Op_OrL) {
+        for (DUIterator_Fast jmax, j = u->fast_outs(jmax); j < jmax; j++) {
+          Node* cmp = u->fast_out(j);
+          if (cmp->Opcode() == Op_CmpL) {
+            // Give CmpL a chance to get optimized
+            phase->record_for_igvn(cmp);
+          }
+        }
+      }
+    }
+  }
+
   return CastP2XNode::bottom_type();
 }
 
 Node *CastP2XNode::Ideal(PhaseGVN *phase, bool can_reshape) {
   return (in(0) && remove_dead_region(phase, can_reshape)) ? this : nullptr;
diff a/src/hotspot/share/opto/castnode.hpp b/src/hotspot/share/opto/castnode.hpp
--- a/src/hotspot/share/opto/castnode.hpp
+++ b/src/hotspot/share/opto/castnode.hpp
@@ -192,10 +192,11 @@
   CheckCastPPNode(Node* ctrl, Node* n, const Type* t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr)
     : ConstraintCastNode(ctrl, n, t, dependency, types) {
     init_class_id(Class_CheckCastPP);
   }
 
+  virtual Node* Identity(PhaseGVN* phase);
   virtual const Type* Value(PhaseGVN* phase) const;
   virtual int   Opcode() const;
   virtual uint  ideal_reg() const { return Op_RegP; }
   bool depends_only_on_test() const { return !type()->isa_rawptr() && ConstraintCastNode::depends_only_on_test(); }
  };
@@ -228,8 +229,6 @@
   virtual const Type *bottom_type() const { return TypeX_X; }
   // Return false to keep node from moving away from an associated card mark.
   virtual bool depends_only_on_test() const { return false; }
 };
 
-
-
 #endif // SHARE_OPTO_CASTNODE_HPP
diff a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp
--- a/src/hotspot/share/opto/cfgnode.cpp
+++ b/src/hotspot/share/opto/cfgnode.cpp
@@ -31,10 +31,11 @@
 #include "opto/addnode.hpp"
 #include "opto/castnode.hpp"
 #include "opto/cfgnode.hpp"
 #include "opto/connode.hpp"
 #include "opto/convertnode.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/loopnode.hpp"
 #include "opto/machnode.hpp"
 #include "opto/movenode.hpp"
 #include "opto/narrowptrnode.hpp"
 #include "opto/mulnode.hpp"
@@ -516,10 +517,11 @@
   if (!cmp->is_Cmp()) {
     return false;
   }
   return true;
 }
+
 //------------------------------Ideal------------------------------------------
 // Return a node which is more "ideal" than the current node.  Must preserve
 // the CFG, but we can still strip out dead paths.
 Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) {
   if( !can_reshape && !in(0) ) return nullptr;     // Already degraded to a Copy
@@ -962,11 +964,12 @@
     return false; // No comparison
   } else if (cmp1->Opcode() == Op_CmpF || cmp1->Opcode() == Op_CmpD ||
              cmp2->Opcode() == Op_CmpF || cmp2->Opcode() == Op_CmpD ||
              cmp1->Opcode() == Op_CmpP || cmp1->Opcode() == Op_CmpN ||
              cmp2->Opcode() == Op_CmpP || cmp2->Opcode() == Op_CmpN ||
-             cmp1->is_SubTypeCheck() || cmp2->is_SubTypeCheck()) {
+             cmp1->is_SubTypeCheck() || cmp2->is_SubTypeCheck() ||
+             cmp1->is_FlatArrayCheck() || cmp2->is_FlatArrayCheck()) {
     // Floats and pointers don't exactly obey trichotomy. To be on the safe side, don't transform their tests.
     // SubTypeCheck is not commutative
     return false;
   } else if (cmp1 != cmp2) {
     if (cmp1->in(1) == cmp2->in(2) &&
@@ -1041,11 +1044,11 @@
   return nullptr;
 }
 
 
 //=============================================================================
-// note that these functions assume that the _adr_type field is flattened
+// note that these functions assume that the _adr_type field is flat
 uint PhiNode::hash() const {
   const Type* at = _adr_type;
   return TypeNode::hash() + (at ? at->hash() : 0);
 }
 bool PhiNode::cmp( const Node &n ) const {
@@ -1059,11 +1062,11 @@
 
 //----------------------------make---------------------------------------------
 // create a new phi with edges matching r and set (initially) to x
 PhiNode* PhiNode::make(Node* r, Node* x, const Type *t, const TypePtr* at) {
   uint preds = r->req();   // Number of predecessor paths
-  assert(t != Type::MEMORY || at == flatten_phi_adr_type(at), "flatten at");
+  assert(t != Type::MEMORY || at == flatten_phi_adr_type(at) || (flatten_phi_adr_type(at) == TypeAryPtr::INLINES && Compile::current()->flat_accesses_share_alias()), "flatten at");
   PhiNode* p = new PhiNode(r, t, at);
   for (uint j = 1; j < preds; j++) {
     // Fill in all inputs, except those which the region does not yet have
     if (r->in(j) != nullptr)
       p->init_req(j, x);
@@ -1187,10 +1190,18 @@
 void PhiNode::verify_adr_type(bool recursive) const {
   if (VMError::is_error_reported())  return;  // muzzle asserts when debugging an error
   if (Node::in_dump())               return;  // muzzle asserts when printing
 
   assert((_type == Type::MEMORY) == (_adr_type != nullptr), "adr_type for memory phis only");
+  // Flat array element shouldn't get their own memory slice until flat_accesses_share_alias is cleared.
+  // It could be the graph has no loads/stores and flat_accesses_share_alias is never cleared. EA could still
+  // creates per element Phis but that wouldn't be a problem as there are no memory accesses for that array.
+  assert(_adr_type == nullptr || _adr_type->isa_aryptr() == nullptr ||
+         _adr_type->is_aryptr()->is_known_instance() ||
+         !_adr_type->is_aryptr()->is_flat() ||
+         !Compile::current()->flat_accesses_share_alias() ||
+         _adr_type == TypeAryPtr::INLINES, "flat array element shouldn't get its own slice yet");
 
   if (!VerifyAliases)       return;  // verify thoroughly only if requested
 
   assert(_adr_type == flatten_phi_adr_type(_adr_type),
          "Phi::adr_type must be pre-normalized");
@@ -1405,10 +1416,11 @@
       }
     }
   }
   return false;
 }
+
 //----------------------------check_cmove_id-----------------------------------
 // Check for CMove'ing a constant after comparing against the constant.
 // Happens all the time now, since if we compare equality vs a constant in
 // the parser, we "know" the variable is constant on one path and we force
 // it.  Thus code like "if( x==0 ) {/*EMPTY*/}" ends up inserting a
@@ -2025,10 +2037,56 @@
     worklist.push(this);
   }
   return delay;
 }
 
+// Push inline type input nodes (and null) down through the phi recursively (can handle data loops).
+InlineTypeNode* PhiNode::push_inline_types_down(PhaseGVN* phase, bool can_reshape, ciInlineKlass* inline_klass) {
+  assert(inline_klass != nullptr, "must be");
+  InlineTypeNode* vt = InlineTypeNode::make_null(*phase, inline_klass, /* transform = */ false)->clone_with_phis(phase, in(0), nullptr, !_type->maybe_null());
+  if (can_reshape) {
+    // Replace phi right away to be able to use the inline
+    // type node when reaching the phi again through data loops.
+    PhaseIterGVN* igvn = phase->is_IterGVN();
+    for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) {
+      Node* u = fast_out(i);
+      igvn->rehash_node_delayed(u);
+      imax -= u->replace_edge(this, vt);
+      --i;
+    }
+    igvn->rehash_node_delayed(this);
+    assert(outcnt() == 0, "should be dead now");
+  }
+  ResourceMark rm;
+  Node_List casts;
+  for (uint i = 1; i < req(); ++i) {
+    Node* n = in(i);
+    while (n->is_ConstraintCast()) {
+      casts.push(n);
+      n = n->in(1);
+    }
+    if (phase->type(n)->is_zero_type()) {
+      n = InlineTypeNode::make_null(*phase, inline_klass);
+    } else if (n->is_Phi()) {
+      assert(can_reshape, "can only handle phis during IGVN");
+      n = phase->transform(n->as_Phi()->push_inline_types_down(phase, can_reshape, inline_klass));
+    }
+    while (casts.size() != 0) {
+      // Push the cast(s) through the InlineTypeNode
+      // TODO 8325106 Can we avoid cloning?
+      Node* cast = casts.pop()->clone();
+      cast->set_req_X(1, n->as_InlineType()->get_oop(), phase);
+      n = n->clone();
+      n->as_InlineType()->set_oop(*phase, phase->transform(cast));
+      n = phase->transform(n);
+    }
+    bool transform = !can_reshape && (i == (req()-1)); // Transform phis on last merge
+    vt->merge_with(phase, n->as_InlineType(), i, transform);
+  }
+  return vt;
+}
+
 // If the Phi's Region is in an irreducible loop, and the Region
 // has had an input removed, but not yet transformed, it could be
 // that the Region (and this Phi) are not reachable from Root.
 // If we allow the Phi to collapse before the Region, this may lead
 // to dead-loop data. Wait for the Region to check for reachability,
@@ -2357,10 +2415,12 @@
   // (MergeMemNode is not dead_loop_safe - need to check for dead loop.)
   if (progress == nullptr && can_reshape && type() == Type::MEMORY) {
     // see if this phi should be sliced
     uint merge_width = 0;
     bool saw_self = false;
+    // TODO revisit this with JDK-8247216
+    bool mergemem_only = true;
     for( uint i=1; i<req(); ++i ) {// For all paths in
       Node *ii = in(i);
       // TOP inputs should not be counted as safe inputs because if the
       // Phi references itself through all other inputs then splitting the
       // Phi through memory merges would create dead loop at later stage.
@@ -2369,15 +2429,17 @@
       }
       if (ii->is_MergeMem()) {
         MergeMemNode* n = ii->as_MergeMem();
         merge_width = MAX2(merge_width, n->req());
         saw_self = saw_self || (n->base_memory() == this);
+      } else {
+        mergemem_only = false;
       }
     }
 
     // This restriction is temporarily necessary to ensure termination:
-    if (!saw_self && adr_type() == TypePtr::BOTTOM)  merge_width = 0;
+    if (!mergemem_only && !saw_self && adr_type() == TypePtr::BOTTOM)  merge_width = 0;
 
     if (merge_width > Compile::AliasIdxRaw) {
       // found at least one non-empty MergeMem
       const TypePtr* at = adr_type();
       if (at != TypePtr::BOTTOM) {
@@ -2444,10 +2506,15 @@
         MergeMemNode* result = MergeMemNode::make(new_base);
         for (uint i = 1; i < req(); ++i) {
           Node *ii = in(i);
           if (ii->is_MergeMem()) {
             MergeMemNode* n = ii->as_MergeMem();
+            if (igvn) {
+              // TODO revisit this with JDK-8247216
+              // Put 'n' on the worklist because it might be modified by MergeMemStream::iteration_setup
+              igvn->_worklist.push(n);
+            }
             for (MergeMemStream mms(result, n); mms.next_non_empty2(); ) {
               // If we have not seen this slice yet, make a phi for it.
               bool made_new_phi = false;
               if (mms.is_empty()) {
                 Node* new_phi = new_base->slice_memory(mms.adr_type(phase->C));
@@ -2562,10 +2629,15 @@
       }
     }
   }
 #endif
 
+  Node* inline_type = try_push_inline_types_down(phase, can_reshape);
+  if (inline_type != this) {
+    return inline_type;
+  }
+
   // Try to convert a Phi with two duplicated convert nodes into a phi of the pre-conversion type and the convert node
   // proceeding the phi, to de-duplicate the convert node and compact the IR.
   if (can_reshape && progress == nullptr) {
     ConvertNode* convert = in(1)->isa_Convert();
     if (convert != nullptr) {
@@ -2605,10 +2677,94 @@
   }
 
   return progress;              // Return any progress
 }
 
+// Check recursively if inputs are either an inline type, constant null
+// or another Phi (including self references through data loops). If so,
+// push the inline types down through the phis to enable folding of loads.
+Node* PhiNode::try_push_inline_types_down(PhaseGVN* phase, const bool can_reshape) {
+  if (!can_be_inline_type()) {
+    return this;
+  }
+
+  ciInlineKlass* inline_klass;
+  if (can_push_inline_types_down(phase, can_reshape, inline_klass)) {
+    assert(inline_klass != nullptr, "must be");
+    return push_inline_types_down(phase, can_reshape, inline_klass);
+  }
+  return this;
+}
+
+bool PhiNode::can_push_inline_types_down(PhaseGVN* phase, const bool can_reshape, ciInlineKlass*& inline_klass) {
+  if (req() <= 2) {
+    // Dead phi.
+    return false;
+  }
+  inline_klass = nullptr;
+
+  // TODO 8302217 We need to prevent endless pushing through
+  bool only_phi = (outcnt() != 0);
+  for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) {
+    Node* n = fast_out(i);
+    if (n->is_InlineType() && n->in(1) == this) {
+      return false;
+    }
+    if (!n->is_Phi()) {
+      only_phi = false;
+    }
+  }
+  if (only_phi) {
+    return false;
+  }
+
+  ResourceMark rm;
+  Unique_Node_List worklist;
+  worklist.push(this);
+  Node_List casts;
+
+  for (uint next = 0; next < worklist.size(); next++) {
+    Node* phi = worklist.at(next);
+    for (uint i = 1; i < phi->req(); i++) {
+      Node* n = phi->in(i);
+      if (n == nullptr) {
+        return false;
+      }
+      while (n->is_ConstraintCast()) {
+        if (n->in(0) != nullptr && n->in(0)->is_top()) {
+          // Will die, don't optimize
+          return false;
+        }
+        casts.push(n);
+        n = n->in(1);
+      }
+      const Type* type = phase->type(n);
+      if (n->is_InlineType() && (inline_klass == nullptr || inline_klass == type->inline_klass())) {
+        inline_klass = type->inline_klass();
+      } else if (n->is_Phi() && can_reshape && n->bottom_type()->isa_ptr()) {
+        worklist.push(n);
+      } else if (!type->is_zero_type()) {
+        return false;
+      }
+    }
+  }
+  if (inline_klass == nullptr) {
+    return false;
+  }
+
+  // Check if cast nodes can be pushed through
+  const Type* t = Type::get_const_type(inline_klass);
+  while (casts.size() != 0 && t != nullptr) {
+    Node* cast = casts.pop();
+    if (t->filter(cast->bottom_type()) == Type::TOP) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 static int compare_types(const Type* const& e1, const Type* const& e2) {
   return (intptr_t)e1 - (intptr_t)e2;
 }
 
 // Collect types at casts that are going to be eliminated at that Phi and store them in a TypeTuple.
@@ -2987,10 +3143,16 @@
     return phase->C->top(); // dead code
   }
   // We only come from CatchProj, unless the CatchProj goes away.
   // If the CatchProj is optimized away, then we just carry the
   // exception oop through.
+
+  // CheckCastPPNode::Ideal() for inline types reuses the exception
+  // paths of a call to perform an allocation: we can see a Phi here.
+  if (in(1)->is_Phi()) {
+    return this;
+  }
   CallNode *call = in(1)->in(0)->as_Call();
 
   return (in(0)->is_CatchProj() && in(0)->in(0)->is_Catch() &&
           in(0)->in(0)->in(1) == in(1)) ? this : call->in(TypeFunc::Parms);
 }
diff a/src/hotspot/share/opto/cfgnode.hpp b/src/hotspot/share/opto/cfgnode.hpp
--- a/src/hotspot/share/opto/cfgnode.hpp
+++ b/src/hotspot/share/opto/cfgnode.hpp
@@ -179,10 +179,13 @@
   static Node* clone_through_phi(Node* root_phi, const Type* t, uint c, PhaseIterGVN* igvn);
   static Node* merge_through_phi(Node* root_phi, PhaseIterGVN* igvn);
 
   bool must_wait_for_region_in_irreducible_loop(PhaseGVN* phase) const;
 
+  bool can_push_inline_types_down(PhaseGVN* phase, bool can_reshape, ciInlineKlass*& inline_klass);
+  InlineTypeNode* push_inline_types_down(PhaseGVN* phase, bool can_reshape, ciInlineKlass* inline_klass);
+
 public:
   // Node layout (parallels RegionNode):
   enum { Region,                // Control input is the Phi's region.
          Input                  // Input values are [1..len)
   };
@@ -252,10 +255,16 @@
            inst_index()  == index  &&
            inst_offset() == offset &&
            type()->higher_equal(tp);
   }
 
+  bool can_be_inline_type() const {
+    return EnableValhalla && _type->isa_instptr() && _type->is_instptr()->can_be_inline_type();
+  }
+
+  Node* try_push_inline_types_down(PhaseGVN* phase, bool can_reshape);
+
   virtual const Type* Value(PhaseGVN* phase) const;
   virtual Node* Identity(PhaseGVN* phase);
   virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
   virtual const RegMask &out_RegMask() const;
   virtual const RegMask &in_RegMask(uint) const;
@@ -436,10 +445,12 @@
   // Takes the type of val and filters it through the test represented
   // by if_proj and returns a more refined type if one is produced.
   // Returns null is it couldn't improve the type.
   static const TypeInt* filtered_int_type(PhaseGVN* phase, Node* val, Node* if_proj);
 
+  bool is_flat_array_check(PhaseTransform* phase, Node** array = nullptr);
+
 #ifndef PRODUCT
   virtual void dump_spec(outputStream *st) const;
 #endif
 
   bool same_condition(const Node* dom, PhaseIterGVN* igvn) const;
@@ -696,10 +707,11 @@
 // the effects on its arguments, and would be finally matched to nothing.
 class BlackholeNode : public MultiNode {
 public:
   BlackholeNode(Node* ctrl) : MultiNode(1) {
     init_req(TypeFunc::Control, ctrl);
+    init_class_id(Class_Blackhole);
   }
   virtual int   Opcode() const;
   virtual uint ideal_reg() const { return 0; } // not matched in the AD file
   virtual const Type* bottom_type() const { return TypeTuple::MEMBAR; }
 
diff a/src/hotspot/share/opto/chaitin.cpp b/src/hotspot/share/opto/chaitin.cpp
--- a/src/hotspot/share/opto/chaitin.cpp
+++ b/src/hotspot/share/opto/chaitin.cpp
@@ -1808,14 +1808,14 @@
   // NOTE: we use TypePtr instead of TypeOopPtr because we can have
   // pointers derived from null!  These are always along paths that
   // can't happen at run-time but the optimizer cannot deduce it so
   // we have to handle it gracefully.
   assert(!derived->bottom_type()->isa_narrowoop() ||
-          derived->bottom_type()->make_ptr()->is_ptr()->_offset == 0, "sanity");
+         derived->bottom_type()->make_ptr()->is_ptr()->offset() == 0, "sanity");
   const TypePtr *tj = derived->bottom_type()->isa_ptr();
   // If its an OOP with a non-zero offset, then it is derived.
-  if( tj == nullptr || tj->_offset == 0 ) {
+  if (tj == nullptr || tj->offset() == 0) {
     derived_base_map[derived->_idx] = derived;
     return derived;
   }
   // Derived is null+offset?  Base is null!
   if( derived->is_Con() ) {
@@ -1977,13 +1977,13 @@
           // Find reaching DEF for base and derived values
           // This works because we are still in SSA during this call.
           Node *derived = lrgs(neighbor)._def;
           const TypePtr *tj = derived->bottom_type()->isa_ptr();
           assert(!derived->bottom_type()->isa_narrowoop() ||
-                  derived->bottom_type()->make_ptr()->is_ptr()->_offset == 0, "sanity");
+                 derived->bottom_type()->make_ptr()->is_ptr()->offset() == 0, "sanity");
           // If its an OOP with a non-zero offset, then it is derived.
-          if( tj && tj->_offset != 0 && tj->isa_oop_ptr() ) {
+          if (tj && tj->offset() != 0 && tj->isa_oop_ptr()) {
             Node *base = find_base_for_derived(derived_base_map, derived, maxlrg);
             assert(base->_idx < _lrg_map.size(), "");
             // Add reaching DEFs of derived pointer and base pointer as a
             // pair of inputs
             n->add_req(derived);
@@ -2269,11 +2269,11 @@
   }
 }
 
 void PhaseChaitin::dump_frame() const {
   const char *fp = OptoReg::regname(OptoReg::c_frame_pointer);
-  const TypeTuple *domain = C->tf()->domain();
+  const TypeTuple *domain = C->tf()->domain_cc();
   const int        argcnt = domain->cnt() - TypeFunc::Parms;
 
   // Incoming arguments in registers dump
   for( int k = 0; k < argcnt; k++ ) {
     OptoReg::Name parmreg = _matcher._parm_regs[k].first();
@@ -2478,24 +2478,24 @@
                 } else if (check->is_Phi()) {
                   for (uint m = 1; m < check->req(); m++) {
                     worklist.push(check->in(m));
                   }
                 } else if (check->is_Con()) {
-                  if (is_derived && check->bottom_type()->is_ptr()->_offset != 0) {
+                  if (is_derived && check->bottom_type()->is_ptr()->offset() != 0) {
                     // Derived is null+non-zero offset, base must be null.
                     assert(check->bottom_type()->is_ptr()->ptr() == TypePtr::Null, "Bad derived pointer");
                   } else {
-                    assert(check->bottom_type()->is_ptr()->_offset == 0, "Bad base pointer");
+                    assert(check->bottom_type()->is_ptr()->offset() == 0, "Bad base pointer");
                     // Base either ConP(nullptr) or loadConP
                     if (check->is_Mach()) {
                       assert(check->as_Mach()->ideal_Opcode() == Op_ConP, "Bad base pointer");
                     } else {
                       assert(check->Opcode() == Op_ConP &&
                              check->bottom_type()->is_ptr()->ptr() == TypePtr::Null, "Bad base pointer");
                     }
                   }
-                } else if (check->bottom_type()->is_ptr()->_offset == 0) {
+                } else if (check->bottom_type()->is_ptr()->offset() == 0) {
                   if (check->is_Proj() || (check->is_Mach() &&
                      (check->as_Mach()->ideal_Opcode() == Op_CreateEx ||
                       check->as_Mach()->ideal_Opcode() == Op_ThreadLocal ||
                       check->as_Mach()->ideal_Opcode() == Op_CMoveP ||
                       check->as_Mach()->ideal_Opcode() == Op_CheckCastPP ||
diff a/src/hotspot/share/opto/classes.cpp b/src/hotspot/share/opto/classes.cpp
--- a/src/hotspot/share/opto/classes.cpp
+++ b/src/hotspot/share/opto/classes.cpp
@@ -30,10 +30,11 @@
 #include "opto/cfgnode.hpp"
 #include "opto/connode.hpp"
 #include "opto/convertnode.hpp"
 #include "opto/countbitsnode.hpp"
 #include "opto/divnode.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/intrinsicnode.hpp"
 #include "opto/locknode.hpp"
 #include "opto/loopnode.hpp"
 #include "opto/machnode.hpp"
 #include "opto/memnode.hpp"
diff a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp
--- a/src/hotspot/share/opto/classes.hpp
+++ b/src/hotspot/share/opto/classes.hpp
@@ -180,10 +180,11 @@
 macro(EncodeISOArray)
 macro(EncodeP)
 macro(EncodePKlass)
 macro(FastLock)
 macro(FastUnlock)
+macro(FlatArrayCheck)
 macro(FmaD)
 macro(FmaF)
 macro(Goto)
 macro(Halt)
 macro(CountPositives)
@@ -368,10 +369,11 @@
 macro(URShiftS)
 macro(URShiftI)
 macro(URShiftL)
 macro(XorI)
 macro(XorL)
+macro(InlineType)
 macro(Vector)
 macro(AddVB)
 macro(AddVS)
 macro(AddVI)
 macro(AddReductionVI)
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
@@ -54,10 +54,11 @@
 #include "opto/connode.hpp"
 #include "opto/convertnode.hpp"
 #include "opto/divnode.hpp"
 #include "opto/escape.hpp"
 #include "opto/idealGraphPrinter.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/loopnode.hpp"
 #include "opto/machnode.hpp"
 #include "opto/macro.hpp"
 #include "opto/matcher.hpp"
 #include "opto/mathexactnode.hpp"
@@ -398,10 +399,13 @@
     remove_parse_predicate(dead->as_ParsePredicate());
   }
   if (dead->for_post_loop_opts_igvn()) {
     remove_from_post_loop_opts_igvn(dead);
   }
+  if (dead->is_InlineType()) {
+    remove_inline_type(dead);
+  }
   if (dead->is_Call()) {
     remove_useless_late_inlines(                &_late_inlines, dead);
     remove_useless_late_inlines(         &_string_late_inlines, dead);
     remove_useless_late_inlines(         &_boxing_late_inlines, dead);
     remove_useless_late_inlines(&_vector_reboxing_late_inlines, dead);
@@ -439,17 +443,26 @@
     }
     if (n->outcnt() == 1 && n->has_special_unique_user()) {
       assert(useful.member(n->unique_out()), "do not push a useless node");
       worklist.push(n->unique_out());
     }
+    if (n->outcnt() == 0) {
+      worklist.push(n);
+    }
   }
 
   remove_useless_nodes(_macro_nodes,        useful); // remove useless macro nodes
   remove_useless_nodes(_parse_predicates,   useful); // remove useless Parse Predicate nodes
   remove_useless_nodes(_template_assertion_predicate_opaqs, useful); // remove useless Assertion Predicate opaque nodes
   remove_useless_nodes(_expensive_nodes,    useful); // remove useless expensive nodes
   remove_useless_nodes(_for_post_loop_igvn, useful); // remove useless node recorded for post loop opts IGVN pass
+  remove_useless_nodes(_inline_type_nodes,  useful); // remove useless inline type nodes
+#ifdef ASSERT
+  if (_modified_nodes != nullptr) {
+    _modified_nodes->remove_useless_nodes(useful.member_set());
+  }
+#endif
   remove_useless_unstable_if_traps(useful);          // remove useless unstable_if traps
   remove_useless_coarsened_locks(useful);            // remove useless coarsened locks nodes
 #ifdef ASSERT
   if (_modified_nodes != nullptr) {
     _modified_nodes->remove_useless_nodes(useful.member_set());
@@ -624,10 +637,11 @@
                   _post_loop_opts_phase(false),
                   _inlining_progress(false),
                   _inlining_incrementally(false),
                   _do_cleanup(false),
                   _has_reserved_stack_access(target->has_reserved_stack_access()),
+                  _has_circular_inline_type(false),
 #ifndef PRODUCT
                   _igv_idx(0),
                   _trace_opto_output(directive->TraceOptoOutputOption),
 #endif
                   _has_method_handle_invokes(false),
@@ -644,10 +658,11 @@
                   _macro_nodes       (comp_arena(), 8, 0, nullptr),
                   _parse_predicates  (comp_arena(), 8, 0, nullptr),
                   _template_assertion_predicate_opaqs (comp_arena(), 8, 0, nullptr),
                   _expensive_nodes   (comp_arena(), 8, 0, nullptr),
                   _for_post_loop_igvn(comp_arena(), 8, 0, nullptr),
+                  _inline_type_nodes (comp_arena(), 8, 0, nullptr),
                   _unstable_if_traps (comp_arena(), 8, 0, nullptr),
                   _coarsened_locks   (comp_arena(), 8, 0, nullptr),
                   _congraph(nullptr),
                   NOT_PRODUCT(_igv_printer(nullptr) COMMA)
                   _unique(0),
@@ -746,21 +761,19 @@
     initial_gvn()->transform(top());
 
     // Set up tf(), start(), and find a CallGenerator.
     CallGenerator* cg = nullptr;
     if (is_osr_compilation()) {
-      const TypeTuple *domain = StartOSRNode::osr_domain();
-      const TypeTuple *range = TypeTuple::make_range(method()->signature());
-      init_tf(TypeFunc::make(domain, range));
-      StartNode* s = new StartOSRNode(root(), domain);
+      init_tf(TypeFunc::make(method(), /* is_osr_compilation = */ true));
+      StartNode* s = new StartOSRNode(root(), tf()->domain_sig());
       initial_gvn()->set_type_bottom(s);
       init_start(s);
       cg = CallGenerator::for_osr(method(), entry_bci());
     } else {
       // Normal case.
       init_tf(TypeFunc::make(method()));
-      StartNode* s = new StartNode(root(), tf()->domain());
+      StartNode* s = new StartNode(root(), tf()->domain_cc());
       initial_gvn()->set_type_bottom(s);
       init_start(s);
       if (method()->intrinsic_id() == vmIntrinsics::_Reference_get) {
         // With java.lang.ref.reference.get() we must go through the
         // intrinsic - even when get() is the root
@@ -881,10 +894,20 @@
   }
 
   // Now that we know the size of all the monitors we can add a fixed slot
   // for the original deopt pc.
   int next_slot = fixed_slots() + (sizeof(address) / VMRegImpl::stack_slot_size);
+  if (needs_stack_repair()) {
+    // One extra slot for the special stack increment value
+    next_slot += 2;
+  }
+  // TODO 8284443 Only reserve extra slot if needed
+  if (InlineTypeReturnedAsFields) {
+    // One extra slot to hold the IsInit information for a nullable
+    // inline type return if we run out of registers.
+    next_slot += 2;
+  }
   set_fixed_slots(next_slot);
 
   // Compute when to use implicit null checks. Used by matching trap based
   // nodes and NullCheck optimization.
   set_allowed_deopt_reasons();
@@ -914,10 +937,11 @@
     _max_node_limit(MaxNodeLimit),
     _post_loop_opts_phase(false),
     _inlining_progress(false),
     _inlining_incrementally(false),
     _has_reserved_stack_access(false),
+    _has_circular_inline_type(false),
 #ifndef PRODUCT
     _igv_idx(0),
     _trace_opto_output(directive->TraceOptoOutputOption),
 #endif
     _has_method_handle_invokes(false),
@@ -1054,10 +1078,14 @@
   Copy::zero_to_bytes(_igv_phase_iter, sizeof(_igv_phase_iter));
 #endif
 
   set_do_freq_based_layout(_directive->BlockLayoutByFrequencyOption);
   _loop_opts_cnt = LoopOptsCount;
+  _has_flat_accesses = false;
+  _flat_accesses_share_alias = true;
+  _scalarize_in_safepoints = false;
+
   set_do_inlining(Inline);
   set_max_inline_size(MaxInlineSize);
   set_freq_inline_size(FreqInlineSize);
   set_do_scheduling(OptoScheduling);
 
@@ -1342,11 +1370,12 @@
   bool is_known_inst = tj->isa_oopptr() != nullptr &&
                        tj->is_oopptr()->is_known_instance();
 
   // Process weird unsafe references.
   if (offset == Type::OffsetBot && (tj->isa_instptr() /*|| tj->isa_klassptr()*/)) {
-    assert(InlineUnsafeOps || StressReflectiveCode, "indeterminate pointers come only from unsafe ops");
+    bool default_value_load = EnableValhalla && tj->is_instptr()->instance_klass() == ciEnv::current()->Class_klass();
+    assert(InlineUnsafeOps || StressReflectiveCode || default_value_load, "indeterminate pointers come only from unsafe ops");
     assert(!is_known_inst, "scalarizable allocation should not have unsafe references");
     tj = TypeOopPtr::BOTTOM;
     ptr = tj->ptr();
     offset = tj->offset();
   }
@@ -1355,10 +1384,19 @@
   const TypeAryPtr* ta = tj->isa_aryptr();
   if (ta && ta->is_stable()) {
     // Erase stability property for alias analysis.
     tj = ta = ta->cast_to_stable(false);
   }
+  if (ta && ta->is_not_flat()) {
+    // Erase not flat property for alias analysis.
+    tj = ta = ta->cast_to_not_flat(false);
+  }
+  if (ta && ta->is_not_null_free()) {
+    // Erase not null free property for alias analysis.
+    tj = ta = ta->cast_to_not_null_free(false);
+  }
+
   if( ta && is_known_inst ) {
     if ( offset != Type::OffsetBot &&
          offset > arrayOopDesc::length_offset_in_bytes() ) {
       offset = Type::OffsetBot; // Flatten constant access into array body only
       tj = ta = ta->
@@ -1368,10 +1406,12 @@
     }
   } else if (ta) {
     // For arrays indexed by constant indices, we flatten the alias
     // space to include all of the array body.  Only the header, klass
     // and array length can be accessed un-aliased.
+    // For flat inline type array, each field has its own slice so
+    // we must include the field offset.
     if( offset != Type::OffsetBot ) {
       if( ta->const_oop() ) { // MethodData* or Method*
         offset = Type::OffsetBot;   // Flatten constant access into array body
         tj = ta = ta->
                 remove_speculative()->
@@ -1408,22 +1448,27 @@
               cast_to_exactness(false);
     }
     // Arrays of known objects become arrays of unknown objects.
     if (ta->elem()->isa_narrowoop() && ta->elem() != TypeNarrowOop::BOTTOM) {
       const TypeAry *tary = TypeAry::make(TypeNarrowOop::BOTTOM, ta->size());
-      tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),tary,nullptr,false,offset);
+      tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),tary,nullptr,false,Type::Offset(offset), ta->field_offset());
     }
     if (ta->elem()->isa_oopptr() && ta->elem() != TypeInstPtr::BOTTOM) {
       const TypeAry *tary = TypeAry::make(TypeInstPtr::BOTTOM, ta->size());
-      tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),tary,nullptr,false,offset);
+      tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),tary,nullptr,false,Type::Offset(offset), ta->field_offset());
+    }
+    // Initially all flattened array accesses share a single slice
+    if (ta->is_flat() && ta->elem() != TypeInstPtr::BOTTOM && _flat_accesses_share_alias) {
+      const TypeAry* tary = TypeAry::make(TypeInstPtr::BOTTOM, ta->size(), /* stable= */ false, /* flat= */ true);
+      tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),tary,nullptr,false,Type::Offset(offset), Type::Offset(Type::OffsetBot));
     }
     // Arrays of bytes and of booleans both use 'bastore' and 'baload' so
     // cannot be distinguished by bytecode alone.
     if (ta->elem() == TypeInt::BOOL) {
       const TypeAry *tary = TypeAry::make(TypeInt::BYTE, ta->size());
       ciKlass* aklass = ciTypeArrayKlass::make(T_BYTE);
-      tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),tary,aklass,false,offset);
+      tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),tary,aklass,false,Type::Offset(offset), ta->field_offset());
     }
     // During the 2nd round of IterGVN, NotNull castings are removed.
     // Make sure the Bottom and NotNull variants alias the same.
     // Also, make sure exact and non-exact variants alias the same.
     if (ptr == TypePtr::NotNull || ta->klass_is_exact() || ta->speculative() != nullptr) {
@@ -1469,11 +1514,11 @@
     // Canonicalize the holder of this field
     if (offset >= 0 && offset < instanceOopDesc::base_offset_in_bytes()) {
       // First handle header references such as a LoadKlassNode, even if the
       // object's klass is unloaded at compile time (4965979).
       if (!is_known_inst) { // Do it only for non-instance types
-        tj = to = TypeInstPtr::make(TypePtr::BotPTR, env()->Object_klass(), false, nullptr, offset);
+        tj = to = TypeInstPtr::make(TypePtr::BotPTR, env()->Object_klass(), false, nullptr, Type::Offset(offset));
       }
     } else if (offset < 0 || offset >= ik->layout_helper_size_in_bytes()) {
       // Static fields are in the space above the normal instance
       // fields in the java.lang.Class instance.
       if (ik != ciEnv::current()->Class_klass()) {
@@ -1484,13 +1529,13 @@
     } else {
       ciInstanceKlass *canonical_holder = ik->get_canonical_holder(offset);
       assert(offset < canonical_holder->layout_helper_size_in_bytes(), "");
       if (!ik->equals(canonical_holder) || tj->offset() != offset) {
         if( is_known_inst ) {
-          tj = to = TypeInstPtr::make(to->ptr(), canonical_holder, true, nullptr, offset, to->instance_id());
+          tj = to = TypeInstPtr::make(to->ptr(), canonical_holder, true, nullptr, Type::Offset(offset), to->instance_id());
         } else {
-          tj = to = TypeInstPtr::make(to->ptr(), canonical_holder, false, nullptr, offset);
+          tj = to = TypeInstPtr::make(to->ptr(), canonical_holder, false, nullptr, Type::Offset(offset));
         }
       }
     }
   }
 
@@ -1502,22 +1547,21 @@
     // inexact types must flatten to the same alias class so
     // use NotNull as the PTR.
     if ( offset == Type::OffsetBot || (offset >= 0 && (size_t)offset < sizeof(Klass)) ) {
       tj = tk = TypeInstKlassPtr::make(TypePtr::NotNull,
                                        env()->Object_klass(),
-                                       offset);
+                                       Type::Offset(offset));
     }
 
     if (tk->isa_aryklassptr() && tk->is_aryklassptr()->elem()->isa_klassptr()) {
       ciKlass* k = ciObjArrayKlass::make(env()->Object_klass());
       if (!k || !k->is_loaded()) {                  // Only fails for some -Xcomp runs
-        tj = tk = TypeInstKlassPtr::make(TypePtr::NotNull, env()->Object_klass(), offset);
+        tj = tk = TypeInstKlassPtr::make(TypePtr::NotNull, env()->Object_klass(), Type::Offset(offset));
       } else {
-        tj = tk = TypeAryKlassPtr::make(TypePtr::NotNull, tk->is_aryklassptr()->elem(), k, offset);
+        tj = tk = TypeAryKlassPtr::make(TypePtr::NotNull, tk->is_aryklassptr()->elem(), k, Type::Offset(offset), tk->is_not_flat(), tk->is_not_null_free(), tk->is_null_free());
       }
     }
-
     // Check for precise loads from the primary supertype array and force them
     // to the supertype cache alias index.  Check for generic array loads from
     // the primary supertype array and also force them to the supertype cache
     // alias index.  Since the same load can reach both, we need to merge
     // these 2 disparate memories into the same alias class.  Since the
@@ -1643,18 +1687,21 @@
   for (int i = 0; i < new_ats; i++)  _alias_types[old_ats+i] = &ats[i];
 }
 
 
 //--------------------------------find_alias_type------------------------------
-Compile::AliasType* Compile::find_alias_type(const TypePtr* adr_type, bool no_create, ciField* original_field) {
+Compile::AliasType* Compile::find_alias_type(const TypePtr* adr_type, bool no_create, ciField* original_field, bool uncached) {
   if (!do_aliasing()) {
     return alias_type(AliasIdxBot);
   }
 
-  AliasCacheEntry* ace = probe_alias_cache(adr_type);
-  if (ace->_adr_type == adr_type) {
-    return alias_type(ace->_index);
+  AliasCacheEntry* ace = nullptr;
+  if (!uncached) {
+    ace = probe_alias_cache(adr_type);
+    if (ace->_adr_type == adr_type) {
+      return alias_type(ace->_index);
+    }
   }
 
   // Handle special cases.
   if (adr_type == nullptr)          return alias_type(AliasIdxTop);
   if (adr_type == TypePtr::BOTTOM)  return alias_type(AliasIdxBot);
@@ -1700,18 +1747,27 @@
     if (flat->isa_instptr()) {
       if (flat->offset() == java_lang_Class::klass_offset()
           && flat->is_instptr()->instance_klass() == env()->Class_klass())
         alias_type(idx)->set_rewritable(false);
     }
+    ciField* field = nullptr;
     if (flat->isa_aryptr()) {
 #ifdef ASSERT
       const int header_size_min  = arrayOopDesc::base_offset_in_bytes(T_BYTE);
       // (T_BYTE has the weakest alignment and size restrictions...)
       assert(flat->offset() < header_size_min, "array body reference must be OffsetBot");
 #endif
+      const Type* elemtype = flat->is_aryptr()->elem();
       if (flat->offset() == TypePtr::OffsetBot) {
-        alias_type(idx)->set_element(flat->is_aryptr()->elem());
+        alias_type(idx)->set_element(elemtype);
+      }
+      int field_offset = flat->is_aryptr()->field_offset().get();
+      if (flat->is_flat() &&
+          field_offset != Type::OffsetBot) {
+        ciInlineKlass* vk = elemtype->inline_klass();
+        field_offset += vk->first_field_offset();
+        field = vk->get_field_by_offset(field_offset, false);
       }
     }
     if (flat->isa_klassptr()) {
       if (flat->offset() == in_bytes(Klass::super_check_offset_offset()))
         alias_type(idx)->set_rewritable(false);
@@ -1719,52 +1775,66 @@
         alias_type(idx)->set_rewritable(false);
       if (flat->offset() == in_bytes(Klass::access_flags_offset()))
         alias_type(idx)->set_rewritable(false);
       if (flat->offset() == in_bytes(Klass::java_mirror_offset()))
         alias_type(idx)->set_rewritable(false);
+      if (flat->offset() == in_bytes(Klass::layout_helper_offset()))
+        alias_type(idx)->set_rewritable(false);
       if (flat->offset() == in_bytes(Klass::secondary_super_cache_offset()))
         alias_type(idx)->set_rewritable(false);
     }
     // %%% (We would like to finalize JavaThread::threadObj_offset(),
     // but the base pointer type is not distinctive enough to identify
     // references into JavaThread.)
 
     // Check for final fields.
     const TypeInstPtr* tinst = flat->isa_instptr();
     if (tinst && tinst->offset() >= instanceOopDesc::base_offset_in_bytes()) {
-      ciField* field;
       if (tinst->const_oop() != nullptr &&
           tinst->instance_klass() == ciEnv::current()->Class_klass() &&
           tinst->offset() >= (tinst->instance_klass()->layout_helper_size_in_bytes())) {
         // static field
         ciInstanceKlass* k = tinst->const_oop()->as_instance()->java_lang_Class_klass()->as_instance_klass();
         field = k->get_field_by_offset(tinst->offset(), true);
+      } else if (tinst->is_inlinetypeptr()) {
+        // Inline type field
+        ciInlineKlass* vk = tinst->inline_klass();
+        field = vk->get_field_by_offset(tinst->offset(), false);
       } else {
         ciInstanceKlass *k = tinst->instance_klass();
         field = k->get_field_by_offset(tinst->offset(), false);
       }
-      assert(field == nullptr ||
-             original_field == nullptr ||
-             (field->holder() == original_field->holder() &&
-              field->offset_in_bytes() == original_field->offset_in_bytes() &&
-              field->is_static() == original_field->is_static()), "wrong field?");
-      // Set field() and is_rewritable() attributes.
-      if (field != nullptr)  alias_type(idx)->set_field(field);
+    }
+    assert(field == nullptr ||
+           original_field == nullptr ||
+           (field->holder() == original_field->holder() &&
+            field->offset_in_bytes() == original_field->offset_in_bytes() &&
+            field->is_static() == original_field->is_static()), "wrong field?");
+    // Set field() and is_rewritable() attributes.
+    if (field != nullptr) {
+      alias_type(idx)->set_field(field);
+      if (flat->isa_aryptr()) {
+        // Fields of flat arrays are rewritable although they are declared final
+        assert(flat->is_flat(), "must be a flat array");
+        alias_type(idx)->set_rewritable(true);
+      }
     }
   }
 
   // Fill the cache for next time.
-  ace->_adr_type = adr_type;
-  ace->_index    = idx;
-  assert(alias_type(adr_type) == alias_type(idx),  "type must be installed");
+  if (!uncached) {
+    ace->_adr_type = adr_type;
+    ace->_index    = idx;
+    assert(alias_type(adr_type) == alias_type(idx),  "type must be installed");
 
-  // Might as well try to fill the cache for the flattened version, too.
-  AliasCacheEntry* face = probe_alias_cache(flat);
-  if (face->_adr_type == nullptr) {
-    face->_adr_type = flat;
-    face->_index    = idx;
-    assert(alias_type(flat) == alias_type(idx), "flat type must work too");
+    // Might as well try to fill the cache for the flattened version, too.
+    AliasCacheEntry* face = probe_alias_cache(flat);
+    if (face->_adr_type == nullptr) {
+      face->_adr_type = flat;
+      face->_index    = idx;
+      assert(alias_type(flat) == alias_type(idx), "flat type must work too");
+    }
   }
 
   return alias_type(idx);
 }
 
@@ -1880,10 +1950,422 @@
       C->clear_major_progress(); // ensure that major progress is now clear
     }
   }
 }
 
+void Compile::add_inline_type(Node* n) {
+  assert(n->is_InlineType(), "unexpected node");
+  _inline_type_nodes.push(n);
+}
+
+void Compile::remove_inline_type(Node* n) {
+  assert(n->is_InlineType(), "unexpected node");
+  if (_inline_type_nodes.contains(n)) {
+    _inline_type_nodes.remove(n);
+  }
+}
+
+// Does the return value keep otherwise useless inline type allocations alive?
+static bool return_val_keeps_allocations_alive(Node* ret_val) {
+  ResourceMark rm;
+  Unique_Node_List wq;
+  wq.push(ret_val);
+  bool some_allocations = false;
+  for (uint i = 0; i < wq.size(); i++) {
+    Node* n = wq.at(i);
+    if (n->outcnt() > 1) {
+      // Some other use for the allocation
+      return false;
+    } else if (n->is_InlineType()) {
+      wq.push(n->in(1));
+    } else if (n->is_Phi()) {
+      for (uint j = 1; j < n->req(); j++) {
+        wq.push(n->in(j));
+      }
+    } else if (n->is_CheckCastPP() &&
+               n->in(1)->is_Proj() &&
+               n->in(1)->in(0)->is_Allocate()) {
+      some_allocations = true;
+    } else if (n->is_CheckCastPP()) {
+      wq.push(n->in(1));
+    }
+  }
+  return some_allocations;
+}
+
+void Compile::process_inline_types(PhaseIterGVN &igvn, bool remove) {
+  // Make sure that the return value does not keep an otherwise unused allocation alive
+  if (tf()->returns_inline_type_as_fields()) {
+    Node* ret = nullptr;
+    for (uint i = 1; i < root()->req(); i++) {
+      Node* in = root()->in(i);
+      if (in->Opcode() == Op_Return) {
+        assert(ret == nullptr, "only one return");
+        ret = in;
+      }
+    }
+    if (ret != nullptr) {
+      Node* ret_val = ret->in(TypeFunc::Parms);
+      if (igvn.type(ret_val)->isa_oopptr() &&
+          return_val_keeps_allocations_alive(ret_val)) {
+        igvn.replace_input_of(ret, TypeFunc::Parms, InlineTypeNode::tagged_klass(igvn.type(ret_val)->inline_klass(), igvn));
+        assert(ret_val->outcnt() == 0, "should be dead now");
+        igvn.remove_dead_node(ret_val);
+      }
+    }
+  }
+  if (_inline_type_nodes.length() == 0) {
+    return;
+  }
+  // Scalarize inline types in safepoint debug info.
+  // Delay this until all inlining is over to avoid getting inconsistent debug info.
+  set_scalarize_in_safepoints(true);
+  for (int i = _inline_type_nodes.length()-1; i >= 0; i--) {
+    _inline_type_nodes.at(i)->as_InlineType()->make_scalar_in_safepoints(&igvn);
+  }
+  if (remove) {
+    // Remove inline type nodes by replacing them with their oop input
+    while (_inline_type_nodes.length() > 0) {
+      InlineTypeNode* vt = _inline_type_nodes.pop()->as_InlineType();
+      if (vt->outcnt() == 0) {
+        igvn.remove_dead_node(vt);
+        continue;
+      }
+      for (DUIterator i = vt->outs(); vt->has_out(i); i++) {
+        DEBUG_ONLY(bool must_be_buffered = false);
+        Node* u = vt->out(i);
+        // Check if any users are blackholes. If so, rewrite them to use either the
+        // allocated buffer, or individual components, instead of the inline type node
+        // that goes away.
+        if (u->is_Blackhole()) {
+          BlackholeNode* bh = u->as_Blackhole();
+
+          // Unlink the old input
+          int idx = bh->find_edge(vt);
+          assert(idx != -1, "The edge should be there");
+          bh->del_req(idx);
+          --i;
+
+          if (vt->is_allocated(&igvn)) {
+            // Already has the allocated instance, blackhole that
+            bh->add_req(vt->get_oop());
+          } else {
+            // Not allocated yet, blackhole the components
+            for (uint c = 0; c < vt->field_count(); c++) {
+              bh->add_req(vt->field_value(c));
+            }
+          }
+
+          // Node modified, record for IGVN
+          igvn.record_for_igvn(bh);
+        }
+#ifdef ASSERT
+        // Verify that inline type is buffered when replacing by oop
+        else if (u->is_InlineType()) {
+          // InlineType uses don't need buffering because they are about to be replaced as well
+        } else if (u->is_Phi()) {
+          // TODO 8302217 Remove this once InlineTypeNodes are reliably pushed through
+        } else {
+          must_be_buffered = true;
+        }
+        if (must_be_buffered && !vt->is_allocated(&igvn)) {
+          vt->dump(0);
+          u->dump(0);
+          assert(false, "Should have been buffered");
+        }
+#endif
+      }
+      igvn.replace_node(vt, vt->get_oop());
+    }
+  }
+  igvn.optimize();
+}
+
+void Compile::adjust_flat_array_access_aliases(PhaseIterGVN& igvn) {
+  if (!_has_flat_accesses) {
+    return;
+  }
+  // Initially, all flat array accesses share the same slice to
+  // keep dependencies with Object[] array accesses (that could be
+  // to a flat array) correct. We're done with parsing so we
+  // now know all flat array accesses in this compile
+  // unit. Let's move flat array accesses to their own slice,
+  // one per element field. This should help memory access
+  // optimizations.
+  ResourceMark rm;
+  Unique_Node_List wq;
+  wq.push(root());
+
+  Node_List mergememnodes;
+  Node_List memnodes;
+
+  // Alias index currently shared by all flat memory accesses
+  int index = get_alias_index(TypeAryPtr::INLINES);
+
+  // Find MergeMem nodes and flat array accesses
+  for (uint i = 0; i < wq.size(); i++) {
+    Node* n = wq.at(i);
+    if (n->is_Mem()) {
+      const TypePtr* adr_type = nullptr;
+      if (n->Opcode() == Op_StoreCM) {
+        adr_type = get_adr_type(get_alias_index(n->in(MemNode::OopStore)->adr_type()));
+      } else {
+        adr_type = get_adr_type(get_alias_index(n->adr_type()));
+      }
+      if (adr_type == TypeAryPtr::INLINES) {
+        memnodes.push(n);
+      }
+    } else if (n->is_MergeMem()) {
+      MergeMemNode* mm = n->as_MergeMem();
+      if (mm->memory_at(index) != mm->base_memory()) {
+        mergememnodes.push(n);
+      }
+    }
+    for (uint j = 0; j < n->req(); j++) {
+      Node* m = n->in(j);
+      if (m != nullptr) {
+        wq.push(m);
+      }
+    }
+  }
+
+  if (memnodes.size() > 0) {
+    _flat_accesses_share_alias = false;
+
+    // We are going to change the slice for the flat array
+    // accesses so we need to clear the cache entries that refer to
+    // them.
+    for (uint i = 0; i < AliasCacheSize; i++) {
+      AliasCacheEntry* ace = &_alias_cache[i];
+      if (ace->_adr_type != nullptr &&
+          ace->_adr_type->is_flat()) {
+        ace->_adr_type = nullptr;
+        ace->_index = (i != 0) ? 0 : AliasIdxTop; // Make sure the nullptr adr_type resolves to AliasIdxTop
+      }
+    }
+
+    // Find what aliases we are going to add
+    int start_alias = num_alias_types()-1;
+    int stop_alias = 0;
+
+    for (uint i = 0; i < memnodes.size(); i++) {
+      Node* m = memnodes.at(i);
+      const TypePtr* adr_type = nullptr;
+      if (m->Opcode() == Op_StoreCM) {
+        adr_type = m->in(MemNode::OopStore)->adr_type();
+        if (adr_type != TypeAryPtr::INLINES) {
+          // store was optimized out and we lost track of the adr_type
+          Node* clone = new StoreCMNode(m->in(MemNode::Control), m->in(MemNode::Memory), m->in(MemNode::Address),
+                                        m->adr_type(), m->in(MemNode::ValueIn), m->in(MemNode::OopStore),
+                                        get_alias_index(adr_type));
+          igvn.register_new_node_with_optimizer(clone);
+          igvn.replace_node(m, clone);
+        }
+      } else {
+        adr_type = m->adr_type();
+#ifdef ASSERT
+        m->as_Mem()->set_adr_type(adr_type);
+#endif
+      }
+      int idx = get_alias_index(adr_type);
+      start_alias = MIN2(start_alias, idx);
+      stop_alias = MAX2(stop_alias, idx);
+    }
+
+    assert(stop_alias >= start_alias, "should have expanded aliases");
+
+    Node_Stack stack(0);
+#ifdef ASSERT
+    VectorSet seen(Thread::current()->resource_area());
+#endif
+    // Now let's fix the memory graph so each flat array access
+    // is moved to the right slice. Start from the MergeMem nodes.
+    uint last = unique();
+    for (uint i = 0; i < mergememnodes.size(); i++) {
+      MergeMemNode* current = mergememnodes.at(i)->as_MergeMem();
+      Node* n = current->memory_at(index);
+      MergeMemNode* mm = nullptr;
+      do {
+        // Follow memory edges through memory accesses, phis and
+        // narrow membars and push nodes on the stack. Once we hit
+        // bottom memory, we pop element off the stack one at a
+        // time, in reverse order, and move them to the right slice
+        // by changing their memory edges.
+        if ((n->is_Phi() && n->adr_type() != TypePtr::BOTTOM) || n->is_Mem() || n->adr_type() == TypeAryPtr::INLINES) {
+          assert(!seen.test_set(n->_idx), "");
+          // Uses (a load for instance) will need to be moved to the
+          // right slice as well and will get a new memory state
+          // that we don't know yet. The use could also be the
+          // backedge of a loop. We put a place holder node between
+          // the memory node and its uses. We replace that place
+          // holder with the correct memory state once we know it,
+          // i.e. when nodes are popped off the stack. Using the
+          // place holder make the logic work in the presence of
+          // loops.
+          if (n->outcnt() > 1) {
+            Node* place_holder = nullptr;
+            assert(!n->has_out_with(Op_Node), "");
+            for (DUIterator k = n->outs(); n->has_out(k); k++) {
+              Node* u = n->out(k);
+              if (u != current && u->_idx < last) {
+                bool success = false;
+                for (uint l = 0; l < u->req(); l++) {
+                  if (!stack.is_empty() && u == stack.node() && l == stack.index()) {
+                    continue;
+                  }
+                  Node* in = u->in(l);
+                  if (in == n) {
+                    if (place_holder == nullptr) {
+                      place_holder = new Node(1);
+                      place_holder->init_req(0, n);
+                    }
+                    igvn.replace_input_of(u, l, place_holder);
+                    success = true;
+                  }
+                }
+                if (success) {
+                  --k;
+                }
+              }
+            }
+          }
+          if (n->is_Phi()) {
+            stack.push(n, 1);
+            n = n->in(1);
+          } else if (n->is_Mem()) {
+            stack.push(n, n->req());
+            n = n->in(MemNode::Memory);
+          } else {
+            assert(n->is_Proj() && n->in(0)->Opcode() == Op_MemBarCPUOrder, "");
+            stack.push(n, n->req());
+            n = n->in(0)->in(TypeFunc::Memory);
+          }
+        } else {
+          assert(n->adr_type() == TypePtr::BOTTOM || (n->Opcode() == Op_Node && n->_idx >= last) || (n->is_Proj() && n->in(0)->is_Initialize()), "");
+          // Build a new MergeMem node to carry the new memory state
+          // as we build it. IGVN should fold extraneous MergeMem
+          // nodes.
+          mm = MergeMemNode::make(n);
+          igvn.register_new_node_with_optimizer(mm);
+          while (stack.size() > 0) {
+            Node* m = stack.node();
+            uint idx = stack.index();
+            if (m->is_Mem()) {
+              // Move memory node to its new slice
+              const TypePtr* adr_type = m->adr_type();
+              int alias = get_alias_index(adr_type);
+              Node* prev = mm->memory_at(alias);
+              igvn.replace_input_of(m, MemNode::Memory, prev);
+              mm->set_memory_at(alias, m);
+            } else if (m->is_Phi()) {
+              // We need as many new phis as there are new aliases
+              igvn.replace_input_of(m, idx, mm);
+              if (idx == m->req()-1) {
+                Node* r = m->in(0);
+                for (uint j = (uint)start_alias; j <= (uint)stop_alias; j++) {
+                  const TypePtr* adr_type = get_adr_type(j);
+                  if (!adr_type->isa_aryptr() || !adr_type->is_flat() || j == (uint)index) {
+                    continue;
+                  }
+                  Node* phi = new PhiNode(r, Type::MEMORY, get_adr_type(j));
+                  igvn.register_new_node_with_optimizer(phi);
+                  for (uint k = 1; k < m->req(); k++) {
+                    phi->init_req(k, m->in(k)->as_MergeMem()->memory_at(j));
+                  }
+                  mm->set_memory_at(j, phi);
+                }
+                Node* base_phi = new PhiNode(r, Type::MEMORY, TypePtr::BOTTOM);
+                igvn.register_new_node_with_optimizer(base_phi);
+                for (uint k = 1; k < m->req(); k++) {
+                  base_phi->init_req(k, m->in(k)->as_MergeMem()->base_memory());
+                }
+                mm->set_base_memory(base_phi);
+              }
+            } else {
+              // This is a MemBarCPUOrder node from
+              // Parse::array_load()/Parse::array_store(), in the
+              // branch that handles flat arrays hidden under
+              // an Object[] array. We also need one new membar per
+              // new alias to keep the unknown access that the
+              // membars protect properly ordered with accesses to
+              // known flat array.
+              assert(m->is_Proj(), "projection expected");
+              Node* ctrl = m->in(0)->in(TypeFunc::Control);
+              igvn.replace_input_of(m->in(0), TypeFunc::Control, top());
+              for (uint j = (uint)start_alias; j <= (uint)stop_alias; j++) {
+                const TypePtr* adr_type = get_adr_type(j);
+                if (!adr_type->isa_aryptr() || !adr_type->is_flat() || j == (uint)index) {
+                  continue;
+                }
+                MemBarNode* mb = new MemBarCPUOrderNode(this, j, nullptr);
+                igvn.register_new_node_with_optimizer(mb);
+                Node* mem = mm->memory_at(j);
+                mb->init_req(TypeFunc::Control, ctrl);
+                mb->init_req(TypeFunc::Memory, mem);
+                ctrl = new ProjNode(mb, TypeFunc::Control);
+                igvn.register_new_node_with_optimizer(ctrl);
+                mem = new ProjNode(mb, TypeFunc::Memory);
+                igvn.register_new_node_with_optimizer(mem);
+                mm->set_memory_at(j, mem);
+              }
+              igvn.replace_node(m->in(0)->as_Multi()->proj_out(TypeFunc::Control), ctrl);
+            }
+            if (idx < m->req()-1) {
+              idx += 1;
+              stack.set_index(idx);
+              n = m->in(idx);
+              break;
+            }
+            // Take care of place holder nodes
+            if (m->has_out_with(Op_Node)) {
+              Node* place_holder = m->find_out_with(Op_Node);
+              if (place_holder != nullptr) {
+                Node* mm_clone = mm->clone();
+                igvn.register_new_node_with_optimizer(mm_clone);
+                Node* hook = new Node(1);
+                hook->init_req(0, mm);
+                igvn.replace_node(place_holder, mm_clone);
+                hook->destruct(&igvn);
+              }
+              assert(!m->has_out_with(Op_Node), "place holder should be gone now");
+            }
+            stack.pop();
+          }
+        }
+      } while(stack.size() > 0);
+      // Fix the memory state at the MergeMem we started from
+      igvn.rehash_node_delayed(current);
+      for (uint j = (uint)start_alias; j <= (uint)stop_alias; j++) {
+        const TypePtr* adr_type = get_adr_type(j);
+        if (!adr_type->isa_aryptr() || !adr_type->is_flat()) {
+          continue;
+        }
+        current->set_memory_at(j, mm);
+      }
+      current->set_memory_at(index, current->base_memory());
+    }
+    igvn.optimize();
+  }
+  print_method(PHASE_SPLIT_INLINES_ARRAY, 2);
+#ifdef ASSERT
+  if (!_flat_accesses_share_alias) {
+    wq.clear();
+    wq.push(root());
+    for (uint i = 0; i < wq.size(); i++) {
+      Node* n = wq.at(i);
+      assert(n->adr_type() != TypeAryPtr::INLINES, "should have been removed from the graph");
+      for (uint j = 0; j < n->req(); j++) {
+        Node* m = n->in(j);
+        if (m != nullptr) {
+          wq.push(m);
+        }
+      }
+    }
+  }
+#endif
+}
+
 void Compile::record_unstable_if_trap(UnstableIfTrap* trap) {
   if (OptimizeUnstableIf) {
     _unstable_if_traps.append(trap);
   }
 }
@@ -1955,11 +2437,11 @@
 
       for (int i = 0; i < len; i++) {
         Node* local = unc->local(jvms, i);
         // kill local using the liveness of next_bci.
         // give up when the local looks like an operand to secure reexecution.
-        if (!live_locals.at(i) && !local->is_top() && local != lhs && local!= rhs) {
+        if (!live_locals.at(i) && !local->is_top() && local != lhs && local != rhs) {
           uint idx = jvms->locoff() + i;
 #ifdef ASSERT
           if (PrintOpto && Verbose) {
             tty->print("[unstable_if] kill local#%d: ", idx);
             local->dump();
@@ -1970,11 +2452,11 @@
           modified = true;
         }
       }
     }
 
-    // keep the mondified trap for late query
+    // keep the modified trap for late query
     if (modified) {
       trap->set_modified();
     } else {
       _unstable_if_traps.delete_at(i);
     }
@@ -2165,11 +2647,14 @@
   // "inlining_incrementally() == false" is used to signal that no inlining is allowed
   // (see LateInlineVirtualCallGenerator::do_late_inline_check() for details).
   // Tracking and verification of modified nodes is disabled by setting "_modified_nodes == nullptr"
   // as if "inlining_incrementally() == true" were set.
   assert(inlining_incrementally() == false, "not allowed");
-  assert(_modified_nodes == nullptr, "not allowed");
+#ifdef ASSERT
+  Unique_Node_List* modified_nodes = _modified_nodes;
+  _modified_nodes = nullptr;
+#endif
   assert(_late_inlines.length() > 0, "sanity");
 
   while (_late_inlines.length() > 0) {
     igvn_worklist()->ensure_empty(); // should be done with igvn
 
@@ -2178,10 +2663,11 @@
     }
     if (failing())  return;
 
     inline_incrementally_cleanup(igvn);
   }
+  DEBUG_ONLY( _modified_nodes = modified_nodes; )
 }
 
 bool Compile::optimize_loops(PhaseIterGVN& igvn, LoopOptsMode mode) {
   if (_loop_opts_cnt > 0) {
     while (major_progress() && (_loop_opts_cnt > 0)) {
@@ -2324,10 +2810,15 @@
 
   // Now that all inlining is over and no PhaseRemoveUseless will run, cut edge from root to loop
   // safepoints
   remove_root_to_sfpts_edges(igvn);
 
+  // Process inline type nodes now that all inlining is over
+  process_inline_types(igvn);
+
+  adjust_flat_array_access_aliases(igvn);
+
   if (failing())  return;
 
   // Perform escape analysis
   if (do_escape_analysis() && ConnectionGraph::has_candidates(this)) {
     if (has_loops()) {
@@ -2447,20 +2938,32 @@
 
 #ifdef ASSERT
   bs->verify_gc_barriers(this, BarrierSetC2::BeforeMacroExpand);
 #endif
 
+  assert(_late_inlines.length() == 0 || IncrementalInlineMH || IncrementalInlineVirtual, "not empty");
+
+  if (_late_inlines.length() > 0) {
+    // More opportunities to optimize virtual and MH calls.
+    // Though it's maybe too late to perform inlining, strength-reducing them to direct calls is still an option.
+    process_late_inline_calls_no_inline(igvn);
+  }
+
   {
     TracePhase tp("macroExpand", &timers[_t_macroExpand]);
     PhaseMacroExpand  mex(igvn);
     if (mex.expand_macro_nodes()) {
       assert(failing(), "must bail out w/ explicit message");
       return;
     }
     print_method(PHASE_MACRO_EXPANSION, 2);
   }
 
+  // Process inline type nodes again and remove them. From here
+  // on we don't need to keep track of field values anymore.
+  process_inline_types(igvn, /* remove= */ true);
+
   {
     TracePhase tp("barrierExpand", &timers[_t_barrierExpand]);
     if (bs->expand_barriers(this, igvn)) {
       assert(failing(), "must bail out w/ explicit message");
       return;
@@ -2473,21 +2976,13 @@
     igvn.optimize();
     if (failing()) return;
   }
 
   DEBUG_ONLY( _modified_nodes = nullptr; )
+  DEBUG_ONLY( _late_inlines.clear(); )
 
   assert(igvn._worklist.size() == 0, "not empty");
-
-  assert(_late_inlines.length() == 0 || IncrementalInlineMH || IncrementalInlineVirtual, "not empty");
-
-  if (_late_inlines.length() > 0) {
-    // More opportunities to optimize virtual and MH calls.
-    // Though it's maybe too late to perform inlining, strength-reducing them to direct calls is still an option.
-    process_late_inline_calls_no_inline(igvn);
-    if (failing())  return;
-  }
  } // (End scope of igvn; run destructor if necessary for asserts.)
 
  check_no_dead_use();
 
  process_print_inlining();
@@ -3111,10 +3606,11 @@
       mem = prev->in(MemNode::Memory);
     }
   }
 }
 
+
 //------------------------------final_graph_reshaping_impl----------------------
 // Implement items 1-5 from final_graph_reshaping below.
 void Compile::final_graph_reshaping_impl(Node *n, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes) {
 
   if ( n->outcnt() == 0 ) return; // dead node
@@ -3263,11 +3759,26 @@
   case Op_StoreCM:
     {
       // Convert OopStore dependence into precedence edge
       Node* prec = n->in(MemNode::OopStore);
       n->del_req(MemNode::OopStore);
-      n->add_prec(prec);
+      if (prec->is_MergeMem()) {
+        MergeMemNode* mm = prec->as_MergeMem();
+        Node* base = mm->base_memory();
+        for (int i = AliasIdxRaw + 1; i < num_alias_types(); i++) {
+          const TypePtr* adr_type = get_adr_type(i);
+          if (adr_type->is_flat()) {
+            Node* m = mm->memory_at(i);
+            n->add_prec(m);
+          }
+        }
+        if (mm->outcnt() == 0) {
+          mm->disconnect_inputs(this);
+        }
+      } else {
+        n->add_prec(prec);
+      }
       eliminate_redundant_card_marks(n);
     }
 
     // fall through
 
@@ -3884,10 +4395,17 @@
       Node* cmp = new CmpLNode(andl, n->in(2));
       n->subsume_by(cmp, this);
     }
     break;
   }
+#ifdef ASSERT
+  case Op_InlineType: {
+    n->dump(-1);
+    assert(false, "inline type node was not removed");
+    break;
+  }
+#endif
   default:
     assert(!n->is_Call(), "");
     assert(!n->is_Mem(), "");
     assert(nop != Op_ProfileBoolean, "should be eliminated during IGVN");
     break;
@@ -4263,20 +4781,20 @@
   if (holder->is_being_initialized()) {
     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() ||
-          accessing_method->is_object_initializer() ||
+      if (accessing_method->is_class_initializer() ||
+          accessing_method->is_object_constructor() ||
           accessing_method->is_static()) {
         return false;
       }
     } else if (accessing_method->holder()->is_subclass_of(holder)) {
       // Access from a subclass. The barrier can be elided only when access happens in <clinit>.
       // In case of <init> or a static method, the barrier is on the subclass is not enough:
       // child class can become fully initialized while its parent class is still being initialized.
-      if (accessing_method->is_static_initializer()) {
+      if (accessing_method->is_class_initializer()) {
         return false;
       }
     }
     ciMethod* root = method(); // the root method of compilation
     if (root != accessing_method) {
@@ -4328,13 +4846,14 @@
         }
         assert(cnt == 0, "Mismatched edge count.");
       } else if (in == nullptr) {
         assert(i == 0 || i >= n->req() ||
                n->is_Region() || n->is_Phi() || n->is_ArrayCopy() ||
+               (n->is_Allocate() && i >= AllocateNode::InlineType) ||
                (n->is_Unlock() && i == (n->req() - 1)) ||
                (n->is_MemBar() && i == 5), // the precedence edge to a membar can be removed during macro node expansion
-              "only region, phi, arraycopy, unlock or membar nodes have null data edges");
+              "only region, phi, arraycopy, allocate, unlock or membar nodes have null data edges");
       } else {
         assert(in->is_top(), "sanity");
         // Nothing to check.
       }
     }
@@ -4460,10 +4979,19 @@
 
   const Type* superelem = superk;
   if (superk->isa_aryklassptr()) {
     int ignored;
     superelem = superk->is_aryklassptr()->base_element_type(ignored);
+
+    // TODO 8325106 Fix comment
+    // Do not fold the subtype check to an array klass pointer comparison for [V? arrays.
+    // [QMyValue is a subtype of [LMyValue but the klass for [QMyValue is not equal to
+    // the klass for [LMyValue. Perform a full test.
+    if (!superk->is_aryklassptr()->is_null_free() && superk->is_aryklassptr()->elem()->isa_instklassptr() &&
+        superk->is_aryklassptr()->elem()->is_instklassptr()->instance_klass()->is_inlinetype()) {
+      return SSC_full_test;
+    }
   }
 
   if (superelem->isa_instklassptr()) {
     ciInstanceKlass* ik = superelem->is_instklassptr()->instance_klass();
     if (!ik->has_subklass()) {
@@ -5020,10 +5548,31 @@
     igvn.check_no_speculative_types();
 #endif
   }
 }
 
+Node* Compile::optimize_acmp(PhaseGVN* phase, Node* a, Node* b) {
+  const TypeInstPtr* ta = phase->type(a)->isa_instptr();
+  const TypeInstPtr* tb = phase->type(b)->isa_instptr();
+  if (!EnableValhalla || ta == nullptr || tb == nullptr ||
+      ta->is_zero_type() || tb->is_zero_type() ||
+      !ta->can_be_inline_type() || !tb->can_be_inline_type()) {
+    // Use old acmp if one operand is null or not an inline type
+    return new CmpPNode(a, b);
+  } else if (ta->is_inlinetypeptr() || tb->is_inlinetypeptr()) {
+    // We know that one operand is an inline type. Therefore,
+    // new acmp will only return true if both operands are nullptr.
+    // Check if both operands are null by or'ing the oops.
+    a = phase->transform(new CastP2XNode(nullptr, a));
+    b = phase->transform(new CastP2XNode(nullptr, b));
+    a = phase->transform(new OrXNode(a, b));
+    return new CmpXNode(a, phase->MakeConX(0));
+  }
+  // Use new acmp
+  return nullptr;
+}
+
 // Auxiliary methods to support randomized stressing/fuzzing.
 
 int Compile::random() {
   _stress_seed = os::next_random(_stress_seed);
   return static_cast<int>(_stress_seed);
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
@@ -49,10 +49,11 @@
 class AbstractLockNode;
 class AddPNode;
 class Block;
 class Bundle;
 class CallGenerator;
+class CallNode;
 class CallStaticJavaNode;
 class CloneMap;
 class CompilationFailureInfo;
 class ConnectionGraph;
 class IdealGraphPrinter;
@@ -91,10 +92,11 @@
 class TypeFunc;
 class TypeVect;
 class Type_Array;
 class Unique_Node_List;
 class UnstableIfTrap;
+class InlineTypeNode;
 class nmethod;
 class Node_Stack;
 struct Final_Reshape_Counts;
 class VerifyMeetResult;
 
@@ -326,10 +328,11 @@
   bool                  _has_split_ifs;         // True if the method _may_ have some split-if
   bool                  _has_unsafe_access;     // True if the method _may_ produce faults in unsafe loads or stores.
   bool                  _has_stringbuilder;     // True StringBuffers or StringBuilders are allocated
   bool                  _has_boxed_value;       // True if a boxed object is allocated
   bool                  _has_reserved_stack_access; // True if the method or an inlined method is annotated with ReservedStackAccess
+  bool                  _has_circular_inline_type; // True if method loads an inline type with a circular, non-flat field
   uint                  _max_vector_size;       // Maximum size of generated vectors
   bool                  _clear_upper_avx;       // Clear upper bits of ymm registers using vzeroupper
   uint                  _trap_hist[trapHistLength];  // Cumulative traps
   bool                  _trap_can_recompile;    // Have we emitted a recompiling trap?
   uint                  _decompile_count;       // Cumulative decompilation counts.
@@ -353,10 +356,13 @@
   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                  _clinit_barrier_on_entry; // True if clinit barrier is needed on nmethod entry
   RTMState              _rtm_state;             // State of Restricted Transactional Memory usage
   int                   _loop_opts_cnt;         // loop opts round
+  bool                  _has_flat_accesses;     // Any known flat array accesses?
+  bool                  _flat_accesses_share_alias; // Initially all flat array share a single slice
+  bool                  _scalarize_in_safepoints; // Scalarize inline types in safepoint debug info
   uint                  _stress_seed;           // Seed for stress testing
 
   // Compilation environment.
   Arena                 _comp_arena;            // Arena with lifetime equivalent to Compile
   void*                 _barrier_set_state;     // Potential GC barrier state for Compile
@@ -369,10 +375,11 @@
   GrowableArray<Node*>  _macro_nodes;           // List of nodes which need to be expanded before matching.
   GrowableArray<ParsePredicateNode*> _parse_predicates; // List of Parse Predicates.
   GrowableArray<Node*>  _template_assertion_predicate_opaqs; // List of Opaque4 nodes for Template Assertion Predicates.
   GrowableArray<Node*>  _expensive_nodes;       // List of nodes that are expensive to compute and that we'd better not let the GVN freely common
   GrowableArray<Node*>  _for_post_loop_igvn;    // List of nodes for IGVN after loop opts are over
+  GrowableArray<Node*>  _inline_type_nodes;     // List of InlineType nodes
   GrowableArray<UnstableIfTrap*> _unstable_if_traps;        // List of ifnodes after IGVN
   GrowableArray<Node_List*> _coarsened_locks;   // List of coarsened Lock and Unlock nodes
   ConnectionGraph*      _congraph;
 #ifndef PRODUCT
   IdealGraphPrinter*    _igv_printer;
@@ -635,10 +642,12 @@
   void          set_has_stringbuilder(bool z)   { _has_stringbuilder = z; }
   bool              has_boxed_value() const     { return _has_boxed_value; }
   void          set_has_boxed_value(bool z)     { _has_boxed_value = z; }
   bool              has_reserved_stack_access() const { return _has_reserved_stack_access; }
   void          set_has_reserved_stack_access(bool z) { _has_reserved_stack_access = z; }
+  bool              has_circular_inline_type() const { return _has_circular_inline_type; }
+  void          set_has_circular_inline_type(bool z) { _has_circular_inline_type = z; }
   uint              max_vector_size() const     { return _max_vector_size; }
   void          set_max_vector_size(uint s)     { _max_vector_size = s; }
   bool              clear_upper_avx() const     { return _clear_upper_avx; }
   void          set_clear_upper_avx(bool s)     { _clear_upper_avx = s; }
   void          set_trap_count(uint r, uint c)  { assert(r < trapHistLength, "oob");        _trap_hist[r] = c; }
@@ -671,10 +680,20 @@
   bool          profile_rtm() const              { return _rtm_state == ProfileRTM; }
   uint              max_node_limit() const       { return (uint)_max_node_limit; }
   void          set_max_node_limit(uint n)       { _max_node_limit = n; }
   bool              clinit_barrier_on_entry()       { return _clinit_barrier_on_entry; }
   void          set_clinit_barrier_on_entry(bool z) { _clinit_barrier_on_entry = z; }
+  void          set_flat_accesses()              { _has_flat_accesses = true; }
+  bool          flat_accesses_share_alias() const { return _flat_accesses_share_alias; }
+  void          set_flat_accesses_share_alias(bool z) { _flat_accesses_share_alias = z; }
+  bool          scalarize_in_safepoints() const { return _scalarize_in_safepoints; }
+  void          set_scalarize_in_safepoints(bool z) { _scalarize_in_safepoints = z; }
+
+  // Support for scalarized inline type calling convention
+  bool              has_scalarized_args() const  { return _method != nullptr && _method->has_scalarized_args(); }
+  bool              needs_stack_repair()  const  { return _method != nullptr && _method->get_Method()->c2_needs_stack_repair(); }
+
   bool              has_monitors() const         { return _has_monitors; }
   void          set_has_monitors(bool v)         { _has_monitors = v; }
 
   // check the CompilerOracle for special behaviours for this compile
   bool          method_has_option(enum CompileCommand option) {
@@ -787,10 +806,17 @@
 
   void record_for_post_loop_opts_igvn(Node* n);
   void remove_from_post_loop_opts_igvn(Node* n);
   void process_for_post_loop_opts_igvn(PhaseIterGVN& igvn);
 
+  // Keep track of inline type nodes for later processing
+  void add_inline_type(Node* n);
+  void remove_inline_type(Node* n);
+  void process_inline_types(PhaseIterGVN &igvn, bool remove = false);
+
+  void adjust_flat_array_access_aliases(PhaseIterGVN& igvn);
+
   void record_unstable_if_trap(UnstableIfTrap* trap);
   bool remove_unstable_if_trap(CallStaticJavaNode* unc, bool yield);
   void remove_useless_unstable_if_traps(Unique_Node_List &useful);
   void process_for_unstable_if_traps(PhaseIterGVN& igvn);
 
@@ -931,15 +957,15 @@
     _last_tf_m = m;
     _last_tf = tf;
   }
 
   AliasType*        alias_type(int                idx)  { assert(idx < num_alias_types(), "oob"); return _alias_types[idx]; }
-  AliasType*        alias_type(const TypePtr* adr_type, ciField* field = nullptr) { return find_alias_type(adr_type, false, field); }
+  AliasType*        alias_type(const TypePtr* adr_type, ciField* field = nullptr, bool uncached = false) { return find_alias_type(adr_type, false, field, uncached); }
   bool         have_alias_type(const TypePtr* adr_type);
   AliasType*        alias_type(ciField*         field);
 
-  int               get_alias_index(const TypePtr* at)  { return alias_type(at)->index(); }
+  int               get_alias_index(const TypePtr* at, bool uncached = false) { return alias_type(at, nullptr, uncached)->index(); }
   const TypePtr*    get_adr_type(uint aidx)             { return alias_type(aidx)->adr_type(); }
   int               get_general_index(uint aidx)        { return alias_type(aidx)->general_index(); }
 
   // Building nodes
   void              rethrow_exceptions(JVMState* jvms);
@@ -1177,11 +1203,11 @@
 
   // Management of the AliasType table.
   void grow_alias_types();
   AliasCacheEntry* probe_alias_cache(const TypePtr* adr_type);
   const TypePtr *flatten_alias_type(const TypePtr* adr_type) const;
-  AliasType* find_alias_type(const TypePtr* adr_type, bool no_create, ciField* field);
+  AliasType* find_alias_type(const TypePtr* adr_type, bool no_create, ciField* field, bool uncached = false);
 
   void verify_top(Node*) const PRODUCT_RETURN;
 
   // Intrinsic setup.
   CallGenerator* make_vm_intrinsic(ciMethod* m, bool is_virtual);          // constructor
@@ -1253,11 +1279,13 @@
                               Node* ctrl = nullptr);
 
   // Convert integer value to a narrowed long type dependent on ctrl (for example, a range check)
   static Node* constrained_convI2L(PhaseGVN* phase, Node* value, const TypeInt* itype, Node* ctrl, bool carry_dependency = false);
 
-  // Auxiliary methods for randomized fuzzing/stressing
+  Node* optimize_acmp(PhaseGVN* phase, Node* a, Node* b);
+
+  // Auxiliary method for randomized fuzzing/stressing
   int random();
   bool randomized_select(int count);
 
   // supporting clone_map
   CloneMap&     clone_map();
diff a/src/hotspot/share/opto/convertnode.cpp b/src/hotspot/share/opto/convertnode.cpp
--- a/src/hotspot/share/opto/convertnode.cpp
+++ b/src/hotspot/share/opto/convertnode.cpp
@@ -25,10 +25,11 @@
 #include "precompiled.hpp"
 #include "opto/addnode.hpp"
 #include "opto/castnode.hpp"
 #include "opto/connode.hpp"
 #include "opto/convertnode.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/matcher.hpp"
 #include "opto/movenode.hpp"
 #include "opto/phaseX.hpp"
 #include "opto/subnode.hpp"
 #include "runtime/stubRoutines.hpp"
@@ -62,11 +63,18 @@
   const TypeInt *ti = t->is_int();
   if( ti->_hi < 0 || ti->_lo > 0 ) return TypeInt::ONE;
   return TypeInt::BOOL;
 }
 
+//------------------------------Ideal------------------------------------------
 Node* Conv2BNode::Ideal(PhaseGVN* phase, bool can_reshape) {
+  if (in(1)->is_InlineType()) {
+    // Null checking a scalarized but nullable inline type. Check the IsInit
+    // input instead of the oop input to avoid keeping buffer allocations alive.
+    set_req_X(1, in(1)->as_InlineType()->get_is_init(), phase);
+    return this;
+  }
   if (!Matcher::match_rule_supported(Op_Conv2B)) {
     if (phase->C->post_loop_opts_phase()) {
       // Get type of comparison to make
       const Type* t = phase->type(in(1));
       Node* cmp = nullptr;
diff a/src/hotspot/share/opto/divnode.cpp b/src/hotspot/share/opto/divnode.cpp
--- a/src/hotspot/share/opto/divnode.cpp
+++ b/src/hotspot/share/opto/divnode.cpp
@@ -1385,11 +1385,11 @@
   return divmod;
 }
 
 //------------------------------match------------------------------------------
 // return result(s) along with their RegMask info
-Node *DivModINode::match( const ProjNode *proj, const Matcher *match ) {
+Node *DivModINode::match(const ProjNode *proj, const Matcher *match, const RegMask* mask) {
   uint ideal_reg = proj->ideal_reg();
   RegMask rm;
   if (proj->_con == div_proj_num) {
     rm = match->divI_proj_mask();
   } else {
@@ -1400,11 +1400,11 @@
 }
 
 
 //------------------------------match------------------------------------------
 // return result(s) along with their RegMask info
-Node *DivModLNode::match( const ProjNode *proj, const Matcher *match ) {
+Node *DivModLNode::match(const ProjNode *proj, const Matcher *match, const RegMask* mask) {
   uint ideal_reg = proj->ideal_reg();
   RegMask rm;
   if (proj->_con == div_proj_num) {
     rm = match->divL_proj_mask();
   } else {
@@ -1438,11 +1438,11 @@
   return divmod;
 }
 
 //------------------------------match------------------------------------------
 // return result(s) along with their RegMask info
-Node* UDivModINode::match( const ProjNode *proj, const Matcher *match ) {
+Node* UDivModINode::match(const ProjNode* proj, const Matcher* match, const RegMask* mask) {
   uint ideal_reg = proj->ideal_reg();
   RegMask rm;
   if (proj->_con == div_proj_num) {
     rm = match->divI_proj_mask();
   } else {
@@ -1453,11 +1453,11 @@
 }
 
 
 //------------------------------match------------------------------------------
 // return result(s) along with their RegMask info
-Node* UDivModLNode::match( const ProjNode *proj, const Matcher *match ) {
+Node* UDivModLNode::match( const ProjNode* proj, const Matcher* match, const RegMask* mask) {
   uint ideal_reg = proj->ideal_reg();
   RegMask rm;
   if (proj->_con == div_proj_num) {
     rm = match->divL_proj_mask();
   } else {
diff a/src/hotspot/share/opto/divnode.hpp b/src/hotspot/share/opto/divnode.hpp
--- a/src/hotspot/share/opto/divnode.hpp
+++ b/src/hotspot/share/opto/divnode.hpp
@@ -211,11 +211,11 @@
 class DivModINode : public DivModNode {
 public:
   DivModINode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {}
   virtual int Opcode() const;
   virtual const Type *bottom_type() const { return TypeTuple::INT_PAIR; }
-  virtual Node *match( const ProjNode *proj, const Matcher *m );
+  virtual Node *match(const ProjNode *proj, const Matcher *m, const RegMask* mask);
 
   // Make a divmod and associated projections from a div or mod.
   static DivModINode* make(Node* div_or_mod);
 };
 
@@ -224,11 +224,11 @@
 class DivModLNode : public DivModNode {
 public:
   DivModLNode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {}
   virtual int Opcode() const;
   virtual const Type *bottom_type() const { return TypeTuple::LONG_PAIR; }
-  virtual Node *match( const ProjNode *proj, const Matcher *m );
+  virtual Node *match(const ProjNode *proj, const Matcher *m, const RegMask* mask);
 
   // Make a divmod and associated projections from a div or mod.
   static DivModLNode* make(Node* div_or_mod);
 };
 
@@ -238,11 +238,11 @@
 class UDivModINode : public DivModNode {
 public:
   UDivModINode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {}
   virtual int Opcode() const;
   virtual const Type *bottom_type() const { return TypeTuple::INT_PAIR; }
-  virtual Node *match( const ProjNode *proj, const Matcher *m );
+  virtual Node* match(const ProjNode* proj, const Matcher* m, const RegMask* mask);
 
   // Make a divmod and associated projections from a div or mod.
   static UDivModINode* make(Node* div_or_mod);
 };
 
@@ -251,11 +251,11 @@
 class UDivModLNode : public DivModNode {
 public:
   UDivModLNode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {}
   virtual int Opcode() const;
   virtual const Type *bottom_type() const { return TypeTuple::LONG_PAIR; }
-  virtual Node *match( const ProjNode *proj, const Matcher *m );
+  virtual Node* match(const ProjNode* proj, const Matcher* m, const RegMask* mask);
 
   // Make a divmod and associated projections from a div or mod.
   static UDivModLNode* make(Node* div_or_mod);
 };
 
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
@@ -36,10 +36,11 @@
 #include "logging/logStream.hpp"
 #include "opto/addnode.hpp"
 #include "opto/callGenerator.hpp"
 #include "opto/castnode.hpp"
 #include "opto/cfgnode.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/mulnode.hpp"
 #include "opto/parse.hpp"
 #include "opto/rootnode.hpp"
 #include "opto/runtime.hpp"
 #include "opto/subnode.hpp"
@@ -575,10 +576,29 @@
   // Try to get the most accurate receiver type
   ciMethod* callee             = orig_callee;
   int       vtable_index       = Method::invalid_vtable_index;
   bool      call_does_dispatch = false;
 
+  // Detect the call to the object or abstract class constructor at the end of a value constructor to know when we are done initializing the larval
+  if (orig_callee->is_object_constructor() && (orig_callee->holder()->is_abstract() || orig_callee->holder()->is_java_lang_Object()) && stack(sp() - nargs)->is_InlineType()) {
+    assert(method()->is_object_constructor() && (method()->holder()->is_inlinetype() || method()->holder()->is_abstract()), "Unexpected caller");
+    InlineTypeNode* receiver = stack(sp() - nargs)->as_InlineType();
+    // TODO 8325106 re-enable the assert and add the same check for the receiver in the caller map
+    //assert(receiver->is_larval(), "must be larval");
+    InlineTypeNode* clone = receiver->clone_if_required(&_gvn, _map);
+    clone->set_is_larval(false);
+    clone = _gvn.transform(clone)->as_InlineType();
+    replace_in_map(receiver, clone);
+
+    if (_caller->has_method()) {
+      // Get receiver from the caller map and update it in the exit map now that we are done initializing it
+      Node* receiver_in_caller = _caller->map()->argument(_caller, 0);
+      assert(receiver_in_caller->bottom_type()->inline_klass() == receiver->bottom_type()->inline_klass(), "Receiver type mismatch");
+      _exits.map()->replace_edge(receiver_in_caller, clone, &_gvn);
+    }
+  }
+
   // Speculative type of the receiver if any
   ciKlass* speculative_receiver_type = nullptr;
   if (is_virtual_or_interface) {
     Node* receiver_node             = stack(sp() - nargs);
     const TypeOopPtr* receiver_type = _gvn.type(receiver_node)->isa_oopptr();
@@ -595,11 +615,11 @@
     speculative_receiver_type = receiver_type != nullptr ? receiver_type->speculative_type() : nullptr;
   }
 
   // Additional receiver subtype checks for interface calls via invokespecial or invokeinterface.
   ciKlass* receiver_constraint = nullptr;
-  if (iter().cur_bc_raw() == Bytecodes::_invokespecial && !orig_callee->is_object_initializer()) {
+  if (iter().cur_bc_raw() == Bytecodes::_invokespecial && !orig_callee->is_object_constructor()) {
     ciInstanceKlass* calling_klass = method()->holder();
     ciInstanceKlass* sender_klass = calling_klass;
     if (sender_klass->is_interface()) {
       receiver_constraint = sender_klass;
     }
@@ -725,11 +745,11 @@
       // Be careful here with return types.
       if (ctype != rtype) {
         BasicType rt = rtype->basic_type();
         BasicType ct = ctype->basic_type();
         if (ct == T_VOID) {
-          // It's OK for a method  to return a value that is discarded.
+          // It's OK for a method to return a value that is discarded.
           // The discarding does not require any special action from the caller.
           // The Java code knows this, at VerifyType.isNullConversion.
           pop_node(rt);  // whatever it was, pop it
         } else if (rt == T_INT || is_subword_type(rt)) {
           // Nothing.  These cases are handled in lambda form bytecode.
@@ -783,10 +803,25 @@
     }
     BasicType ct = ctype->basic_type();
     if (is_reference_type(ct)) {
       record_profiled_return_for_speculation();
     }
+    if (rtype->is_inlinetype() && !peek()->is_InlineType()) {
+      Node* retnode = pop();
+      retnode = InlineTypeNode::make_from_oop(this, retnode, rtype->as_inline_klass(), !gvn().type(retnode)->maybe_null());
+      push_node(T_OBJECT, retnode);
+    }
+
+    // Did we inline a value class constructor from another value class constructor?
+    if (_caller->has_method() && cg->is_inline() && cg->method()->is_object_constructor() && cg->method()->holder()->is_inlinetype() &&
+        _method->is_object_constructor() && _method->holder()->is_inlinetype() && receiver == _caller->map()->argument(_caller, 0)) {
+      // Update the receiver in the exit map because the constructor call updated it.
+      // MethodLiveness::BasicBlock::compute_gen_kill_single ensures that the receiver in local(0) is still live.
+      assert(local(0)->is_InlineType(), "Unexpected receiver");
+      assert(receiver->bottom_type()->inline_klass() == local(0)->bottom_type()->inline_klass(), "Receiver type mismatch");
+      _exits.map()->replace_edge(receiver, local(0), &_gvn);
+    }
   }
 
   // Restart record of parsing work after possible inlining of call
 #ifndef PRODUCT
   parse_histogram()->set_initial_state(bc());
diff a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp
--- a/src/hotspot/share/opto/escape.cpp
+++ b/src/hotspot/share/opto/escape.cpp
@@ -27,10 +27,11 @@
 #include "compiler/compileLog.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/c2/barrierSetC2.hpp"
 #include "libadt/vectset.hpp"
 #include "memory/allocation.hpp"
+#include "memory/metaspace.hpp"
 #include "memory/resourceArea.hpp"
 #include "opto/c2compiler.hpp"
 #include "opto/arraycopynode.hpp"
 #include "opto/callnode.hpp"
 #include "opto/cfgnode.hpp"
@@ -159,10 +160,20 @@
   // add the phantom_obj only once to them.
   ptnodes_worklist.append(phantom_obj);
   java_objects_worklist.append(phantom_obj);
   for( uint next = 0; next < ideal_nodes.size(); ++next ) {
     Node* n = ideal_nodes.at(next);
+    if ((n->Opcode() == Op_LoadX || n->Opcode() == Op_StoreX) &&
+        !n->in(MemNode::Address)->is_AddP() &&
+        _igvn->type(n->in(MemNode::Address))->isa_oopptr()) {
+      // Load/Store at mark work address is at offset 0 so has no AddP which confuses EA
+      Node* addp = new AddPNode(n->in(MemNode::Address), n->in(MemNode::Address), _igvn->MakeConX(0));
+      _igvn->register_new_node_with_optimizer(addp);
+      _igvn->replace_input_of(n, MemNode::Address, addp);
+      ideal_nodes.push(addp);
+      _nodes.at_put_grow(addp->_idx, nullptr, nullptr);
+    }
     // Create PointsTo nodes and add them to Connection Graph. Called
     // only once per ideal node since ideal_nodes is Unique_Node list.
     add_node_to_connection_graph(n, &delayed_worklist);
     PointsToNode* ptn = ptnode_adr(n->_idx);
     if (ptn != nullptr && ptn != phantom_obj) {
@@ -691,11 +702,13 @@
       if (ptn == nullptr || !ptn->scalar_replaceable()) {
         continue;
       }
 
       AllocateNode* alloc = ptn->ideal_node()->as_Allocate();
-      SafePointScalarObjectNode* sobj = mexp.create_scalarized_object_description(alloc, sfpt);
+      Unique_Node_List value_worklist;
+      SafePointScalarObjectNode* sobj = mexp.create_scalarized_object_description(alloc, sfpt, &value_worklist);
+      guarantee(value_worklist.size() == 0, "Unimplemented: Valhalla support for 8287061");
       if (sobj == nullptr) {
         _compile->record_failure(C2Compiler::retry_no_reduce_allocation_merges());
         return;
       }
 
@@ -855,11 +868,11 @@
     const char* name = call->as_CallStaticJava()->_name;
     assert(name != nullptr, "no name");
     // no arg escapes through uncommon traps
     if (strcmp(name, "uncommon_trap") != 0) {
       // process_call_arguments() assumes that all arguments escape globally
-      const TypeTuple* d = call->tf()->domain();
+      const TypeTuple* d = call->tf()->domain_sig();
       for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
         const Type* at = d->field_at(i);
         if (at->isa_oopptr() != nullptr) {
           return true;
         }
@@ -929,10 +942,21 @@
       if ((n->as_Call()->returns_pointer() &&
            n->as_Call()->proj_out_or_null(TypeFunc::Parms) != nullptr) ||
           (n->is_CallStaticJava() &&
            n->as_CallStaticJava()->is_boxing_method())) {
         add_call_node(n->as_Call());
+      } else if (n->as_Call()->tf()->returns_inline_type_as_fields()) {
+        bool returns_oop = false;
+        for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax && !returns_oop; i++) {
+          ProjNode* pn = n->fast_out(i)->as_Proj();
+          if (pn->_con >= TypeFunc::Parms && pn->bottom_type()->isa_ptr()) {
+            returns_oop = true;
+          }
+        }
+        if (returns_oop) {
+          add_call_node(n->as_Call());
+        }
       }
     }
     return;
   }
   // Put this check here to process call arguments since some call nodes
@@ -960,10 +984,11 @@
     }
     case Op_CastX2P: {
       map_ideal_node(n, phantom_obj);
       break;
     }
+    case Op_InlineType:
     case Op_CastPP:
     case Op_CheckCastPP:
     case Op_EncodeP:
     case Op_DecodeN:
     case Op_EncodePKlass:
@@ -1031,12 +1056,14 @@
       }
       break;
     }
     case Op_Proj: {
       // we are only interested in the oop result projection from a call
-      if (n->as_Proj()->_con == TypeFunc::Parms && n->in(0)->is_Call() &&
-          n->in(0)->as_Call()->returns_pointer()) {
+      if (n->as_Proj()->_con >= TypeFunc::Parms && n->in(0)->is_Call() &&
+          (n->in(0)->as_Call()->returns_pointer() || n->bottom_type()->isa_ptr())) {
+        assert((n->as_Proj()->_con == TypeFunc::Parms && n->in(0)->as_Call()->returns_pointer()) ||
+               n->in(0)->as_Call()->tf()->returns_inline_type_as_fields(), "what kind of oop return is it?");
         add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(0), delayed_worklist);
       }
       break;
     }
     case Op_Rethrow: // Exception object escapes
@@ -1134,10 +1161,11 @@
       PointsToNode* ptn_base = ptnode_adr(base->_idx);
       assert(ptn_base != nullptr, "field's base should be registered");
       add_base(n_ptn->as_Field(), ptn_base);
       break;
     }
+    case Op_InlineType:
     case Op_CastPP:
     case Op_CheckCastPP:
     case Op_EncodeP:
     case Op_DecodeN:
     case Op_EncodePKlass:
@@ -1188,12 +1216,12 @@
       }
       break;
     }
     case Op_Proj: {
       // we are only interested in the oop result projection from a call
-      assert(n->as_Proj()->_con == TypeFunc::Parms && n->in(0)->is_Call() &&
-             n->in(0)->as_Call()->returns_pointer(), "Unexpected node type");
+      assert((n->as_Proj()->_con == TypeFunc::Parms && n->in(0)->as_Call()->returns_pointer()) ||
+             n->in(0)->as_Call()->tf()->returns_inline_type_as_fields(), "what kind of oop return is it?");
       add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(0), nullptr);
       break;
     }
     case Op_Rethrow: // Exception object escapes
     case Op_Return: {
@@ -1365,11 +1393,11 @@
 #endif
   return false;
 }
 
 void ConnectionGraph::add_call_node(CallNode* call) {
-  assert(call->returns_pointer(), "only for call which returns pointer");
+  assert(call->returns_pointer() || call->tf()->returns_inline_type_as_fields(), "only for call which returns pointer");
   uint call_idx = call->_idx;
   if (call->is_Allocate()) {
     Node* k = call->in(AllocateNode::KlassNode);
     const TypeKlassPtr* kt = k->bottom_type()->isa_klassptr();
     assert(kt != nullptr, "TypeKlassPtr  required.");
@@ -1441,11 +1469,12 @@
     // For a static call, we know exactly what method is being called.
     // Use bytecode estimator to record whether the call's return value escapes.
     ciMethod* meth = call->as_CallJava()->method();
     if (meth == nullptr) {
       const char* name = call->as_CallStaticJava()->_name;
-      assert(strncmp(name, "_multianewarray", 15) == 0, "TODO: add failed case check");
+      assert(strncmp(name, "_multianewarray", 15) == 0 ||
+             strncmp(name, "_load_unknown_inline", 20) == 0, "TODO: add failed case check");
       // Returns a newly allocated non-escaped object.
       add_java_object(call, PointsToNode::NoEscape);
       set_not_scalar_replaceable(ptnode_adr(call_idx) NOT_PRODUCT(COMMA "is result of multinewarray"));
     } else if (meth->is_boxing_method()) {
       // Returns boxing object
@@ -1472,11 +1501,11 @@
         // it's fields will be marked as NoEscape at least.
         add_java_object(call, PointsToNode::NoEscape);
         set_not_scalar_replaceable(ptnode_adr(call_idx) NOT_PRODUCT(COMMA "is result of call"));
       } else {
         // Determine whether any arguments are returned.
-        const TypeTuple* d = call->tf()->domain();
+        const TypeTuple* d = call->tf()->domain_cc();
         bool ret_arg = false;
         for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
           if (d->field_at(i)->isa_ptr() != nullptr &&
               call_analyzer->is_arg_returned(i - TypeFunc::Parms)) {
             ret_arg = true;
@@ -1520,11 +1549,11 @@
       // fall through
     case Op_CallLeafVector:
     case Op_CallLeaf: {
       // Stub calls, objects do not escape but they are not scale replaceable.
       // Adjust escape state for outgoing arguments.
-      const TypeTuple * d = call->tf()->domain();
+      const TypeTuple * d = call->tf()->domain_sig();
       bool src_has_oops = false;
       for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
         const Type* at = d->field_at(i);
         Node *arg = call->in(i);
         if (arg == nullptr) {
@@ -1551,11 +1580,14 @@
         if (is_arraycopy || arg_esc < PointsToNode::ArgEscape) {
           assert(aat == Type::TOP || aat == TypePtr::NULL_PTR ||
                  aat->isa_ptr() != nullptr, "expecting an Ptr");
           bool arg_has_oops = aat->isa_oopptr() &&
                               (aat->isa_instptr() ||
-                               (aat->isa_aryptr() && (aat->isa_aryptr()->elem() == Type::BOTTOM || aat->isa_aryptr()->elem()->make_oopptr() != nullptr)));
+                               (aat->isa_aryptr() && (aat->isa_aryptr()->elem() == Type::BOTTOM || aat->isa_aryptr()->elem()->make_oopptr() != nullptr)) ||
+                               (aat->isa_aryptr() && aat->isa_aryptr()->elem() != nullptr &&
+                                                               aat->isa_aryptr()->is_flat() &&
+                                                               aat->isa_aryptr()->elem()->inline_klass()->contains_oops()));
           if (i == TypeFunc::Parms) {
             src_has_oops = arg_has_oops;
           }
           //
           // src or dst could be j.l.Object when other is basic type array:
@@ -1600,10 +1632,13 @@
                   strcmp(call->as_CallLeaf()->_name, "multiplyToLen") == 0 ||
                   strcmp(call->as_CallLeaf()->_name, "squareToLen") == 0 ||
                   strcmp(call->as_CallLeaf()->_name, "mulAdd") == 0 ||
                   strcmp(call->as_CallLeaf()->_name, "montgomery_multiply") == 0 ||
                   strcmp(call->as_CallLeaf()->_name, "montgomery_square") == 0 ||
+                  strcmp(call->as_CallLeaf()->_name, "vectorizedMismatch") == 0 ||
+                  strcmp(call->as_CallLeaf()->_name, "load_unknown_inline") == 0 ||
+                  strcmp(call->as_CallLeaf()->_name, "store_unknown_inline") == 0 ||
                   strcmp(call->as_CallLeaf()->_name, "bigIntegerRightShiftWorker") == 0 ||
                   strcmp(call->as_CallLeaf()->_name, "bigIntegerLeftShiftWorker") == 0 ||
                   strcmp(call->as_CallLeaf()->_name, "vectorizedMismatch") == 0 ||
                   strcmp(call->as_CallLeaf()->_name, "arraysort_stub") == 0 ||
                   strcmp(call->as_CallLeaf()->_name, "array_partition_stub") == 0 ||
@@ -1664,11 +1699,11 @@
       }
       BCEscapeAnalyzer* call_analyzer = (meth !=nullptr) ? meth->get_bcea() : nullptr;
       // fall-through if not a Java method or no analyzer information
       if (call_analyzer != nullptr) {
         PointsToNode* call_ptn = ptnode_adr(call->_idx);
-        const TypeTuple* d = call->tf()->domain();
+        const TypeTuple* d = call->tf()->domain_cc();
         for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
           const Type* at = d->field_at(i);
           int k = i - TypeFunc::Parms;
           Node* arg = call->in(i);
           PointsToNode* arg_ptn = ptnode_adr(arg->_idx);
@@ -1708,11 +1743,11 @@
     }
     default: {
       // Fall-through here if not a Java method or no analyzer information
       // or some other type of call, assume the worst case: all arguments
       // globally escape.
-      const TypeTuple* d = call->tf()->domain();
+      const TypeTuple* d = call->tf()->domain_cc();
       for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
         const Type* at = d->field_at(i);
         if (at->isa_oopptr() != nullptr) {
           Node* arg = call->in(i);
           if (arg->is_AddP()) {
@@ -2121,30 +2156,40 @@
 }
 
 // Find fields initializing values for allocations.
 int ConnectionGraph::find_init_values_phantom(JavaObjectNode* pta) {
   assert(pta->escape_state() == PointsToNode::NoEscape, "Not escaped Allocate nodes only");
+  PointsToNode* init_val = phantom_obj;
   Node* alloc = pta->ideal_node();
 
   // Do nothing for Allocate nodes since its fields values are
   // "known" unless they are initialized by arraycopy/clone.
   if (alloc->is_Allocate() && !pta->arraycopy_dst()) {
-    return 0;
+    if (alloc->as_Allocate()->in(AllocateNode::DefaultValue) != nullptr) {
+      // Non-flat inline type arrays are initialized with
+      // the default value instead of null. Handle them here.
+      init_val = ptnode_adr(alloc->as_Allocate()->in(AllocateNode::DefaultValue)->_idx);
+      assert(init_val != nullptr, "default value should be registered");
+    } else {
+      return 0;
+    }
   }
-  assert(pta->arraycopy_dst() || alloc->as_CallStaticJava(), "sanity");
+  // Non-escaped allocation returned from Java or runtime call has unknown values in fields.
+  assert(pta->arraycopy_dst() || alloc->is_CallStaticJava() || init_val != phantom_obj, "sanity");
 #ifdef ASSERT
-  if (!pta->arraycopy_dst() && alloc->as_CallStaticJava()->method() == nullptr) {
+  if (alloc->is_CallStaticJava() && alloc->as_CallStaticJava()->method() == nullptr) {
     const char* name = alloc->as_CallStaticJava()->_name;
-    assert(strncmp(name, "_multianewarray", 15) == 0, "sanity");
+    assert(strncmp(name, "_multianewarray", 15) == 0 ||
+           strncmp(name, "_load_unknown_inline", 20) == 0, "sanity");
   }
 #endif
   // Non-escaped allocation returned from Java or runtime call have unknown values in fields.
   int new_edges = 0;
   for (EdgeIterator i(pta); i.has_next(); i.next()) {
     PointsToNode* field = i.get();
     if (field->is_Field() && field->as_Field()->is_oop()) {
-      if (add_edge(field, phantom_obj)) {
+      if (add_edge(field, init_val)) {
         // New edge was added
         new_edges++;
         add_field_uses_to_worklist(field->as_Field());
       }
     }
@@ -2155,11 +2200,11 @@
 // Find fields initializing values for allocations.
 int ConnectionGraph::find_init_values_null(JavaObjectNode* pta, PhaseValues* phase) {
   assert(pta->escape_state() == PointsToNode::NoEscape, "Not escaped Allocate nodes only");
   Node* alloc = pta->ideal_node();
   // Do nothing for Call nodes since its fields values are unknown.
-  if (!alloc->is_Allocate()) {
+  if (!alloc->is_Allocate() || alloc->as_Allocate()->in(AllocateNode::DefaultValue) != nullptr) {
     return 0;
   }
   InitializeNode* ini = alloc->as_Allocate()->initialization();
   bool visited_bottom_offset = false;
   GrowableArray<int> offsets_worklist;
@@ -2241,13 +2286,13 @@
                 }
               }
               if (missed_obj != nullptr) {
                 tty->print_cr("----------field---------------------------------");
                 field->dump();
-                tty->print_cr("----------missed referernce to object-----------");
+                tty->print_cr("----------missed reference to object------------");
                 missed_obj->dump();
-                tty->print_cr("----------object referernced by init store -----");
+                tty->print_cr("----------object referenced by init store-------");
                 store->dump();
                 val->dump();
                 assert(!field->points_to(missed_obj->as_JavaObject()), "missed JavaObject reference");
               }
             }
@@ -2545,11 +2590,12 @@
     for (int i = 0; i < cnt; i++) {
       Node *n = C->macro_node(i);
       if (n->is_AbstractLock()) { // Lock and Unlock nodes
         AbstractLockNode* alock = n->as_AbstractLock();
         if (!alock->is_non_esc_obj()) {
-          if (not_global_escape(alock->obj_node())) {
+          const Type* obj_type = igvn->type(alock->obj_node());
+          if (not_global_escape(alock->obj_node()) && !obj_type->is_inlinetypeptr()) {
             assert(!alock->is_eliminated() || alock->is_coarsened(), "sanity");
             // The lock could be marked eliminated by lock coarsening
             // code during first IGVN before EA. Replace coarsened flag
             // to eliminate all associated locks/unlocks.
 #ifdef ASSERT
@@ -2586,15 +2632,20 @@
   // MemBarStoreStore node if the allocated object never escapes.
   for (int i = 0; i < storestore_worklist.length(); i++) {
     Node* storestore = storestore_worklist.at(i);
     Node* alloc = storestore->in(MemBarNode::Precedent)->in(0);
     if (alloc->is_Allocate() && not_global_escape(alloc)) {
-      MemBarNode* mb = MemBarNode::make(C, Op_MemBarCPUOrder, Compile::AliasIdxBot);
-      mb->init_req(TypeFunc::Memory,  storestore->in(TypeFunc::Memory));
-      mb->init_req(TypeFunc::Control, storestore->in(TypeFunc::Control));
-      igvn->register_new_node_with_optimizer(mb);
-      igvn->replace_node(storestore, mb);
+      if (alloc->in(AllocateNode::InlineType) != nullptr) {
+        // Non-escaping inline type buffer allocations don't require a membar
+        storestore->as_MemBar()->remove(_igvn);
+      } else {
+        MemBarNode* mb = MemBarNode::make(C, Op_MemBarCPUOrder, Compile::AliasIdxBot);
+        mb->init_req(TypeFunc::Memory,  storestore->in(TypeFunc::Memory));
+        mb->init_req(TypeFunc::Control, storestore->in(TypeFunc::Control));
+        igvn->register_new_node_with_optimizer(mb);
+        igvn->replace_node(storestore, mb);
+      }
     }
   }
 }
 
 // Optimize objects compare.
@@ -2744,12 +2795,13 @@
   dst->set_arraycopy_dst();
 }
 
 bool ConnectionGraph::is_oop_field(Node* n, int offset, bool* unsafe) {
   const Type* adr_type = n->as_AddP()->bottom_type();
+  int field_offset = adr_type->isa_aryptr() ? adr_type->isa_aryptr()->field_offset().get() : Type::OffsetBot;
   BasicType bt = T_INT;
-  if (offset == Type::OffsetBot) {
+  if (offset == Type::OffsetBot && field_offset == Type::OffsetBot) {
     // Check only oop fields.
     if (!adr_type->isa_aryptr() ||
         adr_type->isa_aryptr()->elem() == Type::BOTTOM ||
         adr_type->isa_aryptr()->elem()->make_oopptr() != nullptr) {
       // OffsetBot is used to reference array's element. Ignore first AddP.
@@ -2757,11 +2809,11 @@
         bt = T_OBJECT;
       }
     }
   } else if (offset != oopDesc::klass_offset_in_bytes()) {
     if (adr_type->isa_instptr()) {
-      ciField* field = _compile->alias_type(adr_type->isa_instptr())->field();
+      ciField* field = _compile->alias_type(adr_type->is_ptr())->field();
       if (field != nullptr) {
         bt = field->layout_type();
       } else {
         // Check for unsafe oop field access
         if (n->has_out_with(Op_StoreP, Op_LoadP, Op_StoreN, Op_LoadN) ||
@@ -2776,12 +2828,18 @@
       if (offset == arrayOopDesc::length_offset_in_bytes()) {
         // Ignore array length load.
       } else if (find_second_addp(n, n->in(AddPNode::Base)) != nullptr) {
         // Ignore first AddP.
       } else {
-        const Type* elemtype = adr_type->isa_aryptr()->elem();
-        bt = elemtype->array_element_basic_type();
+        const Type* elemtype = adr_type->is_aryptr()->elem();
+        if (adr_type->is_aryptr()->is_flat() && field_offset != Type::OffsetBot) {
+          ciInlineKlass* vk = elemtype->inline_klass();
+          field_offset += vk->first_field_offset();
+          bt = vk->get_field_by_offset(field_offset, false)->layout_type();
+        } else {
+          bt = elemtype->array_element_basic_type();
+        }
       }
     } else if (adr_type->isa_rawptr() || adr_type->isa_klassptr()) {
       // Allocation initialization, ThreadLocal field access, unsafe access
       if (n->has_out_with(Op_StoreP, Op_LoadP, Op_StoreN, Op_LoadN) ||
           n->has_out_with(Op_GetAndSetP, Op_GetAndSetN, Op_CompareAndExchangeP, Op_CompareAndExchangeN) ||
@@ -2960,13 +3018,11 @@
     assert(offs != Type::OffsetBot ||
            adr->in(AddPNode::Address)->in(0)->is_AllocateArray(),
            "offset must be a constant or it is initialization of array");
     return offs;
   }
-  const TypePtr *t_ptr = adr_type->isa_ptr();
-  assert(t_ptr != nullptr, "must be a pointer type");
-  return t_ptr->offset();
+  return adr_type->is_ptr()->flat_offset();
 }
 
 Node* ConnectionGraph::get_addp_base(Node *addp) {
   assert(addp->is_AddP(), "must be AddP");
   //
@@ -3116,13 +3172,20 @@
     // compute an appropriate address type (cases #3 and #5).
     assert(igvn->type(addp) == TypeRawPtr::NOTNULL, "must be raw pointer");
     assert(addp->in(AddPNode::Address)->is_Proj(), "base of raw address must be result projection from allocation");
     intptr_t offs = (int)igvn->find_intptr_t_con(addp->in(AddPNode::Offset), Type::OffsetBot);
     assert(offs != Type::OffsetBot, "offset must be a constant");
-    t = base_t->add_offset(offs)->is_oopptr();
+    if (base_t->isa_aryptr() != nullptr) {
+      // In the case of a flat inline type array, each field has its
+      // own slice so we need to extract the field being accessed from
+      // the address computation
+      t = base_t->isa_aryptr()->add_field_offset_and_offset(offs)->is_oopptr();
+    } else {
+      t = base_t->add_offset(offs)->is_oopptr();
+    }
   }
-  int inst_id =  base_t->instance_id();
+  int inst_id = base_t->instance_id();
   assert(!t->is_known_instance() || t->instance_id() == inst_id,
                              "old type must be non-instance or match new type");
 
   // The type 't' could be subclass of 'base_t'.
   // As result t->offset() could be large then base_t's size and it will
@@ -3132,21 +3195,32 @@
   // It could happened on subclass's branch (from the type profiling
   // inlining) which was not eliminated during parsing since the exactness
   // of the allocation type was not propagated to the subclass type check.
   //
   // Or the type 't' could be not related to 'base_t' at all.
-  // It could happened when CHA type is different from MDO type on a dead path
+  // It could happen when CHA type is different from MDO type on a dead path
   // (for example, from instanceof check) which is not collapsed during parsing.
   //
   // Do nothing for such AddP node and don't process its users since
   // this code branch will go away.
   //
   if (!t->is_known_instance() &&
       !base_t->maybe_java_subtype_of(t)) {
      return false; // bail out
   }
-  const TypeOopPtr *tinst = base_t->add_offset(t->offset())->is_oopptr();
+  const TypePtr* tinst = base_t->add_offset(t->offset());
+  if (tinst->isa_aryptr() && t->isa_aryptr()) {
+    // In the case of a flat inline type array, each field has its
+    // own slice so we need to keep track of the field being accessed.
+    tinst = tinst->is_aryptr()->with_field_offset(t->is_aryptr()->field_offset().get());
+    // Keep array properties (not flat/null-free)
+    tinst = tinst->is_aryptr()->update_properties(t->is_aryptr());
+    if (tinst == nullptr) {
+      return false; // Skip dead path with inconsistent properties
+    }
+  }
+
   // Do NOT remove the next line: ensure a new alias index is allocated
   // for the instance type. Note: C++ will not remove it since the call
   // has side effect.
   int alias_idx = _compile->get_alias_index(tinst);
   igvn->set_type(addp, tinst);
@@ -3842,10 +3916,17 @@
           tn_t = tn_type->make_ptr()->isa_oopptr();
         } else {
           tn_t = tn_type->isa_oopptr();
         }
         if (tn_t != nullptr && tinst->maybe_java_subtype_of(tn_t)) {
+          if (tn_t->isa_aryptr()) {
+            // Keep array properties (not flat/null-free)
+            tinst = tinst->is_aryptr()->update_properties(tn_t->is_aryptr());
+            if (tinst == nullptr) {
+              continue; // Skip dead path with inconsistent properties
+            }
+          }
           if (tn_type->isa_narrowoop()) {
             tn_type = tinst->make_narrowoop();
           } else {
             tn_type = tinst;
           }
@@ -3867,11 +3948,11 @@
       continue;
     }
     // push allocation's users on appropriate worklist
     for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
       Node *use = n->fast_out(i);
-      if(use->is_Mem() && use->in(MemNode::Address) == n) {
+      if (use->is_Mem() && use->in(MemNode::Address) == n) {
         // Load/store to instance's field
         memnode_worklist.append_if_missing(use);
       } else if (use->is_MemBar()) {
         if (use->in(TypeFunc::Memory) == n) { // Ignore precedent edge
           memnode_worklist.append_if_missing(use);
@@ -3903,10 +3984,13 @@
       } else if (use->Opcode() == Op_EncodeISOArray) {
         if (use->in(MemNode::Memory) == n || use->in(3) == n) {
           // EncodeISOArray overwrites destination array
           memnode_worklist.append_if_missing(use);
         }
+      } else if (use->Opcode() == Op_Return) {
+        // Allocation is referenced by field of returned inline type
+        assert(_compile->tf()->returns_inline_type_as_fields(), "EA: unexpected reference by ReturnNode");
       } else {
         uint op = use->Opcode();
         if ((op == Op_StrCompressedCopy || op == Op_StrInflatedCopy) &&
             (use->in(MemNode::Memory) == n)) {
           // They overwrite memory edge corresponding to destination array,
@@ -3916,11 +4000,11 @@
               op == Op_FastLock || op == Op_AryEq ||
               op == Op_StrComp || op == Op_CountPositives ||
               op == Op_StrCompressedCopy || op == Op_StrInflatedCopy ||
               op == Op_StrEquals || op == Op_VectorizedHashCode ||
               op == Op_StrIndexOf || op == Op_StrIndexOfChar ||
-              op == Op_SubTypeCheck ||
+              op == Op_SubTypeCheck || op == Op_InlineType || op == Op_FlatArrayCheck ||
               BarrierSet::barrier_set()->barrier_set_c2()->is_gc_barrier_node(use))) {
           n->dump();
           use->dump();
           assert(false, "EA: missing allocation reference path");
         }
@@ -4016,10 +4100,13 @@
     } else if (n->Opcode() == Op_StrCompressedCopy ||
                n->Opcode() == Op_EncodeISOArray) {
       // get the memory projection
       n = n->find_out_with(Op_SCMemProj);
       assert(n != nullptr && n->Opcode() == Op_SCMemProj, "memory projection required");
+    } else if (n->is_CallLeaf() && n->as_CallLeaf()->_name != nullptr &&
+               strcmp(n->as_CallLeaf()->_name, "store_unknown_inline") == 0) {
+      n = n->as_CallLeaf()->proj_out(TypeFunc::Memory);
     } else {
       assert(n->is_Mem(), "memory node required.");
       Node *addr = n->in(MemNode::Address);
       const Type *addr_t = igvn->type(addr);
       if (addr_t == Type::TOP) {
@@ -4058,29 +4145,33 @@
       } else if (use->is_MemBar() || use->is_CallLeaf()) {
         if (use->in(TypeFunc::Memory) == n) { // Ignore precedent edge
           memnode_worklist.append_if_missing(use);
         }
 #ifdef ASSERT
-      } else if(use->is_Mem()) {
+      } else if (use->is_Mem()) {
         assert(use->in(MemNode::Memory) != n, "EA: missing memory path");
       } else if (use->is_MergeMem()) {
         assert(mergemem_worklist.contains(use->as_MergeMem()), "EA: missing MergeMem node in the worklist");
       } else if (use->Opcode() == Op_EncodeISOArray) {
         if (use->in(MemNode::Memory) == n || use->in(3) == n) {
           // EncodeISOArray overwrites destination array
           memnode_worklist.append_if_missing(use);
         }
+      } else if (use->is_CallLeaf() && use->as_CallLeaf()->_name != nullptr &&
+                 strcmp(use->as_CallLeaf()->_name, "store_unknown_inline") == 0) {
+        // store_unknown_inline overwrites destination array
+        memnode_worklist.append_if_missing(use);
       } else {
         uint op = use->Opcode();
         if ((use->in(MemNode::Memory) == n) &&
             (op == Op_StrCompressedCopy || op == Op_StrInflatedCopy)) {
           // They overwrite memory edge corresponding to destination array,
           memnode_worklist.append_if_missing(use);
         } else if (!(BarrierSet::barrier_set()->barrier_set_c2()->is_gc_barrier_node(use) ||
               op == Op_AryEq || op == Op_StrComp || op == Op_CountPositives ||
               op == Op_StrCompressedCopy || op == Op_StrInflatedCopy || op == Op_VectorizedHashCode ||
-              op == Op_StrEquals || op == Op_StrIndexOf || op == Op_StrIndexOfChar)) {
+              op == Op_StrEquals || op == Op_StrIndexOf || op == Op_StrIndexOfChar || op == Op_FlatArrayCheck)) {
           n->dump();
           use->dump();
           assert(false, "EA: missing memory path");
         }
 #endif
@@ -4163,11 +4254,11 @@
   //  Phase 4:  Update the inputs of non-instance memory Phis and
   //            the Memory input of memnodes
   // First update the inputs of any non-instance Phi's from
   // which we split out an instance Phi.  Note we don't have
   // to recursively process Phi's encountered on the input memory
-  // chains as is done in split_memory_phi() since they  will
+  // chains as is done in split_memory_phi() since they will
   // also be processed here.
   for (int j = 0; j < orig_phis.length(); j++) {
     PhiNode *phi = orig_phis.at(j);
     int alias_idx = _compile->get_alias_index(phi->adr_type());
     igvn->hash_delete(phi);
diff a/src/hotspot/share/opto/gcm.cpp b/src/hotspot/share/opto/gcm.cpp
--- a/src/hotspot/share/opto/gcm.cpp
+++ b/src/hotspot/share/opto/gcm.cpp
@@ -238,11 +238,11 @@
           node->set_req(0, n);
         }
       }
 
       // process all inputs that are non null
-      for (int i = node->req()-1; i >= 0; --i) {
+      for (int i = node->len()-1; i >= 0; --i) {
         if (node->in(i) != nullptr) {
           spstack.push(node->in(i));
         }
       }
     }
diff a/src/hotspot/share/opto/generateOptoStub.cpp b/src/hotspot/share/opto/generateOptoStub.cpp
--- a/src/hotspot/share/opto/generateOptoStub.cpp
+++ b/src/hotspot/share/opto/generateOptoStub.cpp
@@ -46,12 +46,12 @@
                         int is_fancy_jump,
                         bool pass_tls,
                         bool return_pc) {
   ResourceMark rm;
 
-  const TypeTuple *jdomain = C->tf()->domain();
-  const TypeTuple *jrange  = C->tf()->range();
+  const TypeTuple *jdomain = C->tf()->domain_sig();
+  const TypeTuple *jrange  = C->tf()->range_sig();
 
   // The procedure start
   StartNode* start = new StartNode(root(), jdomain);
   _gvn.set_type_bottom(start);
 
@@ -274,11 +274,11 @@
     ret = new ReturnNode(TypeFunc::Parms, if_null,
                          i_o(),
                          exit_memory,
                          frameptr(),
                          returnadr());
-    if (C->tf()->range()->cnt() > TypeFunc::Parms)
+    if (C->tf()->range_sig()->cnt() > TypeFunc::Parms)
       ret->add_req( map()->in(TypeFunc::Parms) );
     break;
   case 1:    // This is a fancy tail-call jump.  Jump to computed address.
     // Jump to new callee; leave old return address alone.
     ret = new TailCallNode(if_null,
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
@@ -21,10 +21,12 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "ci/ciFlatArrayKlass.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "ci/ciUtilities.hpp"
 #include "classfile/javaClasses.hpp"
 #include "ci/ciObjArray.hpp"
 #include "asm/register.hpp"
 #include "compiler/compileLog.hpp"
@@ -35,13 +37,15 @@
 #include "opto/addnode.hpp"
 #include "opto/castnode.hpp"
 #include "opto/convertnode.hpp"
 #include "opto/graphKit.hpp"
 #include "opto/idealKit.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/intrinsicnode.hpp"
 #include "opto/locknode.hpp"
 #include "opto/machnode.hpp"
+#include "opto/narrowptrnode.hpp"
 #include "opto/opaquenode.hpp"
 #include "opto/parse.hpp"
 #include "opto/rootnode.hpp"
 #include "opto/runtime.hpp"
 #include "opto/subtypenode.hpp"
@@ -51,19 +55,27 @@
 #include "utilities/powerOfTwo.hpp"
 #include "utilities/growableArray.hpp"
 
 //----------------------------GraphKit-----------------------------------------
 // Main utility constructor.
-GraphKit::GraphKit(JVMState* jvms)
+GraphKit::GraphKit(JVMState* jvms, PhaseGVN* gvn)
   : Phase(Phase::Parser),
     _env(C->env()),
-    _gvn(*C->initial_gvn()),
+    _gvn((gvn != nullptr) ? *gvn : *C->initial_gvn()),
     _barrier_set(BarrierSet::barrier_set()->barrier_set_c2())
 {
+  assert(gvn == nullptr || !gvn->is_IterGVN() || gvn->is_IterGVN()->delay_transform(), "delay transform should be enabled");
   _exceptions = jvms->map()->next_exception();
   if (_exceptions != nullptr)  jvms->map()->set_next_exception(nullptr);
   set_jvms(jvms);
+#ifdef ASSERT
+  if (_gvn.is_IterGVN() != nullptr) {
+    assert(_gvn.is_IterGVN()->delay_transform(), "Transformation must be delayed if IterGVN is used");
+    // Save the initial size of _for_igvn worklist for verification (see ~GraphKit)
+    _worklist_size = _gvn.C->igvn_worklist()->size();
+  }
+#endif
 }
 
 // Private constructor for parser.
 GraphKit::GraphKit()
   : Phase(Phase::Parser),
@@ -855,11 +867,11 @@
   ciMethod* cur_method = jvms->method();
   int       cur_bci   = jvms->bci();
   if (cur_method != nullptr && cur_bci != InvocationEntryBci) {
     Bytecodes::Code code = cur_method->java_code_at_bci(cur_bci);
     return Interpreter::bytecode_should_reexecute(code) ||
-           (is_anewarray && code == Bytecodes::_multianewarray);
+           (is_anewarray && (code == Bytecodes::_multianewarray));
     // Reexecute _multianewarray bytecode which was replaced with
     // sequence of [a]newarray. See Parse::do_multianewarray().
     //
     // Note: interpreter should not have it set since this optimization
     // is limited by dimensions and guarded by flag so in some cases
@@ -946,10 +958,12 @@
   // Fill pointer walks backwards from "young:" to "root:" in the diagram above:
   uint debug_ptr = call->req();
 
   // Loop over the map input edges associated with jvms, add them
   // to the call node, & reset all offsets to match call node array.
+
+  JVMState* callee_jvms = nullptr;
   for (JVMState* in_jvms = youngest_jvms; in_jvms != nullptr; ) {
     uint debug_end   = debug_ptr;
     uint debug_start = debug_ptr - in_jvms->debug_size();
     debug_ptr = debug_start;  // back up the ptr
 
@@ -971,23 +985,37 @@
     // Add the Locals
     k = in_jvms->locoff();
     l = in_jvms->loc_size();
     out_jvms->set_locoff(p);
     if (!can_prune_locals) {
-      for (j = 0; j < l; j++)
-        call->set_req(p++, in_map->in(k+j));
+      for (j = 0; j < l; j++) {
+        Node* val = in_map->in(k + j);
+        // Check if there's a larval that has been written in the callee state (constructor) and update it in the caller state
+        if (val->is_InlineType() && val->isa_InlineType()->is_larval() && callee_jvms != nullptr &&
+            callee_jvms->method()->is_object_constructor() && callee_jvms->method()->holder()->is_inlinetype() && val == in_map->argument(in_jvms, 0)) {
+          val = callee_jvms->map()->local(callee_jvms, 0); // Receiver
+        }
+        call->set_req(p++, val);
+      }
     } else {
       p += l;  // already set to top above by add_req_batch
     }
 
     // Add the Expression Stack
     k = in_jvms->stkoff();
     l = in_jvms->sp();
     out_jvms->set_stkoff(p);
     if (!can_prune_locals) {
-      for (j = 0; j < l; j++)
-        call->set_req(p++, in_map->in(k+j));
+      for (j = 0; j < l; j++) {
+        Node* val = in_map->in(k + j);
+        // Check if there's a larval that has been written in the callee state (constructor) and update it in the caller state
+        if (val->is_InlineType() && val->isa_InlineType()->is_larval() && callee_jvms != nullptr &&
+            callee_jvms->method()->is_object_constructor() && callee_jvms->method()->holder()->is_inlinetype() && val == in_map->argument(in_jvms, 0)) {
+          val = callee_jvms->map()->local(callee_jvms, 0); // Receiver
+        }
+        call->set_req(p++, val);
+      }
     } else if (can_prune_locals && stack_slots_not_pruned != 0) {
       // Divide stack into {S0,...,S1}, where S0 is set to top.
       uint s1 = stack_slots_not_pruned;
       stack_slots_not_pruned = 0;  // for next iteration
       if (s1 > l)  s1 = l;
@@ -1022,10 +1050,11 @@
     assert(out_jvms->mon_size()   == in_jvms->mon_size(),   "size must match");
     assert(out_jvms->scl_size()   == in_jvms->scl_size(),   "size must match");
     assert(out_jvms->debug_size() == in_jvms->debug_size(), "size must match");
 
     // Update the two tail pointers in parallel.
+    callee_jvms = out_jvms;
     out_jvms = out_jvms->caller();
     in_jvms  = in_jvms->caller();
   }
 
   assert(debug_ptr == non_debug_edges, "debug info must fit exactly");
@@ -1197,11 +1226,11 @@
 Node* GraphKit::load_object_klass(Node* obj) {
   // Special-case a fresh allocation to avoid building nodes:
   Node* akls = AllocateNode::Ideal_klass(obj, &_gvn);
   if (akls != nullptr)  return akls;
   Node* k_adr = basic_plus_adr(obj, oopDesc::klass_offset_in_bytes());
-  return _gvn.transform(LoadKlassNode::make(_gvn, nullptr, immutable_memory(), k_adr, TypeInstPtr::KLASS));
+  return _gvn.transform(LoadKlassNode::make(_gvn, nullptr, immutable_memory(), k_adr, TypeInstPtr::KLASS, TypeInstKlassPtr::OBJECT));
 }
 
 //-------------------------load_array_length-----------------------------------
 Node* GraphKit::load_array_length(Node* array) {
   // Special-case a fresh allocation to avoid building nodes:
@@ -1246,15 +1275,38 @@
 #endif
 Node* GraphKit::null_check_common(Node* value, BasicType type,
                                   // optional arguments for variations:
                                   bool assert_null,
                                   Node* *null_control,
-                                  bool speculative) {
+                                  bool speculative,
+                                  bool is_init_check) {
   assert(!assert_null || null_control == nullptr, "not both at once");
   if (stopped())  return top();
   NOT_PRODUCT(explicit_null_checks_inserted++);
 
+  if (value->is_InlineType()) {
+    // Null checking a scalarized but nullable inline type. Check the IsInit
+    // input instead of the oop input to avoid keeping buffer allocations alive.
+    InlineTypeNode* vtptr = value->as_InlineType();
+    while (vtptr->get_oop()->is_InlineType()) {
+      vtptr = vtptr->get_oop()->as_InlineType();
+    }
+    null_check_common(vtptr->get_is_init(), T_INT, assert_null, null_control, speculative, true);
+    if (stopped()) {
+      return top();
+    }
+    if (assert_null) {
+      // TODO 8284443 Scalarize here (this currently leads to compilation bailouts)
+      // vtptr = InlineTypeNode::make_null(_gvn, vtptr->type()->inline_klass());
+      // replace_in_map(value, vtptr);
+      // return vtptr;
+      return null();
+    }
+    bool do_replace_in_map = (null_control == nullptr || (*null_control) == top());
+    return cast_not_null(value, do_replace_in_map);
+  }
+
   // Construct null check
   Node *chk = nullptr;
   switch(type) {
     case T_LONG   : chk = new CmpLNode(value, _gvn.zerocon(T_LONG)); break;
     case T_INT    : chk = new CmpINode(value, _gvn.intcon(0)); break;
@@ -1354,11 +1406,11 @@
   // Branch to failure if null
   float ok_prob = PROB_MAX;  // a priori estimate:  nulls never happen
   Deoptimization::DeoptReason reason;
   if (assert_null) {
     reason = Deoptimization::reason_null_assert(speculative);
-  } else if (type == T_OBJECT) {
+  } else if (type == T_OBJECT || is_init_check) {
     reason = Deoptimization::reason_null_check(speculative);
   } else {
     reason = Deoptimization::Reason_div0_check;
   }
   // %%% Since Reason_unhandled is not recorded on a per-bytecode basis,
@@ -1428,14 +1480,23 @@
   }
 
   return value;
 }
 
-
 //------------------------------cast_not_null----------------------------------
 // Cast obj to not-null on this path
 Node* GraphKit::cast_not_null(Node* obj, bool do_replace_in_map) {
+  if (obj->is_InlineType()) {
+    // TODO 8325106 Can we avoid cloning?
+    Node* vt = obj->clone();
+    vt->as_InlineType()->set_is_init(_gvn);
+    vt = _gvn.transform(vt);
+    if (do_replace_in_map) {
+      replace_in_map(obj, vt);
+    }
+    return vt;
+  }
   const Type *t = _gvn.type(obj);
   const Type *t_not_null = t->join_speculative(TypePtr::NOTNULL);
   // Object is already not-null?
   if( t == t_not_null ) return obj;
 
@@ -1557,10 +1618,11 @@
   const TypePtr* adr_type = nullptr; // debug-mode-only argument
   debug_only(adr_type = C->get_adr_type(adr_idx));
   Node* mem = memory(adr_idx);
   Node* ld = LoadNode::make(_gvn, ctl, mem, adr, adr_type, t, bt, mo, control_dependency, require_atomic_access, unaligned, mismatched, unsafe, barrier_data);
   ld = _gvn.transform(ld);
+
   if (((bt == T_OBJECT) && C->do_escape_analysis()) || C->eliminate_boxing()) {
     // Improve graph before escape analysis and boxing elimination.
     record_for_igvn(ld);
     if (ld->is_DecodeN()) {
       // Also record the actual load (LoadN) in case ld is DecodeN
@@ -1608,11 +1670,12 @@
                                 Node* adr,
                                 const TypePtr* adr_type,
                                 Node* val,
                                 const Type* val_type,
                                 BasicType bt,
-                                DecoratorSet decorators) {
+                                DecoratorSet decorators,
+                                bool safe_for_replace) {
   // Transformation of a value which could be null pointer (CastPP #null)
   // could be delayed during Parse (for example, in adjust_map_after_if()).
   // Execute transformation here to avoid barrier generation in such case.
   if (_gvn.type(val) == TypePtr::NULL_PTR) {
     val = _gvn.makecon(TypePtr::NULL_PTR);
@@ -1621,10 +1684,17 @@
   if (stopped()) {
     return top(); // Dead path ?
   }
 
   assert(val != nullptr, "not dead path");
+  if (val->is_InlineType()) {
+    // Store to non-flat field. Buffer the inline type and make sure
+    // the store is re-executed if the allocation triggers deoptimization.
+    PreserveReexecuteState preexecs(this);
+    jvms()->set_should_reexecute(true);
+    val = val->as_InlineType()->buffer(this, safe_for_replace);
+  }
 
   C2AccessValuePtr addr(adr, adr_type);
   C2AccessValue value(val, val_type);
   C2ParseAccess access(this, decorators | C2_WRITE_ACCESS, bt, obj, addr);
   if (access.is_raw()) {
@@ -1637,17 +1707,18 @@
 Node* GraphKit::access_load_at(Node* obj,   // containing obj
                                Node* adr,   // actual address to store val at
                                const TypePtr* adr_type,
                                const Type* val_type,
                                BasicType bt,
-                               DecoratorSet decorators) {
+                               DecoratorSet decorators,
+                               Node* ctl) {
   if (stopped()) {
     return top(); // Dead path ?
   }
 
   C2AccessValuePtr addr(adr, adr_type);
-  C2ParseAccess access(this, decorators | C2_READ_ACCESS, bt, obj, addr);
+  C2ParseAccess access(this, decorators | C2_READ_ACCESS, bt, obj, addr, ctl);
   if (access.is_raw()) {
     return _barrier_set->BarrierSetC2::load_at(access, val_type);
   } else {
     return _barrier_set->load_at(access, val_type);
   }
@@ -1748,11 +1819,12 @@
 }
 
 //-------------------------array_element_address-------------------------
 Node* GraphKit::array_element_address(Node* ary, Node* idx, BasicType elembt,
                                       const TypeInt* sizetype, Node* ctrl) {
-  uint shift  = exact_log2(type2aelembytes(elembt));
+  const TypeAryPtr* arytype = _gvn.type(ary)->is_aryptr();
+  uint shift = arytype->is_flat() ? arytype->flat_log_elem_size() : exact_log2(type2aelembytes(elembt));
   uint header = arrayOopDesc::base_offset_in_bytes(elembt);
 
   // short-circuit a common case (saves lots of confusing waste motion)
   jint idx_con = find_int_con(idx, -1);
   if (idx_con >= 0) {
@@ -1780,16 +1852,52 @@
   return ld;
 }
 
 //-------------------------set_arguments_for_java_call-------------------------
 // Arguments (pre-popped from the stack) are taken from the JVMS.
-void GraphKit::set_arguments_for_java_call(CallJavaNode* call) {
-  // Add the call arguments:
-  uint nargs = call->method()->arg_size();
-  for (uint i = 0; i < nargs; i++) {
-    Node* arg = argument(i);
-    call->init_req(i + TypeFunc::Parms, arg);
+void GraphKit::set_arguments_for_java_call(CallJavaNode* call, bool is_late_inline) {
+  PreserveReexecuteState preexecs(this);
+  if (EnableValhalla) {
+    // Make sure the call is "re-executed", if buffering of inline type arguments triggers deoptimization.
+    // At this point, the call hasn't been executed yet, so we will only ever execute the call once.
+    jvms()->set_should_reexecute(true);
+    int arg_size = method()->get_declared_signature_at_bci(bci())->arg_size_for_bc(java_bc());
+    inc_sp(arg_size);
+  }
+  // Add the call arguments
+  const TypeTuple* domain = call->tf()->domain_sig();
+  uint nargs = domain->cnt();
+  int arg_num = 0;
+  for (uint i = TypeFunc::Parms, idx = TypeFunc::Parms; i < nargs; i++) {
+    Node* arg = argument(i-TypeFunc::Parms);
+    const Type* t = domain->field_at(i);
+    // TODO 8284443 A static call to a mismatched method should still be scalarized
+    if (t->is_inlinetypeptr() && !call->method()->get_Method()->mismatch() && call->method()->is_scalarized_arg(arg_num)) {
+      // We don't pass inline type arguments by reference but instead pass each field of the inline type
+      if (!arg->is_InlineType()) {
+        assert(_gvn.type(arg)->is_zero_type() && !t->inline_klass()->is_null_free(), "Unexpected argument type");
+        arg = InlineTypeNode::make_from_oop(this, arg, t->inline_klass(), t->inline_klass()->is_null_free());
+      }
+      InlineTypeNode* vt = arg->as_InlineType();
+      vt->pass_fields(this, call, idx, true, !t->maybe_null());
+      // If an inline type argument is passed as fields, attach the Method* to the call site
+      // to be able to access the extended signature later via attached_method_before_pc().
+      // For example, see CompiledMethod::preserve_callee_argument_oops().
+      call->set_override_symbolic_info(true);
+      // Register an evol dependency on the callee method to make sure that this method is deoptimized and
+      // re-compiled with a non-scalarized calling convention if the callee method is later marked as mismatched.
+      C->dependencies()->assert_evol_method(call->method());
+      arg_num++;
+      continue;
+    } else if (arg->is_InlineType()) {
+      // Pass inline type argument via oop to callee
+      arg = arg->as_InlineType()->buffer(this);
+    }
+    if (t != Type::HALF) {
+      arg_num++;
+    }
+    call->init_req(idx++, arg);
   }
 }
 
 //---------------------------set_edges_for_java_call---------------------------
 // Connect a newly created call into the current JVMS.
@@ -1823,17 +1931,10 @@
 }
 
 Node* GraphKit::set_results_for_java_call(CallJavaNode* call, bool separate_io_proj, bool deoptimize) {
   if (stopped())  return top();  // maybe the call folded up?
 
-  // Capture the return value, if any.
-  Node* ret;
-  if (call->method() == nullptr ||
-      call->method()->return_type()->basic_type() == T_VOID)
-        ret = top();
-  else  ret = _gvn.transform(new ProjNode(call, TypeFunc::Parms));
-
   // Note:  Since any out-of-line call can produce an exception,
   // we always insert an I_O projection from the call into the result.
 
   make_slow_call_ex(call, env()->Throwable_klass(), separate_io_proj, deoptimize);
 
@@ -1842,10 +1943,42 @@
     // through and exceptional paths, so replace the projections for
     // the fall through path.
     set_i_o(_gvn.transform( new ProjNode(call, TypeFunc::I_O) ));
     set_all_memory(_gvn.transform( new ProjNode(call, TypeFunc::Memory) ));
   }
+
+  // Capture the return value, if any.
+  Node* ret;
+  if (call->method() == nullptr || call->method()->return_type()->basic_type() == T_VOID) {
+    ret = top();
+  } else if (call->tf()->returns_inline_type_as_fields()) {
+    // Return of multiple values (inline type fields): we create a
+    // InlineType node, each field is a projection from the call.
+    ciInlineKlass* vk = call->method()->return_type()->as_inline_klass();
+    uint base_input = TypeFunc::Parms;
+    ret = InlineTypeNode::make_from_multi(this, call, vk, base_input, false, false);
+  } else {
+    ret = _gvn.transform(new ProjNode(call, TypeFunc::Parms));
+    ciType* t = call->method()->return_type();
+    if (t->is_klass()) {
+      const Type* type = TypeOopPtr::make_from_klass(t->as_klass());
+      if (type->is_inlinetypeptr()) {
+        ret = InlineTypeNode::make_from_oop(this, ret, type->inline_klass(), type->inline_klass()->is_null_free());
+      }
+    }
+  }
+
+  // We just called the constructor on a value type receiver. Reload it from the buffer
+  if (call->method()->is_object_constructor() && call->method()->holder()->is_inlinetype()) {
+    InlineTypeNode* receiver = call->in(TypeFunc::Parms)->as_InlineType();
+    assert(receiver->is_larval(), "must be larval");
+    assert(receiver->is_allocated(&gvn()), "larval must be buffered");
+    InlineTypeNode* reloaded = InlineTypeNode::make_from_oop(this, receiver->get_oop(), receiver->bottom_type()->inline_klass(), true);
+    assert(!reloaded->is_larval(), "should not be larval anymore");
+    replace_in_map(receiver, reloaded);
+  }
+
   return ret;
 }
 
 //--------------------set_predefined_input_for_runtime_call--------------------
 // Reading and setting the memory state is way conservative here.
@@ -1932,80 +2065,83 @@
   Node* ex_ctl = top();
 
   SafePointNode* final_state = stop();
 
   // Find all the needed outputs of this call
-  CallProjections callprojs;
-  call->extract_projections(&callprojs, true);
+  CallProjections* callprojs = call->extract_projections(true);
 
   Unique_Node_List wl;
   Node* init_mem = call->in(TypeFunc::Memory);
   Node* final_mem = final_state->in(TypeFunc::Memory);
   Node* final_ctl = final_state->in(TypeFunc::Control);
   Node* final_io = final_state->in(TypeFunc::I_O);
 
   // Replace all the old call edges with the edges from the inlining result
-  if (callprojs.fallthrough_catchproj != nullptr) {
-    C->gvn_replace_by(callprojs.fallthrough_catchproj, final_ctl);
+  if (callprojs->fallthrough_catchproj != nullptr) {
+    C->gvn_replace_by(callprojs->fallthrough_catchproj, final_ctl);
   }
-  if (callprojs.fallthrough_memproj != nullptr) {
+  if (callprojs->fallthrough_memproj != nullptr) {
     if (final_mem->is_MergeMem()) {
       // Parser's exits MergeMem was not transformed but may be optimized
       final_mem = _gvn.transform(final_mem);
     }
-    C->gvn_replace_by(callprojs.fallthrough_memproj,   final_mem);
+    C->gvn_replace_by(callprojs->fallthrough_memproj,   final_mem);
     add_mergemem_users_to_worklist(wl, final_mem);
   }
-  if (callprojs.fallthrough_ioproj != nullptr) {
-    C->gvn_replace_by(callprojs.fallthrough_ioproj,    final_io);
+  if (callprojs->fallthrough_ioproj != nullptr) {
+    C->gvn_replace_by(callprojs->fallthrough_ioproj,    final_io);
   }
 
   // Replace the result with the new result if it exists and is used
-  if (callprojs.resproj != nullptr && result != nullptr) {
-    C->gvn_replace_by(callprojs.resproj, result);
+  if (callprojs->resproj[0] != nullptr && result != nullptr) {
+    // If the inlined code is dead, the result projections for an inline type returned as
+    // fields have not been replaced. They will go away once the call is replaced by TOP below.
+    assert(callprojs->nb_resproj == 1 || (call->tf()->returns_inline_type_as_fields() && stopped()),
+           "unexpected number of results");
+    C->gvn_replace_by(callprojs->resproj[0], result);
   }
 
   if (ejvms == nullptr) {
     // No exception edges to simply kill off those paths
-    if (callprojs.catchall_catchproj != nullptr) {
-      C->gvn_replace_by(callprojs.catchall_catchproj, C->top());
+    if (callprojs->catchall_catchproj != nullptr) {
+      C->gvn_replace_by(callprojs->catchall_catchproj, C->top());
     }
-    if (callprojs.catchall_memproj != nullptr) {
-      C->gvn_replace_by(callprojs.catchall_memproj,   C->top());
+    if (callprojs->catchall_memproj != nullptr) {
+      C->gvn_replace_by(callprojs->catchall_memproj,   C->top());
     }
-    if (callprojs.catchall_ioproj != nullptr) {
-      C->gvn_replace_by(callprojs.catchall_ioproj,    C->top());
+    if (callprojs->catchall_ioproj != nullptr) {
+      C->gvn_replace_by(callprojs->catchall_ioproj,    C->top());
     }
     // Replace the old exception object with top
-    if (callprojs.exobj != nullptr) {
-      C->gvn_replace_by(callprojs.exobj, C->top());
+    if (callprojs->exobj != nullptr) {
+      C->gvn_replace_by(callprojs->exobj, C->top());
     }
   } else {
     GraphKit ekit(ejvms);
 
     // Load my combined exception state into the kit, with all phis transformed:
     SafePointNode* ex_map = ekit.combine_and_pop_all_exception_states();
     replaced_nodes_exception = ex_map->replaced_nodes();
 
     Node* ex_oop = ekit.use_exception_state(ex_map);
 
-    if (callprojs.catchall_catchproj != nullptr) {
-      C->gvn_replace_by(callprojs.catchall_catchproj, ekit.control());
+    if (callprojs->catchall_catchproj != nullptr) {
+      C->gvn_replace_by(callprojs->catchall_catchproj, ekit.control());
       ex_ctl = ekit.control();
     }
-    if (callprojs.catchall_memproj != nullptr) {
+    if (callprojs->catchall_memproj != nullptr) {
       Node* ex_mem = ekit.reset_memory();
-      C->gvn_replace_by(callprojs.catchall_memproj,   ex_mem);
+      C->gvn_replace_by(callprojs->catchall_memproj,   ex_mem);
       add_mergemem_users_to_worklist(wl, ex_mem);
     }
-    if (callprojs.catchall_ioproj != nullptr) {
-      C->gvn_replace_by(callprojs.catchall_ioproj,    ekit.i_o());
+    if (callprojs->catchall_ioproj != nullptr) {
+      C->gvn_replace_by(callprojs->catchall_ioproj,    ekit.i_o());
     }
 
     // Replace the old exception object with the newly created one
-    if (callprojs.exobj != nullptr) {
-      C->gvn_replace_by(callprojs.exobj, ex_oop);
+    if (callprojs->exobj != nullptr) {
+      C->gvn_replace_by(callprojs->exobj, ex_oop);
     }
   }
 
   // Disconnect the call from the graph
   call->disconnect_inputs(C);
@@ -2015,11 +2151,11 @@
   // optimizer doesn't like that.
   while (wl.size() > 0) {
     _gvn.transform(wl.pop());
   }
 
-  if (callprojs.fallthrough_catchproj != nullptr && !final_ctl->is_top() && do_replaced_nodes) {
+  if (callprojs->fallthrough_catchproj != nullptr && !final_ctl->is_top() && do_replaced_nodes) {
     replaced_nodes.apply(C, final_ctl);
   }
   if (!ex_ctl->is_top() && do_replaced_nodes) {
     replaced_nodes_exception.apply(C, ex_ctl);
   }
@@ -2214,11 +2350,11 @@
     const TypeKlassPtr* tklass = TypeKlassPtr::make(exact_kls, Type::trust_interfaces);
     const TypeOopPtr* xtype = tklass->as_instance_type();
     assert(xtype->klass_is_exact(), "Should be exact");
     // Any reason to believe n is not null (from this profiling or a previous one)?
     assert(ptr_kind != ProfileAlwaysNull, "impossible here");
-    const TypePtr* ptr = (ptr_kind == ProfileMaybeNull && current_type->speculative_maybe_null()) ? TypePtr::BOTTOM : TypePtr::NOTNULL;
+    const TypePtr* ptr = (ptr_kind != ProfileNeverNull && current_type->speculative_maybe_null()) ? TypePtr::BOTTOM : TypePtr::NOTNULL;
     // record the new speculative type's depth
     speculative = xtype->cast_to_ptr_type(ptr->ptr())->is_ptr();
     speculative = speculative->with_inline_depth(jvms()->depth());
   } else if (current_type->would_improve_ptr(ptr_kind)) {
     // Profiling report that null was never seen so we can change the
@@ -2237,11 +2373,11 @@
   }
 
   if (speculative != current_type->speculative()) {
     // Build a type with a speculative type (what we think we know
     // about the type but will need a guard when we use it)
-    const TypeOopPtr* spec_type = TypeOopPtr::make(TypePtr::BotPTR, Type::OffsetBot, TypeOopPtr::InstanceBot, speculative);
+    const TypeOopPtr* spec_type = TypeOopPtr::make(TypePtr::BotPTR, Type::Offset::bottom, TypeOopPtr::InstanceBot, speculative);
     // We're changing the type, we need a new CheckCast node to carry
     // the new type. The new type depends on the control: what
     // profiling tells us is only valid from here as far as we can
     // tell.
     Node* cast = new CheckCastPPNode(control(), n, current_type->remove_speculative()->join_speculative(spec_type));
@@ -2271,23 +2407,34 @@
        java_bc() == Bytecodes::_instanceof ||
        java_bc() == Bytecodes::_aastore) &&
       method()->method_data()->is_mature()) {
     ciProfileData* data = method()->method_data()->bci_to_data(bci());
     if (data != nullptr) {
-      if (!data->as_BitData()->null_seen()) {
-        ptr_kind = ProfileNeverNull;
+      if (java_bc() == Bytecodes::_aastore) {
+        ciKlass* array_type = nullptr;
+        ciKlass* element_type = nullptr;
+        ProfilePtrKind element_ptr = ProfileMaybeNull;
+        bool flat_array = true;
+        bool null_free_array = true;
+        method()->array_access_profiled_type(bci(), array_type, element_type, element_ptr, flat_array, null_free_array);
+        exact_kls = element_type;
+        ptr_kind = element_ptr;
       } else {
-        assert(data->is_ReceiverTypeData(), "bad profile data type");
-        ciReceiverTypeData* call = (ciReceiverTypeData*)data->as_ReceiverTypeData();
-        uint i = 0;
-        for (; i < call->row_limit(); i++) {
-          ciKlass* receiver = call->receiver(i);
-          if (receiver != nullptr) {
-            break;
+        if (!data->as_BitData()->null_seen()) {
+          ptr_kind = ProfileNeverNull;
+        } else {
+          assert(data->is_ReceiverTypeData(), "bad profile data type");
+          ciReceiverTypeData* call = (ciReceiverTypeData*)data->as_ReceiverTypeData();
+          uint i = 0;
+          for (; i < call->row_limit(); i++) {
+            ciKlass* receiver = call->receiver(i);
+            if (receiver != nullptr) {
+              break;
+            }
           }
+          ptr_kind = (i == call->row_limit()) ? ProfileAlwaysNull : ProfileMaybeNull;
         }
-        ptr_kind = (i == call->row_limit()) ? ProfileAlwaysNull : ProfileMaybeNull;
       }
     }
   }
   return record_profile_for_speculation(n, exact_kls, ptr_kind);
 }
@@ -2302,14 +2449,14 @@
 void GraphKit::record_profiled_arguments_for_speculation(ciMethod* dest_method, Bytecodes::Code bc) {
   if (!UseTypeSpeculation) {
     return;
   }
   const TypeFunc* tf    = TypeFunc::make(dest_method);
-  int             nargs = tf->domain()->cnt() - TypeFunc::Parms;
+  int             nargs = tf->domain_sig()->cnt() - TypeFunc::Parms;
   int skip = Bytecodes::has_receiver(bc) ? 1 : 0;
   for (int j = skip, i = 0; j < nargs && i < TypeProfileArgsLimit; j++) {
-    const Type *targ = tf->domain()->field_at(j + TypeFunc::Parms);
+    const Type *targ = tf->domain_sig()->field_at(j + TypeFunc::Parms);
     if (is_reference_type(targ->basic_type())) {
       ProfilePtrKind ptr_kind = ProfileMaybeNull;
       ciKlass* better_type = nullptr;
       if (method()->argument_profiled_type(bci(), i, better_type, ptr_kind)) {
         record_profile_for_speculation(argument(j), better_type, ptr_kind);
@@ -2359,13 +2506,13 @@
 
 void GraphKit::round_double_arguments(ciMethod* dest_method) {
   if (Matcher::strict_fp_requires_explicit_rounding) {
     // (Note:  TypeFunc::make has a cache that makes this fast.)
     const TypeFunc* tf    = TypeFunc::make(dest_method);
-    int             nargs = tf->domain()->cnt() - TypeFunc::Parms;
+    int             nargs = tf->domain_sig()->cnt() - TypeFunc::Parms;
     for (int j = 0; j < nargs; j++) {
-      const Type *targ = tf->domain()->field_at(j + TypeFunc::Parms);
+      const Type *targ = tf->domain_sig()->field_at(j + TypeFunc::Parms);
       if (targ->basic_type() == T_DOUBLE) {
         // If any parameters are doubles, they must be rounded before
         // the call, dprecision_rounding does gvn.transform
         Node *arg = argument(j);
         arg = dprecision_rounding(arg);
@@ -2510,11 +2657,11 @@
   if (!is_leaf) {
     call = new CallStaticJavaNode(call_type, call_addr, call_name, adr_type);
   } else if (flags & RC_NO_FP) {
     call = new CallLeafNoFPNode(call_type, call_addr, call_name, adr_type);
   } else  if (flags & RC_VECTOR){
-    uint num_bits = call_type->range()->field_at(TypeFunc::Parms)->is_vect()->length_in_bytes() * BitsPerByte;
+    uint num_bits = call_type->range_sig()->field_at(TypeFunc::Parms)->is_vect()->length_in_bytes() * BitsPerByte;
     call = new CallLeafVectorNode(call_type, call_addr, call_name, adr_type, num_bits);
   } else {
     call = new CallLeafNode(call_type, call_addr, call_name, adr_type);
   }
 
@@ -2590,10 +2737,11 @@
 Node* GraphKit::sign_extend_short(Node* in) {
   Node* tmp = _gvn.transform(new LShiftINode(in, _gvn.intcon(16)));
   return _gvn.transform(new RShiftINode(tmp, _gvn.intcon(16)));
 }
 
+
 //------------------------------merge_memory-----------------------------------
 // Merge memory from one path into the current memory state.
 void GraphKit::merge_memory(Node* new_mem, Node* region, int new_path) {
   for (MergeMemStream mms(merged_memory(), new_mem->as_MergeMem()); mms.next_non_empty2(); ) {
     Node* old_slice = mms.force_memory();
@@ -2886,17 +3034,22 @@
   *ctrl = gvn.transform(r_ok_subtype);
   return gvn.transform(r_not_subtype);
 }
 
 Node* GraphKit::gen_subtype_check(Node* obj_or_subklass, Node* superklass) {
+  const Type* sub_t = _gvn.type(obj_or_subklass);
+  if (sub_t->make_oopptr() != nullptr && sub_t->make_oopptr()->is_inlinetypeptr()) {
+    sub_t = TypeKlassPtr::make(sub_t->inline_klass());
+    obj_or_subklass = makecon(sub_t);
+  }
   bool expand_subtype_check = C->post_loop_opts_phase() ||   // macro node expansion is over
                               ExpandSubTypeCheckAtParseTime; // forced expansion
   if (expand_subtype_check) {
     MergeMemNode* mem = merged_memory();
     Node* ctrl = control();
     Node* subklass = obj_or_subklass;
-    if (!_gvn.type(obj_or_subklass)->isa_klassptr()) {
+    if (!sub_t->isa_klassptr()) {
       subklass = load_object_klass(obj_or_subklass);
     }
 
     Node* n = Phase::gen_subtype_check(subklass, superklass, &ctrl, mem, _gvn, method(), bci());
     set_control(ctrl);
@@ -2910,41 +3063,62 @@
   return _gvn.transform(new IfFalseNode(iff));
 }
 
 // Profile-driven exact type check:
 Node* GraphKit::type_check_receiver(Node* receiver, ciKlass* klass,
-                                    float prob,
-                                    Node* *casted_receiver) {
+                                    float prob, Node* *casted_receiver) {
   assert(!klass->is_interface(), "no exact type check on interfaces");
-
+  Node* fail = top();
+  const Type* rec_t = _gvn.type(receiver);
+  if (rec_t->is_inlinetypeptr()) {
+    if (klass->equals(rec_t->inline_klass())) {
+      (*casted_receiver) = receiver; // Always passes
+    } else {
+      (*casted_receiver) = top();    // Always fails
+      fail = control();
+      set_control(top());
+    }
+    return fail;
+  }
   const TypeKlassPtr* tklass = TypeKlassPtr::make(klass, Type::trust_interfaces);
   Node* recv_klass = load_object_klass(receiver);
-  Node* want_klass = makecon(tklass);
-  Node* cmp = _gvn.transform(new CmpPNode(recv_klass, want_klass));
-  Node* bol = _gvn.transform(new BoolNode(cmp, BoolTest::eq));
-  IfNode* iff = create_and_xform_if(control(), bol, prob, COUNT_UNKNOWN);
-  set_control( _gvn.transform(new IfTrueNode (iff)));
-  Node* fail = _gvn.transform(new IfFalseNode(iff));
+  fail = type_check(recv_klass, tklass, prob);
 
   if (!stopped()) {
     const TypeOopPtr* receiver_type = _gvn.type(receiver)->isa_oopptr();
-    const TypeOopPtr* recvx_type = tklass->as_instance_type();
-    assert(recvx_type->klass_is_exact(), "");
+    const TypeOopPtr* recv_xtype = tklass->as_instance_type();
+    assert(recv_xtype->klass_is_exact(), "");
 
-    if (!receiver_type->higher_equal(recvx_type)) { // ignore redundant casts
+    if (!receiver_type->higher_equal(recv_xtype)) { // ignore redundant casts
       // Subsume downstream occurrences of receiver with a cast to
       // recv_xtype, since now we know what the type will be.
-      Node* cast = new CheckCastPPNode(control(), receiver, recvx_type);
-      (*casted_receiver) = _gvn.transform(cast);
+      Node* cast = new CheckCastPPNode(control(), receiver, recv_xtype);
+      Node* res = _gvn.transform(cast);
+      if (recv_xtype->is_inlinetypeptr()) {
+        assert(!gvn().type(res)->maybe_null(), "receiver should never be null");
+        res = InlineTypeNode::make_from_oop(this, res, recv_xtype->inline_klass());
+      }
+      (*casted_receiver) = res;
       assert(!(*casted_receiver)->is_top(), "that path should be unreachable");
       // (User must make the replace_in_map call.)
     }
   }
 
   return fail;
 }
 
+Node* GraphKit::type_check(Node* recv_klass, const TypeKlassPtr* tklass,
+                           float prob) {
+  Node* want_klass = makecon(tklass);
+  Node* cmp = _gvn.transform(new CmpPNode(recv_klass, want_klass));
+  Node* bol = _gvn.transform(new BoolNode(cmp, BoolTest::eq));
+  IfNode* iff = create_and_xform_if(control(), bol, prob, COUNT_UNKNOWN);
+  set_control(_gvn.transform(new IfTrueNode (iff)));
+  Node* fail = _gvn.transform(new IfFalseNode(iff));
+  return fail;
+}
+
 //------------------------------subtype_check_receiver-------------------------
 Node* GraphKit::subtype_check_receiver(Node* receiver, ciKlass* klass,
                                        Node** casted_receiver) {
   const TypeKlassPtr* tklass = TypeKlassPtr::make(klass, Type::trust_interfaces)->try_improve();
   Node* want_klass = makecon(tklass);
@@ -2953,13 +3127,16 @@
 
   // Ignore interface type information until interface types are properly tracked.
   if (!stopped() && !klass->is_interface()) {
     const TypeOopPtr* receiver_type = _gvn.type(receiver)->isa_oopptr();
     const TypeOopPtr* recv_type = tklass->cast_to_exactness(false)->is_klassptr()->as_instance_type();
-    if (!receiver_type->higher_equal(recv_type)) { // ignore redundant casts
-      Node* cast = new CheckCastPPNode(control(), receiver, recv_type);
-      (*casted_receiver) = _gvn.transform(cast);
+    if (receiver_type != nullptr && !receiver_type->higher_equal(recv_type)) { // ignore redundant casts
+      Node* cast = _gvn.transform(new CheckCastPPNode(control(), receiver, recv_type));
+      if (recv_type->is_inlinetypeptr()) {
+        cast = InlineTypeNode::make_from_oop(this, cast, recv_type->inline_klass());
+      }
+      (*casted_receiver) = cast;
     }
   }
 
   return slow_ctl;
 }
@@ -3064,11 +3241,24 @@
 
   // (No, this isn't a call, but it's enough like a virtual call
   // to use the same ciMethod accessor to get the profile info...)
   // If we have a speculative type use it instead of profiling (which
   // may not help us)
-  ciKlass* exact_kls = spec_klass == nullptr ? profile_has_unique_klass() : spec_klass;
+  ciKlass* exact_kls = spec_klass;
+  if (exact_kls == nullptr) {
+    if (java_bc() == Bytecodes::_aastore) {
+      ciKlass* array_type = nullptr;
+      ciKlass* element_type = nullptr;
+      ProfilePtrKind element_ptr = ProfileMaybeNull;
+      bool flat_array = true;
+      bool null_free_array = true;
+      method()->array_access_profiled_type(bci(), array_type, element_type, element_ptr, flat_array, null_free_array);
+      exact_kls = element_type;
+    } else {
+      exact_kls = profile_has_unique_klass();
+    }
+  }
   if (exact_kls != nullptr) {// no cast failures here
     if (require_klass == nullptr ||
         C->static_subtype_check(require_klass, TypeKlassPtr::make(exact_kls, Type::trust_interfaces)) == Compile::SSC_always_true) {
       // If we narrow the type to match what the type profile sees or
       // the speculative type, we can then remove the rest of the
@@ -3194,11 +3384,11 @@
   // Do we know the type check always succeed?
   bool known_statically = false;
   if (_gvn.type(superklass)->singleton()) {
     const TypeKlassPtr* superk = _gvn.type(superklass)->is_klassptr();
     const TypeKlassPtr* subk = _gvn.type(obj)->is_oopptr()->as_klass_type();
-    if (subk->is_loaded()) {
+    if (subk != nullptr && subk->is_loaded()) {
       int static_res = C->static_subtype_check(superk, subk);
       known_statically = (static_res == Compile::SSC_always_true || static_res == Compile::SSC_always_false);
     }
   }
 
@@ -3250,35 +3440,53 @@
 // array store bytecode.  Stack must be as-if BEFORE doing the bytecode so the
 // uncommon-trap paths work.  Adjust stack after this call.
 // If failure_control is supplied and not null, it is filled in with
 // the control edge for the cast failure.  Otherwise, an appropriate
 // uncommon trap or exception is thrown.
-Node* GraphKit::gen_checkcast(Node *obj, Node* superklass,
-                              Node* *failure_control) {
+Node* GraphKit::gen_checkcast(Node *obj, Node* superklass, Node* *failure_control, bool null_free) {
   kill_dead_locals();           // Benefit all the uncommon traps
-  const TypeKlassPtr *tk = _gvn.type(superklass)->is_klassptr()->try_improve();
-  const TypeOopPtr *toop = tk->cast_to_exactness(false)->as_instance_type();
+  const TypeKlassPtr* klass_ptr_type = _gvn.type(superklass)->is_klassptr();
+  const TypeKlassPtr* improved_klass_ptr_type = klass_ptr_type->try_improve();
+  const TypeOopPtr* toop = improved_klass_ptr_type->cast_to_exactness(false)->as_instance_type();
+  bool safe_for_replace = (failure_control == nullptr);
+  assert(!null_free || toop->is_inlinetypeptr(), "must be an inline type pointer");
 
   // Fast cutout:  Check the case that the cast is vacuously true.
   // This detects the common cases where the test will short-circuit
   // away completely.  We do this before we perform the null check,
   // because if the test is going to turn into zero code, we don't
   // want a residual null check left around.  (Causes a slowdown,
   // for example, in some objArray manipulations, such as a[i]=a[j].)
-  if (tk->singleton()) {
-    const TypeOopPtr* objtp = _gvn.type(obj)->isa_oopptr();
-    if (objtp != nullptr) {
-      switch (C->static_subtype_check(tk, objtp->as_klass_type())) {
+  if (improved_klass_ptr_type->singleton()) {
+    const TypeKlassPtr* kptr = nullptr;
+    const Type* t = _gvn.type(obj);
+    if (t->isa_oop_ptr()) {
+      kptr = t->is_oopptr()->as_klass_type();
+    } else if (obj->is_InlineType()) {
+      ciInlineKlass* vk = t->inline_klass();
+      kptr = TypeInstKlassPtr::make(TypePtr::NotNull, vk, Type::Offset(0));
+    }
+    if (kptr != nullptr) {
+      switch (C->static_subtype_check(improved_klass_ptr_type, kptr)) {
       case Compile::SSC_always_true:
         // If we know the type check always succeed then we don't use
         // the profiling data at this bytecode. Don't lose it, feed it
         // to the type system as a speculative type.
-        return record_profiled_receiver_for_speculation(obj);
+        obj = record_profiled_receiver_for_speculation(obj);
+        if (null_free) {
+          assert(safe_for_replace, "must be");
+          obj = null_check(obj);
+        }
+        assert(stopped() || !toop->is_inlinetypeptr() || obj->is_InlineType(), "should have been scalarized");
+        return obj;
       case Compile::SSC_always_false:
+        if (null_free) {
+          assert(safe_for_replace, "must be");
+          obj = null_check(obj);
+        }
         // It needs a null check because a null will *pass* the cast check.
-        // A non-null value will always produce an exception.
-        if (!objtp->maybe_null()) {
+        if (t->isa_oopptr() != nullptr && !t->is_oopptr()->maybe_null()) {
           bool is_aastore = (java_bc() == Bytecodes::_aastore);
           Deoptimization::DeoptReason reason = is_aastore ?
             Deoptimization::Reason_array_check : Deoptimization::Reason_class_check;
           builtin_throw(reason);
           return top();
@@ -3291,37 +3499,56 @@
       }
     }
   }
 
   ciProfileData* data = nullptr;
-  bool safe_for_replace = false;
   if (failure_control == nullptr) {        // use MDO in regular case only
     assert(java_bc() == Bytecodes::_aastore ||
            java_bc() == Bytecodes::_checkcast,
            "interpreter profiles type checks only for these BCs");
-    data = method()->method_data()->bci_to_data(bci());
-    safe_for_replace = true;
+    if (method()->method_data()->is_mature()) {
+      data = method()->method_data()->bci_to_data(bci());
+    }
   }
 
   // Make the merge point
   enum { _obj_path = 1, _null_path, PATH_LIMIT };
   RegionNode* region = new RegionNode(PATH_LIMIT);
   Node*       phi    = new PhiNode(region, toop);
+  _gvn.set_type(region, Type::CONTROL);
+  _gvn.set_type(phi, toop);
+
   C->set_has_split_ifs(true); // Has chance for split-if optimization
 
   // Use null-cast information if it is available
   bool speculative_not_null = false;
   bool never_see_null = ((failure_control == nullptr)  // regular case only
                          && seems_never_null(obj, data, speculative_not_null));
 
+  if (obj->is_InlineType()) {
+    // Re-execute if buffering during triggers deoptimization
+    PreserveReexecuteState preexecs(this);
+    jvms()->set_should_reexecute(true);
+    obj = obj->as_InlineType()->buffer(this, safe_for_replace);
+  }
+
   // Null check; get casted pointer; set region slot 3
   Node* null_ctl = top();
-  Node* not_null_obj = null_check_oop(obj, &null_ctl, never_see_null, safe_for_replace, speculative_not_null);
+  Node* not_null_obj = nullptr;
+  if (null_free) {
+    assert(safe_for_replace, "must be");
+    not_null_obj = null_check(obj);
+  } else {
+    not_null_obj = null_check_oop(obj, &null_ctl, never_see_null, safe_for_replace, speculative_not_null);
+  }
 
   // If not_null_obj is dead, only null-path is taken
   if (stopped()) {              // Doing instance-of on a null?
     set_control(null_ctl);
+    if (toop->is_inlinetypeptr()) {
+      return InlineTypeNode::make_null(_gvn, toop->inline_klass());
+    }
     return null();
   }
   region->init_req(_null_path, null_ctl);
   phi   ->init_req(_null_path, null());  // Set null path value
   if (null_ctl == top()) {
@@ -3331,21 +3558,21 @@
     region->del_req(_null_path);
     phi   ->del_req(_null_path);
   }
 
   Node* cast_obj = nullptr;
-  if (tk->klass_is_exact()) {
+  if (improved_klass_ptr_type->klass_is_exact()) {
     // The following optimization tries to statically cast the speculative type of the object
     // (for example obtained during profiling) to the type of the superklass and then do a
     // dynamic check that the type of the object is what we expect. To work correctly
     // for checkcast and aastore the type of superklass should be exact.
     const TypeOopPtr* obj_type = _gvn.type(obj)->is_oopptr();
     // We may not have profiling here or it may not help us. If we have
     // a speculative type use it to perform an exact cast.
     ciKlass* spec_obj_type = obj_type->speculative_type();
     if (spec_obj_type != nullptr || data != nullptr) {
-      cast_obj = maybe_cast_profiled_receiver(not_null_obj, tk, spec_obj_type, safe_for_replace);
+      cast_obj = maybe_cast_profiled_receiver(not_null_obj, improved_klass_ptr_type, spec_obj_type, safe_for_replace);
       if (cast_obj != nullptr) {
         if (failure_control != nullptr) // failure is now impossible
           (*failure_control) = top();
         // adjust the type of the phi to the exact klass:
         phi->raise_bottom_type(_gvn.type(cast_obj)->meet_speculative(TypePtr::NULL_PTR));
@@ -3353,19 +3580,31 @@
     }
   }
 
   if (cast_obj == nullptr) {
     // Generate the subtype check
-    Node* not_subtype_ctrl = gen_subtype_check(not_null_obj, superklass );
-
+    Node* improved_superklass = superklass;
+    if (improved_klass_ptr_type != klass_ptr_type && improved_klass_ptr_type->singleton()) {
+      // Only improve the super class for constants which allows subsequent sub type checks to possibly be commoned up.
+      // The other non-constant cases cannot be improved with a cast node here since they could be folded to top.
+      // Additionally, the benefit would only be minor in non-constant cases.
+      improved_superklass = makecon(improved_klass_ptr_type);
+    }
+    Node* not_subtype_ctrl = gen_subtype_check(not_null_obj, improved_superklass);
     // Plug in success path into the merge
     cast_obj = _gvn.transform(new CheckCastPPNode(control(), not_null_obj, toop));
     // Failure path ends in uncommon trap (or may be dead - failure impossible)
     if (failure_control == nullptr) {
       if (not_subtype_ctrl != top()) { // If failure is possible
         PreserveJVMState pjvms(this);
         set_control(not_subtype_ctrl);
+        Node* obj_klass = nullptr;
+        if (not_null_obj->is_InlineType()) {
+          obj_klass = makecon(TypeKlassPtr::make(_gvn.type(not_null_obj)->inline_klass()));
+        } else {
+          obj_klass = load_object_klass(not_null_obj);
+        }
         bool is_aastore = (java_bc() == Bytecodes::_aastore);
         Deoptimization::DeoptReason reason = is_aastore ?
           Deoptimization::Reason_array_check : Deoptimization::Reason_class_check;
         builtin_throw(reason);
       }
@@ -3392,11 +3631,126 @@
 
   // Return final merged results
   set_control( _gvn.transform(region) );
   record_for_igvn(region);
 
-  return record_profiled_receiver_for_speculation(res);
+  bool not_inline = !toop->can_be_inline_type();
+  bool not_flat_in_array = !UseFlatArray || not_inline || (toop->is_inlinetypeptr() && !toop->inline_klass()->flat_in_array());
+  if (EnableValhalla && not_flat_in_array) {
+    // Check if obj has been loaded from an array
+    obj = obj->isa_DecodeN() ? obj->in(1) : obj;
+    Node* array = nullptr;
+    if (obj->isa_Load()) {
+      Node* address = obj->in(MemNode::Address);
+      if (address->isa_AddP()) {
+        array = address->as_AddP()->in(AddPNode::Base);
+      }
+    } else if (obj->is_Phi()) {
+      Node* region = obj->in(0);
+      // TODO make this more robust (see JDK-8231346)
+      if (region->req() == 3 && region->in(2) != nullptr && region->in(2)->in(0) != nullptr) {
+        IfNode* iff = region->in(2)->in(0)->isa_If();
+        if (iff != nullptr) {
+          iff->is_flat_array_check(&_gvn, &array);
+        }
+      }
+    }
+    if (array != nullptr) {
+      const TypeAryPtr* ary_t = _gvn.type(array)->isa_aryptr();
+      if (ary_t != nullptr && !ary_t->is_flat()) {
+        if (!ary_t->is_not_null_free() && not_inline) {
+          // Casting array element to a non-inline-type, mark array as not null-free.
+          Node* cast = _gvn.transform(new CheckCastPPNode(control(), array, ary_t->cast_to_not_null_free()));
+          replace_in_map(array, cast);
+        } else if (!ary_t->is_not_flat()) {
+          // Casting array element to a non-flat type, mark array as not flat.
+          Node* cast = _gvn.transform(new CheckCastPPNode(control(), array, ary_t->cast_to_not_flat()));
+          replace_in_map(array, cast);
+        }
+      }
+    }
+  }
+
+  if (!stopped() && !res->is_InlineType()) {
+    res = record_profiled_receiver_for_speculation(res);
+    if (toop->is_inlinetypeptr()) {
+      Node* vt = InlineTypeNode::make_from_oop(this, res, toop->inline_klass(), !gvn().type(res)->maybe_null());
+      res = vt;
+      if (safe_for_replace) {
+        replace_in_map(obj, vt);
+        replace_in_map(not_null_obj, vt);
+        replace_in_map(res, vt);
+      }
+    }
+  }
+  return res;
+}
+
+Node* GraphKit::inline_type_test(Node* obj, bool is_inline) {
+  Node* mark_adr = basic_plus_adr(obj, oopDesc::mark_offset_in_bytes());
+  Node* mark = make_load(nullptr, mark_adr, TypeX_X, TypeX_X->basic_type(), MemNode::unordered);
+  Node* mask = MakeConX(markWord::inline_type_pattern);
+  Node* masked = _gvn.transform(new AndXNode(mark, mask));
+  Node* cmp = _gvn.transform(new CmpXNode(masked, mask));
+  return _gvn.transform(new BoolNode(cmp, is_inline ? BoolTest::eq : BoolTest::ne));
+}
+
+Node* GraphKit::array_lh_test(Node* klass, jint mask, jint val, bool eq) {
+  Node* lh_adr = basic_plus_adr(klass, in_bytes(Klass::layout_helper_offset()));
+  // Make sure to use immutable memory here to enable hoisting the check out of loops
+  Node* lh_val = _gvn.transform(LoadNode::make(_gvn, nullptr, immutable_memory(), lh_adr, lh_adr->bottom_type()->is_ptr(), TypeInt::INT, T_INT, MemNode::unordered));
+  Node* masked = _gvn.transform(new AndINode(lh_val, intcon(mask)));
+  Node* cmp = _gvn.transform(new CmpINode(masked, intcon(val)));
+  return _gvn.transform(new BoolNode(cmp, eq ? BoolTest::eq : BoolTest::ne));
+}
+
+// TODO 8325106 With JEP 401, flatness is not a property of the Class anymore.
+Node* GraphKit::flat_array_test(Node* array_or_klass, bool flat) {
+  // We can't use immutable memory here because the mark word is mutable.
+  // PhaseIdealLoop::move_flat_array_check_out_of_loop will make sure the
+  // check is moved out of loops (mainly to enable loop unswitching).
+  Node* mem = UseArrayMarkWordCheck ? memory(Compile::AliasIdxRaw) : immutable_memory();
+  Node* cmp = _gvn.transform(new FlatArrayCheckNode(C, mem, array_or_klass));
+  record_for_igvn(cmp); // Give it a chance to be optimized out by IGVN
+  return _gvn.transform(new BoolNode(cmp, flat ? BoolTest::eq : BoolTest::ne));
+}
+
+Node* GraphKit::null_free_array_test(Node* klass, bool null_free) {
+  return array_lh_test(klass, Klass::_lh_null_free_array_bit_inplace, 0, !null_free);
+}
+
+// Deoptimize if 'ary' is a null-free inline type array and 'val' is null
+Node* GraphKit::inline_array_null_guard(Node* ary, Node* val, int nargs, bool safe_for_replace) {
+  RegionNode* region = new RegionNode(3);
+  Node* null_ctl = top();
+  null_check_oop(val, &null_ctl);
+  if (null_ctl != top()) {
+    PreserveJVMState pjvms(this);
+    set_control(null_ctl);
+    {
+      // Deoptimize if null-free array
+      BuildCutout unless(this, null_free_array_test(load_object_klass(ary), /* null_free = */ false), PROB_MAX);
+      inc_sp(nargs);
+      uncommon_trap(Deoptimization::Reason_null_check,
+                    Deoptimization::Action_none);
+    }
+    region->init_req(1, control());
+  }
+  region->init_req(2, control());
+  set_control(_gvn.transform(region));
+  record_for_igvn(region);
+  if (_gvn.type(val) == TypePtr::NULL_PTR) {
+    // Since we were just successfully storing null, the array can't be null free.
+    const TypeAryPtr* ary_t = _gvn.type(ary)->is_aryptr();
+    ary_t = ary_t->cast_to_not_null_free();
+    Node* cast = _gvn.transform(new CheckCastPPNode(control(), ary, ary_t));
+    if (safe_for_replace) {
+      replace_in_map(ary, cast);
+    }
+    ary = cast;
+  }
+  return ary;
 }
 
 //------------------------------next_monitor-----------------------------------
 // What number should be given to the next monitor?
 int GraphKit::next_monitor() {
@@ -3460,10 +3814,11 @@
   // %%% SynchronizationEntryBCI is redundant; use InvocationEntryBci in interfaces
   assert(SynchronizationEntryBCI == InvocationEntryBci, "");
 
   if( !GenerateSynchronizationCode )
     return nullptr;                // Not locking things?
+
   if (stopped())                // Dead monitor?
     return nullptr;
 
   assert(dead_locals_are_killed(), "should kill locals before sync. point");
 
@@ -3528,10 +3883,11 @@
     return;
   if (stopped()) {               // Dead monitor?
     map()->pop_monitor();        // Kill monitor from debug info
     return;
   }
+  assert(!obj->is_InlineType(), "should not unlock on inline type");
 
   // Memory barrier to avoid floating things down past the locked region
   insert_mem_bar(Op_MemBarReleaseLock);
 
   const TypeFunc *tf = OptoRuntime::complete_monitor_exit_Type();
@@ -3568,14 +3924,24 @@
 // almost always feature constant types.
 Node* GraphKit::get_layout_helper(Node* klass_node, jint& constant_value) {
   const TypeKlassPtr* klass_t = _gvn.type(klass_node)->isa_klassptr();
   if (!StressReflectiveCode && klass_t != nullptr) {
     bool xklass = klass_t->klass_is_exact();
-    if (xklass || (klass_t->isa_aryklassptr() && klass_t->is_aryklassptr()->elem() != Type::BOTTOM)) {
+    bool can_be_flat = false;
+    const TypeAryPtr* ary_type = klass_t->as_instance_type()->isa_aryptr();
+    if (UseFlatArray && !xklass && ary_type != nullptr && !ary_type->is_null_free()) {
+      // TODO 8325106 Fix comment
+      // The runtime type of [LMyValue might be [QMyValue due to [QMyValue <: [LMyValue. Don't constant fold.
+      const TypeOopPtr* elem = ary_type->elem()->make_oopptr();
+      can_be_flat = ary_type->can_be_inline_array() && (!elem->is_inlinetypeptr() || elem->inline_klass()->flat_in_array());
+    }
+    if (!can_be_flat && (xklass || (klass_t->isa_aryklassptr() && klass_t->is_aryklassptr()->elem() != Type::BOTTOM))) {
       jint lhelper;
-      if (klass_t->isa_aryklassptr()) {
-        BasicType elem = klass_t->as_instance_type()->isa_aryptr()->elem()->array_element_basic_type();
+      if (klass_t->is_flat()) {
+        lhelper = ary_type->flat_layout_helper();
+      } else if (klass_t->isa_aryklassptr()) {
+        BasicType elem = ary_type->elem()->array_element_basic_type();
         if (is_reference_type(elem, true)) {
           elem = T_OBJECT;
         }
         lhelper = Klass::array_layout_helper(elem);
       } else {
@@ -3600,11 +3966,13 @@
   DEBUG_ONLY(Node* init_in_raw = init_in_merge->base_memory());
   assert(init_in_merge->memory_at(alias_idx) == init_in_raw, "");
 
   Node* prevmem = kit.memory(alias_idx);
   init_in_merge->set_memory_at(alias_idx, prevmem);
-  kit.set_memory(init_out_raw, alias_idx);
+  if (init_out_raw != nullptr) {
+    kit.set_memory(init_out_raw, alias_idx);
+  }
 }
 
 //---------------------------set_output_for_allocation-------------------------
 Node* GraphKit::set_output_for_allocation(AllocateNode* alloc,
                                           const TypeOopPtr* oop_type,
@@ -3639,21 +4007,48 @@
     // and link them properly (as a group) to the InitializeNode.
     assert(init->in(InitializeNode::Memory) == malloc, "");
     MergeMemNode* minit_in = MergeMemNode::make(malloc);
     init->set_req(InitializeNode::Memory, minit_in);
     record_for_igvn(minit_in); // fold it up later, if possible
+    _gvn.set_type(minit_in, Type::MEMORY);
     Node* minit_out = memory(rawidx);
     assert(minit_out->is_Proj() && minit_out->in(0) == init, "");
     // Add an edge in the MergeMem for the header fields so an access
     // to one of those has correct memory state
     set_memory(minit_out, C->get_alias_index(oop_type->add_offset(oopDesc::mark_offset_in_bytes())));
     set_memory(minit_out, C->get_alias_index(oop_type->add_offset(oopDesc::klass_offset_in_bytes())));
     if (oop_type->isa_aryptr()) {
-      const TypePtr* telemref = oop_type->add_offset(Type::OffsetBot);
-      int            elemidx  = C->get_alias_index(telemref);
-      hook_memory_on_init(*this, elemidx, minit_in, minit_out);
+      const TypeAryPtr* arytype = oop_type->is_aryptr();
+      if (arytype->is_flat()) {
+        // Initially all flat array accesses share a single slice
+        // but that changes after parsing. Prepare the memory graph so
+        // it can optimize flat array accesses properly once they
+        // don't share a single slice.
+        assert(C->flat_accesses_share_alias(), "should be set at parse time");
+        C->set_flat_accesses_share_alias(false);
+        ciInlineKlass* vk = arytype->elem()->inline_klass();
+        for (int i = 0, len = vk->nof_nonstatic_fields(); i < len; i++) {
+          ciField* field = vk->nonstatic_field_at(i);
+          if (field->offset_in_bytes() >= TrackedInitializationLimit * HeapWordSize)
+            continue;  // do not bother to track really large numbers of fields
+          int off_in_vt = field->offset_in_bytes() - vk->first_field_offset();
+          const TypePtr* adr_type = arytype->with_field_offset(off_in_vt)->add_offset(Type::OffsetBot);
+          int fieldidx = C->get_alias_index(adr_type, true);
+          // Pass nullptr for init_out. Having per flat array element field memory edges as uses of the Initialize node
+          // can result in per flat array field Phis to be created which confuses the logic of
+          // Compile::adjust_flat_array_access_aliases().
+          hook_memory_on_init(*this, fieldidx, minit_in, nullptr);
+        }
+        C->set_flat_accesses_share_alias(true);
+        hook_memory_on_init(*this, C->get_alias_index(TypeAryPtr::INLINES), minit_in, minit_out);
+      } else {
+        const TypePtr* telemref = oop_type->add_offset(Type::OffsetBot);
+        int            elemidx  = C->get_alias_index(telemref);
+        hook_memory_on_init(*this, elemidx, minit_in, minit_out);
+      }
     } else if (oop_type->isa_instptr()) {
+      set_memory(minit_out, C->get_alias_index(oop_type)); // mark word
       ciInstanceKlass* ik = oop_type->is_instptr()->instance_klass();
       for (int i = 0, len = ik->nof_nonstatic_fields(); i < len; i++) {
         ciField* field = ik->nonstatic_field_at(i);
         if (field->offset_in_bytes() >= TrackedInitializationLimit * HeapWordSize)
           continue;  // do not bother to track really large numbers of fields
@@ -3700,18 +4095,19 @@
 //  - If 'return_size_val', report the total object size to the caller.
 //  - deoptimize_on_exception controls how Java exceptions are handled (rethrow vs deoptimize)
 Node* GraphKit::new_instance(Node* klass_node,
                              Node* extra_slow_test,
                              Node* *return_size_val,
-                             bool deoptimize_on_exception) {
+                             bool deoptimize_on_exception,
+                             InlineTypeNode* inline_type_node) {
   // Compute size in doublewords
   // The size is always an integral number of doublewords, represented
   // as a positive bytewise size stored in the klass's layout_helper.
   // The layout_helper also encodes (in a low bit) the need for a slow path.
   jint  layout_con = Klass::_lh_neutral_value;
   Node* layout_val = get_layout_helper(klass_node, layout_con);
-  int   layout_is_con = (layout_val == nullptr);
+  bool  layout_is_con = (layout_val == nullptr);
 
   if (extra_slow_test == nullptr)  extra_slow_test = intcon(0);
   // Generate the initial go-slow test.  It's either ALWAYS (return a
   // Node for 1) or NEVER (return a null) or perhaps (in the reflective
   // case) a computed value derived from the layout_helper.
@@ -3758,24 +4154,24 @@
   const TypeOopPtr* oop_type = tklass->as_instance_type();
 
   // Now generate allocation code
 
   // The entire memory state is needed for slow path of the allocation
-  // since GC and deoptimization can happened.
+  // since GC and deoptimization can happen.
   Node *mem = reset_memory();
   set_all_memory(mem); // Create new memory state
 
   AllocateNode* alloc = new AllocateNode(C, AllocateNode::alloc_type(Type::TOP),
                                          control(), mem, i_o(),
                                          size, klass_node,
-                                         initial_slow_test);
+                                         initial_slow_test, inline_type_node);
 
   return set_output_for_allocation(alloc, oop_type, deoptimize_on_exception);
 }
 
 //-------------------------------new_array-------------------------------------
-// helper for both newarray and anewarray
+// helper for newarray and anewarray
 // The 'length' parameter is (obviously) the length of the array.
 // The optional arguments are for specialized use by intrinsics:
 //  - If 'return_size_val', report the non-padded array size (sum of header size
 //    and array body) to the caller.
 //  - deoptimize_on_exception controls how Java exceptions are handled (rethrow vs deoptimize)
@@ -3784,11 +4180,11 @@
                           int   nargs,          // number of arguments to push back for uncommon trap
                           Node* *return_size_val,
                           bool deoptimize_on_exception) {
   jint  layout_con = Klass::_lh_neutral_value;
   Node* layout_val = get_layout_helper(klass_node, layout_con);
-  int   layout_is_con = (layout_val == nullptr);
+  bool  layout_is_con = (layout_val == nullptr);
 
   if (!layout_is_con && !StressReflectiveCode &&
       !too_many_traps(Deoptimization::Reason_class_check)) {
     // This is a reflective array creation site.
     // Optimistically assume that it is a subtype of Object[],
@@ -3814,11 +4210,11 @@
   int fast_size_limit = FastAllocateSizeLimit;
   if (layout_is_con) {
     assert(!StressReflectiveCode, "stress mode does not use these paths");
     // Increase the size limit if we have exact knowledge of array type.
     int log2_esize = Klass::layout_helper_log2_element_size(layout_con);
-    fast_size_limit <<= (LogBytesPerLong - log2_esize);
+    fast_size_limit <<= MAX2(LogBytesPerLong - log2_esize, 0);
   }
 
   Node* initial_slow_cmp  = _gvn.transform( new CmpUNode( length, intcon( fast_size_limit ) ) );
   Node* initial_slow_test = _gvn.transform( new BoolNode( initial_slow_cmp, BoolTest::gt ) );
 
@@ -3831,13 +4227,14 @@
   Node* header_size = nullptr;
   // (T_BYTE has the weakest alignment and size restrictions...)
   if (layout_is_con) {
     int       hsize  = Klass::layout_helper_header_size(layout_con);
     int       eshift = Klass::layout_helper_log2_element_size(layout_con);
+    bool is_flat_array = Klass::layout_helper_is_flatArray(layout_con);
     if ((round_mask & ~right_n_bits(eshift)) == 0)
       round_mask = 0;  // strength-reduce it if it goes away completely
-    assert((hsize & right_n_bits(eshift)) == 0, "hsize is pre-rounded");
+    assert(is_flat_array || (hsize & right_n_bits(eshift)) == 0, "hsize is pre-rounded");
     int header_size_min = arrayOopDesc::base_offset_in_bytes(T_BYTE);
     assert(header_size_min <= hsize, "generic minimum is smallest");
     header_size = intcon(hsize);
   } else {
     Node* hss   = intcon(Klass::_lh_header_size_shift);
@@ -3920,20 +4317,49 @@
   // else if round_mask == 0, the size computation is self-rounding
 
   // Now generate allocation code
 
   // The entire memory state is needed for slow path of the allocation
-  // since GC and deoptimization can happened.
+  // since GC and deoptimization can happen.
   Node *mem = reset_memory();
   set_all_memory(mem); // Create new memory state
 
   if (initial_slow_test->is_Bool()) {
     // Hide it behind a CMoveI, or else PhaseIdealLoop::split_up will get sick.
     initial_slow_test = initial_slow_test->as_Bool()->as_int_value(&_gvn);
   }
 
-  const TypeOopPtr* ary_type = _gvn.type(klass_node)->is_klassptr()->as_instance_type();
+  const TypeKlassPtr* ary_klass = _gvn.type(klass_node)->isa_klassptr();
+  const TypeOopPtr* ary_type = ary_klass->as_instance_type();
+  const TypeAryPtr* ary_ptr = ary_type->isa_aryptr();
+
+  // TODO 8325106 Fix comment
+  // Inline type array variants:
+  // - null-ok:              MyValue.ref[] (ciObjArrayKlass "[LMyValue")
+  // - null-free:            MyValue.val[] (ciObjArrayKlass "[QMyValue")
+  // - null-free, flat     : MyValue.val[] (ciFlatArrayKlass "[QMyValue")
+  // Check if array is a null-free, non-flat inline type array
+  // that needs to be initialized with the default inline type.
+  Node* default_value = nullptr;
+  Node* raw_default_value = nullptr;
+  if (ary_ptr != nullptr && ary_ptr->klass_is_exact()) {
+    // Array type is known
+    if (ary_ptr->is_null_free() && !ary_ptr->is_flat()) {
+      ciInlineKlass* vk = ary_ptr->elem()->inline_klass();
+      default_value = InlineTypeNode::default_oop(gvn(), vk);
+      if (UseCompressedOops) {
+        // With compressed oops, the 64-bit init value is built from two 32-bit compressed oops
+        default_value = _gvn.transform(new EncodePNode(default_value, default_value->bottom_type()->make_narrowoop()));
+        Node* lower = _gvn.transform(new CastP2XNode(control(), default_value));
+        Node* upper = _gvn.transform(new LShiftLNode(lower, intcon(32)));
+        raw_default_value = _gvn.transform(new OrLNode(lower, upper));
+      } else {
+        raw_default_value = _gvn.transform(new CastP2XNode(control(), default_value));
+      }
+    }
+  }
+
   Node* valid_length_test = _gvn.intcon(1);
   if (ary_type->isa_aryptr()) {
     BasicType bt = ary_type->isa_aryptr()->elem()->array_element_basic_type();
     jint max = TypeAryPtr::max_array_length(bt);
     Node* valid_length_cmp  = _gvn.transform(new CmpUNode(length, intcon(max)));
@@ -3944,12 +4370,12 @@
   AllocateArrayNode* alloc
     = new AllocateArrayNode(C, AllocateArrayNode::alloc_type(TypeInt::INT),
                             control(), mem, i_o(),
                             size, klass_node,
                             initial_slow_test,
-                            length, valid_length_test);
-
+                            length, valid_length_test,
+                            default_value, raw_default_value);
   // Cast to correct type.  Note that the klass_node may be constant or not,
   // and in the latter case the actual array type will be inexact also.
   // (This happens via a non-constant argument to inline_native_newArray.)
   // In any case, the value of klass_node provides the desired array type.
   const TypeInt* length_type = _gvn.find_int_type(length);
@@ -4092,15 +4518,15 @@
 }
 
 Node* GraphKit::load_String_value(Node* str, bool set_ctrl) {
   int value_offset = java_lang_String::value_offset();
   const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(),
-                                                     false, nullptr, 0);
+                                                     false, nullptr, Type::Offset(0));
   const TypePtr* value_field_type = string_type->add_offset(value_offset);
   const TypeAryPtr* value_type = TypeAryPtr::make(TypePtr::NotNull,
-                                                  TypeAry::make(TypeInt::BYTE, TypeInt::POS),
-                                                  ciTypeArrayKlass::make(T_BYTE), true, 0);
+                                                  TypeAry::make(TypeInt::BYTE, TypeInt::POS, false, false, true, true),
+                                                  ciTypeArrayKlass::make(T_BYTE), true, Type::Offset(0));
   Node* p = basic_plus_adr(str, str, value_offset);
   Node* load = access_load_at(str, p, value_field_type, value_type, T_OBJECT,
                               IN_HEAP | (set_ctrl ? C2_CONTROL_DEPENDENT_LOAD : 0) | MO_UNORDERED);
   return load;
 }
@@ -4109,11 +4535,11 @@
   if (!CompactStrings) {
     return intcon(java_lang_String::CODER_UTF16);
   }
   int coder_offset = java_lang_String::coder_offset();
   const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(),
-                                                     false, nullptr, 0);
+                                                     false, nullptr, Type::Offset(0));
   const TypePtr* coder_field_type = string_type->add_offset(coder_offset);
 
   Node* p = basic_plus_adr(str, str, coder_offset);
   Node* load = access_load_at(str, p, coder_field_type, TypeInt::BYTE, T_BYTE,
                               IN_HEAP | (set_ctrl ? C2_CONTROL_DEPENDENT_LOAD : 0) | MO_UNORDERED);
@@ -4121,21 +4547,21 @@
 }
 
 void GraphKit::store_String_value(Node* str, Node* value) {
   int value_offset = java_lang_String::value_offset();
   const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(),
-                                                     false, nullptr, 0);
+                                                     false, nullptr, Type::Offset(0));
   const TypePtr* value_field_type = string_type->add_offset(value_offset);
 
   access_store_at(str,  basic_plus_adr(str, value_offset), value_field_type,
                   value, TypeAryPtr::BYTES, T_OBJECT, IN_HEAP | MO_UNORDERED);
 }
 
 void GraphKit::store_String_coder(Node* str, Node* value) {
   int coder_offset = java_lang_String::coder_offset();
   const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(),
-                                                     false, nullptr, 0);
+                                                     false, nullptr, Type::Offset(0));
   const TypePtr* coder_field_type = string_type->add_offset(coder_offset);
 
   access_store_at(str, basic_plus_adr(str, coder_offset), coder_field_type,
                   value, TypeInt::BYTE, T_BYTE, IN_HEAP | MO_UNORDERED);
 }
@@ -4244,9 +4670,24 @@
     }
   }
   const Type* con_type = Type::make_constant_from_field(field, holder, field->layout_type(),
                                                         /*is_unsigned_load=*/false);
   if (con_type != nullptr) {
-    return makecon(con_type);
+    Node* con = makecon(con_type);
+    if (field->type()->is_inlinetype()) {
+      con = InlineTypeNode::make_from_oop(this, con, field->type()->as_inline_klass(), field->is_null_free());
+    } else if (con_type->is_inlinetypeptr()) {
+      con = InlineTypeNode::make_from_oop(this, con, con_type->inline_klass(), field->is_null_free());
+    }
+    return con;
   }
   return nullptr;
 }
+
+//---------------------------load_mirror_from_klass----------------------------
+// Given a klass oop, load its java mirror (a java.lang.Class oop).
+Node* GraphKit::load_mirror_from_klass(Node* klass) {
+  Node* p = basic_plus_adr(klass, in_bytes(Klass::java_mirror_offset()));
+  Node* load = make_load(nullptr, p, TypeRawPtr::NOTNULL, T_ADDRESS, MemNode::unordered);
+  // mirror = ((OopHandle)mirror)->resolve();
+  return access_load(load, TypeInstPtr::MIRROR, T_OBJECT, IN_NATIVE);
+}
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
@@ -31,10 +31,11 @@
 #include "opto/addnode.hpp"
 #include "opto/callnode.hpp"
 #include "opto/cfgnode.hpp"
 #include "opto/compile.hpp"
 #include "opto/divnode.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/mulnode.hpp"
 #include "opto/phaseX.hpp"
 #include "opto/subnode.hpp"
 #include "opto/type.hpp"
 #include "runtime/deoptimization.hpp"
@@ -64,10 +65,13 @@
   SafePointNode*    _map;       // Parser map from JVM to Nodes
   SafePointNode*    _exceptions;// Parser map(s) for exception state(s)
   int               _bci;       // JVM Bytecode Pointer
   ciMethod*         _method;    // JVM Current Method
   BarrierSetC2*     _barrier_set;
+#ifdef ASSERT
+  uint              _worklist_size;
+#endif
 
  private:
   int               _sp;        // JVM Expression Stack Pointer; don't modify directly!
 
  private:
@@ -76,27 +80,34 @@
     return _map;
   }
 
  public:
   GraphKit();                   // empty constructor
-  GraphKit(JVMState* jvms);     // the JVM state on which to operate
+  GraphKit(JVMState* jvms, PhaseGVN* gvn = nullptr);     // the JVM state on which to operate
 
 #ifdef ASSERT
   ~GraphKit() {
     assert(failing() || !has_exceptions(),
            "unless compilation failed, user must call transfer_exceptions_into_jvms");
+#if 0
+    // During incremental inlining, the Node_Array of the C->for_igvn() worklist and the IGVN
+    // worklist are shared but the _in_worklist VectorSet is not. To avoid inconsistencies,
+    // we should not add nodes to the _for_igvn worklist when using IGVN for the GraphKit.
+    assert((_gvn.is_IterGVN() == nullptr) || (_gvn.C->for_igvn()->size() == _worklist_size),
+           "GraphKit should not modify _for_igvn worklist after parsing");
+#endif
   }
 #endif
 
   virtual Parse*          is_Parse()          const { return nullptr; }
   virtual LibraryCallKit* is_LibraryCallKit() const { return nullptr; }
 
   ciEnv*        env()               const { return _env; }
   PhaseGVN&     gvn()               const { return _gvn; }
   void*         barrier_set_state() const { return C->barrier_set_state(); }
 
-  void record_for_igvn(Node* n) const { C->record_for_igvn(n); }  // delegate to Compile
+  void record_for_igvn(Node* n) const { _gvn.record_for_igvn(n); }
   void remove_for_igvn(Node* n) const { C->remove_for_igvn(n); }
 
   // Handy well-known nodes:
   Node*         null()          const { return zerocon(T_OBJECT); }
   Node*         top()           const { return C->top(); }
@@ -359,16 +370,16 @@
   // Return the value cast to not-null.
   // Be clever about equivalent dominating null checks.
   Node* null_check_common(Node* value, BasicType type,
                           bool assert_null = false,
                           Node* *null_control = nullptr,
-                          bool speculative = false);
+                          bool speculative = false,
+                          bool is_init_check = false);
   Node* null_check(Node* value, BasicType type = T_OBJECT) {
     return null_check_common(value, type, false, nullptr, !_gvn.type(value)->speculative_maybe_null());
   }
   Node* null_check_receiver() {
-    assert(argument(0)->bottom_type()->isa_ptr(), "must be");
     return null_check(argument(0));
   }
   Node* zero_check_int(Node* value) {
     assert(value->bottom_type()->basic_type() == T_INT,
            "wrong type: %s", type2name(value->bottom_type()->basic_type()));
@@ -602,18 +613,20 @@
                         Node* adr,   // actual address to store val at
                         const TypePtr* adr_type,
                         Node* val,
                         const Type* val_type,
                         BasicType bt,
-                        DecoratorSet decorators);
+                        DecoratorSet decorators,
+                        bool safe_for_replace = true);
 
   Node* access_load_at(Node* obj,   // containing obj
                        Node* adr,   // actual address to load val at
                        const TypePtr* adr_type,
                        const Type* val_type,
                        BasicType bt,
-                       DecoratorSet decorators);
+                       DecoratorSet decorators,
+                       Node* ctl = nullptr);
 
   Node* access_load(Node* adr,   // actual address to load val at
                     const Type* val_type,
                     BasicType bt,
                     DecoratorSet decorators);
@@ -701,11 +714,11 @@
     return n;
   }
 
   // Fill in argument edges for the call from argument(0), argument(1), ...
   // (The next step is to call set_edges_for_java_call.)
-  void  set_arguments_for_java_call(CallJavaNode* call);
+  void  set_arguments_for_java_call(CallJavaNode* call, bool is_late_inline = false);
 
   // Fill in non-argument edges for the call.
   // Transform the call, and update the basics: control, i_o, memory.
   // (The next step is usually to call set_results_for_java_call.)
   void set_edges_for_java_call(CallJavaNode* call,
@@ -840,20 +853,27 @@
   // and the reflective instance-of call.
   Node* gen_instanceof(Node *subobj, Node* superkls, bool safe_for_replace = false);
 
   // Generate a check-cast idiom.  Used by both the check-cast bytecode
   // and the array-store bytecode
-  Node* gen_checkcast( Node *subobj, Node* superkls,
-                       Node* *failure_control = nullptr );
+  Node* gen_checkcast(Node *subobj, Node* superkls, Node* *failure_control = nullptr, bool null_free = false);
+
+  // Inline types
+  Node* inline_type_test(Node* obj, bool is_inline = true);
+  Node* array_lh_test(Node* kls, jint mask, jint val, bool eq = true);
+  Node* flat_array_test(Node* array_or_klass, bool flat = true);
+  Node* null_free_array_test(Node* klass, bool null_free = true);
+  Node* inline_array_null_guard(Node* ary, Node* val, int nargs, bool safe_for_replace = false);
 
   Node* gen_subtype_check(Node* obj, Node* superklass);
 
   // Exact type check used for predicted calls and casts.
   // Rewrites (*casted_receiver) to be casted to the stronger type.
   // (Caller is responsible for doing replace_in_map.)
   Node* type_check_receiver(Node* receiver, ciKlass* klass, float prob,
                             Node* *casted_receiver);
+  Node* type_check(Node* recv_klass, const TypeKlassPtr* tklass, float prob);
 
   // Inexact type check used for predicted calls.
   Node* subtype_check_receiver(Node* receiver, ciKlass* klass,
                                Node** casted_receiver);
 
@@ -863,11 +883,12 @@
                                   bool deoptimize_on_exception=false);
   Node* get_layout_helper(Node* klass_node, jint& constant_value);
   Node* new_instance(Node* klass_node,
                      Node* slow_test = nullptr,
                      Node* *return_size_val = nullptr,
-                     bool deoptimize_on_exception = false);
+                     bool deoptimize_on_exception = false,
+                     InlineTypeNode* inline_type_node = nullptr);
   Node* new_array(Node* klass_node, Node* count_val, int nargs,
                   Node* *return_size_val = nullptr,
                   bool deoptimize_on_exception = false);
 
   // java.lang.String helpers
@@ -900,10 +921,11 @@
 
   void add_parse_predicates(int nargs = 0);
   void add_parse_predicate(Deoptimization::DeoptReason reason, int nargs);
 
   Node* make_constant_from_field(ciField* field, Node* obj);
+  Node* load_mirror_from_klass(Node* klass);
 
   // Vector API support (implemented in vectorIntrinsics.cpp)
   Node* box_vector(Node* in, const TypeInstPtr* vbox_type, BasicType elem_bt, int num_elem, bool deoptimize_on_exception = false);
   Node* unbox_vector(Node* in, const TypeInstPtr* vbox_type, BasicType elem_bt, int num_elem, bool shuffle_to_vector = false);
   Node* vector_shift_count(Node* cnt, int shift_op, BasicType bt, int num_elem);
diff a/src/hotspot/share/opto/idealKit.cpp b/src/hotspot/share/opto/idealKit.cpp
--- a/src/hotspot/share/opto/idealKit.cpp
+++ b/src/hotspot/share/opto/idealKit.cpp
@@ -46,11 +46,10 @@
   _delay_all_transforms = delay_all_transforms;
   _var_ct = 0;
   _cvstate = nullptr;
   // We can go memory state free or else we need the entire memory state
   assert(_initial_memory == nullptr || _initial_memory->Opcode() == Op_MergeMem, "memory must be pre-split");
-  assert(!_gvn.is_IterGVN(), "IdealKit can't be used during Optimize phase");
   int init_size = 5;
   _pending_cvstates = new (C->node_arena()) GrowableArray<Node*>(C->node_arena(), init_size, 0, 0);
   DEBUG_ONLY(_state = new (C->node_arena()) GrowableArray<int>(C->node_arena(), init_size, 0, 0));
   if (!has_declarations) {
      declarations_done();
@@ -78,14 +77,17 @@
       bol = Bool(CmpI(left, right), relop);
     } else {
       assert(left->bottom_type()->isa_long() != nullptr, "what else?");
       bol = Bool(CmpL(left, right), relop);
     }
-
   } else {
     bol = Bool(CmpP(left, right), relop);
   }
+  if_then(bol, prob, cnt, push_new_state);
+}
+
+void IdealKit::if_then(Node* bol, float prob, float cnt, bool push_new_state) {
   // Delay gvn.transform on if-nodes until construction is finished
   // to prevent a constant bool input from discarding a control output.
   IfNode* iff = delay_transform(new IfNode(ctrl(), bol, prob, cnt))->as_If();
   Node* then  = IfTrue(iff);
   Node* elsen = IfFalse(iff);
@@ -294,20 +296,20 @@
 Node* IdealKit::transform(Node* n) {
   if (_delay_all_transforms) {
     return delay_transform(n);
   } else {
     n = gvn().transform(n);
-    C->record_for_igvn(n);
+    gvn().record_for_igvn(n);
     return n;
   }
 }
 
 //-----------------------------delay_transform-----------------------------------
 Node* IdealKit::delay_transform(Node* n) {
   // Delay transform until IterativeGVN
   gvn().set_type(n, n->bottom_type());
-  C->record_for_igvn(n);
+  gvn().record_for_igvn(n);
   return n;
 }
 
 //-----------------------------new_cvstate-----------------------------------
 Node* IdealKit::new_cvstate() {
@@ -523,12 +525,12 @@
   set_memory(mem, adr_idx);
 
   assert(C->alias_type(call->adr_type()) == C->alias_type(adr_type),
          "call node must be constructed correctly");
   Node* res = nullptr;
-  if (slow_call_type->range()->cnt() > TypeFunc::Parms) {
-    assert(slow_call_type->range()->cnt() == TypeFunc::Parms+1, "only one return value");
+  if (slow_call_type->range_sig()->cnt() > TypeFunc::Parms) {
+    assert(slow_call_type->range_sig()->cnt() == TypeFunc::Parms+1, "only one return value");
     res = transform(new ProjNode(call, TypeFunc::Parms));
   }
   return res;
 }
 
diff a/src/hotspot/share/opto/idealKit.hpp b/src/hotspot/share/opto/idealKit.hpp
--- a/src/hotspot/share/opto/idealKit.hpp
+++ b/src/hotspot/share/opto/idealKit.hpp
@@ -161,10 +161,11 @@
   Node* value(IdealVariable& v)         { return _cvstate->in(first_var + v.id()); }
   void dead(IdealVariable& v)           { set(v, (Node*)nullptr); }
   void if_then(Node* left, BoolTest::mask relop, Node* right,
                float prob = PROB_FAIR, float cnt = COUNT_UNKNOWN,
                bool push_new_state = true);
+  void if_then(Node* bol, float prob = PROB_FAIR, float cnt = COUNT_UNKNOWN, bool push_new_state = true);
   void else_();
   void end_if();
   void loop(GraphKit* gkit, int nargs, IdealVariable& iv, Node* init, BoolTest::mask cmp, Node* limit,
             float prob = PROB_LIKELY(0.9), float cnt = COUNT_UNKNOWN);
   void end_loop();
diff a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp
--- a/src/hotspot/share/opto/ifnode.cpp
+++ b/src/hotspot/share/opto/ifnode.cpp
@@ -1240,10 +1240,27 @@
     return true;
   }
   return false;
 }
 
+// Returns true if this IfNode belongs to a flat array check
+// and returns the corresponding array in the 'array' parameter.
+bool IfNode::is_flat_array_check(PhaseTransform* phase, Node** array) {
+  Node* bol = in(1);
+  if (!bol->is_Bool()) {
+    return false;
+  }
+  Node* cmp = bol->in(1);
+  if (cmp->isa_FlatArrayCheck()) {
+    if (array != nullptr) {
+      *array = cmp->in(FlatArrayCheckNode::ArrayOrKlass);
+    }
+    return true;
+  }
+  return false;
+}
+
 // Check that the If that is in between the 2 integer comparisons has
 // no side effect
 bool IfNode::is_side_effect_free_test(ProjNode* proj, PhaseIterGVN* igvn) {
   if (proj == nullptr) {
     return false;
diff a/src/hotspot/share/opto/inlinetypenode.cpp b/src/hotspot/share/opto/inlinetypenode.cpp
--- /dev/null
+++ b/src/hotspot/share/opto/inlinetypenode.cpp
@@ -0,0 +1,1328 @@
+/*
+ * Copyright (c) 2017, 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/ciInlineKlass.hpp"
+#include "gc/shared/barrierSet.hpp"
+#include "gc/shared/gc_globals.hpp"
+#include "opto/addnode.hpp"
+#include "opto/castnode.hpp"
+#include "opto/graphKit.hpp"
+#include "opto/inlinetypenode.hpp"
+#include "opto/rootnode.hpp"
+#include "opto/phaseX.hpp"
+
+// Clones the inline type to handle control flow merges involving multiple inline types.
+// The inputs are replaced by PhiNodes to represent the merged values for the given region.
+InlineTypeNode* InlineTypeNode::clone_with_phis(PhaseGVN* gvn, Node* region, SafePointNode* map, bool is_init) {
+  InlineTypeNode* vt = clone_if_required(gvn, map);
+  const Type* t = Type::get_const_type(inline_klass());
+  gvn->set_type(vt, t);
+  vt->as_InlineType()->set_type(t);
+
+  // Create a PhiNode for merging the oop values
+  PhiNode* oop = PhiNode::make(region, vt->get_oop(), t);
+  gvn->set_type(oop, t);
+  gvn->record_for_igvn(oop);
+  vt->set_oop(*gvn, oop);
+
+  // Create a PhiNode for merging the is_buffered values
+  t = Type::get_const_basic_type(T_BOOLEAN);
+  Node* is_buffered_node = PhiNode::make(region, vt->get_is_buffered(), t);
+  gvn->set_type(is_buffered_node, t);
+  gvn->record_for_igvn(is_buffered_node);
+  vt->set_req(IsBuffered, is_buffered_node);
+
+  // Create a PhiNode for merging the is_init values
+  Node* is_init_node;
+  if (is_init) {
+    is_init_node = gvn->intcon(1);
+  } else {
+    t = Type::get_const_basic_type(T_BOOLEAN);
+    is_init_node = PhiNode::make(region, vt->get_is_init(), t);
+    gvn->set_type(is_init_node, t);
+    gvn->record_for_igvn(is_init_node);
+  }
+  vt->set_req(IsInit, is_init_node);
+
+  // Create a PhiNode each for merging the field values
+  for (uint i = 0; i < vt->field_count(); ++i) {
+    ciType* type = vt->field_type(i);
+    Node*  value = vt->field_value(i);
+    // We limit scalarization for inline types with circular fields and can therefore observe nodes
+    // of the same type but with different scalarization depth during GVN. To avoid inconsistencies
+    // during merging, make sure that we only create Phis for fields that are guaranteed to be scalarized.
+    bool no_circularity = !gvn->C->has_circular_inline_type() || field_is_flat(i);
+    if (value->is_InlineType() && no_circularity) {
+      // Handle inline type fields recursively
+      value = value->as_InlineType()->clone_with_phis(gvn, region, map);
+    } else {
+      t = Type::get_const_type(type);
+      value = PhiNode::make(region, value, t);
+      gvn->set_type(value, t);
+      gvn->record_for_igvn(value);
+    }
+    vt->set_field_value(i, value);
+  }
+  gvn->record_for_igvn(vt);
+  return vt;
+}
+
+// Checks if the inputs of the InlineTypeNode were replaced by PhiNodes
+// for the given region (see InlineTypeNode::clone_with_phis).
+bool InlineTypeNode::has_phi_inputs(Node* region) {
+  // Check oop input
+  bool result = get_oop()->is_Phi() && get_oop()->as_Phi()->region() == region;
+#ifdef ASSERT
+  if (result) {
+    // Check all field value inputs for consistency
+    for (uint i = Values; i < field_count(); ++i) {
+      Node* n = in(i);
+      if (n->is_InlineType()) {
+        assert(n->as_InlineType()->has_phi_inputs(region), "inconsistent phi inputs");
+      } else {
+        assert(n->is_Phi() && n->as_Phi()->region() == region, "inconsistent phi inputs");
+      }
+    }
+  }
+#endif
+  return result;
+}
+
+// Merges 'this' with 'other' by updating the input PhiNodes added by 'clone_with_phis'
+InlineTypeNode* InlineTypeNode::merge_with(PhaseGVN* gvn, const InlineTypeNode* other, int pnum, bool transform) {
+  // Merge oop inputs
+  PhiNode* phi = get_oop()->as_Phi();
+  phi->set_req(pnum, other->get_oop());
+  if (transform) {
+    set_oop(*gvn, gvn->transform(phi));
+  }
+
+  // Merge is_buffered inputs
+  phi = get_is_buffered()->as_Phi();
+  phi->set_req(pnum, other->get_is_buffered());
+  if (transform) {
+    set_req(IsBuffered, gvn->transform(phi));
+  }
+
+  // Merge is_init inputs
+  Node* is_init = get_is_init();
+  if (is_init->is_Phi()) {
+    phi = is_init->as_Phi();
+    phi->set_req(pnum, other->get_is_init());
+    if (transform) {
+      set_req(IsInit, gvn->transform(phi));
+    }
+  } else {
+    assert(is_init->find_int_con(0) == 1, "only with a non null inline type");
+  }
+
+  // Merge field values
+  for (uint i = 0; i < field_count(); ++i) {
+    Node* val1 =        field_value(i);
+    Node* val2 = other->field_value(i);
+    if (val1->is_InlineType()) {
+      if (val2->is_Phi()) {
+        val2 = gvn->transform(val2);
+      }
+      val1->as_InlineType()->merge_with(gvn, val2->as_InlineType(), pnum, transform);
+    } else {
+      assert(val1->is_Phi(), "must be a phi node");
+      val1->set_req(pnum, val2);
+    }
+    if (transform) {
+      set_field_value(i, gvn->transform(val1));
+    }
+  }
+  return this;
+}
+
+// Adds a new merge path to an inline type node with phi inputs
+void InlineTypeNode::add_new_path(Node* region) {
+  assert(has_phi_inputs(region), "must have phi inputs");
+
+  PhiNode* phi = get_oop()->as_Phi();
+  phi->add_req(nullptr);
+  assert(phi->req() == region->req(), "must be same size as region");
+
+  phi = get_is_buffered()->as_Phi();
+  phi->add_req(nullptr);
+  assert(phi->req() == region->req(), "must be same size as region");
+
+  phi = get_is_init()->as_Phi();
+  phi->add_req(nullptr);
+  assert(phi->req() == region->req(), "must be same size as region");
+
+  for (uint i = 0; i < field_count(); ++i) {
+    Node* val = field_value(i);
+    if (val->is_InlineType()) {
+      val->as_InlineType()->add_new_path(region);
+    } else {
+      val->as_Phi()->add_req(nullptr);
+      assert(val->req() == region->req(), "must be same size as region");
+    }
+  }
+}
+
+Node* InlineTypeNode::field_value(uint index) const {
+  assert(index < field_count(), "index out of bounds");
+  return in(Values + index);
+}
+
+// Get the value of the field at the given offset.
+// If 'recursive' is true, flat inline type fields will be resolved recursively.
+Node* InlineTypeNode::field_value_by_offset(int offset, bool recursive) const {
+  // If the field at 'offset' belongs to a flat inline type field, 'index' refers to the
+  // corresponding InlineTypeNode input and 'sub_offset' is the offset in flattened inline type.
+  int index = inline_klass()->field_index_by_offset(offset);
+  int sub_offset = offset - field_offset(index);
+  Node* value = field_value(index);
+  assert(value != nullptr, "field value not found");
+  if (recursive && value->is_InlineType()) {
+    if (field_is_flat(index)) {
+      // Flat inline type field
+      InlineTypeNode* vt = value->as_InlineType();
+      sub_offset += vt->inline_klass()->first_field_offset(); // Add header size
+      return vt->field_value_by_offset(sub_offset, recursive);
+    } else {
+      assert(sub_offset == 0, "should not have a sub offset");
+      return value;
+    }
+  }
+  assert(!(recursive && value->is_InlineType()), "should not be an inline type");
+  assert(sub_offset == 0, "offset mismatch");
+  return value;
+}
+
+void InlineTypeNode::set_field_value(uint index, Node* value) {
+  assert(index < field_count(), "index out of bounds");
+  set_req(Values + index, value);
+}
+
+void InlineTypeNode::set_field_value_by_offset(int offset, Node* value) {
+  set_field_value(field_index(offset), value);
+}
+
+int InlineTypeNode::field_offset(uint index) const {
+  assert(index < field_count(), "index out of bounds");
+  return inline_klass()->declared_nonstatic_field_at(index)->offset_in_bytes();
+}
+
+uint InlineTypeNode::field_index(int offset) const {
+  uint i = 0;
+  for (; i < field_count() && field_offset(i) != offset; i++) { }
+  assert(i < field_count(), "field not found");
+  return i;
+}
+
+ciType* InlineTypeNode::field_type(uint index) const {
+  assert(index < field_count(), "index out of bounds");
+  return inline_klass()->declared_nonstatic_field_at(index)->type();
+}
+
+bool InlineTypeNode::field_is_flat(uint index) const {
+  assert(index < field_count(), "index out of bounds");
+  ciField* field = inline_klass()->declared_nonstatic_field_at(index);
+  assert(!field->is_flat() || field->type()->is_inlinetype(), "must be an inline type");
+  return field->is_flat();
+}
+
+bool InlineTypeNode::field_is_null_free(uint index) const {
+  assert(index < field_count(), "index out of bounds");
+  ciField* field = inline_klass()->declared_nonstatic_field_at(index);
+  assert(!field->is_flat() || field->type()->is_inlinetype(), "must be an inline type");
+  return field->is_null_free();
+}
+
+void InlineTypeNode::make_scalar_in_safepoint(PhaseIterGVN* igvn, Unique_Node_List& worklist, SafePointNode* sfpt) {
+  // Don't scalarize larvals in their own constructor call because the constructor will update them
+  if (is_larval() && sfpt->is_CallJava() && sfpt->as_CallJava()->method() != nullptr && sfpt->as_CallJava()->method()->is_object_constructor() &&
+      sfpt->as_CallJava()->method()->holder()->is_inlinetype() && sfpt->in(TypeFunc::Parms) == this) {
+    assert(is_allocated(igvn), "receiver must be allocated");
+    return;
+  }
+
+  ciInlineKlass* vk = inline_klass();
+  uint nfields = vk->nof_nonstatic_fields();
+  JVMState* jvms = sfpt->jvms();
+  // Replace safepoint edge by SafePointScalarObjectNode and add field values
+  assert(jvms != nullptr, "missing JVMS");
+  uint first_ind = (sfpt->req() - jvms->scloff());
+  SafePointScalarObjectNode* sobj = new SafePointScalarObjectNode(type()->isa_instptr(),
+                                                                  nullptr,
+                                                                  first_ind, nfields);
+  sobj->init_req(0, igvn->C->root());
+  // Nullable inline types have an IsInit field that needs
+  // to be checked before using the field values.
+  if (!igvn->type(get_is_init())->is_int()->is_con(1)) {
+    sfpt->add_req(get_is_init());
+  } else {
+    sfpt->add_req(igvn->C->top());
+  }
+  // Iterate over the inline type fields in order of increasing
+  // offset and add the field values to the safepoint.
+  for (uint j = 0; j < nfields; ++j) {
+    int offset = vk->nonstatic_field_at(j)->offset_in_bytes();
+    Node* value = field_value_by_offset(offset, true /* include flat inline type fields */);
+    if (value->is_InlineType()) {
+      // Add inline type field to the worklist to process later
+      worklist.push(value);
+    }
+    sfpt->add_req(value);
+  }
+  jvms->set_endoff(sfpt->req());
+  sobj = igvn->transform(sobj)->as_SafePointScalarObject();
+  igvn->rehash_node_delayed(sfpt);
+  for (uint i = jvms->debug_start(); i < jvms->debug_end(); i++) {
+    Node* debug = sfpt->in(i);
+    if (debug != nullptr && debug->uncast() == this) {
+      sfpt->set_req(i, sobj);
+    }
+  }
+}
+
+void InlineTypeNode::make_scalar_in_safepoints(PhaseIterGVN* igvn, bool allow_oop) {
+  // If the inline type has a constant or loaded oop, use the oop instead of scalarization
+  // in the safepoint to avoid keeping field loads live just for the debug info.
+  Node* oop = get_oop();
+  // TODO 8325106
+  // TestBasicFunctionality::test3 fails without this. Add more tests?
+  // Add proj nodes here? Recursive handling of phis required? We need a test that fails without.
+  bool use_oop = false;
+  if (allow_oop && is_allocated(igvn) && oop->is_Phi()) {
+    Unique_Node_List worklist;
+    worklist.push(oop);
+    use_oop = true;
+    while (worklist.size() > 0 && use_oop) {
+      Node* n = worklist.pop();
+      for (uint i = 1; i < n->req(); i++) {
+        Node* in = n->in(i);
+        if (in->is_Phi()) {
+          worklist.push(in);
+        // TestNullableArrays.test123 fails when enabling this, probably we should make sure that we don't load from a just allocated object
+        //} else if (!(in->is_Con() || in->is_Parm() || in->is_Load() || (in->isa_DecodeN() && in->in(1)->is_Load()))) {
+        } else if (!(in->is_Con() || in->is_Parm())) {
+          use_oop = false;
+          break;
+        }
+      }
+    }
+  } else {
+    use_oop = allow_oop && is_allocated(igvn) &&
+              (oop->is_Con() || oop->is_Parm() || oop->is_Load() || (oop->isa_DecodeN() && oop->in(1)->is_Load()));
+  }
+
+  ResourceMark rm;
+  Unique_Node_List safepoints;
+  Unique_Node_List vt_worklist;
+  Unique_Node_List worklist;
+  worklist.push(this);
+  while (worklist.size() > 0) {
+    Node* n = worklist.pop();
+    for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
+      Node* use = n->fast_out(i);
+      if (use->is_SafePoint() && !use->is_CallLeaf() && (!use->is_Call() || use->as_Call()->has_debug_use(n))) {
+        safepoints.push(use);
+      } else if (use->is_ConstraintCast()) {
+        worklist.push(use);
+      }
+    }
+  }
+
+  // Process all safepoint uses and scalarize inline type
+  while (safepoints.size() > 0) {
+    SafePointNode* sfpt = safepoints.pop()->as_SafePoint();
+    if (use_oop) {
+      for (uint i = sfpt->jvms()->debug_start(); i < sfpt->jvms()->debug_end(); i++) {
+        Node* debug = sfpt->in(i);
+        if (debug != nullptr && debug->uncast() == this) {
+          sfpt->set_req(i, get_oop());
+        }
+      }
+      igvn->rehash_node_delayed(sfpt);
+    } else {
+      make_scalar_in_safepoint(igvn, vt_worklist, sfpt);
+    }
+  }
+  // Now scalarize non-flat fields
+  for (uint i = 0; i < vt_worklist.size(); ++i) {
+    InlineTypeNode* vt = vt_worklist.at(i)->isa_InlineType();
+    vt->make_scalar_in_safepoints(igvn);
+  }
+  if (outcnt() == 0) {
+    igvn->_worklist.push(this);
+  }
+}
+
+const TypePtr* InlineTypeNode::field_adr_type(Node* base, int offset, ciInstanceKlass* holder, DecoratorSet decorators, PhaseGVN& gvn) const {
+  const TypeAryPtr* ary_type = gvn.type(base)->isa_aryptr();
+  const TypePtr* adr_type = nullptr;
+  bool is_array = ary_type != nullptr;
+  if ((decorators & C2_MISMATCHED) != 0) {
+    adr_type = TypeRawPtr::BOTTOM;
+  } else if (is_array) {
+    // In the case of a flat inline type array, each field has its own slice
+    adr_type = ary_type->with_field_offset(offset)->add_offset(Type::OffsetBot);
+  } else {
+    ciField* field = holder->get_field_by_offset(offset, false);
+    assert(field != nullptr, "field not found");
+    adr_type = gvn.C->alias_type(field)->adr_type();
+  }
+  return adr_type;
+}
+
+// We limit scalarization for inline types with circular fields and can therefore observe nodes
+// of the same type but with different scalarization depth during GVN. This method adjusts the
+// scalarization depth to avoid inconsistencies during merging.
+InlineTypeNode* InlineTypeNode::adjust_scalarization_depth(GraphKit* kit) {
+  if (!kit->C->has_circular_inline_type()) {
+    return this;
+  }
+  GrowableArray<ciType*> visited;
+  visited.push(inline_klass());
+  return adjust_scalarization_depth_impl(kit, visited);
+}
+
+InlineTypeNode* InlineTypeNode::adjust_scalarization_depth_impl(GraphKit* kit, GrowableArray<ciType*>& visited) {
+  InlineTypeNode* val = this;
+  for (uint i = 0; i < field_count(); ++i) {
+    Node* value = field_value(i);
+    Node* new_value = value;
+    ciType* ft = field_type(i);
+    if (value->is_InlineType()) {
+      if (!field_is_flat(i) && visited.contains(ft)) {
+        new_value = value->as_InlineType()->buffer(kit)->get_oop();
+      } else {
+        int old_len = visited.length();
+        visited.push(ft);
+        new_value = value->as_InlineType()->adjust_scalarization_depth_impl(kit, visited);
+        visited.trunc_to(old_len);
+      }
+    } else if (ft->is_inlinetype() && !visited.contains(ft)) {
+      int old_len = visited.length();
+      visited.push(ft);
+      new_value = make_from_oop_impl(kit, value, ft->as_inline_klass(), field_is_null_free(i), visited);
+      visited.trunc_to(old_len);
+    }
+    if (value != new_value) {
+      if (val == this) {
+        val = clone_if_required(&kit->gvn(), kit->map());
+      }
+      val->set_field_value(i, new_value);
+    }
+  }
+  return (val == this) ? this : kit->gvn().transform(val)->as_InlineType();
+}
+
+void InlineTypeNode::load(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder, GrowableArray<ciType*>& visited, int holder_offset, DecoratorSet decorators) {
+  // Initialize the inline type by loading its field values from
+  // memory and adding the values as input edges to the node.
+  for (uint i = 0; i < field_count(); ++i) {
+    int offset = holder_offset + field_offset(i);
+    Node* value = nullptr;
+    ciType* ft = field_type(i);
+    bool null_free = field_is_null_free(i);
+    if (null_free && ft->as_inline_klass()->is_empty()) {
+      // Loading from a field of an empty inline type. Just return the default instance.
+      value = make_default_impl(kit->gvn(), ft->as_inline_klass(), visited);
+    } else if (field_is_flat(i)) {
+      // Recursively load the flat inline type field
+      value = make_from_flat_impl(kit, ft->as_inline_klass(), base, ptr, holder, offset, decorators, visited);
+    } else {
+      const TypeOopPtr* oop_ptr = kit->gvn().type(base)->isa_oopptr();
+      bool is_array = (oop_ptr->isa_aryptr() != nullptr);
+      bool mismatched = (decorators & C2_MISMATCHED) != 0;
+      if (base->is_Con() && !is_array && !mismatched) {
+        // If the oop to the inline type is constant (static final field), we can
+        // also treat the fields as constants because the inline type is immutable.
+        ciObject* constant_oop = oop_ptr->const_oop();
+        ciField* field = holder->get_field_by_offset(offset, false);
+        assert(field != nullptr, "field not found");
+        ciConstant constant = constant_oop->as_instance()->field_value(field);
+        const Type* con_type = Type::make_from_constant(constant, /*require_const=*/ true);
+        assert(con_type != nullptr, "type not found");
+        value = kit->gvn().transform(kit->makecon(con_type));
+        // Check type of constant which might be more precise than the static field type
+        if (con_type->is_inlinetypeptr() && !con_type->is_zero_type()) {
+          ft = con_type->inline_klass();
+          null_free = true;
+        }
+      } else {
+        // Load field value from memory
+        const TypePtr* adr_type = field_adr_type(base, offset, holder, decorators, kit->gvn());
+        Node* adr = kit->basic_plus_adr(base, ptr, offset);
+        BasicType bt = type2field[ft->basic_type()];
+        assert(is_java_primitive(bt) || adr->bottom_type()->is_ptr_to_narrowoop() == UseCompressedOops, "inconsistent");
+        const Type* val_type = Type::get_const_type(ft);
+        value = kit->access_load_at(base, adr, adr_type, val_type, bt, is_array ? (decorators | IS_ARRAY) : decorators);
+      }
+      // Loading a non-flattened inline type from memory
+      if (visited.contains(ft)) {
+        kit->C->set_has_circular_inline_type(true);
+      } else if (ft->is_inlinetype()) {
+        int old_len = visited.length();
+        visited.push(ft);
+        value = make_from_oop_impl(kit, value, ft->as_inline_klass(), null_free, visited);
+        visited.trunc_to(old_len);
+      }
+    }
+    set_field_value(i, value);
+  }
+}
+
+void InlineTypeNode::store_flat(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder, int holder_offset, DecoratorSet decorators) const {
+  if (kit->gvn().type(base)->isa_aryptr()) {
+    kit->C->set_flat_accesses();
+  }
+  // The inline type is embedded into the object without an oop header. Subtract the
+  // offset of the first field to account for the missing header when storing the values.
+  if (holder == nullptr) {
+    holder = inline_klass();
+  }
+  holder_offset -= inline_klass()->first_field_offset();
+  store(kit, base, ptr, holder, holder_offset, decorators);
+}
+
+void InlineTypeNode::store(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder, int holder_offset, DecoratorSet decorators, int offsetOnly) const {
+  // Write field values to memory
+  for (uint i = 0; i < field_count(); ++i) {
+    if (offsetOnly != -1 && offsetOnly != field_offset(i)) continue;
+    int offset = holder_offset + field_offset(i);
+    Node* value = field_value(i);
+    ciType* ft = field_type(i);
+    if (field_is_flat(i)) {
+      // Recursively store the flat inline type field
+      value->as_InlineType()->store_flat(kit, base, ptr, holder, offset, decorators);
+    } else {
+      // Store field value to memory
+      const TypePtr* adr_type = field_adr_type(base, offset, holder, decorators, kit->gvn());
+      Node* adr = kit->basic_plus_adr(base, ptr, offset);
+      BasicType bt = type2field[ft->basic_type()];
+      assert(is_java_primitive(bt) || adr->bottom_type()->is_ptr_to_narrowoop() == UseCompressedOops, "inconsistent");
+      const Type* val_type = Type::get_const_type(ft);
+      bool is_array = (kit->gvn().type(base)->isa_aryptr() != nullptr);
+      kit->access_store_at(base, adr, adr_type, value, val_type, bt, is_array ? (decorators | IS_ARRAY) : decorators);
+    }
+  }
+}
+
+InlineTypeNode* InlineTypeNode::buffer(GraphKit* kit, bool safe_for_replace) {
+  if (kit->gvn().find_int_con(get_is_buffered(), 0) == 1) {
+    // Already buffered
+    return this;
+  }
+
+  // Check if inline type is already buffered
+  Node* not_buffered_ctl = kit->top();
+  Node* not_null_oop = kit->null_check_oop(get_oop(), &not_buffered_ctl, /* never_see_null = */ false, safe_for_replace);
+  if (not_buffered_ctl->is_top()) {
+    // Already buffered
+    InlineTypeNode* vt = clone_if_required(&kit->gvn(), kit->map(), safe_for_replace);
+    vt->set_is_buffered(kit->gvn());
+    vt = kit->gvn().transform(vt)->as_InlineType();
+    if (safe_for_replace) {
+      kit->replace_in_map(this, vt);
+    }
+    return vt;
+  }
+  Node* buffered_ctl = kit->control();
+  kit->set_control(not_buffered_ctl);
+
+  // Inline type is not buffered, check if it is null.
+  Node* null_ctl = kit->top();
+  kit->null_check_common(get_is_init(), T_INT, false, &null_ctl);
+  bool null_free = null_ctl->is_top();
+
+  RegionNode* region = new RegionNode(4);
+  PhiNode* oop = PhiNode::make(region, not_null_oop, type()->join_speculative(null_free ? TypePtr::NOTNULL : TypePtr::BOTTOM));
+
+  // InlineType is already buffered
+  region->init_req(1, buffered_ctl);
+  oop->init_req(1, not_null_oop);
+
+  // InlineType is null
+  region->init_req(2, null_ctl);
+  oop->init_req(2, kit->gvn().zerocon(T_OBJECT));
+
+  PhiNode* io  = PhiNode::make(region, kit->i_o(), Type::ABIO);
+  PhiNode* mem = PhiNode::make(region, kit->merged_memory(), Type::MEMORY, TypePtr::BOTTOM);
+
+  int bci = kit->bci();
+  bool reexecute = kit->jvms()->should_reexecute();
+  if (!kit->stopped()) {
+    assert(!is_allocated(&kit->gvn()), "already buffered");
+
+    // Allocate and initialize buffer
+    PreserveJVMState pjvms(kit);
+    // Propagate re-execution state and bci
+    kit->set_bci(bci);
+    kit->jvms()->set_bci(bci);
+    kit->jvms()->set_should_reexecute(reexecute);
+
+    kit->kill_dead_locals();
+    ciInlineKlass* vk = inline_klass();
+    Node* klass_node = kit->makecon(TypeKlassPtr::make(vk));
+    Node* alloc_oop  = kit->new_instance(klass_node, nullptr, nullptr, /* deoptimize_on_exception */ true, this);
+    // No need to initialize a larval buffer, we make sure that the oop can not escape
+    if (!is_larval()) {
+      // Larval will be initialized later
+      // TODO 8325106 should this use C2_TIGHTLY_COUPLED_ALLOC?
+      store(kit, alloc_oop, alloc_oop, vk);
+
+      // Do not let stores that initialize this buffer be reordered with a subsequent
+      // store that would make this buffer accessible by other threads.
+      AllocateNode* alloc = AllocateNode::Ideal_allocation(alloc_oop);
+      assert(alloc != nullptr, "must have an allocation node");
+      kit->insert_mem_bar(Op_MemBarStoreStore, alloc->proj_out_or_null(AllocateNode::RawAddress));
+    }
+
+    region->init_req(3, kit->control());
+    oop   ->init_req(3, alloc_oop);
+    io    ->init_req(3, kit->i_o());
+    mem   ->init_req(3, kit->merged_memory());
+  }
+
+  // Update GraphKit
+  kit->set_control(kit->gvn().transform(region));
+  kit->set_i_o(kit->gvn().transform(io));
+  kit->set_all_memory(kit->gvn().transform(mem));
+  kit->record_for_igvn(region);
+  kit->record_for_igvn(oop);
+  kit->record_for_igvn(io);
+  kit->record_for_igvn(mem);
+
+  // Use cloned InlineTypeNode to propagate oop from now on
+  Node* res_oop = kit->gvn().transform(oop);
+  InlineTypeNode* vt = clone_if_required(&kit->gvn(), kit->map(), safe_for_replace);
+  vt->set_oop(kit->gvn(), res_oop);
+  vt->set_is_buffered(kit->gvn());
+  vt = kit->gvn().transform(vt)->as_InlineType();
+  if (safe_for_replace) {
+    kit->replace_in_map(this, vt);
+  }
+  // InlineTypeNode::remove_redundant_allocations piggybacks on split if.
+  // Make sure it gets a chance to remove this allocation.
+  kit->C->set_has_split_ifs(true);
+  return vt;
+}
+
+bool InlineTypeNode::is_allocated(PhaseGVN* phase) const {
+  if (phase->find_int_con(get_is_buffered(), 0) == 1) {
+    return true;
+  }
+  Node* oop = get_oop();
+  const Type* oop_type = (phase != nullptr) ? phase->type(oop) : oop->bottom_type();
+  return !oop_type->maybe_null();
+}
+
+// When a call returns multiple values, it has several result
+// projections, one per field. Replacing the result of the call by an
+// inline type node (after late inlining) requires that for each result
+// projection, we find the corresponding inline type field.
+void InlineTypeNode::replace_call_results(GraphKit* kit, CallNode* call, Compile* C) {
+  ciInlineKlass* vk = inline_klass();
+  for (DUIterator_Fast imax, i = call->fast_outs(imax); i < imax; i++) {
+    ProjNode* pn = call->fast_out(i)->as_Proj();
+    uint con = pn->_con;
+    Node* field = nullptr;
+    if (con == TypeFunc::Parms) {
+      field = get_oop();
+    } else if (con == (call->tf()->range_cc()->cnt() - 1)) {
+      field = get_is_init();
+    } else if (con > TypeFunc::Parms) {
+      uint field_nb = con - (TypeFunc::Parms+1);
+      int extra = 0;
+      for (uint j = 0; j < field_nb - extra; j++) {
+        ciField* f = vk->nonstatic_field_at(j);
+        BasicType bt = f->type()->basic_type();
+        if (bt == T_LONG || bt == T_DOUBLE) {
+          extra++;
+        }
+      }
+      ciField* f = vk->nonstatic_field_at(field_nb - extra);
+      field = field_value_by_offset(f->offset_in_bytes(), true);
+    }
+    if (field != nullptr) {
+      C->gvn_replace_by(pn, field);
+      C->initial_gvn()->hash_delete(pn);
+      pn->set_req(0, C->top());
+      --i; --imax;
+    }
+  }
+}
+
+Node* InlineTypeNode::allocate_fields(GraphKit* kit) {
+  InlineTypeNode* vt = clone_if_required(&kit->gvn(), kit->map());
+  for (uint i = 0; i < field_count(); i++) {
+     Node* value = field_value(i);
+     if (field_is_flat(i)) {
+       // Flat inline type field
+       vt->set_field_value(i, value->as_InlineType()->allocate_fields(kit));
+     } else if (value->is_InlineType()) {
+       // Non-flat inline type field
+       vt->set_field_value(i, value->as_InlineType()->buffer(kit));
+     }
+  }
+  vt = kit->gvn().transform(vt)->as_InlineType();
+  kit->replace_in_map(this, vt);
+  return vt;
+}
+
+// Replace a buffer allocation by a dominating allocation
+static void replace_allocation(PhaseIterGVN* igvn, Node* res, Node* dom) {
+  // Remove initializing stores and GC barriers
+  for (DUIterator_Fast imax, i = res->fast_outs(imax); i < imax; i++) {
+    Node* use = res->fast_out(i);
+    if (use->is_AddP()) {
+      for (DUIterator_Fast jmax, j = use->fast_outs(jmax); j < jmax; j++) {
+        Node* store = use->fast_out(j)->isa_Store();
+        if (store != nullptr) {
+          igvn->rehash_node_delayed(store);
+          igvn->replace_in_uses(store, store->in(MemNode::Memory));
+        }
+      }
+    } else if (use->Opcode() == Op_CastP2X) {
+      if (UseG1GC && use->find_out_with(Op_XorX)->in(1) != use) {
+        // The G1 pre-barrier uses a CastP2X both for the pointer of the object
+        // we store into, as well as the value we are storing. Skip if this is a
+        // barrier for storing 'res' into another object.
+        continue;
+      }
+      BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
+      bs->eliminate_gc_barrier(igvn, use);
+      --i; --imax;
+    }
+  }
+  igvn->replace_node(res, dom);
+}
+
+Node* InlineTypeNode::Ideal(PhaseGVN* phase, bool can_reshape) {
+  Node* oop = get_oop();
+  const Type* tinit = phase->type(get_is_init());
+  if (!is_larval(phase) && !is_larval() &&
+      (tinit->isa_int() && tinit->is_int()->is_con(1)) &&
+      (is_default(phase) || inline_klass()->is_empty()) &&
+      inline_klass()->is_initialized() &&
+      (!oop->is_Con() || phase->type(oop)->is_zero_type())) {
+    // Use the pre-allocated oop for null-free default or empty inline types
+    set_oop(*phase, default_oop(*phase, inline_klass()));
+    assert(is_allocated(phase), "should now be allocated");
+    return this;
+  }
+  if (oop->isa_InlineType() && !phase->type(oop)->maybe_null()) {
+    InlineTypeNode* vtptr = oop->as_InlineType();
+    set_oop(*phase, vtptr->get_oop());
+    set_is_buffered(*phase);
+    set_is_init(*phase);
+    for (uint i = Values; i < vtptr->req(); ++i) {
+      set_req(i, vtptr->in(i));
+    }
+    return this;
+  }
+  // TODO 8325106 Re-evaluate this: We prefer a "loaded" oop because it's free. The existing oop might come from a buffering.
+  if (!is_larval(phase) && !is_larval()) {
+    // Save base oop if fields are loaded from memory and the inline
+    // type is not buffered (in this case we should not use the oop).
+    Node* base = is_loaded(phase);
+    if (base != nullptr && get_oop() != base && !phase->type(base)->maybe_null()) {
+      set_oop(*phase, base);
+      assert(is_allocated(phase), "should now be allocated");
+      return this;
+    }
+  }
+
+  if (can_reshape) {
+    PhaseIterGVN* igvn = phase->is_IterGVN();
+    if (is_allocated(phase)) {
+      // Search for and remove re-allocations of this inline type. Ignore scalar replaceable ones,
+      // they will be removed anyway and changing the memory chain will confuse other optimizations.
+      // This can happen with late inlining when we first allocate an inline type argument
+      // but later decide to inline the call after the callee code also triggered allocation.
+      for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) {
+        AllocateNode* alloc = fast_out(i)->isa_Allocate();
+        if (alloc != nullptr && alloc->in(AllocateNode::InlineType) == this && !alloc->_is_scalar_replaceable) {
+          // Found a re-allocation
+          Node* res = alloc->result_cast();
+          if (res != nullptr && res->is_CheckCastPP()) {
+            // Replace allocation by oop and unlink AllocateNode
+            replace_allocation(igvn, res, oop);
+            igvn->replace_input_of(alloc, AllocateNode::InlineType, igvn->C->top());
+            --i; --imax;
+          }
+        }
+      }
+    }
+  }
+
+  return nullptr;
+}
+
+InlineTypeNode* InlineTypeNode::make_uninitialized(PhaseGVN& gvn, ciInlineKlass* vk, bool null_free) {
+  // Create a new InlineTypeNode with uninitialized values and nullptr oop
+  bool use_default_oop = vk->is_empty() && vk->is_initialized() && null_free;
+  Node* oop = use_default_oop ? default_oop(gvn, vk) : gvn.zerocon(T_OBJECT);
+  InlineTypeNode* vt = new InlineTypeNode(vk, oop, null_free);
+  vt->set_is_buffered(gvn, use_default_oop);
+  vt->set_is_init(gvn);
+  return vt;
+}
+
+Node* InlineTypeNode::default_oop(PhaseGVN& gvn, ciInlineKlass* vk) {
+  // Returns the constant oop of the default inline type allocation
+  return gvn.makecon(TypeInstPtr::make(vk->default_instance()));
+}
+
+InlineTypeNode* InlineTypeNode::make_default(PhaseGVN& gvn, ciInlineKlass* vk, bool is_larval) {
+  GrowableArray<ciType*> visited;
+  visited.push(vk);
+  return make_default_impl(gvn, vk, visited, is_larval);
+}
+
+InlineTypeNode* InlineTypeNode::make_default_impl(PhaseGVN& gvn, ciInlineKlass* vk, GrowableArray<ciType*>& visited, bool is_larval) {
+  // Create a new InlineTypeNode with default values
+  Node* oop = vk->is_initialized() && !is_larval ? default_oop(gvn, vk) : gvn.zerocon(T_OBJECT);
+  InlineTypeNode* vt = new InlineTypeNode(vk, oop, /* null_free= */ true);
+  // TODO 8325106 we should be able to set buffered here for non-larvals, right?
+  //vt->set_is_buffered(gvn, vk->is_initialized());
+  vt->set_is_buffered(gvn, false);
+  vt->set_is_init(gvn);
+  vt->set_is_larval(is_larval);
+  for (uint i = 0; i < vt->field_count(); ++i) {
+    ciType* ft = vt->field_type(i);
+    Node* value = gvn.zerocon(ft->basic_type());
+    if (!vt->field_is_flat(i) && visited.contains(ft)) {
+      gvn.C->set_has_circular_inline_type(true);
+    } else if (ft->is_inlinetype()) {
+      int old_len = visited.length();
+      visited.push(ft);
+      ciInlineKlass* vk = ft->as_inline_klass();
+      if (vt->field_is_null_free(i)) {
+        value = make_default_impl(gvn, vk, visited);
+      } else {
+        value = make_null_impl(gvn, vk, visited);
+      }
+      visited.trunc_to(old_len);
+    }
+    vt->set_field_value(i, value);
+  }
+  vt = gvn.transform(vt)->as_InlineType();
+  assert(vt->is_default(&gvn), "must be the default inline type");
+  return vt;
+}
+
+bool InlineTypeNode::is_default(PhaseGVN* gvn) const {
+  const Type* tinit = gvn->type(get_is_init());
+  if (!tinit->isa_int() || !tinit->is_int()->is_con(1)) {
+    return false; // May be null
+  }
+  for (uint i = 0; i < field_count(); ++i) {
+    ciType* ft = field_type(i);
+    Node* value = field_value(i);
+    if (field_is_null_free(i)) {
+      if (!value->is_InlineType() || !value->as_InlineType()->is_default(gvn)) {
+        return false;
+      }
+      continue;
+    } else if (value->is_InlineType()) {
+      if (value->as_InlineType()->is_default(gvn)) {
+        continue;
+      } else {
+        const Type* tinit = gvn->type(value->as_InlineType()->get_is_init());
+        if (tinit->isa_int() && tinit->is_int()->is_con(0)) {
+          continue;
+        }
+        return false;
+      }
+    }
+    if (!gvn->type(value)->is_zero_type()) {
+      return false;
+    }
+  }
+  return true;
+}
+
+InlineTypeNode* InlineTypeNode::make_from_oop(GraphKit* kit, Node* oop, ciInlineKlass* vk, bool null_free, bool is_larval) {
+  GrowableArray<ciType*> visited;
+  visited.push(vk);
+  return make_from_oop_impl(kit, oop, vk, null_free, visited, is_larval);
+}
+
+InlineTypeNode* InlineTypeNode::make_from_oop_impl(GraphKit* kit, Node* oop, ciInlineKlass* vk, bool null_free, GrowableArray<ciType*>& visited, bool is_larval) {
+  PhaseGVN& gvn = kit->gvn();
+
+  if (!is_larval && vk->is_empty() && null_free) {
+    InlineTypeNode* def = make_default_impl(gvn, vk, visited);
+    kit->record_for_igvn(def);
+    return def;
+  }
+  // Create and initialize an InlineTypeNode by loading all field
+  // values from a heap-allocated version and also save the oop.
+  InlineTypeNode* vt = nullptr;
+
+  if (oop->isa_InlineType()) {
+    assert(!is_larval || oop->as_InlineType()->is_larval(), "must be larval");
+    return oop->as_InlineType();
+  } else if (gvn.type(oop)->maybe_null()) {
+    // Add a null check because the oop may be null
+    Node* null_ctl = kit->top();
+    Node* not_null_oop = kit->null_check_oop(oop, &null_ctl);
+    if (kit->stopped()) {
+      // Constant null
+      kit->set_control(null_ctl);
+      if (null_free) {
+        vt = make_default_impl(gvn, vk, visited);
+      } else {
+        vt = make_null_impl(gvn, vk, visited);
+      }
+      kit->record_for_igvn(vt);
+      return vt;
+    }
+    vt = new InlineTypeNode(vk, not_null_oop, null_free);
+    vt->set_is_buffered(gvn);
+    vt->set_is_init(gvn);
+    vt->set_is_larval(is_larval);
+    vt->load(kit, not_null_oop, not_null_oop, vk, visited);
+
+    if (null_ctl != kit->top()) {
+      InlineTypeNode* null_vt = nullptr;
+      if (null_free) {
+        null_vt = make_default_impl(gvn, vk, visited);
+      } else {
+        null_vt = make_null_impl(gvn, vk, visited);
+      }
+      Node* region = new RegionNode(3);
+      region->init_req(1, kit->control());
+      region->init_req(2, null_ctl);
+      vt = vt->clone_with_phis(&gvn, region, kit->map());
+      vt->merge_with(&gvn, null_vt, 2, true);
+      if (!null_free) {
+        vt->set_oop(gvn, oop);
+      }
+      kit->set_control(gvn.transform(region));
+    }
+  } else {
+    // Oop can never be null
+    vt = new InlineTypeNode(vk, oop, /* null_free= */ true);
+    Node* init_ctl = kit->control();
+    vt->set_is_buffered(gvn);
+    vt->set_is_init(gvn);
+    vt->set_is_larval(is_larval);
+    vt->load(kit, oop, oop, vk, visited);
+// TODO 8284443
+//    assert(!null_free || vt->as_InlineType()->is_default(&gvn) || init_ctl != kit->control() || !gvn.type(oop)->is_inlinetypeptr() || oop->is_Con() || oop->Opcode() == Op_InlineType ||
+//           AllocateNode::Ideal_allocation(oop, &gvn) != nullptr || vt->as_InlineType()->is_loaded(&gvn) == oop, "inline type should be loaded");
+  }
+  assert(vt->is_allocated(&gvn) || (null_free && !vk->is_initialized()), "inline type should be allocated");
+  kit->record_for_igvn(vt);
+  return gvn.transform(vt)->as_InlineType();
+}
+
+InlineTypeNode* InlineTypeNode::make_from_flat(GraphKit* kit, ciInlineKlass* vk, Node* obj, Node* ptr, ciInstanceKlass* holder, int holder_offset, DecoratorSet decorators) {
+  GrowableArray<ciType*> visited;
+  visited.push(vk);
+  return make_from_flat_impl(kit, vk, obj, ptr, holder, holder_offset, decorators, visited);
+}
+
+// GraphKit wrapper for the 'make_from_flat' method
+InlineTypeNode* InlineTypeNode::make_from_flat_impl(GraphKit* kit, ciInlineKlass* vk, Node* obj, Node* ptr, ciInstanceKlass* holder, int holder_offset, DecoratorSet decorators, GrowableArray<ciType*>& visited) {
+  if (kit->gvn().type(obj)->isa_aryptr()) {
+    kit->C->set_flat_accesses();
+  }
+  // Create and initialize an InlineTypeNode by loading all field values from
+  // a flat inline type field at 'holder_offset' or from an inline type array.
+  InlineTypeNode* vt = make_uninitialized(kit->gvn(), vk);
+  // The inline type is flattened into the object without an oop header. Subtract the
+  // offset of the first field to account for the missing header when loading the values.
+  holder_offset -= vk->first_field_offset();
+  vt->load(kit, obj, ptr, holder, visited, holder_offset, decorators);
+  assert(vt->is_loaded(&kit->gvn()) != obj, "holder oop should not be used as flattened inline type oop");
+  return kit->gvn().transform(vt)->as_InlineType();
+}
+
+InlineTypeNode* InlineTypeNode::make_from_multi(GraphKit* kit, MultiNode* multi, ciInlineKlass* vk, uint& base_input, bool in, bool null_free) {
+  InlineTypeNode* vt = make_uninitialized(kit->gvn(), vk, null_free);
+  if (!in) {
+    // Keep track of the oop. The returned inline type might already be buffered.
+    Node* oop = kit->gvn().transform(new ProjNode(multi, base_input++));
+    vt->set_oop(kit->gvn(), oop);
+  }
+  GrowableArray<ciType*> visited;
+  visited.push(vk);
+  vt->initialize_fields(kit, multi, base_input, in, null_free, nullptr, visited);
+  return kit->gvn().transform(vt)->as_InlineType();
+}
+
+InlineTypeNode* InlineTypeNode::make_larval(GraphKit* kit, bool allocate) const {
+  ciInlineKlass* vk = inline_klass();
+  InlineTypeNode* res = make_uninitialized(kit->gvn(), vk);
+  for (uint i = 1; i < req(); ++i) {
+    res->set_req(i, in(i));
+  }
+
+  if (allocate) {
+    // Re-execute if buffering triggers deoptimization
+    PreserveReexecuteState preexecs(kit);
+    kit->jvms()->set_should_reexecute(true);
+    Node* klass_node = kit->makecon(TypeKlassPtr::make(vk));
+    Node* alloc_oop  = kit->new_instance(klass_node, nullptr, nullptr, true);
+    AllocateNode* alloc = AllocateNode::Ideal_allocation(alloc_oop);
+    alloc->_larval = true;
+
+    store(kit, alloc_oop, alloc_oop, vk);
+    res->set_oop(kit->gvn(), alloc_oop);
+  }
+  // TODO 8239003
+  //res->set_type(TypeInlineType::make(vk, true));
+  res = kit->gvn().transform(res)->as_InlineType();
+  assert(!allocate || res->is_allocated(&kit->gvn()), "must be allocated");
+  return res;
+}
+
+InlineTypeNode* InlineTypeNode::finish_larval(GraphKit* kit) const {
+  Node* obj = get_oop();
+  Node* mark_addr = kit->basic_plus_adr(obj, oopDesc::mark_offset_in_bytes());
+  Node* mark = kit->make_load(nullptr, mark_addr, TypeX_X, TypeX_X->basic_type(), MemNode::unordered);
+  mark = kit->gvn().transform(new AndXNode(mark, kit->MakeConX(~markWord::larval_bit_in_place)));
+  kit->store_to_memory(kit->control(), mark_addr, mark, TypeX_X->basic_type(), kit->gvn().type(mark_addr)->is_ptr(), MemNode::unordered);
+
+  // Do not let stores that initialize this buffer be reordered with a subsequent
+  // store that would make this buffer accessible by other threads.
+  AllocateNode* alloc = AllocateNode::Ideal_allocation(obj);
+  assert(alloc != nullptr, "must have an allocation node");
+  kit->insert_mem_bar(Op_MemBarStoreStore, alloc->proj_out_or_null(AllocateNode::RawAddress));
+
+  ciInlineKlass* vk = inline_klass();
+  InlineTypeNode* res = make_uninitialized(kit->gvn(), vk);
+  for (uint i = 1; i < req(); ++i) {
+    res->set_req(i, in(i));
+  }
+  // TODO 8239003
+  //res->set_type(TypeInlineType::make(vk, false));
+  res = kit->gvn().transform(res)->as_InlineType();
+  return res;
+}
+
+bool InlineTypeNode::is_larval(PhaseGVN* gvn) const {
+  if (!is_allocated(gvn)) {
+    return false;
+  }
+
+  Node* oop = get_oop();
+  AllocateNode* alloc = AllocateNode::Ideal_allocation(oop);
+  return alloc != nullptr && alloc->_larval;
+}
+
+Node* InlineTypeNode::is_loaded(PhaseGVN* phase, ciInlineKlass* vk, Node* base, int holder_offset) {
+  if (vk == nullptr) {
+    vk = inline_klass();
+  }
+  if (field_count() == 0 && vk->is_initialized()) {
+    const Type* tinit = phase->type(in(IsInit));
+    // TODO 8325106
+    if (false && !is_larval() && tinit->isa_int() && tinit->is_int()->is_con(1)) {
+      assert(is_allocated(phase), "must be allocated");
+      return get_oop();
+    } else {
+      // TODO 8284443
+      return nullptr;
+    }
+  }
+  for (uint i = 0; i < field_count(); ++i) {
+    int offset = holder_offset + field_offset(i);
+    Node* value = field_value(i);
+    if (value->is_InlineType()) {
+      InlineTypeNode* vt = value->as_InlineType();
+      if (vt->type()->inline_klass()->is_empty()) {
+        continue;
+      } else if (field_is_flat(i) && vt->is_InlineType()) {
+        // Check inline type field load recursively
+        base = vt->as_InlineType()->is_loaded(phase, vk, base, offset - vt->type()->inline_klass()->first_field_offset());
+        if (base == nullptr) {
+          return nullptr;
+        }
+        continue;
+      } else {
+        value = vt->get_oop();
+        if (value->Opcode() == Op_CastPP) {
+          // Skip CastPP
+          value = value->in(1);
+        }
+      }
+    }
+    if (value->isa_DecodeN()) {
+      // Skip DecodeN
+      value = value->in(1);
+    }
+    if (value->isa_Load()) {
+      // Check if base and offset of field load matches inline type layout
+      intptr_t loffset = 0;
+      Node* lbase = AddPNode::Ideal_base_and_offset(value->in(MemNode::Address), phase, loffset);
+      if (lbase == nullptr || (lbase != base && base != nullptr) || loffset != offset) {
+        return nullptr;
+      } else if (base == nullptr) {
+        // Set base and check if pointer type matches
+        base = lbase;
+        const TypeInstPtr* vtptr = phase->type(base)->isa_instptr();
+        if (vtptr == nullptr || !vtptr->instance_klass()->equals(vk)) {
+          return nullptr;
+        }
+      }
+    } else {
+      return nullptr;
+    }
+  }
+  return base;
+}
+
+Node* InlineTypeNode::tagged_klass(ciInlineKlass* vk, PhaseGVN& gvn) {
+  const TypeKlassPtr* tk = TypeKlassPtr::make(vk);
+  intptr_t bits = tk->get_con();
+  set_nth_bit(bits, 0);
+  return gvn.longcon((jlong)bits);
+}
+
+void InlineTypeNode::pass_fields(GraphKit* kit, Node* n, uint& base_input, bool in, bool null_free) {
+  if (!null_free && in) {
+    n->init_req(base_input++, get_is_init());
+  }
+  for (uint i = 0; i < field_count(); i++) {
+    Node* arg = field_value(i);
+    if (field_is_flat(i)) {
+      // Flat inline type field
+      arg->as_InlineType()->pass_fields(kit, n, base_input, in);
+    } else {
+      if (arg->is_InlineType()) {
+        // Non-flat inline type field
+        InlineTypeNode* vt = arg->as_InlineType();
+        assert(n->Opcode() != Op_Return || vt->is_allocated(&kit->gvn()), "inline type field should be allocated on return");
+        arg = vt->buffer(kit);
+      }
+      // Initialize call/return arguments
+      n->init_req(base_input++, arg);
+      if (field_type(i)->size() == 2) {
+        n->init_req(base_input++, kit->top());
+      }
+    }
+  }
+  // The last argument is used to pass IsInit information to compiled code and not required here.
+  if (!null_free && !in) {
+    n->init_req(base_input++, kit->top());
+  }
+}
+
+void InlineTypeNode::initialize_fields(GraphKit* kit, MultiNode* multi, uint& base_input, bool in, bool null_free, Node* null_check_region, GrowableArray<ciType*>& visited) {
+  PhaseGVN& gvn = kit->gvn();
+  Node* is_init = nullptr;
+  if (!null_free) {
+    // Nullable inline type
+    if (in) {
+      // Set IsInit field
+      if (multi->is_Start()) {
+        is_init = gvn.transform(new ParmNode(multi->as_Start(), base_input));
+      } else {
+        is_init = multi->as_Call()->in(base_input);
+      }
+      set_req(IsInit, is_init);
+      base_input++;
+    }
+    // Add a null check to make subsequent loads dependent on
+    assert(null_check_region == nullptr, "already set");
+    if (is_init == nullptr) {
+      // Will only be initialized below, use dummy node for now
+      is_init = new Node(1);
+      gvn.set_type_bottom(is_init);
+    }
+    Node* null_ctrl = kit->top();
+    kit->null_check_common(is_init, T_INT, false, &null_ctrl);
+    Node* non_null_ctrl = kit->control();
+    null_check_region = new RegionNode(3);
+    null_check_region->init_req(1, non_null_ctrl);
+    null_check_region->init_req(2, null_ctrl);
+    null_check_region = gvn.transform(null_check_region);
+    kit->set_control(null_check_region);
+  }
+
+  for (uint i = 0; i < field_count(); ++i) {
+    ciType* type = field_type(i);
+    Node* parm = nullptr;
+    if (field_is_flat(i)) {
+      // Flat inline type field
+      InlineTypeNode* vt = make_uninitialized(gvn, type->as_inline_klass());
+      vt->initialize_fields(kit, multi, base_input, in, true, null_check_region, visited);
+      parm = gvn.transform(vt);
+    } else {
+      if (multi->is_Start()) {
+        assert(in, "return from start?");
+        parm = gvn.transform(new ParmNode(multi->as_Start(), base_input));
+      } else if (in) {
+        parm = multi->as_Call()->in(base_input);
+      } else {
+        parm = gvn.transform(new ProjNode(multi->as_Call(), base_input));
+      }
+      // Non-flat inline type field
+      if (type->is_inlinetype()) {
+        if (null_check_region != nullptr) {
+          // We limit scalarization for inline types with circular fields and can therefore observe nodes
+          // of the same type but with different scalarization depth during GVN. To avoid inconsistencies
+          // during merging, make sure that we only create Phis for fields that are guaranteed to be scalarized.
+          if (parm->is_InlineType() && kit->C->has_circular_inline_type()) {
+            parm = parm->as_InlineType()->get_oop();
+          }
+          // Holder is nullable, set field to nullptr if holder is nullptr to avoid loading from uninitialized memory
+          parm = PhiNode::make(null_check_region, parm, TypeInstPtr::make(TypePtr::BotPTR, type->as_inline_klass()));
+          parm->set_req(2, kit->zerocon(T_OBJECT));
+          parm = gvn.transform(parm);
+        }
+        if (visited.contains(type)) {
+          kit->C->set_has_circular_inline_type(true);
+        } else if (!parm->is_InlineType()) {
+          int old_len = visited.length();
+          visited.push(type);
+          parm = make_from_oop_impl(kit, parm, type->as_inline_klass(), field_is_null_free(i), visited);
+          visited.trunc_to(old_len);
+        }
+      }
+      base_input += type->size();
+    }
+    assert(parm != nullptr, "should never be null");
+    assert(field_value(i) == nullptr, "already set");
+    set_field_value(i, parm);
+    gvn.record_for_igvn(parm);
+  }
+  // The last argument is used to pass IsInit information to compiled code
+  if (!null_free && !in) {
+    Node* cmp = is_init->raw_out(0);
+    is_init = gvn.transform(new ProjNode(multi->as_Call(), base_input));
+    set_req(IsInit, is_init);
+    gvn.hash_delete(cmp);
+    cmp->set_req(1, is_init);
+    gvn.hash_find_insert(cmp);
+    base_input++;
+  }
+}
+
+// Search for multiple allocations of this inline type and try to replace them by dominating allocations.
+// Equivalent InlineTypeNodes are merged by GVN, so we just need to search for AllocateNode users to find redundant allocations.
+void InlineTypeNode::remove_redundant_allocations(PhaseIdealLoop* phase) {
+  if (is_larval()) {
+    return;
+  }
+  PhaseIterGVN* igvn = &phase->igvn();
+  // Search for allocations of this inline type. Ignore scalar replaceable ones, they
+  // will be removed anyway and changing the memory chain will confuse other optimizations.
+  for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) {
+    AllocateNode* alloc = fast_out(i)->isa_Allocate();
+    if (alloc != nullptr && alloc->in(AllocateNode::InlineType) == this && !alloc->_is_scalar_replaceable) {
+      Node* res = alloc->result_cast();
+      if (res == nullptr || !res->is_CheckCastPP()) {
+        break; // No unique CheckCastPP
+      }
+      // TODO 8325106
+      // assert((!is_default(igvn) || !inline_klass()->is_initialized()) && !is_allocated(igvn), "re-allocation should be removed by Ideal transformation");
+      // Search for a dominating allocation of the same inline type
+      Node* res_dom = res;
+      for (DUIterator_Fast jmax, j = fast_outs(jmax); j < jmax; j++) {
+        AllocateNode* alloc_other = fast_out(j)->isa_Allocate();
+        if (alloc_other != nullptr && alloc_other->in(AllocateNode::InlineType) == this && !alloc_other->_is_scalar_replaceable) {
+          Node* res_other = alloc_other->result_cast();
+          if (res_other != nullptr && res_other->is_CheckCastPP() && res_other != res_dom &&
+              phase->is_dominator(res_other->in(0), res_dom->in(0))) {
+            res_dom = res_other;
+          }
+        }
+      }
+      if (res_dom != res) {
+        // Replace allocation by dominating one.
+        replace_allocation(igvn, res, res_dom);
+        // The result of the dominated allocation is now unused and will be removed
+        // later in PhaseMacroExpand::eliminate_allocate_node to not confuse loop opts.
+        igvn->_worklist.push(alloc);
+      }
+    }
+  }
+}
+
+InlineTypeNode* InlineTypeNode::make_null(PhaseGVN& gvn, ciInlineKlass* vk, bool transform) {
+  GrowableArray<ciType*> visited;
+  visited.push(vk);
+  return make_null_impl(gvn, vk, visited, transform);
+}
+
+InlineTypeNode* InlineTypeNode::make_null_impl(PhaseGVN& gvn, ciInlineKlass* vk, GrowableArray<ciType*>& visited, bool transform) {
+  InlineTypeNode* vt = new InlineTypeNode(vk, gvn.zerocon(T_OBJECT), /* null_free= */ false);
+  vt->set_is_buffered(gvn);
+  vt->set_is_init(gvn, false);
+  for (uint i = 0; i < vt->field_count(); i++) {
+    ciType* ft = vt->field_type(i);
+    Node* value = gvn.zerocon(ft->basic_type());
+    if (!vt->field_is_flat(i) && visited.contains(ft)) {
+      gvn.C->set_has_circular_inline_type(true);
+    } else if (ft->is_inlinetype()) {
+      int old_len = visited.length();
+      visited.push(ft);
+      value = make_null_impl(gvn, ft->as_inline_klass(), visited);
+      visited.trunc_to(old_len);
+    }
+    vt->set_field_value(i, value);
+  }
+  return transform ? gvn.transform(vt)->as_InlineType() : vt;
+}
+
+InlineTypeNode* InlineTypeNode::clone_if_required(PhaseGVN* gvn, SafePointNode* map, bool safe_for_replace) {
+  if (!safe_for_replace || (map == nullptr && outcnt() != 0)) {
+    return clone()->as_InlineType();
+  }
+  for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) {
+    if (fast_out(i) != map) {
+      return clone()->as_InlineType();
+    }
+  }
+  gvn->hash_delete(this);
+  return this;
+}
+
+const Type* InlineTypeNode::Value(PhaseGVN* phase) const {
+  Node* oop = get_oop();
+  const Type* toop = phase->type(oop);
+#ifdef ASSERT
+  if (oop->is_Con() && toop->is_zero_type() && _type->isa_oopptr()->is_known_instance()) {
+    // We are not allocated (anymore) and should therefore not have an instance id
+    dump(1);
+    assert(false, "Unbuffered inline type should not have known instance id");
+  }
+#endif
+  const Type* t = toop->filter_speculative(_type);
+  if (t->singleton()) {
+    // Don't replace InlineType by a constant
+    t = _type;
+  }
+  const Type* tinit = phase->type(in(IsInit));
+  if (tinit == Type::TOP) {
+    return Type::TOP;
+  }
+  if (tinit->isa_int() && tinit->is_int()->is_con(1)) {
+    t = t->join_speculative(TypePtr::NOTNULL);
+  }
+  return t;
+}
diff a/src/hotspot/share/opto/inlinetypenode.hpp b/src/hotspot/share/opto/inlinetypenode.hpp
--- /dev/null
+++ b/src/hotspot/share/opto/inlinetypenode.hpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2016, 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_VM_OPTO_INLINETYPENODE_HPP
+#define SHARE_VM_OPTO_INLINETYPENODE_HPP
+
+#include "opto/connode.hpp"
+#include "opto/loopnode.hpp"
+#include "opto/node.hpp"
+
+class GraphKit;
+
+//------------------------------InlineTypeNode-------------------------------------
+// Node representing an inline type in C2 IR
+class InlineTypeNode : public TypeNode {
+protected:
+  InlineTypeNode(ciInlineKlass* vk, Node* oop, bool null_free)
+      : TypeNode(TypeInstPtr::make(null_free ? TypePtr::NotNull : TypePtr::BotPTR, vk), Values + vk->nof_declared_nonstatic_fields()) {
+    init_class_id(Class_InlineType);
+    init_req(Oop, oop);
+    Compile::current()->add_inline_type(this);
+    _is_larval = false;
+  }
+
+  enum { Control,    // Control input.
+         Oop,        // Oop to heap allocated buffer.
+         IsBuffered, // True if inline type is heap allocated (or nullptr), false otherwise.
+         IsInit,     // Needs to be checked for nullptr before using the field values.
+         Values      // Nodes corresponding to values of the inline type's fields.
+                     // Nodes are connected in increasing order of the index of the field they correspond to.
+  };
+
+  bool _is_larval;
+
+  virtual uint hash() const { return TypeNode::hash() + _is_larval; }
+  // TODO 8325106 why can't we gvn larvals?
+  virtual bool cmp(const Node &n) const { return TypeNode::cmp(n) && !((InlineTypeNode&)n)._is_larval && !_is_larval; }
+  virtual uint size_of() const { return sizeof(*this); }
+
+  // Get the klass defining the field layout of the inline type
+  ciInlineKlass* inline_klass() const { return type()->inline_klass(); }
+
+  void make_scalar_in_safepoint(PhaseIterGVN* igvn, Unique_Node_List& worklist, SafePointNode* sfpt);
+
+  const TypePtr* field_adr_type(Node* base, int offset, ciInstanceKlass* holder, DecoratorSet decorators, PhaseGVN& gvn) const;
+
+  // Checks if the inline type fields are all set to default values
+  bool is_default(PhaseGVN* gvn) const;
+
+  // Checks if the inline type oop is an allocated buffer with larval state
+  bool is_larval(PhaseGVN* gvn) const;
+
+  // Checks if the inline type is loaded from memory and if so returns the oop
+  Node* is_loaded(PhaseGVN* phase, ciInlineKlass* vk = nullptr, Node* base = nullptr, int holder_offset = 0);
+
+  // Initialize the inline type fields with the inputs or outputs of a MultiNode
+  void initialize_fields(GraphKit* kit, MultiNode* multi, uint& base_input, bool in, bool null_free, Node* null_check_region, GrowableArray<ciType*>& visited);
+
+  InlineTypeNode* adjust_scalarization_depth_impl(GraphKit* kit, GrowableArray<ciType*>& visited);
+
+  static InlineTypeNode* make_default_impl(PhaseGVN& gvn, ciInlineKlass* vk, GrowableArray<ciType*>& visited, bool is_larval = false);
+  static InlineTypeNode* make_from_oop_impl(GraphKit* kit, Node* oop, ciInlineKlass* vk, bool null_free, GrowableArray<ciType*>& visited, bool is_larval = false);
+  static InlineTypeNode* make_null_impl(PhaseGVN& gvn, ciInlineKlass* vk, GrowableArray<ciType*>& visited, bool transform = true);
+  static InlineTypeNode* make_from_flat_impl(GraphKit* kit, ciInlineKlass* vk, Node* obj, Node* ptr, ciInstanceKlass* holder, int holder_offset, DecoratorSet decorators, GrowableArray<ciType*>& visited);
+
+public:
+  // Create with default field values
+  static InlineTypeNode* make_default(PhaseGVN& gvn, ciInlineKlass* vk, bool is_larval = false);
+  // Create uninitialized
+  static InlineTypeNode* make_uninitialized(PhaseGVN& gvn, ciInlineKlass* vk, bool null_free = true);
+  // Create and initialize by loading the field values from an oop
+  static InlineTypeNode* make_from_oop(GraphKit* kit, Node* oop, ciInlineKlass* vk, bool null_free = true, bool is_larval = false);
+  // Create and initialize by loading the field values from a flat field or array
+  static InlineTypeNode* make_from_flat(GraphKit* kit, ciInlineKlass* vk, Node* obj, Node* ptr, ciInstanceKlass* holder = nullptr, int holder_offset = 0, DecoratorSet decorators = IN_HEAP | MO_UNORDERED);
+  // Create and initialize with the inputs or outputs of a MultiNode (method entry or call)
+  static InlineTypeNode* make_from_multi(GraphKit* kit, MultiNode* multi, ciInlineKlass* vk, uint& base_input, bool in, bool null_free = true);
+  // Create with null field values
+  static InlineTypeNode* make_null(PhaseGVN& gvn, ciInlineKlass* vk, bool transform = true);
+
+  // Returns the constant oop of the default inline type allocation
+  static Node* default_oop(PhaseGVN& gvn, ciInlineKlass* vk);
+
+  // Support for control flow merges
+  bool has_phi_inputs(Node* region);
+  InlineTypeNode* clone_with_phis(PhaseGVN* gvn, Node* region, SafePointNode* map = nullptr, bool is_init = false);
+  InlineTypeNode* merge_with(PhaseGVN* gvn, const InlineTypeNode* other, int pnum, bool transform);
+  void add_new_path(Node* region);
+
+  // Get oop for heap allocated inline type (may be TypePtr::NULL_PTR)
+  Node* get_oop() const    { return in(Oop); }
+  void  set_oop(PhaseGVN& gvn, Node* oop) { set_req_X(Oop, oop, &gvn); }
+  Node* get_is_init() const { return in(IsInit); }
+  void  set_is_init(PhaseGVN& gvn, bool init = true) { set_req_X(IsInit, gvn.intcon(init ? 1 : 0), &gvn); }
+  Node* get_is_buffered() const { return in(IsBuffered); }
+  void  set_is_buffered(PhaseGVN& gvn, bool buffered = true) { set_req_X(IsBuffered, gvn.intcon(buffered ? 1 : 0), &gvn); }
+
+  void set_is_larval(bool is_larval) { _is_larval = is_larval; }
+  bool is_larval() { return _is_larval; }
+
+  // Inline type fields
+  uint          field_count() const { return req() - Values; }
+  Node*         field_value(uint index) const;
+  Node*         field_value_by_offset(int offset, bool recursive = false) const;
+  void      set_field_value(uint index, Node* value);
+  void      set_field_value_by_offset(int offset, Node* value);
+  int           field_offset(uint index) const;
+  uint          field_index(int offset) const;
+  ciType*       field_type(uint index) const;
+  bool          field_is_flat(uint index) const;
+  bool          field_is_null_free(uint index) const;
+
+  // Replace InlineTypeNodes in debug info at safepoints with SafePointScalarObjectNodes
+  void make_scalar_in_safepoints(PhaseIterGVN* igvn, bool allow_oop = true);
+
+  // Store the inline type as a flat (headerless) representation
+  void store_flat(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder = nullptr, int holder_offset = 0, DecoratorSet decorators = IN_HEAP | MO_UNORDERED) const;
+  // Store the field values to memory
+  void store(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder, int holder_offset = 0, DecoratorSet decorators = IN_HEAP | MO_UNORDERED, int offset = -1) const;
+  // Initialize the inline type by loading its field values from memory
+  void load(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder, GrowableArray<ciType*>& visited, int holder_offset = 0, DecoratorSet decorators = IN_HEAP | MO_UNORDERED);
+  // Make sure that inline type is fully scalarized
+  InlineTypeNode* adjust_scalarization_depth(GraphKit* kit);
+
+  // Allocates the inline type (if not yet allocated)
+  InlineTypeNode* buffer(GraphKit* kit, bool safe_for_replace = true);
+  bool is_allocated(PhaseGVN* phase) const;
+
+  void replace_call_results(GraphKit* kit, CallNode* call, Compile* C);
+
+  // Allocate all non-flat inline type fields
+  Node* allocate_fields(GraphKit* kit);
+
+  Node* tagged_klass(PhaseGVN& gvn) {
+    return tagged_klass(inline_klass(), gvn);
+  }
+  static Node* tagged_klass(ciInlineKlass* vk, PhaseGVN& gvn);
+  // Pass inline type as fields at a call or return
+  void pass_fields(GraphKit* kit, Node* n, uint& base_input, bool in, bool null_free = true);
+
+  InlineTypeNode* make_larval(GraphKit* kit, bool allocate) const;
+  InlineTypeNode* finish_larval(GraphKit* kit) const;
+
+  // Allocation optimizations
+  void remove_redundant_allocations(PhaseIdealLoop* phase);
+
+  InlineTypeNode* clone_if_required(PhaseGVN* gvn, SafePointNode* map, bool safe_for_replace = true);
+
+  virtual const Type* Value(PhaseGVN* phase) const;
+
+  virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
+
+  virtual int Opcode() const;
+};
+
+#endif // SHARE_VM_OPTO_INLINETYPENODE_HPP
diff a/src/hotspot/share/opto/lcm.cpp b/src/hotspot/share/opto/lcm.cpp
--- a/src/hotspot/share/opto/lcm.cpp
+++ b/src/hotspot/share/opto/lcm.cpp
@@ -275,13 +275,13 @@
         } else {
           // only regular oops are expected here
           tptr = base->bottom_type()->is_ptr();
         }
         // Give up if offset is not a compile-time constant.
-        if (offset == Type::OffsetBot || tptr->_offset == Type::OffsetBot)
+        if (offset == Type::OffsetBot || tptr->offset() == Type::OffsetBot)
           continue;
-        offset += tptr->_offset; // correct if base is offsetted
+        offset += tptr->offset(); // correct if base is offsetted
         // Give up if reference is beyond page size.
         if (MacroAssembler::needs_explicit_null_check(offset))
           continue;
         // Give up if base is a decode node and the heap base is not protected.
         if (base->is_Mach() && base->as_Mach()->ideal_Opcode() == Op_DecodeN &&
@@ -314,11 +314,15 @@
         vidx = j;
         // Ignore DecodeN val which could be hoisted to where needed.
         if( is_decoden ) continue;
       }
       // Block of memory-op input
-      Block *inb = get_block_for_node(mach->in(j));
+      Block* inb = get_block_for_node(mach->in(j));
+      if (mach->in(j)->is_Con() && mach->in(j)->req() == 1 && inb == get_block_for_node(mach)) {
+        // Ignore constant loads scheduled in the same block (we can simply hoist them as well)
+        continue;
+      }
       Block *b = block;          // Start from nul check
       while( b != inb && b->_dom_depth > inb->_dom_depth )
         b = b->_idom;           // search upwards for input
       // See if input dominates null check
       if( b != inb )
@@ -407,10 +411,31 @@
           map_node_to_block(n, block);
         }
       }
     }
   }
+
+  // Hoist constant load inputs as well.
+  for (uint i = 1; i < best->req(); ++i) {
+    Node* n = best->in(i);
+    if (n->is_Con() && get_block_for_node(n) == get_block_for_node(best)) {
+      get_block_for_node(n)->find_remove(n);
+      block->add_inst(n);
+      map_node_to_block(n, block);
+      // Constant loads may kill flags (for example, when XORing a register).
+      // Check for flag-killing projections that also need to be hoisted.
+      for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax; j++) {
+        Node* proj = n->fast_out(j);
+        if (proj->is_MachProj()) {
+          get_block_for_node(proj)->find_remove(proj);
+          block->add_inst(proj);
+          map_node_to_block(proj, block);
+        }
+      }
+    }
+  }
+
   // Hoist the memory candidate up to the end of the test block.
   Block *old_block = get_block_for_node(best);
   old_block->find_remove(best);
   block->add_inst(best);
   map_node_to_block(best, block);
@@ -878,11 +903,11 @@
   // Act as if the call defines the Frame Pointer.
   // Certainly the FP is alive and well after the call.
   regs.Insert(_matcher.c_frame_pointer());
 
   // Set all registers killed and not already defined by the call.
-  uint r_cnt = mcall->tf()->range()->cnt();
+  uint r_cnt = mcall->tf()->range_cc()->cnt();
   int op = mcall->ideal_Opcode();
   MachProjNode *proj = new MachProjNode( mcall, r_cnt+1, RegMask::Empty, MachProjNode::fat_proj );
   map_node_to_block(proj, block);
   block->insert_node(proj, node_cnt++);
 
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
@@ -22,10 +22,11 @@
  *
  */
 
 #include "precompiled.hpp"
 #include "asm/macroAssembler.hpp"
+#include "ci/ciFlatArrayKlass.hpp"
 #include "ci/ciUtilities.inline.hpp"
 #include "classfile/vmIntrinsics.hpp"
 #include "compiler/compileBroker.hpp"
 #include "compiler/compileLog.hpp"
 #include "gc/shared/barrierSet.hpp"
@@ -323,29 +324,33 @@
   case vmIntrinsics::_compressStringC:
   case vmIntrinsics::_compressStringB:          return inline_string_copy( is_compress);
   case vmIntrinsics::_inflateStringC:
   case vmIntrinsics::_inflateStringB:           return inline_string_copy(!is_compress);
 
+  case vmIntrinsics::_makePrivateBuffer:        return inline_unsafe_make_private_buffer();
+  case vmIntrinsics::_finishPrivateBuffer:      return inline_unsafe_finish_private_buffer();
   case vmIntrinsics::_getReference:             return inline_unsafe_access(!is_store, T_OBJECT,   Relaxed, false);
   case vmIntrinsics::_getBoolean:               return inline_unsafe_access(!is_store, T_BOOLEAN,  Relaxed, false);
   case vmIntrinsics::_getByte:                  return inline_unsafe_access(!is_store, T_BYTE,     Relaxed, false);
   case vmIntrinsics::_getShort:                 return inline_unsafe_access(!is_store, T_SHORT,    Relaxed, false);
   case vmIntrinsics::_getChar:                  return inline_unsafe_access(!is_store, T_CHAR,     Relaxed, false);
   case vmIntrinsics::_getInt:                   return inline_unsafe_access(!is_store, T_INT,      Relaxed, false);
   case vmIntrinsics::_getLong:                  return inline_unsafe_access(!is_store, T_LONG,     Relaxed, false);
   case vmIntrinsics::_getFloat:                 return inline_unsafe_access(!is_store, T_FLOAT,    Relaxed, false);
   case vmIntrinsics::_getDouble:                return inline_unsafe_access(!is_store, T_DOUBLE,   Relaxed, false);
+  case vmIntrinsics::_getValue:                 return inline_unsafe_access(!is_store, T_OBJECT,   Relaxed, false, true);
 
   case vmIntrinsics::_putReference:             return inline_unsafe_access( is_store, T_OBJECT,   Relaxed, false);
   case vmIntrinsics::_putBoolean:               return inline_unsafe_access( is_store, T_BOOLEAN,  Relaxed, false);
   case vmIntrinsics::_putByte:                  return inline_unsafe_access( is_store, T_BYTE,     Relaxed, false);
   case vmIntrinsics::_putShort:                 return inline_unsafe_access( is_store, T_SHORT,    Relaxed, false);
   case vmIntrinsics::_putChar:                  return inline_unsafe_access( is_store, T_CHAR,     Relaxed, false);
   case vmIntrinsics::_putInt:                   return inline_unsafe_access( is_store, T_INT,      Relaxed, false);
   case vmIntrinsics::_putLong:                  return inline_unsafe_access( is_store, T_LONG,     Relaxed, false);
   case vmIntrinsics::_putFloat:                 return inline_unsafe_access( is_store, T_FLOAT,    Relaxed, false);
   case vmIntrinsics::_putDouble:                return inline_unsafe_access( is_store, T_DOUBLE,   Relaxed, false);
+  case vmIntrinsics::_putValue:                 return inline_unsafe_access( is_store, T_OBJECT,   Relaxed, false, true);
 
   case vmIntrinsics::_getReferenceVolatile:     return inline_unsafe_access(!is_store, T_OBJECT,   Volatile, false);
   case vmIntrinsics::_getBooleanVolatile:       return inline_unsafe_access(!is_store, T_BOOLEAN,  Volatile, false);
   case vmIntrinsics::_getByteVolatile:          return inline_unsafe_access(!is_store, T_BYTE,     Volatile, false);
   case vmIntrinsics::_getShortVolatile:         return inline_unsafe_access(!is_store, T_SHORT,    Volatile, false);
@@ -506,10 +511,11 @@
   case vmIntrinsics::_writeback0:               return inline_unsafe_writeback0();
   case vmIntrinsics::_writebackPreSync0:        return inline_unsafe_writebackSync0(true);
   case vmIntrinsics::_writebackPostSync0:       return inline_unsafe_writebackSync0(false);
   case vmIntrinsics::_allocateInstance:         return inline_unsafe_allocate();
   case vmIntrinsics::_copyMemory:               return inline_unsafe_copyMemory();
+  case vmIntrinsics::_isFlatArray:              return inline_unsafe_isFlatArray();
   case vmIntrinsics::_getLength:                return inline_native_getLength();
   case vmIntrinsics::_copyOf:                   return inline_array_copyOf(false);
   case vmIntrinsics::_copyOfRange:              return inline_array_copyOf(true);
   case vmIntrinsics::_equalsB:                  return inline_array_equals(StrIntrinsicNode::LL);
   case vmIntrinsics::_equalsC:                  return inline_array_equals(StrIntrinsicNode::UU);
@@ -517,10 +523,11 @@
   case vmIntrinsics::_Preconditions_checkLongIndex: return inline_preconditions_checkIndex(T_LONG);
   case vmIntrinsics::_clone:                    return inline_native_clone(intrinsic()->is_virtual());
 
   case vmIntrinsics::_allocateUninitializedArray: return inline_unsafe_newArray(true);
   case vmIntrinsics::_newArray:                   return inline_unsafe_newArray(false);
+  case vmIntrinsics::_newNullRestrictedArray:   return inline_newNullRestrictedArray();
 
   case vmIntrinsics::_isAssignableFrom:         return inline_native_subtype_check();
 
   case vmIntrinsics::_isInstance:
   case vmIntrinsics::_getModifiers:
@@ -2208,23 +2215,26 @@
 //----------------------------inline_unsafe_access----------------------------
 
 const TypeOopPtr* LibraryCallKit::sharpen_unsafe_type(Compile::AliasType* alias_type, const TypePtr *adr_type) {
   // Attempt to infer a sharper value type from the offset and base type.
   ciKlass* sharpened_klass = nullptr;
+  bool null_free = false;
 
   // See if it is an instance field, with an object type.
   if (alias_type->field() != nullptr) {
     if (alias_type->field()->type()->is_klass()) {
       sharpened_klass = alias_type->field()->type()->as_klass();
+      null_free = alias_type->field()->is_null_free();
     }
   }
 
   const TypeOopPtr* result = nullptr;
   // See if it is a narrow oop array.
   if (adr_type->isa_aryptr()) {
     if (adr_type->offset() >= objArrayOopDesc::base_offset_in_bytes()) {
       const TypeOopPtr* elem_type = adr_type->is_aryptr()->elem()->make_oopptr();
+      null_free = adr_type->is_aryptr()->is_null_free();
       if (elem_type != nullptr && elem_type->is_loaded()) {
         // Sharpen the value type.
         result = elem_type;
       }
     }
@@ -2233,10 +2243,13 @@
   // The sharpened class might be unloaded if there is no class loader
   // contraint in place.
   if (result == nullptr && sharpened_klass != nullptr && sharpened_klass->is_loaded()) {
     // Sharpen the value type.
     result = TypeOopPtr::make_from_klass(sharpened_klass);
+    if (null_free) {
+      result = result->join_speculative(TypePtr::NOTNULL)->is_oopptr();
+    }
   }
   if (result != nullptr) {
 #ifndef PRODUCT
     if (C->print_intrinsics() || C->print_inlining()) {
       tty->print("  from base type:  ");  adr_type->dump(); tty->cr();
@@ -2263,11 +2276,11 @@
         ShouldNotReachHere();
         return 0;
   }
 }
 
-bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, const AccessKind kind, const bool unaligned) {
+bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, const AccessKind kind, const bool unaligned, const bool is_flat) {
   if (callee()->is_static())  return false;  // caller must have the capability!
   DecoratorSet decorators = C2_UNSAFE_ACCESS;
   guarantee(!is_store || kind != Acquire, "Acquire accesses can be produced only for loads");
   guarantee( is_store || kind != Release, "Release accesses can be produced only for stores");
   assert(type != T_OBJECT || !unaligned, "unaligned access not supported with object type");
@@ -2288,17 +2301,17 @@
 #ifdef ASSERT
     if (!is_store) {
       // Object getReference(Object base, int/long offset), etc.
       BasicType rtype = sig->return_type()->basic_type();
       assert(rtype == type, "getter must return the expected value");
-      assert(sig->count() == 2, "oop getter has 2 arguments");
+      assert(sig->count() == 2 || (is_flat && sig->count() == 3), "oop getter has 2 or 3 arguments");
       assert(sig->type_at(0)->basic_type() == T_OBJECT, "getter base is object");
       assert(sig->type_at(1)->basic_type() == T_LONG, "getter offset is correct");
     } else {
       // void putReference(Object base, int/long offset, Object x), etc.
       assert(sig->return_type()->basic_type() == T_VOID, "putter must not return a value");
-      assert(sig->count() == 3, "oop putter has 3 arguments");
+      assert(sig->count() == 3 || (is_flat && sig->count() == 4), "oop putter has 3 arguments");
       assert(sig->type_at(0)->basic_type() == T_OBJECT, "putter base is object");
       assert(sig->type_at(1)->basic_type() == T_LONG, "putter offset is correct");
       BasicType vtype = sig->type_at(sig->count()-1)->basic_type();
       assert(vtype == type, "putter must accept the expected value");
     }
@@ -2320,21 +2333,76 @@
   // We currently rely on the cookies produced by Unsafe.xxxFieldOffset
   // to be plain byte offsets, which are also the same as those accepted
   // by oopDesc::field_addr.
   assert(Unsafe_field_offset_to_byte_offset(11) == 11,
          "fieldOffset must be byte-scaled");
+
+  ciInlineKlass* inline_klass = nullptr;
+  if (is_flat) {
+    const TypeInstPtr* cls = _gvn.type(argument(4))->isa_instptr();
+    if (cls == nullptr || cls->const_oop() == nullptr) {
+      return false;
+    }
+    ciType* mirror_type = cls->const_oop()->as_instance()->java_mirror_type();
+    if (!mirror_type->is_inlinetype()) {
+      return false;
+    }
+    inline_klass = mirror_type->as_inline_klass();
+  }
+
+  if (base->is_InlineType()) {
+    InlineTypeNode* vt = base->as_InlineType();
+    if (is_store) {
+      if (!vt->is_allocated(&_gvn)) {
+        return false;
+      }
+      base = vt->get_oop();
+    } else {
+      if (offset->is_Con()) {
+        long off = find_long_con(offset, 0);
+        ciInlineKlass* vk = vt->type()->inline_klass();
+        if ((long)(int)off != off || !vk->contains_field_offset(off)) {
+          return false;
+        }
+
+        ciField* field = vk->get_non_flat_field_by_offset(off);
+        if (field != nullptr) {
+          BasicType bt = type2field[field->type()->basic_type()];
+          if (bt == T_ARRAY || bt == T_NARROWOOP) {
+            bt = T_OBJECT;
+          }
+          if (bt == type && (!field->is_flat() || field->type() == inline_klass)) {
+            Node* value = vt->field_value_by_offset(off, false);
+            if (value->is_InlineType()) {
+              value = value->as_InlineType()->adjust_scalarization_depth(this);
+            }
+            set_result(value);
+            return true;
+          }
+        }
+      }
+      {
+        // Re-execute the unsafe access if allocation triggers deoptimization.
+        PreserveReexecuteState preexecs(this);
+        jvms()->set_should_reexecute(true);
+        vt = vt->buffer(this);
+      }
+      base = vt->get_oop();
+    }
+  }
+
   // 32-bit machines ignore the high half!
   offset = ConvL2X(offset);
 
   // Save state and restore on bailout
   uint old_sp = sp();
   SafePointNode* old_map = clone_map();
 
   Node* adr = make_unsafe_address(base, offset, type, kind == Relaxed);
 
   if (_gvn.type(base)->isa_ptr() == TypePtr::NULL_PTR) {
-    if (type != T_OBJECT) {
+    if (type != T_OBJECT && (inline_klass == nullptr || !inline_klass->has_object_fields())) {
       decorators |= IN_NATIVE; // off-heap primitive access
     } else {
       set_map(old_map);
       set_sp(old_sp);
       return false; // off-heap oop accesses are not supported
@@ -2348,11 +2416,11 @@
 
   if (!can_access_non_heap) {
     decorators |= IN_HEAP;
   }
 
-  Node* val = is_store ? argument(4) : nullptr;
+  Node* val = is_store ? argument(4 + (is_flat ? 1 : 0)) : nullptr;
 
   const TypePtr* adr_type = _gvn.type(adr)->isa_ptr();
   if (adr_type == TypePtr::NULL_PTR) {
     set_map(old_map);
     set_sp(old_sp);
@@ -2369,11 +2437,32 @@
     set_sp(old_sp);
     return false; // not supported
   }
 
   bool mismatched = false;
-  BasicType bt = alias_type->basic_type();
+  BasicType bt = T_ILLEGAL;
+  ciField* field = nullptr;
+  if (adr_type->isa_instptr()) {
+    const TypeInstPtr* instptr = adr_type->is_instptr();
+    ciInstanceKlass* k = instptr->instance_klass();
+    int off = instptr->offset();
+    if (instptr->const_oop() != nullptr &&
+        k == ciEnv::current()->Class_klass() &&
+        instptr->offset() >= (k->size_helper() * wordSize)) {
+      k = instptr->const_oop()->as_instance()->java_lang_Class_klass()->as_instance_klass();
+      field = k->get_field_by_offset(off, true);
+    } else {
+      field = k->get_non_flat_field_by_offset(off);
+    }
+    if (field != nullptr) {
+      bt = type2field[field->type()->basic_type()];
+    }
+    assert(bt == alias_type->basic_type() || is_flat, "should match");
+  } else {
+    bt = alias_type->basic_type();
+  }
+
   if (bt != T_ILLEGAL) {
     assert(alias_type->adr_type()->is_oopptr(), "should be on-heap access");
     if (bt == T_BYTE && adr_type->isa_aryptr()) {
       // Alias type doesn't differentiate between byte[] and boolean[]).
       // Use address type to get the element type.
@@ -2392,12 +2481,35 @@
     mismatched = (bt != type);
   } else if (alias_type->adr_type()->isa_oopptr()) {
     mismatched = true; // conservatively mark all "wide" on-heap accesses as mismatched
   }
 
+  if (is_flat) {
+    if (adr_type->isa_instptr()) {
+      if (field == nullptr || field->type() != inline_klass) {
+        mismatched = true;
+      }
+    } else if (adr_type->isa_aryptr()) {
+      const Type* elem = adr_type->is_aryptr()->elem();
+      if (!adr_type->is_flat() || elem->inline_klass() != inline_klass) {
+        mismatched = true;
+      }
+    } else {
+      mismatched = true;
+    }
+    if (is_store) {
+      const Type* val_t = _gvn.type(val);
+      if (!val_t->is_inlinetypeptr() || val_t->inline_klass() != inline_klass) {
+        set_map(old_map);
+        set_sp(old_sp);
+        return false;
+      }
+    }
+  }
+
   destruct_map_clone(old_map);
-  assert(!mismatched || alias_type->adr_type()->is_oopptr(), "off-heap access can't be mismatched");
+  assert(!mismatched || is_flat || alias_type->adr_type()->is_oopptr(), "off-heap access can't be mismatched");
 
   if (mismatched) {
     decorators |= C2_MISMATCHED;
   }
 
@@ -2405,14 +2517,16 @@
   const Type *value_type = Type::get_const_basic_type(type);
 
   // Figure out the memory ordering.
   decorators |= mo_decorator_for_access_kind(kind);
 
-  if (!is_store && type == T_OBJECT) {
-    const TypeOopPtr* tjp = sharpen_unsafe_type(alias_type, adr_type);
-    if (tjp != nullptr) {
-      value_type = tjp;
+  if (!is_store) {
+    if (type == T_OBJECT && !is_flat) {
+      const TypeOopPtr* tjp = sharpen_unsafe_type(alias_type, adr_type);
+      if (tjp != nullptr) {
+        value_type = tjp;
+      }
     }
   }
 
   receiver = null_check(receiver);
   if (stopped()) {
@@ -2424,18 +2538,33 @@
   // from intended ones in this API.
 
   if (!is_store) {
     Node* p = nullptr;
     // Try to constant fold a load from a constant field
-    ciField* field = alias_type->field();
-    if (heap_base_oop != top() && field != nullptr && field->is_constant() && !mismatched) {
+
+    if (heap_base_oop != top() && field != nullptr && field->is_constant() && !field->is_flat() && !mismatched) {
       // final or stable field
       p = make_constant_from_field(field, heap_base_oop);
     }
 
     if (p == nullptr) { // Could not constant fold the load
-      p = access_load_at(heap_base_oop, adr, adr_type, value_type, type, decorators);
+      if (is_flat) {
+        if (adr_type->isa_instptr() && !mismatched) {
+          ciInstanceKlass* holder = adr_type->is_instptr()->instance_klass();
+          int offset = adr_type->is_instptr()->offset();
+          p = InlineTypeNode::make_from_flat(this, inline_klass, base, base, holder, offset, decorators);
+        } else {
+          p = InlineTypeNode::make_from_flat(this, inline_klass, base, adr, nullptr, 0, decorators);
+        }
+      } else {
+        p = access_load_at(heap_base_oop, adr, adr_type, value_type, type, decorators);
+        const TypeOopPtr* ptr = value_type->make_oopptr();
+        if (ptr != nullptr && ptr->is_inlinetypeptr()) {
+          // Load a non-flattened inline type from memory
+          p = InlineTypeNode::make_from_oop(this, p, ptr->inline_klass(), !ptr->maybe_null());
+        }
+      }
       // Normalize the value returned by getBoolean in the following cases
       if (type == T_BOOLEAN &&
           (mismatched ||
            heap_base_oop == top() ||                  // - heap_base_oop is null or
            (can_access_non_heap && field == nullptr)) // - heap_base_oop is potentially null
@@ -2469,13 +2598,69 @@
     if (bt == T_ADDRESS) {
       // Repackage the long as a pointer.
       val = ConvL2X(val);
       val = gvn().transform(new CastX2PNode(val));
     }
-    access_store_at(heap_base_oop, adr, adr_type, val, value_type, type, decorators);
+    if (is_flat) {
+      if (adr_type->isa_instptr() && !mismatched) {
+        ciInstanceKlass* holder = adr_type->is_instptr()->instance_klass();
+        int offset = adr_type->is_instptr()->offset();
+        val->as_InlineType()->store_flat(this, base, base, holder, offset, decorators);
+      } else {
+        val->as_InlineType()->store_flat(this, base, adr, nullptr, 0, decorators);
+      }
+    } else {
+      access_store_at(heap_base_oop, adr, adr_type, val, value_type, type, decorators);
+    }
+  }
+
+  if (argument(1)->is_InlineType() && is_store) {
+    InlineTypeNode* value = InlineTypeNode::make_from_oop(this, base, _gvn.type(argument(1))->inline_klass());
+    value = value->make_larval(this, false);
+    replace_in_map(argument(1), value);
+  }
+
+  return true;
+}
+
+bool LibraryCallKit::inline_unsafe_make_private_buffer() {
+  Node* receiver = argument(0);
+  Node* value = argument(1);
+  if (!value->is_InlineType()) {
+    return false;
+  }
+
+  receiver = null_check(receiver);
+  if (stopped()) {
+    return true;
+  }
+
+  set_result(value->as_InlineType()->make_larval(this, true));
+  return true;
+}
+
+bool LibraryCallKit::inline_unsafe_finish_private_buffer() {
+  Node* receiver = argument(0);
+  Node* buffer = argument(1);
+  if (!buffer->is_InlineType()) {
+    return false;
+  }
+  InlineTypeNode* vt = buffer->as_InlineType();
+  if (!vt->is_allocated(&_gvn)) {
+    return false;
+  }
+  // TODO 8239003 Why is this needed?
+  if (AllocateNode::Ideal_allocation(vt->get_oop()) == nullptr) {
+    return false;
+  }
+
+  receiver = null_check(receiver);
+  if (stopped()) {
+    return true;
   }
 
+  set_result(vt->finish_larval(this));
   return true;
 }
 
 //----------------------------inline_unsafe_load_store----------------------------
 // This method serves a couple of different customers (depending on LoadStoreKind):
@@ -2680,10 +2865,23 @@
   int alias_idx = C->get_alias_index(adr_type);
 
   if (is_reference_type(type)) {
     decorators |= IN_HEAP | ON_UNKNOWN_OOP_REF;
 
+    if (oldval != nullptr && oldval->is_InlineType()) {
+      // Re-execute the unsafe access if allocation triggers deoptimization.
+      PreserveReexecuteState preexecs(this);
+      jvms()->set_should_reexecute(true);
+      oldval = oldval->as_InlineType()->buffer(this)->get_oop();
+    }
+    if (newval != nullptr && newval->is_InlineType()) {
+      // Re-execute the unsafe access if allocation triggers deoptimization.
+      PreserveReexecuteState preexecs(this);
+      jvms()->set_should_reexecute(true);
+      newval = newval->as_InlineType()->buffer(this)->get_oop();
+    }
+
     // Transformation of a value which could be null pointer (CastPP #null)
     // could be delayed during Parse (for example, in adjust_map_after_if()).
     // Execute transformation here to avoid barrier generation in such case.
     if (_gvn.type(newval) == TypePtr::NULL_PTR)
       newval = _gvn.makecon(TypePtr::NULL_PTR);
@@ -2866,12 +3064,17 @@
     Node* inst = make_load(nullptr, insp, TypeInt::UBYTE, T_BOOLEAN, MemNode::unordered);
     Node* bits = intcon(InstanceKlass::fully_initialized);
     test = _gvn.transform(new SubINode(inst, bits));
     // The 'test' is non-zero if we need to take a slow path.
   }
-
-  Node* obj = new_instance(kls, test);
+  Node* obj = nullptr;
+  const TypeInstKlassPtr* tkls = _gvn.type(kls)->isa_instklassptr();
+  if (tkls != nullptr && tkls->instance_klass()->is_inlinetype()) {
+    obj = InlineTypeNode::make_default(_gvn, tkls->instance_klass()->as_inline_klass())->buffer(this);
+  } else {
+    obj = new_instance(kls, test);
+  }
   set_result(obj);
   return true;
 }
 
 //------------------------inline_native_time_funcs--------------
@@ -3643,16 +3846,16 @@
 }
 
 const Type* LibraryCallKit::scopedValueCache_type() {
   ciKlass* objects_klass = ciObjArrayKlass::make(env()->Object_klass());
   const TypeOopPtr* etype = TypeOopPtr::make_from_klass(env()->Object_klass());
-  const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS);
+  const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS, /* stable= */ false, /* flat= */ false, /* not_flat= */ true, /* not_null_free= */ true);
 
   // Because we create the scopedValue cache lazily we have to make the
   // type of the result BotPTR.
   bool xk = etype->klass_is_exact();
-  const Type* objects_type = TypeAryPtr::make(TypePtr::BotPTR, arr0, objects_klass, xk, 0);
+  const Type* objects_type = TypeAryPtr::make(TypePtr::BotPTR, arr0, objects_klass, xk, TypeAryPtr::Offset(0));
   return objects_type;
 }
 
 Node* LibraryCallKit::scopedValueCache_helper() {
   Node* thread = _gvn.transform(new ThreadLocalNode());
@@ -3684,19 +3887,10 @@
   access_store_at(nullptr, cache_obj_handle, adr_type, arr, objects_type, T_OBJECT, IN_NATIVE | MO_UNORDERED);
 
   return true;
 }
 
-//---------------------------load_mirror_from_klass----------------------------
-// Given a klass oop, load its java mirror (a java.lang.Class oop).
-Node* LibraryCallKit::load_mirror_from_klass(Node* klass) {
-  Node* p = basic_plus_adr(klass, in_bytes(Klass::java_mirror_offset()));
-  Node* load = make_load(nullptr, p, TypeRawPtr::NOTNULL, T_ADDRESS, MemNode::unordered);
-  // mirror = ((OopHandle)mirror)->resolve();
-  return access_load(load, TypeInstPtr::MIRROR, T_OBJECT, IN_NATIVE);
-}
-
 //-----------------------load_klass_from_mirror_common-------------------------
 // Given a java mirror (a java.lang.Class oop), load its corresponding klass oop.
 // Test the klass oop for null (signifying a primitive Class like Integer.TYPE),
 // and branch to the given path on the region.
 // If never_see_null, take an uncommon trap on null, so we can optimistically
@@ -3735,10 +3929,11 @@
   Node* mbit = _gvn.transform(new AndINode(mods, mask));
   Node* cmp  = _gvn.transform(new CmpINode(mbit, bits));
   Node* bol  = _gvn.transform(new BoolNode(cmp, BoolTest::ne));
   return generate_fair_guard(bol, region);
 }
+
 Node* LibraryCallKit::generate_interface_guard(Node* kls, RegionNode* region) {
   return generate_access_flags_guard(kls, JVM_ACC_INTERFACE, 0, region);
 }
 Node* LibraryCallKit::generate_hidden_class_guard(Node* kls, RegionNode* region) {
   return generate_access_flags_guard(kls, JVM_ACC_IS_HIDDEN_CLASS, 0, region);
@@ -3928,10 +4123,11 @@
   C->set_has_split_ifs(true); // Has chance for split-if optimization
   set_result(region, phi);
   return true;
 }
 
+
 //-------------------------inline_Class_cast-------------------
 bool LibraryCallKit::inline_Class_cast() {
   Node* mirror = argument(0); // Class
   Node* obj    = argument(1);
   const TypeInstPtr* mirror_con = _gvn.type(mirror)->isa_instptr();
@@ -3979,32 +4175,39 @@
   // If mirror is dead, only null-path is taken.
   if (stopped()) {
     return true;
   }
 
-  // Not-subtype or the mirror's klass ptr is null (in case it is a primitive).
-  enum { _bad_type_path = 1, _prim_path = 2, PATH_LIMIT };
+  // Not-subtype or the mirror's klass ptr is nullptr (in case it is a primitive).
+  enum { _bad_type_path = 1, _prim_path = 2, _npe_path = 3, PATH_LIMIT };
   RegionNode* region = new RegionNode(PATH_LIMIT);
   record_for_igvn(region);
 
   // Now load the mirror's klass metaobject, and null-check it.
   // If kls is null, we have a primitive mirror and
   // nothing is an instance of a primitive type.
   Node* kls = load_klass_from_mirror(mirror, false, region, _prim_path);
 
   Node* res = top();
+  Node* io = i_o();
+  Node* mem = merged_memory();
   if (!stopped()) {
+
     Node* bad_type_ctrl = top();
     // Do checkcast optimizations.
     res = gen_checkcast(obj, kls, &bad_type_ctrl);
     region->init_req(_bad_type_path, bad_type_ctrl);
   }
   if (region->in(_prim_path) != top() ||
-      region->in(_bad_type_path) != top()) {
+      region->in(_bad_type_path) != top() ||
+      region->in(_npe_path) != top()) {
     // Let Interpreter throw ClassCastException.
     PreserveJVMState pjvms(this);
     set_control(_gvn.transform(region));
+    // Set IO and memory because gen_checkcast may override them when buffering inline types
+    set_i_o(io);
+    set_all_memory(mem);
     uncommon_trap(Deoptimization::Reason_intrinsic,
                   Deoptimization::Action_maybe_recompile);
   }
   if (!stopped()) {
     set_result(res);
@@ -4034,12 +4237,14 @@
     _both_ref_path,             // {N,N} & subtype check loses => false
     PATH_LIMIT
   };
 
   RegionNode* region = new RegionNode(PATH_LIMIT);
+  RegionNode* prim_region = new RegionNode(2);
   Node*       phi    = new PhiNode(region, TypeInt::BOOL);
   record_for_igvn(region);
+  record_for_igvn(prim_region);
 
   const TypePtr* adr_type = TypeRawPtr::BOTTOM;   // memory type of loads
   const TypeKlassPtr* kls_type = TypeInstKlassPtr::OBJECT_OR_NULL;
   int class_klass_offset = java_lang_Class::klass_offset();
 
@@ -4060,34 +4265,37 @@
   bool never_see_null = !too_many_traps(Deoptimization::Reason_null_check);
   for (which_arg = 0; which_arg <= 1; which_arg++) {
     Node* kls = klasses[which_arg];
     Node* null_ctl = top();
     kls = null_check_oop(kls, &null_ctl, never_see_null);
-    int prim_path = (which_arg == 0 ? _prim_0_path : _prim_1_path);
-    region->init_req(prim_path, null_ctl);
+    if (which_arg == 0) {
+      prim_region->init_req(1, null_ctl);
+    } else {
+      region->init_req(_prim_1_path, null_ctl);
+    }
     if (stopped())  break;
     klasses[which_arg] = kls;
   }
 
   if (!stopped()) {
     // now we have two reference types, in klasses[0..1]
     Node* subk   = klasses[1];  // the argument to isAssignableFrom
     Node* superk = klasses[0];  // the receiver
     region->set_req(_both_ref_path, gen_subtype_check(subk, superk));
-    // now we have a successful reference subtype check
     region->set_req(_ref_subtype_path, control());
   }
 
   // If both operands are primitive (both klasses null), then
   // we must return true when they are identical primitives.
   // It is convenient to test this after the first null klass check.
-  set_control(region->in(_prim_0_path)); // go back to first null check
+  // This path is also used if superc is a value mirror.
+  set_control(_gvn.transform(prim_region));
   if (!stopped()) {
     // Since superc is primitive, make a guard for the superc==subc case.
     Node* cmp_eq = _gvn.transform(new CmpPNode(args[0], args[1]));
     Node* bol_eq = _gvn.transform(new BoolNode(cmp_eq, BoolTest::eq));
-    generate_guard(bol_eq, region, PROB_FAIR);
+    generate_fair_guard(bol_eq, region);
     if (region->req() == PATH_LIMIT+1) {
       // A guard was added.  If the added guard is taken, superc==subc.
       region->swap_edges(PATH_LIMIT, _prim_same_path);
       region->del_req(PATH_LIMIT);
     }
@@ -4114,59 +4322,99 @@
   set_result(_gvn.transform(phi));
   return true;
 }
 
 //---------------------generate_array_guard_common------------------------
-Node* LibraryCallKit::generate_array_guard_common(Node* kls, RegionNode* region,
-                                                  bool obj_array, bool not_array) {
+Node* LibraryCallKit::generate_array_guard_common(Node* kls, RegionNode* region, ArrayKind kind) {
 
   if (stopped()) {
     return nullptr;
   }
 
-  // If obj_array/non_array==false/false:
-  // Branch around if the given klass is in fact an array (either obj or prim).
-  // If obj_array/non_array==false/true:
-  // Branch around if the given klass is not an array klass of any kind.
-  // If obj_array/non_array==true/true:
-  // Branch around if the kls is not an oop array (kls is int[], String, etc.)
-  // If obj_array/non_array==true/false:
-  // Branch around if the kls is an oop array (Object[] or subtype)
-  //
   // Like generate_guard, adds a new path onto the region.
   jint  layout_con = 0;
   Node* layout_val = get_layout_helper(kls, layout_con);
   if (layout_val == nullptr) {
-    bool query = (obj_array
-                  ? Klass::layout_helper_is_objArray(layout_con)
-                  : Klass::layout_helper_is_array(layout_con));
-    if (query == not_array) {
+    bool query = 0;
+    switch(kind) {
+      case ObjectArray:    query = Klass::layout_helper_is_objArray(layout_con); break;
+      case NonObjectArray: query = !Klass::layout_helper_is_objArray(layout_con); break;
+      case TypeArray:      query = Klass::layout_helper_is_typeArray(layout_con); break;
+      case AnyArray:       query = Klass::layout_helper_is_array(layout_con); break;
+      case NonArray:       query = !Klass::layout_helper_is_array(layout_con); break;
+      default:
+        ShouldNotReachHere();
+    }
+    if (!query) {
       return nullptr;                       // never a branch
     } else {                             // always a branch
       Node* always_branch = control();
       if (region != nullptr)
         region->add_req(always_branch);
       set_control(top());
       return always_branch;
     }
   }
+  unsigned int value = 0;
+  BoolTest::mask btest = BoolTest::illegal;
+  switch(kind) {
+    case ObjectArray:
+    case NonObjectArray: {
+      value = Klass::_lh_array_tag_obj_value;
+      layout_val = _gvn.transform(new RShiftINode(layout_val, intcon(Klass::_lh_array_tag_shift)));
+      btest = (kind == ObjectArray) ? BoolTest::eq : BoolTest::ne;
+      break;
+    }
+    case TypeArray: {
+      value = Klass::_lh_array_tag_type_value;
+      layout_val = _gvn.transform(new RShiftINode(layout_val, intcon(Klass::_lh_array_tag_shift)));
+      btest = BoolTest::eq;
+      break;
+    }
+    case AnyArray:    value = Klass::_lh_neutral_value; btest = BoolTest::lt; break;
+    case NonArray:    value = Klass::_lh_neutral_value; btest = BoolTest::gt; break;
+    default:
+      ShouldNotReachHere();
+  }
   // Now test the correct condition.
-  jint  nval = (obj_array
-                ? (jint)(Klass::_lh_array_tag_type_value
-                   <<    Klass::_lh_array_tag_shift)
-                : Klass::_lh_neutral_value);
+  jint nval = (jint)value;
   Node* cmp = _gvn.transform(new CmpINode(layout_val, intcon(nval)));
-  BoolTest::mask btest = BoolTest::lt;  // correct for testing is_[obj]array
-  // invert the test if we are looking for a non-array
-  if (not_array)  btest = BoolTest(btest).negate();
   Node* bol = _gvn.transform(new BoolNode(cmp, btest));
   return generate_fair_guard(bol, region);
 }
 
+//-----------------------inline_newNullRestrictedArray--------------------------
+// public static native Object[] newNullRestrictedArray(Class<?> componentType, int length);
+bool LibraryCallKit::inline_newNullRestrictedArray() {
+  Node* componentType = argument(0);
+  Node* length = argument(1);
+
+  const TypeInstPtr* tp = _gvn.type(componentType)->isa_instptr();
+  if (tp != nullptr) {
+    ciInstanceKlass* ik = tp->instance_klass();
+    if (ik == C->env()->Class_klass()) {
+      ciType* t = tp->java_mirror_type();
+      if (t != nullptr && t->is_inlinetype()) {
+        ciArrayKlass* array_klass = ciArrayKlass::make(t, true);
+        if (array_klass->is_loaded() && array_klass->element_klass()->as_inline_klass()->is_initialized()) {
+          const TypeAryKlassPtr* array_klass_type = TypeKlassPtr::make(array_klass, Type::trust_interfaces)->is_aryklassptr();
+          array_klass_type = array_klass_type->cast_to_null_free();
+          Node* obj = new_array(makecon(array_klass_type), length, 0, nullptr, false);  // no arguments to push
+          AllocateArrayNode* alloc = AllocateArrayNode::Ideal_array_allocation(obj);
+          alloc->set_null_free();
+          set_result(obj);
+          assert(gvn().type(obj)->is_aryptr()->is_null_free(), "must be null-free");
+          return true;
+        }
+      }
+    }
+  }
+  return false;
+}
 
 //-----------------------inline_native_newArray--------------------------
-// private static native Object java.lang.reflect.newArray(Class<?> componentType, int length);
+// private static native Object java.lang.reflect.Array.newArray(Class<?> componentType, int length);
 // private        native Object Unsafe.allocateUninitializedArray0(Class<?> cls, int size);
 bool LibraryCallKit::inline_unsafe_newArray(bool uninitialized) {
   Node* mirror;
   Node* count_val;
   if (uninitialized) {
@@ -4308,15 +4556,27 @@
     RegionNode* bailout = new RegionNode(1);
     record_for_igvn(bailout);
 
     // Despite the generic type of Arrays.copyOf, the mirror might be int, int[], etc.
     // Bail out if that is so.
-    Node* not_objArray = generate_non_objArray_guard(klass_node, bailout);
+    // Inline type array may have object field that would require a
+    // write barrier. Conservatively, go to slow path.
+    // TODO 8251971: Optimize for the case when flat src/dst are later found
+    // to not contain oops (i.e., move this check to the macro expansion phase).
+    BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
+    const TypeAryPtr* orig_t = _gvn.type(original)->isa_aryptr();
+    const TypeKlassPtr* tklass = _gvn.type(klass_node)->is_klassptr();
+    bool exclude_flat = UseFlatArray && bs->array_copy_requires_gc_barriers(true, T_OBJECT, false, false, BarrierSetC2::Parsing) &&
+                        // Can src array be flat and contain oops?
+                        (orig_t == nullptr || (!orig_t->is_not_flat() && (!orig_t->is_flat() || orig_t->elem()->inline_klass()->contains_oops()))) &&
+                        // Can dest array be flat and contain oops?
+                        tklass->can_be_inline_array() && (!tklass->is_flat() || tklass->is_aryklassptr()->elem()->is_instklassptr()->instance_klass()->as_inline_klass()->contains_oops());
+    Node* not_objArray = exclude_flat ? generate_non_objArray_guard(klass_node, bailout) : generate_typeArray_guard(klass_node, bailout);
     if (not_objArray != nullptr) {
       // Improve the klass node's type from the new optimistic assumption:
       ciKlass* ak = ciArrayKlass::make(env()->Object_klass());
-      const Type* akls = TypeKlassPtr::make(TypePtr::NotNull, ak, 0/*offset*/);
+      const Type* akls = TypeKlassPtr::make(TypePtr::NotNull, ak, Type::Offset(0));
       Node* cast = new CastPPNode(control(), klass_node, akls);
       klass_node = _gvn.transform(cast);
     }
 
     // Bail out if either start or end is negative.
@@ -4332,10 +4592,42 @@
     // Without this the new_array would throw
     // NegativeArraySizeException but IllegalArgumentException is what
     // should be thrown
     generate_negative_guard(length, bailout, &length);
 
+    // Handle inline type arrays
+    bool can_validate = !too_many_traps(Deoptimization::Reason_class_check);
+    if (!stopped()) {
+      orig_t = _gvn.type(original)->isa_aryptr();
+      if (orig_t != nullptr && orig_t->is_flat()) {
+        // Src is flat, check that dest is flat as well
+        if (exclude_flat) {
+          // Dest can't be flat, bail out
+          bailout->add_req(control());
+          set_control(top());
+        } else {
+          generate_fair_guard(flat_array_test(klass_node, /* flat = */ false), bailout);
+        }
+      } else if (UseFlatArray && (orig_t == nullptr || !orig_t->is_not_flat()) &&
+                 // If dest is flat, src must be flat as well (guaranteed by src <: dest check if validated).
+                 ((!tklass->is_flat() && tklass->can_be_inline_array()) || !can_validate)) {
+        // Src might be flat and dest might not be flat. Go to the slow path if src is flat.
+        // TODO 8251971: Optimize for the case when src/dest are later found to be both flat.
+        generate_fair_guard(flat_array_test(load_object_klass(original)), bailout);
+        if (orig_t != nullptr) {
+          orig_t = orig_t->cast_to_not_flat();
+          original = _gvn.transform(new CheckCastPPNode(control(), original, orig_t));
+        }
+      }
+      if (!can_validate) {
+        // No validation. The subtype check emitted at macro expansion time will not go to the slow
+        // path but call checkcast_arraycopy which can not handle flat/null-free inline type arrays.
+        // TODO 8251971: Optimize for the case when src/dest are later found to be both flat/null-free.
+        generate_fair_guard(null_free_array_test(klass_node), bailout);
+      }
+    }
+
     if (bailout->req() > 1) {
       PreserveJVMState pjvms(this);
       set_control(_gvn.transform(bailout));
       uncommon_trap(Deoptimization::Reason_intrinsic,
                     Deoptimization::Action_maybe_recompile);
@@ -4374,11 +4666,11 @@
       }
 
       bool validated = false;
       // Reason_class_check rather than Reason_intrinsic because we
       // want to intrinsify even if this traps.
-      if (!too_many_traps(Deoptimization::Reason_class_check)) {
+      if (can_validate) {
         Node* not_subtype_ctrl = gen_subtype_check(original, klass_node);
 
         if (not_subtype_ctrl != top()) {
           PreserveJVMState pjvms(this);
           set_control(not_subtype_ctrl);
@@ -4460,15 +4752,15 @@
   guarantee(method_id == method->intrinsic_id(), "must match");
 
   const TypeFunc* tf = TypeFunc::make(method);
   if (res_not_null) {
     assert(tf->return_type() == T_OBJECT, "");
-    const TypeTuple* range = tf->range();
+    const TypeTuple* range = tf->range_cc();
     const Type** fields = TypeTuple::fields(range->cnt());
     fields[TypeFunc::Parms] = range->field_at(TypeFunc::Parms)->filter_speculative(TypePtr::NOTNULL);
     const TypeTuple* new_range = TypeTuple::make(range->cnt(), fields);
-    tf = TypeFunc::make(tf->domain(), new_range);
+    tf = TypeFunc::make(tf->domain_cc(), new_range);
   }
   CallJavaNode* slow_call;
   if (is_static) {
     assert(!is_virtual, "");
     slow_call = new CallStaticJavaNode(C, tf,
@@ -4521,21 +4813,25 @@
 
   RegionNode* result_reg = new RegionNode(PATH_LIMIT);
   PhiNode*    result_val = new PhiNode(result_reg, TypeInt::INT);
   PhiNode*    result_io  = new PhiNode(result_reg, Type::ABIO);
   PhiNode*    result_mem = new PhiNode(result_reg, Type::MEMORY, TypePtr::BOTTOM);
-  Node* obj = nullptr;
+  Node* obj = argument(0);
+
+  if (gvn().type(obj)->is_inlinetypeptr()) {
+    return false;
+  }
+
   if (!is_static) {
     // Check for hashing null object
     obj = null_check_receiver();
     if (stopped())  return true;        // unconditionally null
     result_reg->init_req(_null_path, top());
     result_val->init_req(_null_path, top());
   } else {
     // Do a null check, and return zero if null.
     // System.identityHashCode(null) == 0
-    obj = argument(0);
     Node* null_ctl = top();
     obj = null_check_oop(obj, &null_ctl);
     result_reg->init_req(_null_path, null_ctl);
     result_val->init_req(_null_path, _gvn.intcon(0));
   }
@@ -4571,11 +4867,12 @@
   // the null check after castPP removal.
   Node* no_ctrl = nullptr;
   Node* header = make_load(no_ctrl, header_addr, TypeX_X, TypeX_X->basic_type(), MemNode::unordered);
 
   // Test the header to see if it is safe to read w.r.t. locking.
-  Node *lock_mask      = _gvn.MakeConX(markWord::lock_mask_in_place);
+  // This also serves as guard against inline types
+  Node *lock_mask      = _gvn.MakeConX(markWord::inline_type_mask_in_place);
   Node *lmasked_header = _gvn.transform(new AndXNode(header, lock_mask));
   if (LockingMode == LM_LIGHTWEIGHT) {
     Node *monitor_val   = _gvn.MakeConX(markWord::monitor_value);
     Node *chk_monitor   = _gvn.transform(new CmpXNode(lmasked_header, monitor_val));
     Node *test_monitor  = _gvn.transform(new BoolNode(chk_monitor, BoolTest::eq));
@@ -4645,11 +4942,20 @@
 //---------------------------inline_native_getClass----------------------------
 // public final native Class<?> java.lang.Object.getClass();
 //
 // Build special case code for calls to getClass on an object.
 bool LibraryCallKit::inline_native_getClass() {
-  Node* obj = null_check_receiver();
+  Node* obj = argument(0);
+  if (obj->is_InlineType()) {
+    const Type* t = _gvn.type(obj);
+    if (t->maybe_null()) {
+      null_check(obj);
+    }
+    set_result(makecon(TypeInstPtr::make(t->inline_klass()->java_mirror())));
+    return true;
+  }
+  obj = null_check_receiver();
   if (stopped())  return true;
   set_result(load_mirror_from_klass(load_object_klass(obj)));
   return true;
 }
 
@@ -4946,10 +5252,25 @@
   return true;
 }
 
 #undef XTOP
 
+// TODO 8325106 Remove this and corresponding tests. Flatness is not a property of the Class anymore with JEP 401.
+//----------------------inline_unsafe_isFlatArray------------------------
+// public native boolean Unsafe.isFlatArray(Class<?> arrayClass);
+// This intrinsic exploits assumptions made by the native implementation
+// (arrayClass is neither null nor primitive) to avoid unnecessary null checks.
+bool LibraryCallKit::inline_unsafe_isFlatArray() {
+  Node* cls = argument(1);
+  Node* p = basic_plus_adr(cls, java_lang_Class::klass_offset());
+  Node* kls = _gvn.transform(LoadKlassNode::make(_gvn, nullptr, immutable_memory(), p,
+                                                 TypeRawPtr::BOTTOM, TypeInstKlassPtr::OBJECT));
+  Node* result = flat_array_test(kls);
+  set_result(result);
+  return true;
+}
+
 //------------------------clone_coping-----------------------------------
 // Helper function for inline_native_clone.
 void LibraryCallKit::copy_to_clone(Node* obj, Node* alloc_obj, Node* obj_size, bool is_array) {
   assert(obj_size != nullptr, "");
   Node* raw_obj = alloc_obj->in(1);
@@ -5010,21 +5331,23 @@
   // Set the reexecute bit for the interpreter to reexecute
   // the bytecode that invokes Object.clone if deoptimization happens.
   { PreserveReexecuteState preexecs(this);
     jvms()->set_should_reexecute(true);
 
-    Node* obj = null_check_receiver();
+    Node* obj = argument(0);
+    obj = null_check_receiver();
     if (stopped())  return true;
 
     const TypeOopPtr* obj_type = _gvn.type(obj)->is_oopptr();
 
     // If we are going to clone an instance, we need its exact type to
     // know the number and types of fields to convert the clone to
     // loads/stores. Maybe a speculative type can help us.
     if (!obj_type->klass_is_exact() &&
         obj_type->speculative_type() != nullptr &&
-        obj_type->speculative_type()->is_instance_klass()) {
+        obj_type->speculative_type()->is_instance_klass() &&
+        !obj_type->speculative_type()->is_inlinetype()) {
       ciInstanceKlass* spec_ik = obj_type->speculative_type()->as_instance_klass();
       if (spec_ik->nof_nonstatic_fields() <= ArrayCopyLoadStoreMaxElem &&
           !spec_ik->has_injected_fields()) {
         if (!obj_type->isa_instptr() ||
             obj_type->is_instptr()->instance_klass()->has_subklass()) {
@@ -5050,64 +5373,78 @@
     PhiNode*    result_i_o = new PhiNode(result_reg, Type::ABIO);
     PhiNode*    result_mem = new PhiNode(result_reg, Type::MEMORY, TypePtr::BOTTOM);
     record_for_igvn(result_reg);
 
     Node* obj_klass = load_object_klass(obj);
+    // We only go to the fast case code if we pass a number of guards.
+    // The paths which do not pass are accumulated in the slow_region.
+    RegionNode* slow_region = new RegionNode(1);
+    record_for_igvn(slow_region);
+
     Node* array_ctl = generate_array_guard(obj_klass, (RegionNode*)nullptr);
     if (array_ctl != nullptr) {
       // It's an array.
       PreserveJVMState pjvms(this);
       set_control(array_ctl);
-      Node* obj_length = load_array_length(obj);
-      Node* array_size = nullptr; // Size of the array without object alignment padding.
-      Node* alloc_obj = new_array(obj_klass, obj_length, 0, &array_size, /*deoptimize_on_exception=*/true);
 
       BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
-      if (bs->array_copy_requires_gc_barriers(true, T_OBJECT, true, false, BarrierSetC2::Parsing)) {
-        // If it is an oop array, it requires very special treatment,
-        // because gc barriers are required when accessing the array.
-        Node* is_obja = generate_objArray_guard(obj_klass, (RegionNode*)nullptr);
-        if (is_obja != nullptr) {
-          PreserveJVMState pjvms2(this);
-          set_control(is_obja);
-          // Generate a direct call to the right arraycopy function(s).
-          // Clones are always tightly coupled.
-          ArrayCopyNode* ac = ArrayCopyNode::make(this, true, obj, intcon(0), alloc_obj, intcon(0), obj_length, true, false);
-          ac->set_clone_oop_array();
-          Node* n = _gvn.transform(ac);
-          assert(n == ac, "cannot disappear");
-          ac->connect_outputs(this, /*deoptimize_on_exception=*/true);
-
-          result_reg->init_req(_objArray_path, control());
-          result_val->init_req(_objArray_path, alloc_obj);
-          result_i_o ->set_req(_objArray_path, i_o());
-          result_mem ->set_req(_objArray_path, reset_memory());
-        }
+      const TypeAryPtr* ary_ptr = obj_type->isa_aryptr();
+      if (UseFlatArray && bs->array_copy_requires_gc_barriers(true, T_OBJECT, true, false, BarrierSetC2::Expansion) &&
+          obj_type->can_be_inline_array() &&
+          (ary_ptr == nullptr || (!ary_ptr->is_not_flat() && (!ary_ptr->is_flat() || ary_ptr->elem()->inline_klass()->contains_oops())))) {
+        // Flat inline type array may have object field that would require a
+        // write barrier. Conservatively, go to slow path.
+        generate_fair_guard(flat_array_test(obj_klass), slow_region);
       }
-      // Otherwise, there are no barriers to worry about.
-      // (We can dispense with card marks if we know the allocation
-      //  comes out of eden (TLAB)...  In fact, ReduceInitialCardMarks
-      //  causes the non-eden paths to take compensating steps to
-      //  simulate a fresh allocation, so that no further
-      //  card marks are required in compiled code to initialize
-      //  the object.)
 
       if (!stopped()) {
-        copy_to_clone(obj, alloc_obj, array_size, true);
-
-        // Present the results of the copy.
-        result_reg->init_req(_array_path, control());
-        result_val->init_req(_array_path, alloc_obj);
-        result_i_o ->set_req(_array_path, i_o());
-        result_mem ->set_req(_array_path, reset_memory());
+        Node* obj_length = load_array_length(obj);
+        Node* array_size = nullptr; // Size of the array without object alignment padding.
+        Node* alloc_obj = new_array(obj_klass, obj_length, 0, &array_size, /*deoptimize_on_exception=*/true);
+
+        BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
+        if (bs->array_copy_requires_gc_barriers(true, T_OBJECT, true, false, BarrierSetC2::Parsing)) {
+          // If it is an oop array, it requires very special treatment,
+          // because gc barriers are required when accessing the array.
+          Node* is_obja = generate_objArray_guard(obj_klass, (RegionNode*)nullptr);
+          if (is_obja != nullptr) {
+            PreserveJVMState pjvms2(this);
+            set_control(is_obja);
+            // Generate a direct call to the right arraycopy function(s).
+            // Clones are always tightly coupled.
+            ArrayCopyNode* ac = ArrayCopyNode::make(this, true, obj, intcon(0), alloc_obj, intcon(0), obj_length, true, false);
+            ac->set_clone_oop_array();
+            Node* n = _gvn.transform(ac);
+            assert(n == ac, "cannot disappear");
+            ac->connect_outputs(this, /*deoptimize_on_exception=*/true);
+
+            result_reg->init_req(_objArray_path, control());
+            result_val->init_req(_objArray_path, alloc_obj);
+            result_i_o ->set_req(_objArray_path, i_o());
+            result_mem ->set_req(_objArray_path, reset_memory());
+          }
+        }
+        // Otherwise, there are no barriers to worry about.
+        // (We can dispense with card marks if we know the allocation
+        //  comes out of eden (TLAB)...  In fact, ReduceInitialCardMarks
+        //  causes the non-eden paths to take compensating steps to
+        //  simulate a fresh allocation, so that no further
+        //  card marks are required in compiled code to initialize
+        //  the object.)
+
+        if (!stopped()) {
+          copy_to_clone(obj, alloc_obj, array_size, true);
+
+          // Present the results of the copy.
+          result_reg->init_req(_array_path, control());
+          result_val->init_req(_array_path, alloc_obj);
+          result_i_o ->set_req(_array_path, i_o());
+          result_mem ->set_req(_array_path, reset_memory());
+        }
       }
     }
 
-    // We only go to the instance fast case code if we pass a number of guards.
-    // The paths which do not pass are accumulated in the slow_region.
-    RegionNode* slow_region = new RegionNode(1);
-    record_for_igvn(slow_region);
     if (!stopped()) {
       // It's an instance (we did array above).  Make the slow-path tests.
       // If this is a virtual call, we generate a funny guard.  We grab
       // the vtable entry corresponding to clone() from the target object.
       // If the target method which we are calling happens to be the
@@ -5235,16 +5572,28 @@
   SafePointNode* sfpt = new SafePointNode(size, old_jvms);
   old_jvms->set_map(sfpt);
   for (uint i = 0; i < size; i++) {
     sfpt->init_req(i, alloc->in(i));
   }
+  int adjustment = 1;
+  // TODO 8325106 why can't we check via the type of the const klass node?
+  if (alloc->is_null_free()) {
+    // A null-free, tightly coupled array allocation can only come from LibraryCallKit::inline_newNullRestrictedArray
+    // which requires both the component type and the array length on stack for re-execution. Re-create and push
+    // the component type.
+    ciArrayKlass* klass = alloc->in(AllocateNode::KlassNode)->bottom_type()->is_aryklassptr()->exact_klass()->as_array_klass();
+    ciInstance* instance = klass->component_mirror_instance();
+    const TypeInstPtr* t_instance = TypeInstPtr::make(instance);
+    sfpt->ins_req(old_jvms->stkoff() + old_jvms->sp(), makecon(t_instance));
+    adjustment++;
+  }
   // re-push array length for deoptimization
-  sfpt->ins_req(old_jvms->stkoff() + old_jvms->sp(), alloc->in(AllocateNode::ALength));
-  old_jvms->set_sp(old_jvms->sp()+1);
-  old_jvms->set_monoff(old_jvms->monoff()+1);
-  old_jvms->set_scloff(old_jvms->scloff()+1);
-  old_jvms->set_endoff(old_jvms->endoff()+1);
+  sfpt->ins_req(old_jvms->stkoff() + old_jvms->sp() + adjustment - 1, alloc->in(AllocateNode::ALength));
+  old_jvms->set_sp(old_jvms->sp() + adjustment);
+  old_jvms->set_monoff(old_jvms->monoff() + adjustment);
+  old_jvms->set_scloff(old_jvms->scloff() + adjustment);
+  old_jvms->set_endoff(old_jvms->endoff() + adjustment);
   old_jvms->set_should_reexecute(true);
 
   sfpt->set_i_o(map()->i_o());
   sfpt->set_memory(map()->memory());
   sfpt->set_control(map()->control());
@@ -5273,15 +5622,14 @@
     map()->replaced_nodes().apply(saved_jvms_before_guards->map(), new_idx);
     set_jvms(saved_jvms_before_guards);
     _reexecute_sp = saved_reexecute_sp;
 
     // Remove the allocation from above the guards
-    CallProjections callprojs;
-    alloc->extract_projections(&callprojs, true);
+    CallProjections* callprojs = alloc->extract_projections(true);
     InitializeNode* init = alloc->initialization();
     Node* alloc_mem = alloc->in(TypeFunc::Memory);
-    C->gvn_replace_by(callprojs.fallthrough_ioproj, alloc->in(TypeFunc::I_O));
+    C->gvn_replace_by(callprojs->fallthrough_ioproj, alloc->in(TypeFunc::I_O));
     C->gvn_replace_by(init->proj_out(TypeFunc::Memory), alloc_mem);
 
     // The CastIINode created in GraphKit::new_array (in AllocateArrayNode::make_ideal_length) must stay below
     // the allocation (i.e. is only valid if the allocation succeeds):
     // 1) replace CastIINode with AllocateArrayNode's length here
@@ -5319,11 +5667,11 @@
     alloc->set_req(TypeFunc::I_O, i_o());
     Node *mem = reset_memory();
     set_all_memory(mem);
     alloc->set_req(TypeFunc::Memory, mem);
     set_control(init->proj_out_or_null(TypeFunc::Control));
-    set_i_o(callprojs.fallthrough_ioproj);
+    set_i_o(callprojs->fallthrough_ioproj);
 
     // Update memory as done in GraphKit::set_output_for_allocation()
     const TypeInt* length_type = _gvn.find_int_type(alloc->in(AllocateNode::ALength));
     const TypeOopPtr* ary_type = _gvn.type(alloc->in(AllocateNode::KlassNode))->is_klassptr()->as_instance_type();
     if (ary_type->isa_aryptr() && length_type != nullptr) {
@@ -5629,11 +5977,11 @@
     BasicType src_elem = top_src->isa_aryptr()->elem()->array_element_basic_type();
     BasicType dest_elem = top_dest->isa_aryptr()->elem()->array_element_basic_type();
     if (is_reference_type(src_elem, true)) src_elem = T_OBJECT;
     if (is_reference_type(dest_elem, true)) dest_elem = T_OBJECT;
 
-    if (src_elem == dest_elem && src_elem == T_OBJECT) {
+    if (src_elem == dest_elem && top_src->is_flat() == top_dest->is_flat() && src_elem == T_OBJECT) {
       // If both arrays are object arrays then having the exact types
       // for both will remove the need for a subtype check at runtime
       // before the call and may make it possible to pick a faster copy
       // routine (without a subtype check on every element)
       // Do we have the exact type of src?
@@ -5656,13 +6004,17 @@
       }
       if (could_have_src && could_have_dest) {
         // If we can have both exact types, emit the missing guards
         if (could_have_src && !src_spec) {
           src = maybe_cast_profiled_obj(src, src_k, true);
+          src_type = _gvn.type(src);
+          top_src = src_type->isa_aryptr();
         }
         if (could_have_dest && !dest_spec) {
           dest = maybe_cast_profiled_obj(dest, dest_k, true);
+          dest_type = _gvn.type(dest);
+          top_dest = dest_type->isa_aryptr();
         }
       }
     }
   }
 
@@ -5674,12 +6026,11 @@
   }
 
   bool negative_length_guard_generated = false;
 
   if (!C->too_many_traps(trap_method, trap_bci, Deoptimization::Reason_intrinsic) &&
-      can_emit_guards &&
-      !src->is_top() && !dest->is_top()) {
+      can_emit_guards && !src->is_top() && !dest->is_top()) {
     // validate arguments: enables transformation the ArrayCopyNode
     validated = true;
 
     RegionNode* slow_region = new RegionNode(1);
     record_for_igvn(slow_region);
@@ -5718,30 +6069,51 @@
 
     // (9) each element of an oop array must be assignable
     Node* dest_klass = load_object_klass(dest);
     if (src != dest) {
       Node* not_subtype_ctrl = gen_subtype_check(src, dest_klass);
+      slow_region->add_req(not_subtype_ctrl);
+    }
 
-      if (not_subtype_ctrl != top()) {
-        PreserveJVMState pjvms(this);
-        set_control(not_subtype_ctrl);
-        uncommon_trap(Deoptimization::Reason_intrinsic,
-                      Deoptimization::Action_make_not_entrant);
-        assert(stopped(), "Should be stopped");
+    const TypeKlassPtr* dest_klass_t = _gvn.type(dest_klass)->is_klassptr();
+    const Type* toop = dest_klass_t->cast_to_exactness(false)->as_instance_type();
+    src = _gvn.transform(new CheckCastPPNode(control(), src, toop));
+    src_type = _gvn.type(src);
+    top_src  = src_type->isa_aryptr();
+
+    // Handle flat inline type arrays (null-free arrays are handled by the subtype check above)
+    if (!stopped() && UseFlatArray) {
+      // If dest is flat, src must be flat as well (guaranteed by src <: dest check). Handle flat src here.
+      assert(top_dest == nullptr || !top_dest->is_flat() || top_src->is_flat(), "src array must be flat");
+      if (top_src != nullptr && top_src->is_flat()) {
+        // Src is flat, check that dest is flat as well
+        if (top_dest != nullptr && !top_dest->is_flat()) {
+          generate_fair_guard(flat_array_test(dest_klass, /* flat = */ false), slow_region);
+          // Since dest is flat and src <: dest, dest must have the same type as src.
+          top_dest = top_src->cast_to_exactness(false);
+          assert(top_dest->is_flat(), "dest must be flat");
+          dest = _gvn.transform(new CheckCastPPNode(control(), dest, top_dest));
+        }
+      } else if (top_src == nullptr || !top_src->is_not_flat()) {
+        // Src might be flat and dest might not be flat. Go to the slow path if src is flat.
+        // TODO 8251971: Optimize for the case when src/dest are later found to be both flat.
+        assert(top_dest == nullptr || !top_dest->is_flat(), "dest array must not be flat");
+        generate_fair_guard(flat_array_test(src), slow_region);
+        if (top_src != nullptr) {
+          top_src = top_src->cast_to_not_flat();
+          src = _gvn.transform(new CheckCastPPNode(control(), src, top_src));
+        }
       }
     }
+
     {
       PreserveJVMState pjvms(this);
       set_control(_gvn.transform(slow_region));
       uncommon_trap(Deoptimization::Reason_intrinsic,
                     Deoptimization::Action_make_not_entrant);
       assert(stopped(), "Should be stopped");
     }
-
-    const TypeKlassPtr* dest_klass_t = _gvn.type(dest_klass)->is_klassptr();
-    const Type *toop = dest_klass_t->cast_to_exactness(false)->as_instance_type();
-    src = _gvn.transform(new CheckCastPPNode(control(), src, toop));
     arraycopy_move_allocation_here(alloc, dest, saved_jvms_before_guards, saved_reexecute_sp, new_idx);
   }
 
   if (stopped()) {
     return true;
diff a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp
--- a/src/hotspot/share/opto/library_call.hpp
+++ b/src/hotspot/share/opto/library_call.hpp
@@ -26,10 +26,11 @@
 #include "classfile/javaClasses.hpp"
 #include "opto/callGenerator.hpp"
 #include "opto/graphKit.hpp"
 #include "opto/castnode.hpp"
 #include "opto/convertnode.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/intrinsicnode.hpp"
 #include "opto/movenode.hpp"
 
 class LibraryIntrinsic : public InlineCallGenerator {
   // Extend the set of intrinsics known to the runtime:
@@ -103,13 +104,21 @@
   bool  try_to_inline(int predicate);
   Node* try_to_predicate(int predicate);
 
   void push_result() {
     // Push the result onto the stack.
-    if (!stopped() && result() != nullptr) {
-      BasicType bt = result()->bottom_type()->basic_type();
-      push_node(bt, result());
+    Node* res = result();
+    if (!stopped() && res != nullptr) {
+      BasicType bt = res->bottom_type()->basic_type();
+      if (C->inlining_incrementally() && res->is_InlineType()) {
+        // The caller expects an oop when incrementally inlining an intrinsic that returns an
+        // inline type. Make sure the call is re-executed if the allocation triggers a deoptimization.
+        PreserveReexecuteState preexecs(this);
+        jvms()->set_should_reexecute(true);
+        res = res->as_InlineType()->buffer(this);
+      }
+      push_node(bt, res);
     }
   }
 
  private:
   void fatal_unexpected_iid(vmIntrinsics::ID iid) {
@@ -136,11 +145,10 @@
                                     Node* length, bool char_count);
   Node* current_thread_helper(Node* &tls_output, ByteSize handle_offset,
                               bool is_immutable);
   Node* generate_current_thread(Node* &tls_output);
   Node* generate_virtual_thread(Node* threadObj);
-  Node* load_mirror_from_klass(Node* klass);
   Node* load_klass_from_mirror_common(Node* mirror, bool never_see_null,
                                       RegionNode* region, int null_path,
                                       int offset);
   Node* load_klass_from_mirror(Node* mirror, bool never_see_null,
                                RegionNode* region, int null_path) {
@@ -158,25 +166,37 @@
   }
   Node* generate_access_flags_guard(Node* kls,
                                     int modifier_mask, int modifier_bits,
                                     RegionNode* region);
   Node* generate_interface_guard(Node* kls, RegionNode* region);
+
+  enum ArrayKind {
+    AnyArray,
+    NonArray,
+    ObjectArray,
+    NonObjectArray,
+    TypeArray
+  };
+
   Node* generate_hidden_class_guard(Node* kls, RegionNode* region);
+
   Node* generate_array_guard(Node* kls, RegionNode* region) {
-    return generate_array_guard_common(kls, region, false, false);
+    return generate_array_guard_common(kls, region, AnyArray);
   }
   Node* generate_non_array_guard(Node* kls, RegionNode* region) {
-    return generate_array_guard_common(kls, region, false, true);
+    return generate_array_guard_common(kls, region, NonArray);
   }
   Node* generate_objArray_guard(Node* kls, RegionNode* region) {
-    return generate_array_guard_common(kls, region, true, false);
+    return generate_array_guard_common(kls, region, ObjectArray);
   }
   Node* generate_non_objArray_guard(Node* kls, RegionNode* region) {
-    return generate_array_guard_common(kls, region, true, true);
+    return generate_array_guard_common(kls, region, NonObjectArray);
+  }
+  Node* generate_typeArray_guard(Node* kls, RegionNode* region) {
+    return generate_array_guard_common(kls, region, TypeArray);
   }
-  Node* generate_array_guard_common(Node* kls, RegionNode* region,
-                                    bool obj_array, bool not_array);
+  Node* generate_array_guard_common(Node* kls, RegionNode* region, ArrayKind kind);
   Node* generate_virtual_guard(Node* obj_klass, RegionNode* slow_region);
   CallJavaNode* generate_method_call(vmIntrinsicID method_id, bool is_virtual, bool is_static, bool res_not_null);
   CallJavaNode* generate_method_call_static(vmIntrinsicID method_id, bool res_not_null) {
     return generate_method_call(method_id, false, true, res_not_null);
   }
@@ -222,17 +242,21 @@
   int classify_unsafe_addr(Node* &base, Node* &offset, BasicType type);
   Node* make_unsafe_address(Node*& base, Node* offset, BasicType type = T_ILLEGAL, bool can_cast = false);
 
   typedef enum { Relaxed, Opaque, Volatile, Acquire, Release } AccessKind;
   DecoratorSet mo_decorator_for_access_kind(AccessKind kind);
-  bool inline_unsafe_access(bool is_store, BasicType type, AccessKind kind, bool is_unaligned);
+  bool inline_unsafe_access(bool is_store, BasicType type, AccessKind kind, bool is_unaligned, bool is_flat = false);
   static bool klass_needs_init_guard(Node* kls);
   bool inline_unsafe_allocate();
   bool inline_unsafe_newArray(bool uninitialized);
+  bool inline_newNullRestrictedArray();
   bool inline_unsafe_writeback0();
   bool inline_unsafe_writebackSync0(bool is_pre);
   bool inline_unsafe_copyMemory();
+  bool inline_unsafe_isFlatArray();
+  bool inline_unsafe_make_private_buffer();
+  bool inline_unsafe_finish_private_buffer();
 
   bool inline_native_currentCarrierThread();
   bool inline_native_currentThread();
   bool inline_native_setCurrentThread();
 
@@ -253,10 +277,11 @@
   bool inline_native_getEventWriter();
   bool inline_native_jvm_commit();
   void extend_setCurrentThread(Node* jt, Node* thread);
 #endif
   bool inline_native_Class_query(vmIntrinsics::ID id);
+  bool inline_primitive_Class_conversion(vmIntrinsics::ID id);
   bool inline_native_subtype_check();
   bool inline_native_getLength();
   bool inline_array_copyOf(bool is_copyOfRange);
   bool inline_array_equals(StrIntrinsicNode::ArgEnc ae);
   bool inline_preconditions_checkIndex(BasicType bt);
diff a/src/hotspot/share/opto/locknode.cpp b/src/hotspot/share/opto/locknode.cpp
--- a/src/hotspot/share/opto/locknode.cpp
+++ b/src/hotspot/share/opto/locknode.cpp
@@ -147,10 +147,22 @@
 //------------------------------cmp--------------------------------------------
 bool FastLockNode::cmp( const Node &n ) const {
   return (&n == this);                // Always fail except on self
 }
 
+const Type* FastLockNode::Value(PhaseGVN* phase) const {
+  const Type* in1_t = phase->type(in(1));
+  if (in1_t == Type::TOP) {
+    return Type::TOP;
+  }
+  if (in1_t->is_inlinetypeptr()) {
+    // Locking on inline types always fails
+    return TypeInt::CC_GT;
+  }
+  return TypeInt::CC;
+}
+
 //=============================================================================
 //-----------------------------hash--------------------------------------------
 uint FastUnlockNode::hash() const { return NO_HASH; }
 
 //------------------------------cmp--------------------------------------------
@@ -177,12 +189,20 @@
 //=============================================================================
 //------------------------------do_monitor_enter-------------------------------
 void Parse::do_monitor_enter() {
   kill_dead_locals();
 
+  Node* obj = peek();
+  const Type* obj_type = gvn().type(obj);
+  if (obj_type->is_inlinetypeptr()) {
+    uncommon_trap(Deoptimization::Reason_class_check,
+                  Deoptimization::Action_none);
+    return;
+  }
+
   // Null check; get casted pointer.
-  Node* obj = null_check(peek());
+  obj = null_check(obj);
   // Check for locking null object
   if (stopped()) return;
 
   // the monitor object is not part of debug info expression stack
   pop();
diff a/src/hotspot/share/opto/locknode.hpp b/src/hotspot/share/opto/locknode.hpp
--- a/src/hotspot/share/opto/locknode.hpp
+++ b/src/hotspot/share/opto/locknode.hpp
@@ -91,11 +91,11 @@
   // LockNode/UnLockNode to avoid creating Phi's.
   virtual uint hash() const ;                  // { return NO_HASH; }
   virtual uint size_of() const;
   virtual bool cmp( const Node &n ) const ;    // Always fail, except on self
   virtual int Opcode() const;
-  virtual const Type* Value(PhaseGVN* phase) const { return TypeInt::CC; }
+  virtual const Type* Value(PhaseGVN* phase) const;
   const Type *sub(const Type *t1, const Type *t2) const { return TypeInt::CC;}
 
   void create_rtm_lock_counter(JVMState* state);
   RTMLockingCounters*       rtm_counters() const { return _rtm_counters; }
   RTMLockingCounters* stack_rtm_counters() const { return _stack_rtm_counters; }
diff a/src/hotspot/share/opto/loopUnswitch.cpp b/src/hotspot/share/opto/loopUnswitch.cpp
--- a/src/hotspot/share/opto/loopUnswitch.cpp
+++ b/src/hotspot/share/opto/loopUnswitch.cpp
@@ -22,10 +22,12 @@
  *
  */
 
 #include "precompiled.hpp"
 #include "memory/allocation.inline.hpp"
+#include "opto/mulnode.hpp"
+#include "opto/addnode.hpp"
 #include "opto/connode.hpp"
 #include "opto/convertnode.hpp"
 #include "opto/loopnode.hpp"
 #include "opto/opaquenode.hpp"
 #include "opto/predicates.hpp"
@@ -50,10 +52,11 @@
 //                                 endloop
 //                               endif
 //
 // Note: the "else" clause may be empty
 
+
 //------------------------------policy_unswitching-----------------------------
 // Return TRUE or FALSE if the loop should be unswitched
 // (ie. clone loop with an invariant test that does not exit the loop)
 bool IdealLoopTree::policy_unswitching( PhaseIdealLoop *phase ) const {
   if (!LoopUnswitching) {
@@ -73,21 +76,27 @@
 
   LoopNode* head = _head->as_Loop();
   if (head->unswitch_count() + 1 > head->unswitch_max()) {
     return false;
   }
-  if (phase->find_unswitching_candidate(this) == nullptr) {
+
+  if (head->is_flat_arrays()) {
+    return false;
+  }
+
+  Node_List unswitch_iffs;
+  if (phase->find_unswitching_candidate(this, unswitch_iffs) == nullptr) {
     return false;
   }
 
   // Too speculative if running low on nodes.
   return phase->may_require_nodes(est_loop_clone_sz(2));
 }
 
 //------------------------------find_unswitching_candidate-----------------------------
 // Find candidate "if" for unswitching
-IfNode* PhaseIdealLoop::find_unswitching_candidate(const IdealLoopTree *loop) const {
+IfNode* PhaseIdealLoop::find_unswitching_candidate(const IdealLoopTree *loop, Node_List& unswitch_iffs) const {
 
   // Find first invariant test that doesn't exit the loop
   LoopNode *head = loop->_head->as_Loop();
   IfNode* unswitch_iff = nullptr;
   Node* n = head->in(LoopNode::LoopBackControl);
@@ -108,10 +117,28 @@
         }
       }
     }
     n = n_dom;
   }
+  if (unswitch_iff != nullptr) {
+    unswitch_iffs.push(unswitch_iff);
+  }
+
+  // Collect all non-flat array checks for unswitching to create a fast loop
+  // without checks (only non-flat array accesses) and a slow loop with checks.
+  if (unswitch_iff == nullptr || unswitch_iff->is_flat_array_check(&_igvn)) {
+    for (uint i = 0; i < loop->_body.size(); i++) {
+      IfNode* n = loop->_body.at(i)->isa_If();
+      if (n != nullptr && n != unswitch_iff && n->is_flat_array_check(&_igvn) &&
+          loop->is_invariant(n->in(1)) && !loop->is_loop_exit(n)) {
+        unswitch_iffs.push(n);
+        if (unswitch_iff == nullptr) {
+          unswitch_iff = n;
+        }
+      }
+    }
+  }
   return unswitch_iff;
 }
 
 //------------------------------do_unswitching-----------------------------
 // Clone loop with an invariant test (that does not exit) and
@@ -122,71 +149,88 @@
   if (has_control_dependencies_from_predicates(head)) {
     return;
   }
 
   // Find first invariant test that doesn't exit the loop
-  IfNode* unswitch_iff = find_unswitching_candidate((const IdealLoopTree *)loop);
-  assert(unswitch_iff != nullptr, "should be at least one");
+  Node_List unswitch_iffs;
+  IfNode* unswitch_iff = find_unswitching_candidate((const IdealLoopTree *)loop, unswitch_iffs);
+  assert(unswitch_iff != nullptr && unswitch_iff == unswitch_iffs.at(0), "should be at least one");
+  bool flat_array_checks = unswitch_iffs.size() > 1;
 
 #ifndef PRODUCT
   if (TraceLoopOpts) {
     tty->print("Unswitch   %d ", head->unswitch_count()+1);
     loop->dump_head();
+    for (uint i = 0; i < unswitch_iffs.size(); i++) {
+      unswitch_iffs.at(i)->dump(3);
+      tty->cr();
+    }
   }
 #endif
 
   C->print_method(PHASE_BEFORE_LOOP_UNSWITCHING, 4, head);
 
   // Need to revert back to normal loop
   if (head->is_CountedLoop() && !head->as_CountedLoop()->is_normal_loop()) {
     head->as_CountedLoop()->set_normal_loop();
   }
 
-  IfNode* invar_iff = create_slow_version_of_loop(loop, old_new, unswitch_iff, CloneIncludesStripMined);
+  IfNode* invar_iff = create_slow_version_of_loop(loop, old_new, unswitch_iffs, CloneIncludesStripMined);
   ProjNode* proj_true = invar_iff->proj_out(1);
   verify_fast_loop(head, proj_true);
 
   // Increment unswitch count
   LoopNode* head_clone = old_new[head->_idx]->as_Loop();
   int nct = head->unswitch_count() + 1;
   head->set_unswitch_count(nct);
   head_clone->set_unswitch_count(nct);
 
-  // Hoist invariant casts out of each loop to the appropriate
-  // control projection.
-
+  // Hoist invariant casts out of each loop to the appropriate control projection.
   Node_List worklist;
-  for (DUIterator_Fast imax, i = unswitch_iff->fast_outs(imax); i < imax; i++) {
-    ProjNode* proj= unswitch_iff->fast_out(i)->as_Proj();
-    // Copy to a worklist for easier manipulation
-    for (DUIterator_Fast jmax, j = proj->fast_outs(jmax); j < jmax; j++) {
-      Node* use = proj->fast_out(j);
-      if (use->Opcode() == Op_CheckCastPP && loop->is_invariant(use->in(1))) {
-        worklist.push(use);
+  for (uint i = 0; i < unswitch_iffs.size(); i++) {
+    IfNode* iff = unswitch_iffs.at(i)->as_If();
+    for (DUIterator_Fast imax, i = iff->fast_outs(imax); i < imax; i++) {
+      ProjNode* proj = iff->fast_out(i)->as_Proj();
+      // Copy to a worklist for easier manipulation
+      for (DUIterator_Fast jmax, j = proj->fast_outs(jmax); j < jmax; j++) {
+        Node* use = proj->fast_out(j);
+        if (use->Opcode() == Op_CheckCastPP && loop->is_invariant(use->in(1))) {
+          worklist.push(use);
+        }
+      }
+      ProjNode* invar_proj = invar_iff->proj_out(proj->_con)->as_Proj();
+      while (worklist.size() > 0) {
+        Node* use = worklist.pop();
+        Node* nuse = use->clone();
+        nuse->set_req(0, invar_proj);
+        _igvn.replace_input_of(use, 1, nuse);
+        register_new_node(nuse, invar_proj);
+        // Same for the clone if we are removing checks from the slow loop
+        if (!flat_array_checks) {
+          Node* use_clone = old_new[use->_idx];
+          _igvn.replace_input_of(use_clone, 1, nuse);
+        }
       }
-    }
-    ProjNode* invar_proj = invar_iff->proj_out(proj->_con)->as_Proj();
-    while (worklist.size() > 0) {
-      Node* use = worklist.pop();
-      Node* nuse = use->clone();
-      nuse->set_req(0, invar_proj);
-      _igvn.replace_input_of(use, 1, nuse);
-      register_new_node(nuse, invar_proj);
-      // Same for the clone
-      Node* use_clone = old_new[use->_idx];
-      _igvn.replace_input_of(use_clone, 1, nuse);
     }
   }
 
   // Hardwire the control paths in the loops into if(true) and if(false)
-  _igvn.rehash_node_delayed(unswitch_iff);
-  dominated_by(proj_true->as_IfProj(), unswitch_iff);
-
+  for (uint i = 0; i < unswitch_iffs.size(); i++) {
+    IfNode* iff = unswitch_iffs.at(i)->as_If();
+    _igvn.rehash_node_delayed(iff);
+    dominated_by(proj_true->as_IfProj(), iff);
+  }
   IfNode* unswitch_iff_clone = old_new[unswitch_iff->_idx]->as_If();
-  _igvn.rehash_node_delayed(unswitch_iff_clone);
-  ProjNode* proj_false = invar_iff->proj_out(0);
-  dominated_by(proj_false->as_IfProj(), unswitch_iff_clone);
+  if (!flat_array_checks) {
+    ProjNode* proj_false = invar_iff->proj_out(0)->as_Proj();
+    _igvn.rehash_node_delayed(unswitch_iff_clone);
+    dominated_by(proj_false->as_IfProj(), unswitch_iff_clone);
+  } else {
+    // Leave the flat array checks in the slow loop and
+    // prevent it from being unswitched again based on these checks.
+    head_clone->mark_flat_arrays();
+  }
 
   // Reoptimize loops
   loop->record_for_igvn();
   for(int i = loop->_body.size() - 1; i >= 0 ; i--) {
     Node *n = loop->_body[i];
@@ -194,13 +238,15 @@
     _igvn._worklist.push(n_clone);
   }
 
 #ifndef PRODUCT
   if (TraceLoopUnswitching) {
-    tty->print_cr("Loop unswitching orig: %d @ %d  new: %d @ %d",
-                  head->_idx,                unswitch_iff->_idx,
-                  old_new[head->_idx]->_idx, unswitch_iff_clone->_idx);
+    for (uint i = 0; i < unswitch_iffs.size(); i++) {
+      tty->print_cr("Loop unswitching orig: %d @ %d  new: %d @ %d",
+                    head->_idx,                unswitch_iffs.at(i)->_idx,
+                    old_new[head->_idx]->_idx, old_new[unswitch_iffs.at(i)->_idx]->_idx);
+    }
   }
 #endif
 
   C->print_method(PHASE_AFTER_LOOP_UNSWITCHING, 4, head_clone);
 
@@ -225,24 +271,50 @@
 //-------------------------create_slow_version_of_loop------------------------
 // Create a slow version of the loop by cloning the loop
 // and inserting an if to select fast-slow versions.
 // Return the inserted if.
 IfNode* PhaseIdealLoop::create_slow_version_of_loop(IdealLoopTree *loop,
-                                                      Node_List &old_new,
-                                                      IfNode* unswitch_iff,
-                                                      CloneLoopMode mode) {
+                                                    Node_List &old_new,
+                                                    Node_List &unswitch_iffs,
+                                                    CloneLoopMode mode) {
   LoopNode* head  = loop->_head->as_Loop();
+  bool counted_loop = head->is_CountedLoop();
   Node*     entry = head->skip_strip_mined()->in(LoopNode::EntryControl);
   _igvn.rehash_node_delayed(entry);
   IdealLoopTree* outer_loop = loop->_parent;
 
   head->verify_strip_mined(1);
 
   // Add test to new "if" outside of loop
-  Node *bol   = unswitch_iff->in(1)->as_Bool();
+  IfNode* unswitch_iff = unswitch_iffs.at(0)->as_If();
+  BoolNode* bol = unswitch_iff->in(1)->as_Bool();
+  if (unswitch_iffs.size() > 1) {
+    // Flat array checks are used on array access to switch between
+    // a legacy object array access and a flat inline type array
+    // access. We want the performance impact on legacy accesses to be
+    // as small as possible so we make two copies of the loop: a fast
+    // one where all accesses are known to be legacy, a slow one where
+    // some accesses are to flat arrays. Flat array checks
+    // can be removed from the fast loop (true proj) but not from the
+    // slow loop (false proj) as it can have a mix of flat/legacy accesses.
+    assert(bol->_test._test == BoolTest::ne, "IfTrue proj must point to flat array");
+    bol = bol->clone()->as_Bool();
+    register_new_node(bol, entry);
+    FlatArrayCheckNode* cmp = bol->in(1)->clone()->as_FlatArrayCheck();
+    register_new_node(cmp, entry);
+    bol->set_req(1, cmp);
+    // Combine all checks into a single one that fails if one array is a flat array
+    assert(cmp->req() == 3, "unexpected number of inputs for FlatArrayCheck");
+    cmp->add_req_batch(C->top(), unswitch_iffs.size() - 1);
+    for (uint i = 0; i < unswitch_iffs.size(); i++) {
+      Node* array = unswitch_iffs.at(i)->in(1)->in(1)->in(FlatArrayCheckNode::ArrayOrKlass);
+      cmp->set_req(FlatArrayCheckNode::ArrayOrKlass + i, array);
+    }
+  }
+
   IfNode* iff = (unswitch_iff->Opcode() == Op_RangeCheck) ? new RangeCheckNode(entry, bol, unswitch_iff->_prob, unswitch_iff->_fcnt) :
-    new IfNode(entry, bol, unswitch_iff->_prob, unswitch_iff->_fcnt);
+      new IfNode(entry, bol, unswitch_iff->_prob, unswitch_iff->_fcnt);
   register_node(iff, outer_loop, entry, dom_depth(entry));
   IfProjNode* iffast = new IfTrueNode(iff);
   register_node(iffast, outer_loop, iff, dom_depth(iff));
   IfProjNode* ifslow = new IfFalseNode(iff);
   register_node(ifslow, outer_loop, iff, dom_depth(iff));
diff a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp
--- a/src/hotspot/share/opto/loopnode.hpp
+++ b/src/hotspot/share/opto/loopnode.hpp
@@ -74,11 +74,12 @@
          HasAtomicPostLoop     = 1<<11,
          StripMined            = 1<<12,
          SubwordLoop           = 1<<13,
          ProfileTripFailed     = 1<<14,
          LoopNestInnerLoop     = 1<<15,
-         LoopNestLongOuterLoop = 1<<16 };
+         LoopNestLongOuterLoop = 1<<16,
+         FlatArrays            = 1<<17};
   char _unswitch_count;
   enum { _unswitch_max=3 };
 
   // Expected trip count from profile data
   float _profile_trip_cnt;
@@ -97,10 +98,11 @@
   bool is_strip_mined() const { return _loop_flags & StripMined; }
   bool is_profile_trip_failed() const { return _loop_flags & ProfileTripFailed; }
   bool is_subword_loop() const { return _loop_flags & SubwordLoop; }
   bool is_loop_nest_inner_loop() const { return _loop_flags & LoopNestInnerLoop; }
   bool is_loop_nest_outer_loop() const { return _loop_flags & LoopNestLongOuterLoop; }
+  bool is_flat_arrays() const { return _loop_flags & FlatArrays; }
 
   void mark_partial_peel_failed() { _loop_flags |= PartialPeelFailed; }
   void mark_was_slp() { _loop_flags |= WasSlpAnalyzed; }
   void mark_passed_slp() { _loop_flags |= PassedSlpAnalysis; }
   void mark_do_unroll_only() { _loop_flags |= DoUnrollOnly; }
@@ -110,10 +112,11 @@
   void clear_strip_mined() { _loop_flags &= ~StripMined; }
   void mark_profile_trip_failed() { _loop_flags |= ProfileTripFailed; }
   void mark_subword_loop() { _loop_flags |= SubwordLoop; }
   void mark_loop_nest_inner_loop() { _loop_flags |= LoopNestInnerLoop; }
   void mark_loop_nest_outer_loop() { _loop_flags |= LoopNestLongOuterLoop; }
+  void mark_flat_arrays() { _loop_flags |= FlatArrays; }
 
   int unswitch_max() { return _unswitch_max; }
   int unswitch_count() { return _unswitch_count; }
 
   void set_unswitch_count(int val) {
@@ -1409,21 +1412,21 @@
 
   // Create a slow version of the loop by cloning the loop
   // and inserting an if to select fast-slow versions.
   // Return the inserted if.
   IfNode* create_slow_version_of_loop(IdealLoopTree *loop,
-                                        Node_List &old_new,
-                                        IfNode* unswitch_iff,
-                                        CloneLoopMode mode);
+                                      Node_List &old_new,
+                                      Node_List &unswitch_iffs,
+                                      CloneLoopMode mode);
 
   // Clone loop with an invariant test (that does not exit) and
   // insert a clone of the test that selects which version to
   // execute.
   void do_unswitching (IdealLoopTree *loop, Node_List &old_new);
 
   // Find candidate "if" for unswitching
-  IfNode* find_unswitching_candidate(const IdealLoopTree *loop) const;
+  IfNode* find_unswitching_candidate(const IdealLoopTree *loop, Node_List& unswitch_iffs) const;
 
   // Range Check Elimination uses this function!
   // Constrain the main loop iterations so the affine function:
   //    low_limit <= scale_con * I + offset  <  upper_limit
   // always holds true.  That is, either increase the number of iterations in
@@ -1539,11 +1542,13 @@
   bool split_up( Node *n, Node *blk1, Node *blk2 );
 
   Node* place_outside_loop(Node* useblock, IdealLoopTree* loop) const;
   Node* try_move_store_before_loop(Node* n, Node *n_ctrl);
   void try_move_store_after_loop(Node* n);
+  void move_flat_array_check_out_of_loop(Node* n);
   bool identical_backtoback_ifs(Node *n);
+  bool flat_array_element_type_check(Node *n);
   bool can_split_if(Node *n_ctrl);
   bool cannot_split_division(const Node* n, const Node* region) const;
   static bool is_divisor_counted_loop_phi(const Node* divisor, const Node* loop);
   bool loop_phi_backedge_type_contains_zero(const Node* phi_divisor, const Type* zero) const;
 
diff a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp
--- a/src/hotspot/share/opto/loopopts.cpp
+++ b/src/hotspot/share/opto/loopopts.cpp
@@ -31,10 +31,11 @@
 #include "opto/callnode.hpp"
 #include "opto/castnode.hpp"
 #include "opto/connode.hpp"
 #include "opto/castnode.hpp"
 #include "opto/divnode.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/loopnode.hpp"
 #include "opto/matcher.hpp"
 #include "opto/mulnode.hpp"
 #include "opto/movenode.hpp"
 #include "opto/opaquenode.hpp"
@@ -60,10 +61,16 @@
   if (n->Opcode() == Op_CastII && region->is_CountedLoop() &&
       n->in(1) == region->as_CountedLoop()->phi()) {
     return nullptr;
   }
 
+  // Inline types should not be split through Phis because they cannot be merged
+  // through Phi nodes but each value input needs to be merged individually.
+  if (n->is_InlineType()) {
+    return nullptr;
+  }
+
   if (cannot_split_division(n, region)) {
     return nullptr;
   }
 
   int wins = 0;
@@ -749,10 +756,14 @@
     }
     // Add in cost any speculative ops
     for (uint j = 1; j < region->req(); j++) {
       Node *proj = region->in(j);
       Node *inp = phi->in(j);
+      if (inp->isa_InlineType()) {
+        // TODO 8302217 This prevents PhiNode::push_inline_types_through
+        return nullptr;
+      }
       if (get_ctrl(inp) == proj) { // Found local op
         cost++;
         // Check for a chain of dependent ops; these will all become
         // speculative in a CMOV.
         for (uint k = 1; k < inp->req(); k++)
@@ -1067,10 +1078,59 @@
       }
     }
   }
 }
 
+// If UseArrayMarkWordCheck is enabled, we can't use immutable memory for the flat array check
+// because we are loading the mark word which is mutable. Although the bits we are interested in
+// are immutable (we check for markWord::unlocked_value), we need to use raw memory to not break
+// anti dependency analysis. Below code will attempt to still move flat array checks out of loops,
+// mainly to enable loop unswitching.
+void PhaseIdealLoop::move_flat_array_check_out_of_loop(Node* n) {
+  // Skip checks for more than one array
+  if (n->req() > 3) {
+    return;
+  }
+  Node* mem = n->in(FlatArrayCheckNode::Memory);
+  Node* array = n->in(FlatArrayCheckNode::ArrayOrKlass)->uncast();
+  IdealLoopTree* check_loop = get_loop(get_ctrl(n));
+  IdealLoopTree* ary_loop = get_loop(get_ctrl(array));
+
+  // Check if array is loop invariant
+  if (!check_loop->is_member(ary_loop)) {
+    // Walk up memory graph from the check until we leave the loop
+    VectorSet wq;
+    wq.set(mem->_idx);
+    while (check_loop->is_member(get_loop(ctrl_or_self(mem)))) {
+      if (mem->is_Phi()) {
+        mem = mem->in(1);
+      } else if (mem->is_MergeMem()) {
+        mem = mem->as_MergeMem()->memory_at(Compile::AliasIdxRaw);
+      } else if (mem->is_Proj()) {
+        mem = mem->in(0);
+      } else if (mem->is_MemBar() || mem->is_SafePoint()) {
+        mem = mem->in(TypeFunc::Memory);
+      } else if (mem->is_Store() || mem->is_LoadStore() || mem->is_ClearArray()) {
+        mem = mem->in(MemNode::Memory);
+      } else {
+#ifdef ASSERT
+        mem->dump();
+#endif
+        ShouldNotReachHere();
+      }
+      if (wq.test_set(mem->_idx)) {
+        return;
+      }
+    }
+    // Replace memory input and re-compute ctrl to move the check out of the loop
+    _igvn.replace_input_of(n, 1, mem);
+    set_ctrl_and_loop(n, get_early_ctrl(n));
+    Node* bol = n->unique_out();
+    set_ctrl_and_loop(bol, get_early_ctrl(bol));
+  }
+}
+
 //------------------------------split_if_with_blocks_pre-----------------------
 // Do the real work in a non-recursive function.  Data nodes want to be
 // cloned in the pre-order so they can feed each other nicely.
 Node *PhaseIdealLoop::split_if_with_blocks_pre( Node *n ) {
   // Cloning these guys is unlikely to win
@@ -1079,10 +1139,16 @@
     return n;
   }
   if (n->is_Proj()) {
     return n;
   }
+
+  if (UseArrayMarkWordCheck && n->isa_FlatArrayCheck()) {
+    move_flat_array_check_out_of_loop(n);
+    return n;
+  }
+
   // Do not clone-up CmpFXXX variations, as these are always
   // followed by a CmpI
   if (n->is_Cmp()) {
     return n;
   }
@@ -1356,16 +1422,118 @@
   }
 
   return out_le;
 }
 
+bool PhaseIdealLoop::flat_array_element_type_check(Node *n) {
+  // If the CmpP is a subtype check for a value that has just been
+  // loaded from an array, the subtype check guarantees the value
+  // can't be stored in a flat array and the load of the value
+  // happens with a flat array check then: push the type check
+  // through the phi of the flat array check. This needs special
+  // logic because the subtype check's input is not a phi but a
+  // LoadKlass that must first be cloned through the phi.
+  if (n->Opcode() != Op_CmpP) {
+    return false;
+  }
+
+  Node* klassptr = n->in(1);
+  Node* klasscon = n->in(2);
+
+  if (klassptr->is_DecodeNarrowPtr()) {
+    klassptr = klassptr->in(1);
+  }
+
+  if (klassptr->Opcode() != Op_LoadKlass && klassptr->Opcode() != Op_LoadNKlass) {
+    return false;
+  }
+
+  if (!klasscon->is_Con()) {
+    return false;
+  }
+
+  Node* addr = klassptr->in(MemNode::Address);
+
+  if (!addr->is_AddP()) {
+    return false;
+  }
+
+  intptr_t offset;
+  Node* obj = AddPNode::Ideal_base_and_offset(addr, &_igvn, offset);
+
+  if (obj == nullptr) {
+    return false;
+  }
+
+  assert(obj != nullptr && addr->in(AddPNode::Base) == addr->in(AddPNode::Address), "malformed AddP?");
+  if (obj->Opcode() == Op_CastPP) {
+    obj = obj->in(1);
+  }
+
+  if (!obj->is_Phi()) {
+    return false;
+  }
+
+  Node* region = obj->in(0);
+
+  Node* phi = PhiNode::make_blank(region, n->in(1));
+  for (uint i = 1; i < region->req(); i++) {
+    Node* in = obj->in(i);
+    Node* ctrl = region->in(i);
+    if (addr->in(AddPNode::Base) != obj) {
+      Node* cast = addr->in(AddPNode::Base);
+      assert(cast->Opcode() == Op_CastPP && cast->in(0) != nullptr, "inconsistent subgraph");
+      Node* cast_clone = cast->clone();
+      cast_clone->set_req(0, ctrl);
+      cast_clone->set_req(1, in);
+      register_new_node(cast_clone, ctrl);
+      const Type* tcast = cast_clone->Value(&_igvn);
+      _igvn.set_type(cast_clone, tcast);
+      cast_clone->as_Type()->set_type(tcast);
+      in = cast_clone;
+    }
+    Node* addr_clone = addr->clone();
+    addr_clone->set_req(AddPNode::Base, in);
+    addr_clone->set_req(AddPNode::Address, in);
+    register_new_node(addr_clone, ctrl);
+    _igvn.set_type(addr_clone, addr_clone->Value(&_igvn));
+    Node* klassptr_clone = klassptr->clone();
+    klassptr_clone->set_req(2, addr_clone);
+    register_new_node(klassptr_clone, ctrl);
+    _igvn.set_type(klassptr_clone, klassptr_clone->Value(&_igvn));
+    if (klassptr != n->in(1)) {
+      Node* decode = n->in(1);
+      assert(decode->is_DecodeNarrowPtr(), "inconsistent subgraph");
+      Node* decode_clone = decode->clone();
+      decode_clone->set_req(1, klassptr_clone);
+      register_new_node(decode_clone, ctrl);
+      _igvn.set_type(decode_clone, decode_clone->Value(&_igvn));
+      klassptr_clone = decode_clone;
+    }
+    phi->set_req(i, klassptr_clone);
+  }
+  register_new_node(phi, region);
+  Node* orig = n->in(1);
+  _igvn.replace_input_of(n, 1, phi);
+  split_if_with_blocks_post(n);
+  if (n->outcnt() != 0) {
+    _igvn.replace_input_of(n, 1, orig);
+    _igvn.remove_dead_node(phi);
+  }
+  return true;
+}
+
 //------------------------------split_if_with_blocks_post----------------------
 // Do the real work in a non-recursive function.  CFG hackery wants to be
 // in the post-order, so it can dirty the I-DOM info and not use the dirtied
 // info.
 void PhaseIdealLoop::split_if_with_blocks_post(Node *n) {
 
+  if (flat_array_element_type_check(n)) {
+    return;
+  }
+
   // Cloning Cmp through Phi's involves the split-if transform.
   // FastLock is not used by an If
   if (n->is_Cmp() && !n->is_FastLock()) {
     Node *n_ctrl = get_ctrl(n);
     // Determine if the Node has inputs from some local Phi.
@@ -1500,10 +1668,15 @@
   }
 
   try_sink_out_of_loop(n);
 
   try_move_store_after_loop(n);
+
+  // Remove multiple allocations of the same inline type
+  if (n->is_InlineType()) {
+    n->as_InlineType()->remove_redundant_allocations(this);
+  }
 }
 
 // Transform:
 //
 // if (some_condition) {
@@ -1959,14 +2132,22 @@
     sample_bool = n->in(1);
     assert(sample_bool->is_Bool(), "wrong type");
   } else {
     sample_bool = n;
   }
-  Node *sample_cmp = sample_bool->in(1);
+  Node* sample_cmp = sample_bool->in(1);
+  const Type* t = Type::TOP;
+  const TypePtr* at = nullptr;
+  if (sample_cmp->is_FlatArrayCheck()) {
+    // Left input of a FlatArrayCheckNode is memory, set the (adr) type of the phi accordingly
+    assert(sample_cmp->in(1)->bottom_type() == Type::MEMORY, "unexpected input type");
+    t = Type::MEMORY;
+    at = TypeRawPtr::BOTTOM;
+  }
 
   // Make Phis to merge the Cmp's inputs.
-  PhiNode *phi1 = new PhiNode(phi->in(0), Type::TOP);
+  PhiNode *phi1 = new PhiNode(phi->in(0), t, at);
   PhiNode *phi2 = new PhiNode(phi->in(0), Type::TOP);
   for (i = 1; i < phi->req(); i++) {
     Node *n1 = sample_opaque == nullptr ? phi->in(i)->in(1)->in(1) : phi->in(i)->in(1)->in(1)->in(1);
     Node *n2 = sample_opaque == nullptr ? phi->in(i)->in(1)->in(2) : phi->in(i)->in(1)->in(1)->in(2);
     phi1->set_req(i, n1);
diff a/src/hotspot/share/opto/machnode.cpp b/src/hotspot/share/opto/machnode.cpp
--- a/src/hotspot/share/opto/machnode.cpp
+++ b/src/hotspot/share/opto/machnode.cpp
@@ -411,10 +411,26 @@
     assert(false, "this path may produce not optimal code");
     return TypePtr::BOTTOM;
   }
   assert(tp->base() != Type::AnyPtr, "not a bare pointer");
 
+  if (tp->isa_aryptr()) {
+    // In the case of a flat inline type array, each field has its
+    // own slice so we need to extract the field being accessed from
+    // the address computation
+    if (offset == Type::OffsetBot) {
+      Node* base;
+      Node* index;
+      const MachOper* oper = memory_inputs(base, index);
+      if (oper != (MachOper*)-1) {
+        offset = oper->constant_disp();
+        return tp->is_aryptr()->add_field_offset_and_offset(offset)->add_offset(Type::OffsetBot);
+      }
+    }
+    return tp->is_aryptr()->add_field_offset_and_offset(offset);
+  }
+
   return tp->add_offset(offset);
 }
 
 
 //-----------------------------operand_index---------------------------------
@@ -703,25 +719,24 @@
 
 //=============================================================================
 
 bool MachCallNode::cmp( const Node &n ) const
 { return _tf == ((MachCallNode&)n)._tf; }
-const Type *MachCallNode::bottom_type() const { return tf()->range(); }
-const Type* MachCallNode::Value(PhaseGVN* phase) const { return tf()->range(); }
+const Type *MachCallNode::bottom_type() const { return tf()->range_cc(); }
+const Type* MachCallNode::Value(PhaseGVN* phase) const { return tf()->range_cc(); }
 
 #ifndef PRODUCT
 void MachCallNode::dump_spec(outputStream *st) const {
   st->print("# ");
   if (tf() != nullptr)  tf()->dump_on(st);
   if (_cnt != COUNT_UNKNOWN)  st->print(" C=%f",_cnt);
   if (jvms() != nullptr)  jvms()->dump_spec(st);
 }
 #endif
 
-#ifndef _LP64
 bool MachCallNode::return_value_is_used() const {
-  if (tf()->range()->cnt() == TypeFunc::Parms) {
+  if (tf()->range_sig()->cnt() == TypeFunc::Parms) {
     // void return
     return false;
   }
 
   // find the projection corresponding to the return value
@@ -732,26 +747,34 @@
       return true;
     }
   }
   return false;
 }
-#endif
 
 // Similar to cousin class CallNode::returns_pointer
 // Because this is used in deoptimization, we want the type info, not the data
 // flow info; the interpreter will "use" things that are dead to the optimizer.
 bool MachCallNode::returns_pointer() const {
-  const TypeTuple *r = tf()->range();
+  const TypeTuple *r = tf()->range_sig();
   return (r->cnt() > TypeFunc::Parms &&
           r->field_at(TypeFunc::Parms)->isa_ptr());
 }
 
+bool MachCallNode::returns_scalarized() const {
+  return tf()->returns_inline_type_as_fields();
+}
+
 //------------------------------Registers--------------------------------------
 const RegMask &MachCallNode::in_RegMask(uint idx) const {
   // Values in the domain use the users calling convention, embodied in the
   // _in_rms array of RegMasks.
-  if (idx < tf()->domain()->cnt()) {
+  if (entry_point() == nullptr && idx == TypeFunc::Parms) {
+    // Null entry point is a special cast where the target of the call
+    // is in a register.
+    return MachNode::in_RegMask(idx);
+  }
+  if (idx < tf()->domain_sig()->cnt()) {
     return _in_rms[idx];
   }
   if (idx == mach_constant_base_node_input()) {
     return MachConstantBaseNode::static_out_RegMask();
   }
@@ -780,11 +803,11 @@
 
 //------------------------------Registers--------------------------------------
 const RegMask &MachCallJavaNode::in_RegMask(uint idx) const {
   // Values in the domain use the users calling convention, embodied in the
   // _in_rms array of RegMasks.
-  if (idx < tf()->domain()->cnt()) {
+  if (idx < tf()->domain_cc()->cnt()) {
     return _in_rms[idx];
   }
   if (idx == mach_constant_base_node_input()) {
     return MachConstantBaseNode::static_out_RegMask();
   }
diff a/src/hotspot/share/opto/machnode.hpp b/src/hotspot/share/opto/machnode.hpp
--- a/src/hotspot/share/opto/machnode.hpp
+++ b/src/hotspot/share/opto/machnode.hpp
@@ -49,10 +49,11 @@
 class MachProjNode;
 class MachPrologNode;
 class MachReturnNode;
 class MachSafePointNode;
 class MachSpillCopyNode;
+class MachVEPNode;
 class Matcher;
 class PhaseRegAlloc;
 class RegMask;
 class RTMLockingCounters;
 class State;
@@ -491,17 +492,46 @@
   int  constant_offset() const { return ((MachConstantNode*) this)->constant_offset(); }
   // Unchecked version to avoid assertions in debug output.
   int  constant_offset_unchecked() const;
 };
 
+//------------------------------MachVEPNode-----------------------------------
+// Machine Inline Type Entry Point Node
+class MachVEPNode : public MachIdealNode {
+public:
+  Label* _verified_entry;
+
+  MachVEPNode(Label* verified_entry, bool verified, bool receiver_only) :
+    _verified_entry(verified_entry),
+    _verified(verified),
+    _receiver_only(receiver_only) {
+    init_class_id(Class_MachVEP);
+  }
+  virtual bool cmp(const Node &n) const {
+    return (_verified_entry == ((MachVEPNode&)n)._verified_entry) &&
+           (_verified == ((MachVEPNode&)n)._verified) &&
+           (_receiver_only == ((MachVEPNode&)n)._receiver_only) &&
+           MachIdealNode::cmp(n);
+  }
+  virtual uint size_of() const { return sizeof(*this); }
+  virtual void emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const;
+
+#ifndef PRODUCT
+  virtual const char* Name() const { return "InlineType Entry-Point"; }
+  virtual void format(PhaseRegAlloc*, outputStream* st) const;
+#endif
+private:
+  bool   _verified;
+  bool   _receiver_only;
+};
+
 //------------------------------MachUEPNode-----------------------------------
 // Machine Unvalidated Entry Point Node
 class MachUEPNode : public MachIdealNode {
 public:
   MachUEPNode( ) {}
   virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const;
-  virtual uint size(PhaseRegAlloc *ra_) const;
 
 #ifndef PRODUCT
   virtual const char *Name() const { return "Unvalidated-Entry-Point"; }
   virtual void format( PhaseRegAlloc *, outputStream *st ) const;
 #endif
@@ -509,13 +539,20 @@
 
 //------------------------------MachPrologNode--------------------------------
 // Machine function Prolog Node
 class MachPrologNode : public MachIdealNode {
 public:
-  MachPrologNode( ) {}
+  Label* _verified_entry;
+
+  MachPrologNode(Label* verified_entry) : _verified_entry(verified_entry) {
+    init_class_id(Class_MachProlog);
+  }
+  virtual bool cmp(const Node &n) const {
+    return (_verified_entry == ((MachPrologNode&)n)._verified_entry) && MachIdealNode::cmp(n);
+  }
+  virtual uint size_of() const { return sizeof(*this); }
   virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const;
-  virtual uint size(PhaseRegAlloc *ra_) const;
   virtual int reloc() const;
 
 #ifndef PRODUCT
   virtual const char *Name() const { return "Prolog"; }
   virtual void format( PhaseRegAlloc *, outputStream *st ) const;
@@ -526,11 +563,10 @@
 // Machine function Epilog Node
 class MachEpilogNode : public MachIdealNode {
 public:
   MachEpilogNode(bool do_poll = false) : _do_polling(do_poll) {}
   virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const;
-  virtual uint size(PhaseRegAlloc *ra_) const;
   virtual int reloc() const;
   virtual const Pipeline *pipeline() const;
 
 private:
   bool _do_polling;
@@ -914,14 +950,15 @@
   virtual bool  pinned() const { return false; }
   virtual const Type* Value(PhaseGVN* phase) const;
   virtual const RegMask &in_RegMask(uint) const;
   virtual int ret_addr_offset() { return 0; }
 
-  NOT_LP64(bool return_value_is_used() const;)
+  bool return_value_is_used() const;
 
   // Similar to cousin class CallNode::returns_pointer
   bool returns_pointer() const;
+  bool returns_scalarized() const;
 
   bool guaranteed_safepoint() const { return _guaranteed_safepoint; }
 
 #ifndef PRODUCT
   virtual void dump_spec(outputStream *st) const;
diff a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp
--- a/src/hotspot/share/opto/macro.cpp
+++ b/src/hotspot/share/opto/macro.cpp
@@ -21,10 +21,11 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "ci/ciFlatArrayKlass.hpp"
 #include "compiler/compileLog.hpp"
 #include "gc/shared/collectedHeap.inline.hpp"
 #include "gc/shared/tlab_globals.hpp"
 #include "libadt/vectset.hpp"
 #include "memory/universe.hpp"
@@ -34,10 +35,11 @@
 #include "opto/castnode.hpp"
 #include "opto/cfgnode.hpp"
 #include "opto/compile.hpp"
 #include "opto/convertnode.hpp"
 #include "opto/graphKit.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/intrinsicnode.hpp"
 #include "opto/locknode.hpp"
 #include "opto/loopnode.hpp"
 #include "opto/macro.hpp"
 #include "opto/memnode.hpp"
@@ -51,10 +53,11 @@
 #include "opto/subtypenode.hpp"
 #include "opto/type.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "runtime/continuation.hpp"
 #include "runtime/sharedRuntime.hpp"
+#include "runtime/stubRoutines.hpp"
 #include "utilities/macros.hpp"
 #include "utilities/powerOfTwo.hpp"
 #if INCLUDE_G1GC
 #include "gc/g1/g1ThreadLocalData.hpp"
 #endif // INCLUDE_G1GC
@@ -80,22 +83,10 @@
     }
   }
   return nreplacements;
 }
 
-void PhaseMacroExpand::migrate_outs(Node *old, Node *target) {
-  assert(old != nullptr, "sanity");
-  for (DUIterator_Fast imax, i = old->fast_outs(imax); i < imax; i++) {
-    Node* use = old->fast_out(i);
-    _igvn.rehash_node_delayed(use);
-    imax -= replace_input(use, old, target);
-    // back up iterator
-    --i;
-  }
-  assert(old->outcnt() == 0, "all uses must be deleted");
-}
-
 Node* PhaseMacroExpand::opt_bits_test(Node* ctrl, Node* region, int edge, Node* word, int mask, int bits, bool return_fast_path) {
   Node* cmp;
   if (mask != 0) {
     Node* and_node = transform_later(new AndXNode(word, MakeConX(mask)));
     cmp = transform_later(new CmpXNode(and_node, MakeConX(bits)));
@@ -154,11 +145,11 @@
   return call;
 }
 
 void PhaseMacroExpand::eliminate_gc_barrier(Node* p2x) {
   BarrierSetC2 *bs = BarrierSet::barrier_set()->barrier_set_c2();
-  bs->eliminate_gc_barrier(this, p2x);
+  bs->eliminate_gc_barrier(&_igvn, p2x);
 #ifndef PRODUCT
   if (PrintOptoStatistics) {
     Atomic::inc(&PhaseMacroExpand::_GC_barriers_removed_counter);
   }
 #endif
@@ -208,11 +199,11 @@
     } else if (mem->is_Store()) {
       const TypePtr* atype = mem->as_Store()->adr_type();
       int adr_idx = phase->C->get_alias_index(atype);
       if (adr_idx == alias_idx) {
         assert(atype->isa_oopptr(), "address type must be oopptr");
-        int adr_offset = atype->offset();
+        int adr_offset = atype->flat_offset();
         uint adr_iid = atype->is_oopptr()->instance_id();
         // Array elements references have the same alias_idx
         // but different offset and different instance_id.
         if (adr_offset == offset && adr_iid == alloc->_idx) {
           return mem;
@@ -253,11 +244,11 @@
         DEBUG_ONLY(mem->dump();)
         assert(false, "Object is not scalar replaceable if a LoadStore node accesses its field");
         return nullptr;
       }
       mem = mem->in(MemNode::Memory);
-   } else if (mem->Opcode() == Op_StrInflatedCopy) {
+    } else if (mem->Opcode() == Op_StrInflatedCopy) {
       Node* adr = mem->in(3); // Destination array
       const TypePtr* atype = adr->bottom_type()->is_ptr();
       int adr_idx = phase->C->get_alias_index(atype);
       if (adr_idx == alias_idx) {
         DEBUG_ONLY(mem->dump();)
@@ -298,45 +289,53 @@
       Node* dest_pos = ac->in(ArrayCopyNode::DestPos);
       const TypeInt* src_pos_t = _igvn.type(src_pos)->is_int();
       const TypeInt* dest_pos_t = _igvn.type(dest_pos)->is_int();
 
       Node* adr = nullptr;
-      const TypePtr* adr_type = nullptr;
+      Node* base = ac->in(ArrayCopyNode::Src);
+      const TypeAryPtr* adr_type = _igvn.type(base)->is_aryptr();
+      if (adr_type->is_flat()) {
+        shift = adr_type->flat_log_elem_size();
+      }
       if (src_pos_t->is_con() && dest_pos_t->is_con()) {
         intptr_t off = ((src_pos_t->get_con() - dest_pos_t->get_con()) << shift) + offset;
-        Node* base = ac->in(ArrayCopyNode::Src);
         adr = _igvn.transform(new AddPNode(base, base, _igvn.MakeConX(off)));
-        adr_type = _igvn.type(base)->is_ptr()->add_offset(off);
+        adr_type = _igvn.type(adr)->is_aryptr();
+        assert(adr_type == _igvn.type(base)->is_aryptr()->add_field_offset_and_offset(off), "incorrect address type");
         if (ac->in(ArrayCopyNode::Src) == ac->in(ArrayCopyNode::Dest)) {
           // Don't emit a new load from src if src == dst but try to get the value from memory instead
-          return value_from_mem(ac->in(TypeFunc::Memory), ctl, ft, ftype, adr_type->isa_oopptr(), alloc);
+          return value_from_mem(ac->in(TypeFunc::Memory), ctl, ft, ftype, adr_type, alloc);
         }
       } else {
+        if (ac->in(ArrayCopyNode::Src) == ac->in(ArrayCopyNode::Dest)) {
+          // Non constant offset in the array: we can't statically
+          // determine the value
+          return nullptr;
+        }
         Node* diff = _igvn.transform(new SubINode(ac->in(ArrayCopyNode::SrcPos), ac->in(ArrayCopyNode::DestPos)));
 #ifdef _LP64
         diff = _igvn.transform(new ConvI2LNode(diff));
 #endif
         diff = _igvn.transform(new LShiftXNode(diff, _igvn.intcon(shift)));
 
         Node* off = _igvn.transform(new AddXNode(_igvn.MakeConX(offset), diff));
-        Node* base = ac->in(ArrayCopyNode::Src);
         adr = _igvn.transform(new AddPNode(base, base, off));
-        adr_type = _igvn.type(base)->is_ptr()->add_offset(Type::OffsetBot);
-        if (ac->in(ArrayCopyNode::Src) == ac->in(ArrayCopyNode::Dest)) {
-          // Non constant offset in the array: we can't statically
-          // determine the value
-          return nullptr;
-        }
+        // In the case of a flat inline type array, each field has its
+        // own slice so we need to extract the field being accessed from
+        // the address computation
+        adr_type = adr_type->add_field_offset_and_offset(offset)->add_offset(Type::OffsetBot)->is_aryptr();
+        adr = _igvn.transform(new CastPPNode(ctl, adr, adr_type));
       }
       MergeMemNode* mergemen = _igvn.transform(MergeMemNode::make(mem))->as_MergeMem();
       BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
       res = ArrayCopyNode::load(bs, &_igvn, ctl, mergemen, adr, adr_type, type, bt);
     }
   }
   if (res != nullptr) {
     if (ftype->isa_narrowoop()) {
       // PhaseMacroExpand::scalar_replacement adds DecodeN nodes
+      assert(res->isa_DecodeN(), "should be narrow oop");
       res = _igvn.transform(new EncodePNode(res, ftype));
     }
     return res;
   }
   return nullptr;
@@ -348,11 +347,11 @@
 // Note: this function is recursive, its depth is limited by the "level" argument
 // Returns the computed Phi, or null if it cannot compute it.
 Node *PhaseMacroExpand::value_from_mem_phi(Node *mem, BasicType ft, const Type *phi_type, const TypeOopPtr *adr_t, AllocateNode *alloc, Node_Stack *value_phis, int level) {
   assert(mem->is_Phi(), "sanity");
   int alias_idx = C->get_alias_index(adr_t);
-  int offset = adr_t->offset();
+  int offset = adr_t->flat_offset();
   int instance_id = adr_t->instance_id();
 
   // Check if an appropriate value phi already exists.
   Node* region = mem->in(0);
   for (DUIterator_Fast kmax, k = region->fast_outs(kmax); k < kmax; k++) {
@@ -383,15 +382,21 @@
 
   for (uint j = 1; j < length; j++) {
     Node *in = mem->in(j);
     if (in == nullptr || in->is_top()) {
       values.at_put(j, in);
-    } else  {
+    } else {
       Node *val = scan_mem_chain(in, alias_idx, offset, start_mem, alloc, &_igvn);
       if (val == start_mem || val == alloc_mem) {
         // hit a sentinel, return appropriate 0 value
-        values.at_put(j, _igvn.zerocon(ft));
+        Node* default_value = alloc->in(AllocateNode::DefaultValue);
+        if (default_value != nullptr) {
+          values.at_put(j, default_value);
+        } else {
+          assert(alloc->in(AllocateNode::RawDefaultValue) == nullptr, "default value may not be null");
+          values.at_put(j, _igvn.zerocon(ft));
+        }
         continue;
       }
       if (val->is_Initialize()) {
         val = val->as_Initialize()->find_captured_store(offset, type2aelembytes(ft), &_igvn);
       }
@@ -407,11 +412,17 @@
         if (is_subword_type(ft)) {
           n = Compile::narrow_value(ft, n, phi_type, &_igvn, true);
         }
         values.at_put(j, n);
       } else if(val->is_Proj() && val->in(0) == alloc) {
-        values.at_put(j, _igvn.zerocon(ft));
+        Node* default_value = alloc->in(AllocateNode::DefaultValue);
+        if (default_value != nullptr) {
+          values.at_put(j, default_value);
+        } else {
+          assert(alloc->in(AllocateNode::RawDefaultValue) == nullptr, "default value may not be null");
+          values.at_put(j, _igvn.zerocon(ft));
+        }
       } else if (val->is_Phi()) {
         val = value_from_mem_phi(val, ft, phi_type, adr_t, alloc, value_phis, level-1);
         if (val == nullptr) {
           return nullptr;
         }
@@ -451,13 +462,12 @@
   assert(adr_t->is_known_instance_field(), "instance required");
   int instance_id = adr_t->instance_id();
   assert((uint)instance_id == alloc->_idx, "wrong allocation");
 
   int alias_idx = C->get_alias_index(adr_t);
-  int offset = adr_t->offset();
+  int offset = adr_t->flat_offset();
   Node *start_mem = C->start()->proj_out_or_null(TypeFunc::Memory);
-  Node *alloc_ctrl = alloc->in(TypeFunc::Control);
   Node *alloc_mem = alloc->in(TypeFunc::Memory);
   VectorSet visited;
 
   bool done = sfpt_mem == alloc_mem;
   Node *mem = sfpt_mem;
@@ -469,21 +479,21 @@
     if (mem == start_mem || mem == alloc_mem) {
       done = true;  // hit a sentinel, return appropriate 0 value
     } else if (mem->is_Initialize()) {
       mem = mem->as_Initialize()->find_captured_store(offset, type2aelembytes(ft), &_igvn);
       if (mem == nullptr) {
-        done = true; // Something go wrong.
+        done = true; // Something went wrong.
       } else if (mem->is_Store()) {
         const TypePtr* atype = mem->as_Store()->adr_type();
         assert(C->get_alias_index(atype) == Compile::AliasIdxRaw, "store is correct memory slice");
         done = true;
       }
     } else if (mem->is_Store()) {
       const TypeOopPtr* atype = mem->as_Store()->adr_type()->isa_oopptr();
       assert(atype != nullptr, "address type must be oopptr");
       assert(C->get_alias_index(atype) == alias_idx &&
-             atype->is_known_instance_field() && atype->offset() == offset &&
+             atype->is_known_instance_field() && atype->flat_offset() == offset &&
              atype->instance_id() == instance_id, "store is correct memory slice");
       done = true;
     } else if (mem->is_Phi()) {
       // try to find a phi's unique input
       Node *unique_input = nullptr;
@@ -512,10 +522,15 @@
     }
   }
   if (mem != nullptr) {
     if (mem == start_mem || mem == alloc_mem) {
       // hit a sentinel, return appropriate 0 value
+      Node* default_value = alloc->in(AllocateNode::DefaultValue);
+      if (default_value != nullptr) {
+        return default_value;
+      }
+      assert(alloc->in(AllocateNode::RawDefaultValue) == nullptr, "default value may not be null");
       return _igvn.zerocon(ft);
     } else if (mem->is_Store()) {
       Node* n = mem->in(MemNode::ValueIn);
       BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
       n = bs->step_over_gc_barrier(n);
@@ -543,31 +558,75 @@
         m = sfpt_mem;
       }
       return make_arraycopy_load(mem->as_ArrayCopy(), offset, ctl, m, ft, ftype, alloc);
     }
   }
-  // Something go wrong.
+  // Something went wrong.
   return nullptr;
 }
 
+// Search the last value stored into the inline type's fields.
+Node* PhaseMacroExpand::inline_type_from_mem(Node* mem, Node* ctl, ciInlineKlass* vk, const TypeAryPtr* adr_type, int offset, AllocateNode* alloc) {
+  // Subtract the offset of the first field to account for the missing oop header
+  offset -= vk->first_field_offset();
+  // Create a new InlineTypeNode and retrieve the field values from memory
+  InlineTypeNode* vt = InlineTypeNode::make_uninitialized(_igvn, vk);
+  transform_later(vt);
+  for (int i = 0; i < vk->nof_declared_nonstatic_fields(); ++i) {
+    ciType* field_type = vt->field_type(i);
+    int field_offset = offset + vt->field_offset(i);
+    Node* value = nullptr;
+    if (vt->field_is_flat(i)) {
+      value = inline_type_from_mem(mem, ctl, field_type->as_inline_klass(), adr_type, field_offset, alloc);
+    } else {
+      const Type* ft = Type::get_const_type(field_type);
+      BasicType bt = type2field[field_type->basic_type()];
+      if (UseCompressedOops && !is_java_primitive(bt)) {
+        ft = ft->make_narrowoop();
+        bt = T_NARROWOOP;
+      }
+      // Each inline type field has its own memory slice
+      adr_type = adr_type->with_field_offset(field_offset);
+      value = value_from_mem(mem, ctl, bt, ft, adr_type, alloc);
+      if (value != nullptr && ft->isa_narrowoop()) {
+        assert(UseCompressedOops, "unexpected narrow oop");
+        if (value->is_EncodeP()) {
+          value = value->in(1);
+        } else {
+          value = transform_later(new DecodeNNode(value, value->get_ptr_type()));
+        }
+      }
+    }
+    if (value != nullptr) {
+      vt->set_field_value(i, value);
+    } else {
+      // We might have reached the TrackedInitializationLimit
+      return nullptr;
+    }
+  }
+  return vt;
+}
+
 // Check the possibility of scalar replacement.
 bool PhaseMacroExpand::can_eliminate_allocation(PhaseIterGVN* igvn, AllocateNode *alloc, GrowableArray <SafePointNode *>* safepoints) {
   //  Scan the uses of the allocation to check for anything that would
   //  prevent us from eliminating it.
   NOT_PRODUCT( const char* fail_eliminate = nullptr; )
   DEBUG_ONLY( Node* disq_node = nullptr; )
   bool can_eliminate = true;
   bool reduce_merge_precheck = (safepoints == nullptr);
 
+  Unique_Node_List worklist;
   Node* res = alloc->result_cast();
   const TypeOopPtr* res_type = nullptr;
   if (res == nullptr) {
     // All users were eliminated.
   } else if (!res->is_CheckCastPP()) {
     NOT_PRODUCT(fail_eliminate = "Allocation does not have unique CheckCastPP";)
     can_eliminate = false;
   } else {
+    worklist.push(res);
     res_type = igvn->type(res)->isa_oopptr();
     if (res_type == nullptr) {
       NOT_PRODUCT(fail_eliminate = "Neither instance or array allocation";)
       can_eliminate = false;
     } else if (res_type->isa_aryptr()) {
@@ -577,14 +636,14 @@
         can_eliminate = false;
       }
     }
   }
 
-  if (can_eliminate && res != nullptr) {
+  while (can_eliminate && worklist.size() > 0) {
     BarrierSetC2 *bs = BarrierSet::barrier_set()->barrier_set_c2();
-    for (DUIterator_Fast jmax, j = res->fast_outs(jmax);
-                               j < jmax && can_eliminate; j++) {
+    res = worklist.pop();
+    for (DUIterator_Fast jmax, j = res->fast_outs(jmax); j < jmax && can_eliminate; j++) {
       Node* use = res->fast_out(j);
 
       if (use->is_AddP()) {
         const TypePtr* addp_type = igvn->type(use)->is_ptr();
         int offset = addp_type->offset();
@@ -626,12 +685,41 @@
         if (sfptMem == nullptr || sfptMem->is_top()) {
           DEBUG_ONLY(disq_node = use;)
           NOT_PRODUCT(fail_eliminate = "null or TOP memory";)
           can_eliminate = false;
         } else if (!reduce_merge_precheck) {
+          if (res->is_Phi() && res->as_Phi()->can_be_inline_type()) {
+            // Can only eliminate allocation if the phi had been replaced by an InlineTypeNode before which did not happen.
+            // TODO 8325106 Why wasn't it replaced by an InlineTypeNode?
+            can_eliminate = false;
+          }
           safepoints->append_if_missing(sfpt);
         }
+      } else if (use->is_InlineType() && use->as_InlineType()->get_oop() == res) {
+        // Look at uses
+        for (DUIterator_Fast kmax, k = use->fast_outs(kmax); k < kmax; k++) {
+          Node* u = use->fast_out(k);
+          if (u->is_InlineType()) {
+            // Use in flat field can be eliminated
+            InlineTypeNode* vt = u->as_InlineType();
+            for (uint i = 0; i < vt->field_count(); ++i) {
+              if (vt->field_value(i) == use && !vt->field_is_flat(i)) {
+                can_eliminate = false; // Use in non-flat field
+                break;
+              }
+            }
+          } else {
+            // Add other uses to the worklist to process individually
+            // TODO will be fixed by 8328470
+            worklist.push(use);
+          }
+        }
+      } else if (use->Opcode() == Op_StoreX && use->in(MemNode::Address) == res) {
+        // Store to mark word of inline type larval buffer
+        assert(res_type->is_inlinetypeptr(), "Unexpected store to mark word");
+      } else if (res_type->is_inlinetypeptr() && use->Opcode() == Op_MemBarRelease) {
+        // Inline type buffer allocations are followed by a membar
       } else if (reduce_merge_precheck && (use->is_Phi() || use->is_EncodeP() || use->Opcode() == Op_MemBarRelease)) {
         // Nothing to do
       } else if (use->Opcode() != Op_CastP2X) { // CastP2X is used by card mark
         if (use->is_Phi()) {
           if (use->outcnt() == 1 && use->unique_out()->Opcode() == Op_Return) {
@@ -647,10 +735,13 @@
             NOT_PRODUCT(fail_eliminate = "Object is referenced by node";)
           }
           DEBUG_ONLY(disq_node = use;)
         }
         can_eliminate = false;
+      } else {
+        assert(use->Opcode() == Op_CastP2X, "should be");
+        assert(!use->has_out_with(Op_OrL), "should have been removed because oop is never null");
       }
     }
   }
 
 #ifndef PRODUCT
@@ -659,11 +750,11 @@
       tty->print("Scalar ");
       if (res == nullptr)
         alloc->dump();
       else
         res->dump();
-    } else if (alloc->_is_scalar_replaceable) {
+    } else {
       tty->print("NotScalar (%s)", fail_eliminate);
       if (res == nullptr)
         alloc->dump();
       else
         res->dump();
@@ -729,11 +820,12 @@
     }
     _igvn._worklist.push(sfpt_done);
   }
 }
 
-SafePointScalarObjectNode* PhaseMacroExpand::create_scalarized_object_description(AllocateNode *alloc, SafePointNode* sfpt) {
+SafePointScalarObjectNode* PhaseMacroExpand::create_scalarized_object_description(AllocateNode *alloc, SafePointNode* sfpt,
+                                                                                  Unique_Node_List* value_worklist) {
   // Fields of scalar objs are referenced only at the end
   // of regular debuginfo at the last (youngest) JVMS.
   // Record relative start index.
   ciInstanceKlass* iklass    = nullptr;
   BasicType basic_elem_type  = T_ILLEGAL;
@@ -761,10 +853,14 @@
       assert(nfields >= 0, "must be an array klass.");
       basic_elem_type = res_type->is_aryptr()->elem()->array_element_basic_type();
       array_base = arrayOopDesc::base_offset_in_bytes(basic_elem_type);
       element_size = type2aelembytes(basic_elem_type);
       field_type = res_type->is_aryptr()->elem();
+      if (res_type->is_flat()) {
+        // Flat inline type array
+        element_size = res_type->is_aryptr()->flat_elem_size();
+      }
     }
   }
 
   SafePointScalarObjectNode* sobj = new SafePointScalarObjectNode(res_type, alloc, first_ind, nfields);
   sobj->init_req(0, C->root());
@@ -777,10 +873,11 @@
     if (iklass != nullptr) {
       field = iklass->nonstatic_field_at(j);
       offset = field->offset_in_bytes();
       ciType* elem_type = field->type();
       basic_elem_type = field->layout_type();
+      assert(!field->is_flat(), "flat inline type fields should not have safepoint uses");
 
       // The next code is taken from Parse::do_get_xxx().
       if (is_reference_type(basic_elem_type)) {
         if (!elem_type->is_loaded()) {
           field_type = TypeInstPtr::BOTTOM;
@@ -802,13 +899,19 @@
       }
     } else {
       offset = array_base + j * (intptr_t)element_size;
     }
 
-    const TypeOopPtr *field_addr_type = res_type->add_offset(offset)->isa_oopptr();
-
-    Node *field_val = value_from_mem(sfpt->memory(), sfpt->control(), basic_elem_type, field_type, field_addr_type, alloc);
+    Node* field_val = nullptr;
+    const TypeOopPtr* field_addr_type = res_type->add_offset(offset)->isa_oopptr();
+    if (res_type->is_flat()) {
+      ciInlineKlass* inline_klass = res_type->is_aryptr()->elem()->inline_klass();
+      assert(inline_klass->flat_in_array(), "must be flat in array");
+      field_val = inline_type_from_mem(sfpt->memory(), sfpt->control(), inline_klass, field_addr_type->isa_aryptr(), 0, alloc);
+    } else {
+      field_val = value_from_mem(sfpt->memory(), sfpt->control(), basic_elem_type, field_type, field_addr_type, alloc);
+    }
 
     // We weren't able to find a value for this field,
     // give up on eliminating this allocation.
     if (field_val == nullptr) {
       uint last = sfpt->req() - 1;
@@ -841,14 +944,27 @@
     if (UseCompressedOops && field_type->isa_narrowoop()) {
       // Enable "DecodeN(EncodeP(Allocate)) --> Allocate" transformation
       // to be able scalar replace the allocation.
       if (field_val->is_EncodeP()) {
         field_val = field_val->in(1);
-      } else {
+      } else if (!field_val->is_InlineType()) {
         field_val = transform_later(new DecodeNNode(field_val, field_val->get_ptr_type()));
       }
     }
+
+    // Keep track of inline types to scalarize them later
+    if (field_val->is_InlineType()) {
+      value_worklist->push(field_val);
+    } else if (field_val->is_Phi()) {
+      PhiNode* phi = field_val->as_Phi();
+      // Eagerly replace inline type phis now since we could be removing an inline type allocation where we must
+      // scalarize all its fields in safepoints.
+      field_val = phi->try_push_inline_types_down(&_igvn, true);
+      if (field_val->is_InlineType()) {
+        value_worklist->push(field_val);
+      }
+    }
     sfpt->add_req(field_val);
   }
 
   sfpt->jvms()->set_endoff(sfpt->req());
 
@@ -858,15 +974,21 @@
 // Do scalar replacement.
 bool PhaseMacroExpand::scalar_replacement(AllocateNode *alloc, GrowableArray <SafePointNode *>& safepoints) {
   GrowableArray <SafePointNode *> safepoints_done;
   Node* res = alloc->result_cast();
   assert(res == nullptr || res->is_CheckCastPP(), "unexpected AllocateNode result");
+  const TypeOopPtr* res_type = nullptr;
+  if (res != nullptr) { // Could be null when there are no users
+    res_type = _igvn.type(res)->isa_oopptr();
+  }
 
   // Process the safepoint uses
+  assert(safepoints.length() == 0 || !res_type->is_inlinetypeptr(), "Inline type allocations should not have safepoint uses");
+  Unique_Node_List value_worklist;
   while (safepoints.length() > 0) {
     SafePointNode* sfpt = safepoints.pop();
-    SafePointScalarObjectNode* sobj = create_scalarized_object_description(alloc, sfpt);
+    SafePointScalarObjectNode* sobj = create_scalarized_object_description(alloc, sfpt, &value_worklist);
 
     if (sobj == nullptr) {
       undo_previous_scalarizations(safepoints_done, alloc);
       return false;
     }
@@ -878,11 +1000,18 @@
     _igvn._worklist.push(sfpt);
 
     // keep it for rollback
     safepoints_done.append_if_missing(sfpt);
   }
-
+  // Scalarize inline types that were added to the safepoint.
+  // Don't allow linking a constant oop (if available) for flat array elements
+  // because Deoptimization::reassign_flat_array_elements needs field values.
+  bool allow_oop = (res_type != nullptr) && !res_type->is_flat();
+  for (uint i = 0; i < value_worklist.size(); ++i) {
+    InlineTypeNode* vt = value_worklist.at(i)->as_InlineType();
+    vt->make_scalar_in_safepoints(&_igvn, allow_oop);
+  }
   return true;
 }
 
 static void disconnect_projections(MultiNode* n, PhaseIterGVN& igvn) {
   Node* ctl_proj = n->proj_out_or_null(TypeFunc::Control);
@@ -894,34 +1023,35 @@
     igvn.replace_node(mem_proj, n->in(TypeFunc::Memory));
   }
 }
 
 // Process users of eliminated allocation.
-void PhaseMacroExpand::process_users_of_allocation(CallNode *alloc) {
+void PhaseMacroExpand::process_users_of_allocation(CallNode *alloc, bool inline_alloc) {
+  Unique_Node_List worklist;
   Node* res = alloc->result_cast();
   if (res != nullptr) {
+    worklist.push(res);
+  }
+  while (worklist.size() > 0) {
+    res = worklist.pop();
     for (DUIterator_Last jmin, j = res->last_outs(jmin); j >= jmin; ) {
       Node *use = res->last_out(j);
       uint oc1 = res->outcnt();
 
       if (use->is_AddP()) {
         for (DUIterator_Last kmin, k = use->last_outs(kmin); k >= kmin; ) {
           Node *n = use->last_out(k);
           uint oc2 = use->outcnt();
           if (n->is_Store()) {
-#ifdef ASSERT
-            // Verify that there is no dependent MemBarVolatile nodes,
-            // they should be removed during IGVN, see MemBarNode::Ideal().
-            for (DUIterator_Fast pmax, p = n->fast_outs(pmax);
-                                       p < pmax; p++) {
-              Node* mb = n->fast_out(p);
-              assert(mb->is_Initialize() || !mb->is_MemBar() ||
-                     mb->req() <= MemBarNode::Precedent ||
-                     mb->in(MemBarNode::Precedent) != n,
-                     "MemBarVolatile should be eliminated for non-escaping object");
+            for (DUIterator_Fast pmax, p = n->fast_outs(pmax); p < pmax; p++) {
+              MemBarNode* mb = n->fast_out(p)->isa_MemBar();
+              if (mb != nullptr && mb->req() <= MemBarNode::Precedent && mb->in(MemBarNode::Precedent) == n) {
+                // MemBarVolatiles should have been removed by MemBarNode::Ideal() for non-inline allocations
+                assert(inline_alloc, "MemBarVolatile should be eliminated for non-escaping object");
+                mb->remove(&_igvn);
+              }
             }
-#endif
             _igvn.replace_node(n, n->in(MemNode::Memory));
           } else {
             eliminate_gc_barrier(n);
           }
           k -= (oc2 - use->outcnt());
@@ -941,16 +1071,15 @@
           }
         } else {
           assert(ac->is_arraycopy_validated() ||
                  ac->is_copyof_validated() ||
                  ac->is_copyofrange_validated(), "unsupported");
-          CallProjections callprojs;
-          ac->extract_projections(&callprojs, true);
+          CallProjections* callprojs = ac->extract_projections(true);
 
-          _igvn.replace_node(callprojs.fallthrough_ioproj, ac->in(TypeFunc::I_O));
-          _igvn.replace_node(callprojs.fallthrough_memproj, ac->in(TypeFunc::Memory));
-          _igvn.replace_node(callprojs.fallthrough_catchproj, ac->in(TypeFunc::Control));
+          _igvn.replace_node(callprojs->fallthrough_ioproj, ac->in(TypeFunc::I_O));
+          _igvn.replace_node(callprojs->fallthrough_memproj, ac->in(TypeFunc::Memory));
+          _igvn.replace_node(callprojs->fallthrough_catchproj, ac->in(TypeFunc::Control));
 
           // Set control to top. IGVN will remove the remaining projections
           ac->set_req(0, top());
           ac->replace_edge(res, top(), &_igvn);
 
@@ -963,10 +1092,33 @@
           if (src->outcnt() == 0 && !src->is_top()) {
             _igvn.remove_dead_node(src);
           }
         }
         _igvn._worklist.push(ac);
+      } else if (use->is_InlineType()) {
+        assert(use->as_InlineType()->get_oop() == res, "unexpected inline type ptr use");
+        // Cut off oop input and remove known instance id from type
+        _igvn.rehash_node_delayed(use);
+        use->as_InlineType()->set_oop(_igvn, _igvn.zerocon(T_OBJECT));
+        const TypeOopPtr* toop = _igvn.type(use)->is_oopptr()->cast_to_instance_id(TypeOopPtr::InstanceBot);
+        _igvn.set_type(use, toop);
+        use->as_InlineType()->set_type(toop);
+        // Process users
+        for (DUIterator_Fast kmax, k = use->fast_outs(kmax); k < kmax; k++) {
+          Node* u = use->fast_out(k);
+          if (!u->is_InlineType()) {
+            worklist.push(u);
+          }
+        }
+      } else if (use->Opcode() == Op_StoreX && use->in(MemNode::Address) == res) {
+        // Store to mark word of inline type larval buffer
+        assert(inline_alloc, "Unexpected store to mark word");
+        _igvn.replace_node(use, use->in(MemNode::Memory));
+      } else if (use->Opcode() == Op_MemBarRelease) {
+        // Inline type buffer allocations are followed by a membar
+        assert(inline_alloc, "Unexpected MemBarRelease");
+        use->as_MemBar()->remove(&_igvn);
       } else {
         eliminate_gc_barrier(use);
       }
       j -= (oc1 - res->outcnt());
     }
@@ -975,110 +1127,123 @@
   }
 
   //
   // Process other users of allocation's projections
   //
-  if (_callprojs.resproj != nullptr && _callprojs.resproj->outcnt() != 0) {
+  if (_callprojs->resproj[0] != nullptr && _callprojs->resproj[0]->outcnt() != 0) {
     // First disconnect stores captured by Initialize node.
     // If Initialize node is eliminated first in the following code,
     // it will kill such stores and DUIterator_Last will assert.
-    for (DUIterator_Fast jmax, j = _callprojs.resproj->fast_outs(jmax);  j < jmax; j++) {
-      Node* use = _callprojs.resproj->fast_out(j);
+    for (DUIterator_Fast jmax, j = _callprojs->resproj[0]->fast_outs(jmax);  j < jmax; j++) {
+      Node* use = _callprojs->resproj[0]->fast_out(j);
       if (use->is_AddP()) {
         // raw memory addresses used only by the initialization
         _igvn.replace_node(use, C->top());
         --j; --jmax;
       }
     }
-    for (DUIterator_Last jmin, j = _callprojs.resproj->last_outs(jmin); j >= jmin; ) {
-      Node* use = _callprojs.resproj->last_out(j);
-      uint oc1 = _callprojs.resproj->outcnt();
+    for (DUIterator_Last jmin, j = _callprojs->resproj[0]->last_outs(jmin); j >= jmin; ) {
+      Node* use = _callprojs->resproj[0]->last_out(j);
+      uint oc1 = _callprojs->resproj[0]->outcnt();
       if (use->is_Initialize()) {
         // Eliminate Initialize node.
         InitializeNode *init = use->as_Initialize();
         assert(init->outcnt() <= 2, "only a control and memory projection expected");
         Node *ctrl_proj = init->proj_out_or_null(TypeFunc::Control);
         if (ctrl_proj != nullptr) {
           _igvn.replace_node(ctrl_proj, init->in(TypeFunc::Control));
 #ifdef ASSERT
           // If the InitializeNode has no memory out, it will die, and tmp will become null
           Node* tmp = init->in(TypeFunc::Control);
-          assert(tmp == nullptr || tmp == _callprojs.fallthrough_catchproj, "allocation control projection");
+          assert(tmp == nullptr || tmp == _callprojs->fallthrough_catchproj, "allocation control projection");
 #endif
         }
         Node *mem_proj = init->proj_out_or_null(TypeFunc::Memory);
         if (mem_proj != nullptr) {
           Node *mem = init->in(TypeFunc::Memory);
 #ifdef ASSERT
           if (mem->is_MergeMem()) {
-            assert(mem->in(TypeFunc::Memory) == _callprojs.fallthrough_memproj, "allocation memory projection");
+            assert(mem->in(TypeFunc::Memory) == _callprojs->fallthrough_memproj, "allocation memory projection");
           } else {
-            assert(mem == _callprojs.fallthrough_memproj, "allocation memory projection");
+            assert(mem == _callprojs->fallthrough_memproj, "allocation memory projection");
           }
 #endif
           _igvn.replace_node(mem_proj, mem);
         }
+      } else if (use->Opcode() == Op_MemBarStoreStore) {
+        // Inline type buffer allocations are followed by a membar
+        assert(inline_alloc, "Unexpected MemBarStoreStore");
+        use->as_MemBar()->remove(&_igvn);
       } else  {
         assert(false, "only Initialize or AddP expected");
       }
-      j -= (oc1 - _callprojs.resproj->outcnt());
+      j -= (oc1 - _callprojs->resproj[0]->outcnt());
     }
   }
-  if (_callprojs.fallthrough_catchproj != nullptr) {
-    _igvn.replace_node(_callprojs.fallthrough_catchproj, alloc->in(TypeFunc::Control));
+  if (_callprojs->fallthrough_catchproj != nullptr) {
+    _igvn.replace_node(_callprojs->fallthrough_catchproj, alloc->in(TypeFunc::Control));
   }
-  if (_callprojs.fallthrough_memproj != nullptr) {
-    _igvn.replace_node(_callprojs.fallthrough_memproj, alloc->in(TypeFunc::Memory));
+  if (_callprojs->fallthrough_memproj != nullptr) {
+    _igvn.replace_node(_callprojs->fallthrough_memproj, alloc->in(TypeFunc::Memory));
   }
-  if (_callprojs.catchall_memproj != nullptr) {
-    _igvn.replace_node(_callprojs.catchall_memproj, C->top());
+  if (_callprojs->catchall_memproj != nullptr) {
+    _igvn.replace_node(_callprojs->catchall_memproj, C->top());
   }
-  if (_callprojs.fallthrough_ioproj != nullptr) {
-    _igvn.replace_node(_callprojs.fallthrough_ioproj, alloc->in(TypeFunc::I_O));
+  if (_callprojs->fallthrough_ioproj != nullptr) {
+    _igvn.replace_node(_callprojs->fallthrough_ioproj, alloc->in(TypeFunc::I_O));
   }
-  if (_callprojs.catchall_ioproj != nullptr) {
-    _igvn.replace_node(_callprojs.catchall_ioproj, C->top());
+  if (_callprojs->catchall_ioproj != nullptr) {
+    _igvn.replace_node(_callprojs->catchall_ioproj, C->top());
   }
-  if (_callprojs.catchall_catchproj != nullptr) {
-    _igvn.replace_node(_callprojs.catchall_catchproj, C->top());
+  if (_callprojs->catchall_catchproj != nullptr) {
+    _igvn.replace_node(_callprojs->catchall_catchproj, C->top());
   }
 }
 
 bool PhaseMacroExpand::eliminate_allocate_node(AllocateNode *alloc) {
   // If reallocation fails during deoptimization we'll pop all
   // interpreter frames for this compiled frame and that won't play
   // nice with JVMTI popframe.
   // We avoid this issue by eager reallocation when the popframe request
   // is received.
-  if (!EliminateAllocations || !alloc->_is_non_escaping) {
+  if (!EliminateAllocations) {
     return false;
   }
   Node* klass = alloc->in(AllocateNode::KlassNode);
   const TypeKlassPtr* tklass = _igvn.type(klass)->is_klassptr();
-  Node* res = alloc->result_cast();
+
+  // Attempt to eliminate inline type buffer allocations
+  // regardless of usage and escape/replaceable status.
+  bool inline_alloc = tklass->isa_instklassptr() &&
+                      tklass->is_instklassptr()->instance_klass()->is_inlinetype();
+  if (!alloc->_is_non_escaping && !inline_alloc) {
+    return false;
+  }
   // Eliminate boxing allocations which are not used
   // regardless scalar replaceable status.
-  bool boxing_alloc = C->eliminate_boxing() &&
+  Node* res = alloc->result_cast();
+  bool boxing_alloc = (res == nullptr) && C->eliminate_boxing() &&
                       tklass->isa_instklassptr() &&
                       tklass->is_instklassptr()->instance_klass()->is_box_klass();
-  if (!alloc->_is_scalar_replaceable && (!boxing_alloc || (res != nullptr))) {
+  if (!alloc->_is_scalar_replaceable && !boxing_alloc && !inline_alloc) {
     return false;
   }
 
-  alloc->extract_projections(&_callprojs, false /*separate_io_proj*/, false /*do_asserts*/);
+  _callprojs = alloc->extract_projections(false /*separate_io_proj*/, false /*do_asserts*/);
 
   GrowableArray <SafePointNode *> safepoints;
   if (!can_eliminate_allocation(&_igvn, alloc, &safepoints)) {
     return false;
   }
 
   if (!alloc->_is_scalar_replaceable) {
-    assert(res == nullptr, "sanity");
+    assert(res == nullptr || inline_alloc, "sanity");
     // We can only eliminate allocation if all debug info references
     // are already replaced with SafePointScalarObject because
     // we can't search for a fields value without instance_id.
     if (safepoints.length() > 0) {
+      assert(!inline_alloc, "Inline type allocations should not have safepoint uses");
       return false;
     }
   }
 
   if (!scalar_replacement(alloc, safepoints)) {
@@ -1095,11 +1260,11 @@
       p = p->caller();
     }
     log->tail("eliminate_allocation");
   }
 
-  process_users_of_allocation(alloc);
+  process_users_of_allocation(alloc, inline_alloc);
 
 #ifndef PRODUCT
   if (PrintEliminateAllocations) {
     if (alloc->is_AllocateArray())
       tty->print_cr("++++ Eliminated: %d AllocateArray", alloc->_idx);
@@ -1117,13 +1282,13 @@
     return false;
   }
 
   assert(boxing->result_cast() == nullptr, "unexpected boxing node result");
 
-  boxing->extract_projections(&_callprojs, false /*separate_io_proj*/, false /*do_asserts*/);
+  _callprojs = boxing->extract_projections(false /*separate_io_proj*/, false /*do_asserts*/);
 
-  const TypeTuple* r = boxing->tf()->range();
+  const TypeTuple* r = boxing->tf()->range_sig();
   assert(r->cnt() > TypeFunc::Parms, "sanity");
   const TypeInstPtr* t = r->field_at(TypeFunc::Parms)->isa_instptr();
   assert(t != nullptr, "sanity");
 
   CompileLog* log = C->log();
@@ -1303,17 +1468,17 @@
     slow_region = new RegionNode(3);
 
     // Now make the initial failure test.  Usually a too-big test but
     // might be a TRUE for finalizers or a fancy class check for
     // newInstance0.
-    IfNode *toobig_iff = new IfNode(ctrl, initial_slow_test, PROB_MIN, COUNT_UNKNOWN);
+    IfNode* toobig_iff = new IfNode(ctrl, initial_slow_test, PROB_MIN, COUNT_UNKNOWN);
     transform_later(toobig_iff);
     // Plug the failing-too-big test into the slow-path region
-    Node *toobig_true = new IfTrueNode( toobig_iff );
+    Node* toobig_true = new IfTrueNode(toobig_iff);
     transform_later(toobig_true);
     slow_region    ->init_req( too_big_or_final_path, toobig_true );
-    toobig_false = new IfFalseNode( toobig_iff );
+    toobig_false = new IfFalseNode(toobig_iff);
     transform_later(toobig_false);
   } else {
     // No initial test, just fall into next case
     assert(allocation_has_use || !expand_fast_path, "Should already have been handled");
     toobig_false = ctrl;
@@ -1348,10 +1513,11 @@
     result_phi_i_o->init_req(slow_result_path, i_o);
 
     // Name successful fast-path variables
     Node* fast_oop_ctrl;
     Node* fast_oop_rawmem;
+
     if (allocation_has_use) {
       Node* needgc_ctrl = nullptr;
       result_phi_rawoop = new PhiNode(result_region, TypeRawPtr::BOTTOM);
 
       intx prefetch_lines = length != nullptr ? AllocatePrefetchLines : AllocateInstancePrefetchLines;
@@ -1405,10 +1571,13 @@
   call->init_req(TypeFunc::FramePtr,  alloc->in(TypeFunc::FramePtr));
 
   call->init_req(TypeFunc::Parms+0, klass_node);
   if (length != nullptr) {
     call->init_req(TypeFunc::Parms+1, length);
+  } else {
+    // Let the runtime know if this is a larval allocation
+    call->init_req(TypeFunc::Parms+1, _igvn.intcon(alloc->_larval));
   }
 
   // Copy debug information and adjust JVMState information, then replace
   // allocate node with the call
   call->copy_call_debug_info(&_igvn, alloc);
@@ -1436,47 +1605,47 @@
   //  Allocate                   Catch
   //        ^---Proj(io) <-------+   ^---CatchProj(io)
   //
   //  We are interested in the CatchProj nodes.
   //
-  call->extract_projections(&_callprojs, false /*separate_io_proj*/, false /*do_asserts*/);
+  _callprojs = call->extract_projections(false /*separate_io_proj*/, false /*do_asserts*/);
 
   // An allocate node has separate memory projections for the uses on
   // the control and i_o paths. Replace the control memory projection with
   // result_phi_rawmem (unless we are only generating a slow call when
   // both memory projections are combined)
-  if (expand_fast_path && _callprojs.fallthrough_memproj != nullptr) {
-    migrate_outs(_callprojs.fallthrough_memproj, result_phi_rawmem);
+  if (expand_fast_path && _callprojs->fallthrough_memproj != nullptr) {
+    _igvn.replace_in_uses(_callprojs->fallthrough_memproj, result_phi_rawmem);
   }
   // Now change uses of catchall_memproj to use fallthrough_memproj and delete
   // catchall_memproj so we end up with a call that has only 1 memory projection.
-  if (_callprojs.catchall_memproj != nullptr ) {
-    if (_callprojs.fallthrough_memproj == nullptr) {
-      _callprojs.fallthrough_memproj = new ProjNode(call, TypeFunc::Memory);
-      transform_later(_callprojs.fallthrough_memproj);
+  if (_callprojs->catchall_memproj != nullptr) {
+    if (_callprojs->fallthrough_memproj == nullptr) {
+      _callprojs->fallthrough_memproj = new ProjNode(call, TypeFunc::Memory);
+      transform_later(_callprojs->fallthrough_memproj);
     }
-    migrate_outs(_callprojs.catchall_memproj, _callprojs.fallthrough_memproj);
-    _igvn.remove_dead_node(_callprojs.catchall_memproj);
+    _igvn.replace_in_uses(_callprojs->catchall_memproj, _callprojs->fallthrough_memproj);
+    _igvn.remove_dead_node(_callprojs->catchall_memproj);
   }
 
   // An allocate node has separate i_o projections for the uses on the control
   // and i_o paths. Always replace the control i_o projection with result i_o
   // otherwise incoming i_o become dead when only a slow call is generated
   // (it is different from memory projections where both projections are
   // combined in such case).
-  if (_callprojs.fallthrough_ioproj != nullptr) {
-    migrate_outs(_callprojs.fallthrough_ioproj, result_phi_i_o);
+  if (_callprojs->fallthrough_ioproj != nullptr) {
+    _igvn.replace_in_uses(_callprojs->fallthrough_ioproj, result_phi_i_o);
   }
   // Now change uses of catchall_ioproj to use fallthrough_ioproj and delete
   // catchall_ioproj so we end up with a call that has only 1 i_o projection.
-  if (_callprojs.catchall_ioproj != nullptr ) {
-    if (_callprojs.fallthrough_ioproj == nullptr) {
-      _callprojs.fallthrough_ioproj = new ProjNode(call, TypeFunc::I_O);
-      transform_later(_callprojs.fallthrough_ioproj);
+  if (_callprojs->catchall_ioproj != nullptr) {
+    if (_callprojs->fallthrough_ioproj == nullptr) {
+      _callprojs->fallthrough_ioproj = new ProjNode(call, TypeFunc::I_O);
+      transform_later(_callprojs->fallthrough_ioproj);
     }
-    migrate_outs(_callprojs.catchall_ioproj, _callprojs.fallthrough_ioproj);
-    _igvn.remove_dead_node(_callprojs.catchall_ioproj);
+    _igvn.replace_in_uses(_callprojs->catchall_ioproj, _callprojs->fallthrough_ioproj);
+    _igvn.remove_dead_node(_callprojs->catchall_ioproj);
   }
 
   // if we generated only a slow call, we are done
   if (!expand_fast_path) {
     // Now we can unhook i_o.
@@ -1491,35 +1660,35 @@
       // Leave i_o attached to this call to avoid problems in preceding graph.
     }
     return;
   }
 
-  if (_callprojs.fallthrough_catchproj != nullptr) {
-    ctrl = _callprojs.fallthrough_catchproj->clone();
+  if (_callprojs->fallthrough_catchproj != nullptr) {
+    ctrl = _callprojs->fallthrough_catchproj->clone();
     transform_later(ctrl);
-    _igvn.replace_node(_callprojs.fallthrough_catchproj, result_region);
+    _igvn.replace_node(_callprojs->fallthrough_catchproj, result_region);
   } else {
     ctrl = top();
   }
   Node *slow_result;
-  if (_callprojs.resproj == nullptr) {
+  if (_callprojs->resproj[0] == nullptr) {
     // no uses of the allocation result
     slow_result = top();
   } else {
-    slow_result = _callprojs.resproj->clone();
+    slow_result = _callprojs->resproj[0]->clone();
     transform_later(slow_result);
-    _igvn.replace_node(_callprojs.resproj, result_phi_rawoop);
+    _igvn.replace_node(_callprojs->resproj[0], result_phi_rawoop);
   }
 
   // Plug slow-path into result merge point
   result_region->init_req( slow_result_path, ctrl);
   transform_later(result_region);
   if (allocation_has_use) {
     result_phi_rawoop->init_req(slow_result_path, slow_result);
     transform_later(result_phi_rawoop);
   }
-  result_phi_rawmem->init_req(slow_result_path, _callprojs.fallthrough_memproj);
+  result_phi_rawmem->init_req(slow_result_path, _callprojs->fallthrough_memproj);
   transform_later(result_phi_rawmem);
   transform_later(result_phi_i_o);
   // This completes all paths into the result merge point
 }
 
@@ -1527,49 +1696,49 @@
 void PhaseMacroExpand::yank_alloc_node(AllocateNode* alloc) {
   Node* ctrl = alloc->in(TypeFunc::Control);
   Node* mem  = alloc->in(TypeFunc::Memory);
   Node* i_o  = alloc->in(TypeFunc::I_O);
 
-  alloc->extract_projections(&_callprojs, false /*separate_io_proj*/, false /*do_asserts*/);
-  if (_callprojs.resproj != nullptr) {
-    for (DUIterator_Fast imax, i = _callprojs.resproj->fast_outs(imax); i < imax; i++) {
-      Node* use = _callprojs.resproj->fast_out(i);
+  _callprojs = alloc->extract_projections(false /*separate_io_proj*/, false /*do_asserts*/);
+  if (_callprojs->resproj[0] != nullptr) {
+    for (DUIterator_Fast imax, i = _callprojs->resproj[0]->fast_outs(imax); i < imax; i++) {
+      Node* use = _callprojs->resproj[0]->fast_out(i);
       use->isa_MemBar()->remove(&_igvn);
       --imax;
       --i; // back up iterator
     }
-    assert(_callprojs.resproj->outcnt() == 0, "all uses must be deleted");
-    _igvn.remove_dead_node(_callprojs.resproj);
+    assert(_callprojs->resproj[0]->outcnt() == 0, "all uses must be deleted");
+    _igvn.remove_dead_node(_callprojs->resproj[0]);
   }
-  if (_callprojs.fallthrough_catchproj != nullptr) {
-    migrate_outs(_callprojs.fallthrough_catchproj, ctrl);
-    _igvn.remove_dead_node(_callprojs.fallthrough_catchproj);
+  if (_callprojs->fallthrough_catchproj != nullptr) {
+    _igvn.replace_in_uses(_callprojs->fallthrough_catchproj, ctrl);
+    _igvn.remove_dead_node(_callprojs->fallthrough_catchproj);
   }
-  if (_callprojs.catchall_catchproj != nullptr) {
-    _igvn.rehash_node_delayed(_callprojs.catchall_catchproj);
-    _callprojs.catchall_catchproj->set_req(0, top());
+  if (_callprojs->catchall_catchproj != nullptr) {
+    _igvn.rehash_node_delayed(_callprojs->catchall_catchproj);
+    _callprojs->catchall_catchproj->set_req(0, top());
   }
-  if (_callprojs.fallthrough_proj != nullptr) {
-    Node* catchnode = _callprojs.fallthrough_proj->unique_ctrl_out();
+  if (_callprojs->fallthrough_proj != nullptr) {
+    Node* catchnode = _callprojs->fallthrough_proj->unique_ctrl_out();
     _igvn.remove_dead_node(catchnode);
-    _igvn.remove_dead_node(_callprojs.fallthrough_proj);
+    _igvn.remove_dead_node(_callprojs->fallthrough_proj);
   }
-  if (_callprojs.fallthrough_memproj != nullptr) {
-    migrate_outs(_callprojs.fallthrough_memproj, mem);
-    _igvn.remove_dead_node(_callprojs.fallthrough_memproj);
+  if (_callprojs->fallthrough_memproj != nullptr) {
+    _igvn.replace_in_uses(_callprojs->fallthrough_memproj, mem);
+    _igvn.remove_dead_node(_callprojs->fallthrough_memproj);
   }
-  if (_callprojs.fallthrough_ioproj != nullptr) {
-    migrate_outs(_callprojs.fallthrough_ioproj, i_o);
-    _igvn.remove_dead_node(_callprojs.fallthrough_ioproj);
+  if (_callprojs->fallthrough_ioproj != nullptr) {
+    _igvn.replace_in_uses(_callprojs->fallthrough_ioproj, i_o);
+    _igvn.remove_dead_node(_callprojs->fallthrough_ioproj);
   }
-  if (_callprojs.catchall_memproj != nullptr) {
-    _igvn.rehash_node_delayed(_callprojs.catchall_memproj);
-    _callprojs.catchall_memproj->set_req(0, top());
+  if (_callprojs->catchall_memproj != nullptr) {
+    _igvn.rehash_node_delayed(_callprojs->catchall_memproj);
+    _callprojs->catchall_memproj->set_req(0, top());
   }
-  if (_callprojs.catchall_ioproj != nullptr) {
-    _igvn.rehash_node_delayed(_callprojs.catchall_ioproj);
-    _callprojs.catchall_ioproj->set_req(0, top());
+  if (_callprojs->catchall_ioproj != nullptr) {
+    _igvn.rehash_node_delayed(_callprojs->catchall_ioproj);
+    _callprojs->catchall_ioproj->set_req(0, top());
   }
 #ifndef PRODUCT
   if (PrintEliminateAllocations) {
     if (alloc->is_AllocateArray()) {
       tty->print_cr("++++ Eliminated: %d AllocateArray", alloc->_idx);
@@ -1683,18 +1852,17 @@
   }
 }
 
 // Helper for PhaseMacroExpand::expand_allocate_common.
 // Initializes the newly-allocated storage.
-Node*
-PhaseMacroExpand::initialize_object(AllocateNode* alloc,
-                                    Node* control, Node* rawmem, Node* object,
-                                    Node* klass_node, Node* length,
-                                    Node* size_in_bytes) {
+Node* PhaseMacroExpand::initialize_object(AllocateNode* alloc,
+                                          Node* control, Node* rawmem, Node* object,
+                                          Node* klass_node, Node* length,
+                                          Node* size_in_bytes) {
   InitializeNode* init = alloc->initialization();
   // Store the klass & mark bits
-  Node* mark_node = alloc->make_ideal_mark(&_igvn, object, control, rawmem);
+  Node* mark_node = alloc->make_ideal_mark(&_igvn, control, rawmem);
   if (!mark_node->is_Con()) {
     transform_later(mark_node);
   }
   rawmem = make_store(control, rawmem, object, oopDesc::mark_offset_in_bytes(), mark_node, TypeX_X->basic_type());
 
@@ -1726,10 +1894,12 @@
     // there can be two Allocates to one Initialize.  The answer in all these
     // edge cases is safety first.  It is always safe to clear immediately
     // within an Allocate, and then (maybe or maybe not) clear some more later.
     if (!(UseTLAB && ZeroTLAB)) {
       rawmem = ClearArrayNode::clear_memory(control, rawmem, object,
+                                            alloc->in(AllocateNode::DefaultValue),
+                                            alloc->in(AllocateNode::RawDefaultValue),
                                             header_size, size_in_bytes,
                                             &_igvn);
     }
   } else {
     if (!init->is_complete()) {
@@ -2095,10 +2265,53 @@
       }
     }
   }
 }
 
+void PhaseMacroExpand::inline_type_guard(Node** ctrl, LockNode* lock) {
+  Node* obj = lock->obj_node();
+  const TypePtr* obj_type = _igvn.type(obj)->make_ptr();
+  if (!obj_type->can_be_inline_type()) {
+    return;
+  }
+  Node* mark = make_load(*ctrl, lock->memory(), obj, oopDesc::mark_offset_in_bytes(), TypeX_X, TypeX_X->basic_type());
+  Node* value_mask = _igvn.MakeConX(markWord::inline_type_pattern);
+  Node* is_value = _igvn.transform(new AndXNode(mark, value_mask));
+  Node* cmp = _igvn.transform(new CmpXNode(is_value, value_mask));
+  Node* bol = _igvn.transform(new BoolNode(cmp, BoolTest::eq));
+  Node* unc_ctrl = generate_slow_guard(ctrl, bol, nullptr);
+
+  int trap_request = Deoptimization::make_trap_request(Deoptimization::Reason_class_check, Deoptimization::Action_none);
+  address call_addr = SharedRuntime::uncommon_trap_blob()->entry_point();
+  const TypePtr* no_memory_effects = nullptr;
+  CallNode* unc = new CallStaticJavaNode(OptoRuntime::uncommon_trap_Type(), call_addr, "uncommon_trap",
+                                         no_memory_effects);
+  unc->init_req(TypeFunc::Control, unc_ctrl);
+  unc->init_req(TypeFunc::I_O, lock->i_o());
+  unc->init_req(TypeFunc::Memory, lock->memory());
+  unc->init_req(TypeFunc::FramePtr,  lock->in(TypeFunc::FramePtr));
+  unc->init_req(TypeFunc::ReturnAdr, lock->in(TypeFunc::ReturnAdr));
+  unc->init_req(TypeFunc::Parms+0, _igvn.intcon(trap_request));
+  unc->set_cnt(PROB_UNLIKELY_MAG(4));
+  unc->copy_call_debug_info(&_igvn, lock);
+
+  assert(unc->peek_monitor_box() == lock->box_node(), "wrong monitor");
+  assert((obj_type->is_inlinetypeptr() && unc->peek_monitor_obj()->is_SafePointScalarObject()) ||
+         (obj->is_InlineType() && obj->in(1) == unc->peek_monitor_obj()) ||
+         (obj == unc->peek_monitor_obj()), "wrong monitor");
+
+  // pop monitor and push obj back on stack: we trap before the monitorenter
+  unc->pop_monitor();
+  unc->grow_stack(unc->jvms(), 1);
+  unc->set_stack(unc->jvms(), unc->jvms()->stk_size()-1, obj);
+  _igvn.register_new_node_with_optimizer(unc);
+
+  unc_ctrl = _igvn.transform(new ProjNode(unc, TypeFunc::Control));
+  Node* halt = _igvn.transform(new HaltNode(unc_ctrl, lock->in(TypeFunc::FramePtr), "monitor enter on inline type"));
+  _igvn.add_input_to(C->root(), halt);
+}
+
 // we have determined that this lock/unlock can be eliminated, we simply
 // eliminate the node without expanding it.
 //
 // Note:  The membar's associated with the lock/unlock are currently not
 //        eliminated.  This should be investigated as a future enhancement.
@@ -2126,25 +2339,28 @@
 
   Node* mem  = alock->in(TypeFunc::Memory);
   Node* ctrl = alock->in(TypeFunc::Control);
   guarantee(ctrl != nullptr, "missing control projection, cannot replace_node() with null");
 
-  alock->extract_projections(&_callprojs, false /*separate_io_proj*/, false /*do_asserts*/);
+  _callprojs = alock->extract_projections(false /*separate_io_proj*/, false /*do_asserts*/);
   // There are 2 projections from the lock.  The lock node will
   // be deleted when its last use is subsumed below.
   assert(alock->outcnt() == 2 &&
-         _callprojs.fallthrough_proj != nullptr &&
-         _callprojs.fallthrough_memproj != nullptr,
+         _callprojs->fallthrough_proj != nullptr &&
+         _callprojs->fallthrough_memproj != nullptr,
          "Unexpected projections from Lock/Unlock");
 
-  Node* fallthroughproj = _callprojs.fallthrough_proj;
-  Node* memproj_fallthrough = _callprojs.fallthrough_memproj;
+  Node* fallthroughproj = _callprojs->fallthrough_proj;
+  Node* memproj_fallthrough = _callprojs->fallthrough_memproj;
 
   // The memory projection from a lock/unlock is RawMem
   // The input to a Lock is merged memory, so extract its RawMem input
   // (unless the MergeMem has been optimized away.)
   if (alock->is_Lock()) {
+    // Deoptimize and re-execute if object is an inline type
+    inline_type_guard(&ctrl, alock->as_Lock());
+
     // Search for MemBarAcquireLock node and delete it also.
     MemBarNode* membar = fallthroughproj->unique_ctrl_out()->as_MemBar();
     assert(membar != nullptr && membar->Opcode() == Op_MemBarAcquireLock, "");
     Node* ctrlproj = membar->proj_out(TypeFunc::Control);
     Node* memproj = membar->proj_out(TypeFunc::Memory);
@@ -2201,43 +2417,46 @@
 
   // Optimize test; set region slot 2
   slow_path = opt_bits_test(ctrl, region, 2, flock, 0, 0);
   mem_phi->init_req(2, mem);
 
+  // Deoptimize and re-execute if object is an inline type
+  inline_type_guard(&slow_path, lock);
+
   // Make slow path call
   CallNode *call = make_slow_call((CallNode *) lock, OptoRuntime::complete_monitor_enter_Type(),
                                   OptoRuntime::complete_monitor_locking_Java(), nullptr, slow_path,
                                   obj, box, nullptr);
 
-  call->extract_projections(&_callprojs, false /*separate_io_proj*/, false /*do_asserts*/);
+  _callprojs = call->extract_projections(false /*separate_io_proj*/, false /*do_asserts*/);
 
   // Slow path can only throw asynchronous exceptions, which are always
   // de-opted.  So the compiler thinks the slow-call can never throw an
   // exception.  If it DOES throw an exception we would need the debug
   // info removed first (since if it throws there is no monitor).
-  assert(_callprojs.fallthrough_ioproj == nullptr && _callprojs.catchall_ioproj == nullptr &&
-         _callprojs.catchall_memproj == nullptr && _callprojs.catchall_catchproj == nullptr, "Unexpected projection from Lock");
+  assert(_callprojs->fallthrough_ioproj == nullptr && _callprojs->catchall_ioproj == nullptr &&
+         _callprojs->catchall_memproj == nullptr && _callprojs->catchall_catchproj == nullptr, "Unexpected projection from Lock");
 
   // Capture slow path
   // disconnect fall-through projection from call and create a new one
   // hook up users of fall-through projection to region
-  Node *slow_ctrl = _callprojs.fallthrough_proj->clone();
+  Node *slow_ctrl = _callprojs->fallthrough_proj->clone();
   transform_later(slow_ctrl);
-  _igvn.hash_delete(_callprojs.fallthrough_proj);
-  _callprojs.fallthrough_proj->disconnect_inputs(C);
+  _igvn.hash_delete(_callprojs->fallthrough_proj);
+  _callprojs->fallthrough_proj->disconnect_inputs(C);
   region->init_req(1, slow_ctrl);
   // region inputs are now complete
   transform_later(region);
-  _igvn.replace_node(_callprojs.fallthrough_proj, region);
+  _igvn.replace_node(_callprojs->fallthrough_proj, region);
 
   Node *memproj = transform_later(new ProjNode(call, TypeFunc::Memory));
 
   mem_phi->init_req(1, memproj);
 
   transform_later(mem_phi);
 
-  _igvn.replace_node(_callprojs.fallthrough_memproj, mem_phi);
+  _igvn.replace_node(_callprojs->fallthrough_memproj, mem_phi);
 }
 
 //------------------------------expand_unlock_node----------------------
 void PhaseMacroExpand::expand_unlock_node(UnlockNode *unlock) {
 
@@ -2266,35 +2485,246 @@
 
   CallNode *call = make_slow_call((CallNode *) unlock, OptoRuntime::complete_monitor_exit_Type(),
                                   CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_unlocking_C),
                                   "complete_monitor_unlocking_C", slow_path, obj, box, thread);
 
-  call->extract_projections(&_callprojs, false /*separate_io_proj*/, false /*do_asserts*/);
-  assert(_callprojs.fallthrough_ioproj == nullptr && _callprojs.catchall_ioproj == nullptr &&
-         _callprojs.catchall_memproj == nullptr && _callprojs.catchall_catchproj == nullptr, "Unexpected projection from Lock");
+  _callprojs = call->extract_projections(false /*separate_io_proj*/, false /*do_asserts*/);
+  assert(_callprojs->fallthrough_ioproj == nullptr && _callprojs->catchall_ioproj == nullptr &&
+         _callprojs->catchall_memproj == nullptr && _callprojs->catchall_catchproj == nullptr, "Unexpected projection from Lock");
 
   // No exceptions for unlocking
   // Capture slow path
   // disconnect fall-through projection from call and create a new one
   // hook up users of fall-through projection to region
-  Node *slow_ctrl = _callprojs.fallthrough_proj->clone();
+  Node *slow_ctrl = _callprojs->fallthrough_proj->clone();
   transform_later(slow_ctrl);
-  _igvn.hash_delete(_callprojs.fallthrough_proj);
-  _callprojs.fallthrough_proj->disconnect_inputs(C);
+  _igvn.hash_delete(_callprojs->fallthrough_proj);
+  _callprojs->fallthrough_proj->disconnect_inputs(C);
   region->init_req(1, slow_ctrl);
   // region inputs are now complete
   transform_later(region);
-  _igvn.replace_node(_callprojs.fallthrough_proj, region);
+  _igvn.replace_node(_callprojs->fallthrough_proj, region);
 
   Node *memproj = transform_later(new ProjNode(call, TypeFunc::Memory) );
   mem_phi->init_req(1, memproj );
   mem_phi->init_req(2, mem);
   transform_later(mem_phi);
 
-  _igvn.replace_node(_callprojs.fallthrough_memproj, mem_phi);
+  _igvn.replace_node(_callprojs->fallthrough_memproj, mem_phi);
 }
 
+// An inline type might be returned from the call but we don't know its
+// type. Either we get a buffered inline type (and nothing needs to be done)
+// or one of the values being returned is the klass of the inline type
+// and we need to allocate an inline type instance of that type and
+// initialize it with other values being returned. In that case, we
+// first try a fast path allocation and initialize the value with the
+// inline klass's pack handler or we fall back to a runtime call.
+void PhaseMacroExpand::expand_mh_intrinsic_return(CallStaticJavaNode* call) {
+  assert(call->method()->is_method_handle_intrinsic(), "must be a method handle intrinsic call");
+  Node* ret = call->proj_out_or_null(TypeFunc::Parms);
+  if (ret == nullptr) {
+    return;
+  }
+  const TypeFunc* tf = call->_tf;
+  const TypeTuple* domain = OptoRuntime::store_inline_type_fields_Type()->domain_cc();
+  const TypeFunc* new_tf = TypeFunc::make(tf->domain_sig(), tf->domain_cc(), tf->range_sig(), domain);
+  call->_tf = new_tf;
+  // Make sure the change of type is applied before projections are processed by igvn
+  _igvn.set_type(call, call->Value(&_igvn));
+  _igvn.set_type(ret, ret->Value(&_igvn));
+
+  // Before any new projection is added:
+  CallProjections* projs = call->extract_projections(true, true);
+
+  // Create temporary hook nodes that will be replaced below.
+  // Add an input to prevent hook nodes from being dead.
+  Node* ctl = new Node(call);
+  Node* mem = new Node(ctl);
+  Node* io = new Node(ctl);
+  Node* ex_ctl = new Node(ctl);
+  Node* ex_mem = new Node(ctl);
+  Node* ex_io = new Node(ctl);
+  Node* res = new Node(ctl);
+
+  // Allocate a new buffered inline type only if a new one is not returned
+  Node* cast = transform_later(new CastP2XNode(ctl, res));
+  Node* mask = MakeConX(0x1);
+  Node* masked = transform_later(new AndXNode(cast, mask));
+  Node* cmp = transform_later(new CmpXNode(masked, mask));
+  Node* bol = transform_later(new BoolNode(cmp, BoolTest::eq));
+  IfNode* allocation_iff = new IfNode(ctl, bol, PROB_MAX, COUNT_UNKNOWN);
+  transform_later(allocation_iff);
+  Node* allocation_ctl = transform_later(new IfTrueNode(allocation_iff));
+  Node* no_allocation_ctl = transform_later(new IfFalseNode(allocation_iff));
+  Node* no_allocation_res = transform_later(new CheckCastPPNode(no_allocation_ctl, res, TypeInstPtr::BOTTOM));
+
+  // Try to allocate a new buffered inline instance either from TLAB or eden space
+  Node* needgc_ctrl = nullptr; // needgc means slowcase, i.e. allocation failed
+  CallLeafNoFPNode* handler_call;
+  const bool alloc_in_place = UseTLAB;
+  if (alloc_in_place) {
+    Node* fast_oop_ctrl = nullptr;
+    Node* fast_oop_rawmem = nullptr;
+    Node* mask2 = MakeConX(-2);
+    Node* masked2 = transform_later(new AndXNode(cast, mask2));
+    Node* rawklassptr = transform_later(new CastX2PNode(masked2));
+    Node* klass_node = transform_later(new CheckCastPPNode(allocation_ctl, rawklassptr, TypeInstKlassPtr::OBJECT_OR_NULL));
+    Node* layout_val = make_load(nullptr, mem, klass_node, in_bytes(Klass::layout_helper_offset()), TypeInt::INT, T_INT);
+    Node* size_in_bytes = ConvI2X(layout_val);
+    BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
+    Node* fast_oop = bs->obj_allocate(this, mem, allocation_ctl, size_in_bytes, io, needgc_ctrl,
+                                      fast_oop_ctrl, fast_oop_rawmem,
+                                      AllocateInstancePrefetchLines);
+    // Allocation succeed, initialize buffered inline instance header firstly,
+    // and then initialize its fields with an inline class specific handler
+    Node* mark_node = makecon(TypeRawPtr::make((address)markWord::inline_type_prototype().value()));
+    fast_oop_rawmem = make_store(fast_oop_ctrl, fast_oop_rawmem, fast_oop, oopDesc::mark_offset_in_bytes(), mark_node, T_ADDRESS);
+    fast_oop_rawmem = make_store(fast_oop_ctrl, fast_oop_rawmem, fast_oop, oopDesc::klass_offset_in_bytes(), klass_node, T_METADATA);
+    if (UseCompressedClassPointers) {
+      fast_oop_rawmem = make_store(fast_oop_ctrl, fast_oop_rawmem, fast_oop, oopDesc::klass_gap_offset_in_bytes(), intcon(0), T_INT);
+    }
+    Node* fixed_block  = make_load(fast_oop_ctrl, fast_oop_rawmem, klass_node, in_bytes(InstanceKlass::adr_inlineklass_fixed_block_offset()), TypeRawPtr::BOTTOM, T_ADDRESS);
+    Node* pack_handler = make_load(fast_oop_ctrl, fast_oop_rawmem, fixed_block, in_bytes(InlineKlass::pack_handler_offset()), TypeRawPtr::BOTTOM, T_ADDRESS);
+    handler_call = new CallLeafNoFPNode(OptoRuntime::pack_inline_type_Type(),
+                                        nullptr,
+                                        "pack handler",
+                                        TypeRawPtr::BOTTOM);
+    handler_call->init_req(TypeFunc::Control, fast_oop_ctrl);
+    handler_call->init_req(TypeFunc::Memory, fast_oop_rawmem);
+    handler_call->init_req(TypeFunc::I_O, top());
+    handler_call->init_req(TypeFunc::FramePtr, call->in(TypeFunc::FramePtr));
+    handler_call->init_req(TypeFunc::ReturnAdr, top());
+    handler_call->init_req(TypeFunc::Parms, pack_handler);
+    handler_call->init_req(TypeFunc::Parms+1, fast_oop);
+  } else {
+    needgc_ctrl = allocation_ctl;
+  }
+
+  // Allocation failed, fall back to a runtime call
+  CallStaticJavaNode* slow_call = new CallStaticJavaNode(OptoRuntime::store_inline_type_fields_Type(),
+                                                         StubRoutines::store_inline_type_fields_to_buf(),
+                                                         "store_inline_type_fields",
+                                                         TypePtr::BOTTOM);
+  slow_call->init_req(TypeFunc::Control, needgc_ctrl);
+  slow_call->init_req(TypeFunc::Memory, mem);
+  slow_call->init_req(TypeFunc::I_O, io);
+  slow_call->init_req(TypeFunc::FramePtr, call->in(TypeFunc::FramePtr));
+  slow_call->init_req(TypeFunc::ReturnAdr, call->in(TypeFunc::ReturnAdr));
+  slow_call->init_req(TypeFunc::Parms, res);
+
+  Node* slow_ctl = transform_later(new ProjNode(slow_call, TypeFunc::Control));
+  Node* slow_mem = transform_later(new ProjNode(slow_call, TypeFunc::Memory));
+  Node* slow_io = transform_later(new ProjNode(slow_call, TypeFunc::I_O));
+  Node* slow_res = transform_later(new ProjNode(slow_call, TypeFunc::Parms));
+  Node* slow_catc = transform_later(new CatchNode(slow_ctl, slow_io, 2));
+  Node* slow_norm = transform_later(new CatchProjNode(slow_catc, CatchProjNode::fall_through_index, CatchProjNode::no_handler_bci));
+  Node* slow_excp = transform_later(new CatchProjNode(slow_catc, CatchProjNode::catch_all_index,    CatchProjNode::no_handler_bci));
+
+  Node* ex_r = new RegionNode(3);
+  Node* ex_mem_phi = new PhiNode(ex_r, Type::MEMORY, TypePtr::BOTTOM);
+  Node* ex_io_phi = new PhiNode(ex_r, Type::ABIO);
+  ex_r->init_req(1, slow_excp);
+  ex_mem_phi->init_req(1, slow_mem);
+  ex_io_phi->init_req(1, slow_io);
+  ex_r->init_req(2, ex_ctl);
+  ex_mem_phi->init_req(2, ex_mem);
+  ex_io_phi->init_req(2, ex_io);
+  transform_later(ex_r);
+  transform_later(ex_mem_phi);
+  transform_later(ex_io_phi);
+
+  // We don't know how many values are returned. This assumes the
+  // worst case, that all available registers are used.
+  for (uint i = TypeFunc::Parms+1; i < domain->cnt(); i++) {
+    if (domain->field_at(i) == Type::HALF) {
+      slow_call->init_req(i, top());
+      if (alloc_in_place) {
+        handler_call->init_req(i+1, top());
+      }
+      continue;
+    }
+    Node* proj = transform_later(new ProjNode(call, i));
+    slow_call->init_req(i, proj);
+    if (alloc_in_place) {
+      handler_call->init_req(i+1, proj);
+    }
+  }
+  // We can safepoint at that new call
+  slow_call->copy_call_debug_info(&_igvn, call);
+  transform_later(slow_call);
+  if (alloc_in_place) {
+    transform_later(handler_call);
+  }
+
+  Node* fast_ctl = nullptr;
+  Node* fast_res = nullptr;
+  MergeMemNode* fast_mem = nullptr;
+  if (alloc_in_place) {
+    fast_ctl = transform_later(new ProjNode(handler_call, TypeFunc::Control));
+    Node* rawmem = transform_later(new ProjNode(handler_call, TypeFunc::Memory));
+    fast_res = transform_later(new ProjNode(handler_call, TypeFunc::Parms));
+    fast_mem = MergeMemNode::make(mem);
+    fast_mem->set_memory_at(Compile::AliasIdxRaw, rawmem);
+    transform_later(fast_mem);
+  }
+
+  Node* r = new RegionNode(alloc_in_place ? 4 : 3);
+  Node* mem_phi = new PhiNode(r, Type::MEMORY, TypePtr::BOTTOM);
+  Node* io_phi = new PhiNode(r, Type::ABIO);
+  Node* res_phi = new PhiNode(r, TypeInstPtr::BOTTOM);
+  r->init_req(1, no_allocation_ctl);
+  mem_phi->init_req(1, mem);
+  io_phi->init_req(1, io);
+  res_phi->init_req(1, no_allocation_res);
+  r->init_req(2, slow_norm);
+  mem_phi->init_req(2, slow_mem);
+  io_phi->init_req(2, slow_io);
+  res_phi->init_req(2, slow_res);
+  if (alloc_in_place) {
+    r->init_req(3, fast_ctl);
+    mem_phi->init_req(3, fast_mem);
+    io_phi->init_req(3, io);
+    res_phi->init_req(3, fast_res);
+  }
+  transform_later(r);
+  transform_later(mem_phi);
+  transform_later(io_phi);
+  transform_later(res_phi);
+
+  // Do not let stores that initialize this buffer be reordered with a subsequent
+  // store that would make this buffer accessible by other threads.
+  MemBarNode* mb = MemBarNode::make(C, Op_MemBarStoreStore, Compile::AliasIdxBot);
+  transform_later(mb);
+  mb->init_req(TypeFunc::Memory, mem_phi);
+  mb->init_req(TypeFunc::Control, r);
+  r = new ProjNode(mb, TypeFunc::Control);
+  transform_later(r);
+  mem_phi = new ProjNode(mb, TypeFunc::Memory);
+  transform_later(mem_phi);
+
+  assert(projs->nb_resproj == 1, "unexpected number of results");
+  _igvn.replace_in_uses(projs->fallthrough_catchproj, r);
+  _igvn.replace_in_uses(projs->fallthrough_memproj, mem_phi);
+  _igvn.replace_in_uses(projs->fallthrough_ioproj, io_phi);
+  _igvn.replace_in_uses(projs->resproj[0], res_phi);
+  _igvn.replace_in_uses(projs->catchall_catchproj, ex_r);
+  _igvn.replace_in_uses(projs->catchall_memproj, ex_mem_phi);
+  _igvn.replace_in_uses(projs->catchall_ioproj, ex_io_phi);
+  // The CatchNode should not use the ex_io_phi. Re-connect it to the catchall_ioproj.
+  Node* cn = projs->fallthrough_catchproj->in(0);
+  _igvn.replace_input_of(cn, 1, projs->catchall_ioproj);
+
+  _igvn.replace_node(ctl, projs->fallthrough_catchproj);
+  _igvn.replace_node(mem, projs->fallthrough_memproj);
+  _igvn.replace_node(io, projs->fallthrough_ioproj);
+  _igvn.replace_node(res, projs->resproj[0]);
+  _igvn.replace_node(ex_ctl, projs->catchall_catchproj);
+  _igvn.replace_node(ex_mem, projs->catchall_memproj);
+  _igvn.replace_node(ex_io, projs->catchall_ioproj);
+ }
+
 void PhaseMacroExpand::expand_subtypecheck_node(SubTypeCheckNode *check) {
   assert(check->in(SubTypeCheckNode::Control) == nullptr, "should be pinned");
   Node* bol = check->unique_out();
   Node* obj_or_subklass = check->in(SubTypeCheckNode::ObjOrSubKlass);
   Node* superklass = check->in(SubTypeCheckNode::SuperKlass);
@@ -2316,11 +2746,11 @@
     Node* subklass = nullptr;
     if (_igvn.type(obj_or_subklass)->isa_klassptr()) {
       subklass = obj_or_subklass;
     } else {
       Node* k_adr = basic_plus_adr(obj_or_subklass, oopDesc::klass_offset_in_bytes());
-      subklass = _igvn.transform(LoadKlassNode::make(_igvn, nullptr, C->immutable_memory(), k_adr, TypeInstPtr::KLASS));
+      subklass = _igvn.transform(LoadKlassNode::make(_igvn, nullptr, C->immutable_memory(), k_adr, TypeInstPtr::KLASS, TypeInstKlassPtr::OBJECT));
     }
 
     Node* not_subtype_ctrl = Phase::gen_subtype_check(subklass, superklass, &ctrl, nullptr, _igvn, check->method(), check->bci());
 
     _igvn.replace_input_of(iff, 0, C->top());
@@ -2328,10 +2758,123 @@
     _igvn.replace_node(iffalse, ctrl);
   }
   _igvn.replace_node(check, C->top());
 }
 
+// FlatArrayCheckNode (array1 array2 ...) is expanded into:
+//
+// long mark = array1.mark | array2.mark | ...;
+// long locked_bit = markWord::unlocked_value & array1.mark & array2.mark & ...;
+// if (locked_bit == 0) {
+//   // One array is locked, load prototype header from the klass
+//   mark = array1.klass.proto | array2.klass.proto | ...
+// }
+// if ((mark & markWord::flat_array_bit_in_place) == 0) {
+//    ...
+// }
+void PhaseMacroExpand::expand_flatarraycheck_node(FlatArrayCheckNode* check) {
+  bool array_inputs = _igvn.type(check->in(FlatArrayCheckNode::ArrayOrKlass))->isa_oopptr() != nullptr;
+  if (UseArrayMarkWordCheck && array_inputs) {
+    Node* mark = MakeConX(0);
+    Node* locked_bit = MakeConX(markWord::unlocked_value);
+    Node* mem = check->in(FlatArrayCheckNode::Memory);
+    for (uint i = FlatArrayCheckNode::ArrayOrKlass; i < check->req(); ++i) {
+      Node* ary = check->in(i);
+      const TypeOopPtr* t = _igvn.type(ary)->isa_oopptr();
+      assert(t != nullptr, "Mixing array and klass inputs");
+      assert(!t->is_flat() && !t->is_not_flat(), "Should have been optimized out");
+      Node* mark_adr = basic_plus_adr(ary, oopDesc::mark_offset_in_bytes());
+      Node* mark_load = _igvn.transform(LoadNode::make(_igvn, nullptr, mem, mark_adr, mark_adr->bottom_type()->is_ptr(), TypeX_X, TypeX_X->basic_type(), MemNode::unordered));
+      mark = _igvn.transform(new OrXNode(mark, mark_load));
+      locked_bit = _igvn.transform(new AndXNode(locked_bit, mark_load));
+    }
+    assert(!mark->is_Con(), "Should have been optimized out");
+    Node* cmp = _igvn.transform(new CmpXNode(locked_bit, MakeConX(0)));
+    Node* is_unlocked = _igvn.transform(new BoolNode(cmp, BoolTest::ne));
+
+    // BoolNode might be shared, replace each if user
+    Node* old_bol = check->unique_out();
+    assert(old_bol->is_Bool() && old_bol->as_Bool()->_test._test == BoolTest::ne, "unexpected condition");
+    for (DUIterator_Last imin, i = old_bol->last_outs(imin); i >= imin; --i) {
+      IfNode* old_iff = old_bol->last_out(i)->as_If();
+      Node* ctrl = old_iff->in(0);
+      RegionNode* region = new RegionNode(3);
+      Node* mark_phi = new PhiNode(region, TypeX_X);
+
+      // Check if array is unlocked
+      IfNode* iff = _igvn.transform(new IfNode(ctrl, is_unlocked, PROB_MAX, COUNT_UNKNOWN))->as_If();
+
+      // Unlocked: Use bits from mark word
+      region->init_req(1, _igvn.transform(new IfTrueNode(iff)));
+      mark_phi->init_req(1, mark);
+
+      // Locked: Load prototype header from klass
+      ctrl = _igvn.transform(new IfFalseNode(iff));
+      Node* proto = MakeConX(0);
+      for (uint i = FlatArrayCheckNode::ArrayOrKlass; i < check->req(); ++i) {
+        Node* ary = check->in(i);
+        // Make loads control dependent to make sure they are only executed if array is locked
+        Node* klass_adr = basic_plus_adr(ary, oopDesc::klass_offset_in_bytes());
+        Node* klass = _igvn.transform(LoadKlassNode::make(_igvn, ctrl, C->immutable_memory(), klass_adr, TypeInstPtr::KLASS, TypeInstKlassPtr::OBJECT));
+        Node* proto_adr = basic_plus_adr(klass, in_bytes(Klass::prototype_header_offset()));
+        Node* proto_load = _igvn.transform(LoadNode::make(_igvn, ctrl, C->immutable_memory(), proto_adr, proto_adr->bottom_type()->is_ptr(), TypeX_X, TypeX_X->basic_type(), MemNode::unordered));
+        proto = _igvn.transform(new OrXNode(proto, proto_load));
+      }
+      region->init_req(2, ctrl);
+      mark_phi->init_req(2, proto);
+
+      // Check if flat array bits are set
+      Node* mask = MakeConX(markWord::flat_array_bit_in_place);
+      Node* masked = _igvn.transform(new AndXNode(_igvn.transform(mark_phi), mask));
+      cmp = _igvn.transform(new CmpXNode(masked, MakeConX(0)));
+      Node* is_not_flat = _igvn.transform(new BoolNode(cmp, BoolTest::eq));
+
+      ctrl = _igvn.transform(region);
+      iff = _igvn.transform(new IfNode(ctrl, is_not_flat, PROB_MAX, COUNT_UNKNOWN))->as_If();
+      _igvn.replace_node(old_iff, iff);
+    }
+    _igvn.replace_node(check, C->top());
+  } else {
+    // Fall back to layout helper check
+    Node* lhs = intcon(0);
+    for (uint i = FlatArrayCheckNode::ArrayOrKlass; i < check->req(); ++i) {
+      Node* array_or_klass = check->in(i);
+      Node* klass = nullptr;
+      const TypePtr* t = _igvn.type(array_or_klass)->is_ptr();
+      assert(!t->is_flat() && !t->is_not_flat(), "Should have been optimized out");
+      if (t->isa_oopptr() != nullptr) {
+        Node* klass_adr = basic_plus_adr(array_or_klass, oopDesc::klass_offset_in_bytes());
+        klass = transform_later(LoadKlassNode::make(_igvn, nullptr, C->immutable_memory(), klass_adr, TypeInstPtr::KLASS, TypeInstKlassPtr::OBJECT));
+      } else {
+        assert(t->isa_klassptr(), "Unexpected input type");
+        klass = array_or_klass;
+      }
+      Node* lh_addr = basic_plus_adr(klass, in_bytes(Klass::layout_helper_offset()));
+      Node* lh_val = _igvn.transform(LoadNode::make(_igvn, nullptr, C->immutable_memory(), lh_addr, lh_addr->bottom_type()->is_ptr(), TypeInt::INT, T_INT, MemNode::unordered));
+      lhs = _igvn.transform(new OrINode(lhs, lh_val));
+    }
+    Node* masked = transform_later(new AndINode(lhs, intcon(Klass::_lh_array_tag_flat_value_bit_inplace)));
+    Node* cmp = transform_later(new CmpINode(masked, intcon(0)));
+    Node* bol = transform_later(new BoolNode(cmp, BoolTest::eq));
+    Node* m2b = transform_later(new Conv2BNode(masked));
+    // The matcher expects the input to If nodes to be produced by a Bool(CmpI..)
+    // pattern, but the input to other potential users (e.g. Phi) to be some
+    // other pattern (e.g. a Conv2B node, possibly idealized as a CMoveI).
+    Node* old_bol = check->unique_out();
+    for (DUIterator_Last imin, i = old_bol->last_outs(imin); i >= imin; --i) {
+      Node* user = old_bol->last_out(i);
+      for (uint j = 0; j < user->req(); j++) {
+        Node* n = user->in(j);
+        if (n == old_bol) {
+          _igvn.replace_input_of(user, j, user->is_If() ? bol : m2b);
+        }
+      }
+    }
+    _igvn.replace_node(check, C->top());
+  }
+}
+
 //---------------------------eliminate_macro_nodes----------------------
 // Eliminate scalar replaced allocations and associated locks.
 void PhaseMacroExpand::eliminate_macro_nodes() {
   if (C->macro_count() == 0)
     return;
@@ -2388,13 +2931,17 @@
         if (success && PrintOptoStatistics) {
           Atomic::inc(&PhaseMacroExpand::_objs_scalar_replaced_counter);
         }
 #endif
         break;
-      case Node::Class_CallStaticJava:
-        success = eliminate_boxing_node(n->as_CallStaticJava());
+      case Node::Class_CallStaticJava: {
+        CallStaticJavaNode* call = n->as_CallStaticJava();
+        if (!call->method()->is_method_handle_intrinsic()) {
+          success = eliminate_boxing_node(n->as_CallStaticJava());
+        }
         break;
+      }
       case Node::Class_Lock:
       case Node::Class_Unlock:
         assert(!n->as_AbstractLock()->is_eliminated(), "sanity");
         _has_locks = true;
         break;
@@ -2404,10 +2951,12 @@
         break;
       case Node::Class_SubTypeCheck:
         break;
       case Node::Class_Opaque1:
         break;
+      case Node::Class_FlatArrayCheck:
+        break;
       default:
         assert(n->Opcode() == Op_LoopLimit ||
                n->Opcode() == Op_Opaque3   ||
                n->Opcode() == Op_Opaque4   ||
                n->Opcode() == Op_MaxL      ||
@@ -2446,14 +2995,17 @@
         // Remove it from macro list and put on IGVN worklist to optimize.
         C->remove_macro_node(n);
         _igvn._worklist.push(n);
         success = true;
       } else if (n->Opcode() == Op_CallStaticJava) {
-        // Remove it from macro list and put on IGVN worklist to optimize.
-        C->remove_macro_node(n);
-        _igvn._worklist.push(n);
-        success = true;
+        CallStaticJavaNode* call = n->as_CallStaticJava();
+        if (!call->method()->is_method_handle_intrinsic()) {
+          // Remove it from macro list and put on IGVN worklist to optimize.
+          C->remove_macro_node(n);
+          _igvn._worklist.push(n);
+          success = true;
+        }
       } else if (n->is_Opaque1()) {
         _igvn.replace_node(n, n->in(1));
         success = true;
 #if INCLUDE_RTM_OPT
       } else if ((n->Opcode() == Op_Opaque3) && ((Opaque3Node*)n)->rtm_opt()) {
@@ -2564,10 +3116,17 @@
       expand_arraycopy_node(n->as_ArrayCopy());
       break;
     case Node::Class_SubTypeCheck:
       expand_subtypecheck_node(n->as_SubTypeCheck());
       break;
+    case Node::Class_CallStaticJava:
+      expand_mh_intrinsic_return(n->as_CallStaticJava());
+      C->remove_macro_node(n);
+      break;
+    case Node::Class_FlatArrayCheck:
+      expand_flatarraycheck_node(n->as_FlatArrayCheck());
+      break;
     default:
       assert(false, "unknown node type in macro list");
     }
     assert(C->macro_count() == (old_macro_count - 1), "expansion must have deleted one node from macro list");
     if (C->failing())  return true;
diff a/src/hotspot/share/opto/macro.hpp b/src/hotspot/share/opto/macro.hpp
--- a/src/hotspot/share/opto/macro.hpp
+++ b/src/hotspot/share/opto/macro.hpp
@@ -79,11 +79,11 @@
                               const char* &name,
                               bool dest_uninitialized);
 
 private:
   // projections extracted from a call node
-  CallProjections _callprojs;
+  CallProjections* _callprojs;
 
   // Additional data collected during macro expansion
   bool _has_locks;
 
   void expand_allocate(AllocateNode *alloc);
@@ -94,57 +94,68 @@
                               address slow_call_address,
                               Node* valid_length_test);
   void yank_alloc_node(AllocateNode* alloc);
   Node *value_from_mem(Node *mem, Node *ctl, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, AllocateNode *alloc);
   Node *value_from_mem_phi(Node *mem, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, AllocateNode *alloc, Node_Stack *value_phis, int level);
+  Node* inline_type_from_mem(Node* mem, Node* ctl, ciInlineKlass* vk, const TypeAryPtr* adr_type, int offset, AllocateNode* alloc);
 
   bool eliminate_boxing_node(CallStaticJavaNode *boxing);
   bool eliminate_allocate_node(AllocateNode *alloc);
   void undo_previous_scalarizations(GrowableArray <SafePointNode *> safepoints_done, AllocateNode* alloc);
   bool scalar_replacement(AllocateNode *alloc, GrowableArray <SafePointNode *>& safepoints);
-  void process_users_of_allocation(CallNode *alloc);
+  void process_users_of_allocation(CallNode *alloc, bool inline_alloc = false);
 
   void eliminate_gc_barrier(Node *p2x);
   void mark_eliminated_box(Node* box, Node* obj);
   void mark_eliminated_locking_nodes(AbstractLockNode *alock);
   bool eliminate_locking_node(AbstractLockNode *alock);
   void expand_lock_node(LockNode *lock);
+  void inline_type_guard(Node** ctrl, LockNode* lock);
   void expand_unlock_node(UnlockNode *unlock);
+  void expand_mh_intrinsic_return(CallStaticJavaNode* call);
 
   // More helper methods modeled after GraphKit for array copy
   void insert_mem_bar(Node** ctrl, Node** mem, int opcode, Node* precedent = nullptr);
   Node* array_element_address(Node* ary, Node* idx, BasicType elembt);
   Node* ConvI2L(Node* offset);
 
   // helper methods modeled after LibraryCallKit for array copy
   Node* generate_guard(Node** ctrl, Node* test, RegionNode* region, float true_prob);
   Node* generate_slow_guard(Node** ctrl, Node* test, RegionNode* region);
+  Node* generate_fair_guard(Node** ctrl, Node* test, RegionNode* region);
 
   void generate_partial_inlining_block(Node** ctrl, MergeMemNode** mem, const TypePtr* adr_type,
                                        RegionNode** exit_block, Node** result_memory, Node* length,
                                        Node* src_start, Node* dst_start, BasicType type);
 
   void generate_negative_guard(Node** ctrl, Node* index, RegionNode* region);
   void generate_limit_guard(Node** ctrl, Node* offset, Node* subseq_length, Node* array_length, RegionNode* region);
 
   // More helper methods for array copy
   Node* generate_nonpositive_guard(Node** ctrl, Node* index, bool never_negative);
+  Node* array_lh_test(Node* array, jint mask);
+  Node* generate_flat_array_guard(Node** ctrl, Node* array, RegionNode* region);
+  Node* generate_null_free_array_guard(Node** ctrl, Node* array, RegionNode* region);
+
   void finish_arraycopy_call(Node* call, Node** ctrl, MergeMemNode** mem, const TypePtr* adr_type);
   Node* generate_arraycopy(ArrayCopyNode *ac,
                            AllocateArrayNode* alloc,
                            Node** ctrl, MergeMemNode* mem, Node** io,
                            const TypePtr* adr_type,
                            BasicType basic_elem_type,
                            Node* src,  Node* src_offset,
                            Node* dest, Node* dest_offset,
                            Node* copy_length,
+                           Node* dest_length,
                            bool disjoint_bases = false,
                            bool length_never_negative = false,
                            RegionNode* slow_region = nullptr);
   void generate_clear_array(Node* ctrl, MergeMemNode* merge_mem,
                             const TypePtr* adr_type,
                             Node* dest,
+                            Node* val,
+                            Node* raw_val,
                             BasicType basic_elem_type,
                             Node* slice_idx,
                             Node* slice_len,
                             Node* dest_size);
   bool generate_block_arraycopy(Node** ctrl, MergeMemNode** mem, Node* io,
@@ -176,15 +187,19 @@
                                     BasicType basic_elem_type,
                                     bool disjoint_bases,
                                     Node* src,  Node* src_offset,
                                     Node* dest, Node* dest_offset,
                                     Node* copy_length, bool dest_uninitialized);
-
+  const TypePtr* adjust_for_flat_array(const TypeAryPtr* top_dest, Node*& src_offset,
+                                       Node*& dest_offset, Node*& length, BasicType& dest_elem,
+                                       Node*& dest_length);
   void expand_arraycopy_node(ArrayCopyNode *ac);
 
   void expand_subtypecheck_node(SubTypeCheckNode *check);
 
+  void expand_flatarraycheck_node(FlatArrayCheckNode* check);
+
   int replace_input(Node *use, Node *oldref, Node *newref);
   void migrate_outs(Node *old, Node *target);
   Node* opt_bits_test(Node* ctrl, Node* region, int edge, Node* word, int mask, int bits, bool return_fast_path = false);
   void copy_predefined_input_for_runtime_call(Node * ctrl, CallNode* oldcall, CallNode* call);
   CallNode* make_slow_call(CallNode *oldcall, const TypeFunc* slow_call_type, address slow_call,
@@ -203,11 +218,11 @@
     _igvn.set_delay_transform(true);
   }
   void eliminate_macro_nodes();
   bool expand_macro_nodes();
 
-  SafePointScalarObjectNode* create_scalarized_object_description(AllocateNode *alloc, SafePointNode* sfpt);
+  SafePointScalarObjectNode* create_scalarized_object_description(AllocateNode *alloc, SafePointNode* sfpt, Unique_Node_List* value_worklist);
   static bool can_eliminate_allocation(PhaseIterGVN *igvn, AllocateNode *alloc, GrowableArray <SafePointNode *> *safepoints);
 
 
   PhaseIterGVN &igvn() const { return _igvn; }
 
diff a/src/hotspot/share/opto/macroArrayCopy.cpp b/src/hotspot/share/opto/macroArrayCopy.cpp
--- a/src/hotspot/share/opto/macroArrayCopy.cpp
+++ b/src/hotspot/share/opto/macroArrayCopy.cpp
@@ -21,10 +21,11 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "ci/ciFlatArrayKlass.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/tlab_globals.hpp"
 #include "opto/arraycopynode.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "opto/convertnode.hpp"
@@ -137,14 +138,18 @@
   *ctrl = if_fast;
 
   return if_slow;
 }
 
-inline Node* PhaseMacroExpand::generate_slow_guard(Node** ctrl, Node* test, RegionNode* region) {
+Node* PhaseMacroExpand::generate_slow_guard(Node** ctrl, Node* test, RegionNode* region) {
   return generate_guard(ctrl, test, region, PROB_UNLIKELY_MAG(3));
 }
 
+inline Node* PhaseMacroExpand::generate_fair_guard(Node** ctrl, Node* test, RegionNode* region) {
+  return generate_guard(ctrl, test, region, PROB_FAIR);
+}
+
 void PhaseMacroExpand::generate_negative_guard(Node** ctrl, Node* index, RegionNode* region) {
   if ((*ctrl)->is_top())
     return;                // already stopped
   if (_igvn.type(index)->higher_equal(TypeInt::POS)) // [0,maxint]
     return;                // index is already adequately typed
@@ -281,10 +286,30 @@
   Node* is_notp = generate_guard(ctrl, bol_le, nullptr, PROB_MIN);
 
   return is_notp;
 }
 
+Node* PhaseMacroExpand::array_lh_test(Node* array, jint mask) {
+  Node* klass_adr = basic_plus_adr(array, oopDesc::klass_offset_in_bytes());
+  Node* klass = transform_later(LoadKlassNode::make(_igvn, nullptr, C->immutable_memory(), klass_adr, TypeInstPtr::KLASS, TypeInstKlassPtr::OBJECT));
+  Node* lh_addr = basic_plus_adr(klass, in_bytes(Klass::layout_helper_offset()));
+  Node* lh_val = _igvn.transform(LoadNode::make(_igvn, nullptr, C->immutable_memory(), lh_addr, lh_addr->bottom_type()->is_ptr(), TypeInt::INT, T_INT, MemNode::unordered));
+  Node* masked = transform_later(new AndINode(lh_val, intcon(mask)));
+  Node* cmp = transform_later(new CmpINode(masked, intcon(0)));
+  return transform_later(new BoolNode(cmp, BoolTest::ne));
+}
+
+Node* PhaseMacroExpand::generate_flat_array_guard(Node** ctrl, Node* array, RegionNode* region) {
+  assert(UseFlatArray, "can never be flat");
+  return generate_fair_guard(ctrl, array_lh_test(array, Klass::_lh_array_tag_flat_value_bit_inplace), region);
+}
+
+Node* PhaseMacroExpand::generate_null_free_array_guard(Node** ctrl, Node* array, RegionNode* region) {
+  assert(EnableValhalla, "can never be null free");
+  return generate_fair_guard(ctrl, array_lh_test(array, Klass::_lh_null_free_array_bit_inplace), region);
+}
+
 void PhaseMacroExpand::finish_arraycopy_call(Node* call, Node** ctrl, MergeMemNode** mem, const TypePtr* adr_type) {
   transform_later(call);
 
   *ctrl = new ProjNode(call,TypeFunc::Control);
   transform_later(*ctrl);
@@ -375,10 +400,11 @@
                                            const TypePtr* adr_type,
                                            BasicType basic_elem_type,
                                            Node* src,  Node* src_offset,
                                            Node* dest, Node* dest_offset,
                                            Node* copy_length,
+                                           Node* dest_length,
                                            bool disjoint_bases,
                                            bool length_never_negative,
                                            RegionNode* slow_region) {
   if (slow_region == nullptr) {
     slow_region = new RegionNode(1);
@@ -386,10 +412,12 @@
   }
 
   Node* original_dest = dest;
   bool  dest_needs_zeroing   = false;
   bool  acopy_to_uninitialized = false;
+  Node* default_value = nullptr;
+  Node* raw_default_value = nullptr;
 
   // See if this is the initialization of a newly-allocated array.
   // If so, we will take responsibility here for initializing it to zero.
   // (Note:  Because tightly_coupled_allocation performs checks on the
   // out-edges of the dest, we need to avoid making derived pointers
@@ -416,10 +444,12 @@
       // From this point on, every exit path is responsible for
       // initializing any non-copied parts of the object to zero.
       // Also, if this flag is set we make sure that arraycopy interacts properly
       // with G1, eliding pre-barriers. See CR 6627983.
       dest_needs_zeroing = true;
+      default_value = alloc->in(AllocateNode::DefaultValue);
+      raw_default_value = alloc->in(AllocateNode::RawDefaultValue);
     } else {
       // dest_need_zeroing = false;
     }
   } else {
     // No zeroing elimination needed here.
@@ -485,18 +515,19 @@
     }
 
     // copy_length is 0.
     if (dest_needs_zeroing) {
       assert(!local_ctrl->is_top(), "no ctrl?");
-      Node* dest_length = alloc->in(AllocateNode::ALength);
       if (copy_length->eqv_uncast(dest_length)
           || _igvn.find_int_con(dest_length, 1) <= 0) {
         // There is no zeroing to do. No need for a secondary raw memory barrier.
       } else {
         // Clear the whole thing since there are no source elements to copy.
         generate_clear_array(local_ctrl, local_mem,
-                             adr_type, dest, basic_elem_type,
+                             adr_type, dest,
+                             default_value, raw_default_value,
+                             basic_elem_type,
                              intcon(0), nullptr,
                              alloc->in(AllocateNode::AllocSize));
         // Use a secondary InitializeNode as raw memory barrier.
         // Currently it is needed only on this path since other
         // paths have stub or runtime calls as raw memory barriers.
@@ -523,17 +554,18 @@
   if (!(*ctrl)->is_top() && dest_needs_zeroing) {
     // We have to initialize the *uncopied* part of the array to zero.
     // The copy destination is the slice dest[off..off+len].  The other slices
     // are dest_head = dest[0..off] and dest_tail = dest[off+len..dest.length].
     Node* dest_size   = alloc->in(AllocateNode::AllocSize);
-    Node* dest_length = alloc->in(AllocateNode::ALength);
     Node* dest_tail   = transform_later( new AddINode(dest_offset, copy_length));
 
     // If there is a head section that needs zeroing, do it now.
     if (_igvn.find_int_con(dest_offset, -1) != 0) {
       generate_clear_array(*ctrl, mem,
-                           adr_type, dest, basic_elem_type,
+                           adr_type, dest,
+                           default_value, raw_default_value,
+                           basic_elem_type,
                            intcon(0), dest_offset,
                            nullptr);
     }
 
     // Next, perform a dynamic check on the tail length.
@@ -578,21 +610,25 @@
     if (tail_ctl != nullptr) {
       Node* notail_ctl = (*ctrl)->is_top() ? nullptr : *ctrl;
       *ctrl = tail_ctl;
       if (notail_ctl == nullptr) {
         generate_clear_array(*ctrl, mem,
-                             adr_type, dest, basic_elem_type,
+                             adr_type, dest,
+                             default_value, raw_default_value,
+                             basic_elem_type,
                              dest_tail, nullptr,
                              dest_size);
       } else {
         // Make a local merge.
         Node* done_ctl = transform_later(new RegionNode(3));
         Node* done_mem = transform_later(new PhiNode(done_ctl, Type::MEMORY, adr_type));
         done_ctl->init_req(1, notail_ctl);
         done_mem->init_req(1, mem->memory_at(alias_idx));
         generate_clear_array(*ctrl, mem,
-                             adr_type, dest, basic_elem_type,
+                             adr_type, dest,
+                             default_value, raw_default_value,
+                             basic_elem_type,
                              dest_tail, nullptr,
                              dest_size);
         done_ctl->init_req(2, *ctrl);
         done_mem->init_req(2, mem->memory_at(alias_idx));
         *ctrl = done_ctl;
@@ -766,11 +802,13 @@
     // Generate the slow path, if needed.
     local_mem->set_memory_at(alias_idx, slow_mem);
 
     if (dest_needs_zeroing) {
       generate_clear_array(local_ctrl, local_mem,
-                           adr_type, dest, basic_elem_type,
+                           adr_type, dest,
+                           default_value, raw_default_value,
+                           basic_elem_type,
                            intcon(0), nullptr,
                            alloc->in(AllocateNode::AllocSize));
     }
 
     local_mem = generate_slow_arraycopy(ac,
@@ -828,15 +866,15 @@
     assert((*ctrl)->is_Proj(), "MemBar control projection");
     assert((*ctrl)->in(0)->isa_MemBar(), "MemBar node");
     (*ctrl)->in(0)->isa_MemBar()->set_trailing_partial_array_copy();
   }
 
-  _igvn.replace_node(_callprojs.fallthrough_memproj, out_mem);
-  if (_callprojs.fallthrough_ioproj != nullptr) {
-    _igvn.replace_node(_callprojs.fallthrough_ioproj, *io);
+  _igvn.replace_node(_callprojs->fallthrough_memproj, out_mem);
+  if (_callprojs->fallthrough_ioproj != nullptr) {
+    _igvn.replace_node(_callprojs->fallthrough_ioproj, *io);
   }
-  _igvn.replace_node(_callprojs.fallthrough_catchproj, *ctrl);
+  _igvn.replace_node(_callprojs->fallthrough_catchproj, *ctrl);
 
 #ifdef ASSERT
   const TypeOopPtr* dest_t = _igvn.type(dest)->is_oopptr();
   if (dest_t->is_known_instance() && !is_partial_array_copy) {
     ArrayCopyNode* ac = nullptr;
@@ -872,10 +910,12 @@
 // If dest_size is non-null, zeroing extends to the end of the object.
 // If slice_len is non-null, the slice_idx value must be a constant.
 void PhaseMacroExpand::generate_clear_array(Node* ctrl, MergeMemNode* merge_mem,
                                             const TypePtr* adr_type,
                                             Node* dest,
+                                            Node* val,
+                                            Node* raw_val,
                                             BasicType basic_elem_type,
                                             Node* slice_idx,
                                             Node* slice_len,
                                             Node* dest_size) {
   // one or the other but not both of slice_len and dest_size:
@@ -910,16 +950,16 @@
                        BytesPerLong);
   }
 
   if (start_con >= 0 && end_con >= 0) {
     // Constant start and end.  Simple.
-    mem = ClearArrayNode::clear_memory(ctrl, mem, dest,
+    mem = ClearArrayNode::clear_memory(ctrl, mem, dest, val, raw_val,
                                        start_con, end_con, &_igvn);
   } else if (start_con >= 0 && dest_size != top()) {
     // Constant start, pre-rounded end after the tail of the array.
     Node* end = dest_size;
-    mem = ClearArrayNode::clear_memory(ctrl, mem, dest,
+    mem = ClearArrayNode::clear_memory(ctrl, mem, dest, val, raw_val,
                                        start_con, end, &_igvn);
   } else if (start_con >= 0 && slice_len != top()) {
     // Constant start, non-constant end.  End needs rounding up.
     // End offset = round_up(abase + ((slice_idx_con + slice_len) << scale), 8)
     intptr_t end_base  = abase + (slice_idx_con << scale);
@@ -928,11 +968,11 @@
     if (scale != 0)
       end = transform_later(new LShiftXNode(end, intcon(scale) ));
     end_base += end_round;
     end = transform_later(new AddXNode(end, MakeConX(end_base)) );
     end = transform_later(new AndXNode(end, MakeConX(~end_round)) );
-    mem = ClearArrayNode::clear_memory(ctrl, mem, dest,
+    mem = ClearArrayNode::clear_memory(ctrl, mem, dest, val, raw_val,
                                        start_con, end, &_igvn);
   } else if (start_con < 0 && dest_size != top()) {
     // Non-constant start, pre-rounded end after the tail of the array.
     // This is almost certainly a "round-to-end" operation.
     Node* start = slice_idx;
@@ -957,16 +997,22 @@
       start = transform_later(new AndXNode(start, MakeConX(~to_clear)) );
       if (bump_bit != 0) {
         // Store a zero to the immediately preceding jint:
         Node* x1 = transform_later(new AddXNode(start, MakeConX(-bump_bit)) );
         Node* p1 = basic_plus_adr(dest, x1);
-        mem = StoreNode::make(_igvn, ctrl, mem, p1, adr_type, intcon(0), T_INT, MemNode::unordered);
+        if (val == nullptr) {
+          assert(raw_val == nullptr, "val may not be null");
+          mem = StoreNode::make(_igvn, ctrl, mem, p1, adr_type, intcon(0), T_INT, MemNode::unordered);
+        } else {
+          assert(_igvn.type(val)->isa_narrowoop(), "should be narrow oop");
+          mem = new StoreNNode(ctrl, mem, p1, adr_type, val, MemNode::unordered);
+        }
         mem = transform_later(mem);
       }
     }
     Node* end = dest_size; // pre-rounded
-    mem = ClearArrayNode::clear_memory(ctrl, mem, dest,
+    mem = ClearArrayNode::clear_memory(ctrl, mem, dest, raw_val,
                                        start, end, &_igvn);
   } else {
     // Non-constant start, unrounded non-constant end.
     // (Nobody zeroes a random midsection of an array using this routine.)
     ShouldNotReachHere();       // fix caller
@@ -1078,15 +1124,15 @@
 
   call->set_cnt(PROB_UNLIKELY_MAG(4));  // Same effect as RC_UNCOMMON.
   _igvn.replace_node(ac, call);
   transform_later(call);
 
-  call->extract_projections(&_callprojs, false /*separate_io_proj*/, false /*do_asserts*/);
-  *ctrl = _callprojs.fallthrough_catchproj->clone();
+  _callprojs = call->extract_projections(false /*separate_io_proj*/, false /*do_asserts*/);
+  *ctrl = _callprojs->fallthrough_catchproj->clone();
   transform_later(*ctrl);
 
-  Node* m = _callprojs.fallthrough_memproj->clone();
+  Node* m = _callprojs->fallthrough_memproj->clone();
   transform_later(m);
 
   uint alias_idx = C->get_alias_index(adr_type);
   MergeMemNode* out_mem;
   if (alias_idx != Compile::AliasIdxBot) {
@@ -1096,13 +1142,13 @@
     out_mem = MergeMemNode::make(m);
   }
   transform_later(out_mem);
 
   // When src is negative and arraycopy is before an infinite loop,_callprojs.fallthrough_ioproj
-  // could be null. Skip clone and update null fallthrough_ioproj.
-  if (_callprojs.fallthrough_ioproj != nullptr) {
-    *io = _callprojs.fallthrough_ioproj->clone();
+  // could be nullptr. Skip clone and update nullptr fallthrough_ioproj.
+  if (_callprojs->fallthrough_ioproj != nullptr) {
+    *io = _callprojs->fallthrough_ioproj->clone();
     transform_later(*io);
   } else {
     *io = nullptr;
   }
 
@@ -1230,10 +1276,46 @@
     return true;
   }
   return false;
 }
 
+const TypePtr* PhaseMacroExpand::adjust_for_flat_array(const TypeAryPtr* top_dest, Node*& src_offset,
+                                                       Node*& dest_offset, Node*& length, BasicType& dest_elem,
+                                                       Node*& dest_length) {
+#ifdef ASSERT
+  BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
+  bool needs_barriers = top_dest->elem()->inline_klass()->contains_oops() &&
+    bs->array_copy_requires_gc_barriers(dest_length != nullptr, T_OBJECT, false, false, BarrierSetC2::Optimization);
+  assert(!needs_barriers || StressReflectiveCode, "Flat arracopy would require GC barriers");
+#endif
+  int elem_size = top_dest->flat_elem_size();
+  if (elem_size >= 8) {
+    if (elem_size > 8) {
+      // treat as array of long but scale length, src offset and dest offset
+      assert((elem_size % 8) == 0, "not a power of 2?");
+      int factor = elem_size / 8;
+      length = transform_later(new MulINode(length, intcon(factor)));
+      src_offset = transform_later(new MulINode(src_offset, intcon(factor)));
+      dest_offset = transform_later(new MulINode(dest_offset, intcon(factor)));
+      if (dest_length != nullptr) {
+        dest_length = transform_later(new MulINode(dest_length, intcon(factor)));
+      }
+      elem_size = 8;
+    }
+    dest_elem = T_LONG;
+  } else if (elem_size == 4) {
+    dest_elem = T_INT;
+  } else if (elem_size == 2) {
+    dest_elem = T_CHAR;
+  } else if (elem_size == 1) {
+    dest_elem = T_BYTE;
+  } else {
+    ShouldNotReachHere();
+  }
+  return TypeRawPtr::BOTTOM;
+}
+
 #undef XTOP
 
 void PhaseMacroExpand::expand_arraycopy_node(ArrayCopyNode *ac) {
   Node* ctrl = ac->in(TypeFunc::Control);
   Node* io = ac->in(TypeFunc::I_O);
@@ -1247,29 +1329,58 @@
   if (ac->is_clonebasic()) {
     BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
     bs->clone_at_expansion(this, ac);
     return;
   } else if (ac->is_copyof() || ac->is_copyofrange() || ac->is_clone_oop_array()) {
-    Node* mem = ac->in(TypeFunc::Memory);
-    merge_mem = MergeMemNode::make(mem);
-    transform_later(merge_mem);
+    const Type* src_type = _igvn.type(src);
+    const Type* dest_type = _igvn.type(dest);
+    const TypeAryPtr* top_src = src_type->isa_aryptr();
+    const TypeAryPtr* top_dest = dest_type->isa_aryptr();
+    BasicType dest_elem = T_OBJECT;
+    if (top_dest != nullptr && top_dest->elem() != Type::BOTTOM) {
+      dest_elem = top_dest->elem()->array_element_basic_type();
+    }
+    if (is_reference_type(dest_elem, true)) dest_elem = T_OBJECT;
+
+    if (top_src != nullptr && top_src->is_flat()) {
+      // If src is flat, dest is guaranteed to be flat as well
+      top_dest = top_src;
+    }
 
     AllocateArrayNode* alloc = nullptr;
+    Node* dest_length = nullptr;
     if (ac->is_alloc_tightly_coupled()) {
       alloc = AllocateArrayNode::Ideal_array_allocation(dest);
       assert(alloc != nullptr, "expect alloc");
+      dest_length = alloc->in(AllocateNode::ALength);
     }
 
-    const TypePtr* adr_type = _igvn.type(dest)->is_oopptr()->add_offset(Type::OffsetBot);
-    if (ac->_dest_type != TypeOopPtr::BOTTOM) {
-      adr_type = ac->_dest_type->add_offset(Type::OffsetBot)->is_ptr();
+    Node* mem = ac->in(TypeFunc::Memory);
+    const TypePtr* adr_type = nullptr;
+    if (top_dest->is_flat()) {
+      assert(dest_length != nullptr || StressReflectiveCode, "must be tightly coupled");
+      // Copy to a flat array modifies multiple memory slices. Conservatively insert a barrier
+      // on all slices to prevent writes into the source from floating below the arraycopy.
+      insert_mem_bar(&ctrl, &mem, Op_MemBarCPUOrder);
+      adr_type = adjust_for_flat_array(top_dest, src_offset, dest_offset, length, dest_elem, dest_length);
+    } else {
+      adr_type = dest_type->is_oopptr()->add_offset(Type::OffsetBot);
+      if (ac->_dest_type != TypeOopPtr::BOTTOM) {
+        adr_type = ac->_dest_type->add_offset(Type::OffsetBot)->is_ptr();
+      }
+      if (ac->_src_type != ac->_dest_type) {
+        adr_type = TypeRawPtr::BOTTOM;
+      }
     }
+    merge_mem = MergeMemNode::make(mem);
+    transform_later(merge_mem);
+
     generate_arraycopy(ac, alloc, &ctrl, merge_mem, &io,
-                       adr_type, T_OBJECT,
+                       adr_type, dest_elem,
                        src, src_offset, dest, dest_offset, length,
+                       dest_length,
                        true, !ac->is_copyofrange());
-
     return;
   }
 
   AllocateArrayNode* alloc = nullptr;
   if (ac->is_alloc_tightly_coupled()) {
@@ -1299,13 +1410,11 @@
     dest_elem = top_dest->elem()->array_element_basic_type();
   }
   if (is_reference_type(src_elem, true)) src_elem = T_OBJECT;
   if (is_reference_type(dest_elem, true)) dest_elem = T_OBJECT;
 
-  if (ac->is_arraycopy_validated() &&
-      dest_elem != T_CONFLICT &&
-      src_elem == T_CONFLICT) {
+  if (ac->is_arraycopy_validated() && dest_elem != T_CONFLICT && src_elem == T_CONFLICT) {
     src_elem = dest_elem;
   }
 
   if (src_elem == T_CONFLICT || dest_elem == T_CONFLICT) {
     // Conservatively insert a memory barrier on all memory slices.
@@ -1320,33 +1429,41 @@
 
     // Call StubRoutines::generic_arraycopy stub.
     Node* mem = generate_arraycopy(ac, nullptr, &ctrl, merge_mem, &io,
                                    TypeRawPtr::BOTTOM, T_CONFLICT,
                                    src, src_offset, dest, dest_offset, length,
+                                   nullptr,
                                    // If a  negative length guard was generated for the ArrayCopyNode,
                                    // the length of the array can never be negative.
                                    false, ac->has_negative_length_guard());
     return;
   }
 
   assert(!ac->is_arraycopy_validated() || (src_elem == dest_elem && dest_elem != T_VOID), "validated but different basic types");
 
   // (2) src and dest arrays must have elements of the same BasicType
   // Figure out the size and type of the elements we will be copying.
-  if (src_elem != dest_elem || dest_elem == T_VOID) {
+  //
+  // We have no stub to copy flat inline type arrays with oop
+  // fields if we need to emit write barriers.
+  //
+  BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
+  if (src_elem != dest_elem || top_src->is_flat() != top_dest->is_flat() || dest_elem == T_VOID ||
+      (top_src->is_flat() && top_dest->elem()->inline_klass()->contains_oops() &&
+       bs->array_copy_requires_gc_barriers(alloc != nullptr, T_OBJECT, false, false, BarrierSetC2::Optimization))) {
     // The component types are not the same or are not recognized.  Punt.
     // (But, avoid the native method wrapper to JVM_ArrayCopy.)
     {
       Node* mem = ac->in(TypeFunc::Memory);
       merge_mem = generate_slow_arraycopy(ac, &ctrl, mem, &io, TypePtr::BOTTOM, src, src_offset, dest, dest_offset, length, false);
     }
 
-    _igvn.replace_node(_callprojs.fallthrough_memproj, merge_mem);
-    if (_callprojs.fallthrough_ioproj != nullptr) {
-      _igvn.replace_node(_callprojs.fallthrough_ioproj, io);
+    _igvn.replace_node(_callprojs->fallthrough_memproj, merge_mem);
+    if (_callprojs->fallthrough_ioproj != nullptr) {
+      _igvn.replace_node(_callprojs->fallthrough_ioproj, io);
     }
-    _igvn.replace_node(_callprojs.fallthrough_catchproj, ctrl);
+    _igvn.replace_node(_callprojs->fallthrough_catchproj, ctrl);
     return;
   }
 
   //---------------------------------------------------------------------------
   // We will make a fast path for this call to arraycopy.
@@ -1359,15 +1476,18 @@
   // (6) length must not be negative.
   // (7) src_offset + length must not exceed length of src.
   // (8) dest_offset + length must not exceed length of dest.
   // (9) each element of an oop array must be assignable
 
-  {
-    Node* mem = ac->in(TypeFunc::Memory);
-    merge_mem = MergeMemNode::make(mem);
-    transform_later(merge_mem);
+  Node* mem = ac->in(TypeFunc::Memory);
+  if (top_dest->is_flat()) {
+    // Copy to a flat array modifies multiple memory slices. Conservatively insert a barrier
+    // on all slices to prevent writes into the source from floating below the arraycopy.
+    insert_mem_bar(&ctrl, &mem, Op_MemBarCPUOrder);
   }
+  merge_mem = MergeMemNode::make(mem);
+  transform_later(merge_mem);
 
   RegionNode* slow_region = new RegionNode(1);
   transform_later(slow_region);
 
   if (!ac->is_arraycopy_validated()) {
@@ -1404,21 +1524,43 @@
                          alen,
                          slow_region);
 
     // (9) each element of an oop array must be assignable
     // The generate_arraycopy subroutine checks this.
+
+    // Handle inline type arrays
+    if (!top_src->is_flat()) {
+      if (UseFlatArray && !top_src->is_not_flat()) {
+        // Src might be flat and dest might not be flat. Go to the slow path if src is flat.
+        generate_flat_array_guard(&ctrl, src, slow_region);
+      }
+      if (EnableValhalla) {
+        // No validation. The subtype check emitted at macro expansion time will not go to the slow
+        // path but call checkcast_arraycopy which can not handle flat/null-free inline type arrays.
+        generate_null_free_array_guard(&ctrl, dest, slow_region);
+      }
+    } else {
+      assert(top_dest->is_flat(), "dest array must be flat");
+    }
   }
+
   // This is where the memory effects are placed:
   const TypePtr* adr_type = nullptr;
-  if (ac->_dest_type != TypeOopPtr::BOTTOM) {
+  Node* dest_length = (alloc != nullptr) ? alloc->in(AllocateNode::ALength) : nullptr;
+
+  if (top_dest->is_flat()) {
+    adr_type = adjust_for_flat_array(top_dest, src_offset, dest_offset, length, dest_elem, dest_length);
+  } else if (ac->_dest_type != TypeOopPtr::BOTTOM) {
     adr_type = ac->_dest_type->add_offset(Type::OffsetBot)->is_ptr();
   } else {
     adr_type = TypeAryPtr::get_array_body_type(dest_elem);
   }
 
   generate_arraycopy(ac, alloc, &ctrl, merge_mem, &io,
                      adr_type, dest_elem,
                      src, src_offset, dest, dest_offset, length,
+                     dest_length,
                      // If a  negative length guard was generated for the ArrayCopyNode,
                      // the length of the array can never be negative.
-                     false, ac->has_negative_length_guard(), slow_region);
+                     false, ac->has_negative_length_guard(),
+                     slow_region);
 }
diff a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp
--- a/src/hotspot/share/opto/matcher.cpp
+++ b/src/hotspot/share/opto/matcher.cpp
@@ -183,10 +183,54 @@
     }
   }
 }
 #endif
 
+// Array of RegMask, one per returned values (inline type instances can
+// be returned as multiple return values, one per field)
+RegMask* Matcher::return_values_mask(const TypeFunc* tf) {
+  const TypeTuple* range = tf->range_cc();
+  uint cnt = range->cnt() - TypeFunc::Parms;
+  if (cnt == 0) {
+    return nullptr;
+  }
+  RegMask* mask = NEW_RESOURCE_ARRAY(RegMask, cnt);
+  BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, cnt);
+  VMRegPair* vm_parm_regs = NEW_RESOURCE_ARRAY(VMRegPair, cnt);
+  for (uint i = 0; i < cnt; i++) {
+    sig_bt[i] = range->field_at(i+TypeFunc::Parms)->basic_type();
+  }
+
+  int regs = SharedRuntime::java_return_convention(sig_bt, vm_parm_regs, cnt);
+  if (regs <= 0) {
+    // We ran out of registers to store the IsInit information for a nullable inline type return.
+    // Since it is only set in the 'call_epilog', we can simply put it on the stack.
+    assert(tf->returns_inline_type_as_fields(), "should have been tested during graph construction");
+    // TODO 8284443 Can we teach the register allocator to reserve a stack slot instead?
+    // mask[--cnt] = STACK_ONLY_mask does not work (test with -XX:+StressGCM)
+    int slot = C->fixed_slots() - 2;
+    if (C->needs_stack_repair()) {
+      slot -= 2; // Account for stack increment value
+    }
+    mask[--cnt].Clear();
+    mask[cnt].Insert(OptoReg::stack2reg(slot));
+  }
+  for (uint i = 0; i < cnt; i++) {
+    mask[i].Clear();
+
+    OptoReg::Name reg1 = OptoReg::as_OptoReg(vm_parm_regs[i].first());
+    if (OptoReg::is_valid(reg1)) {
+      mask[i].Insert(reg1);
+    }
+    OptoReg::Name reg2 = OptoReg::as_OptoReg(vm_parm_regs[i].second());
+    if (OptoReg::is_valid(reg2)) {
+      mask[i].Insert(reg2);
+    }
+  }
+
+  return mask;
+}
 
 //---------------------------match---------------------------------------------
 void Matcher::match( ) {
   if( MaxLabelRootDepth < 100 ) { // Too small?
     assert(false, "invalid MaxLabelRootDepth, increase it to 100 minimum");
@@ -198,33 +242,21 @@
 #ifdef _LP64
   // Pointers take 2 slots in 64-bit land
   _return_addr_mask.Insert(OptoReg::add(return_addr(),1));
 #endif
 
-  // Map a Java-signature return type into return register-value
-  // machine registers for 0, 1 and 2 returned values.
-  const TypeTuple *range = C->tf()->range();
-  if( range->cnt() > TypeFunc::Parms ) { // If not a void function
-    // Get ideal-register return type
-    uint ireg = range->field_at(TypeFunc::Parms)->ideal_reg();
-    // Get machine return register
-    uint sop = C->start()->Opcode();
-    OptoRegPair regs = return_value(ireg);
-
-    // And mask for same
-    _return_value_mask = RegMask(regs.first());
-    if( OptoReg::is_valid(regs.second()) )
-      _return_value_mask.Insert(regs.second());
-  }
+  // Map Java-signature return types into return register-value
+  // machine registers.
+  _return_values_mask = return_values_mask(C->tf());
 
   // ---------------
   // Frame Layout
 
   // Need the method signature to determine the incoming argument types,
   // because the types determine which registers the incoming arguments are
   // in, and this affects the matched code.
-  const TypeTuple *domain = C->tf()->domain();
+  const TypeTuple *domain = C->tf()->domain_cc();
   uint             argcnt = domain->cnt() - TypeFunc::Parms;
   BasicType *sig_bt        = NEW_RESOURCE_ARRAY( BasicType, argcnt );
   VMRegPair *vm_parm_regs  = NEW_RESOURCE_ARRAY( VMRegPair, argcnt );
   _parm_regs               = NEW_RESOURCE_ARRAY( OptoRegPair, argcnt );
   _calling_convention_mask = NEW_RESOURCE_ARRAY( RegMask, argcnt );
@@ -537,10 +569,11 @@
   // Add in the incoming argument area
   OptoReg::Name init_in = OptoReg::add(_old_SP, C->out_preserve_stack_slots());
   for (i = init_in; i < _in_arg_limit; i = OptoReg::add(i,1)) {
     C->FIRST_STACK_mask().Insert(i);
   }
+
   // Add in all bits past the outgoing argument area
   guarantee(RegMask::can_represent_arg(OptoReg::add(_out_arg_limit,-1)),
             "must be able to represent all call arguments in reg mask");
   OptoReg::Name init = _out_arg_limit;
   for (i = init; RegMask::can_represent(i); i = OptoReg::add(i,1)) {
@@ -795,16 +828,15 @@
   assert( start, "Expect a start node" );
 
   // Input RegMask array shared by all Returns.
   // The type for doubles and longs has a count of 2, but
   // there is only 1 returned value
-  uint ret_edge_cnt = TypeFunc::Parms + ((C->tf()->range()->cnt() == TypeFunc::Parms) ? 0 : 1);
+  uint ret_edge_cnt = C->tf()->range_cc()->cnt();
   RegMask *ret_rms  = init_input_masks( ret_edge_cnt + soe_cnt, _return_addr_mask, c_frame_ptr_mask );
-  // Returns have 0 or 1 returned values depending on call signature.
-  // Return register is specified by return_value in the AD file.
-  if (ret_edge_cnt > TypeFunc::Parms)
-    ret_rms[TypeFunc::Parms+0] = _return_value_mask;
+  for (i = TypeFunc::Parms; i < ret_edge_cnt; i++) {
+    ret_rms[i] = _return_values_mask[i-TypeFunc::Parms];
+  }
 
   // Input RegMask array shared by all Rethrows.
   uint reth_edge_cnt = TypeFunc::Parms+1;
   RegMask *reth_rms  = init_input_masks( reth_edge_cnt + soe_cnt, _return_addr_mask, c_frame_ptr_mask );
   // Rethrow takes exception oop only, but in the argument 0 slot.
@@ -867,11 +899,11 @@
       default          : ShouldNotReachHere();
     }
   }
 
   // Next unused projection number from Start.
-  int proj_cnt = C->tf()->domain()->cnt();
+  int proj_cnt = C->tf()->domain_cc()->cnt();
 
   // Do all the save-on-entry registers.  Make projections from Start for
   // them, and give them a use at the exit points.  To the allocator, they
   // look like incoming register arguments.
   for( i = 0; i < _last_Mach_Reg; i++ ) {
@@ -1140,11 +1172,15 @@
               m->as_MachMemBar()->set_adr_type(n->adr_type());
             }
           } else {                  // Nothing the matcher cares about
             if (n->is_Proj() && n->in(0) != nullptr && n->in(0)->is_Multi()) {       // Projections?
               // Convert to machine-dependent projection
-              m = n->in(0)->as_Multi()->match( n->as_Proj(), this );
+              RegMask* mask = nullptr;
+              if (n->in(0)->is_Call() && n->in(0)->as_Call()->tf()->returns_inline_type_as_fields()) {
+                mask = return_values_mask(n->in(0)->as_Call()->tf());
+              }
+              m = n->in(0)->as_Multi()->match(n->as_Proj(), this, mask);
               NOT_PRODUCT(record_new2old(m, n);)
               if (m->in(0) != nullptr) // m might be top
                 collect_null_checks(m, n);
             } else {                // Else just a regular 'ol guy
               m = n->clone();       // So just clone into new-space
@@ -1280,11 +1316,11 @@
   const TypeTuple *domain;
   ciMethod*        method = nullptr;
   bool             is_method_handle_invoke = false;  // for special kill effects
   if( sfpt->is_Call() ) {
     call = sfpt->as_Call();
-    domain = call->tf()->domain();
+    domain = call->tf()->domain_cc();
     cnt = domain->cnt();
 
     // Match just the call, nothing else
     MachNode *m = match_tree(call);
     if (C->failing())  return nullptr;
@@ -1359,17 +1395,20 @@
   if( call != nullptr && call->is_CallRuntime() )
     out_arg_limit_per_call = OptoReg::add(out_arg_limit_per_call,C->varargs_C_out_slots_killed());
 
 
   // Do the normal argument list (parameters) register masks
-  int argcnt = cnt - TypeFunc::Parms;
+  // Null entry point is a special cast where the target of the call
+  // is in a register.
+  int adj = (call != nullptr && call->entry_point() == nullptr) ? 1 : 0;
+  int argcnt = cnt - TypeFunc::Parms - adj;
   if( argcnt > 0 ) {          // Skip it all if we have no args
     BasicType *sig_bt  = NEW_RESOURCE_ARRAY( BasicType, argcnt );
     VMRegPair *parm_regs = NEW_RESOURCE_ARRAY( VMRegPair, argcnt );
     int i;
     for( i = 0; i < argcnt; i++ ) {
-      sig_bt[i] = domain->field_at(i+TypeFunc::Parms)->basic_type();
+      sig_bt[i] = domain->field_at(i+TypeFunc::Parms+adj)->basic_type();
     }
     // V-call to pick proper calling convention
     call->calling_convention( sig_bt, parm_regs, argcnt );
 
 #ifdef ASSERT
@@ -1406,11 +1445,11 @@
     // Return results now can have 2 bits returned.
     // Compute max over all outgoing arguments both per call-site
     // and over the entire method.
     for( i = 0; i < argcnt; i++ ) {
       // Address of incoming argument mask to fill in
-      RegMask *rm = &mcall->_in_rms[i+TypeFunc::Parms];
+      RegMask *rm = &mcall->_in_rms[i+TypeFunc::Parms+adj];
       VMReg first = parm_regs[i].first();
       VMReg second = parm_regs[i].second();
       if(!first->is_valid() &&
          !second->is_valid()) {
         continue;               // Avoid Halves
@@ -1424,16 +1463,18 @@
           rm->Insert(r);
         }
       }
       // Grab first register, adjust stack slots and insert in mask.
       OptoReg::Name reg1 = warp_outgoing_stk_arg(first, begin_out_arg_area, out_arg_limit_per_call );
-      if (OptoReg::is_valid(reg1))
+      if (OptoReg::is_valid(reg1)) {
         rm->Insert( reg1 );
+      }
       // Grab second register (if any), adjust stack slots and insert in mask.
       OptoReg::Name reg2 = warp_outgoing_stk_arg(second, begin_out_arg_area, out_arg_limit_per_call );
-      if (OptoReg::is_valid(reg2))
+      if (OptoReg::is_valid(reg2)) {
         rm->Insert( reg2 );
+      }
     } // End of for all arguments
   }
 
   // Compute the max stack slot killed by any call.  These will not be
   // available for debug info, and will be used to adjust FIRST_STACK_mask
@@ -1445,11 +1486,11 @@
     // Kill the outgoing argument area, including any non-argument holes and
     // any legacy C-killed slots.  Use Fat-Projections to do the killing.
     // Since the max-per-method covers the max-per-call-site and debug info
     // is excluded on the max-per-method basis, debug info cannot land in
     // this killed area.
-    uint r_cnt = mcall->tf()->range()->cnt();
+    uint r_cnt = mcall->tf()->range_sig()->cnt();
     MachProjNode *proj = new MachProjNode( mcall, r_cnt+10000, RegMask::Empty, MachProjNode::fat_proj );
     if (!RegMask::can_represent_arg(OptoReg::Name(out_arg_limit_per_call-1))) {
       // Bailout. We do not have space to represent all arguments.
       C->record_method_not_compilable("unsupported outgoing calling sequence");
     } else {
@@ -1467,11 +1508,11 @@
     jvms->set_map(sfpt);
   }
 
   // Debug inputs begin just after the last incoming parameter
   assert((mcall == nullptr) || (mcall->jvms() == nullptr) ||
-         (mcall->jvms()->debug_start() + mcall->_jvmadj == mcall->tf()->domain()->cnt()), "");
+         (mcall->jvms()->debug_start() + mcall->_jvmadj == mcall->tf()->domain_cc()->cnt()), "");
 
   // Add additional edges.
   if (msfpt->mach_constant_base_node_input() != (uint)-1 && !msfpt->is_MachCallLeaf()) {
     // For these calls we can not add MachConstantBase in expand(), as the
     // ins are not complete then.
@@ -2151,11 +2192,11 @@
       bool mem_op = false;
       int mem_addr_idx = MemNode::Address;
       if (find_shared_visit(mstack, n, nop, mem_op, mem_addr_idx)) {
         continue;
       }
-      for (int i = n->req() - 1; i >= 0; --i) { // For my children
+      for (int i = n->len() - 1; i >= 0; --i) { // For my children
         Node* m = n->in(i); // Get ith input
         if (m == nullptr) {
           continue;  // Ignore nulls
         }
         if (clone_node(n, m, mstack)) {
@@ -2457,10 +2498,17 @@
       n->set_req(2, pair2);
       n->del_req(4);
       n->del_req(3);
       break;
     }
+    case Op_ClearArray: {
+      Node* pair = new BinaryNode(n->in(2), n->in(3));
+      n->set_req(2, pair);
+      n->set_req(3, n->in(4));
+      n->del_req(4);
+      break;
+    }
     case Op_VectorCmpMasked:
     case Op_CopySignD:
     case Op_SignumVF:
     case Op_SignumVD:
     case Op_SignumF:
diff a/src/hotspot/share/opto/matcher.hpp b/src/hotspot/share/opto/matcher.hpp
--- a/src/hotspot/share/opto/matcher.hpp
+++ b/src/hotspot/share/opto/matcher.hpp
@@ -273,10 +273,12 @@
   void match();
 
   // Helper for match
   OptoReg::Name warp_incoming_stk_arg( VMReg reg );
 
+  RegMask* return_values_mask(const TypeFunc* tf);
+
   // Transform, then walk.  Does implicit DCE while walking.
   // Name changed from "transform" to avoid it being virtual.
   Node *xform( Node *old_space_node, int Nodes );
 
   // Match a single Ideal Node - turn it into a 1-Node tree; Label & Reduce.
@@ -407,11 +409,11 @@
   virtual OptoReg::Name return_addr() const;
   RegMask              _return_addr_mask;
   // Return value register.  On Intel it is EAX.
   static OptoRegPair   return_value(uint ideal_reg);
   static OptoRegPair c_return_value(uint ideal_reg);
-  RegMask                     _return_value_mask;
+  RegMask*            _return_values_mask;
   // Inline Cache Register
   static OptoReg::Name  inline_cache_reg();
   static int            inline_cache_reg_encode();
 
   // Register for DIVI projection of divmodI
diff a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp
--- a/src/hotspot/share/opto/memnode.cpp
+++ b/src/hotspot/share/opto/memnode.cpp
@@ -21,11 +21,13 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "ci/ciFlatArrayKlass.hpp"
 #include "classfile/javaClasses.hpp"
+#include "classfile/systemDictionary.hpp"
 #include "compiler/compileLog.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/c2/barrierSetC2.hpp"
 #include "gc/shared/tlab_globals.hpp"
 #include "memory/allocation.inline.hpp"
@@ -36,10 +38,11 @@
 #include "opto/cfgnode.hpp"
 #include "opto/regalloc.hpp"
 #include "opto/compile.hpp"
 #include "opto/connode.hpp"
 #include "opto/convertnode.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/loopnode.hpp"
 #include "opto/machnode.hpp"
 #include "opto/matcher.hpp"
 #include "opto/memnode.hpp"
 #include "opto/mulnode.hpp"
@@ -229,20 +232,23 @@
         ->is_oopptr()->cast_to_instance_id(t_oop->instance_id());
       if (t_oop->is_aryptr()) {
         mem_t = mem_t->is_aryptr()
                      ->cast_to_stable(t_oop->is_aryptr()->is_stable())
                      ->cast_to_size(t_oop->is_aryptr()->size())
+                     ->cast_to_not_flat(t_oop->is_aryptr()->is_not_flat())
+                     ->cast_to_not_null_free(t_oop->is_aryptr()->is_not_null_free())
                      ->with_offset(t_oop->is_aryptr()->offset())
                      ->is_aryptr();
       }
       do_split = mem_t == t_oop;
     }
     if (do_split) {
       // clone the Phi with our address type
       result = mphi->split_out_instance(t_adr, igvn);
     } else {
-      assert(phase->C->get_alias_index(t) == phase->C->get_alias_index(t_adr), "correct memory chain");
+      // TODO 8325106
+      // assert(phase->C->get_alias_index(t) == phase->C->get_alias_index(t_adr), "correct memory chain");
     }
   }
   return result;
 }
 
@@ -255,11 +261,11 @@
     assert(alias_idx >= Compile::AliasIdxRaw, "must not be a bad alias_idx");
     bool consistent =  adr_check == nullptr || adr_check->empty() ||
                        phase->C->must_alias(adr_check, alias_idx );
     // Sometimes dead array references collapse to a[-1], a[-2], or a[-3]
     if( !consistent && adr_check != nullptr && !adr_check->empty() &&
-               tp->isa_aryptr() &&        tp->offset() == Type::OffsetBot &&
+        tp->isa_aryptr() &&        tp->offset() == Type::OffsetBot &&
         adr_check->isa_aryptr() && adr_check->offset() != Type::OffsetBot &&
         ( adr_check->offset() == arrayOopDesc::length_offset_in_bytes() ||
           adr_check->offset() == oopDesc::klass_offset_in_bytes() ||
           adr_check->offset() == oopDesc::mark_offset_in_bytes() ) ) {
       // don't assert if it is dead code.
@@ -988,11 +994,11 @@
     bool non_volatile = (atp->field() != nullptr) && !atp->field()->is_volatile();
     bool is_stable_ary = FoldStableValues &&
                          (tp != nullptr) && (tp->isa_aryptr() != nullptr) &&
                          tp->isa_aryptr()->is_stable();
 
-    return (eliminate_boxing && non_volatile) || is_stable_ary;
+    return (eliminate_boxing && non_volatile) || is_stable_ary || tp->is_inlinetypeptr();
   }
 
   return false;
 }
 
@@ -1045,11 +1051,11 @@
       const TypeAryPtr* ary_t = phase->type(in(MemNode::Address))->isa_aryptr();
       BasicType ary_elem = ary_t->isa_aryptr()->elem()->array_element_basic_type();
       if (is_reference_type(ary_elem, true)) ary_elem = T_OBJECT;
 
       uint header = arrayOopDesc::base_offset_in_bytes(ary_elem);
-      uint shift  = exact_log2(type2aelembytes(ary_elem));
+      uint shift  = ary_t->is_flat() ? ary_t->flat_log_elem_size() : exact_log2(type2aelembytes(ary_elem));
 
       Node* diff = phase->transform(new SubINode(ac->in(ArrayCopyNode::SrcPos), ac->in(ArrayCopyNode::DestPos)));
 #ifdef _LP64
       diff = phase->transform(new ConvI2LNode(diff));
 #endif
@@ -1164,11 +1170,11 @@
         return nullptr;
       }
       // LoadVector/StoreVector needs additional check to ensure the types match.
       if (st->is_StoreVector()) {
         const TypeVect*  in_vt = st->as_StoreVector()->vect_type();
-        const TypeVect* out_vt = as_LoadVector()->vect_type();
+        const TypeVect* out_vt = is_Load() ? as_LoadVector()->vect_type() : as_StoreVector()->vect_type();
         if (in_vt != out_vt) {
           return nullptr;
         }
       }
       return st->in(MemNode::ValueIn);
@@ -1182,10 +1188,15 @@
         (ld_off >= st->in(0)->as_Allocate()->minimum_header_size())) {
       // return a zero value for the load's basic type
       // (This is one of the few places where a generic PhaseTransform
       // can create new nodes.  Think of it as lazily manifesting
       // virtually pre-existing constants.)
+      Node* default_value = ld_alloc->in(AllocateNode::DefaultValue);
+      if (default_value != nullptr) {
+        return default_value;
+      }
+      assert(ld_alloc->in(AllocateNode::RawDefaultValue) == nullptr, "default value may not be null");
       if (memory_type() != T_VOID) {
         if (ReduceBulkZeroing || find_array_copy_clone(ld_alloc, in(MemNode::Memory)) == nullptr) {
           // If ReduceBulkZeroing is disabled, we need to check if the allocation does not belong to an
           // ArrayCopyNode clone. If it does, then we cannot assume zero since the initialization is done
           // by the ArrayCopyNode.
@@ -1249,10 +1260,27 @@
 }
 
 //------------------------------Identity---------------------------------------
 // Loads are identity if previous store is to same address
 Node* LoadNode::Identity(PhaseGVN* phase) {
+  // Loading from an InlineType? The InlineType has the values of
+  // all fields as input. Look for the field with matching offset.
+  Node* addr = in(Address);
+  intptr_t offset;
+  Node* base = AddPNode::Ideal_base_and_offset(addr, phase, offset);
+  if (base != nullptr && base->is_InlineType() && offset > oopDesc::klass_offset_in_bytes()) {
+    Node* value = base->as_InlineType()->field_value_by_offset((int)offset, true);
+    if (value != nullptr) {
+      if (Opcode() == Op_LoadN) {
+        // Encode oop value if we are loading a narrow oop
+        assert(!phase->type(value)->isa_narrowoop(), "should already be decoded");
+        value = phase->transform(new EncodePNode(value, bottom_type()));
+      }
+      return value;
+    }
+  }
+
   // If the previous store-maker is the right kind of Store, and the store is
   // to the same address, then we are equal to the value stored.
   Node* mem = in(Memory);
   Node* value = can_see_stored_value(mem, phase);
   if( value ) {
@@ -2007,10 +2035,11 @@
     // In fact, that could have been the original type of p1, and p1 could have
     // had an original form like p1:(AddP x x (LShiftL quux 3)), where the
     // expression (LShiftL quux 3) independently optimized to the constant 8.
     if ((t->isa_int() == nullptr) && (t->isa_long() == nullptr)
         && (_type->isa_vect() == nullptr)
+        && !ary->is_flat()
         && Opcode() != Op_LoadKlass && Opcode() != Op_LoadNKlass) {
       // t might actually be lower than _type, if _type is a unique
       // concrete subclass of abstract class t.
       if (off_beyond_header || off == Type::OffsetBot) {  // is the offset beyond the header?
         const Type* jt = t->join_speculative(_type);
@@ -2042,20 +2071,24 @@
   } else if (tp->base() == Type::InstPtr) {
     assert( off != Type::OffsetBot ||
             // arrays can be cast to Objects
             !tp->isa_instptr() ||
             tp->is_instptr()->instance_klass()->is_java_lang_Object() ||
+            // Default value load
+            tp->is_instptr()->instance_klass() == ciEnv::current()->Class_klass() ||
             // unsafe field access may not have a constant offset
             C->has_unsafe_access(),
             "Field accesses must be precise" );
     // For oop loads, we expect the _type to be precise.
 
-    // Optimize loads from constant fields.
     const TypeInstPtr* tinst = tp->is_instptr();
+    BasicType bt = memory_type();
+
+    // Optimize loads from constant fields.
     ciObject* const_oop = tinst->const_oop();
     if (!is_mismatched_access() && off != Type::OffsetBot && const_oop != nullptr && const_oop->is_instance()) {
-      const Type* con_type = Type::make_constant_from_field(const_oop->as_instance(), off, is_unsigned(), memory_type());
+      const Type* con_type = Type::make_constant_from_field(const_oop->as_instance(), off, is_unsigned(), bt);
       if (con_type != nullptr) {
         return con_type;
       }
     }
   } else if (tp->base() == Type::KlassPtr || tp->base() == Type::InstKlassPtr || tp->base() == Type::AryKlassPtr) {
@@ -2066,25 +2099,42 @@
            // also allow array-loading from the primary supertype
            // array during subtype checks
            Opcode() == Op_LoadKlass,
            "Field accesses must be precise");
     // For klass/static loads, we expect the _type to be precise
-  } else if (tp->base() == Type::RawPtr && adr->is_Load() && off == 0) {
-    /* With mirrors being an indirect in the Klass*
-     * the VM is now using two loads. LoadKlass(LoadP(LoadP(Klass, mirror_offset), zero_offset))
-     * The LoadP from the Klass has a RawPtr type (see LibraryCallKit::load_mirror_from_klass).
-     *
-     * So check the type and klass of the node before the LoadP.
-     */
-    Node* adr2 = adr->in(MemNode::Address);
-    const TypeKlassPtr* tkls = phase->type(adr2)->isa_klassptr();
-    if (tkls != nullptr && !StressReflectiveCode) {
-      if (tkls->is_loaded() && tkls->klass_is_exact() && tkls->offset() == in_bytes(Klass::java_mirror_offset())) {
-        ciKlass* klass = tkls->exact_klass();
-        assert(adr->Opcode() == Op_LoadP, "must load an oop from _java_mirror");
-        assert(Opcode() == Op_LoadP, "must load an oop from _java_mirror");
-        return TypeInstPtr::make(klass->java_mirror());
+  } else if (tp->base() == Type::RawPtr && !StressReflectiveCode) {
+    if (adr->is_Load() && off == 0) {
+      /* With mirrors being an indirect in the Klass*
+       * the VM is now using two loads. LoadKlass(LoadP(LoadP(Klass, mirror_offset), zero_offset))
+       * The LoadP from the Klass has a RawPtr type (see LibraryCallKit::load_mirror_from_klass).
+       *
+       * So check the type and klass of the node before the LoadP.
+       */
+      Node* adr2 = adr->in(MemNode::Address);
+      const TypeKlassPtr* tkls = phase->type(adr2)->isa_klassptr();
+      if (tkls != nullptr) {
+        if (tkls->is_loaded() && tkls->klass_is_exact() && tkls->offset() == in_bytes(Klass::java_mirror_offset())) {
+          ciKlass* klass = tkls->exact_klass();
+          assert(adr->Opcode() == Op_LoadP, "must load an oop from _java_mirror");
+          assert(Opcode() == Op_LoadP, "must load an oop from _java_mirror");
+          return TypeInstPtr::make(klass->java_mirror());
+        }
+      }
+    } else {
+      // Check for a load of the default value offset from the InlineKlassFixedBlock:
+      // LoadI(LoadP(inline_klass, adr_inlineklass_fixed_block_offset), default_value_offset_offset)
+      // TODO 8325106 remove?
+      intptr_t offset = 0;
+      Node* base = AddPNode::Ideal_base_and_offset(adr, phase, offset);
+      if (base != nullptr && base->is_Load() && offset == in_bytes(InlineKlass::default_value_offset_offset())) {
+        const TypeKlassPtr* tkls = phase->type(base->in(MemNode::Address))->isa_klassptr();
+        if (tkls != nullptr && tkls->is_loaded() && tkls->klass_is_exact() && tkls->exact_klass()->is_inlinetype() &&
+            tkls->offset() == in_bytes(InstanceKlass::adr_inlineklass_fixed_block_offset())) {
+          assert(base->Opcode() == Op_LoadP, "must load an oop from klass");
+          assert(Opcode() == Op_LoadI, "must load an int from fixed block");
+          return TypeInt::make(tkls->exact_klass()->as_inline_klass()->default_value_offset());
+        }
       }
     }
   }
 
   const TypeKlassPtr *tkls = tp->isa_klassptr();
@@ -2186,14 +2236,22 @@
     if (mem->is_Parm() && mem->in(0)->is_Start()) {
       assert(mem->as_Parm()->_con == TypeFunc::Memory, "must be memory Parm");
       return Type::get_zero_type(_type->basic_type());
     }
   }
-
   Node* alloc = is_new_object_mark_load();
   if (alloc != nullptr) {
-    return TypeX::make(markWord::prototype().value());
+    if (EnableValhalla) {
+      // The mark word may contain property bits (inline, flat, null-free)
+      Node* klass_node = alloc->in(AllocateNode::KlassNode);
+      const TypeKlassPtr* tkls = phase->type(klass_node)->isa_klassptr();
+      if (tkls != nullptr && tkls->is_loaded() && tkls->klass_is_exact()) {
+        return TypeX::make(tkls->exact_klass()->prototype_header().value());
+      }
+    } else {
+      return TypeX::make(markWord::prototype().value());
+    }
   }
 
   return _type;
 }
 
@@ -2340,11 +2398,12 @@
 }
 
 //=============================================================================
 //----------------------------LoadKlassNode::make------------------------------
 // Polymorphic factory method:
-Node* LoadKlassNode::make(PhaseGVN& gvn, Node* ctl, Node* mem, Node* adr, const TypePtr* at, const TypeKlassPtr* tk) {
+Node* LoadKlassNode::make(PhaseGVN& gvn, Node* ctl, Node* mem, Node* adr, const TypePtr* at,
+                          const TypeKlassPtr* tk) {
   // sanity check the alias category against the created node type
   const TypePtr *adr_type = adr->bottom_type()->isa_ptr();
   assert(adr_type != nullptr, "expecting TypeKlassPtr");
 #ifdef _LP64
   if (adr_type->is_ptr_to_narrowklass()) {
@@ -2416,11 +2475,11 @@
       return tinst->as_klass_type(true);
     }
   }
 
   // Check for loading klass from an array
-  const TypeAryPtr *tary = tp->isa_aryptr();
+  const TypeAryPtr* tary = tp->isa_aryptr();
   if (tary != nullptr &&
       tary->offset() == oopDesc::klass_offset_in_bytes()) {
     return tary->as_klass_type(true);
   }
 
@@ -2698,11 +2757,11 @@
   Node* value   = in(MemNode::ValueIn);
   // Back-to-back stores to same address?  Fold em up.  Generally
   // unsafe if I have intervening uses...  Also disallowed for StoreCM
   // since they must follow each StoreP operation.  Redundant StoreCMs
   // are eliminated just before matching in final_graph_reshape.
-  {
+  if (phase->C->get_adr_type(phase->C->get_alias_index(adr_type())) != TypeAryPtr::INLINES) {
     Node* st = mem;
     // If Store 'st' has more than one use, we cannot fold 'st' away.
     // For example, 'st' might be the final state at a conditional
     // return.  Or, 'st' might be used by some node which is live at
     // the same time 'st' is live, which might be unschedulable.  So,
@@ -2718,10 +2777,11 @@
              st->Opcode() == Op_StoreVectorScatter ||
              Opcode() == Op_StoreVectorScatter ||
              phase->C->get_alias_index(adr_type()) == Compile::AliasIdxRaw ||
              (Opcode() == Op_StoreL && st->Opcode() == Op_StoreI) || // expanded ClearArrayNode
              (Opcode() == Op_StoreI && st->Opcode() == Op_StoreL) || // initialization by arraycopy
+             (Opcode() == Op_StoreL && st->Opcode() == Op_StoreN) ||
              (is_mismatched_access() || st->as_Store()->is_mismatched_access()),
              "no mismatched stores, except on raw memory: %s %s", NodeClassNames[Opcode()], NodeClassNames[st->Opcode()]);
 
       if (st->in(MemNode::Address)->eqv_uncast(address) &&
           st->as_Store()->memory_size() <= this->memory_size()) {
@@ -2814,18 +2874,18 @@
   }
 
   // Store of zero anywhere into a freshly-allocated object?
   // Then the store is useless.
   // (It must already have been captured by the InitializeNode.)
-  if (result == this &&
-      ReduceFieldZeroing && phase->type(val)->is_zero_type()) {
+  if (result == this && ReduceFieldZeroing) {
     // a newly allocated object is already all-zeroes everywhere
-    if (mem->is_Proj() && mem->in(0)->is_Allocate()) {
+    if (mem->is_Proj() && mem->in(0)->is_Allocate() &&
+        (phase->type(val)->is_zero_type() || mem->in(0)->in(AllocateNode::DefaultValue) == val)) {
       result = mem;
     }
 
-    if (result == this) {
+    if (result == this && phase->type(val)->is_zero_type()) {
       // the store may also apply to zero-bits in an earlier object
       Node* prev_mem = find_previous_store(phase);
       // Steps (a), (b):  Walk past independent stores to find an exact match.
       if (prev_mem != nullptr) {
         Node* prev_val = can_see_stored_value(prev_mem, phase);
@@ -3006,13 +3066,17 @@
   Node* progress = StoreNode::Ideal(phase, can_reshape);
   if (progress != nullptr) return progress;
 
   Node* my_store = in(MemNode::OopStore);
   if (my_store->is_MergeMem()) {
-    Node* mem = my_store->as_MergeMem()->memory_at(oop_alias_idx());
-    set_req_X(MemNode::OopStore, mem, phase);
-    return this;
+    if (oop_alias_idx() != phase->C->get_alias_index(TypeAryPtr::INLINES) ||
+        phase->C->flat_accesses_share_alias()) {
+      // The alias that was recorded is no longer accurate enough.
+      Node* mem = my_store->as_MergeMem()->memory_at(oop_alias_idx());
+      set_req_X(MemNode::OopStore, mem, phase);
+      return this;
+    }
   }
 
   return nullptr;
 }
 
@@ -3179,11 +3243,11 @@
   if (size <= 0 || size % unit != 0)  return nullptr;
   intptr_t count = size / unit;
   // Length too long; communicate this to matchers and assemblers.
   // Assemblers are responsible to produce fast hardware clears for it.
   if (size > InitArrayShortSize) {
-    return new ClearArrayNode(in(0), in(1), in(2), in(3), true);
+    return new ClearArrayNode(in(0), in(1), in(2), in(3), in(4), true);
   } else if (size > 2 && Matcher::match_rule_supported_vector(Op_ClearArray, 4, T_LONG)) {
     return nullptr;
   }
   if (!IdealizeClearArrayNode) return nullptr;
   Node *mem = in(1);
@@ -3197,18 +3261,18 @@
   else              atp = atp->add_offset(Type::OffsetBot);
   // Get base for derived pointer purposes
   if( adr->Opcode() != Op_AddP ) Unimplemented();
   Node *base = adr->in(1);
 
-  Node *zero = phase->makecon(TypeLong::ZERO);
+  Node *val = in(4);
   Node *off  = phase->MakeConX(BytesPerLong);
-  mem = new StoreLNode(in(0),mem,adr,atp,zero,MemNode::unordered,false);
+  mem = new StoreLNode(in(0), mem, adr, atp, val, MemNode::unordered, false);
   count--;
   while( count-- ) {
     mem = phase->transform(mem);
     adr = phase->transform(new AddPNode(base,adr,off));
-    mem = new StoreLNode(in(0),mem,adr,atp,zero,MemNode::unordered,false);
+    mem = new StoreLNode(in(0), mem, adr, atp, val, MemNode::unordered, false);
   }
   return mem;
 }
 
 //----------------------------step_through----------------------------------
@@ -3238,31 +3302,40 @@
 }
 
 //----------------------------clear_memory-------------------------------------
 // Generate code to initialize object storage to zero.
 Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest,
+                                   Node* val,
+                                   Node* raw_val,
                                    intptr_t start_offset,
                                    Node* end_offset,
                                    PhaseGVN* phase) {
   intptr_t offset = start_offset;
 
   int unit = BytesPerLong;
   if ((offset % unit) != 0) {
     Node* adr = new AddPNode(dest, dest, phase->MakeConX(offset));
     adr = phase->transform(adr);
     const TypePtr* atp = TypeRawPtr::BOTTOM;
-    mem = StoreNode::make(*phase, ctl, mem, adr, atp, phase->zerocon(T_INT), T_INT, MemNode::unordered);
+    if (val != nullptr) {
+      assert(phase->type(val)->isa_narrowoop(), "should be narrow oop");
+      mem = new StoreNNode(ctl, mem, adr, atp, val, MemNode::unordered);
+    } else {
+      assert(raw_val == nullptr, "val may not be null");
+      mem = StoreNode::make(*phase, ctl, mem, adr, atp, phase->zerocon(T_INT), T_INT, MemNode::unordered);
+    }
     mem = phase->transform(mem);
     offset += BytesPerInt;
   }
   assert((offset % unit) == 0, "");
 
   // Initialize the remaining stuff, if any, with a ClearArray.
-  return clear_memory(ctl, mem, dest, phase->MakeConX(offset), end_offset, phase);
+  return clear_memory(ctl, mem, dest, raw_val, phase->MakeConX(offset), end_offset, phase);
 }
 
 Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest,
+                                   Node* raw_val,
                                    Node* start_offset,
                                    Node* end_offset,
                                    PhaseGVN* phase) {
   if (start_offset == end_offset) {
     // nothing to do
@@ -3281,15 +3354,20 @@
   }
 
   // Bulk clear double-words
   Node* zsize = phase->transform(new SubXNode(zend, zbase) );
   Node* adr = phase->transform(new AddPNode(dest, dest, start_offset) );
-  mem = new ClearArrayNode(ctl, mem, zsize, adr, false);
+  if (raw_val == nullptr) {
+    raw_val = phase->MakeConX(0);
+  }
+  mem = new ClearArrayNode(ctl, mem, zsize, adr, raw_val, false);
   return phase->transform(mem);
 }
 
 Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest,
+                                   Node* val,
+                                   Node* raw_val,
                                    intptr_t start_offset,
                                    intptr_t end_offset,
                                    PhaseGVN* phase) {
   if (start_offset == end_offset) {
     // nothing to do
@@ -3300,18 +3378,24 @@
   intptr_t done_offset = end_offset;
   if ((done_offset % BytesPerLong) != 0) {
     done_offset -= BytesPerInt;
   }
   if (done_offset > start_offset) {
-    mem = clear_memory(ctl, mem, dest,
+    mem = clear_memory(ctl, mem, dest, val, raw_val,
                        start_offset, phase->MakeConX(done_offset), phase);
   }
   if (done_offset < end_offset) { // emit the final 32-bit store
     Node* adr = new AddPNode(dest, dest, phase->MakeConX(done_offset));
     adr = phase->transform(adr);
     const TypePtr* atp = TypeRawPtr::BOTTOM;
-    mem = StoreNode::make(*phase, ctl, mem, adr, atp, phase->zerocon(T_INT), T_INT, MemNode::unordered);
+    if (val != nullptr) {
+      assert(phase->type(val)->isa_narrowoop(), "should be narrow oop");
+      mem = new StoreNNode(ctl, mem, adr, atp, val, MemNode::unordered);
+    } else {
+      assert(raw_val == nullptr, "val may not be null");
+      mem = StoreNode::make(*phase, ctl, mem, adr, atp, phase->zerocon(T_INT), T_INT, MemNode::unordered);
+    }
     mem = phase->transform(mem);
     done_offset += BytesPerInt;
   }
   assert(done_offset == end_offset, "");
   return mem;
@@ -3452,11 +3536,11 @@
   return TypeTuple::MEMBAR;
 }
 
 //------------------------------match------------------------------------------
 // Construct projections for memory.
-Node *MemBarNode::match( const ProjNode *proj, const Matcher *m ) {
+Node *MemBarNode::match(const ProjNode *proj, const Matcher *m, const RegMask* mask) {
   switch (proj->_con) {
   case TypeFunc::Control:
   case TypeFunc::Memory:
     return new MachProjNode(this,proj->_con,RegMask::Empty,MachProjNode::unmatched_proj);
   }
@@ -3739,11 +3823,13 @@
 
 // convenience function
 // return false if the init contains any stores already
 bool AllocateNode::maybe_set_complete(PhaseGVN* phase) {
   InitializeNode* init = initialization();
-  if (init == nullptr || init->is_complete())  return false;
+  if (init == nullptr || init->is_complete()) {
+    return false;
+  }
   init->remove_extra_zeroes();
   // for now, if this allocation has already collected any inits, bail:
   if (init->is_non_zero())  return false;
   init->set_complete(phase);
   return true;
@@ -3917,10 +4003,16 @@
                 // after the InitializeNode. We check the control of the
                 // object/array that is loaded from. If it's the same as
                 // the store control then we cannot capture the store.
                 assert(!n->is_Store(), "2 stores to same slice on same control?");
                 Node* base = other_adr;
+                if (base->is_Phi()) {
+                  // In rare case, base may be a PhiNode and it may read
+                  // the same memory slice between InitializeNode and store.
+                  failed = true;
+                  break;
+                }
                 assert(base->is_AddP(), "should be addp but is %s", base->Name());
                 base = base->in(AddPNode::Base);
                 if (base != nullptr) {
                   base = base->uncast();
                   if (base->is_Proj() && base->in(0) == alloc) {
@@ -4503,10 +4595,12 @@
       if (zeroes_needed > zeroes_done) {
         intptr_t zsize = zeroes_needed - zeroes_done;
         // Do some incremental zeroing on rawmem, in parallel with inits.
         zeroes_done = align_down(zeroes_done, BytesPerInt);
         rawmem = ClearArrayNode::clear_memory(rawctl, rawmem, rawptr,
+                                              allocation()->in(AllocateNode::DefaultValue),
+                                              allocation()->in(AllocateNode::RawDefaultValue),
                                               zeroes_done, zeroes_needed,
                                               phase);
         zeroes_done = zeroes_needed;
         if (zsize > InitArrayShortSize && ++big_init_gaps > 2)
           do_zeroing = false;   // leave the hole, next time
@@ -4562,10 +4656,12 @@
           zeroes_done = size_limit;
       }
     }
     if (zeroes_done < size_limit) {
       rawmem = ClearArrayNode::clear_memory(rawctl, rawmem, rawptr,
+                                            allocation()->in(AllocateNode::DefaultValue),
+                                            allocation()->in(AllocateNode::RawDefaultValue),
                                             zeroes_done, size_in_bytes, phase);
     }
   }
 
   set_complete(phase);
diff a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp
--- a/src/hotspot/share/opto/memnode.hpp
+++ b/src/hotspot/share/opto/memnode.hpp
@@ -124,10 +124,14 @@
 #else
     return 0;
 #endif
   }
 
+#ifdef ASSERT
+  void set_adr_type(const TypePtr* adr_type) { _adr_type = adr_type; }
+#endif
+
   // Return the barrier data of n, if available, or 0 otherwise.
   static uint8_t barrier_data(const Node* n);
 
   // Map a load or store opcode to its corresponding store opcode.
   // (Return -1 if unknown.)
@@ -553,11 +557,10 @@
   virtual const Type* Value(PhaseGVN* phase) const;
   virtual Node* Identity(PhaseGVN* phase);
   virtual bool depends_only_on_test() const { return true; }
 };
 
-
 //------------------------------StoreNode--------------------------------------
 // Store value; requires Store, Address and Value
 class StoreNode : public MemNode {
 private:
   // On platforms with weak memory ordering (e.g., PPC, Ia64) we distinguish
@@ -1092,13 +1095,15 @@
 
 //------------------------------ClearArray-------------------------------------
 class ClearArrayNode: public Node {
 private:
   bool _is_large;
+  bool _word_copy_only;
 public:
-  ClearArrayNode( Node *ctrl, Node *arymem, Node *word_cnt, Node *base, bool is_large)
-    : Node(ctrl,arymem,word_cnt,base), _is_large(is_large) {
+  ClearArrayNode( Node *ctrl, Node *arymem, Node *word_cnt, Node *base, Node* val, bool is_large)
+    : Node(ctrl, arymem, word_cnt, base, val), _is_large(is_large),
+      _word_copy_only(val->bottom_type()->isa_long() && (!val->bottom_type()->is_long()->is_con() || val->bottom_type()->is_long()->get_con() != 0)) {
     init_class_id(Class_ClearArray);
   }
   virtual int         Opcode() const;
   virtual const Type *bottom_type() const { return Type::MEMORY; }
   // ClearArray modifies array elements, and so affects only the
@@ -1106,24 +1111,30 @@
   virtual const class TypePtr *adr_type() const;
   virtual Node* Identity(PhaseGVN* phase);
   virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
   virtual uint match_edge(uint idx) const;
   bool is_large() const { return _is_large; }
+  bool word_copy_only() const { return _word_copy_only; }
 
   // Clear the given area of an object or array.
   // The start offset must always be aligned mod BytesPerInt.
   // The end offset must always be aligned mod BytesPerLong.
   // Return the new memory.
   static Node* clear_memory(Node* control, Node* mem, Node* dest,
+                            Node* val,
+                            Node* raw_val,
                             intptr_t start_offset,
                             intptr_t end_offset,
                             PhaseGVN* phase);
   static Node* clear_memory(Node* control, Node* mem, Node* dest,
+                            Node* val,
+                            Node* raw_val,
                             intptr_t start_offset,
                             Node* end_offset,
                             PhaseGVN* phase);
   static Node* clear_memory(Node* control, Node* mem, Node* dest,
+                            Node* raw_val,
                             Node* start_offset,
                             Node* end_offset,
                             PhaseGVN* phase);
   // Return allocation input memory edge if it is different instance
   // or itself if it is the one we are looking for.
@@ -1171,11 +1182,11 @@
   virtual const class TypePtr *adr_type() const { return _adr_type; }
   virtual const Type* Value(PhaseGVN* phase) const;
   virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
   virtual uint match_edge(uint idx) const { return 0; }
   virtual const Type *bottom_type() const { return TypeTuple::MEMBAR; }
-  virtual Node *match( const ProjNode *proj, const Matcher *m );
+  virtual Node *match(const ProjNode *proj, const Matcher *m, const RegMask* mask);
   // Factory method.  Builds a wide or narrow membar.
   // Optional 'precedent' becomes an extra edge if not null.
   static MemBarNode* make(Compile* C, int opcode,
                           int alias_idx = Compile::AliasIdxBot,
                           Node* precedent = nullptr);
diff a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp
--- a/src/hotspot/share/opto/mulnode.cpp
+++ b/src/hotspot/share/opto/mulnode.cpp
@@ -195,10 +195,22 @@
     const Type *zero = add_id();        // The multiplicative zero
     if( t1->higher_equal( zero ) ) return zero;
     if( t2->higher_equal( zero ) ) return zero;
   }
 
+  // Code pattern on return from a call that returns an __Value.  Can
+  // be optimized away if the return value turns out to be an oop.
+  if (op == Op_AndX &&
+      in(1) != nullptr &&
+      in(1)->Opcode() == Op_CastP2X &&
+      in(1)->in(1) != nullptr &&
+      phase->type(in(1)->in(1))->isa_oopptr() &&
+      t2->isa_intptr_t()->_lo >= 0 &&
+      t2->isa_intptr_t()->_hi <= MinObjAlignmentInBytesMask) {
+    return add_id();
+  }
+
   // Either input is BOTTOM ==> the result is the local BOTTOM
   if( t1 == Type::BOTTOM || t2 == Type::BOTTOM )
     return bottom_type();
 
 #if defined(IA32)
@@ -813,10 +825,18 @@
         jlong mask = max_julong >> shift;
         if( (mask&con) == mask )  // If AND is useless, skip it
           return usr;
       }
     }
+
+    // Check if this is part of an inline type test
+    if (con == markWord::inline_type_pattern && in(1)->is_Load() &&
+        phase->type(in(1)->in(MemNode::Address))->is_inlinetypeptr() &&
+        phase->type(in(1)->in(MemNode::Address))->is_ptr()->offset() == oopDesc::mark_offset_in_bytes()) {
+      assert(EnableValhalla, "should only be used for inline types");
+      return in(2); // Obj is known to be an inline type
+    }
   }
   return MulNode::Identity(phase);
 }
 
 //------------------------------Ideal------------------------------------------
diff a/src/hotspot/share/opto/multnode.cpp b/src/hotspot/share/opto/multnode.cpp
--- a/src/hotspot/share/opto/multnode.cpp
+++ b/src/hotspot/share/opto/multnode.cpp
@@ -38,11 +38,11 @@
 //------------------------------MultiNode--------------------------------------
 const RegMask &MultiNode::out_RegMask() const {
   return RegMask::Empty;
 }
 
-Node *MultiNode::match( const ProjNode *proj, const Matcher *m ) { return proj->clone(); }
+Node *MultiNode::match(const ProjNode *proj, const Matcher *m, const RegMask* mask) { return proj->clone(); }
 
 //------------------------------proj_out---------------------------------------
 // Get a named projection or null if not found
 ProjNode* MultiNode::proj_out_or_null(uint which_proj) const {
   assert((Opcode() != Op_If && Opcode() != Op_RangeCheck) || which_proj == (uint)true || which_proj == (uint)false, "must be 1 or 0");
diff a/src/hotspot/share/opto/multnode.hpp b/src/hotspot/share/opto/multnode.hpp
--- a/src/hotspot/share/opto/multnode.hpp
+++ b/src/hotspot/share/opto/multnode.hpp
@@ -42,11 +42,11 @@
   virtual const Type *bottom_type() const = 0;
   virtual bool       is_CFG() const { return true; }
   virtual uint hash() const { return NO_HASH; }  // CFG nodes do not hash
   virtual bool depends_only_on_test() const { return false; }
   virtual const RegMask &out_RegMask() const;
-  virtual Node *match( const ProjNode *proj, const Matcher *m );
+  virtual Node *match(const ProjNode *proj, const Matcher *m, const RegMask* mask);
   virtual uint ideal_reg() const { return NotAMachineReg; }
   ProjNode* proj_out(uint which_proj) const; // Get a named projection
   ProjNode* proj_out_or_null(uint which_proj) const;
   ProjNode* proj_out_or_null(uint which_proj, bool is_io_use) const;
 };
diff a/src/hotspot/share/opto/narrowptrnode.cpp b/src/hotspot/share/opto/narrowptrnode.cpp
--- a/src/hotspot/share/opto/narrowptrnode.cpp
+++ b/src/hotspot/share/opto/narrowptrnode.cpp
@@ -40,11 +40,11 @@
 const Type* DecodeNNode::Value(PhaseGVN* phase) const {
   const Type *t = phase->type( in(1) );
   if (t == Type::TOP) return Type::TOP;
   if (t == TypeNarrowOop::NULL_PTR) return TypePtr::NULL_PTR;
 
-  assert(t->isa_narrowoop(), "only  narrowoop here");
+  assert(t->isa_narrowoop(), "only narrowoop here");
   return t->make_ptr();
 }
 
 Node* EncodePNode::Identity(PhaseGVN* phase) {
   const Type *t = phase->type( in(1) );
diff a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp
--- a/src/hotspot/share/opto/node.cpp
+++ b/src/hotspot/share/opto/node.cpp
@@ -31,10 +31,11 @@
 #include "opto/ad.hpp"
 #include "opto/callGenerator.hpp"
 #include "opto/castnode.hpp"
 #include "opto/cfgnode.hpp"
 #include "opto/connode.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/loopnode.hpp"
 #include "opto/machnode.hpp"
 #include "opto/matcher.hpp"
 #include "opto/node.hpp"
 #include "opto/opcodes.hpp"
@@ -557,10 +558,13 @@
     // Scalar replacement and macro expansion might modify the JVMState.
     // Clone it to make sure it's not shared between SafePointNodes.
     n->as_SafePoint()->clone_jvms(C);
     n->as_SafePoint()->clone_replaced_nodes();
   }
+  if (n->is_InlineType()) {
+    C->add_inline_type(n);
+  }
   Compile::current()->record_modified_node(n);
   return n;                     // Return the clone
 }
 
 //---------------------------setup_is_top--------------------------------------
@@ -617,10 +621,13 @@
     compile->remove_parse_predicate(as_ParsePredicate());
   }
   if (for_post_loop_opts_igvn()) {
     compile->remove_from_post_loop_opts_igvn(this);
   }
+  if (is_InlineType()) {
+    compile->remove_inline_type(this);
+  }
 
   if (is_SafePoint()) {
     as_SafePoint()->delete_replaced_nodes();
 
     if (is_CallStaticJava()) {
diff a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp
--- a/src/hotspot/share/opto/node.hpp
+++ b/src/hotspot/share/opto/node.hpp
@@ -81,10 +81,11 @@
 class EncodeNarrowPtrNode;
 class EncodePNode;
 class EncodePKlassNode;
 class FastLockNode;
 class FastUnlockNode;
+class FlatArrayCheckNode;
 class HaltNode;
 class IfNode;
 class IfProjNode;
 class IfFalseNode;
 class IfTrueNode;
@@ -113,16 +114,18 @@
 class MachIfNode;
 class MachJumpNode;
 class MachNode;
 class MachNullCheckNode;
 class MachProjNode;
+class MachPrologNode;
 class MachReturnNode;
 class MachSafePointNode;
 class MachSpillCopyNode;
 class MachTempNode;
 class MachMergeNode;
 class MachMemBarNode;
+class MachVEPNode;
 class Matcher;
 class MemBarNode;
 class MemBarStoreStoreNode;
 class MemNode;
 class MergeMemNode;
@@ -168,10 +171,11 @@
 class SubNode;
 class SubTypeCheckNode;
 class Type;
 class TypeNode;
 class UnlockNode;
+class InlineTypeNode;
 class UnorderedReductionNode;
 class VectorNode;
 class LoadVectorNode;
 class LoadVectorMaskedNode;
 class StoreVectorMaskedNode;
@@ -678,10 +682,11 @@
         DEFINE_CLASS_ID(NeverBranch, MultiBranch, 2)
       DEFINE_CLASS_ID(Start,       Multi, 2)
       DEFINE_CLASS_ID(MemBar,      Multi, 3)
         DEFINE_CLASS_ID(Initialize,       MemBar, 0)
         DEFINE_CLASS_ID(MemBarStoreStore, MemBar, 1)
+        DEFINE_CLASS_ID(Blackhole,        MemBar, 2)
 
     DEFINE_CLASS_ID(Mach,  Node, 1)
       DEFINE_CLASS_ID(MachReturn, Mach, 0)
         DEFINE_CLASS_ID(MachSafePoint, MachReturn, 0)
           DEFINE_CLASS_ID(MachCall, MachSafePoint, 0)
@@ -699,10 +704,12 @@
       DEFINE_CLASS_ID(MachConstantBase, Mach, 4)
       DEFINE_CLASS_ID(MachConstant,     Mach, 5)
         DEFINE_CLASS_ID(MachJump,       MachConstant, 0)
       DEFINE_CLASS_ID(MachMerge,        Mach, 6)
       DEFINE_CLASS_ID(MachMemBar,       Mach, 7)
+      DEFINE_CLASS_ID(MachProlog,       Mach, 8)
+      DEFINE_CLASS_ID(MachVEP,          Mach, 9)
 
     DEFINE_CLASS_ID(Type,  Node, 2)
       DEFINE_CLASS_ID(Phi,   Type, 0)
       DEFINE_CLASS_ID(ConstraintCast, Type, 1)
         DEFINE_CLASS_ID(CastII, ConstraintCast, 0)
@@ -725,17 +732,18 @@
         DEFINE_CLASS_ID(VectorReinterpret, Vector, 2)
         DEFINE_CLASS_ID(ShiftV, Vector, 3)
         DEFINE_CLASS_ID(CompressV, Vector, 4)
         DEFINE_CLASS_ID(ExpandV, Vector, 5)
         DEFINE_CLASS_ID(CompressM, Vector, 6)
-        DEFINE_CLASS_ID(Reduction, Vector, 7)
+        DEFINE_CLASS_ID(Reduction, Vector, 9)
           DEFINE_CLASS_ID(UnorderedReduction, Reduction, 0)
         DEFINE_CLASS_ID(NegV, Vector, 8)
-      DEFINE_CLASS_ID(Con, Type, 8)
+      DEFINE_CLASS_ID(InlineType, Type, 8)
+      DEFINE_CLASS_ID(Con, Type, 9)
           DEFINE_CLASS_ID(ConI, Con, 0)
-      DEFINE_CLASS_ID(SafePointScalarMerge, Type, 9)
-      DEFINE_CLASS_ID(Convert, Type, 10)
+      DEFINE_CLASS_ID(SafePointScalarMerge, Type, 10)
+      DEFINE_CLASS_ID(Convert, Type, 11)
 
 
     DEFINE_CLASS_ID(Proj,  Node, 3)
       DEFINE_CLASS_ID(CatchProj, Proj, 0)
       DEFINE_CLASS_ID(JumpProj,  Proj, 1)
@@ -769,13 +777,14 @@
           DEFINE_CLASS_ID(LongCountedLoop,   BaseCountedLoop, 1)
         DEFINE_CLASS_ID(OuterStripMinedLoop, Loop, 2)
 
     DEFINE_CLASS_ID(Sub,   Node, 6)
       DEFINE_CLASS_ID(Cmp,   Sub, 0)
-        DEFINE_CLASS_ID(FastLock,   Cmp, 0)
-        DEFINE_CLASS_ID(FastUnlock, Cmp, 1)
-        DEFINE_CLASS_ID(SubTypeCheck,Cmp, 2)
+        DEFINE_CLASS_ID(FastLock,       Cmp, 0)
+        DEFINE_CLASS_ID(FastUnlock,     Cmp, 1)
+        DEFINE_CLASS_ID(SubTypeCheck,   Cmp, 2)
+        DEFINE_CLASS_ID(FlatArrayCheck, Cmp, 3)
 
     DEFINE_CLASS_ID(MergeMem, Node, 7)
     DEFINE_CLASS_ID(Bool,     Node, 8)
     DEFINE_CLASS_ID(AddP,     Node, 9)
     DEFINE_CLASS_ID(BoxLock,  Node, 10)
@@ -871,10 +880,11 @@
   DEFINE_CLASS_QUERY(Allocate)
   DEFINE_CLASS_QUERY(AllocateArray)
   DEFINE_CLASS_QUERY(ArrayCopy)
   DEFINE_CLASS_QUERY(BaseCountedLoop)
   DEFINE_CLASS_QUERY(BaseCountedLoopEnd)
+  DEFINE_CLASS_QUERY(Blackhole)
   DEFINE_CLASS_QUERY(Bool)
   DEFINE_CLASS_QUERY(BoxLock)
   DEFINE_CLASS_QUERY(Call)
   DEFINE_CLASS_QUERY(CallDynamicJava)
   DEFINE_CLASS_QUERY(CallJava)
@@ -901,10 +911,11 @@
   DEFINE_CLASS_QUERY(EncodeNarrowPtr)
   DEFINE_CLASS_QUERY(EncodeP)
   DEFINE_CLASS_QUERY(EncodePKlass)
   DEFINE_CLASS_QUERY(FastLock)
   DEFINE_CLASS_QUERY(FastUnlock)
+  DEFINE_CLASS_QUERY(FlatArrayCheck)
   DEFINE_CLASS_QUERY(Halt)
   DEFINE_CLASS_QUERY(If)
   DEFINE_CLASS_QUERY(RangeCheck)
   DEFINE_CLASS_QUERY(IfProj)
   DEFINE_CLASS_QUERY(IfFalse)
@@ -933,16 +944,18 @@
   DEFINE_CLASS_QUERY(MachGoto)
   DEFINE_CLASS_QUERY(MachIf)
   DEFINE_CLASS_QUERY(MachJump)
   DEFINE_CLASS_QUERY(MachNullCheck)
   DEFINE_CLASS_QUERY(MachProj)
+  DEFINE_CLASS_QUERY(MachProlog)
   DEFINE_CLASS_QUERY(MachReturn)
   DEFINE_CLASS_QUERY(MachSafePoint)
   DEFINE_CLASS_QUERY(MachSpillCopy)
   DEFINE_CLASS_QUERY(MachTemp)
   DEFINE_CLASS_QUERY(MachMemBar)
   DEFINE_CLASS_QUERY(MachMerge)
+  DEFINE_CLASS_QUERY(MachVEP)
   DEFINE_CLASS_QUERY(Mem)
   DEFINE_CLASS_QUERY(MemBar)
   DEFINE_CLASS_QUERY(MemBarStoreStore)
   DEFINE_CLASS_QUERY(MergeMem)
   DEFINE_CLASS_QUERY(Move)
@@ -969,10 +982,11 @@
   DEFINE_CLASS_QUERY(Start)
   DEFINE_CLASS_QUERY(Store)
   DEFINE_CLASS_QUERY(Sub)
   DEFINE_CLASS_QUERY(SubTypeCheck)
   DEFINE_CLASS_QUERY(Type)
+  DEFINE_CLASS_QUERY(InlineType)
   DEFINE_CLASS_QUERY(UnorderedReduction)
   DEFINE_CLASS_QUERY(Vector)
   DEFINE_CLASS_QUERY(VectorMaskCmp)
   DEFINE_CLASS_QUERY(VectorUnbox)
   DEFINE_CLASS_QUERY(VectorReinterpret)
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
@@ -31,10 +31,11 @@
 #include "compiler/compileBroker.hpp"
 #include "compiler/compilerDirectives.hpp"
 #include "compiler/disassembler.hpp"
 #include "compiler/oopMap.hpp"
 #include "gc/shared/barrierSet.hpp"
+#include "gc/shared/gc_globals.hpp"
 #include "gc/shared/c2/barrierSetC2.hpp"
 #include "memory/allocation.inline.hpp"
 #include "memory/allocation.hpp"
 #include "opto/ad.hpp"
 #include "opto/block.hpp"
@@ -244,11 +245,19 @@
     _buf_sizes(),
     _block(nullptr),
     _index(0) {
   C->set_output(this);
   if (C->stub_name() == nullptr) {
-    _orig_pc_slot = C->fixed_slots() - (sizeof(address) / VMRegImpl::stack_slot_size);
+    int fixed_slots = C->fixed_slots();
+    if (C->needs_stack_repair()) {
+      fixed_slots -= 2;
+    }
+    // TODO 8284443 Only reserve extra slot if needed
+    if (InlineTypeReturnedAsFields) {
+      fixed_slots -= 2;
+    }
+    _orig_pc_slot = fixed_slots - (sizeof(address) / VMRegImpl::stack_slot_size);
   }
 }
 
 PhaseOutput::~PhaseOutput() {
   C->set_output(nullptr);
@@ -285,28 +294,38 @@
   Block *broot = C->cfg()->get_root_block();
 
   const StartNode *start = entry->head()->as_Start();
 
   // Replace StartNode with prolog
-  MachPrologNode *prolog = new MachPrologNode();
+  Label verified_entry;
+  MachPrologNode* prolog = new MachPrologNode(&verified_entry);
   entry->map_node(prolog, 0);
   C->cfg()->map_node_to_block(prolog, entry);
   C->cfg()->unmap_node_from_block(start); // start is no longer in any block
 
   // Virtual methods need an unverified entry point
-
-  if( C->is_osr_compilation() ) {
-    if( PoisonOSREntry ) {
+  if (C->is_osr_compilation()) {
+    if (PoisonOSREntry) {
       // TODO: Should use a ShouldNotReachHereNode...
       C->cfg()->insert( broot, 0, new MachBreakpointNode() );
     }
   } else {
-    if( C->method() && !C->method()->flags().is_static() ) {
-      // Insert unvalidated entry point
-      C->cfg()->insert( broot, 0, new MachUEPNode() );
+    if (C->method()) {
+      if (C->method()->has_scalarized_args()) {
+        // Add entry point to unpack all inline type arguments
+        C->cfg()->insert(broot, 0, new MachVEPNode(&verified_entry, /* verified */ true, /* receiver_only */ false));
+        if (!C->method()->is_static()) {
+          // Add verified/unverified entry points to only unpack inline type receiver at interface calls
+          C->cfg()->insert(broot, 0, new MachVEPNode(&verified_entry, /* verified */ false, /* receiver_only */ false));
+          C->cfg()->insert(broot, 0, new MachVEPNode(&verified_entry, /* verified */ true,  /* receiver_only */ true));
+          C->cfg()->insert(broot, 0, new MachVEPNode(&verified_entry, /* verified */ false, /* receiver_only */ true));
+        }
+      } else if (!C->method()->is_static()) {
+        // Insert unvalidated entry point
+        C->cfg()->insert(broot, 0, new MachUEPNode());
+      }
     }
-
   }
 
   // Break before main entry point
   if ((C->method() && C->directive()->BreakAtExecuteOption) ||
       (OptoBreakpoint && C->is_method_compilation())       ||
@@ -342,10 +361,35 @@
   // Must be done before ScheduleAndBundle due to SPARC delay slots
   uint* blk_starts = NEW_RESOURCE_ARRAY(uint, C->cfg()->number_of_blocks() + 1);
   blk_starts[0] = 0;
   shorten_branches(blk_starts);
 
+  if (!C->is_osr_compilation() && C->has_scalarized_args()) {
+    // Compute the offsets of the entry points required by the inline type calling convention
+    if (!C->method()->is_static()) {
+      // We have entries at the beginning of the method, implemented by the first 4 nodes.
+      // Entry                     (unverified) @ offset 0
+      // Verified_Inline_Entry_RO
+      // Inline_Entry              (unverified)
+      // Verified_Inline_Entry
+      uint offset = 0;
+      _code_offsets.set_value(CodeOffsets::Entry, offset);
+
+      offset += ((MachVEPNode*)broot->get_node(0))->size(C->regalloc());
+      _code_offsets.set_value(CodeOffsets::Verified_Inline_Entry_RO, offset);
+
+      offset += ((MachVEPNode*)broot->get_node(1))->size(C->regalloc());
+      _code_offsets.set_value(CodeOffsets::Inline_Entry, offset);
+
+      offset += ((MachVEPNode*)broot->get_node(2))->size(C->regalloc());
+      _code_offsets.set_value(CodeOffsets::Verified_Inline_Entry, offset);
+    } else {
+      _code_offsets.set_value(CodeOffsets::Entry, -1); // will be patched later
+      _code_offsets.set_value(CodeOffsets::Verified_Inline_Entry, 0);
+    }
+  }
+
   ScheduleAndBundle();
   if (C->failing()) {
     return;
   }
 
@@ -502,11 +546,13 @@
           reloc_size += CallStubImpl::reloc_call_trampoline();
 
           MachCallNode *mcall = mach->as_MachCall();
           // This destination address is NOT PC-relative
 
-          mcall->method_set((intptr_t)mcall->entry_point());
+          if (mcall->entry_point() != nullptr) {
+            mcall->method_set((intptr_t)mcall->entry_point());
+          }
 
           if (mcall->is_MachCallJava() && mcall->as_MachCallJava()->_method) {
             stub_size  += CompiledStaticCall::to_interp_stub_size();
             reloc_size += CompiledStaticCall::reloc_to_interp_stub();
           }
@@ -757,15 +803,31 @@
     ObjectValue* sv = (ObjectValue*) sv_for_node_id(objs, spobj->_idx);
     if (sv == nullptr) {
       ciKlass* cik = t->is_oopptr()->exact_klass();
       assert(cik->is_instance_klass() ||
              cik->is_array_klass(), "Not supported allocation.");
+      uint first_ind = spobj->first_index(sfpt->jvms());
+      // Nullable, scalarized inline types have an is_init input
+      // that needs to be checked before using the field values.
+      ScopeValue* is_init = nullptr;
+      if (cik->is_inlinetype()) {
+        Node* init_node = sfpt->in(first_ind++);
+        assert(init_node != nullptr, "is_init node not found");
+        if (!init_node->is_top()) {
+          const TypeInt* init_type = init_node->bottom_type()->is_int();
+          if (init_node->is_Con()) {
+            is_init = new ConstantIntValue(init_type->get_con());
+          } else {
+            OptoReg::Name init_reg = C->regalloc()->get_reg_first(init_node);
+            is_init = new_loc_value(C->regalloc(), init_reg, Location::normal);
+          }
+        }
+      }
       sv = new ObjectValue(spobj->_idx,
-                           new ConstantOopWriteValue(cik->java_mirror()->constant_encoding()));
+                           new ConstantOopWriteValue(cik->java_mirror()->constant_encoding()), is_init);
       set_sv_for_object_node(objs, sv);
 
-      uint first_ind = spobj->first_index(sfpt->jvms());
       for (uint i = 0; i < spobj->n_fields(); i++) {
         Node* fld_node = sfpt->in(first_ind+i);
         (void)FillLocArray(sv->field_values()->length(), sfpt, fld_node, sv->field_values(), objs);
       }
     }
@@ -980,10 +1042,11 @@
   MachCallNode      *mcall;
 
   int safepoint_pc_offset = current_offset;
   bool is_method_handle_invoke = false;
   bool return_oop = false;
+  bool return_scalarized = false;
   bool has_ea_local_in_scope = sfn->_has_ea_local_in_scope;
   bool arg_escape = false;
 
   // Add the safepoint in the DebugInfoRecorder
   if( !mach->is_MachCall() ) {
@@ -1000,13 +1063,16 @@
       }
       arg_escape = mcall->as_MachCallJava()->_arg_escape;
     }
 
     // Check if a call returns an object.
-    if (mcall->returns_pointer()) {
+    if (mcall->returns_pointer() || mcall->returns_scalarized()) {
       return_oop = true;
     }
+    if (mcall->returns_scalarized()) {
+      return_scalarized = true;
+    }
     safepoint_pc_offset += mcall->ret_addr_offset();
     C->debug_info()->add_safepoint(safepoint_pc_offset, mcall->_oop_map);
   }
 
   // Loop over the JVMState list to add scope information
@@ -1165,10 +1231,11 @@
       jvms->bci(),
       jvms->should_reexecute(),
       rethrow_exception,
       is_method_handle_invoke,
       return_oop,
+      return_scalarized,
       has_ea_local_in_scope,
       arg_escape,
       locvals,
       expvals,
       monvals
@@ -1540,12 +1607,14 @@
         bool observe_safepoint = is_sfn;
         // Remember the start of the last call in a basic block
         if (is_mcall) {
           MachCallNode *mcall = mach->as_MachCall();
 
-          // This destination address is NOT PC-relative
-          mcall->method_set((intptr_t)mcall->entry_point());
+          if (mcall->entry_point() != nullptr) {
+            // This destination address is NOT PC-relative
+            mcall->method_set((intptr_t)mcall->entry_point());
+          }
 
           // Save the return address
           call_returns[block->_pre_order] = current_offset + mcall->ret_addr_offset();
 
           observe_safepoint = mcall->guaranteed_safepoint();
@@ -1705,11 +1774,10 @@
         return;
       }
 
       assert(!is_mcall || (call_returns[block->_pre_order] <= (uint)current_offset),
              "ret_addr_offset() not within emitted code");
-
 #ifdef ASSERT
       uint n_size = n->size(C->regalloc());
       if (n_size < (current_offset-instr_offset)) {
         MachNode* mach = n->as_Mach();
         n->dump();
@@ -3138,10 +3206,23 @@
             t->is_ptr()->offset() != 0 ) {
           last_safept_node->add_prec( m );
           break;
         }
       }
+
+      // Do not allow a CheckCastPP node whose input is a raw pointer to
+      // float past a safepoint.  This can occur when a buffered inline
+      // type is allocated in a loop and the CheckCastPP from that
+      // allocation is reused outside the loop.  If the use inside the
+      // loop is scalarized the CheckCastPP will no longer be connected
+      // to the loop safepoint.  See JDK-8264340.
+      if (m->is_Mach() && m->as_Mach()->ideal_Opcode() == Op_CheckCastPP) {
+        Node *def = m->in(1);
+        if (def != nullptr && def->bottom_type()->base() == Type::RawPtr) {
+          last_safept_node->add_prec(m);
+        }
+      }
     }
 
     if( n->jvms() ) {           // Precedence edge from derived to safept
       // Check if last_safept_node was moved by pinch-point insertion in anti_do_use()
       if( b->get_node(last_safept) != last_safept_node ) {
@@ -3296,10 +3377,29 @@
     }
 
     ResourceMark rm;
     _scratch_const_size = const_size;
     int size = C2Compiler::initial_code_buffer_size(const_size);
+    if (C->has_scalarized_args()) {
+      // Inline type entry points (MachVEPNodes) require lots of space for GC barriers and oop verification
+      // when loading object fields from the buffered argument. Increase scratch buffer size accordingly.
+      ciMethod* method = C->method();
+      int barrier_size = UseZGC ? 200 : (7 DEBUG_ONLY(+ 37));
+      int arg_num = 0;
+      if (!method->is_static()) {
+        if (method->is_scalarized_arg(arg_num)) {
+          size += method->holder()->as_inline_klass()->oop_count() * barrier_size;
+        }
+        arg_num++;
+      }
+      for (ciSignatureStream str(method->signature()); !str.at_return_type(); str.next()) {
+        if (method->is_scalarized_arg(arg_num)) {
+          size += str.type()->as_inline_klass()->oop_count() * barrier_size;
+        }
+        arg_num++;
+      }
+    }
     blob = BufferBlob::create("Compile::scratch_buffer", size);
     // Record the buffer blob for next time.
     set_scratch_buffer_blob(blob);
     // Have we run out of code space?
     if (scratch_buffer_blob() == nullptr) {
@@ -3366,12 +3466,14 @@
   n->emit(buf, C->regalloc());
 
   // Emitting into the scratch buffer should not fail
   assert (!C->failing(), "Must not have pending failure. Reason is: %s", C->failure_reason());
 
-  if (is_branch) // Restore label.
+  // Restore label.
+  if (is_branch) {
     n->as_MachBranch()->label_set(saveL, save_bnum);
+  }
 
   // End scratch_emit_size section.
   set_in_scratch_emit_size(false);
 
   return buf.insts_size();
@@ -3411,28 +3513,37 @@
     if (C->is_osr_compilation()) {
       _code_offsets.set_value(CodeOffsets::Verified_Entry, 0);
       _code_offsets.set_value(CodeOffsets::OSR_Entry, _first_block_size);
     } else {
       _code_offsets.set_value(CodeOffsets::Verified_Entry, _first_block_size);
+      if (_code_offsets.value(CodeOffsets::Verified_Inline_Entry) == -1) {
+        _code_offsets.set_value(CodeOffsets::Verified_Inline_Entry, _first_block_size);
+      }
+      if (_code_offsets.value(CodeOffsets::Verified_Inline_Entry_RO) == -1) {
+        _code_offsets.set_value(CodeOffsets::Verified_Inline_Entry_RO, _first_block_size);
+      }
+      if (_code_offsets.value(CodeOffsets::Entry) == -1) {
+        _code_offsets.set_value(CodeOffsets::Entry, _first_block_size);
+      }
       _code_offsets.set_value(CodeOffsets::OSR_Entry, 0);
     }
 
     C->env()->register_method(target,
-                                     entry_bci,
-                                     &_code_offsets,
-                                     _orig_pc_slot_offset_in_bytes,
-                                     code_buffer(),
-                                     frame_size_in_words(),
-                                     oop_map_set(),
-                                     &_handler_table,
-                                     inc_table(),
-                                     compiler,
-                                     has_unsafe_access,
-                                     SharedRuntime::is_wide_vector(C->max_vector_size()),
-                                     C->has_monitors(),
-                                     0,
-                                     C->rtm_state());
+                              entry_bci,
+                              &_code_offsets,
+                              _orig_pc_slot_offset_in_bytes,
+                              code_buffer(),
+                              frame_size_in_words(),
+                              _oop_map_set,
+                              &_handler_table,
+                              inc_table(),
+                              compiler,
+                              has_unsafe_access,
+                              SharedRuntime::is_wide_vector(C->max_vector_size()),
+                              C->has_monitors(),
+                              0,
+                              C->rtm_state());
 
     if (C->log() != nullptr) { // Print code cache state into compiler log
       C->log()->code_cache_state();
     }
   }
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
@@ -443,11 +443,11 @@
  private:
   // Create a JVMS & map for the initial state of this method.
   SafePointNode* create_entry_map();
 
   // OSR helpers
-  Node *fetch_interpreter_state(int index, BasicType bt, Node *local_addrs, Node *local_addrs_base);
+  Node* fetch_interpreter_state(int index, const Type* type, Node* local_addrs, Node* local_addrs_base);
   Node* check_interpreter_type(Node* l, const Type* type, SafePointNode* &bad_type_exit);
   void  load_interpreter_state(Node* osr_buf);
 
   // Functions for managing basic blocks:
   void init_blocks();
@@ -489,17 +489,18 @@
 
   // Parse this bytecode, and alter the Parsers JVM->Node mapping
   void do_one_bytecode();
 
   // helper function to generate array store check
-  void array_store_check();
+  Node* array_store_check(Node*& adr, const Type*& elemtype);
   // Helper function to generate array load
   void array_load(BasicType etype);
   // Helper function to generate array store
   void array_store(BasicType etype);
   // Helper function to compute array addressing
   Node* array_addressing(BasicType type, int vals, const Type*& elemtype);
+  Node* record_profile_for_speculation_at_array_load(Node* ld);
 
   void clinit_deopt();
 
   void rtm_deopt();
 
@@ -544,17 +545,17 @@
 
   // common code for making initial checks and forming addresses
   void do_field_access(bool is_get, bool is_field);
 
   // common code for actually performing the load or store
-  void do_get_xxx(Node* obj, ciField* field, bool is_field);
+  void do_get_xxx(Node* obj, ciField* field);
   void do_put_xxx(Node* obj, ciField* field, bool is_field);
 
   // implementation of object creation bytecodes
   void do_new();
   void do_newarray(BasicType elemtype);
-  void do_anewarray();
+  void do_newarray();
   void do_multianewarray();
   Node* expand_multianewarray(ciArrayKlass* array_klass, Node* *lengths, int ndimensions, int nargs);
 
   // implementation of jsr/ret
   void do_jsr();
@@ -565,13 +566,18 @@
   bool    seems_never_taken(float prob) const;
   bool    path_is_suitable_for_uncommon_trap(float prob) const;
   bool    seems_stable_comparison() const;
 
   void    do_ifnull(BoolTest::mask btest, Node* c);
-  void    do_if(BoolTest::mask btest, Node* c);
+  void    do_if(BoolTest::mask btest, Node* c, bool can_trap = true, bool new_path = false, Node** ctrl_taken = nullptr);
+  void    do_acmp(BoolTest::mask btest, Node* left, Node* right);
+  void    acmp_always_null_input(Node* input, const TypeOopPtr* tinput, BoolTest::mask btest, Node* eq_region);
+  void    acmp_known_non_inline_type_input(Node* input, const TypeOopPtr* tinput, ProfilePtrKind input_ptr, ciKlass* input_type, BoolTest::mask btest, Node* eq_region);
+  Node*   acmp_null_check(Node* input, const TypeOopPtr* tinput, ProfilePtrKind input_ptr, Node*& null_ctl);
+  void    acmp_unknown_non_inline_type_input(Node* input, const TypeOopPtr* tinput, ProfilePtrKind input_ptr, BoolTest::mask btest, Node* eq_region);
   int     repush_if_args();
-  void    adjust_map_after_if(BoolTest::mask btest, Node* c, float prob, Block* path);
+  void    adjust_map_after_if(BoolTest::mask btest, Node* c, float prob, Block* path, bool can_trap = true);
   void    sharpen_type_after_if(BoolTest::mask btest,
                                 Node* con, const Type* tcon,
                                 Node* val, const Type* tval);
   void    maybe_add_predicate_after_if(Block* path);
   IfNode* jump_if_fork_int(Node* a, Node* b, BoolTest::mask mask, float prob, float cnt);
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
@@ -28,11 +28,13 @@
 #include "memory/resourceArea.hpp"
 #include "oops/method.hpp"
 #include "opto/addnode.hpp"
 #include "opto/c2compiler.hpp"
 #include "opto/castnode.hpp"
+#include "opto/convertnode.hpp"
 #include "opto/idealGraphPrinter.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/locknode.hpp"
 #include "opto/memnode.hpp"
 #include "opto/opaquenode.hpp"
 #include "opto/parse.hpp"
 #include "opto/rootnode.hpp"
@@ -100,14 +102,20 @@
 
 //------------------------------ON STACK REPLACEMENT---------------------------
 
 // Construct a node which can be used to get incoming state for
 // on stack replacement.
-Node *Parse::fetch_interpreter_state(int index,
-                                     BasicType bt,
-                                     Node *local_addrs,
-                                     Node *local_addrs_base) {
+Node* Parse::fetch_interpreter_state(int index,
+                                     const Type* type,
+                                     Node* local_addrs,
+                                     Node* local_addrs_base) {
+  BasicType bt = type->basic_type();
+  if (type == TypePtr::NULL_PTR) {
+    // Ptr types are mixed together with T_ADDRESS but nullptr is
+    // really for T_OBJECT types so correct it.
+    bt = T_OBJECT;
+  }
   Node *mem = memory(Compile::AliasIdxRaw);
   Node *adr = basic_plus_adr( local_addrs_base, local_addrs, -index*wordSize );
   Node *ctl = control();
 
   // Very similar to LoadNode::make, except we handle un-aligned longs and
@@ -145,11 +153,10 @@
 // The type is the type predicted by ciTypeFlow.  Note that it is
 // not a general type, but can only come from Type::get_typeflow_type.
 // The safepoint is a map which will feed an uncommon trap.
 Node* Parse::check_interpreter_type(Node* l, const Type* type,
                                     SafePointNode* &bad_type_exit) {
-
   const TypeOopPtr* tp = type->isa_oopptr();
 
   // TypeFlow may assert null-ness if a type appears unloaded.
   if (type == TypePtr::NULL_PTR ||
       (tp != nullptr && !tp->is_loaded())) {
@@ -169,10 +176,16 @@
   // toward more specific classes.  Make sure these specific classes
   // are still in effect.
   if (tp != nullptr && !tp->is_same_java_type_as(TypeInstPtr::BOTTOM)) {
     // TypeFlow asserted a specific object type.  Value must have that type.
     Node* bad_type_ctrl = nullptr;
+    if (tp->is_inlinetypeptr() && !tp->maybe_null()) {
+      // Check inline types for null here to prevent checkcast from adding an
+      // exception state before the bytecode entry (use 'bad_type_ctrl' instead).
+      l = null_check_oop(l, &bad_type_ctrl);
+      bad_type_exit->control()->add_req(bad_type_ctrl);
+    }
     l = gen_checkcast(l, makecon(tp->as_klass_type()->cast_to_exactness(true)), &bad_type_ctrl);
     bad_type_exit->control()->add_req(bad_type_ctrl);
   }
 
   assert(_gvn.type(l)->higher_equal(type), "must constrain OSR typestate");
@@ -185,11 +198,10 @@
 void Parse::load_interpreter_state(Node* osr_buf) {
   int index;
   int max_locals = jvms()->loc_size();
   int max_stack  = jvms()->stk_size();
 
-
   // Mismatch between method and jvms can occur since map briefly held
   // an OSR entry state (which takes up one RawPtr word).
   assert(max_locals == method()->max_locals(), "sanity");
   assert(max_stack  >= method()->max_stack(),  "sanity");
   assert((int)jvms()->endoff() == TypeFunc::Parms + max_locals + max_stack, "sanity");
@@ -224,18 +236,16 @@
   Node *monitors_addr = basic_plus_adr(osr_buf, osr_buf, (max_locals+mcnt*2-1)*wordSize);
   for (index = 0; index < mcnt; index++) {
     // Make a BoxLockNode for the monitor.
     Node *box = _gvn.transform(new BoxLockNode(next_monitor()));
 
-
     // Displaced headers and locked objects are interleaved in the
     // temp OSR buffer.  We only copy the locked objects out here.
     // Fetch the locked object from the OSR temp buffer and copy to our fastlock node.
-    Node *lock_object = fetch_interpreter_state(index*2, T_OBJECT, monitors_addr, osr_buf);
+    Node* lock_object = fetch_interpreter_state(index*2, Type::get_const_basic_type(T_OBJECT), monitors_addr, osr_buf);
     // Try and copy the displaced header to the BoxNode
-    Node *displaced_hdr = fetch_interpreter_state((index*2) + 1, T_ADDRESS, monitors_addr, osr_buf);
-
+    Node* displaced_hdr = fetch_interpreter_state((index*2) + 1, Type::get_const_basic_type(T_ADDRESS), monitors_addr, osr_buf);
 
     store_to_memory(control(), box, displaced_hdr, T_ADDRESS, Compile::AliasIdxRaw, MemNode::unordered);
 
     // Build a bogus FastLockNode (no code will be generated) and push the
     // monitor into our debug info.
@@ -299,17 +309,11 @@
     // makes it go dead.
     if (type == Type::BOTTOM) {
       continue;
     }
     // Construct code to access the appropriate local.
-    BasicType bt = type->basic_type();
-    if (type == TypePtr::NULL_PTR) {
-      // Ptr types are mixed together with T_ADDRESS but null is
-      // really for T_OBJECT types so correct it.
-      bt = T_OBJECT;
-    }
-    Node *value = fetch_interpreter_state(index, bt, locals_addr, osr_buf);
+    Node* value = fetch_interpreter_state(index, type, locals_addr, osr_buf);
     set_local(index, value);
   }
 
   // Extract the needed stack entries from the interpreter frame.
   for (index = 0; index < sp(); index++) {
@@ -501,11 +505,13 @@
     _entry_bci = InvocationEntryBci;
     _flow = method()->get_flow_analysis();
   }
 
   if (_flow->failing()) {
-    assert(false, "type flow analysis failed during parsing");
+    // TODO Adding a trap due to an unloaded return type in ciTypeFlow::StateVector::do_invoke
+    // can lead to this. Re-enable once 8284443 is fixed.
+    //assert(false, "type flow analysis failed during parsing");
     C->record_method_not_compilable(_flow->failure_reason());
 #ifndef PRODUCT
       if (PrintOpto && (Verbose || WizardMode)) {
         if (is_osr_parse()) {
           tty->print_cr("OSR @%d type flow bailout: %s", _entry_bci, _flow->failure_reason());
@@ -595,10 +601,33 @@
     if (log)  log->done("parse");
     C->set_default_node_notes(caller_nn);
     return;
   }
 
+  // Handle inline type arguments
+  int arg_size = method()->arg_size();
+  for (int i = 0; i < arg_size; i++) {
+    Node* parm = local(i);
+    const Type* t = _gvn.type(parm);
+    if (t->is_inlinetypeptr()) {
+      // Create InlineTypeNode from the oop and replace the parameter
+      bool is_larval = (i == 0) && method()->is_object_constructor() && !method()->holder()->is_abstract() && !method()->holder()->is_java_lang_Object();
+      Node* vt = InlineTypeNode::make_from_oop(this, parm, t->inline_klass(), !t->maybe_null(), is_larval);
+      replace_in_map(parm, vt);
+    } else if (UseTypeSpeculation && (i == (arg_size - 1)) && !is_osr_parse() && method()->has_vararg() &&
+               t->isa_aryptr() != nullptr && !t->is_aryptr()->is_null_free() && !t->is_aryptr()->is_not_null_free()) {
+      // Speculate on varargs Object array being not null-free (and therefore also not flat)
+      const TypePtr* spec_type = t->speculative();
+      spec_type = (spec_type != nullptr && spec_type->isa_aryptr() != nullptr) ? spec_type : t->is_aryptr();
+      spec_type = spec_type->remove_speculative()->is_aryptr()->cast_to_not_null_free();
+      spec_type = TypeOopPtr::make(TypePtr::BotPTR, Type::Offset::bottom, TypeOopPtr::InstanceBot, spec_type);
+      Node* cast = _gvn.transform(new CheckCastPPNode(control(), parm, t->join_speculative(spec_type)));
+      // TODO 8325106 Shouldn't we use replace_in_map here?
+      set_local(i, cast);
+    }
+  }
+
   entry_map = map();  // capture any changes performed by method setup code
   assert(jvms()->endoff() == map()->req(), "map matches JVMS layout");
 
   // We begin parsing as if we have just encountered a jump to the
   // method entry.
@@ -780,12 +809,12 @@
   gvn().set_type_bottom(memphi);
   _exits.set_i_o(iophi);
   _exits.set_all_memory(memphi);
 
   // Add a return value to the exit state.  (Do not push it yet.)
-  if (tf()->range()->cnt() > TypeFunc::Parms) {
-    const Type* ret_type = tf()->range()->field_at(TypeFunc::Parms);
+  if (tf()->range_sig()->cnt() > TypeFunc::Parms) {
+    const Type* ret_type = tf()->range_sig()->field_at(TypeFunc::Parms);
     if (ret_type->isa_int()) {
       BasicType ret_bt = method()->return_type()->basic_type();
       if (ret_bt == T_BOOLEAN ||
           ret_bt == T_CHAR ||
           ret_bt == T_BYTE ||
@@ -803,26 +832,26 @@
     }
     int         ret_size = type2size[ret_type->basic_type()];
     Node*       ret_phi  = new PhiNode(region, ret_type);
     gvn().set_type_bottom(ret_phi);
     _exits.ensure_stack(ret_size);
-    assert((int)(tf()->range()->cnt() - TypeFunc::Parms) == ret_size, "good tf range");
+    assert((int)(tf()->range_sig()->cnt() - TypeFunc::Parms) == ret_size, "good tf range");
     assert(method()->return_type()->size() == ret_size, "tf agrees w/ method");
     _exits.set_argument(0, ret_phi);  // here is where the parser finds it
     // Note:  ret_phi is not yet pushed, until do_exits.
   }
 }
 
-
 //----------------------------build_start_state-------------------------------
 // Construct a state which contains only the incoming arguments from an
 // unknown caller.  The method & bci will be null & InvocationEntryBci.
 JVMState* Compile::build_start_state(StartNode* start, const TypeFunc* tf) {
-  int        arg_size = tf->domain()->cnt();
-  int        max_size = MAX2(arg_size, (int)tf->range()->cnt());
+  int        arg_size = tf->domain_sig()->cnt();
+  int        max_size = MAX2(arg_size, (int)tf->range_cc()->cnt());
   JVMState*  jvms     = new (this) JVMState(max_size - TypeFunc::Parms);
   SafePointNode* map  = new SafePointNode(max_size, jvms);
+  jvms->set_map(map);
   record_for_igvn(map);
   assert(arg_size == TypeFunc::Parms + (is_osr_compilation() ? 1 : method()->arg_size()), "correct arg_size");
   Node_Notes* old_nn = default_node_notes();
   if (old_nn != nullptr && has_method()) {
     Node_Notes* entry_nn = old_nn->clone(this);
@@ -830,23 +859,42 @@
     entry_jvms->set_offsets(0);
     entry_jvms->set_bci(entry_bci());
     entry_nn->set_jvms(entry_jvms);
     set_default_node_notes(entry_nn);
   }
-  uint i;
-  for (i = 0; i < (uint)arg_size; i++) {
-    Node* parm = initial_gvn()->transform(new ParmNode(start, i));
+  PhaseGVN& gvn = *initial_gvn();
+  uint i = 0;
+  int arg_num = 0;
+  for (uint j = 0; i < (uint)arg_size; i++) {
+    const Type* t = tf->domain_sig()->field_at(i);
+    Node* parm = nullptr;
+    if (t->is_inlinetypeptr() && method()->is_scalarized_arg(arg_num)) {
+      // Inline type arguments are not passed by reference: we get an argument per
+      // field of the inline type. Build InlineTypeNodes from the inline type arguments.
+      GraphKit kit(jvms, &gvn);
+      kit.set_control(map->control());
+      Node* old_mem = map->memory();
+      // Use immutable memory for inline type loads and restore it below
+      kit.set_all_memory(C->immutable_memory());
+      parm = InlineTypeNode::make_from_multi(&kit, start, t->inline_klass(), j, /* in= */ true, /* null_free= */ !t->maybe_null());
+      map->set_control(kit.control());
+      map->set_memory(old_mem);
+    } else {
+      parm = gvn.transform(new ParmNode(start, j++));
+    }
     map->init_req(i, parm);
     // Record all these guys for later GVN.
     record_for_igvn(parm);
+    if (i >= TypeFunc::Parms && t != Type::HALF) {
+      arg_num++;
+    }
   }
   for (; i < map->req(); i++) {
     map->init_req(i, top());
   }
   assert(jvms->argoff() == TypeFunc::Parms, "parser gets arguments here");
   set_default_node_notes(old_nn);
-  jvms->set_map(map);
   return jvms;
 }
 
 //-----------------------------make_node_notes---------------------------------
 Node_Notes* Parse::make_node_notes(Node_Notes* caller_nn) {
@@ -869,16 +917,38 @@
                              kit.i_o(),
                              kit.reset_memory(),
                              kit.frameptr(),
                              kit.returnadr());
   // Add zero or 1 return values
-  int ret_size = tf()->range()->cnt() - TypeFunc::Parms;
+  int ret_size = tf()->range_sig()->cnt() - TypeFunc::Parms;
   if (ret_size > 0) {
     kit.inc_sp(-ret_size);  // pop the return value(s)
     kit.sync_jvms();
-    ret->add_req(kit.argument(0));
-    // Note:  The second dummy edge is not needed by a ReturnNode.
+    Node* res = kit.argument(0);
+    if (tf()->returns_inline_type_as_fields()) {
+      // Multiple return values (inline type fields): add as many edges
+      // to the Return node as returned values.
+      InlineTypeNode* vt = res->as_InlineType();
+      ret->add_req_batch(nullptr, tf()->range_cc()->cnt() - TypeFunc::Parms);
+      if (vt->is_allocated(&kit.gvn()) && !StressCallingConvention) {
+        ret->init_req(TypeFunc::Parms, vt);
+      } else {
+        // Return the tagged klass pointer to signal scalarization to the caller
+        Node* tagged_klass = vt->tagged_klass(kit.gvn());
+        // Return null if the inline type is null (IsInit field is not set)
+        Node* conv   = kit.gvn().transform(new ConvI2LNode(vt->get_is_init()));
+        Node* shl    = kit.gvn().transform(new LShiftLNode(conv, kit.intcon(63)));
+        Node* shr    = kit.gvn().transform(new RShiftLNode(shl, kit.intcon(63)));
+        tagged_klass = kit.gvn().transform(new AndLNode(tagged_klass, shr));
+        ret->init_req(TypeFunc::Parms, tagged_klass);
+      }
+      uint idx = TypeFunc::Parms + 1;
+      vt->pass_fields(&kit, ret, idx, false, false);
+    } else {
+      ret->add_req(res);
+      // Note:  The second dummy edge is not needed by a ReturnNode.
+    }
   }
   // bind it to root
   root()->add_req(ret);
   record_for_igvn(ret);
   initial_gvn()->transform(ret);
@@ -998,11 +1068,11 @@
   // "All bets are off" unless the first publication occurs after a
   // normal return from the constructor.  We do not attempt to detect
   // such unusual early publications.  But no barrier is needed on
   // exceptional returns, since they cannot publish normally.
   //
-  if (method()->is_initializer() &&
+  if ((method()->is_object_constructor() || method()->is_class_initializer()) &&
        (wrote_final() ||
          (AlwaysSafeConstructors && wrote_fields()) ||
          (support_IRIW_for_not_multiple_copy_atomic_cpu && wrote_volatile()))) {
     _exits.insert_mem_bar(Op_MemBarRelease, alloc_with_final());
 
@@ -1036,12 +1106,12 @@
     mms.set_memory(_gvn.transform(mms.memory()));
   }
   // Clean up input MergeMems created by transforming the slices
   _gvn.transform(_exits.merged_memory());
 
-  if (tf()->range()->cnt() > TypeFunc::Parms) {
-    const Type* ret_type = tf()->range()->field_at(TypeFunc::Parms);
+  if (tf()->range_sig()->cnt() > TypeFunc::Parms) {
+    const Type* ret_type = tf()->range_sig()->field_at(TypeFunc::Parms);
     Node*       ret_phi  = _gvn.transform( _exits.argument(0) );
     if (!_exits.control()->is_top() && _gvn.type(ret_phi)->empty()) {
       // If the type we set for the ret_phi in build_exits() is too optimistic and
       // the ret_phi is top now, there's an extremely small chance that it may be due to class
       // loading.  It could also be due to an error, so mark this method as not compilable because
@@ -1141,12 +1211,18 @@
   _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()) {
     GraphKit kit(_caller);
-    kit.null_check_receiver_before_call(method());
+    Node* receiver = kit.argument(0);
+    Node* null_free = kit.null_check_receiver_before_call(method());
     _caller = kit.transfer_exceptions_into_jvms();
+    if (receiver->is_InlineType() && receiver->as_InlineType()->is_larval()) {
+      // Replace the larval inline type receiver in the exit map as well to make sure that
+      // we can find and update it in Parse::do_call when we are done with the initialization.
+      _exits.map()->replace_edge(receiver, null_free);
+    }
     if (kit.stopped()) {
       _exits.add_exception_states_from(_caller);
       _exits.set_jvms(_caller);
       return nullptr;
     }
@@ -1179,11 +1255,11 @@
     set_all_memory(reset_memory());
   }
   assert(merged_memory(), "");
 
   // Now add the locals which are initially bound to arguments:
-  uint arg_size = tf()->domain()->cnt();
+  uint arg_size = tf()->domain_sig()->cnt();
   ensure_stack(arg_size - TypeFunc::Parms);  // OSR methods have funny args
   for (i = TypeFunc::Parms; i < arg_size; i++) {
     map()->init_req(i, inmap->argument(_caller, i - TypeFunc::Parms));
   }
 
@@ -1253,10 +1329,11 @@
       ciInstance* mirror = _method->holder()->java_mirror();
       const TypeInstPtr *t_lock = TypeInstPtr::make(mirror);
       lock_obj = makecon(t_lock);
     } else {                  // Else pass the "this" pointer,
       lock_obj = local(0);    // which is Parm0 from StartNode
+      assert(!_gvn.type(lock_obj)->make_oopptr()->can_be_inline_type(), "can't be an inline type");
     }
     // Clear out dead values from the debug info.
     kill_dead_locals();
     // Build the FastLockNode
     _synch_lock = shared_lock(lock_obj);
@@ -1686,10 +1763,46 @@
 
   // Zap extra stack slots to top
   assert(sp() == target->start_sp(), "");
   clean_stack(sp());
 
+  // Check for merge conflicts involving inline types
+  JVMState* old_jvms = map()->jvms();
+  int old_bci = bci();
+  JVMState* tmp_jvms = old_jvms->clone_shallow(C);
+  tmp_jvms->set_should_reexecute(true);
+  tmp_jvms->bind_map(map());
+  // Execution needs to restart a the next bytecode (entry of next
+  // block)
+  if (target->is_merged() ||
+      pnum > PhiNode::Input ||
+      target->is_handler() ||
+      target->is_loop_head()) {
+    set_parse_bci(target->start());
+    for (uint j = TypeFunc::Parms; j < map()->req(); j++) {
+      Node* n = map()->in(j);                 // Incoming change to target state.
+      const Type* t = nullptr;
+      if (tmp_jvms->is_loc(j)) {
+        t = target->local_type_at(j - tmp_jvms->locoff());
+      } else if (tmp_jvms->is_stk(j) && j < (uint)sp() + tmp_jvms->stkoff()) {
+        t = target->stack_type_at(j - tmp_jvms->stkoff());
+      }
+      if (t != nullptr && t != Type::BOTTOM) {
+        if (n->is_InlineType() && !t->is_inlinetypeptr()) {
+          // Allocate inline type in src block to be able to merge it with oop in target block
+          map()->set_req(j, n->as_InlineType()->buffer(this));
+        } else if (!n->is_InlineType() && t->is_inlinetypeptr()) {
+          // Scalarize null in src block to be able to merge it with inline type in target block
+          assert(gvn().type(n)->is_zero_type(), "Should have been scalarized");
+          map()->set_req(j, InlineTypeNode::make_null(gvn(), t->inline_klass()));
+        }
+      }
+    }
+  }
+  old_jvms->bind_map(map());
+  set_parse_bci(old_bci);
+
   if (!target->is_merged()) {   // No prior mapping at this bci
     if (TraceOptoParse) { tty->print(" with empty state");  }
 
     // If this path is dead, do not bother capturing it as a merge.
     // It is "as if" we had 1 fewer predecessors from the beginning.
@@ -1740,10 +1853,11 @@
 #ifdef ASSERT
     if (target->is_SEL_head()) {
       target->mark_merged_backedge(block());
     }
 #endif
+
     // We must not manufacture more phis if the target is already parsed.
     bool nophi = target->is_parsed();
 
     SafePointNode* newin = map();// Hang on to incoming mapping
     Block* save_block = block(); // Hang on to incoming block;
@@ -1775,18 +1889,22 @@
     }
 
     // Update all the non-control inputs to map:
     assert(TypeFunc::Parms == newin->jvms()->locoff(), "parser map should contain only youngest jvms");
     bool check_elide_phi = target->is_SEL_backedge(save_block);
+    bool last_merge = (pnum == PhiNode::Input);
     for (uint j = 1; j < newin->req(); j++) {
       Node* m = map()->in(j);   // Current state of target.
       Node* n = newin->in(j);   // Incoming change to target state.
       PhiNode* phi;
-      if (m->is_Phi() && m->as_Phi()->region() == r)
+      if (m->is_Phi() && m->as_Phi()->region() == r) {
         phi = m->as_Phi();
-      else
+      } else if (m->is_InlineType() && m->as_InlineType()->has_phi_inputs(r)) {
+        phi = m->as_InlineType()->get_oop()->as_Phi();
+      } else {
         phi = nullptr;
+      }
       if (m != n) {             // Different; must merge
         switch (j) {
         // Frame pointer and Return Address never changes
         case TypeFunc::FramePtr:// Drop m, use the original value
         case TypeFunc::ReturnAdr:
@@ -1816,15 +1934,38 @@
       // At this point, n might be top if:
       //  - there is no phi (because TypeFlow detected a conflict), or
       //  - the corresponding control edges is top (a dead incoming path)
       // It is a bug if we create a phi which sees a garbage value on a live path.
 
-      if (phi != nullptr) {
+      // Merging two inline types?
+      if (phi != nullptr && phi->bottom_type()->is_inlinetypeptr()) {
+        // Reload current state because it may have been updated by ensure_phi
+        m = map()->in(j);
+        InlineTypeNode* vtm = m->as_InlineType(); // Current inline type
+        InlineTypeNode* vtn = n->as_InlineType(); // Incoming inline type
+        assert(vtm->get_oop() == phi, "Inline type should have Phi input");
+        if (TraceOptoParse) {
+#ifdef ASSERT
+          tty->print_cr("\nMerging inline types");
+          tty->print_cr("Current:");
+          vtm->dump(2);
+          tty->print_cr("Incoming:");
+          vtn->dump(2);
+          tty->cr();
+#endif
+        }
+        // Do the merge
+        vtm->merge_with(&_gvn, vtn, pnum, last_merge);
+        if (last_merge) {
+          map()->set_req(j, _gvn.transform(vtm));
+          record_for_igvn(vtm);
+        }
+      } else if (phi != nullptr) {
         assert(n != top() || r->in(pnum) == top(), "live value must not be garbage");
         assert(phi->region() == r, "");
         phi->set_req(pnum, n);  // Then add 'n' to the merge
-        if (pnum == PhiNode::Input) {
+        if (last_merge) {
           // Last merge for this Phi.
           // So far, Phis have had a reasonable type from ciTypeFlow.
           // Now _gvn will join that with the meet of current inputs.
           // BOTTOM is never permissible here, 'cause pessimistically
           // Phis of pointers cannot lose the basic pointer type.
@@ -1836,12 +1977,11 @@
           record_for_igvn(phi);
         }
       }
     } // End of for all values to be merged
 
-    if (pnum == PhiNode::Input &&
-        !r->in(0)) {         // The occasional useless Region
+    if (last_merge && !r->in(0)) {         // The occasional useless Region
       assert(control() == r, "");
       set_control(r->nonnull_req());
     }
 
     map()->merge_replaced_nodes_with(newin);
@@ -1989,10 +2129,12 @@
       }
     } else {
       if (n->is_Phi() && n->as_Phi()->region() == r) {
         assert(n->req() == pnum, "must be same size as region");
         n->add_req(nullptr);
+      } else if (n->is_InlineType() && n->as_InlineType()->has_phi_inputs(r)) {
+        n->as_InlineType()->add_new_path(r);
       }
     }
   }
 
   return pnum;
@@ -2011,10 +2153,14 @@
   if (o == top())  return nullptr; // TOP always merges into TOP
 
   if (o->is_Phi() && o->as_Phi()->region() == region) {
     return o->as_Phi();
   }
+  InlineTypeNode* vt = o->isa_InlineType();
+  if (vt != nullptr && vt->has_phi_inputs(region)) {
+    return vt->get_oop()->as_Phi();
+  }
 
   // Now use a Phi here for merging
   assert(!nocreate, "Cannot build a phi for a block already parsed.");
   const JVMState* jvms = map->jvms();
   const Type* t = nullptr;
@@ -2030,12 +2176,12 @@
   } else {
     assert(false, "no type information for this phi");
   }
 
   // If the type falls to bottom, then this must be a local that
-  // is mixing ints and oops or some such.  Forcing it to top
-  // makes it go dead.
+  // is already dead or is mixing ints and oops or some such.
+  // Forcing it to top makes it go dead.
   if (t == Type::BOTTOM) {
     map->set_req(idx, top());
     return nullptr;
   }
 
@@ -2044,15 +2190,25 @@
   if (t == Type::TOP || t == Type::HALF) {
     map->set_req(idx, top());
     return nullptr;
   }
 
-  PhiNode* phi = PhiNode::make(region, o, t);
-  gvn().set_type(phi, t);
-  if (C->do_escape_analysis()) record_for_igvn(phi);
-  map->set_req(idx, phi);
-  return phi;
+  if (vt != nullptr && t->is_inlinetypeptr()) {
+    // Inline types are merged by merging their field values.
+    // Create a cloned InlineTypeNode with phi inputs that
+    // represents the merged inline type and update the map.
+    // TODO 8325106 Why can't we pass map here?
+    vt = vt->clone_with_phis(&_gvn, region);
+    map->set_req(idx, vt);
+    return vt->get_oop()->as_Phi();
+  } else {
+    PhiNode* phi = PhiNode::make(region, o, t);
+    gvn().set_type(phi, t);
+    if (C->do_escape_analysis()) record_for_igvn(phi);
+    map->set_req(idx, phi);
+    return phi;
+  }
 }
 
 //--------------------------ensure_memory_phi----------------------------------
 // Turn the idx'th slice of the current memory into a Phi
 PhiNode *Parse::ensure_memory_phi(int idx, bool nocreate) {
@@ -2216,17 +2372,55 @@
       method()->intrinsic_id() == vmIntrinsics::_Object_init) {
     call_register_finalizer();
   }
 
   // Do not set_parse_bci, so that return goo is credited to the return insn.
-  set_bci(InvocationEntryBci);
+  // vreturn can trigger an allocation so vreturn can throw. Setting
+  // the bci here breaks exception handling. Commenting this out
+  // doesn't seem to break anything.
+  //  set_bci(InvocationEntryBci);
   if (method()->is_synchronized() && GenerateSynchronizationCode) {
     shared_unlock(_synch_lock->box_node(), _synch_lock->obj_node());
   }
   if (C->env()->dtrace_method_probes()) {
     make_dtrace_method_exit(method());
   }
+  // frame pointer is always same, already captured
+  if (value != nullptr) {
+    Node* phi = _exits.argument(0);
+    const Type* return_type = phi->bottom_type();
+    const TypeInstPtr* tr = return_type->isa_instptr();
+    assert(!value->is_InlineType() || !value->as_InlineType()->is_larval(), "returning a larval");
+    if ((tf()->returns_inline_type_as_fields() || (_caller->has_method() && !Compile::current()->inlining_incrementally())) &&
+        return_type->is_inlinetypeptr()) {
+      // Inline type is returned as fields, make sure it is scalarized
+      if (!value->is_InlineType()) {
+        value = InlineTypeNode::make_from_oop(this, value, return_type->inline_klass(), false);
+      }
+      if (!_caller->has_method() || Compile::current()->inlining_incrementally()) {
+        // Returning from root or an incrementally inlined method. Make sure all non-flat
+        // fields are buffered and re-execute if allocation triggers deoptimization.
+        PreserveReexecuteState preexecs(this);
+        assert(tf()->returns_inline_type_as_fields(), "must be returned as fields");
+        jvms()->set_should_reexecute(true);
+        inc_sp(1);
+        value = value->as_InlineType()->allocate_fields(this);
+      }
+    } else if (value->is_InlineType()) {
+      // Inline type is returned as oop, make sure it is buffered and re-execute
+      // if allocation triggers deoptimization.
+      PreserveReexecuteState preexecs(this);
+      jvms()->set_should_reexecute(true);
+      inc_sp(1);
+      value = value->as_InlineType()->buffer(this);
+    }
+    // ...else
+    // If returning oops to an interface-return, there is a silent free
+    // cast from oop to interface allowed by the Verifier. Make it explicit here.
+    phi->add_req(value);
+  }
+
   SafePointNode* exit_return = _exits.map();
   exit_return->in( TypeFunc::Control  )->add_req( control() );
   exit_return->in( TypeFunc::I_O      )->add_req( i_o    () );
   Node *mem = exit_return->in( TypeFunc::Memory   );
   for (MergeMemStream mms(mem->as_MergeMem(), merged_memory()); mms.next_non_empty2(); ) {
@@ -2240,19 +2434,10 @@
       mms.set_memory(phi);
     }
     mms.memory()->add_req(mms.memory2());
   }
 
-  // frame pointer is always same, already captured
-  if (value != nullptr) {
-    // If returning oops to an interface-return, there is a silent free
-    // cast from oop to interface allowed by the Verifier.  Make it explicit
-    // here.
-    Node* phi = _exits.argument(0);
-    phi->add_req(value);
-  }
-
   if (_first_return) {
     _exits.map()->transfer_replaced_nodes_from(map(), _new_idx);
     _first_return = false;
   } else {
     _exits.map()->merge_replaced_nodes_with(map());
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
@@ -22,10 +22,11 @@
  *
  */
 
 #include "precompiled.hpp"
 #include "ci/ciMethodData.hpp"
+#include "ci/ciSymbols.hpp"
 #include "classfile/vmSymbols.hpp"
 #include "compiler/compileLog.hpp"
 #include "interpreter/linkResolver.hpp"
 #include "jvm_io.h"
 #include "memory/resourceArea.hpp"
@@ -34,10 +35,12 @@
 #include "opto/addnode.hpp"
 #include "opto/castnode.hpp"
 #include "opto/convertnode.hpp"
 #include "opto/divnode.hpp"
 #include "opto/idealGraphPrinter.hpp"
+#include "opto/idealKit.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/matcher.hpp"
 #include "opto/memnode.hpp"
 #include "opto/mulnode.hpp"
 #include "opto/opaquenode.hpp"
 #include "opto/parse.hpp"
@@ -48,62 +51,278 @@
 #ifndef PRODUCT
 extern uint explicit_null_checks_inserted,
             explicit_null_checks_elided;
 #endif
 
+Node* Parse::record_profile_for_speculation_at_array_load(Node* ld) {
+  // Feed unused profile data to type speculation
+  if (UseTypeSpeculation && UseArrayLoadStoreProfile) {
+    ciKlass* array_type = nullptr;
+    ciKlass* element_type = nullptr;
+    ProfilePtrKind element_ptr = ProfileMaybeNull;
+    bool flat_array = true;
+    bool null_free_array = true;
+    method()->array_access_profiled_type(bci(), array_type, element_type, element_ptr, flat_array, null_free_array);
+    if (element_type != nullptr || element_ptr != ProfileMaybeNull) {
+      ld = record_profile_for_speculation(ld, element_type, element_ptr);
+    }
+  }
+  return ld;
+}
+
+
 //---------------------------------array_load----------------------------------
 void Parse::array_load(BasicType bt) {
   const Type* elemtype = Type::TOP;
-  bool big_val = bt == T_DOUBLE || bt == T_LONG;
   Node* adr = array_addressing(bt, 0, elemtype);
   if (stopped())  return;     // guaranteed null or range check
 
-  pop();                      // index (already used)
-  Node* array = pop();        // the array itself
+  Node* idx = pop();
+  Node* ary = pop();
+
+  // Handle inline type arrays
+  const TypeOopPtr* elemptr = elemtype->make_oopptr();
+  const TypeAryPtr* ary_t = _gvn.type(ary)->is_aryptr();
+  if (ary_t->is_flat()) {
+    // Load from flat inline type array
+    Node* vt = InlineTypeNode::make_from_flat(this, elemtype->inline_klass(), ary, adr);
+    push(vt);
+    return;
+  } else if (ary_t->is_null_free()) {
+    // Load from non-flat inline type array (elements can never be null)
+    bt = T_OBJECT;
+  } else if (!ary_t->is_not_flat()) {
+    // Cannot statically determine if array is a flat array, emit runtime check
+    assert(UseFlatArray && is_reference_type(bt) && elemptr->can_be_inline_type() && !ary_t->klass_is_exact() && !ary_t->is_not_null_free() &&
+           (!elemptr->is_inlinetypeptr() || elemptr->inline_klass()->flat_in_array()), "array can't be flat");
+    IdealKit ideal(this);
+    IdealVariable res(ideal);
+    ideal.declarations_done();
+    ideal.if_then(flat_array_test(ary, /* flat = */ false)); {
+      // non-flat array
+      assert(ideal.ctrl()->in(0)->as_If()->is_flat_array_check(&_gvn), "Should be found");
+      sync_kit(ideal);
+      const TypeAryPtr* adr_type = TypeAryPtr::get_array_body_type(bt);
+      Node* ld = access_load_at(ary, adr, adr_type, elemptr, bt,
+                                IN_HEAP | IS_ARRAY | C2_CONTROL_DEPENDENT_LOAD);
+      if (elemptr->is_inlinetypeptr()) {
+        assert(elemptr->maybe_null(), "null free array should be handled above");
+        ld = InlineTypeNode::make_from_oop(this, ld, elemptr->inline_klass(), false);
+      }
+      ideal.sync_kit(this);
+      ideal.set(res, ld);
+    } ideal.else_(); {
+      // flat array
+      sync_kit(ideal);
+      if (elemptr->is_inlinetypeptr()) {
+        // Element type is known, cast and load from flat representation
+        ciInlineKlass* vk = elemptr->inline_klass();
+        assert(vk->flat_in_array() && elemptr->maybe_null(), "never/always flat - should be optimized");
+        ciArrayKlass* array_klass = ciArrayKlass::make(vk, /* null_free */ true);
+        const TypeAryPtr* arytype = TypeOopPtr::make_from_klass(array_klass)->isa_aryptr();
+        Node* cast = _gvn.transform(new CheckCastPPNode(control(), ary, arytype));
+        Node* casted_adr = array_element_address(cast, idx, T_OBJECT, ary_t->size(), control());
+        // Re-execute flat array load if buffering triggers deoptimization
+        PreserveReexecuteState preexecs(this);
+        jvms()->set_should_reexecute(true);
+        inc_sp(2);
+        Node* vt = InlineTypeNode::make_from_flat(this, vk, cast, casted_adr)->buffer(this, false);
+        ideal.set(res, vt);
+        ideal.sync_kit(this);
+      } else {
+        // Element type is unknown, emit runtime call
+
+        // Below membars keep this access to an unknown flat array correctly
+        // ordered with other unknown and known flat array accesses.
+        insert_mem_bar_volatile(Op_MemBarCPUOrder, C->get_alias_index(TypeAryPtr::INLINES));
+
+        Node* call = nullptr;
+        {
+          // Re-execute flat array load if runtime call triggers deoptimization
+          PreserveReexecuteState preexecs(this);
+          jvms()->set_bci(_bci);
+          jvms()->set_should_reexecute(true);
+          inc_sp(2);
+          kill_dead_locals();
+          call = make_runtime_call(RC_NO_LEAF | RC_NO_IO,
+                                   OptoRuntime::load_unknown_inline_type(),
+                                   OptoRuntime::load_unknown_inline_Java(),
+                                   nullptr, TypeRawPtr::BOTTOM,
+                                   ary, idx);
+        }
+        make_slow_call_ex(call, env()->Throwable_klass(), false);
+        Node* buffer = _gvn.transform(new ProjNode(call, TypeFunc::Parms));
+
+        insert_mem_bar_volatile(Op_MemBarCPUOrder, C->get_alias_index(TypeAryPtr::INLINES));
+
+        // Keep track of the information that the inline type is in flat arrays
+        const Type* unknown_value = elemptr->is_instptr()->cast_to_flat_in_array();
+        buffer = _gvn.transform(new CheckCastPPNode(control(), buffer, unknown_value));
+
+        ideal.sync_kit(this);
+        ideal.set(res, buffer);
+      }
+    } ideal.end_if();
+    sync_kit(ideal);
+    Node* ld = _gvn.transform(ideal.value(res));
+    ld = record_profile_for_speculation_at_array_load(ld);
+    push_node(bt, ld);
+    return;
+  }
 
   if (elemtype == TypeInt::BOOL) {
     bt = T_BOOLEAN;
   }
   const TypeAryPtr* adr_type = TypeAryPtr::get_array_body_type(bt);
-
-  Node* ld = access_load_at(array, adr, adr_type, elemtype, bt,
+  Node* ld = access_load_at(ary, adr, adr_type, elemtype, bt,
                             IN_HEAP | IS_ARRAY | C2_CONTROL_DEPENDENT_LOAD);
-  if (big_val) {
-    push_pair(ld);
-  } else {
-    push(ld);
+  ld = record_profile_for_speculation_at_array_load(ld);
+  // Loading an inline type from a non-flat array
+  if (elemptr != nullptr && elemptr->is_inlinetypeptr()) {
+    assert(!ary_t->is_null_free() || !elemptr->maybe_null(), "inline type array elements should never be null");
+    ld = InlineTypeNode::make_from_oop(this, ld, elemptr->inline_klass(), !elemptr->maybe_null());
   }
+  push_node(bt, ld);
 }
 
 
 //--------------------------------array_store----------------------------------
 void Parse::array_store(BasicType bt) {
   const Type* elemtype = Type::TOP;
-  bool big_val = bt == T_DOUBLE || bt == T_LONG;
-  Node* adr = array_addressing(bt, big_val ? 2 : 1, elemtype);
+  Node* adr = array_addressing(bt, type2size[bt], elemtype);
   if (stopped())  return;     // guaranteed null or range check
+  Node* cast_val = nullptr;
   if (bt == T_OBJECT) {
-    array_store_check();
-    if (stopped()) {
-      return;
-    }
-  }
-  Node* val;                  // Oop to store
-  if (big_val) {
-    val = pop_pair();
-  } else {
-    val = pop();
+    cast_val = array_store_check(adr, elemtype);
+    if (stopped()) return;
   }
-  pop();                      // index (already used)
-  Node* array = pop();        // the array itself
+  Node* val = pop_node(bt); // Value to store
+  Node* idx = pop();        // Index in the array
+  Node* ary = pop();        // The array itself
+
+  const TypeAryPtr* ary_t = _gvn.type(ary)->is_aryptr();
+  const TypeAryPtr* adr_type = TypeAryPtr::get_array_body_type(bt);
 
   if (elemtype == TypeInt::BOOL) {
     bt = T_BOOLEAN;
+  } else if (bt == T_OBJECT) {
+    elemtype = elemtype->make_oopptr();
+    const Type* tval = _gvn.type(cast_val);
+    // Based on the value to be stored, try to determine if the array is not null-free and/or not flat.
+    // This is only legal for non-null stores because the array_store_check always passes for null, even
+    // if the array is null-free. Null stores are handled in GraphKit::gen_inline_array_null_guard().
+    bool not_null_free = !tval->maybe_null() && !tval->is_oopptr()->can_be_inline_type();
+    bool not_flat = not_null_free || (tval->is_inlinetypeptr() && !tval->inline_klass()->flat_in_array());
+    if (!ary_t->is_not_null_free() && not_null_free) {
+      // Storing a non-inline type, mark array as not null-free (-> not flat).
+      ary_t = ary_t->cast_to_not_null_free();
+      Node* cast = _gvn.transform(new CheckCastPPNode(control(), ary, ary_t));
+      replace_in_map(ary, cast);
+      ary = cast;
+    } else if (!ary_t->is_not_flat() && not_flat) {
+      // Storing to a non-flat array, mark array as not flat.
+      ary_t = ary_t->cast_to_not_flat();
+      Node* cast = _gvn.transform(new CheckCastPPNode(control(), ary, ary_t));
+      replace_in_map(ary, cast);
+      ary = cast;
+    }
+
+    if (ary_t->is_flat()) {
+      // Store to flat inline type array
+      assert(!tval->maybe_null(), "should be guaranteed by array store check");
+      // Re-execute flat array store if buffering triggers deoptimization
+      PreserveReexecuteState preexecs(this);
+      inc_sp(3);
+      jvms()->set_should_reexecute(true);
+      cast_val->as_InlineType()->store_flat(this, ary, adr, nullptr, 0, MO_UNORDERED | IN_HEAP | IS_ARRAY);
+      return;
+    } else if (ary_t->is_null_free()) {
+      // Store to non-flat inline type array (elements can never be null)
+      assert(!tval->maybe_null(), "should be guaranteed by array store check");
+      if (elemtype->inline_klass()->is_empty()) {
+        // Ignore empty inline stores, array is already initialized.
+        return;
+      }
+    } else if (!ary_t->is_not_flat() && (tval != TypePtr::NULL_PTR || StressReflectiveCode)) {
+      // Array might be a flat array, emit runtime checks (for nullptr, a simple inline_array_null_guard is sufficient).
+      assert(UseFlatArray && !not_flat && elemtype->is_oopptr()->can_be_inline_type() &&
+             !ary_t->klass_is_exact() && !ary_t->is_not_null_free(), "array can't be a flat array");
+      IdealKit ideal(this);
+      ideal.if_then(flat_array_test(ary, /* flat = */ false)); {
+        // non-flat array
+        assert(ideal.ctrl()->in(0)->as_If()->is_flat_array_check(&_gvn), "Should be found");
+        sync_kit(ideal);
+        Node* cast_ary = inline_array_null_guard(ary, cast_val, 3);
+        inc_sp(3);
+        access_store_at(cast_ary, adr, adr_type, cast_val, elemtype, bt, MO_UNORDERED | IN_HEAP | IS_ARRAY, false);
+        dec_sp(3);
+        ideal.sync_kit(this);
+      } ideal.else_(); {
+        sync_kit(ideal);
+        // flat array
+        Node* null_ctl = top();
+        Node* val = null_check_oop(cast_val, &null_ctl);
+        if (null_ctl != top()) {
+          PreserveJVMState pjvms(this);
+          inc_sp(3);
+          set_control(null_ctl);
+          uncommon_trap(Deoptimization::Reason_null_check, Deoptimization::Action_none);
+          dec_sp(3);
+        }
+        // Try to determine the inline klass
+        ciInlineKlass* vk = nullptr;
+        if (tval->is_inlinetypeptr()) {
+          vk = tval->inline_klass();
+        } else if (elemtype->is_inlinetypeptr()) {
+          vk = elemtype->inline_klass();
+        }
+        Node* casted_ary = ary;
+        if (vk != nullptr && !stopped()) {
+          // Element type is known, cast and store to flat representation
+          assert(vk->flat_in_array() && elemtype->maybe_null(), "never/always flat - should be optimized");
+          ciArrayKlass* array_klass = ciArrayKlass::make(vk, /* null_free */ true);
+          const TypeAryPtr* arytype = TypeOopPtr::make_from_klass(array_klass)->isa_aryptr();
+          casted_ary = _gvn.transform(new CheckCastPPNode(control(), casted_ary, arytype));
+          Node* casted_adr = array_element_address(casted_ary, idx, T_OBJECT, arytype->size(), control());
+          if (!val->is_InlineType()) {
+            assert(!gvn().type(val)->maybe_null(), "inline type array elements should never be null");
+            val = InlineTypeNode::make_from_oop(this, val, vk);
+          }
+          // Re-execute flat array store if buffering triggers deoptimization
+          PreserveReexecuteState preexecs(this);
+          inc_sp(3);
+          jvms()->set_should_reexecute(true);
+          val->as_InlineType()->store_flat(this, casted_ary, casted_adr, nullptr, 0, MO_UNORDERED | IN_HEAP | IS_ARRAY);
+        } else if (!stopped()) {
+          // Element type is unknown, emit runtime call
+
+          // Below membars keep this access to an unknown flat array correctly
+          // ordered with other unknown and known flat array accesses.
+          insert_mem_bar_volatile(Op_MemBarCPUOrder, C->get_alias_index(TypeAryPtr::INLINES));
+
+          make_runtime_call(RC_LEAF,
+                            OptoRuntime::store_unknown_inline_type(),
+                            CAST_FROM_FN_PTR(address, OptoRuntime::store_unknown_inline),
+                            "store_unknown_inline", TypeRawPtr::BOTTOM,
+                            val, casted_ary, idx);
+
+          insert_mem_bar_volatile(Op_MemBarCPUOrder, C->get_alias_index(TypeAryPtr::INLINES));
+        }
+        ideal.sync_kit(this);
+      }
+      ideal.end_if();
+      sync_kit(ideal);
+      return;
+    } else if (!ary_t->is_not_null_free()) {
+      // Array is not flat but may be null free
+      assert(elemtype->is_oopptr()->can_be_inline_type() && !ary_t->klass_is_exact(), "array can't be null-free");
+      ary = inline_array_null_guard(ary, cast_val, 3, true);
+    }
   }
-  const TypeAryPtr* adr_type = TypeAryPtr::get_array_body_type(bt);
-
-  access_store_at(array, adr, adr_type, val, elemtype, bt, MO_UNORDERED | IN_HEAP | IS_ARRAY);
+  inc_sp(3);
+  access_store_at(ary, adr, adr_type, val, elemtype, bt, MO_UNORDERED | IN_HEAP | IS_ARRAY);
+  dec_sp(3);
 }
 
 
 //------------------------------array_addressing-------------------------------
 // Pull array and index from the stack.  Compute pointer-to-element.
@@ -200,10 +419,126 @@
     }
   }
   // Check for always knowing you are throwing a range-check exception
   if (stopped())  return top();
 
+  // This could be an access to an inline type array. We can't tell if it's
+  // flat or not. Knowing the exact type avoids runtime checks and leads to
+  // a much simpler graph shape. Check profile information.
+  if (!arytype->is_flat() && !arytype->is_not_flat()) {
+    // First check the speculative type
+    Deoptimization::DeoptReason reason = Deoptimization::Reason_speculate_class_check;
+    ciKlass* array_type = arytype->speculative_type();
+    if (too_many_traps_or_recompiles(reason) || array_type == nullptr) {
+      // No speculative type, check profile data at this bci
+      array_type = nullptr;
+      reason = Deoptimization::Reason_class_check;
+      if (UseArrayLoadStoreProfile && !too_many_traps_or_recompiles(reason)) {
+        ciKlass* element_type = nullptr;
+        ProfilePtrKind element_ptr = ProfileMaybeNull;
+        bool flat_array = true;
+        bool null_free_array = true;
+        method()->array_access_profiled_type(bci(), array_type, element_type, element_ptr, flat_array, null_free_array);
+      }
+    }
+    if (array_type != nullptr) {
+      // Speculate that this array has the exact type reported by profile data
+      Node* better_ary = nullptr;
+      DEBUG_ONLY(Node* old_control = control();)
+      Node* slow_ctl = type_check_receiver(ary, array_type, 1.0, &better_ary);
+      if (stopped()) {
+        // The check always fails and therefore profile information is incorrect. Don't use it.
+        assert(old_control == slow_ctl, "type check should have been removed");
+        set_control(slow_ctl);
+      } else if (!slow_ctl->is_top()) {
+        { PreserveJVMState pjvms(this);
+          set_control(slow_ctl);
+          uncommon_trap_exact(reason, Deoptimization::Action_maybe_recompile);
+        }
+        replace_in_map(ary, better_ary);
+        ary = better_ary;
+        arytype  = _gvn.type(ary)->is_aryptr();
+        elemtype = arytype->elem();
+      }
+    }
+  } else if (UseTypeSpeculation && UseArrayLoadStoreProfile) {
+    // No need to speculate: feed profile data at this bci for the
+    // array to type speculation
+    ciKlass* array_type = nullptr;
+    ciKlass* element_type = nullptr;
+    ProfilePtrKind element_ptr = ProfileMaybeNull;
+    bool flat_array = true;
+    bool null_free_array = true;
+    method()->array_access_profiled_type(bci(), array_type, element_type, element_ptr, flat_array, null_free_array);
+    if (array_type != nullptr) {
+      ary = record_profile_for_speculation(ary, array_type, ProfileMaybeNull);
+    }
+  }
+
+  // We have no exact array type from profile data. Check profile data
+  // for a non null-free or non flat array. Non null-free implies non
+  // flat so check this one first. Speculating on a non null-free
+  // array doesn't help aaload but could be profitable for a
+  // subsequent aastore.
+  if (!arytype->is_null_free() && !arytype->is_not_null_free()) {
+    bool null_free_array = true;
+    Deoptimization::DeoptReason reason = Deoptimization::Reason_none;
+    if (arytype->speculative() != nullptr &&
+        arytype->speculative()->is_aryptr()->is_not_null_free() &&
+        !too_many_traps_or_recompiles(Deoptimization::Reason_speculate_class_check)) {
+      null_free_array = false;
+      reason = Deoptimization::Reason_speculate_class_check;
+    } else if (UseArrayLoadStoreProfile && !too_many_traps_or_recompiles(Deoptimization::Reason_class_check)) {
+      ciKlass* array_type = nullptr;
+      ciKlass* element_type = nullptr;
+      ProfilePtrKind element_ptr = ProfileMaybeNull;
+      bool flat_array = true;
+      method()->array_access_profiled_type(bci(), array_type, element_type, element_ptr, flat_array, null_free_array);
+      reason = Deoptimization::Reason_class_check;
+    }
+    if (!null_free_array) {
+      { // Deoptimize if null-free array
+        BuildCutout unless(this, null_free_array_test(load_object_klass(ary), /* null_free = */ false), PROB_MAX);
+        uncommon_trap_exact(reason, Deoptimization::Action_maybe_recompile);
+      }
+      assert(!stopped(), "null-free array should have been caught earlier");
+      Node* better_ary = _gvn.transform(new CheckCastPPNode(control(), ary, arytype->cast_to_not_null_free()));
+      replace_in_map(ary, better_ary);
+      ary = better_ary;
+      arytype = _gvn.type(ary)->is_aryptr();
+    }
+  }
+
+  if (!arytype->is_flat() && !arytype->is_not_flat()) {
+    bool flat_array = true;
+    Deoptimization::DeoptReason reason = Deoptimization::Reason_none;
+    if (arytype->speculative() != nullptr &&
+        arytype->speculative()->is_aryptr()->is_not_flat() &&
+        !too_many_traps_or_recompiles(Deoptimization::Reason_speculate_class_check)) {
+      flat_array = false;
+      reason = Deoptimization::Reason_speculate_class_check;
+    } else if (UseArrayLoadStoreProfile && !too_many_traps_or_recompiles(reason)) {
+      ciKlass* array_type = nullptr;
+      ciKlass* element_type = nullptr;
+      ProfilePtrKind element_ptr = ProfileMaybeNull;
+      bool null_free_array = true;
+      method()->array_access_profiled_type(bci(), array_type, element_type, element_ptr, flat_array, null_free_array);
+      reason = Deoptimization::Reason_class_check;
+    }
+    if (!flat_array) {
+      { // Deoptimize if flat array
+        BuildCutout unless(this, flat_array_test(ary, /* flat = */ false), PROB_MAX);
+        uncommon_trap_exact(reason, Deoptimization::Action_maybe_recompile);
+      }
+      assert(!stopped(), "flat array should have been caught earlier");
+      Node* better_ary = _gvn.transform(new CheckCastPPNode(control(), ary, arytype->cast_to_not_flat()));
+      replace_in_map(ary, better_ary);
+      ary = better_ary;
+      arytype = _gvn.type(ary)->is_aryptr();
+    }
+  }
+
   // Make array address computation control dependent to prevent it
   // from floating above the range check during loop optimizations.
   Node* ptr = array_element_address(ary, idx, type, sizetype, control());
   assert(ptr != top(), "top should go hand-in-hand with stopped");
 
@@ -1455,11 +1790,11 @@
     adjust_map_after_if(BoolTest(btest).negate(), c, 1.0-prob, next_block);
   }
 }
 
 //------------------------------------do_if------------------------------------
-void Parse::do_if(BoolTest::mask btest, Node* c) {
+void Parse::do_if(BoolTest::mask btest, Node* c, bool can_trap, bool new_path, Node** ctrl_taken) {
   int target_bci = iter().get_dest();
 
   Block* branch_block = successor_for_bci(target_bci);
   Block* next_block   = successor_for_bci(iter().next_bci());
 
@@ -1539,33 +1874,445 @@
   { PreserveJVMState pjvms(this);
     taken_branch = _gvn.transform(taken_branch);
     set_control(taken_branch);
 
     if (stopped()) {
-      if (C->eliminate_boxing()) {
-        // Mark the successor block as parsed
+      if (C->eliminate_boxing() && !new_path) {
+        // Mark the successor block as parsed (if we haven't created a new path)
         branch_block->next_path_num();
       }
     } else {
-      adjust_map_after_if(taken_btest, c, prob, branch_block);
+      adjust_map_after_if(taken_btest, c, prob, branch_block, can_trap);
       if (!stopped()) {
-        merge(target_bci);
+        if (new_path) {
+          // Merge by using a new path
+          merge_new_path(target_bci);
+        } else if (ctrl_taken != nullptr) {
+          // Don't merge but save taken branch to be wired by caller
+          *ctrl_taken = control();
+        } else {
+          merge(target_bci);
+        }
       }
     }
   }
 
   untaken_branch = _gvn.transform(untaken_branch);
   set_control(untaken_branch);
 
   // Branch not taken.
-  if (stopped()) {
+  if (stopped() && ctrl_taken == nullptr) {
     if (C->eliminate_boxing()) {
-      // Mark the successor block as parsed
+      // Mark the successor block as parsed (if caller does not re-wire control flow)
       next_block->next_path_num();
     }
   } else {
-    adjust_map_after_if(untaken_btest, c, untaken_prob, next_block);
+    adjust_map_after_if(untaken_btest, c, untaken_prob, next_block, can_trap);
+  }
+}
+
+
+static ProfilePtrKind speculative_ptr_kind(const TypeOopPtr* t) {
+  if (t->speculative() == nullptr) {
+    return ProfileUnknownNull;
+  }
+  if (t->speculative_always_null()) {
+    return ProfileAlwaysNull;
+  }
+  if (t->speculative_maybe_null()) {
+    return ProfileMaybeNull;
+  }
+  return ProfileNeverNull;
+}
+
+void Parse::acmp_always_null_input(Node* input, const TypeOopPtr* tinput, BoolTest::mask btest, Node* eq_region) {
+  inc_sp(2);
+  Node* cast = null_check_common(input, T_OBJECT, true, nullptr,
+                                 !too_many_traps_or_recompiles(Deoptimization::Reason_speculate_null_check) &&
+                                 speculative_ptr_kind(tinput) == ProfileAlwaysNull);
+  dec_sp(2);
+  if (btest == BoolTest::ne) {
+    {
+      PreserveJVMState pjvms(this);
+      replace_in_map(input, cast);
+      int target_bci = iter().get_dest();
+      merge(target_bci);
+    }
+    record_for_igvn(eq_region);
+    set_control(_gvn.transform(eq_region));
+  } else {
+    replace_in_map(input, cast);
+  }
+}
+
+Node* Parse::acmp_null_check(Node* input, const TypeOopPtr* tinput, ProfilePtrKind input_ptr, Node*& null_ctl) {
+  inc_sp(2);
+  null_ctl = top();
+  Node* cast = null_check_oop(input, &null_ctl,
+                              input_ptr == ProfileNeverNull || (input_ptr == ProfileUnknownNull && !too_many_traps_or_recompiles(Deoptimization::Reason_null_check)),
+                              false,
+                              speculative_ptr_kind(tinput) == ProfileNeverNull &&
+                              !too_many_traps_or_recompiles(Deoptimization::Reason_speculate_null_check));
+  dec_sp(2);
+  assert(!stopped(), "null input should have been caught earlier");
+  return cast;
+}
+
+void Parse::acmp_known_non_inline_type_input(Node* input, const TypeOopPtr* tinput, ProfilePtrKind input_ptr, ciKlass* input_type, BoolTest::mask btest, Node* eq_region) {
+  Node* ne_region = new RegionNode(1);
+  Node* null_ctl;
+  Node* cast = acmp_null_check(input, tinput, input_ptr, null_ctl);
+  ne_region->add_req(null_ctl);
+
+  Node* slow_ctl = type_check_receiver(cast, input_type, 1.0, &cast);
+  {
+    PreserveJVMState pjvms(this);
+    inc_sp(2);
+    set_control(slow_ctl);
+    Deoptimization::DeoptReason reason;
+    if (tinput->speculative_type() != nullptr && !too_many_traps_or_recompiles(Deoptimization::Reason_speculate_class_check)) {
+      reason = Deoptimization::Reason_speculate_class_check;
+    } else {
+      reason = Deoptimization::Reason_class_check;
+    }
+    uncommon_trap_exact(reason, Deoptimization::Action_maybe_recompile);
+  }
+  ne_region->add_req(control());
+
+  record_for_igvn(ne_region);
+  set_control(_gvn.transform(ne_region));
+  if (btest == BoolTest::ne) {
+    {
+      PreserveJVMState pjvms(this);
+      if (null_ctl == top()) {
+        replace_in_map(input, cast);
+      }
+      int target_bci = iter().get_dest();
+      merge(target_bci);
+    }
+    record_for_igvn(eq_region);
+    set_control(_gvn.transform(eq_region));
+  } else {
+    if (null_ctl == top()) {
+      replace_in_map(input, cast);
+    }
+    set_control(_gvn.transform(ne_region));
+  }
+}
+
+void Parse::acmp_unknown_non_inline_type_input(Node* input, const TypeOopPtr* tinput, ProfilePtrKind input_ptr, BoolTest::mask btest, Node* eq_region) {
+  Node* ne_region = new RegionNode(1);
+  Node* null_ctl;
+  Node* cast = acmp_null_check(input, tinput, input_ptr, null_ctl);
+  ne_region->add_req(null_ctl);
+
+  {
+    BuildCutout unless(this, inline_type_test(cast, /* is_inline = */ false), PROB_MAX);
+    inc_sp(2);
+    uncommon_trap_exact(Deoptimization::Reason_class_check, Deoptimization::Action_maybe_recompile);
+  }
+
+  ne_region->add_req(control());
+
+  record_for_igvn(ne_region);
+  set_control(_gvn.transform(ne_region));
+  if (btest == BoolTest::ne) {
+    {
+      PreserveJVMState pjvms(this);
+      if (null_ctl == top()) {
+        replace_in_map(input, cast);
+      }
+      int target_bci = iter().get_dest();
+      merge(target_bci);
+    }
+    record_for_igvn(eq_region);
+    set_control(_gvn.transform(eq_region));
+  } else {
+    if (null_ctl == top()) {
+      replace_in_map(input, cast);
+    }
+    set_control(_gvn.transform(ne_region));
+  }
+}
+
+void Parse::do_acmp(BoolTest::mask btest, Node* left, Node* right) {
+  ciKlass* left_type = nullptr;
+  ciKlass* right_type = nullptr;
+  ProfilePtrKind left_ptr = ProfileUnknownNull;
+  ProfilePtrKind right_ptr = ProfileUnknownNull;
+  bool left_inline_type = true;
+  bool right_inline_type = true;
+
+  // Leverage profiling at acmp
+  if (UseACmpProfile) {
+    method()->acmp_profiled_type(bci(), left_type, right_type, left_ptr, right_ptr, left_inline_type, right_inline_type);
+    if (too_many_traps_or_recompiles(Deoptimization::Reason_class_check)) {
+      left_type = nullptr;
+      right_type = nullptr;
+      left_inline_type = true;
+      right_inline_type = true;
+    }
+    if (too_many_traps_or_recompiles(Deoptimization::Reason_null_check)) {
+      left_ptr = ProfileUnknownNull;
+      right_ptr = ProfileUnknownNull;
+    }
+  }
+
+  if (UseTypeSpeculation) {
+    record_profile_for_speculation(left, left_type, left_ptr);
+    record_profile_for_speculation(right, right_type, right_ptr);
+  }
+
+  if (!EnableValhalla) {
+    Node* cmp = CmpP(left, right);
+    cmp = optimize_cmp_with_klass(cmp);
+    do_if(btest, cmp);
+    return;
+  }
+
+  // Check for equality before potentially allocating
+  if (left == right) {
+    do_if(btest, makecon(TypeInt::CC_EQ));
+    return;
+  }
+
+  // Allocate inline type operands and re-execute on deoptimization
+  if (left->is_InlineType()) {
+    if (_gvn.type(right)->is_zero_type() ||
+        (right->is_InlineType() && _gvn.type(right->as_InlineType()->get_is_init())->is_zero_type())) {
+      // Null checking a scalarized but nullable inline type. Check the IsInit
+      // input instead of the oop input to avoid keeping buffer allocations alive.
+      Node* cmp = CmpI(left->as_InlineType()->get_is_init(), intcon(0));
+      do_if(btest, cmp);
+      return;
+    } else {
+      PreserveReexecuteState preexecs(this);
+      inc_sp(2);
+      jvms()->set_should_reexecute(true);
+      left = left->as_InlineType()->buffer(this)->get_oop();
+    }
+  }
+  if (right->is_InlineType()) {
+    PreserveReexecuteState preexecs(this);
+    inc_sp(2);
+    jvms()->set_should_reexecute(true);
+    right = right->as_InlineType()->buffer(this)->get_oop();
+  }
+
+  // First, do a normal pointer comparison
+  const TypeOopPtr* tleft = _gvn.type(left)->isa_oopptr();
+  const TypeOopPtr* tright = _gvn.type(right)->isa_oopptr();
+  Node* cmp = CmpP(left, right);
+  cmp = optimize_cmp_with_klass(cmp);
+  if (tleft == nullptr || !tleft->can_be_inline_type() ||
+      tright == nullptr || !tright->can_be_inline_type()) {
+    // This is sufficient, if one of the operands can't be an inline type
+    do_if(btest, cmp);
+    return;
+  }
+
+  // Don't add traps to unstable if branches because additional checks are required to
+  // decide if the operands are equal/substitutable and we therefore shouldn't prune
+  // branches for one if based on the profiling of the acmp branches.
+  // Also, OptimizeUnstableIf would set an incorrect re-rexecution state because it
+  // assumes that there is a 1-1 mapping between the if and the acmp branches and that
+  // hitting a trap means that we will take the corresponding acmp branch on re-execution.
+  const bool can_trap = true;
+
+  Node* eq_region = nullptr;
+  if (btest == BoolTest::eq) {
+    do_if(btest, cmp, !can_trap, true);
+    if (stopped()) {
+      // Pointers are equal, operands must be equal
+      return;
+    }
+  } else {
+    assert(btest == BoolTest::ne, "only eq or ne");
+    Node* is_not_equal = nullptr;
+    eq_region = new RegionNode(3);
+    {
+      PreserveJVMState pjvms(this);
+      // Pointers are not equal, but more checks are needed to determine if the operands are (not) substitutable
+      do_if(btest, cmp, !can_trap, false, &is_not_equal);
+      if (!stopped()) {
+        eq_region->init_req(1, control());
+      }
+    }
+    if (is_not_equal == nullptr || is_not_equal->is_top()) {
+      record_for_igvn(eq_region);
+      set_control(_gvn.transform(eq_region));
+      return;
+    }
+    set_control(is_not_equal);
+  }
+
+  // Prefer speculative types if available
+  if (!too_many_traps_or_recompiles(Deoptimization::Reason_speculate_class_check)) {
+    if (tleft->speculative_type() != nullptr) {
+      left_type = tleft->speculative_type();
+    }
+    if (tright->speculative_type() != nullptr) {
+      right_type = tright->speculative_type();
+    }
+  }
+
+  if (speculative_ptr_kind(tleft) != ProfileMaybeNull && speculative_ptr_kind(tleft) != ProfileUnknownNull) {
+    ProfilePtrKind speculative_left_ptr = speculative_ptr_kind(tleft);
+    if (speculative_left_ptr == ProfileAlwaysNull && !too_many_traps_or_recompiles(Deoptimization::Reason_speculate_null_assert)) {
+      left_ptr = speculative_left_ptr;
+    } else if (speculative_left_ptr == ProfileNeverNull && !too_many_traps_or_recompiles(Deoptimization::Reason_speculate_null_check)) {
+      left_ptr = speculative_left_ptr;
+    }
+  }
+  if (speculative_ptr_kind(tright) != ProfileMaybeNull && speculative_ptr_kind(tright) != ProfileUnknownNull) {
+    ProfilePtrKind speculative_right_ptr = speculative_ptr_kind(tright);
+    if (speculative_right_ptr == ProfileAlwaysNull && !too_many_traps_or_recompiles(Deoptimization::Reason_speculate_null_assert)) {
+      right_ptr = speculative_right_ptr;
+    } else if (speculative_right_ptr == ProfileNeverNull && !too_many_traps_or_recompiles(Deoptimization::Reason_speculate_null_check)) {
+      right_ptr = speculative_right_ptr;
+    }
+  }
+
+  if (left_ptr == ProfileAlwaysNull) {
+    // Comparison with null. Assert the input is indeed null and we're done.
+    acmp_always_null_input(left, tleft, btest, eq_region);
+    return;
+  }
+  if (right_ptr == ProfileAlwaysNull) {
+    // Comparison with null. Assert the input is indeed null and we're done.
+    acmp_always_null_input(right, tright, btest, eq_region);
+    return;
+  }
+  if (left_type != nullptr && !left_type->is_inlinetype()) {
+    // Comparison with an object of known type
+    acmp_known_non_inline_type_input(left, tleft, left_ptr, left_type, btest, eq_region);
+    return;
+  }
+  if (right_type != nullptr && !right_type->is_inlinetype()) {
+    // Comparison with an object of known type
+    acmp_known_non_inline_type_input(right, tright, right_ptr, right_type, btest, eq_region);
+    return;
+  }
+  if (!left_inline_type) {
+    // Comparison with an object known not to be an inline type
+    acmp_unknown_non_inline_type_input(left, tleft, left_ptr, btest, eq_region);
+    return;
+  }
+  if (!right_inline_type) {
+    // Comparison with an object known not to be an inline type
+    acmp_unknown_non_inline_type_input(right, tright, right_ptr, btest, eq_region);
+    return;
+  }
+
+  // Pointers are not equal, check if first operand is non-null
+  Node* ne_region = new RegionNode(6);
+  Node* null_ctl;
+  Node* not_null_right = acmp_null_check(right, tright, right_ptr, null_ctl);
+  ne_region->init_req(1, null_ctl);
+
+  // First operand is non-null, check if it is an inline type
+  Node* is_value = inline_type_test(not_null_right);
+  IfNode* is_value_iff = create_and_map_if(control(), is_value, PROB_FAIR, COUNT_UNKNOWN);
+  Node* not_value = _gvn.transform(new IfFalseNode(is_value_iff));
+  ne_region->init_req(2, not_value);
+  set_control(_gvn.transform(new IfTrueNode(is_value_iff)));
+
+  // The first operand is an inline type, check if the second operand is non-null
+  Node* not_null_left = acmp_null_check(left, tleft, left_ptr, null_ctl);
+  ne_region->init_req(3, null_ctl);
+
+  // Check if both operands are of the same class.
+  Node* kls_left = load_object_klass(not_null_left);
+  Node* kls_right = load_object_klass(not_null_right);
+  Node* kls_cmp = CmpP(kls_left, kls_right);
+  Node* kls_bol = _gvn.transform(new BoolNode(kls_cmp, BoolTest::ne));
+  IfNode* kls_iff = create_and_map_if(control(), kls_bol, PROB_FAIR, COUNT_UNKNOWN);
+  Node* kls_ne = _gvn.transform(new IfTrueNode(kls_iff));
+  set_control(_gvn.transform(new IfFalseNode(kls_iff)));
+  ne_region->init_req(4, kls_ne);
+
+  if (stopped()) {
+    record_for_igvn(ne_region);
+    set_control(_gvn.transform(ne_region));
+    if (btest == BoolTest::ne) {
+      {
+        PreserveJVMState pjvms(this);
+        int target_bci = iter().get_dest();
+        merge(target_bci);
+      }
+      record_for_igvn(eq_region);
+      set_control(_gvn.transform(eq_region));
+    }
+    return;
+  }
+
+  // Both operands are values types of the same class, we need to perform a
+  // substitutability test. Delegate to ValueObjectMethods::isSubstitutable().
+  Node* ne_io_phi = PhiNode::make(ne_region, i_o());
+  Node* mem = reset_memory();
+  Node* ne_mem_phi = PhiNode::make(ne_region, mem);
+
+  Node* eq_io_phi = nullptr;
+  Node* eq_mem_phi = nullptr;
+  if (eq_region != nullptr) {
+    eq_io_phi = PhiNode::make(eq_region, i_o());
+    eq_mem_phi = PhiNode::make(eq_region, mem);
+  }
+
+  set_all_memory(mem);
+
+  kill_dead_locals();
+  ciMethod* subst_method = ciEnv::current()->ValueObjectMethods_klass()->find_method(ciSymbols::isSubstitutable_name(), ciSymbols::object_object_boolean_signature());
+  CallStaticJavaNode *call = new CallStaticJavaNode(C, TypeFunc::make(subst_method), SharedRuntime::get_resolve_static_call_stub(), subst_method);
+  call->set_override_symbolic_info(true);
+  call->init_req(TypeFunc::Parms, not_null_left);
+  call->init_req(TypeFunc::Parms+1, not_null_right);
+  inc_sp(2);
+  set_edges_for_java_call(call, false, false);
+  Node* ret = set_results_for_java_call(call, false, true);
+  dec_sp(2);
+
+  // Test the return value of ValueObjectMethods::isSubstitutable()
+  // This is the last check, do_if can emit traps now.
+  Node* subst_cmp = _gvn.transform(new CmpINode(ret, intcon(1)));
+  Node* ctl = C->top();
+  if (btest == BoolTest::eq) {
+    PreserveJVMState pjvms(this);
+    do_if(btest, subst_cmp, can_trap);
+    if (!stopped()) {
+      ctl = control();
+    }
+  } else {
+    assert(btest == BoolTest::ne, "only eq or ne");
+    PreserveJVMState pjvms(this);
+    do_if(btest, subst_cmp, can_trap, false, &ctl);
+    if (!stopped()) {
+      eq_region->init_req(2, control());
+      eq_io_phi->init_req(2, i_o());
+      eq_mem_phi->init_req(2, reset_memory());
+    }
+  }
+  ne_region->init_req(5, ctl);
+  ne_io_phi->init_req(5, i_o());
+  ne_mem_phi->init_req(5, reset_memory());
+
+  record_for_igvn(ne_region);
+  set_control(_gvn.transform(ne_region));
+  set_i_o(_gvn.transform(ne_io_phi));
+  set_all_memory(_gvn.transform(ne_mem_phi));
+
+  if (btest == BoolTest::ne) {
+    {
+      PreserveJVMState pjvms(this);
+      int target_bci = iter().get_dest();
+      merge(target_bci);
+    }
+
+    record_for_igvn(eq_region);
+    set_control(_gvn.transform(eq_region));
+    set_i_o(_gvn.transform(eq_io_phi));
+    set_all_memory(_gvn.transform(eq_mem_phi));
   }
 }
 
 bool Parse::path_is_suitable_for_uncommon_trap(float prob) const {
   // Don't want to speculate on uncommon traps when running with -Xcomp
@@ -1591,11 +2338,11 @@
 // Adjust the JVM state to reflect the result of taking this path.
 // Basically, it means inspecting the CmpNode controlling this
 // branch, seeing how it constrains a tested value, and then
 // deciding if it's worth our while to encode this constraint
 // as graph nodes in the current abstract interpretation map.
-void Parse::adjust_map_after_if(BoolTest::mask btest, Node* c, float prob, Block* path) {
+void Parse::adjust_map_after_if(BoolTest::mask btest, Node* c, float prob, Block* path, bool can_trap) {
   if (!c->is_Cmp()) {
     maybe_add_predicate_after_if(path);
     return;
   }
 
@@ -1603,11 +2350,11 @@
     return;                             // nothing to do
   }
 
   bool is_fallthrough = (path == successor_for_bci(iter().next_bci()));
 
-  if (path_is_suitable_for_uncommon_trap(prob)) {
+  if (can_trap && path_is_suitable_for_uncommon_trap(prob)) {
     repush_if_args();
     Node* call = uncommon_trap(Deoptimization::Reason_unstable_if,
                   Deoptimization::Action_reinterpret,
                   nullptr,
                   (is_fallthrough ? "taken always" : "taken never"));
@@ -1700,10 +2447,13 @@
             assert(tcc != obj_type && tcc->higher_equal(obj_type), "must improve");
             // Delay transform() call to allow recovery of pre-cast value
             // at the control merge.
             _gvn.set_type_bottom(ccast);
             record_for_igvn(ccast);
+            if (tboth->is_inlinetypeptr()) {
+              ccast = InlineTypeNode::make_from_oop(this, ccast, tboth->exact_klass(true)->as_inline_klass());
+            }
             // Here's the payoff.
             replace_in_map(obj, ccast);
           }
        }
     }
@@ -1804,10 +2554,14 @@
       if (obj_type->speculative_type_not_null() != nullptr) {
         ciKlass* k = obj_type->speculative_type();
         inc_sp(2);
         obj = maybe_cast_profiled_obj(obj, k);
         dec_sp(2);
+        if (obj->is_InlineType()) {
+          assert(obj->as_InlineType()->is_allocated(&_gvn), "must be allocated");
+          obj = obj->as_InlineType()->get_oop();
+        }
         // Make the CmpP use the casted obj
         addp = basic_plus_adr(obj, addp->in(AddPNode::Offset));
         load_klass = load_klass->clone();
         load_klass->set_req(2, addp);
         load_klass = _gvn.transform(load_klass);
@@ -2650,37 +3404,41 @@
   handle_if_null:
     // If this is a backwards branch in the bytecodes, add Safepoint
     maybe_add_safepoint(iter().get_dest());
     a = null();
     b = pop();
-    if (!_gvn.type(b)->speculative_maybe_null() &&
-        !too_many_traps(Deoptimization::Reason_speculate_null_check)) {
-      inc_sp(1);
-      Node* null_ctl = top();
-      b = null_check_oop(b, &null_ctl, true, true, true);
-      assert(null_ctl->is_top(), "no null control here");
-      dec_sp(1);
-    } else if (_gvn.type(b)->speculative_always_null() &&
-               !too_many_traps(Deoptimization::Reason_speculate_null_assert)) {
-      inc_sp(1);
-      b = null_assert(b);
-      dec_sp(1);
-    }
-    c = _gvn.transform( new CmpPNode(b, a) );
+    if (b->is_InlineType()) {
+      // Null checking a scalarized but nullable inline type. Check the IsInit
+      // input instead of the oop input to avoid keeping buffer allocations alive
+      c = _gvn.transform(new CmpINode(b->as_InlineType()->get_is_init(), zerocon(T_INT)));
+    } else {
+      if (!_gvn.type(b)->speculative_maybe_null() &&
+          !too_many_traps(Deoptimization::Reason_speculate_null_check)) {
+        inc_sp(1);
+        Node* null_ctl = top();
+        b = null_check_oop(b, &null_ctl, true, true, true);
+        assert(null_ctl->is_top(), "no null control here");
+        dec_sp(1);
+      } else if (_gvn.type(b)->speculative_always_null() &&
+                 !too_many_traps(Deoptimization::Reason_speculate_null_assert)) {
+        inc_sp(1);
+        b = null_assert(b);
+        dec_sp(1);
+      }
+      c = _gvn.transform( new CmpPNode(b, a) );
+    }
     do_ifnull(btest, c);
     break;
 
   case Bytecodes::_if_acmpeq: btest = BoolTest::eq; goto handle_if_acmp;
   case Bytecodes::_if_acmpne: btest = BoolTest::ne; goto handle_if_acmp;
   handle_if_acmp:
     // If this is a backwards branch in the bytecodes, add Safepoint
     maybe_add_safepoint(iter().get_dest());
     a = pop();
     b = pop();
-    c = _gvn.transform( new CmpPNode(b, a) );
-    c = optimize_cmp_with_klass(c);
-    do_if(btest, c);
+    do_acmp(btest, b, a);
     break;
 
   case Bytecodes::_ifeq: btest = BoolTest::eq; goto handle_ifxx;
   case Bytecodes::_ifne: btest = BoolTest::ne; goto handle_ifxx;
   case Bytecodes::_iflt: btest = BoolTest::lt; goto handle_ifxx;
@@ -2731,11 +3489,11 @@
     break;
   case Bytecodes::_instanceof:
     do_instanceof();
     break;
   case Bytecodes::_anewarray:
-    do_anewarray();
+    do_newarray();
     break;
   case Bytecodes::_newarray:
     do_newarray((BasicType)iter().get_index());
     break;
   case Bytecodes::_multianewarray:
diff a/src/hotspot/share/opto/parse3.cpp b/src/hotspot/share/opto/parse3.cpp
--- a/src/hotspot/share/opto/parse3.cpp
+++ b/src/hotspot/share/opto/parse3.cpp
@@ -24,13 +24,15 @@
 
 #include "precompiled.hpp"
 #include "compiler/compileLog.hpp"
 #include "interpreter/linkResolver.hpp"
 #include "memory/universe.hpp"
+#include "oops/flatArrayKlass.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "opto/addnode.hpp"
 #include "opto/castnode.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/memnode.hpp"
 #include "opto/parse.hpp"
 #include "opto/rootnode.hpp"
 #include "opto/runtime.hpp"
 #include "opto/subnode.hpp"
@@ -38,28 +40,41 @@
 #include "runtime/handles.inline.hpp"
 
 //=============================================================================
 // Helper methods for _get* and _put* bytecodes
 //=============================================================================
+
 void Parse::do_field_access(bool is_get, bool is_field) {
   bool will_link;
   ciField* field = iter().get_field(will_link);
   assert(will_link, "getfield: typeflow responsibility");
 
   ciInstanceKlass* field_holder = field->holder();
 
+  if (is_get && is_field && field_holder->is_inlinetype() && peek()->is_InlineType()) {
+    InlineTypeNode* vt = peek()->as_InlineType();
+    null_check(vt);
+    Node* value = vt->field_value_by_offset(field->offset_in_bytes());
+    if (value->is_InlineType()) {
+      value = value->as_InlineType()->adjust_scalarization_depth(this);
+    }
+    pop();
+    push_node(field->layout_type(), value);
+    return;
+  }
+
   if (is_field == field->is_static()) {
     // Interpreter will throw java_lang_IncompatibleClassChangeError
     // Check this before allowing <clinit> methods to access static fields
     uncommon_trap(Deoptimization::Reason_unhandled,
                   Deoptimization::Action_none);
     return;
   }
 
   // Deoptimize on putfield writes to call site target field outside of CallSite ctor.
   if (!is_get && field->is_call_site_target() &&
-      !(method()->holder() == field_holder && method()->is_object_initializer())) {
+      !(method()->holder() == field_holder && method()->is_object_constructor())) {
     uncommon_trap(Deoptimization::Reason_unhandled,
                   Deoptimization::Action_reinterpret,
                   nullptr, "put to call site target field");
     return;
   }
@@ -86,32 +101,33 @@
     assert(_gvn.type(obj)->higher_equal(tjp), "cast_up is no longer needed");
 #endif
 
     if (is_get) {
       (void) pop();  // pop receiver before getting
-      do_get_xxx(obj, field, is_field);
+      do_get_xxx(obj, field);
     } else {
       do_put_xxx(obj, field, is_field);
+      if (stopped()) {
+        return;
+      }
       (void) pop();  // pop receiver after putting
     }
   } else {
     const TypeInstPtr* tip = TypeInstPtr::make(field_holder->java_mirror());
     obj = _gvn.makecon(tip);
     if (is_get) {
-      do_get_xxx(obj, field, is_field);
+      do_get_xxx(obj, field);
     } else {
       do_put_xxx(obj, field, is_field);
     }
   }
 }
 
-
-void Parse::do_get_xxx(Node* obj, ciField* field, bool is_field) {
+void Parse::do_get_xxx(Node* obj, ciField* field) {
   BasicType bt = field->layout_type();
-
   // Does this field have a constant value?  If so, just push the value.
-  if (field->is_constant() &&
+  if (field->is_constant() && !field->is_flat() &&
       // Keep consistent with types found by ciTypeFlow: for an
       // unloaded field type, ciTypeFlow::StateVector::do_getstatic()
       // speculates the field is null. The code in the rest of this
       // method does the same. We must not bypass it and use a non
       // null constant here.
@@ -123,51 +139,63 @@
       return;
     }
   }
 
   ciType* field_klass = field->type();
-  bool is_vol = field->is_volatile();
-
-  // Compute address and memory type.
   int offset = field->offset_in_bytes();
-  const TypePtr* adr_type = C->alias_type(field)->adr_type();
-  Node *adr = basic_plus_adr(obj, obj, offset);
-
-  // Build the resultant type of the load
-  const Type *type;
-
   bool must_assert_null = false;
 
-  DecoratorSet decorators = IN_HEAP;
-  decorators |= is_vol ? MO_SEQ_CST : MO_UNORDERED;
-
-  bool is_obj = is_reference_type(bt);
-
-  if (is_obj) {
-    if (!field->type()->is_loaded()) {
-      type = TypeInstPtr::BOTTOM;
-      must_assert_null = true;
-    } else if (field->is_static_constant()) {
-      // This can happen if the constant oop is non-perm.
-      ciObject* con = field->constant_value().as_object();
-      // Do not "join" in the previous type; it doesn't add value,
-      // and may yield a vacuous result if the field is of interface type.
-      if (con->is_null_object()) {
-        type = TypePtr::NULL_PTR;
+  Node* ld = nullptr;
+  if (field->is_null_free() && field_klass->as_inline_klass()->is_empty()) {
+    // Loading from a field of an empty inline type. Just return the default instance.
+    ld = InlineTypeNode::make_default(_gvn, field_klass->as_inline_klass());
+  } else if (field->is_flat()) {
+    // Loading from a flat inline type field.
+    ld = InlineTypeNode::make_from_flat(this, field_klass->as_inline_klass(), obj, obj, field->holder(), offset);
+  } else {
+    // Build the resultant type of the load
+    const Type* type;
+    if (is_reference_type(bt)) {
+      if (!field_klass->is_loaded()) {
+        type = TypeInstPtr::BOTTOM;
+        must_assert_null = true;
+      } else if (field->is_static_constant()) {
+        // This can happen if the constant oop is non-perm.
+        ciObject* con = field->constant_value().as_object();
+        // Do not "join" in the previous type; it doesn't add value,
+        // and may yield a vacuous result if the field is of interface type.
+        if (con->is_null_object()) {
+          type = TypePtr::NULL_PTR;
+        } else {
+          type = TypeOopPtr::make_from_constant(con)->isa_oopptr();
+        }
+        assert(type != nullptr, "field singleton type must be consistent");
       } else {
-        type = TypeOopPtr::make_from_constant(con)->isa_oopptr();
+        type = TypeOopPtr::make_from_klass(field_klass->as_klass());
+        if (field->is_null_free() && field->is_static()) {
+          // Check if static inline type field is already initialized
+          ciInstance* mirror = field->holder()->java_mirror();
+          ciObject* val = mirror->field_value(field).as_object();
+          if (!val->is_null_object()) {
+            type = type->join_speculative(TypePtr::NOTNULL);
+          }
+        }
       }
-      assert(type != nullptr, "field singleton type must be consistent");
     } else {
-      type = TypeOopPtr::make_from_klass(field_klass->as_klass());
+      type = Type::get_const_basic_type(bt);
+    }
+    Node* adr = basic_plus_adr(obj, obj, offset);
+    const TypePtr* adr_type = C->alias_type(field)->adr_type();
+    DecoratorSet decorators = IN_HEAP;
+    decorators |= field->is_volatile() ? MO_SEQ_CST : MO_UNORDERED;
+    ld = access_load_at(obj, adr, adr_type, type, bt, decorators);
+    if (field_klass->is_inlinetype()) {
+      // Load a non-flattened inline type from memory
+      ld = InlineTypeNode::make_from_oop(this, ld, field_klass->as_inline_klass(), field->is_null_free());
     }
-  } else {
-    type = Type::get_const_basic_type(bt);
   }
 
-  Node* ld = access_load_at(obj, adr, adr_type, type, bt, decorators);
-
   // Adjust Java stack
   if (type2size[bt] == 1)
     push(ld);
   else
     push_pair(ld);
@@ -186,47 +214,129 @@
     if (PrintOpto && (Verbose || WizardMode)) {
       method()->print_name(); tty->print_cr(" asserting nullness of field at bci: %d", bci());
     }
     if (C->log() != nullptr) {
       C->log()->elem("assert_null reason='field' klass='%d'",
-                     C->log()->identify(field->type()));
+                     C->log()->identify(field_klass));
     }
     // If there is going to be a trap, put it at the next bytecode:
     set_bci(iter().next_bci());
     null_assert(peek());
     set_bci(iter().cur_bci()); // put it back
   }
 }
 
 void Parse::do_put_xxx(Node* obj, ciField* field, bool is_field) {
   bool is_vol = field->is_volatile();
-
-  // Compute address and memory type.
   int offset = field->offset_in_bytes();
-  const TypePtr* adr_type = C->alias_type(field)->adr_type();
-  Node* adr = basic_plus_adr(obj, obj, offset);
   BasicType bt = field->layout_type();
-  // Value to be stored
   Node* val = type2size[bt] == 1 ? pop() : pop_pair();
 
-  DecoratorSet decorators = IN_HEAP;
-  decorators |= is_vol ? MO_SEQ_CST : MO_UNORDERED;
+  if (obj->is_InlineType()) {
+    // TODO 8325106 Factor into own method
+    // TODO 8325106 Assert that we only do this in the constructor and align with checks in ::do_call
+    //if (_method->is_object_constructor() && _method->holder()->is_inlinetype()) {
+    assert(obj->as_InlineType()->is_larval(), "must be larval");
+
+    // TODO 8325106 Assert that holder is null-free
+    /*
+    int holder_depth = field->type()->size();
+    null_check(peek(holder_depth));
+    if (stopped()) {
+      return;
+    }
+    */
+
+    if (field->is_null_free()) {
+      PreserveReexecuteState preexecs(this);
+      jvms()->set_should_reexecute(true);
+      inc_sp(1);
+      val = null_check(val);
+      if (stopped()) {
+        return;
+      }
+    }
+    if (!val->is_InlineType() && field->type()->is_inlinetype()) {
+      // Scalarize inline type field value
+      val = InlineTypeNode::make_from_oop(this, val, field->type()->as_inline_klass(), field->is_null_free());
+    } else if (val->is_InlineType() && !field->is_flat()) {
+      // Field value needs to be allocated because it can be merged with an oop.
+      // Re-execute if buffering triggers deoptimization.
+      PreserveReexecuteState preexecs(this);
+      jvms()->set_should_reexecute(true);
+      inc_sp(1);
+      val = val->as_InlineType()->buffer(this);
+    }
+
+    // Clone the inline type node and set the new field value
+    InlineTypeNode* new_vt = obj->as_InlineType()->clone_if_required(&_gvn, _map);
+    new_vt->set_field_value_by_offset(field->offset_in_bytes(), val);
+    {
+      PreserveReexecuteState preexecs(this);
+      jvms()->set_should_reexecute(true);
+      inc_sp(1);
+      new_vt = new_vt->adjust_scalarization_depth(this);
+    }
+
+    // TODO 8325106 Double check and explain these checks
+    if ((!_caller->has_method() || C->inlining_incrementally() || _caller->method()->is_object_constructor()) && new_vt->is_allocated(&gvn())) {
+      assert(new_vt->as_InlineType()->is_allocated(&gvn()), "must be buffered");
+      // We need to store to the buffer
+      // TODO 8325106 looks like G1BarrierSetC2::g1_can_remove_pre_barrier is not strong enough to remove the pre barrier
+      // TODO is it really guaranteed that the preval is null?
+      new_vt->store(this, new_vt->get_oop(), new_vt->get_oop(), new_vt->bottom_type()->inline_klass(), 0, C2_TIGHTLY_COUPLED_ALLOC | IN_HEAP | MO_UNORDERED, field->offset_in_bytes());
+
+      // Preserve allocation ptr to create precedent edge to it in membar
+      // generated on exit from constructor.
+      AllocateNode* alloc = AllocateNode::Ideal_allocation(new_vt->get_oop());
+      if (alloc != nullptr) {
+        set_alloc_with_final(new_vt->get_oop());
+      }
+      set_wrote_final(true);
+    }
 
-  bool is_obj = is_reference_type(bt);
+    replace_in_map(obj, _gvn.transform(new_vt));
+    return;
+  }
 
-  // Store the value.
-  const Type* field_type;
-  if (!field->type()->is_loaded()) {
-    field_type = TypeInstPtr::BOTTOM;
+  if (field->is_null_free()) {
+    PreserveReexecuteState preexecs(this);
+    inc_sp(1);
+    jvms()->set_should_reexecute(true);
+    val = null_check(val);
+  }
+  if (field->is_null_free() && field->type()->as_inline_klass()->is_empty()) {
+    // Storing to a field of an empty inline type. Ignore.
+    return;
+  } else if (field->is_flat()) {
+    // Storing to a flat inline type field.
+    if (!val->is_InlineType()) {
+      val = InlineTypeNode::make_from_oop(this, val, field->type()->as_inline_klass());
+    }
+    inc_sp(1);
+    val->as_InlineType()->store_flat(this, obj, obj, field->holder(), offset);
+    dec_sp(1);
   } else {
-    if (is_obj) {
-      field_type = TypeOopPtr::make_from_klass(field->type()->as_klass());
+    // Store the value.
+    const Type* field_type;
+    if (!field->type()->is_loaded()) {
+      field_type = TypeInstPtr::BOTTOM;
     } else {
-      field_type = Type::BOTTOM;
+      if (is_reference_type(bt)) {
+        field_type = TypeOopPtr::make_from_klass(field->type()->as_klass());
+      } else {
+        field_type = Type::BOTTOM;
+      }
     }
+    Node* adr = basic_plus_adr(obj, obj, offset);
+    const TypePtr* adr_type = C->alias_type(field)->adr_type();
+    DecoratorSet decorators = IN_HEAP;
+    decorators |= is_vol ? MO_SEQ_CST : MO_UNORDERED;
+    inc_sp(1);
+    access_store_at(obj, adr, adr_type, val, field_type, bt, decorators);
+    dec_sp(1);
   }
-  access_store_at(obj, adr, adr_type, val, field_type, bt, decorators);
 
   if (is_field) {
     // Remember we wrote a volatile field.
     // For not multiple copy atomic cpu (ppc64) a barrier should be issued
     // in constructors which have such stores. See do_exits() in parse1.cpp.
@@ -254,27 +364,36 @@
     }
   }
 }
 
 //=============================================================================
-void Parse::do_anewarray() {
+
+void Parse::do_newarray() {
   bool will_link;
   ciKlass* klass = iter().get_klass(will_link);
 
   // Uncommon Trap when class that array contains is not loaded
   // we need the loaded class for the rest of graph; do not
   // initialize the container class (see Java spec)!!!
-  assert(will_link, "anewarray: typeflow responsibility");
+  assert(will_link, "newarray: typeflow responsibility");
+
+  ciArrayKlass* array_klass = ciArrayKlass::make(klass);
 
-  ciObjArrayKlass* array_klass = ciObjArrayKlass::make(klass);
   // Check that array_klass object is loaded
   if (!array_klass->is_loaded()) {
     // Generate uncommon_trap for unloaded array_class
     uncommon_trap(Deoptimization::Reason_unloaded,
                   Deoptimization::Action_reinterpret,
                   array_klass);
     return;
+  } else if (array_klass->element_klass() != nullptr &&
+             array_klass->element_klass()->is_inlinetype() &&
+             !array_klass->element_klass()->as_inline_klass()->is_initialized()) {
+    uncommon_trap(Deoptimization::Reason_uninitialized,
+                  Deoptimization::Action_reinterpret,
+                  nullptr);
+    return;
   }
 
   kill_dead_locals();
 
   const TypeKlassPtr* array_klass_type = TypeKlassPtr::make(array_klass, Type::trust_interfaces);
@@ -331,11 +450,22 @@
 
   // get the lengths from the stack (first dimension is on top)
   Node** length = NEW_RESOURCE_ARRAY(Node*, ndimensions + 1);
   length[ndimensions] = nullptr;  // terminating null for make_runtime_call
   int j;
-  for (j = ndimensions-1; j >= 0 ; j--) length[j] = pop();
+  ciKlass* elem_klass = array_klass;
+  for (j = ndimensions-1; j >= 0; j--) {
+    length[j] = pop();
+    elem_klass = elem_klass->as_array_klass()->element_klass();
+  }
+  if (elem_klass != nullptr && elem_klass->is_inlinetype() && !elem_klass->as_inline_klass()->is_initialized()) {
+    inc_sp(ndimensions);
+    uncommon_trap(Deoptimization::Reason_uninitialized,
+                  Deoptimization::Action_reinterpret,
+                  nullptr);
+    return;
+  }
 
   // The original expression was of this form: new T[length0][length1]...
   // It is often the case that the lengths are small (except the last).
   // If that happens, use the fast 1-d creator a constant number of times.
   const int expand_limit = MIN2((int)MultiArrayExpandLimit, 100);
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
@@ -21,14 +21,18 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "ci/ciSymbols.hpp"
 #include "compiler/compileLog.hpp"
+#include "oops/flatArrayKlass.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "opto/addnode.hpp"
+#include "opto/castnode.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/memnode.hpp"
 #include "opto/mulnode.hpp"
 #include "opto/parse.hpp"
 #include "opto/rootnode.hpp"
 #include "opto/runtime.hpp"
@@ -63,11 +67,10 @@
 //=============================================================================
 //------------------------------do_checkcast-----------------------------------
 void Parse::do_checkcast() {
   bool will_link;
   ciKlass* klass = iter().get_klass(will_link);
-
   Node *obj = peek();
 
   // Throw uncommon trap if class is not loaded or the value we are casting
   // _from_ is not loaded, and value is not null.  If the value _is_ null,
   // then the checkcast does nothing.
@@ -136,44 +139,63 @@
   push(res);
 }
 
 //------------------------------array_store_check------------------------------
 // pull array from stack and check that the store is valid
-void Parse::array_store_check() {
-
+Node* Parse::array_store_check(Node*& adr, const Type*& elemtype) {
   // Shorthand access to array store elements without popping them.
   Node *obj = peek(0);
   Node *idx = peek(1);
   Node *ary = peek(2);
 
   if (_gvn.type(obj) == TypePtr::NULL_PTR) {
     // There's never a type check on null values.
     // This cutout lets us avoid the uncommon_trap(Reason_array_check)
     // below, which turns into a performance liability if the
     // gen_checkcast folds up completely.
-    return;
+    if (_gvn.type(ary)->is_aryptr()->is_null_free()) {
+      null_check(obj);
+    }
+    return obj;
   }
 
   // Extract the array klass type
-  int klass_offset = oopDesc::klass_offset_in_bytes();
-  Node* p = basic_plus_adr( ary, ary, klass_offset );
-  // p's type is array-of-OOPS plus klass_offset
-  Node* array_klass = _gvn.transform(LoadKlassNode::make(_gvn, nullptr, immutable_memory(), p, TypeInstPtr::KLASS));
+  Node* array_klass = load_object_klass(ary);
   // Get the array klass
-  const TypeKlassPtr *tak = _gvn.type(array_klass)->is_klassptr();
+  const TypeKlassPtr* tak = _gvn.type(array_klass)->is_klassptr();
 
   // The type of array_klass is usually INexact array-of-oop.  Heroically
   // cast array_klass to EXACT array and uncommon-trap if the cast fails.
   // Make constant out of the inexact array klass, but use it only if the cast
   // succeeds.
   bool always_see_exact_class = false;
-  if (MonomorphicArrayCheck
-      && !too_many_traps(Deoptimization::Reason_array_check)
-      && !tak->klass_is_exact()
-      && tak != TypeInstKlassPtr::OBJECT) {
-      // Regarding the fourth condition in the if-statement from above:
-      //
+  if (MonomorphicArrayCheck && !tak->klass_is_exact()) {
+    // Make a constant out of the inexact array klass
+    const TypeKlassPtr* extak = nullptr;
+    const TypeOopPtr* ary_t = _gvn.type(ary)->is_oopptr();
+    ciKlass* ary_spec = ary_t->speculative_type();
+    Deoptimization::DeoptReason reason = Deoptimization::Reason_none;
+    // Try to cast the array to an exact type from profile data. First
+    // check the speculative type.
+    if (ary_spec != nullptr && !too_many_traps(Deoptimization::Reason_speculate_class_check)) {
+      extak = TypeKlassPtr::make(ary_spec);
+      reason = Deoptimization::Reason_speculate_class_check;
+    } else if (UseArrayLoadStoreProfile) {
+      // No speculative type: check profile data at this bci.
+      reason = Deoptimization::Reason_class_check;
+      if (!too_many_traps(reason)) {
+        ciKlass* array_type = nullptr;
+        ciKlass* element_type = nullptr;
+        ProfilePtrKind element_ptr = ProfileMaybeNull;
+        bool flat_array = true;
+        bool null_free_array = true;
+        method()->array_access_profiled_type(bci(), array_type, element_type, element_ptr, flat_array, null_free_array);
+        if (array_type != nullptr) {
+          extak = TypeKlassPtr::make(array_type);
+        }
+      }
+    } else if (!too_many_traps(Deoptimization::Reason_array_check) && tak != TypeInstKlassPtr::OBJECT) {
       // If the compiler has determined that the type of array 'ary' (represented
       // by 'array_klass') is java/lang/Object, the compiler must not assume that
       // the array 'ary' is monomorphic.
       //
       // If 'ary' were of type java/lang/Object, this arraystore would have to fail,
@@ -188,56 +210,69 @@
       // java/lang/Object is the superclass of all arrays, but it is represented by the VM
       // as an InstanceKlass. The checks generated by gen_checkcast() (see below) expect
       // 'array_klass' to be ObjArrayKlass, which can result in invalid memory accesses.
       //
       // See issue JDK-8057622 for details.
-
-    always_see_exact_class = true;
-    // (If no MDO at all, hope for the best, until a trap actually occurs.)
-
-    // Make a constant out of the inexact array klass
-    const TypeKlassPtr *extak = tak->cast_to_exactness(true);
-
-    if (extak->exact_klass(true) != nullptr) {
+      extak = tak->cast_to_exactness(true);
+      reason = Deoptimization::Reason_array_check;
+    }
+    if (extak != nullptr && extak->exact_klass(true) != nullptr) {
       Node* con = makecon(extak);
-      Node* cmp = _gvn.transform(new CmpPNode( array_klass, con ));
-      Node* bol = _gvn.transform(new BoolNode( cmp, BoolTest::eq ));
-      Node* ctrl= control();
-      { BuildCutout unless(this, bol, PROB_MAX);
-        uncommon_trap(Deoptimization::Reason_array_check,
-                      Deoptimization::Action_maybe_recompile,
-                      extak->exact_klass());
-      }
-      if (stopped()) {          // MUST uncommon-trap?
-        set_control(ctrl);      // Then Don't Do It, just fall into the normal checking
-      } else {                  // Cast array klass to exactness:
-        // Use the exact constant value we know it is.
-        replace_in_map(array_klass,con);
+      Node* cmp = _gvn.transform(new CmpPNode(array_klass, con));
+      Node* bol = _gvn.transform(new BoolNode(cmp, BoolTest::eq));
+      // Only do it if the check does not always pass/fail
+      if (!bol->is_Con()) {
+        always_see_exact_class = true;
+        { BuildCutout unless(this, bol, PROB_MAX);
+          uncommon_trap(reason,
+                        Deoptimization::Action_maybe_recompile,
+                        extak->exact_klass());
+        }
+        // Cast array klass to exactness
+        replace_in_map(array_klass, con);
+        array_klass = con;
+        Node* cast = _gvn.transform(new CheckCastPPNode(control(), ary, extak->as_instance_type()));
+        replace_in_map(ary, cast);
+        ary = cast;
+
+        // Recompute element type and address
+        const TypeAryPtr* arytype = _gvn.type(ary)->is_aryptr();
+        elemtype = arytype->elem();
+        adr = array_element_address(ary, idx, T_OBJECT, arytype->size(), control());
+
         CompileLog* log = C->log();
         if (log != nullptr) {
           log->elem("cast_up reason='monomorphic_array' from='%d' to='(exact)'",
                     log->identify(extak->exact_klass()));
         }
-        array_klass = con;      // Use cast value moving forward
       }
     }
   }
 
   // Come here for polymorphic array klasses
 
   // Extract the array element class
-  int element_klass_offset = in_bytes(ObjArrayKlass::element_klass_offset());
+  int element_klass_offset = in_bytes(ArrayKlass::element_klass_offset());
+
   Node *p2 = basic_plus_adr(array_klass, array_klass, element_klass_offset);
   // We are allowed to use the constant type only if cast succeeded. If always_see_exact_class is true,
   // we must set a control edge from the IfTrue node created by the uncommon_trap above to the
   // LoadKlassNode.
   Node* a_e_klass = _gvn.transform(LoadKlassNode::make(_gvn, always_see_exact_class ? control() : nullptr,
                                                        immutable_memory(), p2, tak));
 
+  // If we statically know that this is an inline type array, use precise element klass for checkcast
+  const TypeAryPtr* arytype = _gvn.type(ary)->is_aryptr();
+  bool null_free = false;
+  if (elemtype->make_ptr()->is_inlinetypeptr()) {
+    // We statically know that this is an inline type array, use precise klass ptr
+    null_free = arytype->is_flat() || !elemtype->make_ptr()->maybe_null();
+    a_e_klass = makecon(TypeKlassPtr::make(elemtype->inline_klass()));
+  }
+
   // Check (the hard way) and throw if not a subklass.
-  // Result is ignored, we just need the CFG effects.
-  gen_checkcast(obj, a_e_klass);
+  return gen_checkcast(obj, a_e_klass, nullptr, null_free);
 }
 
 
 //------------------------------do_new-----------------------------------------
 void Parse::do_new() {
@@ -260,10 +295,15 @@
   if (C->needs_clinit_barrier(klass, method())) {
     clinit_barrier(klass, method());
     if (stopped())  return;
   }
 
+  if (klass->is_inlinetype()) {
+    push(InlineTypeNode::make_default(_gvn, klass->as_inline_klass(), /* is_larval */ true));
+    return;
+  }
+
   Node* kls = makecon(TypeKlassPtr::make(klass));
   Node* obj = new_instance(kls);
 
   // Push resultant oop onto stack
   push(obj);
diff a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp
--- a/src/hotspot/share/opto/phaseX.cpp
+++ b/src/hotspot/share/opto/phaseX.cpp
@@ -1158,22 +1158,22 @@
 }
 
 //------------------------------transform--------------------------------------
 // Non-recursive: idealize Node 'n' with respect to its inputs and its value
 Node *PhaseIterGVN::transform( Node *n ) {
-  if (_delay_transform) {
-    // Register the node but don't optimize for now
-    register_new_node_with_optimizer(n);
-    return n;
-  }
-
   // If brand new node, make space in type array, and give it a type.
   ensure_type_or_null(n);
   if (type_or_null(n) == nullptr) {
     set_type_bottom(n);
   }
 
+  if (_delay_transform) {
+    // Add the node to the worklist but don't optimize for now
+    _worklist.push(n);
+    return n;
+  }
+
   return transform_old(n);
 }
 
 Node *PhaseIterGVN::transform_old(Node* n) {
   NOT_PRODUCT(set_transforms());
@@ -1440,10 +1440,23 @@
   }
 #endif
   temp->destruct(this);     // reuse the _idx of this little guy
 }
 
+void PhaseIterGVN::replace_in_uses(Node* n, Node* m) {
+  assert(n != nullptr, "sanity");
+  for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
+    Node* u = n->fast_out(i);
+    if (u != n) {
+      rehash_node_delayed(u);
+      int nb = u->replace_edge(n, m);
+      --i, imax -= nb;
+    }
+  }
+  assert(n->outcnt() == 0, "all uses must be deleted");
+}
+
 //------------------------------add_users_to_worklist--------------------------
 void PhaseIterGVN::add_users_to_worklist0(Node* n, Unique_Node_List& worklist) {
   for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
     worklist.push(n->fast_out(i));  // Push on worklist
   }
@@ -1592,10 +1605,19 @@
         }
       }
     }
   }
 
+  // Inline type nodes can have other inline types as users. If an input gets
+  // updated, make sure that inline type users get a chance for optimization.
+  if (use->is_InlineType()) {
+    for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) {
+      Node* u = use->fast_out(i2);
+      if (u->is_InlineType())
+        worklist.push(u);
+    }
+  }
   // If changed Cast input, notify down for Phi, Sub, and Xor - all do "uncast"
   // Patterns:
   // ConstraintCast+ -> Sub
   // ConstraintCast+ -> Phi
   // ConstraintCast+ -> Xor
@@ -1668,10 +1690,18 @@
   // of the mirror load depends on the type of 'n'. See LoadNode::Value().
   //   LoadBarrier?(LoadP(LoadP(AddP(foo:Klass, #java_mirror))))
   BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
   bool has_load_barrier_nodes = bs->has_load_barrier_nodes();
 
+  if (use_op == Op_CastP2X) {
+    for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) {
+      Node* u = use->fast_out(i2);
+      if (u->Opcode() == Op_AndX) {
+        worklist.push(u);
+      }
+    }
+  }
   if (use_op == Op_LoadP && use->bottom_type()->isa_rawptr()) {
     for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) {
       Node* u = use->fast_out(i2);
       const Type* ut = u->bottom_type();
       if (u->Opcode() == Op_LoadP && ut->isa_instptr()) {
@@ -1686,10 +1716,20 @@
         }
         worklist.push(u);
       }
     }
   }
+  // Give CallStaticJavaNode::remove_useless_allocation a chance to run
+  if (use->is_Region()) {
+    Node* c = use;
+    do {
+      c = c->unique_ctrl_out_or_null();
+    } while (c != nullptr && c->is_Region());
+    if (c != nullptr && c->is_CallStaticJava() && c->as_CallStaticJava()->uncommon_trap_request() != 0) {
+      worklist.push(c);
+    }
+  }
   if (use->Opcode() == Op_OpaqueZeroTripGuard) {
     assert(use->outcnt() <= 1, "OpaqueZeroTripGuard can't be shared");
     if (use->outcnt() == 1) {
       Node* cmp = use->unique_out();
       worklist.push(cmp);
@@ -1878,10 +1918,11 @@
 void PhaseCCP::push_more_uses(Unique_Node_List& worklist, Node* parent, const Node* use) const {
   push_phis(worklist, use);
   push_catch(worklist, use);
   push_cmpu(worklist, use);
   push_counted_loop_phi(worklist, parent, use);
+  push_cast(worklist, use);
   push_loadp(worklist, use);
   push_and(worklist, parent, use);
   push_cast_ii(worklist, parent, use);
   push_opaque_zero_trip_guard(worklist, use);
 }
@@ -1939,10 +1980,22 @@
       worklist.push(phi);
     }
   }
 }
 
+void PhaseCCP::push_cast(Unique_Node_List& worklist, const Node* use) {
+  uint use_op = use->Opcode();
+  if (use_op == Op_CastP2X) {
+    for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) {
+      Node* u = use->fast_out(i2);
+      if (u->Opcode() == Op_AndX) {
+        worklist.push(u);
+      }
+    }
+  }
+}
+
 // Loading the java mirror from a Klass requires two loads and the type of the mirror load depends on the type of 'n'.
 // See LoadNode::Value().
 void PhaseCCP::push_loadp(Unique_Node_List& worklist, const Node* use) const {
   BarrierSetC2* barrier_set = BarrierSet::barrier_set()->barrier_set_c2();
   bool has_load_barrier_nodes = barrier_set->has_load_barrier_nodes();
diff a/src/hotspot/share/opto/phaseX.hpp b/src/hotspot/share/opto/phaseX.hpp
--- a/src/hotspot/share/opto/phaseX.hpp
+++ b/src/hotspot/share/opto/phaseX.hpp
@@ -481,11 +481,11 @@
     }
   }
 
   // Idealize new Node 'n' with respect to its inputs and its value
   virtual Node *transform( Node *a_node );
-  virtual void record_for_igvn(Node *n) { }
+  virtual void record_for_igvn(Node *n) { _worklist.push(n); }
 
   // Iterative worklist. Reference to "C->igvn_worklist()".
   Unique_Node_List &_worklist;
 
   // Given def-use info and an initial worklist, apply Node::Ideal,
@@ -537,10 +537,12 @@
     add_users_to_worklist(old);
     hash_delete(old); // Yank from hash before hacking edges
     subsume_node(old, nn);
   }
 
+  void replace_in_uses(Node* n, Node* m);
+
   // Delayed node rehash: remove a node from the hash table and rehash it during
   // next optimizing pass
   void rehash_node_delayed(Node* n) {
     hash_delete(n);
     _worklist.push(n);
@@ -619,10 +621,11 @@
   void push_more_uses(Unique_Node_List& worklist, Node* parent, const Node* use) const;
   void push_phis(Unique_Node_List& worklist, const Node* use) const;
   static void push_catch(Unique_Node_List& worklist, const Node* use);
   void push_cmpu(Unique_Node_List& worklist, const Node* use) const;
   static void push_counted_loop_phi(Unique_Node_List& worklist, Node* parent, const Node* use);
+  static void push_cast(Unique_Node_List& worklist, const Node* use);
   void push_loadp(Unique_Node_List& worklist, const Node* use) const;
   static void push_load_barrier(Unique_Node_List& worklist, const BarrierSetC2* barrier_set, const Node* use);
   void push_and(Unique_Node_List& worklist, const Node* parent, const Node* use) const;
   void push_cast_ii(Unique_Node_List& worklist, const Node* parent, const Node* use) const;
   void push_opaque_zero_trip_guard(Unique_Node_List& worklist, const Node* use) const;
diff a/src/hotspot/share/opto/phasetype.hpp b/src/hotspot/share/opto/phasetype.hpp
--- a/src/hotspot/share/opto/phasetype.hpp
+++ b/src/hotspot/share/opto/phasetype.hpp
@@ -95,10 +95,12 @@
   flags(POSTALLOC_EXPAND,               "Post-Allocation Expand") \
   flags(MACH_ANALYSIS,                  "After mach analysis") \
   flags(FINAL_CODE,                     "Final Code") \
   flags(END,                            "End") \
   flags(FAILURE,                        "Failure") \
+  flags(SPLIT_INLINES_ARRAY,            "Split inlines array") \
+  flags(SPLIT_INLINES_ARRAY_IGVN,       "IGVN after split inlines array") \
   flags(ALL,                            "All") \
   flags(DEBUG,                          "Debug")
 
 #define table_entry(name, description) PHASE_##name,
 enum CompilerPhaseType {
diff a/src/hotspot/share/opto/replacednodes.cpp b/src/hotspot/share/opto/replacednodes.cpp
--- a/src/hotspot/share/opto/replacednodes.cpp
+++ b/src/hotspot/share/opto/replacednodes.cpp
@@ -136,11 +136,10 @@
     // these uses.
     assert(stack.is_empty(), "");
     stack.push(initial, 1);
     Node* use = initial->raw_out(0);
     stack.push(use, 0);
-
     while (!stack.is_empty()) {
       assert(stack.size() > 1, "at least initial + one use");
       Node* n = stack.node();
 
       uint current_size = stack.size();
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
@@ -44,10 +44,12 @@
 #include "interpreter/linkResolver.hpp"
 #include "logging/log.hpp"
 #include "logging/logStream.hpp"
 #include "memory/oopFactory.hpp"
 #include "memory/resourceArea.hpp"
+#include "oops/flatArrayKlass.hpp"
+#include "oops/flatArrayOop.inline.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/typeArrayOop.inline.hpp"
 #include "opto/ad.hpp"
@@ -104,13 +106,13 @@
 address OptoRuntime::_vtable_must_compile_Java                    = nullptr;
 address OptoRuntime::_complete_monitor_locking_Java               = nullptr;
 address OptoRuntime::_monitor_notify_Java                         = nullptr;
 address OptoRuntime::_monitor_notifyAll_Java                      = nullptr;
 address OptoRuntime::_rethrow_Java                                = nullptr;
-
 address OptoRuntime::_slow_arraycopy_Java                         = nullptr;
 address OptoRuntime::_register_finalizer_Java                     = nullptr;
+address OptoRuntime::_load_unknown_inline                         = nullptr;
 #if INCLUDE_JVMTI
 address OptoRuntime::_notify_jvmti_vthread_start                  = nullptr;
 address OptoRuntime::_notify_jvmti_vthread_end                    = nullptr;
 address OptoRuntime::_notify_jvmti_vthread_mount                  = nullptr;
 address OptoRuntime::_notify_jvmti_vthread_unmount                = nullptr;
@@ -162,13 +164,13 @@
 #endif
   gen(env, _complete_monitor_locking_Java  , complete_monitor_enter_Type  , SharedRuntime::complete_monitor_locking_C, 0, false, false);
   gen(env, _monitor_notify_Java            , monitor_notify_Type          , monitor_notify_C                ,    0 , false, false);
   gen(env, _monitor_notifyAll_Java         , monitor_notify_Type          , monitor_notifyAll_C             ,    0 , false, false);
   gen(env, _rethrow_Java                   , rethrow_Type                 , rethrow_C                       ,    2 , true , true );
-
   gen(env, _slow_arraycopy_Java            , slow_arraycopy_Type          , SharedRuntime::slow_arraycopy_C ,    0 , false, false);
   gen(env, _register_finalizer_Java        , register_finalizer_Type      , register_finalizer              ,    0 , false, false);
+  gen(env, _load_unknown_inline            , load_unknown_inline_type     , load_unknown_inline             ,    0 , true,  false);
 
   return true;
 }
 
 #undef gen
@@ -210,11 +212,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(void, OptoRuntime::new_instance_C(Klass* klass, bool is_larval, JavaThread* current))
   JRT_BLOCK;
 #ifndef PRODUCT
   SharedRuntime::_new_instance_ctr++;         // new instance requires GC
 #endif
   assert(check_compiled_frame(current), "incorrect caller");
@@ -230,11 +232,15 @@
   }
 
   if (!HAS_PENDING_EXCEPTION) {
     // Scavenge and allocate an instance.
     Handle holder(current, klass->klass_holder()); // keep the klass alive
-    oop result = InstanceKlass::cast(klass)->allocate_instance(THREAD);
+    instanceOop result = InstanceKlass::cast(klass)->allocate_instance(THREAD);
+    if (is_larval) {
+      // Check if this is a larval buffer allocation
+      result->set_mark(result->mark().enter_larval_state());
+    }
     current->set_vm_result(result);
 
     // Pass oops back through thread local storage.  Our apparent type to Java
     // is that we return an oop, but we can block on exit from this routine and
     // a GC can trash the oop in C's return register.  The generated stub will
@@ -258,22 +264,21 @@
   assert(check_compiled_frame(current), "incorrect caller");
 
   // Scavenge and allocate an instance.
   oop result;
 
-  if (array_type->is_typeArray_klass()) {
+  if (array_type->is_flatArray_klass()) {
+    Klass* elem_type = FlatArrayKlass::cast(array_type)->element_klass();
+    result = oopFactory::new_valueArray(elem_type, len, THREAD);
+  } else if (array_type->is_typeArray_klass()) {
     // The oopFactory likes to work with the element type.
     // (We could bypass the oopFactory, since it doesn't add much value.)
     BasicType elem_type = TypeArrayKlass::cast(array_type)->element_type();
     result = oopFactory::new_typeArray(elem_type, len, THREAD);
   } else {
-    // Although the oopFactory likes to work with the elem_type,
-    // the compiler prefers the array_type, since it must already have
-    // that latter value in hand for the fast path.
     Handle holder(current, array_type->klass_holder()); // keep the array klass alive
-    Klass* elem_type = ObjArrayKlass::cast(array_type)->element_klass();
-    result = oopFactory::new_objArray(elem_type, len, THREAD);
+    result = ObjArrayKlass::cast(array_type)->allocate(len, THREAD);
   }
 
   // Pass oops back through thread local storage.  Our apparent type to Java
   // is that we return an oop, but we can block on exit from this routine and
   // a GC can trash the oop in C's return register.  The generated stub will
@@ -463,13 +468,14 @@
   JRT_BLOCK_END;
 JRT_END
 
 const TypeFunc *OptoRuntime::new_instance_Type() {
   // create input type (domain)
-  const Type **fields = TypeTuple::fields(1);
+  const Type **fields = TypeTuple::fields(2);
   fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // Klass to be allocated
-  const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+1, fields);
+  fields[TypeFunc::Parms+1] = TypeInt::BOOL;        // is_larval
+  const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+2, fields);
 
   // create result type (range)
   fields = TypeTuple::fields(1);
   fields[TypeFunc::Parms+0] = TypeRawPtr::NOTNULL; // Returned oop
 
@@ -599,11 +605,11 @@
   // create result type (range)
   fields = TypeTuple::fields(0);
 
   const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0,fields);
 
-  return TypeFunc::make(domain,range);
+  return TypeFunc::make(domain, range);
 }
 
 
 //-----------------------------------------------------------------------------
 const TypeFunc *OptoRuntime::complete_monitor_exit_Type() {
@@ -1687,11 +1693,11 @@
   // create result type (range)
   fields = TypeTuple::fields(0);
 
   const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0,fields);
 
-  return TypeFunc::make(domain,range);
+  return TypeFunc::make(domain, range);
 }
 
 #if INCLUDE_JFR
 const TypeFunc *OptoRuntime::class_id_load_barrier_Type() {
   // create input type (domain)
@@ -1720,11 +1726,11 @@
   // create result type (range)
   fields = TypeTuple::fields(0);
 
   const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0,fields);
 
-  return TypeFunc::make(domain,range);
+  return TypeFunc::make(domain, range);
 }
 
 const TypeFunc *OptoRuntime::dtrace_object_alloc_Type() {
   // create input type (domain)
   const Type **fields = TypeTuple::fields(2);
@@ -1736,11 +1742,11 @@
   // create result type (range)
   fields = TypeTuple::fields(0);
 
   const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0,fields);
 
-  return TypeFunc::make(domain,range);
+  return TypeFunc::make(domain, range);
 }
 
 
 JRT_ENTRY_NO_ASYNC(void, OptoRuntime::register_finalizer(oopDesc* obj, JavaThread* current))
   assert(oopDesc::is_oop(obj), "must be a valid oop");
@@ -1860,5 +1866,109 @@
   tempst.print(" at " INTPTR_FORMAT,  p2i(exception_pc));
   tempst.print("]");
 
   st->print_raw_cr(tempst.freeze());
 }
+
+const TypeFunc *OptoRuntime::store_inline_type_fields_Type() {
+  // create input type (domain)
+  uint total = SharedRuntime::java_return_convention_max_int + SharedRuntime::java_return_convention_max_float*2;
+  const Type **fields = TypeTuple::fields(total);
+  // We don't know the number of returned values and their
+  // types. Assume all registers available to the return convention
+  // are used.
+  fields[TypeFunc::Parms] = TypePtr::BOTTOM;
+  uint i = 1;
+  for (; i < SharedRuntime::java_return_convention_max_int; i++) {
+    fields[TypeFunc::Parms+i] = TypeInt::INT;
+  }
+  for (; i < total; i+=2) {
+    fields[TypeFunc::Parms+i] = Type::DOUBLE;
+    fields[TypeFunc::Parms+i+1] = Type::HALF;
+  }
+  const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms + total, fields);
+
+  // create result type (range)
+  fields = TypeTuple::fields(1);
+  fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL;
+
+  const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+1,fields);
+
+  return TypeFunc::make(domain, range);
+}
+
+const TypeFunc *OptoRuntime::pack_inline_type_Type() {
+  // create input type (domain)
+  uint total = 1 + SharedRuntime::java_return_convention_max_int + SharedRuntime::java_return_convention_max_float*2;
+  const Type **fields = TypeTuple::fields(total);
+  // We don't know the number of returned values and their
+  // types. Assume all registers available to the return convention
+  // are used.
+  fields[TypeFunc::Parms] = TypeRawPtr::BOTTOM;
+  fields[TypeFunc::Parms+1] = TypeRawPtr::BOTTOM;
+  uint i = 2;
+  for (; i < SharedRuntime::java_return_convention_max_int+1; i++) {
+    fields[TypeFunc::Parms+i] = TypeInt::INT;
+  }
+  for (; i < total; i+=2) {
+    fields[TypeFunc::Parms+i] = Type::DOUBLE;
+    fields[TypeFunc::Parms+i+1] = Type::HALF;
+  }
+  const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms + total, fields);
+
+  // create result type (range)
+  fields = TypeTuple::fields(1);
+  fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL;
+
+  const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+1,fields);
+
+  return TypeFunc::make(domain, range);
+}
+
+JRT_BLOCK_ENTRY(void, OptoRuntime::load_unknown_inline(flatArrayOopDesc* array, int index, JavaThread* current))
+  JRT_BLOCK;
+  flatArrayHandle vah(current, array);
+  oop buffer = flatArrayOopDesc::value_alloc_copy_from_index(vah, index, THREAD);
+  deoptimize_caller_frame(current, HAS_PENDING_EXCEPTION);
+  current->set_vm_result(buffer);
+  JRT_BLOCK_END;
+JRT_END
+
+const TypeFunc* OptoRuntime::load_unknown_inline_type() {
+  // create input type (domain)
+  const Type** fields = TypeTuple::fields(2);
+  fields[TypeFunc::Parms] = TypeOopPtr::NOTNULL;
+  fields[TypeFunc::Parms+1] = TypeInt::POS;
+
+  const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms+2, fields);
+
+  // create result type (range)
+  fields = TypeTuple::fields(1);
+  fields[TypeFunc::Parms] = TypeInstPtr::NOTNULL;
+
+  const TypeTuple* range = TypeTuple::make(TypeFunc::Parms+1, fields);
+
+  return TypeFunc::make(domain, range);
+}
+
+JRT_LEAF(void, OptoRuntime::store_unknown_inline(instanceOopDesc* buffer, flatArrayOopDesc* array, int index))
+{
+  assert(buffer != nullptr, "can't store null into flat array");
+  array->value_copy_to_index(buffer, index);
+}
+JRT_END
+
+const TypeFunc* OptoRuntime::store_unknown_inline_type() {
+  // create input type (domain)
+  const Type** fields = TypeTuple::fields(3);
+  fields[TypeFunc::Parms] = TypeInstPtr::NOTNULL;
+  fields[TypeFunc::Parms+1] = TypeOopPtr::NOTNULL;
+  fields[TypeFunc::Parms+2] = TypeInt::POS;
+
+  const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms+3, fields);
+
+  // create result type (range)
+  fields = TypeTuple::fields(0);
+  const TypeTuple* range = TypeTuple::make(TypeFunc::Parms, fields);
+
+  return TypeFunc::make(domain, range);
+}
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
@@ -133,10 +133,11 @@
   static address _monitor_notify_Java;
   static address _monitor_notifyAll_Java;
 
   static address _slow_arraycopy_Java;
   static address _register_finalizer_Java;
+  static address _load_unknown_inline;
 #if INCLUDE_JVMTI
   static address _notify_jvmti_vthread_start;
   static address _notify_jvmti_vthread_end;
   static address _notify_jvmti_vthread_mount;
   static address _notify_jvmti_vthread_unmount;
@@ -145,11 +146,11 @@
   //
   // Implementation of runtime methods
   // =================================
 
   // Allocate storage for a Java instance.
-  static void new_instance_C(Klass* instance_klass, JavaThread* current);
+  static void new_instance_C(Klass* instance_klass, bool is_larval, JavaThread* current);
 
   // Allocate storage for a objArray or typeArray
   static void new_array_C(Klass* array_klass, int len, JavaThread* current);
   static void new_array_nozero_C(Klass* array_klass, int len, JavaThread* current);
 
@@ -212,10 +213,11 @@
   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 load_unknown_inline_Java()              { return _load_unknown_inline; }
 #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; }
@@ -314,10 +316,18 @@
 
   // Dtrace support
   static const TypeFunc* dtrace_method_entry_exit_Type();
   static const TypeFunc* dtrace_object_alloc_Type();
 
+  static const TypeFunc* store_inline_type_fields_Type();
+  static const TypeFunc* pack_inline_type_Type();
+
+  static void load_unknown_inline(flatArrayOopDesc* array, int index, JavaThread* current);
+  static const TypeFunc* load_unknown_inline_type();
+  static void store_unknown_inline(instanceOopDesc* buffer, flatArrayOopDesc* array, int index);
+  static const TypeFunc* store_unknown_inline_type();
+
  private:
  static NamedCounter * volatile _named_counters;
 
  public:
  // helper function which creates a named counter labeled with the
diff a/src/hotspot/share/opto/split_if.cpp b/src/hotspot/share/opto/split_if.cpp
--- a/src/hotspot/share/opto/split_if.cpp
+++ b/src/hotspot/share/opto/split_if.cpp
@@ -24,10 +24,11 @@
 
 #include "precompiled.hpp"
 #include "memory/allocation.inline.hpp"
 #include "opto/addnode.hpp"
 #include "opto/callnode.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/loopnode.hpp"
 #include "opto/movenode.hpp"
 #include "opto/node.hpp"
 #include "opto/opaquenode.hpp"
 
@@ -622,12 +623,11 @@
         Node* m = n->out(j);
         // If m is dead, throw it away, and declare progress
         if (_loop_or_ctrl[m->_idx] == nullptr) {
           _igvn.remove_dead_node(m);
           // fall through
-        }
-        else if (m != iff && split_up(m, region, iff)) {
+        } else if (m != iff && split_up(m, region, iff)) {
           // fall through
         } else {
           continue;
         }
         // Something unpredictable changed.
diff a/src/hotspot/share/opto/stringopts.cpp b/src/hotspot/share/opto/stringopts.cpp
--- a/src/hotspot/share/opto/stringopts.cpp
+++ b/src/hotspot/share/opto/stringopts.cpp
@@ -313,41 +313,41 @@
 }
 
 
 void StringConcat::eliminate_call(CallNode* call) {
   Compile* C = _stringopts->C;
-  CallProjections projs;
-  call->extract_projections(&projs, false);
-  if (projs.fallthrough_catchproj != nullptr) {
-    C->gvn_replace_by(projs.fallthrough_catchproj, call->in(TypeFunc::Control));
+  CallProjections* projs = call->extract_projections(false);
+  if (projs->fallthrough_catchproj != nullptr) {
+    C->gvn_replace_by(projs->fallthrough_catchproj, call->in(TypeFunc::Control));
   }
-  if (projs.fallthrough_memproj != nullptr) {
-    C->gvn_replace_by(projs.fallthrough_memproj, call->in(TypeFunc::Memory));
+  if (projs->fallthrough_memproj != nullptr) {
+    C->gvn_replace_by(projs->fallthrough_memproj, call->in(TypeFunc::Memory));
   }
-  if (projs.catchall_memproj != nullptr) {
-    C->gvn_replace_by(projs.catchall_memproj, C->top());
+  if (projs->catchall_memproj != nullptr) {
+    C->gvn_replace_by(projs->catchall_memproj, C->top());
   }
-  if (projs.fallthrough_ioproj != nullptr) {
-    C->gvn_replace_by(projs.fallthrough_ioproj, call->in(TypeFunc::I_O));
+  if (projs->fallthrough_ioproj != nullptr) {
+    C->gvn_replace_by(projs->fallthrough_ioproj, call->in(TypeFunc::I_O));
   }
-  if (projs.catchall_ioproj != nullptr) {
-    C->gvn_replace_by(projs.catchall_ioproj, C->top());
+  if (projs->catchall_ioproj != nullptr) {
+    C->gvn_replace_by(projs->catchall_ioproj, C->top());
   }
-  if (projs.catchall_catchproj != nullptr) {
+  if (projs->catchall_catchproj != nullptr) {
     // EA can't cope with the partially collapsed graph this
     // creates so put it on the worklist to be collapsed later.
-    for (SimpleDUIterator i(projs.catchall_catchproj); i.has_next(); i.next()) {
+    for (SimpleDUIterator i(projs->catchall_catchproj); i.has_next(); i.next()) {
       Node *use = i.get();
       int opc = use->Opcode();
       if (opc == Op_CreateEx || opc == Op_Region) {
         _stringopts->record_dead_node(use);
       }
     }
-    C->gvn_replace_by(projs.catchall_catchproj, C->top());
+    C->gvn_replace_by(projs->catchall_catchproj, C->top());
   }
-  if (projs.resproj != nullptr) {
-    C->gvn_replace_by(projs.resproj, C->top());
+  if (projs->resproj[0] != nullptr) {
+    assert(projs->nb_resproj == 1, "unexpected number of results");
+    C->gvn_replace_by(projs->resproj[0], C->top());
   }
   C->gvn_replace_by(call, C->top());
 }
 
 void StringConcat::eliminate_initialize(InitializeNode* init) {
diff a/src/hotspot/share/opto/subnode.cpp b/src/hotspot/share/opto/subnode.cpp
--- a/src/hotspot/share/opto/subnode.cpp
+++ b/src/hotspot/share/opto/subnode.cpp
@@ -28,10 +28,11 @@
 #include "gc/shared/c2/barrierSetC2.hpp"
 #include "memory/allocation.inline.hpp"
 #include "opto/addnode.hpp"
 #include "opto/callnode.hpp"
 #include "opto/cfgnode.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/loopnode.hpp"
 #include "opto/matcher.hpp"
 #include "opto/movenode.hpp"
 #include "opto/mulnode.hpp"
 #include "opto/opaquenode.hpp"
@@ -882,21 +883,53 @@
     }
   }
   return nullptr;                  // No change
 }
 
-Node *CmpLNode::Ideal( PhaseGVN *phase, bool can_reshape ) {
+//------------------------------Ideal------------------------------------------
+Node* CmpLNode::Ideal(PhaseGVN* phase, bool can_reshape) {
+  Node* a = nullptr;
+  Node* b = nullptr;
+  if (is_double_null_check(phase, a, b) && (phase->type(a)->is_zero_type() || phase->type(b)->is_zero_type())) {
+    // Degraded to a simple null check, use old acmp
+    return new CmpPNode(a, b);
+  }
   const TypeLong *t2 = phase->type(in(2))->isa_long();
   if (Opcode() == Op_CmpL && in(1)->Opcode() == Op_ConvI2L && t2 && t2->is_con()) {
     const jlong con = t2->get_con();
     if (con >= min_jint && con <= max_jint) {
       return new CmpINode(in(1)->in(1), phase->intcon((jint)con));
     }
   }
   return nullptr;
 }
 
+// Match double null check emitted by Compile::optimize_acmp()
+bool CmpLNode::is_double_null_check(PhaseGVN* phase, Node*& a, Node*& b) const {
+  if (in(1)->Opcode() == Op_OrL &&
+      in(1)->in(1)->Opcode() == Op_CastP2X &&
+      in(1)->in(2)->Opcode() == Op_CastP2X &&
+      in(2)->bottom_type()->is_zero_type()) {
+    assert(EnableValhalla, "unexpected double null check");
+    a = in(1)->in(1)->in(1);
+    b = in(1)->in(2)->in(1);
+    return true;
+  }
+  return false;
+}
+
+//------------------------------Value------------------------------------------
+const Type* CmpLNode::Value(PhaseGVN* phase) const {
+  Node* a = nullptr;
+  Node* b = nullptr;
+  if (is_double_null_check(phase, a, b) && (!phase->type(a)->maybe_null() || !phase->type(b)->maybe_null())) {
+    // One operand is never nullptr, emit constant false
+    return TypeInt::CC_GT;
+  }
+  return SubNode::Value(phase);
+}
+
 //=============================================================================
 // Simplify a CmpL (compare 2 longs ) node, based on local information.
 // If both inputs are constants, compare them.
 const Type *CmpLNode::sub( const Type *t1, const Type *t2 ) const {
   const TypeLong *r0 = t1->is_long(); // Handy access
@@ -1018,11 +1051,26 @@
       unrelated_classes = xklass1;
     } else if ((p0 && !p0->maybe_java_subtype_of(p1)) ||
                (k0 && !k0->maybe_java_subtype_of(k1))) {
       unrelated_classes = xklass0;
     }
-
+    if (!unrelated_classes) {
+      // Handle inline type arrays
+      if ((r0->flat_in_array() && r1->not_flat_in_array()) ||
+          (r1->flat_in_array() && r0->not_flat_in_array())) {
+        // One type is in flat arrays but the other type is not. Must be unrelated.
+        unrelated_classes = true;
+      } else if ((r0->is_not_flat() && r1->is_flat()) ||
+                 (r1->is_not_flat() && r0->is_flat())) {
+        // One type is a non-flat array and the other type is a flat array. Must be unrelated.
+        unrelated_classes = true;
+      } else if ((r0->is_not_null_free() && r1->is_null_free()) ||
+                 (r1->is_not_null_free() && r0->is_null_free())) {
+        // One type is a nullable array and the other type is a null-free array. Must be unrelated.
+        unrelated_classes = true;
+      }
+    }
     if (unrelated_classes) {
       // The oops classes are known to be unrelated. If the joined PTRs of
       // two oops is not Null and not Bottom, then we are sure that one
       // of the two oops is non-null, and the comparison will always fail.
       TypePtr::PTR jp = r0->join_ptr(r1->_ptr);
@@ -1103,11 +1151,18 @@
 //
 // Also check for the case of comparing an unknown klass loaded from the primary
 // super-type array vs a known klass with no subtypes.  This amounts to
 // checking to see an unknown klass subtypes a known klass with no subtypes;
 // this only happens on an exact match.  We can shorten this test by 1 load.
-Node *CmpPNode::Ideal( PhaseGVN *phase, bool can_reshape ) {
+Node* CmpPNode::Ideal(PhaseGVN *phase, bool can_reshape) {
+  // TODO 8284443 in(1) could be cast?
+  if (in(1)->is_InlineType() && phase->type(in(2))->is_zero_type()) {
+    // Null checking a scalarized but nullable inline type. Check the IsInit
+    // input instead of the oop input to avoid keeping buffer allocations alive.
+    return new CmpINode(in(1)->as_InlineType()->get_is_init(), phase->intcon(0));
+  }
+
   // Normalize comparisons between Java mirrors into comparisons of the low-
   // level klass, where a dependent load could be shortened.
   //
   // The new pattern has a nice effect of matching the same pattern used in the
   // fast path of instanceof/checkcast/Class.isInstance(), which allows
@@ -1175,10 +1230,19 @@
 
   // Verify that we understand the situation
   if (con2 != (intptr_t) superklass->super_check_offset())
     return nullptr;                // Might be element-klass loading from array klass
 
+  // TODO 8325106 Fix comment
+  // Do not fold the subtype check to an array klass pointer comparison for [V? arrays.
+  // [QMyValue is a subtype of [LMyValue but the klass for [QMyValue is not equal to
+  // the klass for [LMyValue. Do not bypass the klass load from the primary supertype array.
+  if (superklass->is_obj_array_klass() && !superklass->as_array_klass()->is_elem_null_free() &&
+      superklass->as_array_klass()->element_klass()->is_inlinetype()) {
+    return nullptr;
+  }
+
   // If 'superklass' has no subklasses and is not an interface, then we are
   // assured that the only input which will pass the type check is
   // 'superklass' itself.
   //
   // We could be more liberal here, and allow the optimization on interfaces
@@ -1318,10 +1382,47 @@
     // Testing value required the precision of a double
   }
   return nullptr;                  // No change
 }
 
+//=============================================================================
+//------------------------------Value------------------------------------------
+const Type* FlatArrayCheckNode::Value(PhaseGVN* phase) const {
+  bool all_not_flat = true;
+  for (uint i = ArrayOrKlass; i < req(); ++i) {
+    const Type* t = phase->type(in(i));
+    if (t == Type::TOP) {
+      return Type::TOP;
+    }
+    if (t->is_ptr()->is_flat()) {
+      // One of the input arrays is flat, check always passes
+      return TypeInt::CC_EQ;
+    } else if (!t->is_ptr()->is_not_flat()) {
+      // One of the input arrays might be flat
+      all_not_flat = false;
+    }
+  }
+  if (all_not_flat) {
+    // None of the input arrays can be flat, check always fails
+    return TypeInt::CC_GT;
+  }
+  return TypeInt::CC;
+}
+
+//------------------------------Ideal------------------------------------------
+Node* FlatArrayCheckNode::Ideal(PhaseGVN* phase, bool can_reshape) {
+  bool changed = false;
+  // Remove inputs that are known to be non-flat
+  for (uint i = ArrayOrKlass; i < req(); ++i) {
+    const Type* t = phase->type(in(i));
+    if (t->isa_ptr() && t->is_ptr()->is_not_flat()) {
+      del_req(i--);
+      changed = true;
+    }
+  }
+  return changed ? this : nullptr;
+}
 
 //=============================================================================
 //------------------------------cc2logical-------------------------------------
 // Convert a condition code type to a logical type
 const Type *BoolTest::cc2logical( const Type *CC ) const {
diff a/src/hotspot/share/opto/subnode.hpp b/src/hotspot/share/opto/subnode.hpp
--- a/src/hotspot/share/opto/subnode.hpp
+++ b/src/hotspot/share/opto/subnode.hpp
@@ -203,12 +203,14 @@
 // Compare 2 long values, returning condition codes (-1, 0 or 1).
 class CmpLNode : public CmpNode {
 public:
   CmpLNode( Node *in1, Node *in2 ) : CmpNode(in1,in2) {}
   virtual int    Opcode() const;
-  virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
+  virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
+  virtual const Type* Value(PhaseGVN* phase) const;
   virtual const Type *sub( const Type *, const Type * ) const;
+  bool is_double_null_check(PhaseGVN* phase, Node*& a, Node*& b) const;
 };
 
 //------------------------------CmpULNode---------------------------------------
 // Compare 2 unsigned long values, returning condition codes (-1, 0 or 1).
 class CmpULNode : public CmpNode {
@@ -295,10 +297,30 @@
   }
   virtual int Opcode() const;
   virtual uint ideal_reg() const { return Op_RegI; }
 };
 
+//--------------------------FlatArrayCheckNode---------------------------------
+// Returns true if one of the input array objects or array klass ptrs (there
+// can be multiple) is flat.
+class FlatArrayCheckNode : public CmpNode {
+public:
+  enum {
+    Control,
+    Memory,
+    ArrayOrKlass
+  };
+  FlatArrayCheckNode(Compile* C, Node* mem, Node* array_or_klass) : CmpNode(mem, array_or_klass) {
+    init_class_id(Class_FlatArrayCheck);
+    init_flags(Flag_is_macro);
+    C->add_macro_node(this);
+  }
+  virtual int Opcode() const;
+  virtual const Type* sub(const Type*, const Type*) const { ShouldNotReachHere(); return nullptr; }
+  const Type* Value(PhaseGVN* phase) const;
+  virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
+};
 
 //------------------------------BoolTest---------------------------------------
 // Convert condition codes to a boolean test value (0 or -1).
 // We pick the values as 3 bits; the low order 2 bits we compare against the
 // condition codes, the high bit flips the sense of the result.
diff a/src/hotspot/share/opto/subtypenode.cpp b/src/hotspot/share/opto/subtypenode.cpp
--- a/src/hotspot/share/opto/subtypenode.cpp
+++ b/src/hotspot/share/opto/subtypenode.cpp
@@ -45,10 +45,31 @@
       Compile::current()->dependencies()->assert_leaf_type(superklass);
       return TypeInt::CC_GT;
     }
   }
 
+  // FIXME: shouldn't this be encoded in helper methods of the type system (maybe_java_subtype_of() etc.?)
+  // Similar to logic in CmpPNode::sub()
+  bool unrelated_classes = false;
+  // Handle inline type arrays
+  if (subk->flat_in_array() && superk->not_flat_in_array()) {
+    // The subtype is in flat arrays and the supertype is not in flat arrays. Must be unrelated.
+    unrelated_classes = true;
+  } else if (subk->is_not_flat() && superk->is_flat()) {
+    // The subtype is a non-flat array and the supertype is a flat array. Must be unrelated.
+    unrelated_classes = true;
+  } else if (subk->is_not_null_free() && superk->is_null_free()) {
+    // The subtype is a nullable array and the supertype is null-free array. Must be unrelated.
+    unrelated_classes = true;
+  }
+  if (unrelated_classes) {
+    TypePtr::PTR jp = sub_t->is_ptr()->join_ptr(super_t->is_ptr()->_ptr);
+    if (jp != TypePtr::Null && jp != TypePtr::BotPTR) {
+      return TypeInt::CC_GT;
+    }
+  }
+
   if (subk != nullptr) {
     switch (Compile::current()->static_subtype_check(superk, subk, false)) {
       case Compile::SSC_always_false:
         return TypeInt::CC_GT;
       case Compile::SSC_always_true:
@@ -109,11 +130,12 @@
     return this;
   }
 
   // Verify that optimizing the subtype check to a simple code pattern
   // when possible would not constant fold better
-  assert(verify(phase), "missing Value() optimization");
+  // TODO 8325106
+  // assert(verify(phase), "missing Value() optimization");
 
   return nullptr;
 }
 
 #ifdef ASSERT
@@ -242,6 +264,6 @@
     st->print(" profiled at: ");
     _method->print_short_name(st);
     st->print(":%d", _bci);
   }
 }
-#endif
+#endif
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
@@ -21,10 +21,13 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "ci/ciFlatArrayKlass.hpp"
+#include "ci/ciField.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "ci/ciMethodData.hpp"
 #include "ci/ciTypeFlow.hpp"
 #include "classfile/javaClasses.hpp"
 #include "classfile/symbolTable.hpp"
 #include "compiler/compileLog.hpp"
@@ -47,10 +50,55 @@
 
 // Optimization - Graph Style
 
 // Dictionary of types shared among compilations.
 Dict* Type::_shared_type_dict = nullptr;
+const Type::Offset Type::Offset::top(Type::OffsetTop);
+const Type::Offset Type::Offset::bottom(Type::OffsetBot);
+
+const Type::Offset Type::Offset::meet(const Type::Offset other) const {
+  // Either is 'TOP' offset?  Return the other offset!
+  if (_offset == OffsetTop) return other;
+  if (other._offset == OffsetTop) return *this;
+  // If either is different, return 'BOTTOM' offset
+  if (_offset != other._offset) return bottom;
+  return Offset(_offset);
+}
+
+const Type::Offset Type::Offset::dual() const {
+  if (_offset == OffsetTop) return bottom;// Map 'TOP' into 'BOTTOM'
+  if (_offset == OffsetBot) return top;// Map 'BOTTOM' into 'TOP'
+  return Offset(_offset);               // Map everything else into self
+}
+
+const Type::Offset Type::Offset::add(intptr_t offset) const {
+  // Adding to 'TOP' offset?  Return 'TOP'!
+  if (_offset == OffsetTop || offset == OffsetTop) return top;
+  // Adding to 'BOTTOM' offset?  Return 'BOTTOM'!
+  if (_offset == OffsetBot || offset == OffsetBot) return bottom;
+  // Addition overflows or "accidentally" equals to OffsetTop? Return 'BOTTOM'!
+  offset += (intptr_t)_offset;
+  if (offset != (int)offset || offset == OffsetTop) return bottom;
+
+  // assert( _offset >= 0 && _offset+offset >= 0, "" );
+  // It is possible to construct a negative offset during PhaseCCP
+
+  return Offset((int)offset);        // Sum valid offsets
+}
+
+void Type::Offset::dump2(outputStream *st) const {
+  if (_offset == 0) {
+    return;
+  } else if (_offset == OffsetTop) {
+    st->print("+top");
+  }
+  else if (_offset == OffsetBot) {
+    st->print("+bot");
+  } else if (_offset) {
+    st->print("+%d", _offset);
+  }
+}
 
 // Array which maps compiler types to Basic Types
 const Type::TypeInfo Type::_type_info[Type::lastype] = {
   { Bad,             T_ILLEGAL,    "bad",           false, Node::NotAMachineReg, relocInfo::none          },  // Bad
   { Control,         T_ILLEGAL,    "control",       false, 0,                    relocInfo::none          },  // Control
@@ -221,10 +269,13 @@
 
   case T_ADDRESS:
     assert(type->is_return_address(), "");
     return TypeRawPtr::make((address)(intptr_t)type->as_return_address()->bci());
 
+  case T_OBJECT:
+    return Type::get_const_type(type->unwrap())->join_speculative(type->is_null_free() ? TypePtr::NOTNULL : TypePtr::BOTTOM);
+
   default:
     // make sure we did not mix up the cases:
     assert(type != ciTypeFlow::StateVector::bottom_type(), "");
     assert(type != ciTypeFlow::StateVector::top_type(), "");
     assert(type != ciTypeFlow::StateVector::null_type(), "");
@@ -527,13 +578,13 @@
   const Type **floop =(const Type**)shared_type_arena->AmallocWords(2*sizeof(Type*));
   floop[0] = Type::CONTROL;
   floop[1] = TypeInt::INT;
   TypeTuple::LOOPBODY = TypeTuple::make( 2, floop );
 
-  TypePtr::NULL_PTR= TypePtr::make(AnyPtr, TypePtr::Null, 0);
-  TypePtr::NOTNULL = TypePtr::make(AnyPtr, TypePtr::NotNull, OffsetBot);
-  TypePtr::BOTTOM  = TypePtr::make(AnyPtr, TypePtr::BotPTR, OffsetBot);
+  TypePtr::NULL_PTR= TypePtr::make(AnyPtr, TypePtr::Null, Offset(0));
+  TypePtr::NOTNULL = TypePtr::make(AnyPtr, TypePtr::NotNull, Offset::bottom);
+  TypePtr::BOTTOM  = TypePtr::make(AnyPtr, TypePtr::BotPTR, Offset::bottom);
 
   TypeRawPtr::BOTTOM = TypeRawPtr::make( TypePtr::BotPTR );
   TypeRawPtr::NOTNULL= TypeRawPtr::make( TypePtr::NotNull );
 
   const Type **fmembar = TypeTuple::fields(0);
@@ -546,16 +597,16 @@
 
   TypeInstPtr::NOTNULL = TypeInstPtr::make(TypePtr::NotNull, current->env()->Object_klass());
   TypeInstPtr::BOTTOM  = TypeInstPtr::make(TypePtr::BotPTR,  current->env()->Object_klass());
   TypeInstPtr::MIRROR  = TypeInstPtr::make(TypePtr::NotNull, current->env()->Class_klass());
   TypeInstPtr::MARK    = TypeInstPtr::make(TypePtr::BotPTR,  current->env()->Object_klass(),
-                                           false, 0, oopDesc::mark_offset_in_bytes());
+                                           false, 0, Offset(oopDesc::mark_offset_in_bytes()));
   TypeInstPtr::KLASS   = TypeInstPtr::make(TypePtr::BotPTR,  current->env()->Object_klass(),
-                                           false, 0, oopDesc::klass_offset_in_bytes());
-  TypeOopPtr::BOTTOM  = TypeOopPtr::make(TypePtr::BotPTR, OffsetBot, TypeOopPtr::InstanceBot);
+                                           false, 0, Offset(oopDesc::klass_offset_in_bytes()));
+  TypeOopPtr::BOTTOM  = TypeOopPtr::make(TypePtr::BotPTR, Offset::bottom, TypeOopPtr::InstanceBot);
 
-  TypeMetadataPtr::BOTTOM = TypeMetadataPtr::make(TypePtr::BotPTR, nullptr, OffsetBot);
+  TypeMetadataPtr::BOTTOM = TypeMetadataPtr::make(TypePtr::BotPTR, nullptr, Offset::bottom);
 
   TypeNarrowOop::NULL_PTR = TypeNarrowOop::make( TypePtr::NULL_PTR );
   TypeNarrowOop::BOTTOM   = TypeNarrowOop::make( TypeInstPtr::BOTTOM );
 
   TypeNarrowKlass::NULL_PTR = TypeNarrowKlass::make( TypePtr::NULL_PTR );
@@ -574,47 +625,49 @@
   array_interfaces.push(current->env()->Cloneable_klass());
   array_interfaces.push(current->env()->Serializable_klass());
   TypeAryPtr::_array_interfaces = TypeInterfaces::make(&array_interfaces);
   TypeAryKlassPtr::_array_interfaces = TypeAryPtr::_array_interfaces;
 
-  TypeAryPtr::RANGE   = TypeAryPtr::make( TypePtr::BotPTR, TypeAry::make(Type::BOTTOM,TypeInt::POS), nullptr /* current->env()->Object_klass() */, false, arrayOopDesc::length_offset_in_bytes());
+  TypeAryPtr::RANGE   = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(Type::BOTTOM,TypeInt::POS), nullptr /* current->env()->Object_klass() */, false, Offset(arrayOopDesc::length_offset_in_bytes()));
 
-  TypeAryPtr::NARROWOOPS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeNarrowOop::BOTTOM, TypeInt::POS), nullptr /*ciArrayKlass::make(o)*/,  false,  Type::OffsetBot);
+  TypeAryPtr::NARROWOOPS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeNarrowOop::BOTTOM, TypeInt::POS), nullptr /*ciArrayKlass::make(o)*/,  false,  Offset::bottom);
 
 #ifdef _LP64
   if (UseCompressedOops) {
     assert(TypeAryPtr::NARROWOOPS->is_ptr_to_narrowoop(), "array of narrow oops must be ptr to narrow oop");
     TypeAryPtr::OOPS  = TypeAryPtr::NARROWOOPS;
   } else
 #endif
   {
     // There is no shared klass for Object[].  See note in TypeAryPtr::klass().
-    TypeAryPtr::OOPS  = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInstPtr::BOTTOM,TypeInt::POS), nullptr /*ciArrayKlass::make(o)*/,  false,  Type::OffsetBot);
+    TypeAryPtr::OOPS  = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInstPtr::BOTTOM,TypeInt::POS), nullptr /*ciArrayKlass::make(o)*/,  false,  Offset::bottom);
   }
-  TypeAryPtr::BYTES   = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::BYTE      ,TypeInt::POS), ciTypeArrayKlass::make(T_BYTE),   true,  Type::OffsetBot);
-  TypeAryPtr::SHORTS  = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::SHORT     ,TypeInt::POS), ciTypeArrayKlass::make(T_SHORT),  true,  Type::OffsetBot);
-  TypeAryPtr::CHARS   = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::CHAR      ,TypeInt::POS), ciTypeArrayKlass::make(T_CHAR),   true,  Type::OffsetBot);
-  TypeAryPtr::INTS    = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::INT       ,TypeInt::POS), ciTypeArrayKlass::make(T_INT),    true,  Type::OffsetBot);
-  TypeAryPtr::LONGS   = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeLong::LONG     ,TypeInt::POS), ciTypeArrayKlass::make(T_LONG),   true,  Type::OffsetBot);
-  TypeAryPtr::FLOATS  = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(Type::FLOAT        ,TypeInt::POS), ciTypeArrayKlass::make(T_FLOAT),  true,  Type::OffsetBot);
-  TypeAryPtr::DOUBLES = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(Type::DOUBLE       ,TypeInt::POS), ciTypeArrayKlass::make(T_DOUBLE), true,  Type::OffsetBot);
+  TypeAryPtr::BYTES   = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::BYTE      ,TypeInt::POS), ciTypeArrayKlass::make(T_BYTE),   true,  Offset::bottom);
+  TypeAryPtr::SHORTS  = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::SHORT     ,TypeInt::POS), ciTypeArrayKlass::make(T_SHORT),  true,  Offset::bottom);
+  TypeAryPtr::CHARS   = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::CHAR      ,TypeInt::POS), ciTypeArrayKlass::make(T_CHAR),   true,  Offset::bottom);
+  TypeAryPtr::INTS    = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::INT       ,TypeInt::POS), ciTypeArrayKlass::make(T_INT),    true,  Offset::bottom);
+  TypeAryPtr::LONGS   = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeLong::LONG     ,TypeInt::POS), ciTypeArrayKlass::make(T_LONG),   true,  Offset::bottom);
+  TypeAryPtr::FLOATS  = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(Type::FLOAT        ,TypeInt::POS), ciTypeArrayKlass::make(T_FLOAT),  true,  Offset::bottom);
+  TypeAryPtr::DOUBLES = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(Type::DOUBLE       ,TypeInt::POS), ciTypeArrayKlass::make(T_DOUBLE), true,  Offset::bottom);
+  TypeAryPtr::INLINES = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInstPtr::BOTTOM,TypeInt::POS, /* stable= */ false, /* flat= */ true), nullptr, false, Offset::bottom);
 
   // Nobody should ask _array_body_type[T_NARROWOOP]. Use null as assert.
   TypeAryPtr::_array_body_type[T_NARROWOOP] = nullptr;
   TypeAryPtr::_array_body_type[T_OBJECT]  = TypeAryPtr::OOPS;
+  TypeAryPtr::_array_body_type[T_PRIMITIVE_OBJECT] = TypeAryPtr::OOPS;
   TypeAryPtr::_array_body_type[T_ARRAY]   = TypeAryPtr::OOPS; // arrays are stored in oop arrays
   TypeAryPtr::_array_body_type[T_BYTE]    = TypeAryPtr::BYTES;
   TypeAryPtr::_array_body_type[T_BOOLEAN] = TypeAryPtr::BYTES;  // boolean[] is a byte array
   TypeAryPtr::_array_body_type[T_SHORT]   = TypeAryPtr::SHORTS;
   TypeAryPtr::_array_body_type[T_CHAR]    = TypeAryPtr::CHARS;
   TypeAryPtr::_array_body_type[T_INT]     = TypeAryPtr::INTS;
   TypeAryPtr::_array_body_type[T_LONG]    = TypeAryPtr::LONGS;
   TypeAryPtr::_array_body_type[T_FLOAT]   = TypeAryPtr::FLOATS;
   TypeAryPtr::_array_body_type[T_DOUBLE]  = TypeAryPtr::DOUBLES;
 
-  TypeInstKlassPtr::OBJECT = TypeInstKlassPtr::make(TypePtr::NotNull, current->env()->Object_klass(), 0);
-  TypeInstKlassPtr::OBJECT_OR_NULL = TypeInstKlassPtr::make(TypePtr::BotPTR, current->env()->Object_klass(), 0);
+  TypeInstKlassPtr::OBJECT = TypeInstKlassPtr::make(TypePtr::NotNull, current->env()->Object_klass(), Offset(0));
+  TypeInstKlassPtr::OBJECT_OR_NULL = TypeInstKlassPtr::make(TypePtr::BotPTR, current->env()->Object_klass(), Offset(0));
 
   const Type **fi2c = TypeTuple::fields(2);
   fi2c[TypeFunc::Parms+0] = TypeInstPtr::BOTTOM; // Method*
   fi2c[TypeFunc::Parms+1] = TypeRawPtr::BOTTOM; // argument pointer
   TypeTuple::START_I2C = TypeTuple::make(TypeFunc::Parms+2, fi2c);
@@ -649,10 +702,11 @@
   _const_basic_type[T_LONG]        = TypeLong::LONG;
   _const_basic_type[T_FLOAT]       = Type::FLOAT;
   _const_basic_type[T_DOUBLE]      = Type::DOUBLE;
   _const_basic_type[T_OBJECT]      = TypeInstPtr::BOTTOM;
   _const_basic_type[T_ARRAY]       = TypeInstPtr::BOTTOM; // there is no separate bottom for arrays
+  _const_basic_type[T_PRIMITIVE_OBJECT] = TypeInstPtr::BOTTOM;
   _const_basic_type[T_VOID]        = TypePtr::NULL_PTR;   // reflection represents void this way
   _const_basic_type[T_ADDRESS]     = TypeRawPtr::BOTTOM;  // both interpreter return addresses & random raw ptrs
   _const_basic_type[T_CONFLICT]    = Type::BOTTOM;        // why not?
 
   _zero_type[T_NARROWOOP]   = TypeNarrowOop::NULL_PTR;
@@ -665,10 +719,11 @@
   _zero_type[T_LONG]        = TypeLong::ZERO;
   _zero_type[T_FLOAT]       = TypeF::ZERO;
   _zero_type[T_DOUBLE]      = TypeD::ZERO;
   _zero_type[T_OBJECT]      = TypePtr::NULL_PTR;
   _zero_type[T_ARRAY]       = TypePtr::NULL_PTR; // null array is null oop
+  _zero_type[T_PRIMITIVE_OBJECT] = TypePtr::NULL_PTR;
   _zero_type[T_ADDRESS]     = TypePtr::NULL_PTR; // raw pointers use the same null
   _zero_type[T_VOID]        = Type::TOP;         // the only void value is no value at all
 
   // get_zero_type() should not happen for T_CONFLICT
   _zero_type[T_CONFLICT]= nullptr;
@@ -935,10 +990,13 @@
 };
 
 void Type::check_symmetrical(const Type* t, const Type* mt, const VerifyMeet& verify) const {
   Compile* C = Compile::current();
   const Type* mt2 = verify.meet(t, this);
+
+  // Verify that:
+  //      this meet t == t meet this
   if (mt != mt2) {
     tty->print_cr("=== Meet Not Commutative ===");
     tty->print("t           = ");   t->dump(); tty->cr();
     tty->print("this        = ");      dump(); tty->cr();
     tty->print("t meet this = "); mt2->dump(); tty->cr();
@@ -951,10 +1009,16 @@
 
   // Interface meet Oop is Not Symmetric:
   // Interface:AnyNull meet Oop:AnyNull == Interface:AnyNull
   // Interface:NotNull meet Oop:NotNull == java/lang/Object:NotNull
 
+  // Verify that:
+  //      !(t meet this)  meet !t ==
+  //      (!t join !this) meet !t == !t
+  // and
+  //      !(t meet this)  meet !this ==
+  //      (!t join !this) meet !this == !this
   if (t2t != t->_dual || t2this != this->_dual) {
     tty->print_cr("=== Meet Not Symmetric ===");
     tty->print("t   =                   ");              t->dump(); tty->cr();
     tty->print("this=                   ");                 dump(); tty->cr();
     tty->print("mt=(t meet this)=       ");             mt->dump(); tty->cr();
@@ -2121,15 +2185,33 @@
 const TypeTuple *TypeTuple::INT_PAIR;
 const TypeTuple *TypeTuple::LONG_PAIR;
 const TypeTuple *TypeTuple::INT_CC_PAIR;
 const TypeTuple *TypeTuple::LONG_CC_PAIR;
 
+static void collect_inline_fields(ciInlineKlass* vk, const Type** field_array, uint& pos) {
+  for (int j = 0; j < vk->nof_nonstatic_fields(); j++) {
+    ciField* field = vk->nonstatic_field_at(j);
+    // TODO 8325106 The field could be null free, right? Shouldn't we set the type to null-free here?
+    BasicType bt = field->type()->basic_type();
+    const Type* ft = Type::get_const_type(field->type());
+    field_array[pos++] = ft;
+    if (type2size[bt] == 2) {
+      field_array[pos++] = Type::HALF;
+    }
+  }
+}
+
 //------------------------------make-------------------------------------------
 // Make a TypeTuple from the range of a method signature
-const TypeTuple *TypeTuple::make_range(ciSignature* sig, InterfaceHandling interface_handling) {
+const TypeTuple *TypeTuple::make_range(ciSignature* sig, InterfaceHandling interface_handling, bool ret_vt_fields) {
   ciType* return_type = sig->return_type();
   uint arg_cnt = return_type->size();
+  if (ret_vt_fields) {
+    arg_cnt = return_type->as_inline_klass()->inline_arg_slots() + 1;
+    // InlineTypeNode::IsInit field used for null checking
+    arg_cnt++;
+  }
   const Type **field_array = fields(arg_cnt);
   switch (return_type->basic_type()) {
   case T_LONG:
     field_array[TypeFunc::Parms]   = TypeLong::LONG;
     field_array[TypeFunc::Parms+1] = Type::HALF;
@@ -2137,10 +2219,21 @@
   case T_DOUBLE:
     field_array[TypeFunc::Parms]   = Type::DOUBLE;
     field_array[TypeFunc::Parms+1] = Type::HALF;
     break;
   case T_OBJECT:
+    if (return_type->is_inlinetype() && ret_vt_fields) {
+      uint pos = TypeFunc::Parms;
+      field_array[pos++] = get_const_type(return_type); // Oop might be null when returning as fields
+      collect_inline_fields(return_type->as_inline_klass(), field_array, pos);
+      // InlineTypeNode::IsInit field used for null checking
+      field_array[pos++] = get_const_basic_type(T_BOOLEAN);
+      break;
+    } else {
+      field_array[TypeFunc::Parms] = get_const_type(return_type, interface_handling)->join_speculative(TypePtr::BOTTOM);
+    }
+    break;
   case T_ARRAY:
   case T_BOOLEAN:
   case T_CHAR:
   case T_FLOAT:
   case T_BYTE:
@@ -2155,38 +2248,55 @@
   }
   return (TypeTuple*)(new TypeTuple(TypeFunc::Parms + arg_cnt, field_array))->hashcons();
 }
 
 // Make a TypeTuple from the domain of a method signature
-const TypeTuple *TypeTuple::make_domain(ciInstanceKlass* recv, ciSignature* sig, InterfaceHandling interface_handling) {
-  uint arg_cnt = sig->size();
+const TypeTuple *TypeTuple::make_domain(ciMethod* method, InterfaceHandling interface_handling, bool vt_fields_as_args) {
+  ciSignature* sig = method->signature();
+  uint arg_cnt = sig->size() + (method->is_static() ? 0 : 1);
+  if (vt_fields_as_args) {
+    arg_cnt = 0;
+    assert(method->get_sig_cc() != nullptr, "Should have scalarized signature");
+    for (ExtendedSignature sig_cc = ExtendedSignature(method->get_sig_cc(), SigEntryFilter()); !sig_cc.at_end(); ++sig_cc) {
+      arg_cnt += type2size[(*sig_cc)._bt];
+    }
+  }
 
   uint pos = TypeFunc::Parms;
-  const Type **field_array;
-  if (recv != nullptr) {
-    arg_cnt++;
-    field_array = fields(arg_cnt);
-    // Use get_const_type here because it respects UseUniqueSubclasses:
-    field_array[pos++] = get_const_type(recv, interface_handling)->join_speculative(TypePtr::NOTNULL);
-  } else {
-    field_array = fields(arg_cnt);
+  const Type** field_array = fields(arg_cnt);
+  if (!method->is_static()) {
+    ciInstanceKlass* recv = method->holder();
+    if (vt_fields_as_args && recv->is_inlinetype() && recv->as_inline_klass()->can_be_passed_as_fields() && method->is_scalarized_arg(0)) {
+      collect_inline_fields(recv->as_inline_klass(), field_array, pos);
+    } else {
+      field_array[pos++] = get_const_type(recv, interface_handling)->join_speculative(TypePtr::NOTNULL);
+    }
   }
 
   int i = 0;
   while (pos < TypeFunc::Parms + arg_cnt) {
     ciType* type = sig->type_at(i);
+    BasicType bt = type->basic_type();
 
-    switch (type->basic_type()) {
+    switch (bt) {
     case T_LONG:
       field_array[pos++] = TypeLong::LONG;
       field_array[pos++] = Type::HALF;
       break;
     case T_DOUBLE:
       field_array[pos++] = Type::DOUBLE;
       field_array[pos++] = Type::HALF;
       break;
     case T_OBJECT:
+      if (type->is_inlinetype() && vt_fields_as_args && method->is_scalarized_arg(i + (method->is_static() ? 0 : 1))) {
+        // InlineTypeNode::IsInit field used for null checking
+        field_array[pos++] = get_const_basic_type(T_BOOLEAN);
+        collect_inline_fields(type->as_inline_klass(), field_array, pos);
+      } else {
+        field_array[pos++] = get_const_type(type, interface_handling);
+      }
+      break;
     case T_ARRAY:
     case T_FLOAT:
     case T_INT:
       field_array[pos++] = get_const_type(type, interface_handling);
       break;
@@ -2199,10 +2309,11 @@
     default:
       ShouldNotReachHere();
     }
     i++;
   }
+  assert(pos == TypeFunc::Parms + arg_cnt, "wrong number of arguments");
 
   return (TypeTuple*)(new TypeTuple(TypeFunc::Parms + arg_cnt, field_array))->hashcons();
 }
 
 const TypeTuple *TypeTuple::make( uint cnt, const Type **fields ) {
@@ -2333,16 +2444,17 @@
   else
     return size;
 }
 
 //------------------------------make-------------------------------------------
-const TypeAry* TypeAry::make(const Type* elem, const TypeInt* size, bool stable) {
+const TypeAry* TypeAry::make(const Type* elem, const TypeInt* size, bool stable,
+                             bool flat, bool not_flat, bool not_null_free) {
   if (UseCompressedOops && elem->isa_oopptr()) {
     elem = elem->make_narrowoop();
   }
   size = normalize_array_size(size);
-  return (TypeAry*)(new TypeAry(elem,size,stable))->hashcons();
+  return (TypeAry*)(new TypeAry(elem, size, stable, flat, not_flat, not_null_free))->hashcons();
 }
 
 //------------------------------meet-------------------------------------------
 // Compute the MEET of two types.  It returns a new Type object.
 const Type *TypeAry::xmeet( const Type *t ) const {
@@ -2360,11 +2472,14 @@
 
   case Array: {                 // Meeting 2 arrays?
     const TypeAry *a = t->is_ary();
     return TypeAry::make(_elem->meet_speculative(a->_elem),
                          _size->xmeet(a->_size)->is_int(),
-                         _stable && a->_stable);
+                         _stable && a->_stable,
+                         _flat && a->_flat,
+                         _not_flat && a->_not_flat,
+                         _not_null_free && a->_not_null_free);
   }
   case Top:
     break;
   }
   return this;                  // Return the double constant
@@ -2373,40 +2488,45 @@
 //------------------------------xdual------------------------------------------
 // Dual: compute field-by-field dual
 const Type *TypeAry::xdual() const {
   const TypeInt* size_dual = _size->dual()->is_int();
   size_dual = normalize_array_size(size_dual);
-  return new TypeAry(_elem->dual(), size_dual, !_stable);
+  return new TypeAry(_elem->dual(), size_dual, !_stable, !_flat, !_not_flat, !_not_null_free);
 }
 
 //------------------------------eq---------------------------------------------
 // Structural equality check for Type representations
 bool TypeAry::eq( const Type *t ) const {
   const TypeAry *a = (const TypeAry*)t;
   return _elem == a->_elem &&
     _stable == a->_stable &&
-    _size == a->_size;
+    _size == a->_size &&
+    _flat == a->_flat &&
+    _not_flat == a->_not_flat &&
+    _not_null_free == a->_not_null_free;
+
 }
 
 //------------------------------hash-------------------------------------------
 // Type-specific hashing function.
 uint TypeAry::hash(void) const {
-  return (uint)(uintptr_t)_elem + (uint)(uintptr_t)_size + (uint)(_stable ? 43 : 0);
+  return (uint)(uintptr_t)_elem + (uint)(uintptr_t)_size + (uint)(_stable ? 43 : 0) +
+      (uint)(_flat ? 44 : 0) + (uint)(_not_flat ? 45 : 0) + (uint)(_not_null_free ? 46 : 0);
 }
 
 /**
  * Return same type without a speculative part in the element
  */
 const TypeAry* TypeAry::remove_speculative() const {
-  return make(_elem->remove_speculative(), _size, _stable);
+  return make(_elem->remove_speculative(), _size, _stable, _flat, _not_flat, _not_null_free);
 }
 
 /**
  * Return same type with cleaned up speculative part of element
  */
 const Type* TypeAry::cleanup_speculative() const {
-  return make(_elem->cleanup_speculative(), _size, _stable);
+  return make(_elem->cleanup_speculative(), _size, _stable, _flat, _not_flat, _not_null_free);
 }
 
 /**
  * Return same type but with a different inline depth (used for speculation)
  *
@@ -2421,10 +2541,15 @@
 
 //------------------------------dump2------------------------------------------
 #ifndef PRODUCT
 void TypeAry::dump2( Dict &d, uint depth, outputStream *st ) const {
   if (_stable)  st->print("stable:");
+  if (_flat) st->print("flat:");
+  if (Verbose) {
+    if (_not_flat) st->print("not flat:");
+    if (_not_null_free) st->print("not null free:");
+  }
   _elem->dump2(d, depth, st);
   st->print("[");
   _size->dump2(d, depth, st);
   st->print("]");
 }
@@ -2460,12 +2585,21 @@
   const TypeInstPtr* tinst;
   if (_elem->isa_narrowoop())
     tinst = _elem->make_ptr()->isa_instptr();
   else
     tinst = _elem->isa_instptr();
-  if (tinst)
-    return tinst->instance_klass()->is_final();
+  if (tinst) {
+    if (tinst->instance_klass()->is_final()) {
+      // TODO 8325106 Fix comment
+      // Even if MyValue is exact, [LMyValue is not exact due to [QMyValue <: [LMyValue.
+      if (tinst->is_inlinetypeptr() && (tinst->ptr() == TypePtr::BotPTR || tinst->ptr() == TypePtr::TopPTR)) {
+        return false;
+      }
+      return true;
+    }
+    return false;
+  }
   const TypeAryPtr*  tap;
   if (_elem->isa_narrowoop())
     tap = _elem->make_ptr()->isa_aryptr();
   else
     tap = _elem->isa_aryptr();
@@ -2657,11 +2791,11 @@
   { /* NotNull */ NotNull,   NotNull,   NotNull,  BotPTR, NotNull, BotPTR,},
   { /* BotPTR  */ BotPTR,    BotPTR,    BotPTR,   BotPTR, BotPTR,  BotPTR,}
 };
 
 //------------------------------make-------------------------------------------
-const TypePtr *TypePtr::make(TYPES t, enum PTR ptr, int offset, const TypePtr* speculative, int inline_depth) {
+const TypePtr* TypePtr::make(TYPES t, enum PTR ptr, Offset offset, const TypePtr* speculative, int inline_depth) {
   return (TypePtr*)(new TypePtr(t,ptr,offset, speculative, inline_depth))->hashcons();
 }
 
 //------------------------------cast_to_ptr_type-------------------------------
 const TypePtr* TypePtr::cast_to_ptr_type(PTR ptr) const {
@@ -2671,11 +2805,11 @@
 }
 
 //------------------------------get_con----------------------------------------
 intptr_t TypePtr::get_con() const {
   assert( _ptr == Null, "" );
-  return _offset;
+  return offset();
 }
 
 //------------------------------meet-------------------------------------------
 // Compute the MEET of two types.  It returns a new Type object.
 const Type *TypePtr::xmeet(const Type *t) const {
@@ -2742,24 +2876,17 @@
   }
   return this;
 }
 
 //------------------------------meet_offset------------------------------------
-int TypePtr::meet_offset( int offset ) const {
-  // Either is 'TOP' offset?  Return the other offset!
-  if( _offset == OffsetTop ) return offset;
-  if( offset == OffsetTop ) return _offset;
-  // If either is different, return 'BOTTOM' offset
-  if( _offset != offset ) return OffsetBot;
-  return _offset;
+Type::Offset TypePtr::meet_offset(int offset) const {
+  return _offset.meet(Offset(offset));
 }
 
 //------------------------------dual_offset------------------------------------
-int TypePtr::dual_offset( ) const {
-  if( _offset == OffsetTop ) return OffsetBot;// Map 'TOP' into 'BOTTOM'
-  if( _offset == OffsetBot ) return OffsetTop;// Map 'BOTTOM' into 'TOP'
-  return _offset;               // Map everything else into self
+Type::Offset TypePtr::dual_offset() const {
+  return _offset.dual();
 }
 
 //------------------------------xdual------------------------------------------
 // Dual: compute field-by-field dual
 const TypePtr::PTR TypePtr::ptr_dual[TypePtr::lastPTR] = {
@@ -2768,45 +2895,34 @@
 const Type *TypePtr::xdual() const {
   return new TypePtr(AnyPtr, dual_ptr(), dual_offset(), dual_speculative(), dual_inline_depth());
 }
 
 //------------------------------xadd_offset------------------------------------
-int TypePtr::xadd_offset( intptr_t offset ) const {
-  // Adding to 'TOP' offset?  Return 'TOP'!
-  if( _offset == OffsetTop || offset == OffsetTop ) return OffsetTop;
-  // Adding to 'BOTTOM' offset?  Return 'BOTTOM'!
-  if( _offset == OffsetBot || offset == OffsetBot ) return OffsetBot;
-  // Addition overflows or "accidentally" equals to OffsetTop? Return 'BOTTOM'!
-  offset += (intptr_t)_offset;
-  if (offset != (int)offset || offset == OffsetTop) return OffsetBot;
-
-  // assert( _offset >= 0 && _offset+offset >= 0, "" );
-  // It is possible to construct a negative offset during PhaseCCP
-
-  return (int)offset;        // Sum valid offsets
+Type::Offset TypePtr::xadd_offset(intptr_t offset) const {
+  return _offset.add(offset);
 }
 
 //------------------------------add_offset-------------------------------------
 const TypePtr *TypePtr::add_offset( intptr_t offset ) const {
   return make(AnyPtr, _ptr, xadd_offset(offset), _speculative, _inline_depth);
 }
 
 const TypePtr *TypePtr::with_offset(intptr_t offset) const {
-  return make(AnyPtr, _ptr, offset, _speculative, _inline_depth);
+  return make(AnyPtr, _ptr, Offset(offset), _speculative, _inline_depth);
 }
 
 //------------------------------eq---------------------------------------------
 // Structural equality check for Type representations
 bool TypePtr::eq( const Type *t ) const {
   const TypePtr *a = (const TypePtr*)t;
-  return _ptr == a->ptr() && _offset == a->offset() && eq_speculative(a) && _inline_depth == a->_inline_depth;
+  return _ptr == a->ptr() && _offset == a->_offset && eq_speculative(a) && _inline_depth == a->_inline_depth;
 }
 
 //------------------------------hash-------------------------------------------
 // Type-specific hashing function.
 uint TypePtr::hash(void) const {
-  return (uint)_ptr + (uint)_offset + (uint)hash_speculative() + (uint)_inline_depth;
+  return (uint)_ptr + (uint)offset() + (uint)hash_speculative() + (uint)_inline_depth;
 }
 
 /**
  * Return same type without a speculative part
  */
@@ -3068,13 +3184,11 @@
 
 #ifndef PRODUCT
 void TypePtr::dump2( Dict &d, uint depth, outputStream *st ) const {
   if( _ptr == Null ) st->print("null");
   else st->print("%s *", ptr_msg[_ptr]);
-  if( _offset == OffsetTop ) st->print("+top");
-  else if( _offset == OffsetBot ) st->print("+bot");
-  else if( _offset ) st->print("+%d", _offset);
+  _offset.dump2(st);
   dump_inline_depth(st);
   dump_speculative(st);
 }
 
 /**
@@ -3105,15 +3219,15 @@
 //------------------------------singleton--------------------------------------
 // TRUE if Type is a singleton type, FALSE otherwise.   Singletons are simple
 // constants
 bool TypePtr::singleton(void) const {
   // TopPTR, Null, AnyNull, Constant are all singletons
-  return (_offset != OffsetBot) && !below_centerline(_ptr);
+  return (_offset != Offset::bottom) && !below_centerline(_ptr);
 }
 
 bool TypePtr::empty(void) const {
-  return (_offset == OffsetTop) || above_centerline(_ptr);
+  return (_offset == Offset::top) || above_centerline(_ptr);
 }
 
 //=============================================================================
 // Convenience common pre-built types.
 const TypeRawPtr *TypeRawPtr::BOTTOM;
@@ -3501,11 +3615,11 @@
   ShouldNotReachHere();
   return Type::singleton();
 }
 
 //------------------------------TypeOopPtr-------------------------------------
-TypeOopPtr::TypeOopPtr(TYPES t, PTR ptr, ciKlass* k, const TypeInterfaces* interfaces, bool xk, ciObject* o, int offset,
+TypeOopPtr::TypeOopPtr(TYPES t, PTR ptr, ciKlass* k, const TypeInterfaces* interfaces, bool xk, ciObject* o, Offset offset, Offset field_offset,
                        int instance_id, const TypePtr* speculative, int inline_depth)
   : TypePtr(t, ptr, offset, speculative, inline_depth),
     _const_oop(o), _klass(k),
     _interfaces(interfaces),
     _klass_is_exact(xk),
@@ -3517,58 +3631,73 @@
   if (klass() != nullptr && klass()->is_loaded()) {
     interfaces->verify_is_loaded();
   }
 #endif
   if (Compile::current()->eliminate_boxing() && (t == InstPtr) &&
-      (offset > 0) && xk && (k != 0) && k->is_instance_klass()) {
-    _is_ptr_to_boxed_value = k->as_instance_klass()->is_boxed_value_offset(offset);
+      (offset.get() > 0) && xk && (k != 0) && k->is_instance_klass()) {
+    _is_ptr_to_boxed_value = k->as_instance_klass()->is_boxed_value_offset(offset.get());
   }
 #ifdef _LP64
-  if (_offset > 0 || _offset == Type::OffsetTop || _offset == Type::OffsetBot) {
-    if (_offset == oopDesc::klass_offset_in_bytes()) {
+  if (this->offset() > 0 || this->offset() == Type::OffsetTop || this->offset() == Type::OffsetBot) {
+    if (this->offset() == oopDesc::klass_offset_in_bytes()) {
       _is_ptr_to_narrowklass = UseCompressedClassPointers;
     } else if (klass() == nullptr) {
       // Array with unknown body type
       assert(this->isa_aryptr(), "only arrays without klass");
       _is_ptr_to_narrowoop = UseCompressedOops;
-    } else if (this->isa_aryptr()) {
-      _is_ptr_to_narrowoop = (UseCompressedOops && klass()->is_obj_array_klass() &&
-                             _offset != arrayOopDesc::length_offset_in_bytes());
+    } else if (UseCompressedOops && this->isa_aryptr() && this->offset() != arrayOopDesc::length_offset_in_bytes()) {
+      if (klass()->is_obj_array_klass()) {
+        _is_ptr_to_narrowoop = true;
+      } else if (klass()->is_flat_array_klass() && field_offset != Offset::top && field_offset != Offset::bottom) {
+        // Check if the field of the inline type array element contains oops
+        ciInlineKlass* vk = klass()->as_flat_array_klass()->element_klass()->as_inline_klass();
+        int foffset = field_offset.get() + vk->first_field_offset();
+        ciField* field = vk->get_field_by_offset(foffset, false);
+        assert(field != nullptr, "missing field");
+        BasicType bt = field->layout_type();
+        _is_ptr_to_narrowoop = UseCompressedOops && ::is_reference_type(bt);
+      }
     } else if (klass()->is_instance_klass()) {
-      ciInstanceKlass* ik = klass()->as_instance_klass();
       if (this->isa_klassptr()) {
         // Perm objects don't use compressed references
-      } else if (_offset == OffsetBot || _offset == OffsetTop) {
+      } else if (_offset == Offset::bottom || _offset == Offset::top) {
         // unsafe access
         _is_ptr_to_narrowoop = UseCompressedOops;
       } else {
         assert(this->isa_instptr(), "must be an instance ptr.");
-
         if (klass() == ciEnv::current()->Class_klass() &&
-            (_offset == java_lang_Class::klass_offset() ||
-             _offset == java_lang_Class::array_klass_offset())) {
+            (this->offset() == java_lang_Class::klass_offset() ||
+             this->offset() == java_lang_Class::array_klass_offset())) {
           // Special hidden fields from the Class.
           assert(this->isa_instptr(), "must be an instance ptr.");
           _is_ptr_to_narrowoop = false;
         } else if (klass() == ciEnv::current()->Class_klass() &&
-                   _offset >= InstanceMirrorKlass::offset_of_static_fields()) {
+                   this->offset() >= InstanceMirrorKlass::offset_of_static_fields()) {
           // Static fields
           ciField* field = nullptr;
           if (const_oop() != nullptr) {
             ciInstanceKlass* k = const_oop()->as_instance()->java_lang_Class_klass()->as_instance_klass();
-            field = k->get_field_by_offset(_offset, true);
-          }
-          if (field != nullptr) {
-            BasicType basic_elem_type = field->layout_type();
-            _is_ptr_to_narrowoop = UseCompressedOops && ::is_reference_type(basic_elem_type);
-          } else {
-            // unsafe access
-            _is_ptr_to_narrowoop = UseCompressedOops;
+            // TODO 8325106 remove?
+            if (k->is_inlinetype() && this->offset() == k->as_inline_klass()->default_value_offset()) {
+              // Special hidden field that contains the oop of the default inline type
+              // basic_elem_type = T_PRIMITIVE_OBJECT;
+             _is_ptr_to_narrowoop = UseCompressedOops;
+            } else {
+              field = k->get_field_by_offset(this->offset(), true);
+              if (field != nullptr) {
+                BasicType basic_elem_type = field->layout_type();
+                _is_ptr_to_narrowoop = UseCompressedOops && ::is_reference_type(basic_elem_type);
+              } else {
+                // unsafe access
+                _is_ptr_to_narrowoop = UseCompressedOops;
+              }
+            }
           }
         } else {
           // Instance fields which contains a compressed oop references.
-          ciField* field = ik->get_field_by_offset(_offset, false);
+          ciInstanceKlass* ik = klass()->as_instance_klass();
+          ciField* field = ik->get_field_by_offset(this->offset(), false);
           if (field != nullptr) {
             BasicType basic_elem_type = field->layout_type();
             _is_ptr_to_narrowoop = UseCompressedOops && ::is_reference_type(basic_elem_type);
           } else if (klass()->equals(ciEnv::current()->Object_klass())) {
             // Compile::find_alias_type() cast exactness on all types to verify
@@ -3584,18 +3713,18 @@
   }
 #endif
 }
 
 //------------------------------make-------------------------------------------
-const TypeOopPtr *TypeOopPtr::make(PTR ptr, int offset, int instance_id,
-                                     const TypePtr* speculative, int inline_depth) {
+const TypeOopPtr *TypeOopPtr::make(PTR ptr, Offset offset, int instance_id,
+                                   const TypePtr* speculative, int inline_depth) {
   assert(ptr != Constant, "no constant generic pointers");
   ciKlass*  k = Compile::current()->env()->Object_klass();
   bool      xk = false;
   ciObject* o = nullptr;
   const TypeInterfaces* interfaces = TypeInterfaces::make();
-  return (TypeOopPtr*)(new TypeOopPtr(OopPtr, ptr, k, interfaces, xk, o, offset, instance_id, speculative, inline_depth))->hashcons();
+  return (TypeOopPtr*)(new TypeOopPtr(OopPtr, ptr, k, interfaces, xk, o, offset, Offset::bottom, instance_id, speculative, inline_depth))->hashcons();
 }
 
 
 //------------------------------cast_to_ptr_type-------------------------------
 const TypeOopPtr* TypeOopPtr::cast_to_ptr_type(PTR ptr) const {
@@ -3616,11 +3745,10 @@
   // There is no such thing as an exact general oop.
   // Return self unchanged.
   return this;
 }
 
-
 //------------------------------as_klass_type----------------------------------
 // Return the klass type corresponding to this instance or array type.
 // It is the type that is loaded from an object of this type.
 const TypeKlassPtr* TypeOopPtr::as_klass_type(bool try_for_exact) const {
   ShouldNotReachHere();
@@ -3662,11 +3790,11 @@
     return TypePtr::BOTTOM;     // Oop meet raw is not well defined
 
   case AnyPtr: {
     // Found an AnyPtr type vs self-OopPtr type
     const TypePtr *tp = t->is_ptr();
-    int offset = meet_offset(tp->offset());
+    Offset offset = meet_offset(tp->offset());
     PTR ptr = meet_ptr(tp->ptr());
     const TypePtr* speculative = xmeet_speculative(tp);
     int depth = meet_inline_depth(tp->inline_depth());
     switch (tp->ptr()) {
     case Null:
@@ -3704,17 +3832,17 @@
 //------------------------------xdual------------------------------------------
 // Dual of a pure heap pointer.  No relevant klass or oop information.
 const Type *TypeOopPtr::xdual() const {
   assert(klass() == Compile::current()->env()->Object_klass(), "no klasses here");
   assert(const_oop() == nullptr,             "no constants here");
-  return new TypeOopPtr(_base, dual_ptr(), klass(), _interfaces, klass_is_exact(), const_oop(), dual_offset(), dual_instance_id(), dual_speculative(), dual_inline_depth());
+  return new TypeOopPtr(_base, dual_ptr(), klass(), _interfaces, klass_is_exact(), const_oop(), dual_offset(), Offset::bottom, dual_instance_id(), dual_speculative(), dual_inline_depth());
 }
 
 //--------------------------make_from_klass_common-----------------------------
 // Computes the element-type given a klass.
-const TypeOopPtr* TypeOopPtr::make_from_klass_common(ciKlass* klass, bool klass_change, bool try_for_exact, InterfaceHandling interface_handling) {
-  if (klass->is_instance_klass()) {
+const TypeOopPtr* TypeOopPtr::make_from_klass_common(ciKlass *klass, bool klass_change, bool try_for_exact, InterfaceHandling interface_handling) {
+  if (klass->is_instance_klass() || klass->is_inlinetype()) {
     Compile* C = Compile::current();
     Dependencies* deps = C->dependencies();
     assert((deps != nullptr) == (C->method() != nullptr && C->method()->code_size() > 0), "sanity");
     // Element is an instance
     bool klass_is_exact = false;
@@ -3737,29 +3865,46 @@
         deps->assert_leaf_type(ik);
         klass_is_exact = true;
       }
     }
     const TypeInterfaces* interfaces = TypePtr::interfaces(klass, true, true, false, interface_handling);
-    return TypeInstPtr::make(TypePtr::BotPTR, klass, interfaces, klass_is_exact, nullptr, 0);
+    return TypeInstPtr::make(TypePtr::BotPTR, klass, interfaces, klass_is_exact, nullptr, Offset(0));
   } else if (klass->is_obj_array_klass()) {
-    // Element is an object array. Recursively call ourself.
-    ciKlass* eklass = klass->as_obj_array_klass()->element_klass();
-    const TypeOopPtr *etype = TypeOopPtr::make_from_klass_common(eklass, false, try_for_exact, interface_handling);
-    bool xk = etype->klass_is_exact();
-    const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS);
+    // Element is an object or inline type array. Recursively call ourself.
+    const TypeOopPtr* etype = TypeOopPtr::make_from_klass_common(klass->as_array_klass()->element_klass(), /* klass_change= */ false, try_for_exact, interface_handling);
+    // Determine null-free/flat properties
+    const TypeOopPtr* exact_etype = etype;
+    if (etype->can_be_inline_type()) {
+      // Use exact type if element can be an inline type
+      exact_etype = TypeOopPtr::make_from_klass_common(klass->as_array_klass()->element_klass(), /* klass_change= */ true, /* try_for_exact= */ true, interface_handling);
+    }
+    bool not_null_free = !exact_etype->can_be_inline_type();
+    bool not_flat = !UseFlatArray || not_null_free || (exact_etype->is_inlinetypeptr() && !exact_etype->inline_klass()->flat_in_array());
+
+    // TODO 8325106 Fix comment
+    // Even if MyValue is exact, [LMyValue is not exact due to [QMyValue <: [LMyValue.
+    bool xk = etype->klass_is_exact() && !etype->is_inlinetypeptr();
+    const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS, /* stable= */ false, /* flat= */ false, not_flat, not_null_free);
     // We used to pass NotNull in here, asserting that the sub-arrays
     // are all not-null.  This is not true in generally, as code can
-    // slam nulls down in the subarrays.
-    const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::BotPTR, arr0, nullptr, xk, 0);
+    // slam nullptrs down in the subarrays.
+    const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::BotPTR, arr0, nullptr, xk, Offset(0));
     return arr;
   } else if (klass->is_type_array_klass()) {
     // Element is an typeArray
     const Type* etype = get_const_basic_type(klass->as_type_array_klass()->element_type());
-    const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS);
+    const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS,
+                                        /* stable= */ false, /* flat= */ false, /* not_flat= */ true, /* not_null_free= */ true);
     // We used to pass NotNull in here, asserting that the array pointer
     // is not-null. That was not true in general.
-    const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::BotPTR, arr0, klass, true, 0);
+    const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::BotPTR, arr0, klass, true, Offset(0));
+    return arr;
+  } else if (klass->is_flat_array_klass()) {
+    const TypeOopPtr* etype = TypeOopPtr::make_from_klass_raw(klass->as_array_klass()->element_klass(), trust_interfaces);
+    etype = etype->join_speculative(TypePtr::NOTNULL)->is_oopptr();
+    const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS, /* stable= */ false, /* flat= */ true);
+    const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::BotPTR, arr0, klass, true, Offset(0));
     return arr;
   } else {
     ShouldNotReachHere();
     return nullptr;
   }
@@ -3771,54 +3916,66 @@
   assert(!o->is_null_object(), "null object not yet handled here.");
 
   const bool make_constant = require_constant || o->should_be_constant();
 
   ciKlass* klass = o->klass();
-  if (klass->is_instance_klass()) {
-    // Element is an instance
+  if (klass->is_instance_klass() || klass->is_inlinetype()) {
+    // Element is an instance or inline type
     if (make_constant) {
       return TypeInstPtr::make(o);
     } else {
-      return TypeInstPtr::make(TypePtr::NotNull, klass, true, nullptr, 0);
+      return TypeInstPtr::make(TypePtr::NotNull, klass, true, nullptr, Offset(0));
     }
   } else if (klass->is_obj_array_klass()) {
     // Element is an object array. Recursively call ourself.
-    const TypeOopPtr *etype =
-      TypeOopPtr::make_from_klass_raw(klass->as_obj_array_klass()->element_klass(), trust_interfaces);
-    const TypeAry* arr0 = TypeAry::make(etype, TypeInt::make(o->as_array()->length()));
+    const TypeOopPtr* etype = TypeOopPtr::make_from_klass_raw(klass->as_array_klass()->element_klass(), trust_interfaces);
+    const TypeAry* arr0 = TypeAry::make(etype, TypeInt::make(o->as_array()->length()),
+                                        /* stable= */ false, /* flat= */ false, /* not_flat= */ true, /* not_null_free= */ true);
     // We used to pass NotNull in here, asserting that the sub-arrays
     // are all not-null.  This is not true in generally, as code can
     // slam nulls down in the subarrays.
     if (make_constant) {
-      return TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0);
+      return TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, Offset(0));
     } else {
-      return TypeAryPtr::make(TypePtr::NotNull, arr0, klass, true, 0);
+      return TypeAryPtr::make(TypePtr::NotNull, arr0, klass, true, Offset(0));
     }
   } else if (klass->is_type_array_klass()) {
     // Element is an typeArray
-    const Type* etype =
-      (Type*)get_const_basic_type(klass->as_type_array_klass()->element_type());
-    const TypeAry* arr0 = TypeAry::make(etype, TypeInt::make(o->as_array()->length()));
+    const Type* etype = (Type*)get_const_basic_type(klass->as_type_array_klass()->element_type());
+    const TypeAry* arr0 = TypeAry::make(etype, TypeInt::make(o->as_array()->length()),
+                                        /* stable= */ false, /* flat= */ false, /* not_flat= */ true, /* not_null_free= */ true);
     // We used to pass NotNull in here, asserting that the array pointer
     // is not-null. That was not true in general.
     if (make_constant) {
-      return TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0);
+      return TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, Offset(0));
+    } else {
+      return TypeAryPtr::make(TypePtr::NotNull, arr0, klass, true, Offset(0));
+    }
+  } else if (klass->is_flat_array_klass()) {
+    const TypeOopPtr* etype = TypeOopPtr::make_from_klass_raw(klass->as_array_klass()->element_klass(), trust_interfaces);
+    etype = etype->join_speculative(TypePtr::NOTNULL)->is_oopptr();
+    const TypeAry* arr0 = TypeAry::make(etype, TypeInt::make(o->as_array()->length()), /* stable= */ false, /* flat= */ true);
+    // We used to pass NotNull in here, asserting that the sub-arrays
+    // are all not-null.  This is not true in generally, as code can
+    // slam nullptrs down in the subarrays.
+    if (make_constant) {
+      return TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, Offset(0));
     } else {
-      return TypeAryPtr::make(TypePtr::NotNull, arr0, klass, true, 0);
+      return TypeAryPtr::make(TypePtr::NotNull, arr0, klass, true, Offset(0));
     }
   }
 
   fatal("unhandled object type");
   return nullptr;
 }
 
 //------------------------------get_con----------------------------------------
 intptr_t TypeOopPtr::get_con() const {
   assert( _ptr == Null || _ptr == Constant, "" );
-  assert( _offset >= 0, "" );
+  assert(offset() >= 0, "");
 
-  if (_offset != 0) {
+  if (offset() != 0) {
     // After being ported to the compiler interface, the compiler no longer
     // directly manipulates the addresses of oops.  Rather, it only has a pointer
     // to a handle at compile time.  This handle is embedded in the generated
     // code and dereferenced at the time the nmethod is made.  Until that time,
     // it is not reasonable to do arithmetic with the addresses of oops (we don't
@@ -3875,16 +4032,11 @@
 #ifndef PRODUCT
 void TypeOopPtr::dump2( Dict &d, uint depth, outputStream *st ) const {
   st->print("oopptr:%s", ptr_msg[_ptr]);
   if( _klass_is_exact ) st->print(":exact");
   if( const_oop() ) st->print(INTPTR_FORMAT, p2i(const_oop()));
-  switch( _offset ) {
-  case OffsetTop: st->print("+top"); break;
-  case OffsetBot: st->print("+any"); break;
-  case         0: break;
-  default:        st->print("+%d",_offset); break;
-  }
+  _offset.dump2(st);
   if (_instance_id == InstanceTop)
     st->print(",iid=top");
   else if (_instance_id != InstanceBot)
     st->print(",iid=%d",_instance_id);
 
@@ -3897,20 +4049,20 @@
 // TRUE if Type is a singleton type, FALSE otherwise.   Singletons are simple
 // constants
 bool TypeOopPtr::singleton(void) const {
   // detune optimizer to not generate constant oop + constant offset as a constant!
   // TopPTR, Null, AnyNull, Constant are all singletons
-  return (_offset == 0) && !below_centerline(_ptr);
+  return (offset() == 0) && !below_centerline(_ptr);
 }
 
 //------------------------------add_offset-------------------------------------
 const TypePtr* TypeOopPtr::add_offset(intptr_t offset) const {
   return make(_ptr, xadd_offset(offset), _instance_id, add_offset_speculative(offset), _inline_depth);
 }
 
 const TypeOopPtr* TypeOopPtr::with_offset(intptr_t offset) const {
-  return make(_ptr, offset, _instance_id, with_offset_speculative(offset), _inline_depth);
+  return make(_ptr, Offset(offset), _instance_id, with_offset_speculative(offset), _inline_depth);
 }
 
 /**
  * Return same type without a speculative part
  */
@@ -4019,26 +4171,30 @@
   }
   return _interfaces->exact_klass();
 }
 
 //------------------------------TypeInstPtr-------------------------------------
-TypeInstPtr::TypeInstPtr(PTR ptr, ciKlass* k, const TypeInterfaces* interfaces, bool xk, ciObject* o, int off,
-                         int instance_id, const TypePtr* speculative, int inline_depth)
-  : TypeOopPtr(InstPtr, ptr, k, interfaces, xk, o, off, instance_id, speculative, inline_depth) {
+TypeInstPtr::TypeInstPtr(PTR ptr, ciKlass* k, const TypeInterfaces* interfaces, bool xk, ciObject* o, Offset off,
+                         bool flat_in_array, int instance_id, const TypePtr* speculative, int inline_depth)
+  : TypeOopPtr(InstPtr, ptr, k, interfaces, xk, o, off, Offset::bottom, instance_id, speculative, inline_depth),
+    _flat_in_array(flat_in_array) {
   assert(k == nullptr || !k->is_loaded() || !k->is_interface(), "no interface here");
   assert(k != nullptr &&
          (k->is_loaded() || o == nullptr),
          "cannot have constants with non-loaded klass");
+  assert(!klass()->flat_in_array() || flat_in_array, "Should be flat in array");
+  assert(!flat_in_array || can_be_inline_type(), "Only inline types can be flat in array");
 };
 
 //------------------------------make-------------------------------------------
 const TypeInstPtr *TypeInstPtr::make(PTR ptr,
                                      ciKlass* k,
                                      const TypeInterfaces* interfaces,
                                      bool xk,
                                      ciObject* o,
-                                     int offset,
+                                     Offset offset,
+                                     bool flat_in_array,
                                      int instance_id,
                                      const TypePtr* speculative,
                                      int inline_depth) {
   assert( !k->is_loaded() || k->is_instance_klass(), "Must be for instance");
   // Either const_oop() is null or else ptr is Constant
@@ -4056,13 +4212,16 @@
     if (!xk && ik->is_final())     xk = true;   // no inexact final klass
     assert(!ik->is_interface(), "no interface here");
     if (xk && ik->is_interface())  xk = false;  // no exact interface
   }
 
+  // Check if this type is known to be flat in arrays
+  flat_in_array = flat_in_array || k->flat_in_array();
+
   // Now hash this baby
   TypeInstPtr *result =
-    (TypeInstPtr*)(new TypeInstPtr(ptr, k, interfaces, xk, o ,offset, instance_id, speculative, inline_depth))->hashcons();
+    (TypeInstPtr*)(new TypeInstPtr(ptr, k, interfaces, xk, o, offset, flat_in_array, instance_id, speculative, inline_depth))->hashcons();
 
   return result;
 }
 
 const TypeInterfaces* TypePtr::interfaces(ciKlass*& k, bool klass, bool interface, bool array, InterfaceHandling interface_handling) {
@@ -4124,35 +4283,35 @@
 //------------------------------cast_to_ptr_type-------------------------------
 const TypeInstPtr* TypeInstPtr::cast_to_ptr_type(PTR ptr) const {
   if( ptr == _ptr ) return this;
   // Reconstruct _sig info here since not a problem with later lazy
   // construction, _sig will show up on demand.
-  return make(ptr, klass(), _interfaces, klass_is_exact(), ptr == Constant ? const_oop() : nullptr, _offset, _instance_id, _speculative, _inline_depth);
+  return make(ptr, klass(), _interfaces, klass_is_exact(), ptr == Constant ? const_oop() : nullptr, _offset, _flat_in_array, _instance_id, _speculative, _inline_depth);
 }
 
 
 //-----------------------------cast_to_exactness-------------------------------
 const TypeInstPtr* TypeInstPtr::cast_to_exactness(bool klass_is_exact) const {
   if( klass_is_exact == _klass_is_exact ) return this;
   if (!_klass->is_loaded())  return this;
   ciInstanceKlass* ik = _klass->as_instance_klass();
   if( (ik->is_final() || _const_oop) )  return this;  // cannot clear xk
   assert(!ik->is_interface(), "no interface here");
-  return make(ptr(), klass(), _interfaces, klass_is_exact, const_oop(), _offset, _instance_id, _speculative, _inline_depth);
+  return make(ptr(), klass(), _interfaces, klass_is_exact, const_oop(), _offset, _flat_in_array, _instance_id, _speculative, _inline_depth);
 }
 
 //-----------------------------cast_to_instance_id----------------------------
 const TypeInstPtr* TypeInstPtr::cast_to_instance_id(int instance_id) const {
   if( instance_id == _instance_id ) return this;
-  return make(_ptr, klass(),  _interfaces, _klass_is_exact, const_oop(), _offset, instance_id, _speculative, _inline_depth);
+  return make(_ptr, klass(), _interfaces, _klass_is_exact, const_oop(), _offset, _flat_in_array, instance_id, _speculative, _inline_depth);
 }
 
 //------------------------------xmeet_unloaded---------------------------------
 // Compute the MEET of two InstPtrs when at least one is unloaded.
 // Assume classes are different since called after check for same name/class-loader
 const TypeInstPtr *TypeInstPtr::xmeet_unloaded(const TypeInstPtr *tinst, const TypeInterfaces* interfaces) const {
-  int off = meet_offset(tinst->offset());
+  Offset off = meet_offset(tinst->offset());
   PTR ptr = meet_ptr(tinst->ptr());
   int instance_id = meet_instance_id(tinst->instance_id());
   const TypePtr* speculative = xmeet_speculative(tinst);
   int depth = meet_inline_depth(tinst->inline_depth());
 
@@ -4173,11 +4332,11 @@
     //  BOTTOM  | ........................Object-BOTTOM ..................|
     //
     assert(loaded->ptr() != TypePtr::Null, "insanity check");
     //
     if (loaded->ptr() == TypePtr::TopPTR)        { return unloaded; }
-    else if (loaded->ptr() == TypePtr::AnyNull)  { return make(ptr, unloaded->klass(), interfaces, false, nullptr, off, instance_id, speculative, depth); }
+    else if (loaded->ptr() == TypePtr::AnyNull)  { return make(ptr, unloaded->klass(), interfaces, false, nullptr, off, false, instance_id, speculative, depth); }
     else if (loaded->ptr() == TypePtr::BotPTR)   { return TypeInstPtr::BOTTOM; }
     else if (loaded->ptr() == TypePtr::Constant || loaded->ptr() == TypePtr::NotNull) {
       if (unloaded->ptr() == TypePtr::BotPTR)    { return TypeInstPtr::BOTTOM;  }
       else                                       { return TypeInstPtr::NOTNULL; }
     }
@@ -4234,20 +4393,20 @@
   }
 
   case OopPtr: {                // Meeting to OopPtrs
     // Found a OopPtr type vs self-InstPtr type
     const TypeOopPtr *tp = t->is_oopptr();
-    int offset = meet_offset(tp->offset());
+    Offset offset = meet_offset(tp->offset());
     PTR ptr = meet_ptr(tp->ptr());
     switch (tp->ptr()) {
     case TopPTR:
     case AnyNull: {
       int instance_id = meet_instance_id(InstanceTop);
       const TypePtr* speculative = xmeet_speculative(tp);
       int depth = meet_inline_depth(tp->inline_depth());
       return make(ptr, klass(), _interfaces, klass_is_exact(),
-                  (ptr == Constant ? const_oop() : nullptr), offset, instance_id, speculative, depth);
+                  (ptr == Constant ? const_oop() : nullptr), offset, flat_in_array(), instance_id, speculative, depth);
     }
     case NotNull:
     case BotPTR: {
       int instance_id = meet_instance_id(tp->instance_id());
       const TypePtr* speculative = xmeet_speculative(tp);
@@ -4259,11 +4418,11 @@
   }
 
   case AnyPtr: {                // Meeting to AnyPtrs
     // Found an AnyPtr type vs self-InstPtr type
     const TypePtr *tp = t->is_ptr();
-    int offset = meet_offset(tp->offset());
+    Offset offset = meet_offset(tp->offset());
     PTR ptr = meet_ptr(tp->ptr());
     int instance_id = meet_instance_id(InstanceTop);
     const TypePtr* speculative = xmeet_speculative(tp);
     int depth = meet_inline_depth(tp->inline_depth());
     switch (tp->ptr()) {
@@ -4271,11 +4430,11 @@
       if( ptr == Null ) return TypePtr::make(AnyPtr, ptr, offset, speculative, depth);
       // else fall through to AnyNull
     case TopPTR:
     case AnyNull: {
       return make(ptr, klass(), _interfaces, klass_is_exact(),
-                  (ptr == Constant ? const_oop() : nullptr), offset, instance_id, speculative, depth);
+                  (ptr == Constant ? const_oop() : nullptr), offset, flat_in_array(), instance_id, speculative, depth);
     }
     case NotNull:
     case BotPTR:
       return TypePtr::make(AnyPtr, ptr, offset, speculative,depth);
     default: typerr(t);
@@ -4299,11 +4458,11 @@
   */
 
   case InstPtr: {                // Meeting 2 Oops?
     // Found an InstPtr sub-type vs self-InstPtr type
     const TypeInstPtr *tinst = t->is_instptr();
-    int off = meet_offset(tinst->offset());
+    Offset off = meet_offset(tinst->offset());
     PTR ptr = meet_ptr(tinst->ptr());
     int instance_id = meet_instance_id(tinst->instance_id());
     const TypePtr* speculative = xmeet_speculative(tinst);
     int depth = meet_inline_depth(tinst->inline_depth());
     const TypeInterfaces* interfaces = meet_interfaces(tinst);
@@ -4311,12 +4470,13 @@
     ciKlass* tinst_klass = tinst->klass();
     ciKlass* this_klass  = klass();
 
     ciKlass* res_klass = nullptr;
     bool res_xk = false;
+    bool res_flat_in_array = false;
     const Type* res;
-    MeetResult kind = meet_instptr(ptr, interfaces, this, tinst, res_klass, res_xk);
+    MeetResult kind = meet_instptr(ptr, interfaces, this, tinst, res_klass, res_xk, res_flat_in_array);
 
     if (kind == UNLOADED) {
       // One of these classes has not been loaded
       const TypeInstPtr* unloaded_meet = xmeet_unloaded(tinst, interfaces);
 #ifndef PRODUCT
@@ -4353,11 +4513,11 @@
           assert(!this_klass->is_interface(), "");
           o = this_oop;
         } else
           ptr = NotNull;
       }
-      res = make(ptr, res_klass, interfaces, res_xk, o, off, instance_id, speculative, depth);
+      res = make(ptr, res_klass, interfaces, res_xk, o, off, res_flat_in_array, instance_id, speculative, depth);
     }
 
     return res;
 
   } // End of case InstPtr
@@ -4365,26 +4525,32 @@
   } // End of switch
   return this;                  // Return the double constant
 }
 
 template<class T> TypePtr::MeetResult TypePtr::meet_instptr(PTR& ptr, const TypeInterfaces*& interfaces, const T* this_type, const T* other_type,
-                                                            ciKlass*& res_klass, bool& res_xk) {
+                                                            ciKlass*& res_klass, bool& res_xk, bool& res_flat_in_array) {
   ciKlass* this_klass = this_type->klass();
   ciKlass* other_klass = other_type->klass();
+  const bool this_flat_in_array = this_type->flat_in_array();
+  const bool other_flat_in_array = other_type->flat_in_array();
+  const bool this_not_flat_in_array = this_type->not_flat_in_array();
+  const bool other_not_flat_in_array = other_type->not_flat_in_array();
+
   bool this_xk = this_type->klass_is_exact();
   bool other_xk = other_type->klass_is_exact();
   PTR this_ptr = this_type->ptr();
   PTR other_ptr = other_type->ptr();
   const TypeInterfaces* this_interfaces = this_type->interfaces();
   const TypeInterfaces* other_interfaces = other_type->interfaces();
   // Check for easy case; klasses are equal (and perhaps not loaded!)
   // If we have constants, then we created oops so classes are loaded
   // and we can handle the constants further down.  This case handles
   // both-not-loaded or both-loaded classes
-  if (ptr != Constant && this_klass->equals(other_klass) && this_xk == other_xk) {
+  if (ptr != Constant && this_klass->equals(other_klass) && this_xk == other_xk && this_flat_in_array == other_flat_in_array) {
     res_klass = this_klass;
     res_xk = this_xk;
+    res_flat_in_array = this_flat_in_array;
     return QUICK;
   }
 
   // Classes require inspection in the Java klass hierarchy.  Must be loaded.
   if (!other_klass->is_loaded() || !this_klass->is_loaded()) {
@@ -4414,34 +4580,54 @@
   // If a proper supertype is exact, there can be no subtyping relationship!
   // If both types are equal to the subtype, exactness is and-ed below the
   // centerline and or-ed above it.  (N.B. Constants are always exact.)
 
   // Check for subtyping:
+  // Flat in array matrix, yes = y, no = n, maybe = m, top/empty = T:
+  //        yes maybe no   -> Super Klass
+  //   yes   y    y    y
+  // maybe   y    m    m
+  //    no   T    n    n
+  //    |
+  //    v
+  // Sub Klass
+
   const T* subtype = nullptr;
   bool subtype_exact = false;
+  bool flat_in_array = false;
   if (this_type->is_same_java_type_as(other_type)) {
     subtype = this_type;
     subtype_exact = below_centerline(ptr) ? (this_xk && other_xk) : (this_xk || other_xk);
-  } else if (!other_xk && this_type->is_meet_subtype_of(other_type)) {
+    flat_in_array = below_centerline(ptr) ? (this_flat_in_array && other_flat_in_array) : (this_flat_in_array || other_flat_in_array);
+  } else if (!other_xk && is_meet_subtype_of(this_type, other_type)) {
     subtype = this_type;     // Pick subtyping class
     subtype_exact = this_xk;
-  } else if(!this_xk && other_type->is_meet_subtype_of(this_type)) {
+    bool other_flat_this_maybe_flat = other_flat_in_array && (!this_flat_in_array && !this_not_flat_in_array);
+    flat_in_array = this_flat_in_array || other_flat_this_maybe_flat;
+  } else if (!this_xk && is_meet_subtype_of(other_type, this_type)) {
     subtype = other_type;    // Pick subtyping class
     subtype_exact = other_xk;
+    bool this_flat_other_maybe_flat = this_flat_in_array && (!other_flat_in_array && !other_not_flat_in_array);
+    flat_in_array = other_flat_in_array || this_flat_other_maybe_flat;
   }
 
   if (subtype) {
-    if (above_centerline(ptr)) { // both are up?
+    if (above_centerline(ptr)) {
+      // Both types are empty.
       this_type = other_type = subtype;
       this_xk = other_xk = subtype_exact;
     } else if (above_centerline(this_ptr) && !above_centerline(other_ptr)) {
-      this_type = other_type; // tinst is down; keep down man
+      // this_type is empty while other_type is not. Take other_type.
+      this_type = other_type;
       this_xk = other_xk;
+      flat_in_array = other_flat_in_array;
     } else if (above_centerline(other_ptr) && !above_centerline(this_ptr)) {
+      // other_type is empty while this_type is not. Take this_type.
       other_type = this_type; // this is down; keep down man
-      other_xk = this_xk;
+      flat_in_array = this_flat_in_array;
     } else {
+      // this_type and other_type are both non-empty.
       this_xk = subtype_exact;  // either they are equal, or we'll do an LCA
     }
   }
 
   // Check for classes now being equal
@@ -4449,10 +4635,11 @@
     // If the klasses are equal, the constants may still differ.  Fall to
     // NotNull if they do (neither constant is null; that is a special case
     // handled elsewhere).
     res_klass = this_type->klass();
     res_xk = this_xk;
+    res_flat_in_array = subtype ? flat_in_array : this_flat_in_array;
     return SUBTYPE;
   } // Else classes are not equal
 
   // Since klasses are different, we require a LCA in the Java
   // class hierarchy - which means we have to fall to at least NotNull.
@@ -4465,47 +4652,52 @@
   // Now we find the LCA of Java classes
   ciKlass* k = this_klass->least_common_ancestor(other_klass);
 
   res_klass = k;
   res_xk = false;
+  res_flat_in_array = this_flat_in_array && other_flat_in_array;
 
   return LCA;
 }
 
+template<class T> bool TypePtr::is_meet_subtype_of(const T* sub_type, const T* super_type) {
+  return sub_type->is_meet_subtype_of(super_type) && !(super_type->flat_in_array() && sub_type->not_flat_in_array());
+}
+
 //------------------------java_mirror_type--------------------------------------
 ciType* TypeInstPtr::java_mirror_type() const {
   // must be a singleton type
   if( const_oop() == nullptr )  return nullptr;
 
   // must be of type java.lang.Class
   if( klass() != ciEnv::current()->Class_klass() )  return nullptr;
-
   return const_oop()->as_instance()->java_mirror_type();
 }
 
 
 //------------------------------xdual------------------------------------------
 // Dual: do NOT dual on klasses.  This means I do NOT understand the Java
 // inheritance mechanism.
 const Type *TypeInstPtr::xdual() const {
-  return new TypeInstPtr(dual_ptr(), klass(), _interfaces, klass_is_exact(), const_oop(), dual_offset(), dual_instance_id(), dual_speculative(), dual_inline_depth());
+  return new TypeInstPtr(dual_ptr(), klass(), _interfaces, klass_is_exact(), const_oop(), dual_offset(), flat_in_array(), dual_instance_id(), dual_speculative(), dual_inline_depth());
 }
 
 //------------------------------eq---------------------------------------------
 // Structural equality check for Type representations
 bool TypeInstPtr::eq( const Type *t ) const {
   const TypeInstPtr *p = t->is_instptr();
   return
     klass()->equals(p->klass()) &&
+    flat_in_array() == p->flat_in_array() &&
     _interfaces->eq(p->_interfaces) &&
     TypeOopPtr::eq(p);          // Check sub-type stuff
 }
 
 //------------------------------hash-------------------------------------------
 // Type-specific hashing function.
 uint TypeInstPtr::hash(void) const {
-  return klass()->hash() + TypeOopPtr::hash() + _interfaces->hash();
+  return klass()->hash() + TypeOopPtr::hash() + _interfaces->hash() + (uint)flat_in_array();
 }
 
 bool TypeInstPtr::is_java_subtype_of_helper(const TypeOopPtr* other, bool this_exact, bool other_exact) const {
   return TypePtr::is_java_subtype_of_helper_for_instance(this, other, this_exact, other_exact);
 }
@@ -4555,17 +4747,18 @@
     break;
   default:
     break;
   }
 
-  if( _offset ) {               // Dump offset, if any
-    if( _offset == OffsetBot )      st->print("+any");
-    else if( _offset == OffsetTop ) st->print("+unknown");
-    else st->print("+%d", _offset);
-  }
+  _offset.dump2(st);
 
   st->print(" *");
+
+  if (flat_in_array() && !klass()->is_inlinetype()) {
+    st->print(" (flat in array)");
+  }
+
   if (_instance_id == InstanceTop)
     st->print(",iid=top");
   else if (_instance_id != InstanceBot)
     st->print(",iid=%d",_instance_id);
 
@@ -4574,38 +4767,42 @@
 }
 #endif
 
 //------------------------------add_offset-------------------------------------
 const TypePtr* TypeInstPtr::add_offset(intptr_t offset) const {
-  return make(_ptr, klass(), _interfaces, klass_is_exact(), const_oop(), xadd_offset(offset),
+  return make(_ptr, klass(), _interfaces, klass_is_exact(), const_oop(), xadd_offset(offset), flat_in_array(),
               _instance_id, add_offset_speculative(offset), _inline_depth);
 }
 
 const TypeInstPtr* TypeInstPtr::with_offset(intptr_t offset) const {
-  return make(_ptr, klass(), _interfaces, klass_is_exact(), const_oop(), offset,
+  return make(_ptr, klass(), _interfaces, klass_is_exact(), const_oop(), Offset(offset), flat_in_array(),
               _instance_id, with_offset_speculative(offset), _inline_depth);
 }
 
 const TypeInstPtr* TypeInstPtr::remove_speculative() const {
   if (_speculative == nullptr) {
     return this;
   }
   assert(_inline_depth == InlineDepthTop || _inline_depth == InlineDepthBottom, "non speculative type shouldn't have inline depth");
-  return make(_ptr, klass(), _interfaces, klass_is_exact(), const_oop(), _offset,
+  return make(_ptr, klass(), _interfaces, klass_is_exact(), const_oop(), _offset, flat_in_array(),
               _instance_id, nullptr, _inline_depth);
 }
 
 const TypePtr* TypeInstPtr::with_inline_depth(int depth) const {
   if (!UseInlineDepthForSpeculativeTypes) {
     return this;
   }
-  return make(_ptr, klass(), _interfaces, klass_is_exact(), const_oop(), _offset, _instance_id, _speculative, depth);
+  return make(_ptr, klass(), _interfaces, klass_is_exact(), const_oop(), _offset, flat_in_array(), _instance_id, _speculative, depth);
 }
 
 const TypePtr* TypeInstPtr::with_instance_id(int instance_id) const {
   assert(is_known_instance(), "should be known");
-  return make(_ptr, klass(), _interfaces, klass_is_exact(), const_oop(), _offset, instance_id, _speculative, _inline_depth);
+  return make(_ptr, klass(), _interfaces, klass_is_exact(), const_oop(), _offset, flat_in_array(), instance_id, _speculative, _inline_depth);
+}
+
+const TypeInstPtr *TypeInstPtr::cast_to_flat_in_array() const {
+  return make(_ptr, klass(), _interfaces, klass_is_exact(), const_oop(), _offset, true, _instance_id, _speculative, _inline_depth);
 }
 
 const TypeKlassPtr* TypeInstPtr::as_klass_type(bool try_for_exact) const {
   bool xk = klass_is_exact();
   ciInstanceKlass* ik = klass()->as_instance_klass();
@@ -4615,11 +4812,11 @@
       Dependencies* deps = C->dependencies();
       deps->assert_leaf_type(ik);
       xk = true;
     }
   }
-  return TypeInstKlassPtr::make(xk ? TypePtr::Constant : TypePtr::NotNull, klass(), _interfaces, 0);
+  return TypeInstKlassPtr::make(xk ? TypePtr::Constant : TypePtr::NotNull, klass(), _interfaces, Offset(0), flat_in_array());
 }
 
 template <class T1, class T2> bool TypePtr::is_meet_subtype_of_helper_for_instance(const T1* this_one, const T2* other, bool this_xk, bool other_xk) {
   static_assert(std::is_base_of<T2, T1>::value, "");
 
@@ -4660,11 +4857,10 @@
   const TypePtr* other_elem = other_ary->elem()->make_ptr();
   const TypePtr* this_elem = this_one->elem()->make_ptr();
   if (other_elem != nullptr && this_elem != nullptr) {
     return this_one->is_reference_type(this_elem)->is_meet_subtype_of_helper(this_one->is_reference_type(other_elem), this_xk, other_xk);
   }
-
   if (other_elem == nullptr && this_elem == nullptr) {
     return this_one->klass()->is_subtype_of(other->klass());
   }
 
   return false;
@@ -4692,27 +4888,31 @@
 const TypeAryPtr *TypeAryPtr::CHARS;
 const TypeAryPtr *TypeAryPtr::INTS;
 const TypeAryPtr *TypeAryPtr::LONGS;
 const TypeAryPtr *TypeAryPtr::FLOATS;
 const TypeAryPtr *TypeAryPtr::DOUBLES;
+const TypeAryPtr *TypeAryPtr::INLINES;
 
 //------------------------------make-------------------------------------------
-const TypeAryPtr *TypeAryPtr::make(PTR ptr, const TypeAry *ary, ciKlass* k, bool xk, int offset,
+const TypeAryPtr* TypeAryPtr::make(PTR ptr, const TypeAry *ary, ciKlass* k, bool xk, Offset offset, Offset field_offset,
                                    int instance_id, const TypePtr* speculative, int inline_depth) {
   assert(!(k == nullptr && ary->_elem->isa_int()),
          "integral arrays must be pre-equipped with a class");
   if (!xk)  xk = ary->ary_must_be_exact();
   assert(instance_id <= 0 || xk, "instances are always exactly typed");
   if (k != nullptr && k->is_loaded() && k->is_obj_array_klass() &&
       k->as_obj_array_klass()->base_element_klass()->is_interface()) {
     k = nullptr;
   }
-  return (TypeAryPtr*)(new TypeAryPtr(ptr, nullptr, ary, k, xk, offset, instance_id, false, speculative, inline_depth))->hashcons();
+  if (k != nullptr && k->is_flat_array_klass() && !ary->_flat) {
+    k = nullptr;
+  }
+  return (TypeAryPtr*)(new TypeAryPtr(ptr, nullptr, ary, k, xk, offset, field_offset, instance_id, false, speculative, inline_depth))->hashcons();
 }
 
 //------------------------------make-------------------------------------------
-const TypeAryPtr *TypeAryPtr::make(PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, int offset,
+const TypeAryPtr* TypeAryPtr::make(PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, Offset offset, Offset field_offset,
                                    int instance_id, const TypePtr* speculative, int inline_depth,
                                    bool is_autobox_cache) {
   assert(!(k == nullptr && ary->_elem->isa_int()),
          "integral arrays must be pre-equipped with a class");
   assert( (ptr==Constant && o) || (ptr!=Constant && !o), "" );
@@ -4720,31 +4920,34 @@
   assert(instance_id <= 0 || xk, "instances are always exactly typed");
   if (k != nullptr && k->is_loaded() && k->is_obj_array_klass() &&
       k->as_obj_array_klass()->base_element_klass()->is_interface()) {
     k = nullptr;
   }
-  return (TypeAryPtr*)(new TypeAryPtr(ptr, o, ary, k, xk, offset, instance_id, is_autobox_cache, speculative, inline_depth))->hashcons();
+  if (k != nullptr && k->is_flat_array_klass() && !ary->_flat) {
+    k = nullptr;
+  }
+  return (TypeAryPtr*)(new TypeAryPtr(ptr, o, ary, k, xk, offset, field_offset, instance_id, is_autobox_cache, speculative, inline_depth))->hashcons();
 }
 
 //------------------------------cast_to_ptr_type-------------------------------
 const TypeAryPtr* TypeAryPtr::cast_to_ptr_type(PTR ptr) const {
   if( ptr == _ptr ) return this;
-  return make(ptr, ptr == Constant ? const_oop() : nullptr, _ary, klass(), klass_is_exact(), _offset, _instance_id, _speculative, _inline_depth);
+  return make(ptr, ptr == Constant ? const_oop() : nullptr, _ary, klass(), klass_is_exact(), _offset, _field_offset, _instance_id, _speculative, _inline_depth, _is_autobox_cache);
 }
 
 
 //-----------------------------cast_to_exactness-------------------------------
 const TypeAryPtr* TypeAryPtr::cast_to_exactness(bool klass_is_exact) const {
   if( klass_is_exact == _klass_is_exact ) return this;
   if (_ary->ary_must_be_exact())  return this;  // cannot clear xk
-  return make(ptr(), const_oop(), _ary, klass(), klass_is_exact, _offset, _instance_id, _speculative, _inline_depth);
+  return make(ptr(), const_oop(), _ary, klass(), klass_is_exact, _offset, _field_offset, _instance_id, _speculative, _inline_depth, _is_autobox_cache);
 }
 
 //-----------------------------cast_to_instance_id----------------------------
 const TypeAryPtr* TypeAryPtr::cast_to_instance_id(int instance_id) const {
   if( instance_id == _instance_id ) return this;
-  return make(_ptr, const_oop(), _ary, klass(), _klass_is_exact, _offset, instance_id, _speculative, _inline_depth);
+  return make(_ptr, const_oop(), _ary, klass(), _klass_is_exact, _offset, _field_offset, instance_id, _speculative, _inline_depth, _is_autobox_cache);
 }
 
 
 //-----------------------------max_array_length-------------------------------
 // A wrapper around arrayOopDesc::max_array_length(etype) with some input normalization.
@@ -4796,12 +4999,72 @@
 //-------------------------------cast_to_size----------------------------------
 const TypeAryPtr* TypeAryPtr::cast_to_size(const TypeInt* new_size) const {
   assert(new_size != nullptr, "");
   new_size = narrow_size_type(new_size);
   if (new_size == size())  return this;
-  const TypeAry* new_ary = TypeAry::make(elem(), new_size, is_stable());
-  return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _instance_id, _speculative, _inline_depth);
+  const TypeAry* new_ary = TypeAry::make(elem(), new_size, is_stable(), is_flat(), is_not_flat(), is_not_null_free());
+  return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _field_offset, _instance_id, _speculative, _inline_depth, _is_autobox_cache);
+}
+
+//-------------------------------cast_to_not_flat------------------------------
+const TypeAryPtr* TypeAryPtr::cast_to_not_flat(bool not_flat) const {
+  if (not_flat == is_not_flat()) {
+    return this;
+  }
+  assert(!not_flat || !is_flat(), "inconsistency");
+  const TypeAry* new_ary = TypeAry::make(elem(), size(), is_stable(), is_flat(), not_flat, is_not_null_free());
+  const TypeAryPtr* res = make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _field_offset, _instance_id, _speculative, _inline_depth, _is_autobox_cache);
+  // We keep the speculative part if it contains information about flat-/nullability.
+  // Make sure it's removed if it's not better than the non-speculative type anymore.
+  if (res->speculative() == res->remove_speculative()) {
+    return res->remove_speculative();
+  }
+  return res;
+}
+
+//-------------------------------cast_to_not_null_free-------------------------
+const TypeAryPtr* TypeAryPtr::cast_to_not_null_free(bool not_null_free) const {
+  if (not_null_free == is_not_null_free()) {
+    return this;
+  }
+  assert(!not_null_free || !is_flat(), "inconsistency");
+  const TypeAry* new_ary = TypeAry::make(elem(), size(), is_stable(), is_flat(), /* not_flat= */ not_null_free ? true : is_not_flat(), not_null_free);
+  const TypeAryPtr* res = make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _field_offset,
+                               _instance_id, _speculative, _inline_depth, _is_autobox_cache);
+  // We keep the speculative part if it contains information about flat-/nullability.
+  // Make sure it's removed if it's not better than the non-speculative type anymore.
+  if (res->speculative() == res->remove_speculative()) {
+    return res->remove_speculative();
+  }
+  return res;
+}
+
+//---------------------------------update_properties---------------------------
+const TypeAryPtr* TypeAryPtr::update_properties(const TypeAryPtr* from) const {
+  if ((from->is_flat()          && is_not_flat()) ||
+      (from->is_not_flat()      && is_flat()) ||
+      (from->is_null_free()     && is_not_null_free()) ||
+      (from->is_not_null_free() && is_null_free())) {
+    return nullptr; // Inconsistent properties
+  } else if (from->is_not_null_free()) {
+    return cast_to_not_null_free(); // Implies not flat
+  } else if (from->is_not_flat()) {
+    return cast_to_not_flat();
+  }
+  return this;
+}
+
+jint TypeAryPtr::flat_layout_helper() const {
+  return klass()->as_flat_array_klass()->layout_helper();
+}
+
+int TypeAryPtr::flat_elem_size() const {
+  return klass()->as_flat_array_klass()->element_byte_size();
+}
+
+int TypeAryPtr::flat_log_elem_size() const {
+  return klass()->as_flat_array_klass()->log2_element_size();
 }
 
 //------------------------------cast_to_stable---------------------------------
 const TypeAryPtr* TypeAryPtr::cast_to_stable(bool stable, int stable_dimension) const {
   if (stable_dimension <= 0 || (stable_dimension == 1 && stable == this->is_stable()))
@@ -4813,13 +5076,13 @@
   if (stable_dimension > 1 && elem_ptr != nullptr && elem_ptr->isa_aryptr()) {
     // If this is widened from a narrow oop, TypeAry::make will re-narrow it.
     elem = elem_ptr = elem_ptr->is_aryptr()->cast_to_stable(stable, stable_dimension - 1);
   }
 
-  const TypeAry* new_ary = TypeAry::make(elem, size(), stable);
+  const TypeAry* new_ary = TypeAry::make(elem, size(), stable, is_flat(), is_not_flat(), is_not_null_free());
 
-  return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _instance_id, _speculative, _inline_depth);
+  return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _field_offset, _instance_id, _speculative, _inline_depth, _is_autobox_cache);
 }
 
 //-----------------------------stable_dimension--------------------------------
 int TypeAryPtr::stable_dimension() const {
   if (!is_stable())  return 0;
@@ -4835,27 +5098,28 @@
   if (is_autobox_cache())  return this;
   const TypeOopPtr* etype = elem()->make_oopptr();
   if (etype == nullptr)  return this;
   // The pointers in the autobox arrays are always non-null.
   etype = etype->cast_to_ptr_type(TypePtr::NotNull)->is_oopptr();
-  const TypeAry* new_ary = TypeAry::make(etype, size(), is_stable());
-  return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _instance_id, _speculative, _inline_depth, /*is_autobox_cache=*/true);
+  const TypeAry* new_ary = TypeAry::make(etype, size(), is_stable(), is_flat(), is_not_flat(), is_not_null_free());
+  return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _field_offset, _instance_id, _speculative, _inline_depth, /*is_autobox_cache=*/true);
 }
 
 //------------------------------eq---------------------------------------------
 // Structural equality check for Type representations
 bool TypeAryPtr::eq( const Type *t ) const {
   const TypeAryPtr *p = t->is_aryptr();
   return
     _ary == p->_ary &&  // Check array
-    TypeOopPtr::eq(p);  // Check sub-parts
+    TypeOopPtr::eq(p) &&// Check sub-parts
+    _field_offset == p->_field_offset;
 }
 
 //------------------------------hash-------------------------------------------
 // Type-specific hashing function.
 uint TypeAryPtr::hash(void) const {
-  return (uint)(uintptr_t)_ary + TypeOopPtr::hash();
+  return (uint)(uintptr_t)_ary + TypeOopPtr::hash() + _field_offset.get();
 }
 
 bool TypeAryPtr::is_java_subtype_of_helper(const TypeOopPtr* other, bool this_exact, bool other_exact) const {
   return TypePtr::is_java_subtype_of_helper_for_array(this, other, this_exact, other_exact);
 }
@@ -4895,20 +5159,20 @@
     typerr(t);
 
   case OopPtr: {                // Meeting to OopPtrs
     // Found a OopPtr type vs self-AryPtr type
     const TypeOopPtr *tp = t->is_oopptr();
-    int offset = meet_offset(tp->offset());
+    Offset offset = meet_offset(tp->offset());
     PTR ptr = meet_ptr(tp->ptr());
     int depth = meet_inline_depth(tp->inline_depth());
     const TypePtr* speculative = xmeet_speculative(tp);
     switch (tp->ptr()) {
     case TopPTR:
     case AnyNull: {
       int instance_id = meet_instance_id(InstanceTop);
       return make(ptr, (ptr == Constant ? const_oop() : nullptr),
-                  _ary, _klass, _klass_is_exact, offset, instance_id, speculative, depth);
+                  _ary, _klass, _klass_is_exact, offset, _field_offset, instance_id, speculative, depth);
     }
     case BotPTR:
     case NotNull: {
       int instance_id = meet_instance_id(tp->instance_id());
       return TypeOopPtr::make(ptr, offset, instance_id, speculative, depth);
@@ -4918,11 +5182,11 @@
   }
 
   case AnyPtr: {                // Meeting two AnyPtrs
     // Found an AnyPtr type vs self-AryPtr type
     const TypePtr *tp = t->is_ptr();
-    int offset = meet_offset(tp->offset());
+    Offset offset = meet_offset(tp->offset());
     PTR ptr = meet_ptr(tp->ptr());
     const TypePtr* speculative = xmeet_speculative(tp);
     int depth = meet_inline_depth(tp->inline_depth());
     switch (tp->ptr()) {
     case TopPTR:
@@ -4934,11 +5198,11 @@
       if( ptr == Null ) return TypePtr::make(AnyPtr, ptr, offset, speculative, depth);
       // else fall through to AnyNull
     case AnyNull: {
       int instance_id = meet_instance_id(InstanceTop);
       return make(ptr, (ptr == Constant ? const_oop() : nullptr),
-                  _ary, _klass, _klass_is_exact, offset, instance_id, speculative, depth);
+                  _ary, _klass, _klass_is_exact, offset, _field_offset, instance_id, speculative, depth);
     }
     default: ShouldNotReachHere();
     }
   }
 
@@ -4948,22 +5212,40 @@
   case AryKlassPtr:
   case RawPtr: return TypePtr::BOTTOM;
 
   case AryPtr: {                // Meeting 2 references?
     const TypeAryPtr *tap = t->is_aryptr();
-    int off = meet_offset(tap->offset());
+    Offset off = meet_offset(tap->offset());
+    Offset field_off = meet_field_offset(tap->field_offset());
     const TypeAry *tary = _ary->meet_speculative(tap->_ary)->is_ary();
     PTR ptr = meet_ptr(tap->ptr());
     int instance_id = meet_instance_id(tap->instance_id());
     const TypePtr* speculative = xmeet_speculative(tap);
     int depth = meet_inline_depth(tap->inline_depth());
 
     ciKlass* res_klass = nullptr;
     bool res_xk = false;
+    bool res_flat = false;
+    bool res_not_flat = false;
+    bool res_not_null_free = false;
     const Type* elem = tary->_elem;
-    if (meet_aryptr(ptr, elem, this, tap, res_klass, res_xk) == NOT_SUBTYPE) {
+    if (meet_aryptr(ptr, elem, this, tap, res_klass, res_xk, res_flat, res_not_flat, res_not_null_free) == NOT_SUBTYPE) {
       instance_id = InstanceBot;
+    } else if (this->is_flat() != tap->is_flat()) {
+      // Meeting flat inline type array with non-flat array. Adjust (field) offset accordingly.
+      if (tary->_flat) {
+        // Result is in a flat representation
+        off = Offset(is_flat() ? offset() : tap->offset());
+        field_off = is_flat() ? field_offset() : tap->field_offset();
+      } else if (below_centerline(ptr)) {
+        // Result is in a non-flat representation
+        off = Offset(flat_offset()).meet(Offset(tap->flat_offset()));
+        field_off = (field_off == Offset::top) ? Offset::top : Offset::bottom;
+      } else if (flat_offset() == tap->flat_offset()) {
+        off = Offset(!is_flat() ? offset() : tap->offset());
+        field_off = !is_flat() ? field_offset() : tap->field_offset();
+      }
     }
 
     ciObject* o = nullptr;             // Assume not constant when done
     ciObject* this_oop = const_oop();
     ciObject* tap_oop = tap->const_oop();
@@ -4977,17 +5259,17 @@
         o = this_oop;
       } else {
         ptr = NotNull;
       }
     }
-    return make(ptr, o, TypeAry::make(elem, tary->_size, tary->_stable), res_klass, res_xk, off, instance_id, speculative, depth);
+    return make(ptr, o, TypeAry::make(elem, tary->_size, tary->_stable, res_flat, res_not_flat, res_not_null_free), res_klass, res_xk, off, field_off, instance_id, speculative, depth);
   }
 
   // All arrays inherit from Object class
   case InstPtr: {
     const TypeInstPtr *tp = t->is_instptr();
-    int offset = meet_offset(tp->offset());
+    Offset offset = meet_offset(tp->offset());
     PTR ptr = meet_ptr(tp->ptr());
     int instance_id = meet_instance_id(tp->instance_id());
     const TypePtr* speculative = xmeet_speculative(tp);
     int depth = meet_inline_depth(tp->inline_depth());
     const TypeInterfaces* interfaces = meet_interfaces(tp);
@@ -4998,18 +5280,18 @@
     case TopPTR:
     case AnyNull:                // Fall 'down' to dual of object klass
       // For instances when a subclass meets a superclass we fall
       // below the centerline when the superclass is exact. We need to
       // do the same here.
-      if (tp->klass()->equals(ciEnv::current()->Object_klass()) && this_interfaces->contains(tp_interfaces) && !tp->klass_is_exact()) {
-        return TypeAryPtr::make(ptr, _ary, _klass, _klass_is_exact, offset, instance_id, speculative, depth);
+      if (tp->klass()->equals(ciEnv::current()->Object_klass()) && this_interfaces->contains(tp_interfaces) && !tp->klass_is_exact() && !tp->flat_in_array()) {
+        return TypeAryPtr::make(ptr, _ary, _klass, _klass_is_exact, offset, _field_offset, instance_id, speculative, depth);
       } else {
         // cannot subclass, so the meet has to fall badly below the centerline
         ptr = NotNull;
         instance_id = InstanceBot;
         interfaces = this_interfaces->intersection_with(tp_interfaces);
-        return TypeInstPtr::make(ptr, ciEnv::current()->Object_klass(), interfaces, false, nullptr,offset, instance_id, speculative, depth);
+        return TypeInstPtr::make(ptr, ciEnv::current()->Object_klass(), interfaces, false, nullptr, offset, false, instance_id, speculative, depth);
       }
     case Constant:
     case NotNull:
     case BotPTR:                // Fall down to object klass
       // LCA is object_klass, but if we subclass from the top we can do better
@@ -5017,14 +5299,14 @@
         // If 'tp'  is above the centerline and it is Object class
         // then we can subclass in the Java class hierarchy.
         // For instances when a subclass meets a superclass we fall
         // below the centerline when the superclass is exact. We need
         // to do the same here.
-        if (tp->klass()->equals(ciEnv::current()->Object_klass()) && this_interfaces->contains(tp_interfaces) && !tp->klass_is_exact()) {
+        if (tp->klass()->equals(ciEnv::current()->Object_klass()) && this_interfaces->contains(tp_interfaces) && !tp->klass_is_exact() && !tp->flat_in_array()) {
           // that is, my array type is a subtype of 'tp' klass
           return make(ptr, (ptr == Constant ? const_oop() : nullptr),
-                      _ary, _klass, _klass_is_exact, offset, instance_id, speculative, depth);
+                      _ary, _klass, _klass_is_exact, offset, _field_offset, instance_id, speculative, depth);
         }
       }
       // The other case cannot happen, since t cannot be a subtype of an array.
       // The meet falls down to Object class below centerline.
       if (ptr == Constant) {
@@ -5032,38 +5314,48 @@
       }
       if (instance_id > 0) {
         instance_id = InstanceBot;
       }
       interfaces = this_interfaces->intersection_with(tp_interfaces);
-      return TypeInstPtr::make(ptr, ciEnv::current()->Object_klass(), interfaces, false, nullptr, offset, instance_id, speculative, depth);
+      return TypeInstPtr::make(ptr, ciEnv::current()->Object_klass(), interfaces, false, nullptr, offset, false, instance_id, speculative, depth);
     default: typerr(t);
     }
   }
   }
   return this;                  // Lint noise
 }
 
 
-template<class T> TypePtr::MeetResult TypePtr::meet_aryptr(PTR& ptr, const Type*& elem, const T* this_ary,
-                                                           const T* other_ary, ciKlass*& res_klass, bool& res_xk) {
+template<class T> TypePtr::MeetResult TypePtr::meet_aryptr(PTR& ptr, const Type*& elem, const T* this_ary, const T* other_ary,
+                                                           ciKlass*& res_klass, bool& res_xk, bool &res_flat, bool& res_not_flat, bool& res_not_null_free) {
   int dummy;
   bool this_top_or_bottom = (this_ary->base_element_type(dummy) == Type::TOP || this_ary->base_element_type(dummy) == Type::BOTTOM);
   bool other_top_or_bottom = (other_ary->base_element_type(dummy) == Type::TOP || other_ary->base_element_type(dummy) == Type::BOTTOM);
   ciKlass* this_klass = this_ary->klass();
   ciKlass* other_klass = other_ary->klass();
   bool this_xk = this_ary->klass_is_exact();
   bool other_xk = other_ary->klass_is_exact();
   PTR this_ptr = this_ary->ptr();
   PTR other_ptr = other_ary->ptr();
+  bool this_flat = this_ary->is_flat();
+  bool this_not_flat = this_ary->is_not_flat();
+  bool other_flat = other_ary->is_flat();
+  bool other_not_flat = other_ary->is_not_flat();
+  bool this_not_null_free = this_ary->is_not_null_free();
+  bool other_not_null_free = other_ary->is_not_null_free();
   res_klass = nullptr;
   MeetResult result = SUBTYPE;
+  res_flat = this_flat && other_flat;
+  res_not_flat = this_not_flat && other_not_flat;
+  res_not_null_free = this_not_null_free && other_not_null_free;
+
   if (elem->isa_int()) {
     // Integral array element types have irrelevant lattice relations.
     // It is the klass that determines array layout, not the element type.
-    if (this_top_or_bottom)
-      res_klass = other_klass;
-    else if (other_top_or_bottom || other_klass == this_klass) {
+      if (this_top_or_bottom) {
+        res_klass = other_klass;
+      } else if (other_top_or_bottom || other_klass == this_klass) {
       res_klass = this_klass;
     } else {
       // Something like byte[int+] meets char[int+].
       // This must fall to bottom, not (int[-128..65535])[int+].
       // instance_id = InstanceBot;
@@ -5101,35 +5393,51 @@
     case AnyNull:
     case TopPTR:
       // Compute new klass on demand, do not use tap->_klass
       if (below_centerline(this_ptr)) {
         res_xk = this_xk;
+        if (this_ary->is_flat()) {
+          elem = this_ary->elem();
+        }
       } else {
         res_xk = (other_xk || this_xk);
       }
-      return result;
+      break;
     case Constant: {
       if (this_ptr == Constant) {
         res_xk = true;
-      } else if(above_centerline(this_ptr)) {
+      } else if (above_centerline(this_ptr)) {
         res_xk = true;
       } else {
         // Only precise for identical arrays
         res_xk = this_xk && (this_ary->is_same_java_type_as(other_ary) || (this_top_or_bottom && other_top_or_bottom));
+        // TODO 8325106 Fix comment
+        // Even if MyValue is exact, [LMyValue is not exact due to [QMyValue <: [LMyValue.
+        if (res_xk && !res_not_null_free) {
+          res_xk = false;
+        }
       }
-      return result;
+      break;
     }
     case NotNull:
     case BotPTR:
       // Compute new klass on demand, do not use tap->_klass
       if (above_centerline(this_ptr)) {
         res_xk = other_xk;
+        if (other_ary->is_flat()) {
+          elem = other_ary->elem();
+        }
       } else {
         res_xk = (other_xk && this_xk) &&
                  (this_ary->is_same_java_type_as(other_ary) || (this_top_or_bottom && other_top_or_bottom)); // Only precise for identical arrays
+        // TODO 8325106 Fix comment
+        // Even if MyValue is exact, [LMyValue is not exact due to [QMyValue <: [LMyValue.
+        if (res_xk && !res_not_null_free) {
+          res_xk = false;
+        }
       }
-      return result;
+      break;
     default:  {
       ShouldNotReachHere();
       return result;
     }
   }
@@ -5138,11 +5446,20 @@
 
 
 //------------------------------xdual------------------------------------------
 // Dual: compute field-by-field dual
 const Type *TypeAryPtr::xdual() const {
-  return new TypeAryPtr(dual_ptr(), _const_oop, _ary->dual()->is_ary(),_klass, _klass_is_exact, dual_offset(), dual_instance_id(), is_autobox_cache(), dual_speculative(), dual_inline_depth());
+  return new TypeAryPtr(dual_ptr(), _const_oop, _ary->dual()->is_ary(), _klass, _klass_is_exact, dual_offset(), dual_field_offset(), dual_instance_id(), is_autobox_cache(), dual_speculative(), dual_inline_depth());
+}
+
+Type::Offset TypeAryPtr::meet_field_offset(const Type::Offset offset) const {
+  return _field_offset.meet(offset);
+}
+
+//------------------------------dual_offset------------------------------------
+Type::Offset TypeAryPtr::dual_field_offset() const {
+  return _field_offset.dual();
 }
 
 //------------------------------dump2------------------------------------------
 #ifndef PRODUCT
 void TypeAryPtr::dump2( Dict &d, uint depth, outputStream *st ) const {
@@ -5166,23 +5483,32 @@
     break;
   default:
     break;
   }
 
-  if( _offset != 0 ) {
+  if (is_flat()) {
+    st->print(":flat");
+    st->print("(");
+    _field_offset.dump2(st);
+    st->print(")");
+  }
+  if (is_null_free()) {
+    st->print(":null_free");
+  }
+  if (offset() != 0) {
     int header_size = objArrayOopDesc::header_size() * wordSize;
-    if( _offset == OffsetTop )       st->print("+undefined");
-    else if( _offset == OffsetBot )  st->print("+any");
-    else if( _offset < header_size ) st->print("+%d", _offset);
+    if( _offset == Offset::top )       st->print("+undefined");
+    else if( _offset == Offset::bottom )  st->print("+any");
+    else if( offset() < header_size ) st->print("+%d", offset());
     else {
       BasicType basic_elem_type = elem()->basic_type();
       if (basic_elem_type == T_ILLEGAL) {
         st->print("+any");
       } else {
         int array_base = arrayOopDesc::base_offset_in_bytes(basic_elem_type);
         int elem_size = type2aelembytes(basic_elem_type);
-        st->print("[%d]", (_offset - array_base)/elem_size);
+        st->print("[%d]", (offset() - array_base)/elem_size);
       }
     }
   }
   st->print(" *");
   if (_instance_id == InstanceTop)
@@ -5195,48 +5521,109 @@
 }
 #endif
 
 bool TypeAryPtr::empty(void) const {
   if (_ary->empty())       return true;
+  // FIXME: Does this belong here? Or in the meet code itself?
+  if (is_flat() && is_not_flat()) {
+    return true;
+  }
   return TypeOopPtr::empty();
 }
 
 //------------------------------add_offset-------------------------------------
 const TypePtr* TypeAryPtr::add_offset(intptr_t offset) const {
-  return make(_ptr, _const_oop, _ary, _klass, _klass_is_exact, xadd_offset(offset), _instance_id, add_offset_speculative(offset), _inline_depth);
+  return make(_ptr, _const_oop, _ary, _klass, _klass_is_exact, xadd_offset(offset), _field_offset, _instance_id, add_offset_speculative(offset), _inline_depth, _is_autobox_cache);
 }
 
 const TypeAryPtr* TypeAryPtr::with_offset(intptr_t offset) const {
-  return make(_ptr, _const_oop, _ary, _klass, _klass_is_exact, offset, _instance_id, with_offset_speculative(offset), _inline_depth);
+  return make(_ptr, _const_oop, _ary, _klass, _klass_is_exact, Offset(offset), _field_offset, _instance_id, with_offset_speculative(offset), _inline_depth, _is_autobox_cache);
 }
 
 const TypeAryPtr* TypeAryPtr::with_ary(const TypeAry* ary) const {
-  return make(_ptr, _const_oop, ary, _klass, _klass_is_exact, _offset, _instance_id, _speculative, _inline_depth);
+  return make(_ptr, _const_oop, ary, _klass, _klass_is_exact, _offset, _field_offset, _instance_id, _speculative, _inline_depth, _is_autobox_cache);
 }
 
 const TypeAryPtr* TypeAryPtr::remove_speculative() const {
   if (_speculative == nullptr) {
     return this;
   }
   assert(_inline_depth == InlineDepthTop || _inline_depth == InlineDepthBottom, "non speculative type shouldn't have inline depth");
-  return make(_ptr, _const_oop, _ary->remove_speculative()->is_ary(), _klass, _klass_is_exact, _offset, _instance_id, nullptr, _inline_depth);
+  return make(_ptr, _const_oop, _ary->remove_speculative()->is_ary(), _klass, _klass_is_exact, _offset, _field_offset, _instance_id, nullptr, _inline_depth, _is_autobox_cache);
+}
+
+const Type* TypeAryPtr::cleanup_speculative() const {
+  if (speculative() == nullptr) {
+    return this;
+  }
+  // Keep speculative part if it contains information about flat-/nullability
+  const TypeAryPtr* spec_aryptr = speculative()->isa_aryptr();
+  if (spec_aryptr != nullptr && !above_centerline(spec_aryptr->ptr()) &&
+      (spec_aryptr->is_not_flat() || spec_aryptr->is_not_null_free())) {
+    return this;
+  }
+  return TypeOopPtr::cleanup_speculative();
 }
 
 const TypePtr* TypeAryPtr::with_inline_depth(int depth) const {
   if (!UseInlineDepthForSpeculativeTypes) {
     return this;
   }
-  return make(_ptr, _const_oop, _ary->remove_speculative()->is_ary(), _klass, _klass_is_exact, _offset, _instance_id, _speculative, depth);
+  return make(_ptr, _const_oop, _ary->remove_speculative()->is_ary(), _klass, _klass_is_exact, _offset, _field_offset, _instance_id, _speculative, depth, _is_autobox_cache);
+}
+
+const TypeAryPtr* TypeAryPtr::with_field_offset(int offset) const {
+  return make(_ptr, _const_oop, _ary->remove_speculative()->is_ary(), _klass, _klass_is_exact, _offset, Offset(offset), _instance_id, _speculative, _inline_depth, _is_autobox_cache);
+}
+
+const TypePtr* TypeAryPtr::add_field_offset_and_offset(intptr_t offset) const {
+  int adj = 0;
+  if (is_flat() && offset != Type::OffsetBot && offset != Type::OffsetTop) {
+    if (_offset.get() != OffsetBot && _offset.get() != OffsetTop) {
+      adj = _offset.get();
+      offset += _offset.get();
+    }
+    uint header = arrayOopDesc::base_offset_in_bytes(T_OBJECT);
+    if (_field_offset.get() != OffsetBot && _field_offset.get() != OffsetTop) {
+      offset += _field_offset.get();
+      if (_offset.get() == OffsetBot || _offset.get() == OffsetTop) {
+        offset += header;
+      }
+    }
+    if (elem()->make_oopptr()->is_inlinetypeptr() && (offset >= (intptr_t)header || offset < 0)) {
+      // Try to get the field of the inline type array element we are pointing to
+      ciInlineKlass* vk = elem()->inline_klass();
+      int shift = flat_log_elem_size();
+      int mask = (1 << shift) - 1;
+      intptr_t field_offset = ((offset - header) & mask);
+      ciField* field = vk->get_field_by_offset(field_offset + vk->first_field_offset(), false);
+      if (field != nullptr) {
+        return with_field_offset(field_offset)->add_offset(offset - field_offset - adj);
+      }
+    }
+  }
+  return add_offset(offset - adj);
+}
+
+// Return offset incremented by field_offset for flat inline type arrays
+int TypeAryPtr::flat_offset() const {
+  int offset = _offset.get();
+  if (offset != Type::OffsetBot && offset != Type::OffsetTop &&
+      _field_offset != Offset::bottom && _field_offset != Offset::top) {
+    offset += _field_offset.get();
+  }
+  return offset;
 }
 
 const TypePtr* TypeAryPtr::with_instance_id(int instance_id) const {
   assert(is_known_instance(), "should be known");
-  return make(_ptr, _const_oop, _ary->remove_speculative()->is_ary(), _klass, _klass_is_exact, _offset, instance_id, _speculative, _inline_depth);
+  return make(_ptr, _const_oop, _ary->remove_speculative()->is_ary(), _klass, _klass_is_exact, _offset, _field_offset, instance_id, _speculative, _inline_depth);
 }
 
 //=============================================================================
 
+
 //------------------------------hash-------------------------------------------
 // Type-specific hashing function.
 uint TypeNarrowPtr::hash(void) const {
   return _ptrtype->hash() + 7;
 }
@@ -5323,11 +5710,10 @@
   case KlassPtr:
   case InstKlassPtr:
   case AryKlassPtr:
   case NarrowOop:
   case NarrowKlass:
-
   case Bottom:                  // Ye Olde Default
     return Type::BOTTOM;
   case Top:
     return this;
 
@@ -5407,11 +5793,11 @@
 // TRUE if Type is a singleton type, FALSE otherwise.   Singletons are simple
 // constants
 bool TypeMetadataPtr::singleton(void) const {
   // detune optimizer to not generate constant metadata + constant offset as a constant!
   // TopPTR, Null, AnyNull, Constant are all singletons
-  return (_offset == 0) && !below_centerline(_ptr);
+  return (offset() == 0) && !below_centerline(_ptr);
 }
 
 //------------------------------add_offset-------------------------------------
 const TypePtr* TypeMetadataPtr::add_offset( intptr_t offset ) const {
   return make( _ptr, _metadata, xadd_offset(offset));
@@ -5427,13 +5813,13 @@
 }
 
  //------------------------------get_con----------------------------------------
 intptr_t TypeMetadataPtr::get_con() const {
   assert( _ptr == Null || _ptr == Constant, "" );
-  assert( _offset >= 0, "" );
+  assert(offset() >= 0, "");
 
-  if (_offset != 0) {
+  if (offset() != 0) {
     // After being ported to the compiler interface, the compiler no longer
     // directly manipulates the addresses of oops.  Rather, it only has a pointer
     // to a handle at compile time.  This handle is embedded in the generated
     // code and dereferenced at the time the nmethod is made.  Until that time,
     // it is not reasonable to do arithmetic with the addresses of oops (we don't
@@ -5480,11 +5866,11 @@
     typerr(t);
 
   case AnyPtr: {
     // Found an AnyPtr type vs self-OopPtr type
     const TypePtr *tp = t->is_ptr();
-    int offset = meet_offset(tp->offset());
+    Offset offset = meet_offset(tp->offset());
     PTR ptr = meet_ptr(tp->ptr());
     switch (tp->ptr()) {
     case Null:
       if (ptr == Null)  return TypePtr::make(AnyPtr, ptr, offset, tp->speculative(), tp->inline_depth());
       // else fall through:
@@ -5508,11 +5894,11 @@
   case AryPtr:
     return TypePtr::BOTTOM;     // Oop meet raw is not well defined
 
   case MetadataPtr: {
     const TypeMetadataPtr *tp = t->is_metadataptr();
-    int offset = meet_offset(tp->offset());
+    Offset offset = meet_offset(tp->offset());
     PTR tptr = tp->ptr();
     PTR ptr = meet_ptr(tptr);
     ciMetadata* md = (tptr == TopPTR) ? metadata() : tp->metadata();
     if (tptr == TopPTR || _ptr == TopPTR ||
         metadata()->equals(tp->metadata())) {
@@ -5541,76 +5927,77 @@
 //------------------------------dump2------------------------------------------
 #ifndef PRODUCT
 void TypeMetadataPtr::dump2( Dict &d, uint depth, outputStream *st ) const {
   st->print("metadataptr:%s", ptr_msg[_ptr]);
   if( metadata() ) st->print(INTPTR_FORMAT, p2i(metadata()));
-  switch( _offset ) {
+  switch (offset()) {
   case OffsetTop: st->print("+top"); break;
   case OffsetBot: st->print("+any"); break;
   case         0: break;
-  default:        st->print("+%d",_offset); break;
+  default:        st->print("+%d",offset()); break;
   }
 }
 #endif
 
 
 //=============================================================================
 // Convenience common pre-built type.
 const TypeMetadataPtr *TypeMetadataPtr::BOTTOM;
 
-TypeMetadataPtr::TypeMetadataPtr(PTR ptr, ciMetadata* metadata, int offset):
+TypeMetadataPtr::TypeMetadataPtr(PTR ptr, ciMetadata* metadata, Offset offset):
   TypePtr(MetadataPtr, ptr, offset), _metadata(metadata) {
 }
 
 const TypeMetadataPtr* TypeMetadataPtr::make(ciMethod* m) {
-  return make(Constant, m, 0);
+  return make(Constant, m, Offset(0));
 }
 const TypeMetadataPtr* TypeMetadataPtr::make(ciMethodData* m) {
-  return make(Constant, m, 0);
+  return make(Constant, m, Offset(0));
 }
 
 //------------------------------make-------------------------------------------
 // Create a meta data constant
-const TypeMetadataPtr *TypeMetadataPtr::make(PTR ptr, ciMetadata* m, int offset) {
+const TypeMetadataPtr* TypeMetadataPtr::make(PTR ptr, ciMetadata* m, Offset offset) {
   assert(m == nullptr || !m->is_klass(), "wrong type");
   return (TypeMetadataPtr*)(new TypeMetadataPtr(ptr, m, offset))->hashcons();
 }
 
 
 const TypeKlassPtr* TypeAryPtr::as_klass_type(bool try_for_exact) const {
   const Type* elem = _ary->_elem;
   bool xk = klass_is_exact();
   if (elem->make_oopptr() != nullptr) {
     elem = elem->make_oopptr()->as_klass_type(try_for_exact);
-    if (elem->is_klassptr()->klass_is_exact()) {
+    if (elem->is_klassptr()->klass_is_exact() &&
+        // TODO 8325106 Fix comment
+        // Even if MyValue is exact, [LMyValue is not exact due to [QMyValue <: [LMyValue.
+        (is_null_free() || is_flat() || !_ary->_elem->make_oopptr()->is_inlinetypeptr())) {
       xk = true;
     }
   }
-  return TypeAryKlassPtr::make(xk ? TypePtr::Constant : TypePtr::NotNull, elem, klass(), 0);
+  return TypeAryKlassPtr::make(xk ? TypePtr::Constant : TypePtr::NotNull, elem, klass(), Offset(0), is_not_flat(), is_not_null_free(), is_null_free());
 }
 
-const TypeKlassPtr* TypeKlassPtr::make(ciKlass *klass, InterfaceHandling interface_handling) {
+const TypeKlassPtr* TypeKlassPtr::make(ciKlass* klass, InterfaceHandling interface_handling) {
   if (klass->is_instance_klass()) {
     return TypeInstKlassPtr::make(klass, interface_handling);
   }
   return TypeAryKlassPtr::make(klass, interface_handling);
 }
 
-const TypeKlassPtr* TypeKlassPtr::make(PTR ptr, ciKlass* klass, int offset, InterfaceHandling interface_handling) {
+const TypeKlassPtr* TypeKlassPtr::make(PTR ptr, ciKlass* klass, Offset offset, InterfaceHandling interface_handling) {
   if (klass->is_instance_klass()) {
     const TypeInterfaces* interfaces = TypePtr::interfaces(klass, true, true, false, interface_handling);
     return TypeInstKlassPtr::make(ptr, klass, interfaces, offset);
   }
   return TypeAryKlassPtr::make(ptr, klass, offset, interface_handling);
 }
 
-
-//------------------------------TypeKlassPtr-----------------------------------
-TypeKlassPtr::TypeKlassPtr(TYPES t, PTR ptr, ciKlass* klass, const TypeInterfaces* interfaces, int offset)
+TypeKlassPtr::TypeKlassPtr(TYPES t, PTR ptr, ciKlass* klass, const TypeInterfaces* interfaces, Offset offset)
   : TypePtr(t, ptr, offset), _klass(klass), _interfaces(interfaces) {
   assert(klass == nullptr || !klass->is_loaded() || (klass->is_instance_klass() && !klass->is_interface()) ||
-         klass->is_type_array_klass() || !klass->as_obj_array_klass()->base_element_klass()->is_interface(), "no interface here");
+         klass->is_type_array_klass() || klass->is_flat_array_klass() || !klass->as_obj_array_klass()->base_element_klass()->is_interface(), "no interface here");
 }
 
 // Is there a single ciKlass* that can represent that type?
 ciKlass* TypeKlassPtr::exact_klass_helper() const {
   assert(_klass->is_instance_klass() && !_klass->is_interface(), "No interface");
@@ -5645,11 +6032,11 @@
 // TRUE if Type is a singleton type, FALSE otherwise.   Singletons are simple
 // constants
 bool TypeKlassPtr::singleton(void) const {
   // detune optimizer to not generate constant klass + constant offset as a constant!
   // TopPTR, Null, AnyNull, Constant are all singletons
-  return (_offset == 0) && !below_centerline(_ptr);
+  return (offset() == 0) && !below_centerline(_ptr);
 }
 
 // Do not allow interface-vs.-noninterface joins to collapse to top.
 const Type *TypeKlassPtr::filter_helper(const Type *kills, bool include_speculative) const {
   // logic here mirrors the one from TypeOopPtr::filter. See comments
@@ -5677,13 +6064,13 @@
 }
 
 //------------------------------get_con----------------------------------------
 intptr_t TypeKlassPtr::get_con() const {
   assert( _ptr == Null || _ptr == Constant, "" );
-  assert( _offset >= 0, "" );
+  assert( offset() >= 0, "" );
 
-  if (_offset != 0) {
+  if (offset() != 0) {
     // After being ported to the compiler interface, the compiler no longer
     // directly manipulates the addresses of oops.  Rather, it only has a pointer
     // to a handle at compile time.  This handle is embedded in the generated
     // code and dereferenced at the time the nmethod is made.  Until that time,
     // it is not reasonable to do arithmetic with the addresses of oops (we don't
@@ -5723,17 +6110,14 @@
     if (_ptr == Constant) st->print(":exact");
     break;
   default:
     break;
   }
-
-  if (_offset) {               // Dump offset, if any
-    if (_offset == OffsetBot)      { st->print("+any"); }
-    else if (_offset == OffsetTop) { st->print("+unknown"); }
-    else                            { st->print("+%d", _offset); }
+  if (Verbose) {
+    if (isa_instklassptr() && is_instklassptr()->flat_in_array()) st->print(":flat in array");
   }
-
+  _offset.dump2(st);
   st->print(" *");
 }
 #endif
 
 //=============================================================================
@@ -5745,39 +6129,42 @@
 
 bool TypeInstKlassPtr::eq(const Type *t) const {
   const TypeKlassPtr *p = t->is_klassptr();
   return
     klass()->equals(p->klass()) &&
+    flat_in_array() == p->flat_in_array() &&
     TypeKlassPtr::eq(p);
 }
 
 uint TypeInstKlassPtr::hash(void) const {
-  return klass()->hash() + TypeKlassPtr::hash();
+  return klass()->hash() + TypeKlassPtr::hash() + (uint)flat_in_array();
 }
 
-const TypeInstKlassPtr *TypeInstKlassPtr::make(PTR ptr, ciKlass* k, const TypeInterfaces* interfaces, int offset) {
+const TypeInstKlassPtr *TypeInstKlassPtr::make(PTR ptr, ciKlass* k, const TypeInterfaces* interfaces, Offset offset, bool flat_in_array) {
+  flat_in_array = flat_in_array || k->flat_in_array();
+
   TypeInstKlassPtr *r =
-    (TypeInstKlassPtr*)(new TypeInstKlassPtr(ptr, k, interfaces, offset))->hashcons();
+    (TypeInstKlassPtr*)(new TypeInstKlassPtr(ptr, k, interfaces, offset, flat_in_array))->hashcons();
 
   return r;
 }
 
 //------------------------------add_offset-------------------------------------
 // Access internals of klass object
-const TypePtr* TypeInstKlassPtr::add_offset( intptr_t offset ) const {
-  return make( _ptr, klass(), _interfaces, xadd_offset(offset) );
+const TypePtr *TypeInstKlassPtr::add_offset( intptr_t offset ) const {
+  return make(_ptr, klass(), _interfaces, xadd_offset(offset), flat_in_array());
 }
 
 const TypeInstKlassPtr* TypeInstKlassPtr::with_offset(intptr_t offset) const {
-  return make(_ptr, klass(), _interfaces, offset);
+  return make(_ptr, klass(), _interfaces, Offset(offset), flat_in_array());
 }
 
 //------------------------------cast_to_ptr_type-------------------------------
 const TypeInstKlassPtr* TypeInstKlassPtr::cast_to_ptr_type(PTR ptr) const {
   assert(_base == InstKlassPtr, "subclass must override cast_to_ptr_type");
   if( ptr == _ptr ) return this;
-  return make(ptr, _klass, _interfaces, _offset);
+  return make(ptr, _klass, _interfaces, _offset, flat_in_array());
 }
 
 
 bool TypeInstKlassPtr::must_be_exact() const {
   if (!_klass->is_loaded())  return false;
@@ -5789,11 +6176,11 @@
 //-----------------------------cast_to_exactness-------------------------------
 const TypeKlassPtr* TypeInstKlassPtr::cast_to_exactness(bool klass_is_exact) const {
   if (klass_is_exact == (_ptr == Constant)) return this;
   if (must_be_exact()) return this;
   ciKlass* k = klass();
-  return make(klass_is_exact ? Constant : NotNull, k, _interfaces, _offset);
+  return make(klass_is_exact ? Constant : NotNull, k, _interfaces, _offset, flat_in_array());
 }
 
 
 //-----------------------------as_instance_type--------------------------------
 // Corresponding type for an instance of the given class.
@@ -5821,11 +6208,11 @@
           xk = sub->is_final();
         }
       }
     }
   }
-  return TypeInstPtr::make(TypePtr::BotPTR, k, interfaces, xk, nullptr, 0);
+  return TypeInstPtr::make(TypePtr::BotPTR, k, interfaces, xk, nullptr, Offset(0), flat_in_array() && !klass()->is_inlinetype());
 }
 
 //------------------------------xmeet------------------------------------------
 // Compute the MEET of two types, return a new Type object.
 const Type    *TypeInstKlassPtr::xmeet( const Type *t ) const {
@@ -5854,19 +6241,19 @@
     typerr(t);
 
   case AnyPtr: {                // Meeting to AnyPtrs
     // Found an AnyPtr type vs self-KlassPtr type
     const TypePtr *tp = t->is_ptr();
-    int offset = meet_offset(tp->offset());
+    Offset offset = meet_offset(tp->offset());
     PTR ptr = meet_ptr(tp->ptr());
     switch (tp->ptr()) {
     case TopPTR:
       return this;
     case Null:
       if( ptr == Null ) return TypePtr::make(AnyPtr, ptr, offset, tp->speculative(), tp->inline_depth());
     case AnyNull:
-      return make( ptr, klass(), _interfaces, offset );
+      return make(ptr, klass(), _interfaces, offset, flat_in_array());
     case BotPTR:
     case NotNull:
       return TypePtr::make(AnyPtr, ptr, offset, tp->speculative(), tp->inline_depth());
     default: typerr(t);
     }
@@ -5875,11 +6262,11 @@
   case RawPtr:
   case MetadataPtr:
   case OopPtr:
   case AryPtr:                  // Meet with AryPtr
   case InstPtr:                 // Meet with InstPtr
-    return TypePtr::BOTTOM;
+      return TypePtr::BOTTOM;
 
   //
   //             A-top         }
   //           /   |   \       }  Tops
   //       B-top A-any C-top   }
@@ -5895,34 +6282,35 @@
   //             A-bot         }
   //
 
   case InstKlassPtr: {  // Meet two KlassPtr types
     const TypeInstKlassPtr *tkls = t->is_instklassptr();
-    int  off     = meet_offset(tkls->offset());
+    Offset  off     = meet_offset(tkls->offset());
     PTR  ptr     = meet_ptr(tkls->ptr());
     const TypeInterfaces* interfaces = meet_interfaces(tkls);
 
     ciKlass* res_klass = nullptr;
     bool res_xk = false;
-    switch(meet_instptr(ptr, interfaces, this, tkls, res_klass, res_xk)) {
+    bool res_flat_in_array = false;
+    switch(meet_instptr(ptr, interfaces, this, tkls, res_klass, res_xk, res_flat_in_array)) {
       case UNLOADED:
         ShouldNotReachHere();
       case SUBTYPE:
       case NOT_SUBTYPE:
       case LCA:
       case QUICK: {
         assert(res_xk == (ptr == Constant), "");
-        const Type* res = make(ptr, res_klass, interfaces, off);
+        const Type* res = make(ptr, res_klass, interfaces, off, res_flat_in_array);
         return res;
       }
       default:
         ShouldNotReachHere();
     }
   } // End of case KlassPtr
   case AryKlassPtr: {                // All arrays inherit from Object class
     const TypeAryKlassPtr *tp = t->is_aryklassptr();
-    int offset = meet_offset(tp->offset());
+    Offset offset = meet_offset(tp->offset());
     PTR ptr = meet_ptr(tp->ptr());
     const TypeInterfaces* interfaces = meet_interfaces(tp);
     const TypeInterfaces* tp_interfaces = tp->_interfaces;
     const TypeInterfaces* this_interfaces = _interfaces;
 
@@ -5931,16 +6319,16 @@
     case AnyNull:                // Fall 'down' to dual of object klass
       // For instances when a subclass meets a superclass we fall
       // below the centerline when the superclass is exact. We need to
       // do the same here.
       if (klass()->equals(ciEnv::current()->Object_klass()) && tp_interfaces->contains(this_interfaces) && !klass_is_exact()) {
-        return TypeAryKlassPtr::make(ptr, tp->elem(), tp->klass(), offset);
+        return TypeAryKlassPtr::make(ptr, tp->elem(), tp->klass(), offset, tp->is_not_flat(), tp->is_not_null_free(), tp->is_null_free());
       } else {
         // cannot subclass, so the meet has to fall badly below the centerline
         ptr = NotNull;
         interfaces = _interfaces->intersection_with(tp->_interfaces);
-        return make(ptr, ciEnv::current()->Object_klass(), interfaces, offset);
+        return make(ptr, ciEnv::current()->Object_klass(), interfaces, offset, false);
       }
     case Constant:
     case NotNull:
     case BotPTR:                // Fall down to object klass
       // LCA is object_klass, but if we subclass from the top we can do better
@@ -5950,20 +6338,19 @@
         // For instances when a subclass meets a superclass we fall
         // below the centerline when the superclass is exact. We need
         // to do the same here.
         if (klass()->equals(ciEnv::current()->Object_klass()) && tp_interfaces->contains(this_interfaces) && !klass_is_exact()) {
           // that is, tp's array type is a subtype of my klass
-          return TypeAryKlassPtr::make(ptr,
-                                       tp->elem(), tp->klass(), offset);
+          return TypeAryKlassPtr::make(ptr, tp->elem(), tp->klass(), offset, tp->is_not_flat(), tp->is_not_null_free(), tp->is_null_free());
         }
       }
       // The other case cannot happen, since I cannot be a subtype of an array.
       // The meet falls down to Object class below centerline.
       if( ptr == Constant )
          ptr = NotNull;
       interfaces = this_interfaces->intersection_with(tp_interfaces);
-      return make(ptr, ciEnv::current()->Object_klass(), interfaces, offset);
+      return make(ptr, ciEnv::current()->Object_klass(), interfaces, offset, false);
     default: typerr(t);
     }
   }
 
   } // End of switch
@@ -5971,11 +6358,11 @@
 }
 
 //------------------------------xdual------------------------------------------
 // Dual: compute field-by-field dual
 const Type    *TypeInstKlassPtr::xdual() const {
-  return new TypeInstKlassPtr(dual_ptr(), klass(), _interfaces, dual_offset());
+  return new TypeInstKlassPtr(dual_ptr(), klass(), _interfaces, dual_offset(), flat_in_array());
 }
 
 template <class T1, class T2> bool TypePtr::is_java_subtype_of_helper_for_instance(const T1* this_one, const T2* other, bool this_exact, bool other_exact) {
   static_assert(std::is_base_of<T2, T1>::value, "");
   if (!this_one->is_loaded() || !other->is_loaded()) {
@@ -6072,48 +6459,88 @@
     }
   }
   return this;
 }
 
+bool TypeInstKlassPtr::can_be_inline_array() const {
+  return _klass->equals(ciEnv::current()->Object_klass()) && TypeAryKlassPtr::_array_interfaces->contains(_interfaces);
+}
+
+bool TypeAryKlassPtr::can_be_inline_array() const {
+  return _elem->isa_instklassptr() && _elem->is_instklassptr()->_klass->can_be_inline_klass();
+}
 
-const TypeAryKlassPtr *TypeAryKlassPtr::make(PTR ptr, const Type* elem, ciKlass* k, int offset) {
-  return (TypeAryKlassPtr*)(new TypeAryKlassPtr(ptr, elem, k, offset))->hashcons();
+bool TypeInstPtr::can_be_inline_array() const {
+  return _klass->equals(ciEnv::current()->Object_klass()) && TypeAryPtr::_array_interfaces->contains(_interfaces);
 }
 
-const TypeAryKlassPtr *TypeAryKlassPtr::make(PTR ptr, ciKlass* k, int offset, InterfaceHandling interface_handling) {
+bool TypeAryPtr::can_be_inline_array() const {
+  return elem()->make_ptr() && elem()->make_ptr()->isa_instptr() && elem()->make_ptr()->is_instptr()->_klass->can_be_inline_klass();
+}
+
+const TypeAryKlassPtr *TypeAryKlassPtr::make(PTR ptr, const Type* elem, ciKlass* k, Offset offset, bool not_flat, bool not_null_free, bool null_free) {
+  return (TypeAryKlassPtr*)(new TypeAryKlassPtr(ptr, elem, k, offset, not_flat, not_null_free, null_free))->hashcons();
+}
+
+const TypeAryKlassPtr* TypeAryKlassPtr::make(PTR ptr, ciKlass* k, Offset offset, InterfaceHandling interface_handling, bool not_flat, bool not_null_free, bool null_free) {
   if (k->is_obj_array_klass()) {
     // Element is an object array. Recursively call ourself.
     ciKlass* eklass = k->as_obj_array_klass()->element_klass();
-    const TypeKlassPtr *etype = TypeKlassPtr::make(eklass, interface_handling)->cast_to_exactness(false);
-    return TypeAryKlassPtr::make(ptr, etype, nullptr, offset);
+    const TypeKlassPtr* etype = TypeKlassPtr::make(eklass, interface_handling)->cast_to_exactness(false);
+    // TODO 8325106 Fix comment
+    // Even if MyValue is exact, [LMyValue is not exact due to [QMyValue <: [LMyValue.
+    if (etype->klass_is_exact() && etype->isa_instklassptr() && etype->is_instklassptr()->klass()->is_inlinetype() && !null_free) {
+      etype = TypeInstKlassPtr::make(NotNull, etype->is_instklassptr()->klass(), Offset(etype->is_instklassptr()->offset()));
+    }
+    return TypeAryKlassPtr::make(ptr, etype, nullptr, offset, not_flat, not_null_free, null_free);
   } else if (k->is_type_array_klass()) {
     // Element is an typeArray
     const Type* etype = get_const_basic_type(k->as_type_array_klass()->element_type());
-    return TypeAryKlassPtr::make(ptr, etype, k, offset);
+    return TypeAryKlassPtr::make(ptr, etype, k, offset, not_flat, not_null_free, null_free);
+  } else if (k->is_flat_array_klass()) {
+    ciKlass* eklass = k->as_flat_array_klass()->element_klass();
+    const TypeKlassPtr* etype = TypeKlassPtr::make(eklass);
+    return TypeAryKlassPtr::make(ptr, etype, k, offset, not_flat, not_null_free, null_free);
   } else {
     ShouldNotReachHere();
     return nullptr;
   }
 }
 
+const TypeAryKlassPtr* TypeAryKlassPtr::make(PTR ptr, ciKlass* k, Offset offset, InterfaceHandling interface_handling) {
+  // TODO 8325106 remove?
+  bool null_free = k->as_array_klass()->is_elem_null_free();
+  bool not_null_free = (ptr == Constant) ? !null_free : !k->is_flat_array_klass() && (k->is_type_array_klass() || !k->as_array_klass()->element_klass()->can_be_inline_klass(false));
+
+  bool not_flat = !UseFlatArray || not_null_free || (k->as_array_klass()->element_klass() != nullptr &&
+                                                     k->as_array_klass()->element_klass()->is_inlinetype() &&
+                                                     !k->as_array_klass()->element_klass()->flat_in_array());
+
+  return TypeAryKlassPtr::make(ptr, k, offset, interface_handling, not_flat, not_null_free, null_free);
+}
+
 const TypeAryKlassPtr* TypeAryKlassPtr::make(ciKlass* klass, InterfaceHandling interface_handling) {
-  return TypeAryKlassPtr::make(Constant, klass, 0, interface_handling);
+  return TypeAryKlassPtr::make(Constant, klass, Offset(0), interface_handling);
 }
 
 //------------------------------eq---------------------------------------------
 // Structural equality check for Type representations
 bool TypeAryKlassPtr::eq(const Type *t) const {
   const TypeAryKlassPtr *p = t->is_aryklassptr();
   return
     _elem == p->_elem &&  // Check array
+    _not_flat == p->_not_flat &&
+    _not_null_free == p->_not_null_free &&
+    _null_free == p->_null_free &&
     TypeKlassPtr::eq(p);  // Check sub-parts
 }
 
 //------------------------------hash-------------------------------------------
 // Type-specific hashing function.
 uint TypeAryKlassPtr::hash(void) const {
-  return (uint)(uintptr_t)_elem + TypeKlassPtr::hash();
+  return (uint)(uintptr_t)_elem + TypeKlassPtr::hash() + (uint)(_not_flat ? 43 : 0) +
+      (uint)(_not_null_free ? 44 : 0) + (uint)(_null_free ? 45 : 0);
 }
 
 //----------------------compute_klass------------------------------------------
 // Compute the defining klass for this class
 ciKlass* TypeAryPtr::compute_klass() const {
@@ -6125,14 +6552,19 @@
   if (el->isa_narrowoop()) {
     el = el->make_ptr();
   }
 
   // Get element klass
-  if ((tinst = el->isa_instptr()) != nullptr) {
-    // Leave k_ary at null.
+  if (is_flat() && el->is_inlinetypeptr()) {
+    // Klass is required by TypeAryPtr::flat_layout_helper() and others
+    if (el->inline_klass() != nullptr) {
+      k_ary = ciArrayKlass::make(el->inline_klass(), /* null_free */ true);
+    }
+  } else if ((tinst = el->isa_instptr()) != nullptr) {
+    // Leave k_ary at nullptr.
   } else if ((tary = el->isa_aryptr()) != nullptr) {
-    // Leave k_ary at null.
+    // Leave k_ary at nullptr.
   } else if ((el->base() == Type::Top) ||
              (el->base() == Type::Bottom)) {
     // element type of Bottom occurs from meet of basic type
     // and object; Top occurs when doing join on Bottom.
     // Leave k_ary at null.
@@ -6176,11 +6608,11 @@
   if (_ary->_elem->make_ptr() && _ary->_elem->make_ptr()->isa_oopptr()) {
     ciKlass* k = _ary->_elem->make_ptr()->is_oopptr()->exact_klass_helper();
     if (k == nullptr) {
       return nullptr;
     }
-    k = ciObjArrayKlass::make(k);
+    k = ciArrayKlass::make(k, is_null_free());
     return k;
   }
 
   return klass();
 }
@@ -6196,44 +6628,70 @@
 }
 
 //------------------------------add_offset-------------------------------------
 // Access internals of klass object
 const TypePtr* TypeAryKlassPtr::add_offset(intptr_t offset) const {
-  return make(_ptr, elem(), klass(), xadd_offset(offset));
+  return make(_ptr, elem(), klass(), xadd_offset(offset), is_not_flat(), is_not_null_free(), _null_free);
 }
 
 const TypeAryKlassPtr* TypeAryKlassPtr::with_offset(intptr_t offset) const {
-  return make(_ptr, elem(), klass(), offset);
+  return make(_ptr, elem(), klass(), Offset(offset), is_not_flat(), is_not_null_free(), _null_free);
 }
 
 //------------------------------cast_to_ptr_type-------------------------------
 const TypeAryKlassPtr* TypeAryKlassPtr::cast_to_ptr_type(PTR ptr) const {
   assert(_base == AryKlassPtr, "subclass must override cast_to_ptr_type");
   if (ptr == _ptr) return this;
-  return make(ptr, elem(), _klass, _offset);
+  return make(ptr, elem(), _klass, _offset, is_not_flat(), is_not_null_free(), _null_free);
 }
 
 bool TypeAryKlassPtr::must_be_exact() const {
   if (_elem == Type::BOTTOM) return false;
   if (_elem == Type::TOP   ) return false;
   const TypeKlassPtr*  tk = _elem->isa_klassptr();
   if (!tk)             return true;   // a primitive type, like int
+  // TODO 8325106 Fix comment
+  // Even if MyValue is exact, [LMyValue is not exact due to [QMyValue <: [LMyValue.
+  if (tk->isa_instklassptr() && tk->klass()->is_inlinetype() && !is_null_free()) {
+    return false;
+  }
   return tk->must_be_exact();
 }
 
 
 //-----------------------------cast_to_exactness-------------------------------
 const TypeKlassPtr *TypeAryKlassPtr::cast_to_exactness(bool klass_is_exact) const {
-  if (must_be_exact()) return this;  // cannot clear xk
+  if (must_be_exact() && !klass_is_exact) return this;  // cannot clear xk
+  if (klass_is_exact == this->klass_is_exact()) {
+    return this;
+  }
   ciKlass* k = _klass;
   const Type* elem = this->elem();
   if (elem->isa_klassptr() && !klass_is_exact) {
     elem = elem->is_klassptr()->cast_to_exactness(klass_is_exact);
   }
-  return make(klass_is_exact ? Constant : NotNull, elem, k, _offset);
+  bool not_flat = is_not_flat();
+  bool not_null_free = is_not_null_free();
+  if (_elem->isa_klassptr()) {
+    if (klass_is_exact || _elem->isa_aryklassptr()) {
+      assert(!is_null_free() && !is_flat(), "null-free (or flat) inline type arrays should always be exact");
+      // An array can't be null-free (or flat) if the klass is exact
+      not_null_free = true;
+      not_flat = true;
+    } else {
+      // Klass is not exact (anymore), re-compute null-free/flat properties
+      const TypeOopPtr* exact_etype = TypeOopPtr::make_from_klass_unique(_elem->is_instklassptr()->instance_klass());
+      not_null_free = !exact_etype->can_be_inline_type();
+      not_flat = !UseFlatArray || not_null_free || (exact_etype->is_inlinetypeptr() && !exact_etype->inline_klass()->flat_in_array());
+    }
+  }
+  return make(klass_is_exact ? Constant : NotNull, elem, k, _offset, not_flat, not_null_free, _null_free);
 }
 
+const TypeAryKlassPtr* TypeAryKlassPtr::cast_to_null_free() const {
+  return make(_ptr, elem(), klass(), _offset, is_not_flat(), false, true);
+}
 
 //-----------------------------as_instance_type--------------------------------
 // Corresponding type for an instance of the given class.
 // It will be NotNull, and exact if and only if the klass type is exact.
 const TypeOopPtr* TypeAryKlassPtr::as_instance_type(bool klass_change) const {
@@ -6244,11 +6702,15 @@
     el = elem()->is_klassptr()->as_instance_type(false)->cast_to_exactness(false);
     k = nullptr;
   } else {
     el = elem();
   }
-  return TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(el, TypeInt::POS), k, xk, 0);
+  bool null_free = _null_free;
+  if (null_free && el->isa_ptr()) {
+    el = el->is_ptr()->join_speculative(TypePtr::NOTNULL);
+  }
+  return TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(el, TypeInt::POS, false, is_flat(), is_not_flat(), is_not_null_free()), k, xk, Offset(0));
 }
 
 
 //------------------------------xmeet------------------------------------------
 // Compute the MEET of two types, return a new Type object.
@@ -6278,19 +6740,19 @@
     typerr(t);
 
   case AnyPtr: {                // Meeting to AnyPtrs
     // Found an AnyPtr type vs self-KlassPtr type
     const TypePtr *tp = t->is_ptr();
-    int offset = meet_offset(tp->offset());
+    Offset offset = meet_offset(tp->offset());
     PTR ptr = meet_ptr(tp->ptr());
     switch (tp->ptr()) {
     case TopPTR:
       return this;
     case Null:
       if( ptr == Null ) return TypePtr::make(AnyPtr, ptr, offset, tp->speculative(), tp->inline_depth());
     case AnyNull:
-      return make( ptr, _elem, klass(), offset );
+      return make(ptr, _elem, klass(), offset, is_not_flat(), is_not_null_free(), is_null_free());
     case BotPTR:
     case NotNull:
       return TypePtr::make(AnyPtr, ptr, offset, tp->speculative(), tp->inline_depth());
     default: typerr(t);
     }
@@ -6319,23 +6781,38 @@
   //             A-bot         }
   //
 
   case AryKlassPtr: {  // Meet two KlassPtr types
     const TypeAryKlassPtr *tap = t->is_aryklassptr();
-    int off = meet_offset(tap->offset());
+    Offset off = meet_offset(tap->offset());
     const Type* elem = _elem->meet(tap->_elem);
-
     PTR ptr = meet_ptr(tap->ptr());
     ciKlass* res_klass = nullptr;
     bool res_xk = false;
-    meet_aryptr(ptr, elem, this, tap, res_klass, res_xk);
+    bool res_flat = false;
+    bool res_not_flat = false;
+    bool res_not_null_free = false;
+    MeetResult res = meet_aryptr(ptr, elem, this, tap,
+                                 res_klass, res_xk, res_flat, res_not_flat, res_not_null_free);
     assert(res_xk == (ptr == Constant), "");
-    return make(ptr, elem, res_klass, off);
+    bool null_free = meet_null_free(tap->_null_free);
+    if (res == NOT_SUBTYPE) {
+      null_free = false;
+    } else if (res == SUBTYPE) {
+      if (above_centerline(tap->ptr()) && !above_centerline(this->ptr())) {
+        null_free = _null_free;
+      } else if (above_centerline(this->ptr()) && !above_centerline(tap->ptr())) {
+        null_free = tap->_null_free;
+      } else if (above_centerline(this->ptr()) && above_centerline(tap->ptr())) {
+        null_free = _null_free || tap->_null_free;
+      }
+    }
+    return make(ptr, elem, res_klass, off, res_not_flat, res_not_null_free, null_free);
   } // End of case KlassPtr
   case InstKlassPtr: {
     const TypeInstKlassPtr *tp = t->is_instklassptr();
-    int offset = meet_offset(tp->offset());
+    Offset offset = meet_offset(tp->offset());
     PTR ptr = meet_ptr(tp->ptr());
     const TypeInterfaces* interfaces = meet_interfaces(tp);
     const TypeInterfaces* tp_interfaces = tp->_interfaces;
     const TypeInterfaces* this_interfaces = _interfaces;
 
@@ -6344,16 +6821,16 @@
     case AnyNull:                // Fall 'down' to dual of object klass
       // For instances when a subclass meets a superclass we fall
       // below the centerline when the superclass is exact. We need to
       // do the same here.
       if (tp->klass()->equals(ciEnv::current()->Object_klass()) && this_interfaces->intersection_with(tp_interfaces)->eq(tp_interfaces) && !tp->klass_is_exact()) {
-        return TypeAryKlassPtr::make(ptr, _elem, _klass, offset);
+        return TypeAryKlassPtr::make(ptr, _elem, _klass, offset, is_not_flat(), is_not_null_free(), is_null_free());
       } else {
         // cannot subclass, so the meet has to fall badly below the centerline
         ptr = NotNull;
         interfaces = this_interfaces->intersection_with(tp->_interfaces);
-        return TypeInstKlassPtr::make(ptr, ciEnv::current()->Object_klass(), interfaces, offset);
+        return TypeInstKlassPtr::make(ptr, ciEnv::current()->Object_klass(), interfaces, offset, false);
       }
     case Constant:
     case NotNull:
     case BotPTR:                // Fall down to object klass
       // LCA is object_klass, but if we subclass from the top we can do better
@@ -6363,19 +6840,19 @@
         // For instances when a subclass meets a superclass we fall
         // below the centerline when the superclass is exact. We need
         // to do the same here.
         if (tp->klass()->equals(ciEnv::current()->Object_klass()) && this_interfaces->intersection_with(tp_interfaces)->eq(tp_interfaces) && !tp->klass_is_exact()) {
           // that is, my array type is a subtype of 'tp' klass
-          return make(ptr, _elem, _klass, offset);
+          return make(ptr, _elem, _klass, offset, is_not_flat(), is_not_null_free(), is_null_free());
         }
       }
       // The other case cannot happen, since t cannot be a subtype of an array.
       // The meet falls down to Object class below centerline.
       if (ptr == Constant)
          ptr = NotNull;
       interfaces = this_interfaces->intersection_with(tp_interfaces);
-      return TypeInstKlassPtr::make(ptr, ciEnv::current()->Object_klass(), interfaces, offset);
+      return TypeInstKlassPtr::make(ptr, ciEnv::current()->Object_klass(), interfaces, offset, false);
     default: typerr(t);
     }
   }
 
   } // End of switch
@@ -6408,10 +6885,14 @@
   }
 
   const TypePtr* other_elem = other_ary->elem()->make_ptr();
   const TypePtr* this_elem = this_one->elem()->make_ptr();
   if (this_elem != nullptr && other_elem != nullptr) {
+    if (other->is_null_free() && !this_one->is_null_free()) {
+      // TODO 8325106 Fix comment
+      return false; // [LMyValue is not a subtype of [QMyValue
+    }
     return this_one->is_reference_type(this_elem)->is_java_subtype_of_helper(this_one->is_reference_type(other_elem), this_exact, other_exact);
   }
   if (this_elem == nullptr && other_elem == nullptr) {
     return this_one->klass()->is_subtype_of(other->klass());
   }
@@ -6494,21 +6975,21 @@
 }
 
 //------------------------------xdual------------------------------------------
 // Dual: compute field-by-field dual
 const Type    *TypeAryKlassPtr::xdual() const {
-  return new TypeAryKlassPtr(dual_ptr(), elem()->dual(), klass(), dual_offset());
+  return new TypeAryKlassPtr(dual_ptr(), elem()->dual(), klass(), dual_offset(), !is_not_flat(), !is_not_null_free(), dual_null_free());
 }
 
 // Is there a single ciKlass* that can represent that type?
 ciKlass* TypeAryKlassPtr::exact_klass_helper() const {
   if (elem()->isa_klassptr()) {
     ciKlass* k = elem()->is_klassptr()->exact_klass_helper();
     if (k == nullptr) {
       return nullptr;
     }
-    k = ciObjArrayKlass::make(k);
+    k = ciArrayKlass::make(k, _null_free);
     return k;
   }
 
   return klass();
 }
@@ -6551,17 +7032,19 @@
     if( _ptr == Constant ) st->print(":exact");
     break;
   default:
     break;
   }
-
-  if( _offset ) {               // Dump offset, if any
-    if( _offset == OffsetBot )      { st->print("+any"); }
-    else if( _offset == OffsetTop ) { st->print("+unknown"); }
-    else                            { st->print("+%d", _offset); }
+  if (is_flat()) st->print(":flat");
+  if (_null_free) st->print(":null free");
+  if (Verbose) {
+    if (_not_flat) st->print(":not flat");
+    if (_not_null_free) st->print(":not null free");
   }
 
+  _offset.dump2(st);
+
   st->print(" *");
 }
 #endif
 
 const Type* TypeAryKlassPtr::base_element_type(int& dims) const {
@@ -6576,28 +7059,54 @@
 
 //=============================================================================
 // Convenience common pre-built types.
 
 //------------------------------make-------------------------------------------
-const TypeFunc *TypeFunc::make( const TypeTuple *domain, const TypeTuple *range ) {
-  return (TypeFunc*)(new TypeFunc(domain,range))->hashcons();
+const TypeFunc *TypeFunc::make(const TypeTuple *domain_sig, const TypeTuple* domain_cc,
+                               const TypeTuple *range_sig, const TypeTuple *range_cc) {
+  return (TypeFunc*)(new TypeFunc(domain_sig, domain_cc, range_sig, range_cc))->hashcons();
+}
+
+const TypeFunc *TypeFunc::make(const TypeTuple *domain, const TypeTuple *range) {
+  return make(domain, domain, range, range);
+}
+
+//------------------------------osr_domain-----------------------------
+const TypeTuple* osr_domain() {
+  const Type **fields = TypeTuple::fields(2);
+  fields[TypeFunc::Parms+0] = TypeRawPtr::BOTTOM;  // address of osr buffer
+  return TypeTuple::make(TypeFunc::Parms+1, fields);
 }
 
 //------------------------------make-------------------------------------------
-const TypeFunc *TypeFunc::make(ciMethod* method) {
+const TypeFunc* TypeFunc::make(ciMethod* method, bool is_osr_compilation) {
   Compile* C = Compile::current();
-  const TypeFunc* tf = C->last_tf(method); // check cache
-  if (tf != nullptr)  return tf;  // The hit rate here is almost 50%.
-  const TypeTuple *domain;
-  if (method->is_static()) {
-    domain = TypeTuple::make_domain(nullptr, method->signature(), ignore_interfaces);
-  } else {
-    domain = TypeTuple::make_domain(method->holder(), method->signature(), ignore_interfaces);
+  const TypeFunc* tf = nullptr;
+  if (!is_osr_compilation) {
+    tf = C->last_tf(method); // check cache
+    if (tf != nullptr)  return tf;  // The hit rate here is almost 50%.
+  }
+  // Inline types are not passed/returned by reference, instead each field of
+  // the inline type is passed/returned as an argument. We maintain two views of
+  // the argument/return list here: one based on the signature (with an inline
+  // type argument/return as a single slot), one based on the actual calling
+  // convention (with an inline type argument/return as a list of its fields).
+  bool has_scalar_args = method->has_scalarized_args() && !is_osr_compilation;
+  // Fall back to the non-scalarized calling convention when compiling a call via a mismatching method
+  if (method != C->method() && method->get_Method()->mismatch()) {
+    has_scalar_args = false;
+  }
+  const TypeTuple* domain_sig = is_osr_compilation ? osr_domain() : TypeTuple::make_domain(method, ignore_interfaces, false);
+  const TypeTuple* domain_cc = has_scalar_args ? TypeTuple::make_domain(method, ignore_interfaces, true) : domain_sig;
+  ciSignature* sig = method->signature();
+  bool has_scalar_ret = sig->return_type()->is_inlinetype() && sig->return_type()->as_inline_klass()->can_be_returned_as_fields();
+  const TypeTuple* range_sig = TypeTuple::make_range(sig, ignore_interfaces, false);
+  const TypeTuple* range_cc = has_scalar_ret ? TypeTuple::make_range(sig, ignore_interfaces, true) : range_sig;
+  tf = TypeFunc::make(domain_sig, domain_cc, range_sig, range_cc);
+  if (!is_osr_compilation) {
+    C->set_last_tf(method, tf);  // fill cache
   }
-  const TypeTuple *range  = TypeTuple::make_range(method->signature(), ignore_interfaces);
-  tf = TypeFunc::make(domain, range);
-  C->set_last_tf(method, tf);  // fill cache
   return tf;
 }
 
 //------------------------------meet-------------------------------------------
 // Compute the MEET of two types.  It returns a new Type object.
@@ -6628,46 +7137,48 @@
 
 //------------------------------eq---------------------------------------------
 // Structural equality check for Type representations
 bool TypeFunc::eq( const Type *t ) const {
   const TypeFunc *a = (const TypeFunc*)t;
-  return _domain == a->_domain &&
-    _range == a->_range;
+  return _domain_sig == a->_domain_sig &&
+    _domain_cc == a->_domain_cc &&
+    _range_sig == a->_range_sig &&
+    _range_cc == a->_range_cc;
 }
 
 //------------------------------hash-------------------------------------------
 // Type-specific hashing function.
 uint TypeFunc::hash(void) const {
-  return (uint)(uintptr_t)_domain + (uint)(uintptr_t)_range;
+  return (uint)(intptr_t)_domain_sig + (uint)(intptr_t)_domain_cc + (uint)(intptr_t)_range_sig + (uint)(intptr_t)_range_cc;
 }
 
 //------------------------------dump2------------------------------------------
 // Dump Function Type
 #ifndef PRODUCT
 void TypeFunc::dump2( Dict &d, uint depth, outputStream *st ) const {
-  if( _range->cnt() <= Parms )
+  if( _range_sig->cnt() <= Parms )
     st->print("void");
   else {
     uint i;
-    for (i = Parms; i < _range->cnt()-1; i++) {
-      _range->field_at(i)->dump2(d,depth,st);
+    for (i = Parms; i < _range_sig->cnt()-1; i++) {
+      _range_sig->field_at(i)->dump2(d,depth,st);
       st->print("/");
     }
-    _range->field_at(i)->dump2(d,depth,st);
+    _range_sig->field_at(i)->dump2(d,depth,st);
   }
   st->print(" ");
   st->print("( ");
   if( !depth || d[this] ) {     // Check for recursive dump
     st->print("...)");
     return;
   }
   d.Insert((void*)this,(void*)this);    // Stop recursion
-  if (Parms < _domain->cnt())
-    _domain->field_at(Parms)->dump2(d,depth-1,st);
-  for (uint i = Parms+1; i < _domain->cnt(); i++) {
+  if (Parms < _domain_sig->cnt())
+    _domain_sig->field_at(Parms)->dump2(d,depth-1,st);
+  for (uint i = Parms+1; i < _domain_sig->cnt(); i++) {
     st->print(", ");
-    _domain->field_at(i)->dump2(d,depth-1,st);
+    _domain_sig->field_at(i)->dump2(d,depth-1,st);
   }
   st->print(" )");
 }
 #endif
 
@@ -6683,10 +7194,10 @@
   return false;                 // Never empty
 }
 
 
 BasicType TypeFunc::return_type() const{
-  if (range()->cnt() == TypeFunc::Parms) {
+  if (range_sig()->cnt() == TypeFunc::Parms) {
     return T_VOID;
   }
-  return range()->field_at(TypeFunc::Parms)->basic_type();
+  return range_sig()->field_at(TypeFunc::Parms)->basic_type();
 }
diff a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp
--- a/src/hotspot/share/opto/type.hpp
+++ b/src/hotspot/share/opto/type.hpp
@@ -23,12 +23,14 @@
  */
 
 #ifndef SHARE_OPTO_TYPE_HPP
 #define SHARE_OPTO_TYPE_HPP
 
+#include "ci/ciInlineKlass.hpp"
 #include "opto/adlcVMDeps.hpp"
 #include "runtime/handles.hpp"
+#include "runtime/sharedRuntime.hpp"
 
 // Portions of code courtesy of Clifford Click
 
 // Optimization - Graph Style
 
@@ -134,10 +136,34 @@
   enum OFFSET_SIGNALS {
     OffsetTop = -2000000000,    // undefined offset
     OffsetBot = -2000000001     // any possible offset
   };
 
+  class Offset {
+  private:
+    int _offset;
+
+  public:
+    explicit Offset(int offset) : _offset(offset) {}
+
+    const Offset meet(const Offset other) const;
+    const Offset dual() const;
+    const Offset add(intptr_t offset) const;
+    bool operator==(const Offset& other) const {
+      return _offset == other._offset;
+    }
+    bool operator!=(const Offset& other) const {
+      return _offset != other._offset;
+    }
+    int get() const { return _offset; }
+
+    void dump2(outputStream *st) const;
+
+    static const Offset top;
+    static const Offset bottom;
+  };
+
   // Min and max WIDEN values.
   enum WIDEN {
     WidenMin = 0,
     WidenMax = 3
   };
@@ -324,10 +350,13 @@
   const TypeAryKlassPtr   *is_aryklassptr() const;    // assert if not AryKlassPtr
 
   virtual bool      is_finite() const;           // Has a finite value
   virtual bool      is_nan()    const;           // Is not a number (NaN)
 
+  bool is_inlinetypeptr() const;
+  virtual ciInlineKlass* inline_klass() const;
+
   // Returns this ptr type or the equivalent ptr type for this compressed pointer.
   const TypePtr* make_ptr() const;
 
   // Returns this oopptr type or the equivalent oopptr type for this compressed pointer.
   // Asserts if the underlying type is not an oopptr or narrowoop.
@@ -722,12 +751,12 @@
     assert(i < _cnt, "oob");
     _fields[i] = t;
   }
 
   static const TypeTuple *make( uint cnt, const Type **fields );
-  static const TypeTuple *make_range(ciSignature *sig, InterfaceHandling interface_handling = ignore_interfaces);
-  static const TypeTuple *make_domain(ciInstanceKlass* recv, ciSignature *sig, InterfaceHandling interface_handling);
+  static const TypeTuple *make_range(ciSignature* sig, InterfaceHandling interface_handling = ignore_interfaces, bool ret_vt_fields = false);
+  static const TypeTuple *make_domain(ciMethod* method, InterfaceHandling interface_handling, bool vt_fields_as_args = false);
 
   // Subroutine call type with space allocated for argument types
   // Memory for Control, I_O, Memory, FramePtr, and ReturnAdr is allocated implicitly
   static const Type **fields( uint arg_cnt );
 
@@ -752,26 +781,33 @@
 };
 
 //------------------------------TypeAry----------------------------------------
 // Class of Array Types
 class TypeAry : public Type {
-  TypeAry(const Type* elem, const TypeInt* size, bool stable) : Type(Array),
-      _elem(elem), _size(size), _stable(stable) {}
+  TypeAry(const Type* elem, const TypeInt* size, bool stable, bool flat, bool not_flat, bool not_null_free) : Type(Array),
+      _elem(elem), _size(size), _stable(stable), _flat(flat), _not_flat(not_flat), _not_null_free(not_null_free) {}
 public:
   virtual bool eq( const Type *t ) const;
   virtual uint hash() const;             // Type specific hashing
   virtual bool singleton(void) const;    // TRUE if type is a singleton
   virtual bool empty(void) const;        // TRUE if type is vacuous
 
 private:
   const Type *_elem;            // Element type of array
   const TypeInt *_size;         // Elements in array
   const bool _stable;           // Are elements @Stable?
+
+  // Inline type array properties
+  const bool _flat;             // Array is flat
+  const bool _not_flat;         // Array is never flat
+  const bool _not_null_free;    // Array is never null-free
+
   friend class TypeAryPtr;
 
 public:
-  static const TypeAry* make(const Type* elem, const TypeInt* size, bool stable = false);
+  static const TypeAry* make(const Type* elem, const TypeInt* size, bool stable = false,
+                             bool flat = false, bool not_flat = false, bool not_null_free = false);
 
   virtual const Type *xmeet( const Type *t ) const;
   virtual const Type *xdual() const;    // Compute dual right now.
   bool ary_must_be_exact() const;  // true if arrays of such are never generic
   virtual const TypeAry* remove_speculative() const;
@@ -928,11 +964,11 @@
   static const TypeInterfaces* interfaces(ciKlass*& k, bool klass, bool interface, bool array, InterfaceHandling interface_handling);
 
 public:
   enum PTR { TopPTR, AnyNull, Constant, Null, NotNull, BotPTR, lastPTR };
 protected:
-  TypePtr(TYPES t, PTR ptr, int offset,
+  TypePtr(TYPES t, PTR ptr, Offset offset,
           const TypePtr* speculative = nullptr,
           int inline_depth = InlineDepthBottom) :
     Type(t), _speculative(speculative), _inline_depth(inline_depth), _offset(offset),
     _ptr(ptr) {}
   static const PTR ptr_meet[lastPTR][lastPTR];
@@ -984,51 +1020,55 @@
     SUBTYPE,
     NOT_SUBTYPE,
     LCA
   };
   template<class T> static TypePtr::MeetResult meet_instptr(PTR& ptr, const TypeInterfaces*& interfaces, const T* this_type,
-                                                            const T* other_type, ciKlass*& res_klass, bool& res_xk);
+                                                            const T* other_type, ciKlass*& res_klass, bool& res_xk, bool& res_flat_array);
+ private:
+  template<class T> static bool is_meet_subtype_of(const T* sub_type, const T* super_type);
+ protected:
 
   template<class T> static MeetResult meet_aryptr(PTR& ptr, const Type*& elem, const T* this_ary, const T* other_ary,
-                                                  ciKlass*& res_klass, bool& res_xk);
+                                                  ciKlass*& res_klass, bool& res_xk, bool &res_flat, bool &res_not_flat, bool &res_not_null_free);
 
   template <class T1, class T2> static bool is_java_subtype_of_helper_for_instance(const T1* this_one, const T2* other, bool this_exact, bool other_exact);
   template <class T1, class T2> static bool is_same_java_type_as_helper_for_instance(const T1* this_one, const T2* other);
   template <class T1, class T2> static bool maybe_java_subtype_of_helper_for_instance(const T1* this_one, const T2* other, bool this_exact, bool other_exact);
   template <class T1, class T2> static bool is_java_subtype_of_helper_for_array(const T1* this_one, const T2* other, bool this_exact, bool other_exact);
   template <class T1, class T2> static bool is_same_java_type_as_helper_for_array(const T1* this_one, const T2* other);
   template <class T1, class T2> static bool maybe_java_subtype_of_helper_for_array(const T1* this_one, const T2* other, bool this_exact, bool other_exact);
   template <class T1, class T2> static bool is_meet_subtype_of_helper_for_instance(const T1* this_one, const T2* other, bool this_xk, bool other_xk);
   template <class T1, class T2> static bool is_meet_subtype_of_helper_for_array(const T1* this_one, const T2* other, bool this_xk, bool other_xk);
 public:
-  const int _offset;            // Offset into oop, with TOP & BOT
+  const Offset _offset;         // Offset into oop, with TOP & BOT
   const PTR _ptr;               // Pointer equivalence class
 
-  int offset() const { return _offset; }
+  int offset() const { return _offset.get(); }
   PTR ptr()    const { return _ptr; }
 
-  static const TypePtr *make(TYPES t, PTR ptr, int offset,
+  static const TypePtr* make(TYPES t, PTR ptr, Offset offset,
                              const TypePtr* speculative = nullptr,
                              int inline_depth = InlineDepthBottom);
 
   // Return a 'ptr' version of this type
   virtual const TypePtr* cast_to_ptr_type(PTR ptr) const;
 
   virtual intptr_t get_con() const;
 
-  int xadd_offset( intptr_t offset ) const;
+  Type::Offset xadd_offset(intptr_t offset) const;
   virtual const TypePtr* add_offset(intptr_t offset) const;
   virtual const TypePtr* with_offset(intptr_t offset) const;
+  virtual int flat_offset() const { return offset(); }
   virtual bool eq(const Type *t) const;
   virtual uint hash() const;             // Type specific hashing
 
   virtual bool singleton(void) const;    // TRUE if type is a singleton
   virtual bool empty(void) const;        // TRUE if type is vacuous
   virtual const Type *xmeet( const Type *t ) const;
   virtual const Type *xmeet_helper( const Type *t ) const;
-  int meet_offset( int offset ) const;
-  int dual_offset( ) const;
+  Offset meet_offset(int offset) const;
+  Offset dual_offset() const;
   virtual const Type *xdual() const;    // Compute dual right now.
 
   // meet, dual and join over pointer equivalence sets
   PTR meet_ptr( const PTR in_ptr ) const { return ptr_meet[in_ptr][ptr()]; }
   PTR dual_ptr()                   const { return ptr_dual[ptr()];      }
@@ -1052,10 +1092,18 @@
   virtual bool would_improve_ptr(ProfilePtrKind maybe_null) const;
   virtual const TypePtr* with_inline_depth(int depth) const;
 
   virtual bool maybe_null() const { return meet_ptr(Null) == ptr(); }
 
+  virtual bool can_be_inline_type() const { return false; }
+  virtual bool flat_in_array()      const { return false; }
+  virtual bool not_flat_in_array()  const { return true; }
+  virtual bool is_flat()            const { return false; }
+  virtual bool is_not_flat()        const { return false; }
+  virtual bool is_null_free()       const { return false; }
+  virtual bool is_not_null_free()   const { return false; }
+
   // Tests for relation to centerline of type lattice:
   static bool above_centerline(PTR ptr) { return (ptr <= AnyNull); }
   static bool below_centerline(PTR ptr) { return (ptr >= NotNull); }
   // Convenience common pre-built types.
   static const TypePtr *NULL_PTR;
@@ -1069,11 +1117,11 @@
 //------------------------------TypeRawPtr-------------------------------------
 // Class of raw pointers, pointers to things other than Oops.  Examples
 // include the stack pointer, top of heap, card-marking area, handles, etc.
 class TypeRawPtr : public TypePtr {
 protected:
-  TypeRawPtr( PTR ptr, address bits ) : TypePtr(RawPtr,ptr,0), _bits(bits){}
+  TypeRawPtr(PTR ptr, address bits) : TypePtr(RawPtr,ptr,Offset(0)), _bits(bits){}
 public:
   virtual bool eq( const Type *t ) const;
   virtual uint hash() const;    // Type specific hashing
 
   const address _bits;          // Constant value, if applicable
@@ -1105,11 +1153,11 @@
   friend class TypeAry;
   friend class TypePtr;
   friend class TypeInstPtr;
   friend class TypeAryPtr;
 protected:
- TypeOopPtr(TYPES t, PTR ptr, ciKlass* k, const TypeInterfaces* interfaces, bool xk, ciObject* o, int offset, int instance_id,
+ TypeOopPtr(TYPES t, PTR ptr, ciKlass* k, const TypeInterfaces* interfaces, bool xk, ciObject* o, Offset offset, Offset field_offset, int instance_id,
             const TypePtr* speculative, int inline_depth);
 public:
   virtual bool eq( const Type *t ) const;
   virtual uint hash() const;             // Type specific hashing
   virtual bool singleton(void) const;    // TRUE if type is a singleton
@@ -1146,11 +1194,11 @@
 
   // Do not allow interface-vs.-noninterface joins to collapse to top.
   virtual const Type *filter_helper(const Type *kills, bool include_speculative) const;
 
   virtual ciKlass* exact_klass_helper() const { return nullptr; }
-  virtual ciKlass* klass() const { return _klass;     }
+  virtual ciKlass* klass() const { return _klass; }
 
 public:
 
   bool is_java_subtype_of(const TypeOopPtr* other) const {
     return is_java_subtype_of_helper(other, klass_is_exact(), other->klass_is_exact());
@@ -1193,11 +1241,11 @@
   // If require_constant, produce a null if a singleton is not possible.
   static const TypeOopPtr* make_from_constant(ciObject* o,
                                               bool require_constant = false);
 
   // Make a generic (unclassed) pointer to an oop.
-  static const TypeOopPtr* make(PTR ptr, int offset, int instance_id,
+  static const TypeOopPtr* make(PTR ptr, Offset offset, int instance_id,
                                 const TypePtr* speculative = nullptr,
                                 int inline_depth = InlineDepthBottom);
 
   ciObject* const_oop()    const { return _const_oop; }
   // Exact klass, possibly an interface or an array of interface
@@ -1212,11 +1260,14 @@
   bool is_ptr_to_narrowoop_nv() const { return _is_ptr_to_narrowoop; }
   bool is_ptr_to_narrowklass_nv() const { return _is_ptr_to_narrowklass; }
   bool is_ptr_to_boxed_value()   const { return _is_ptr_to_boxed_value; }
   bool is_known_instance()       const { return _instance_id > 0; }
   int  instance_id()             const { return _instance_id; }
-  bool is_known_instance_field() const { return is_known_instance() && _offset >= 0; }
+  bool is_known_instance_field() const { return is_known_instance() && _offset.get() >= 0; }
+
+  virtual bool can_be_inline_type() const { return (_klass == nullptr || _klass->can_be_inline_klass(_klass_is_exact)); }
+  virtual bool can_be_inline_array() const { ShouldNotReachHere(); return false; }
 
   virtual intptr_t get_con() const;
 
   virtual const TypeOopPtr* cast_to_ptr_type(PTR ptr) const;
 
@@ -1275,15 +1326,16 @@
 
 //------------------------------TypeInstPtr------------------------------------
 // Class of Java object pointers, pointing either to non-array Java instances
 // or to a Klass* (including array klasses).
 class TypeInstPtr : public TypeOopPtr {
-  TypeInstPtr(PTR ptr, ciKlass* k, const TypeInterfaces* interfaces, bool xk, ciObject* o, int off, int instance_id,
-              const TypePtr* speculative, int inline_depth);
+  TypeInstPtr(PTR ptr, ciKlass* k, const TypeInterfaces* interfaces, bool xk, ciObject* o, Offset offset,
+              bool flat_in_array, int instance_id, const TypePtr* speculative,
+              int inline_depth);
   virtual bool eq( const Type *t ) const;
   virtual uint hash() const;             // Type specific hashing
-
+  bool _flat_in_array; // Type is flat in arrays
   ciKlass* exact_klass_helper() const;
 
 public:
 
   // Instance klass, ignoring any interface
@@ -1298,45 +1350,47 @@
 
   // Make a pointer to a constant oop.
   static const TypeInstPtr *make(ciObject* o) {
     ciKlass* k = o->klass();
     const TypeInterfaces* interfaces = TypePtr::interfaces(k, true, false, false, ignore_interfaces);
-    return make(TypePtr::Constant, k, interfaces, true, o, 0, InstanceBot);
+    return make(TypePtr::Constant, k, interfaces, true, o, Offset(0));
   }
   // Make a pointer to a constant oop with offset.
-  static const TypeInstPtr *make(ciObject* o, int offset) {
+  static const TypeInstPtr *make(ciObject* o, Offset offset) {
     ciKlass* k = o->klass();
     const TypeInterfaces* interfaces = TypePtr::interfaces(k, true, false, false, ignore_interfaces);
-    return make(TypePtr::Constant, k, interfaces, true, o, offset, InstanceBot);
+    return make(TypePtr::Constant, k, interfaces, true, o, offset);
   }
 
   // Make a pointer to some value of type klass.
   static const TypeInstPtr *make(PTR ptr, ciKlass* klass, InterfaceHandling interface_handling = ignore_interfaces) {
     const TypeInterfaces* interfaces = TypePtr::interfaces(klass, true, true, false, interface_handling);
-    return make(ptr, klass, interfaces, false, nullptr, 0, InstanceBot);
+    return make(ptr, klass, interfaces, false, nullptr, Offset(0));
   }
 
   // Make a pointer to some non-polymorphic value of exactly type klass.
   static const TypeInstPtr *make_exact(PTR ptr, ciKlass* klass) {
     const TypeInterfaces* interfaces = TypePtr::interfaces(klass, true, false, false, ignore_interfaces);
-    return make(ptr, klass, interfaces, true, nullptr, 0, InstanceBot);
+    return make(ptr, klass, interfaces, true, nullptr, Offset(0));
   }
 
   // Make a pointer to some value of type klass with offset.
-  static const TypeInstPtr *make(PTR ptr, ciKlass* klass, int offset) {
+  static const TypeInstPtr *make(PTR ptr, ciKlass* klass, Offset offset) {
     const TypeInterfaces* interfaces = TypePtr::interfaces(klass, true, false, false, ignore_interfaces);
-    return make(ptr, klass, interfaces, false, nullptr, offset, InstanceBot);
+    return make(ptr, klass, interfaces, false, nullptr, offset);
   }
 
-  static const TypeInstPtr *make(PTR ptr, ciKlass* k, const TypeInterfaces* interfaces, bool xk, ciObject* o, int offset,
+  // Make a pointer to an oop.
+  static const TypeInstPtr* make(PTR ptr, ciKlass* k, const TypeInterfaces* interfaces, bool xk, ciObject* o, Offset offset,
+                                 bool flat_in_array = false,
                                  int instance_id = InstanceBot,
                                  const TypePtr* speculative = nullptr,
                                  int inline_depth = InlineDepthBottom);
 
-  static const TypeInstPtr *make(PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id = InstanceBot) {
+  static const TypeInstPtr *make(PTR ptr, ciKlass* k, bool xk, ciObject* o, Offset offset, int instance_id = InstanceBot) {
     const TypeInterfaces* interfaces = TypePtr::interfaces(k, true, false, false, ignore_interfaces);
-    return make(ptr, k, interfaces, xk, o, offset, instance_id);
+    return make(ptr, k, interfaces, xk, o, offset, false, instance_id);
   }
 
   /** Create constant type for a constant boxed value */
   const Type* get_const_boxed_value() const;
 
@@ -1357,17 +1411,23 @@
   // Speculative type helper methods.
   virtual const TypeInstPtr* remove_speculative() const;
   virtual const TypePtr* with_inline_depth(int depth) const;
   virtual const TypePtr* with_instance_id(int instance_id) const;
 
+  virtual const TypeInstPtr* cast_to_flat_in_array() const;
+  virtual bool flat_in_array() const { return _flat_in_array; }
+  virtual bool not_flat_in_array() const { return !can_be_inline_type() || (_klass->is_inlinetype() && !flat_in_array()); }
+
   // the core of the computation of the meet of 2 types
   virtual const Type *xmeet_helper(const Type *t) const;
   virtual const TypeInstPtr *xmeet_unloaded(const TypeInstPtr *tinst, const TypeInterfaces* interfaces) const;
   virtual const Type *xdual() const;    // Compute dual right now.
 
   const TypeKlassPtr* as_klass_type(bool try_for_exact = false) const;
 
+  virtual bool can_be_inline_array() const;
+
   // Convenience common pre-built types.
   static const TypeInstPtr *NOTNULL;
   static const TypeInstPtr *BOTTOM;
   static const TypeInstPtr *MIRROR;
   static const TypeInstPtr *MARK;
@@ -1388,32 +1448,40 @@
 //------------------------------TypeAryPtr-------------------------------------
 // Class of Java array pointers
 class TypeAryPtr : public TypeOopPtr {
   friend class Type;
   friend class TypePtr;
+  friend class TypeInstPtr;
 
-  TypeAryPtr( PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk,
-              int offset, int instance_id, bool is_autobox_cache,
-              const TypePtr* speculative, int inline_depth)
-    : TypeOopPtr(AryPtr,ptr,k,_array_interfaces,xk,o,offset, instance_id, speculative, inline_depth),
+  TypeAryPtr(PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk,
+             Offset offset, Offset field_offset, int instance_id, bool is_autobox_cache,
+             const TypePtr* speculative, int inline_depth)
+    : TypeOopPtr(AryPtr, ptr, k, _array_interfaces, xk, o, offset, field_offset, instance_id, speculative, inline_depth),
     _ary(ary),
-    _is_autobox_cache(is_autobox_cache)
+    _is_autobox_cache(is_autobox_cache),
+    _field_offset(field_offset)
  {
     int dummy;
     bool top_or_bottom = (base_element_type(dummy) == Type::TOP || base_element_type(dummy) == Type::BOTTOM);
 
     if (UseCompressedOops && (elem()->make_oopptr() != nullptr && !top_or_bottom) &&
-        _offset != 0 && _offset != arrayOopDesc::length_offset_in_bytes() &&
-        _offset != arrayOopDesc::klass_offset_in_bytes()) {
+        _offset.get() != 0 && _offset.get() != arrayOopDesc::length_offset_in_bytes() &&
+        _offset.get() != arrayOopDesc::klass_offset_in_bytes()) {
       _is_ptr_to_narrowoop = true;
     }
 
   }
   virtual bool eq( const Type *t ) const;
   virtual uint hash() const;    // Type specific hashing
   const TypeAry *_ary;          // Array we point into
   const bool     _is_autobox_cache;
+  // For flat inline type arrays, each field of the inline type in
+  // the array has its own memory slice so we need to keep track of
+  // which field is accessed
+  const Offset _field_offset;
+  Offset meet_field_offset(const Type::Offset offset) const;
+  Offset dual_field_offset() const;
 
   ciKlass* compute_klass() const;
 
   // A pointer to delay allocation to Type::Initialize_shared()
 
@@ -1437,21 +1505,30 @@
   const TypeAry* ary() const  { return _ary; }
   const Type*    elem() const { return _ary->_elem; }
   const TypeInt* size() const { return _ary->_size; }
   bool      is_stable() const { return _ary->_stable; }
 
+  // Inline type array properties
+  bool is_flat()          const { return _ary->_flat; }
+  bool is_not_flat()      const { return _ary->_not_flat; }
+  bool is_null_free()     const { return is_flat() || (_ary->_elem->make_ptr() != nullptr && _ary->_elem->make_ptr()->is_inlinetypeptr() && (_ary->_elem->make_ptr()->ptr() == NotNull || _ary->_elem->make_ptr()->ptr() == AnyNull)); }
+  bool is_not_null_free() const { return _ary->_not_null_free; }
+
   bool is_autobox_cache() const { return _is_autobox_cache; }
 
-  static const TypeAryPtr *make(PTR ptr, const TypeAry *ary, ciKlass* k, bool xk, int offset,
+  static const TypeAryPtr* make(PTR ptr, const TypeAry *ary, ciKlass* k, bool xk, Offset offset,
+                                Offset field_offset = Offset::bottom,
                                 int instance_id = InstanceBot,
                                 const TypePtr* speculative = nullptr,
                                 int inline_depth = InlineDepthBottom);
   // Constant pointer to array
-  static const TypeAryPtr *make(PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, int offset,
+  static const TypeAryPtr* make(PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, Offset offset,
+                                Offset field_offset = Offset::bottom,
                                 int instance_id = InstanceBot,
                                 const TypePtr* speculative = nullptr,
-                                int inline_depth = InlineDepthBottom, bool is_autobox_cache = false);
+                                int inline_depth = InlineDepthBottom,
+                                bool is_autobox_cache = false);
 
   // Return a 'ptr' version of this type
   virtual const TypeAryPtr* cast_to_ptr_type(PTR ptr) const;
 
   virtual const TypeAryPtr* cast_to_exactness(bool klass_is_exact) const;
@@ -1466,25 +1543,43 @@
   virtual const TypeAryPtr *with_offset( intptr_t offset ) const;
   const TypeAryPtr* with_ary(const TypeAry* ary) const;
 
   // Speculative type helper methods.
   virtual const TypeAryPtr* remove_speculative() const;
+  virtual const Type* cleanup_speculative() const;
   virtual const TypePtr* with_inline_depth(int depth) const;
   virtual const TypePtr* with_instance_id(int instance_id) const;
 
   // the core of the computation of the meet of 2 types
   virtual const Type *xmeet_helper(const Type *t) const;
   virtual const Type *xdual() const;    // Compute dual right now.
 
+  // Inline type array properties
+  const TypeAryPtr* cast_to_not_flat(bool not_flat = true) const;
+  const TypeAryPtr* cast_to_not_null_free(bool not_null_free = true) const;
+  const TypeAryPtr* update_properties(const TypeAryPtr* new_type) const;
+  jint flat_layout_helper() const;
+  int flat_elem_size() const;
+  int flat_log_elem_size() const;
+
   const TypeAryPtr* cast_to_stable(bool stable, int stable_dimension = 1) const;
   int stable_dimension() const;
 
   const TypeAryPtr* cast_to_autobox_cache() const;
 
-  static jint max_array_length(BasicType etype) ;
+  static jint max_array_length(BasicType etype);
+
+  int flat_offset() const;
+  const Offset field_offset() const { return _field_offset; }
+  const TypeAryPtr* with_field_offset(int offset) const;
+  const TypePtr* add_field_offset_and_offset(intptr_t offset) const;
+
+  virtual bool can_be_inline_type() const { return false; }
   virtual const TypeKlassPtr* as_klass_type(bool try_for_exact = false) const;
 
+  virtual bool can_be_inline_array() const;
+
   // Convenience common pre-built types.
   static const TypeAryPtr *RANGE;
   static const TypeAryPtr *OOPS;
   static const TypeAryPtr *NARROWOOPS;
   static const TypeAryPtr *BYTES;
@@ -1492,10 +1587,11 @@
   static const TypeAryPtr *CHARS;
   static const TypeAryPtr *INTS;
   static const TypeAryPtr *LONGS;
   static const TypeAryPtr *FLOATS;
   static const TypeAryPtr *DOUBLES;
+  static const TypeAryPtr *INLINES;
   // selects one of the above:
   static const TypeAryPtr *get_array_body_type(BasicType elem) {
     assert((uint)elem <= T_CONFLICT && _array_body_type[elem] != nullptr, "bad elem type");
     return _array_body_type[elem];
   }
@@ -1510,11 +1606,11 @@
 
 //------------------------------TypeMetadataPtr-------------------------------------
 // Some kind of metadata, either Method*, MethodData* or CPCacheOop
 class TypeMetadataPtr : public TypePtr {
 protected:
-  TypeMetadataPtr(PTR ptr, ciMetadata* metadata, int offset);
+  TypeMetadataPtr(PTR ptr, ciMetadata* metadata, Offset offset);
   // Do not allow interface-vs.-noninterface joins to collapse to top.
   virtual const Type *filter_helper(const Type *kills, bool include_speculative) const;
 public:
   virtual bool eq( const Type *t ) const;
   virtual uint hash() const;             // Type specific hashing
@@ -1522,11 +1618,11 @@
 
 private:
   ciMetadata*   _metadata;
 
 public:
-  static const TypeMetadataPtr* make(PTR ptr, ciMetadata* m, int offset);
+  static const TypeMetadataPtr* make(PTR ptr, ciMetadata* m, Offset offset);
 
   static const TypeMetadataPtr* make(ciMethod* m);
   static const TypeMetadataPtr* make(ciMethodData* m);
 
   ciMetadata* metadata() const { return _metadata; }
@@ -1553,11 +1649,11 @@
 class TypeKlassPtr : public TypePtr {
   friend class TypeInstKlassPtr;
   friend class TypeAryKlassPtr;
   friend class TypePtr;
 protected:
-  TypeKlassPtr(TYPES t, PTR ptr, ciKlass* klass, const TypeInterfaces* interfaces, int offset);
+  TypeKlassPtr(TYPES t, PTR ptr, ciKlass* klass, const TypeInterfaces* interfaces, Offset offset);
 
   virtual const Type *filter_helper(const Type *kills, bool include_speculative) const;
 
 public:
   virtual bool eq( const Type *t ) const;
@@ -1592,11 +1688,11 @@
   // Exact klass, possibly an interface or an array of interface
   ciKlass* exact_klass(bool maybe_null = false) const { assert(klass_is_exact(), ""); ciKlass* k = exact_klass_helper(); assert(k != nullptr || maybe_null, ""); return k;  }
   virtual bool klass_is_exact()    const { return _ptr == Constant; }
 
   static const TypeKlassPtr* make(ciKlass* klass, InterfaceHandling interface_handling = ignore_interfaces);
-  static const TypeKlassPtr *make(PTR ptr, ciKlass* klass, int offset, InterfaceHandling interface_handling = ignore_interfaces);
+  static const TypeKlassPtr *make(PTR ptr, ciKlass* klass, Offset offset, InterfaceHandling interface_handling = ignore_interfaces);
 
   virtual bool  is_loaded() const { return _klass->is_loaded(); }
 
   virtual const TypeKlassPtr* cast_to_ptr_type(PTR ptr) const { ShouldNotReachHere(); return nullptr; }
 
@@ -1611,10 +1707,11 @@
 
   virtual intptr_t get_con() const;
 
   virtual const TypeKlassPtr* with_offset(intptr_t offset) const { ShouldNotReachHere(); return nullptr; }
 
+  virtual bool can_be_inline_array() const { ShouldNotReachHere(); return false; }
   virtual const TypeKlassPtr* try_improve() const { return this; }
 
 #ifndef PRODUCT
   virtual void dump2( Dict &d, uint depth, outputStream *st ) const; // Specialized per-Type dumping
 #endif
@@ -1645,17 +1742,19 @@
 };
 
 // Instance klass pointer, mirrors TypeInstPtr
 class TypeInstKlassPtr : public TypeKlassPtr {
 
-  TypeInstKlassPtr(PTR ptr, ciKlass* klass, const TypeInterfaces* interfaces, int offset)
-    : TypeKlassPtr(InstKlassPtr, ptr, klass, interfaces, offset) {
+  TypeInstKlassPtr(PTR ptr, ciKlass* klass, const TypeInterfaces* interfaces, Offset offset, bool flat_in_array)
+    : TypeKlassPtr(InstKlassPtr, ptr, klass, interfaces, offset), _flat_in_array(flat_in_array) {
     assert(klass->is_instance_klass() && (!klass->is_loaded() || !klass->is_interface()), "");
   }
 
   virtual bool must_be_exact() const;
 
+  const bool _flat_in_array; // Type is flat in arrays
+
 public:
   // Instance klass ignoring any interface
   ciInstanceKlass* instance_klass() const {
     assert(!klass()->is_interface(), "");
     return klass()->as_instance_klass();
@@ -1663,17 +1762,19 @@
 
   bool is_same_java_type_as_helper(const TypeKlassPtr* other) const;
   bool is_java_subtype_of_helper(const TypeKlassPtr* other, bool this_exact, bool other_exact) const;
   bool maybe_java_subtype_of_helper(const TypeKlassPtr* other, bool this_exact, bool other_exact) const;
 
+  virtual bool can_be_inline_type() const { return (_klass == nullptr || _klass->can_be_inline_klass(klass_is_exact())); }
+
   static const TypeInstKlassPtr *make(ciKlass* k, InterfaceHandling interface_handling) {
     const TypeInterfaces* interfaces = TypePtr::interfaces(k, true, true, false, interface_handling);
-    return make(TypePtr::Constant, k, interfaces, 0);
+    return make(TypePtr::Constant, k, interfaces, Offset(0));
   }
-  static const TypeInstKlassPtr* make(PTR ptr, ciKlass* k, const TypeInterfaces* interfaces, int offset);
+  static const TypeInstKlassPtr* make(PTR ptr, ciKlass* k, const TypeInterfaces* interfaces, Offset offset, bool flat_in_array = false);
 
-  static const TypeInstKlassPtr* make(PTR ptr, ciKlass* k, int offset) {
+  static const TypeInstKlassPtr* make(PTR ptr, ciKlass* k, Offset offset) {
     const TypeInterfaces* interfaces = TypePtr::interfaces(k, true, false, false, ignore_interfaces);
     return make(ptr, k, interfaces, offset);
   }
 
   virtual const TypeInstKlassPtr* cast_to_ptr_type(PTR ptr) const;
@@ -1690,10 +1791,15 @@
   virtual const Type    *xdual() const;
   virtual const TypeInstKlassPtr* with_offset(intptr_t offset) const;
 
   virtual const TypeKlassPtr* try_improve() const;
 
+  virtual bool flat_in_array() const { return _flat_in_array; }
+  virtual bool not_flat_in_array() const { return !_klass->can_be_inline_klass() || (_klass->is_inlinetype() && !flat_in_array()); }
+
+  virtual bool can_be_inline_array() const;
+
   // Convenience common pre-built types.
   static const TypeInstKlassPtr* OBJECT; // Not-null object klass or below
   static const TypeInstKlassPtr* OBJECT_OR_NULL; // Maybe-null version of same
 private:
   virtual bool is_meet_subtype_of_helper(const TypeKlassPtr* other, bool this_xk, bool other_xk) const;
@@ -1704,37 +1810,49 @@
   friend class TypeInstKlassPtr;
   friend class Type;
   friend class TypePtr;
 
   const Type *_elem;
+  const bool _not_flat;      // Array is never flat
+  const bool _not_null_free; // Array is never null-free
+  const bool _null_free;
 
   static const TypeInterfaces* _array_interfaces;
-  TypeAryKlassPtr(PTR ptr, const Type *elem, ciKlass* klass, int offset)
-    : TypeKlassPtr(AryKlassPtr, ptr, klass, _array_interfaces, offset), _elem(elem) {
-    assert(klass == nullptr || klass->is_type_array_klass() || !klass->as_obj_array_klass()->base_element_klass()->is_interface(), "");
+  TypeAryKlassPtr(PTR ptr, const Type *elem, ciKlass* klass, Offset offset, bool not_flat, int not_null_free, bool null_free)
+    : TypeKlassPtr(AryKlassPtr, ptr, klass, _array_interfaces, offset), _elem(elem), _not_flat(not_flat), _not_null_free(not_null_free), _null_free(null_free) {
+    assert(klass == nullptr || klass->is_type_array_klass() || klass->is_flat_array_klass() || !klass->as_obj_array_klass()->base_element_klass()->is_interface(), "");
   }
 
   virtual ciKlass* exact_klass_helper() const;
   // Only guaranteed non null for array of basic types
   virtual ciKlass* klass() const;
 
   virtual bool must_be_exact() const;
 
+  bool dual_null_free() const {
+    return _null_free;
+  }
+
+  bool meet_null_free(bool other) const {
+    return _null_free && other;
+  }
+
 public:
 
   // returns base element type, an instance klass (and not interface) for object arrays
   const Type* base_element_type(int& dims) const;
 
-  static const TypeAryKlassPtr *make(PTR ptr, ciKlass* k, int offset, InterfaceHandling interface_handling);
+  static const TypeAryKlassPtr* make(PTR ptr, ciKlass* k, Offset offset, InterfaceHandling interface_handling, bool not_flat, bool not_null_free, bool null_free);
 
   bool is_same_java_type_as_helper(const TypeKlassPtr* other) const;
   bool is_java_subtype_of_helper(const TypeKlassPtr* other, bool this_exact, bool other_exact) const;
   bool maybe_java_subtype_of_helper(const TypeKlassPtr* other, bool this_exact, bool other_exact) const;
 
   bool  is_loaded() const { return (_elem->isa_klassptr() ? _elem->is_klassptr()->is_loaded() : true); }
 
-  static const TypeAryKlassPtr *make(PTR ptr, const Type *elem, ciKlass* k, int offset);
+  static const TypeAryKlassPtr* make(PTR ptr, const Type* elem, ciKlass* k, Offset offset, bool not_flat, bool not_null_free, bool null_free);
+  static const TypeAryKlassPtr* make(PTR ptr, ciKlass* k, Offset offset, InterfaceHandling interface_handling);
   static const TypeAryKlassPtr* make(ciKlass* klass, InterfaceHandling interface_handling);
 
   const Type *elem() const { return _elem; }
 
   virtual bool eq(const Type *t) const;
@@ -1742,10 +1860,12 @@
 
   virtual const TypeAryKlassPtr* cast_to_ptr_type(PTR ptr) const;
 
   virtual const TypeKlassPtr *cast_to_exactness(bool klass_is_exact) const;
 
+  const TypeAryKlassPtr* cast_to_null_free() const;
+
   // corresponding pointer to instance, for a given class
   virtual const TypeOopPtr* as_instance_type(bool klass_change = true) const;
 
   virtual const TypePtr *add_offset( intptr_t offset ) const;
   virtual const Type    *xmeet( const Type *t ) const;
@@ -1755,10 +1875,16 @@
 
   virtual bool empty(void) const {
     return TypeKlassPtr::empty() || _elem->empty();
   }
 
+  bool is_flat()          const { return klass() != nullptr && klass()->is_flat_array_klass(); }
+  bool is_not_flat()      const { return _not_flat; }
+  bool is_null_free()     const { return _null_free; }
+  bool is_not_null_free() const { return _not_null_free; }
+  virtual bool can_be_inline_array() const;
+
 #ifndef PRODUCT
   virtual void dump2( Dict &d, uint depth, outputStream *st ) const; // Specialized per-Type dumping
 #endif
 private:
   virtual bool is_meet_subtype_of_helper(const TypeKlassPtr* other, bool this_xk, bool other_xk) const;
@@ -1890,18 +2016,30 @@
 };
 
 //------------------------------TypeFunc---------------------------------------
 // Class of Array Types
 class TypeFunc : public Type {
-  TypeFunc( const TypeTuple *domain, const TypeTuple *range ) : Type(Function),  _domain(domain), _range(range) {}
+  TypeFunc(const TypeTuple *domain_sig, const TypeTuple *domain_cc, const TypeTuple *range_sig, const TypeTuple *range_cc)
+    : Type(Function), _domain_sig(domain_sig), _domain_cc(domain_cc), _range_sig(range_sig), _range_cc(range_cc) {}
   virtual bool eq( const Type *t ) const;
   virtual uint hash() const;             // Type specific hashing
   virtual bool singleton(void) const;    // TRUE if type is a singleton
   virtual bool empty(void) const;        // TRUE if type is vacuous
 
-  const TypeTuple* const _domain;     // Domain of inputs
-  const TypeTuple* const _range;      // Range of results
+  // Domains of inputs: inline type arguments are not passed by
+  // reference, instead each field of the inline type is passed as an
+  // argument. We maintain 2 views of the argument list here: one
+  // based on the signature (with an inline type argument as a single
+  // slot), one based on the actual calling convention (with a value
+  // type argument as a list of its fields).
+  const TypeTuple* const _domain_sig;
+  const TypeTuple* const _domain_cc;
+  // Range of results. Similar to domains: an inline type result can be
+  // returned in registers in which case range_cc lists all fields and
+  // is the actual calling convention.
+  const TypeTuple* const _range_sig;
+  const TypeTuple* const _range_cc;
 
 public:
   // Constants are shared among ADLC and VM
   enum { Control    = AdlcVMDeps::Control,
          I_O        = AdlcVMDeps::I_O,
@@ -1911,22 +2049,27 @@
          Parms      = AdlcVMDeps::Parms
   };
 
 
   // Accessors:
-  const TypeTuple* domain() const { return _domain; }
-  const TypeTuple* range()  const { return _range; }
-
-  static const TypeFunc *make(ciMethod* method);
-  static const TypeFunc *make(ciSignature signature, const Type* extra);
+  const TypeTuple* domain_sig() const { return _domain_sig; }
+  const TypeTuple* domain_cc()  const { return _domain_cc; }
+  const TypeTuple* range_sig()  const { return _range_sig; }
+  const TypeTuple* range_cc()   const { return _range_cc; }
+
+  static const TypeFunc* make(ciMethod* method, bool is_osr_compilation = false);
+  static const TypeFunc *make(const TypeTuple* domain_sig, const TypeTuple* domain_cc,
+                              const TypeTuple* range_sig, const TypeTuple* range_cc);
   static const TypeFunc *make(const TypeTuple* domain, const TypeTuple* range);
 
   virtual const Type *xmeet( const Type *t ) const;
   virtual const Type *xdual() const;    // Compute dual right now.
 
   BasicType return_type() const;
 
+  bool returns_inline_type_as_fields() const { return range_sig() != range_cc(); }
+
 #ifndef PRODUCT
   virtual void dump2( Dict &d, uint depth, outputStream *st ) const; // Specialized per-Type dumping
 #endif
   // Convenience common pre-built types.
 };
@@ -2178,10 +2321,18 @@
       (_base == DoubleCon) || (_base == DoubleBot) )
     return true;
   return false;
 }
 
+inline bool Type::is_inlinetypeptr() const {
+  return isa_instptr() != nullptr && is_instptr()->instance_klass()->is_inlinetype();
+}
+
+inline ciInlineKlass* Type::inline_klass() const {
+  return make_ptr()->is_instptr()->instance_klass()->as_inline_klass();
+}
+
 
 // ===============================================================
 // Things that need to be 64-bits in the 64-bit build but
 // 32-bits in the 32-bit build.  Done this way to get full
 // optimization AND strong typing.
@@ -2204,10 +2355,11 @@
 // For array index arithmetic
 #define MulXNode     MulLNode
 #define AndXNode     AndLNode
 #define OrXNode      OrLNode
 #define CmpXNode     CmpLNode
+#define CmpUXNode    CmpULNode
 #define SubXNode     SubLNode
 #define LShiftXNode  LShiftLNode
 // For object size computation:
 #define AddXNode     AddLNode
 #define RShiftXNode  RShiftLNode
@@ -2222,10 +2374,11 @@
 #define Op_AddX      Op_AddL
 #define Op_SubX      Op_SubL
 #define Op_XorX      Op_XorL
 #define Op_URShiftX  Op_URShiftL
 #define Op_LoadX     Op_LoadL
+#define Op_StoreX    Op_StoreL
 // conversions
 #define ConvI2X(x)   ConvI2L(x)
 #define ConvL2X(x)   (x)
 #define ConvX2I(x)   ConvL2I(x)
 #define ConvX2L(x)   (x)
@@ -2250,10 +2403,11 @@
 // For array index arithmetic
 #define MulXNode     MulINode
 #define AndXNode     AndINode
 #define OrXNode      OrINode
 #define CmpXNode     CmpINode
+#define CmpUXNode    CmpUNode
 #define SubXNode     SubINode
 #define LShiftXNode  LShiftINode
 // For object size computation:
 #define AddXNode     AddINode
 #define RShiftXNode  RShiftINode
@@ -2268,10 +2422,11 @@
 #define Op_AddX      Op_AddI
 #define Op_SubX      Op_SubI
 #define Op_XorX      Op_XorI
 #define Op_URShiftX  Op_URShiftI
 #define Op_LoadX     Op_LoadI
+#define Op_StoreX    Op_StoreI
 // conversions
 #define ConvI2X(x)   (x)
 #define ConvL2X(x)   ConvL2I(x)
 #define ConvX2I(x)   (x)
 #define ConvX2L(x)   ConvI2L(x)
diff a/src/hotspot/share/opto/vector.cpp b/src/hotspot/share/opto/vector.cpp
--- a/src/hotspot/share/opto/vector.cpp
+++ b/src/hotspot/share/opto/vector.cpp
@@ -204,11 +204,11 @@
       PhaseGVN& gvn = kit.gvn();
 
       // Adjust JVMS from post-call to pre-call state: put args on stack
       uint nargs = call->method()->arg_size();
       kit.ensure_stack(kit.sp() + nargs);
-      for (uint i = TypeFunc::Parms; i < call->tf()->domain()->cnt(); i++) {
+      for (uint i = TypeFunc::Parms; i < call->tf()->domain_sig()->cnt(); i++) {
         kit.push(call->in(i));
       }
       jvms = kit.sync_jvms();
 
       Node* new_vbox = nullptr;
diff a/src/hotspot/share/precompiled/precompiled.hpp b/src/hotspot/share/precompiled/precompiled.hpp
--- a/src/hotspot/share/precompiled/precompiled.hpp
+++ b/src/hotspot/share/precompiled/precompiled.hpp
@@ -71,11 +71,13 @@
 
 #ifdef TARGET_COMPILER_visCPP
 // For Visual Studio, including the *.inline.hpp files actually
 // increased performance.
 #include "memory/allocation.inline.hpp"
+#ifdef JDK_8254007_IS_FIXED
 #include "oops/access.inline.hpp"
 #include "oops/oop.inline.hpp"
+#endif
 #include "runtime/handles.inline.hpp"
 #endif // TARGET_COMPILER_visCPP
 
 #endif // !DONT_USE_PRECOMPILED_HEADER
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
@@ -53,10 +53,12 @@
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
 #include "nmt/memTracker.hpp"
 #include "oops/access.inline.hpp"
 #include "oops/arrayOop.hpp"
+#include "oops/flatArrayOop.inline.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "oops/instanceKlass.inline.hpp"
 #include "oops/instanceOop.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/markWord.hpp"
 #include "oops/method.hpp"
@@ -424,12 +426,13 @@
 
   // The slot is the index of the field description in the field-array
   // The jfieldID is the offset of the field within the object
   // It may also have hash bits for k, if VerifyJNIFields is turned on.
   int offset = InstanceKlass::cast(k1)->field_offset( slot );
+  bool is_flat = InstanceKlass::cast(k1)->field_is_flat(slot);
   assert(InstanceKlass::cast(k1)->contains_field_offset(offset), "stay within object");
-  ret = jfieldIDWorkaround::to_instance_jfieldID(k1, offset);
+  ret = jfieldIDWorkaround::to_instance_jfieldID(k1, offset, is_flat);
   return ret;
 JNI_END
 
 
 DT_RETURN_MARK_DECL(ToReflectedMethod, jobject
@@ -442,11 +445,11 @@
   DT_RETURN_MARK(ToReflectedMethod, jobject, (const jobject&)ret);
 
   methodHandle m (THREAD, Method::resolve_jmethod_id(method_id));
   assert(m->is_static() == (isStatic != 0), "jni_ToReflectedMethod access flags doesn't match");
   oop reflection_method;
-  if (m->is_initializer()) {
+  if (m->is_object_constructor()) {
     reflection_method = Reflection::new_constructor(m, CHECK_NULL);
   } else {
     reflection_method = Reflection::new_method(m, false, CHECK_NULL);
   }
   ret = JNIHandles::make_local(THREAD, reflection_method);
@@ -800,11 +803,11 @@
     // float is coerced to double w/ va_arg
     case T_FLOAT:       push_float((jfloat) va_arg(_ap, jdouble)); break;
     case T_DOUBLE:      push_double(va_arg(_ap, jdouble)); break;
 
     case T_ARRAY:
-    case T_OBJECT:      push_object(va_arg(_ap, jobject)); break;
+    case T_OBJECT: push_object(va_arg(_ap, jobject)); break;
     default:            ShouldNotReachHere();
     }
   }
 
  public:
@@ -840,11 +843,12 @@
     case T_BOOLEAN:     push_boolean((_ap++)->z); break;
     case T_LONG:        push_long((_ap++)->j); break;
     case T_FLOAT:       push_float((_ap++)->f); break;
     case T_DOUBLE:      push_double((_ap++)->d); break;
     case T_ARRAY:
-    case T_OBJECT:      push_object((_ap++)->l); break;
+    case T_OBJECT:
+    case T_PRIMITIVE_OBJECT: push_object((_ap++)->l); break;
     default:            ShouldNotReachHere();
     }
   }
 
  public:
@@ -967,11 +971,17 @@
   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);
+  oop clazzoop = JNIHandles::resolve_non_null(clazz);
+  Klass* k = java_lang_Class::as_Klass(clazzoop);
+  if (k == nullptr || k->is_inline_klass()) {
+    ResourceMark rm(THREAD);
+    THROW_(vmSymbols::java_lang_InstantiationException(), nullptr);
+  }
+  instanceOop i = InstanceKlass::allocate_instance(clazzoop, CHECK_NULL);
   ret = JNIHandles::make_local(THREAD, i);
   return ret;
 JNI_END
 
 DT_RETURN_MARK_DECL(NewObjectA, jobject
@@ -981,17 +991,25 @@
   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);
+  oop clazzoop = JNIHandles::resolve_non_null(clazz);
+  Klass* k = java_lang_Class::as_Klass(clazzoop);
+  if (k == nullptr) {
+    ResourceMark rm(THREAD);
+    THROW_(vmSymbols::java_lang_InstantiationException(), nullptr);
+  }
+
+  instanceOop i = InstanceKlass::allocate_instance(clazzoop, 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;
-JNI_END
+  JNI_END
 
 
 DT_RETURN_MARK_DECL(NewObjectV, jobject
                     , HOTSPOT_JNI_NEWOBJECTV_RETURN(_ret_ref));
 
@@ -999,15 +1017,23 @@
   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);
+  oop clazzoop = JNIHandles::resolve_non_null(clazz);
+  Klass* k = java_lang_Class::as_Klass(clazzoop);
+  if (k == nullptr) {
+    ResourceMark rm(THREAD);
+    THROW_(vmSymbols::java_lang_InstantiationException(), nullptr);
+  }
+
+  instanceOop i = InstanceKlass::allocate_instance(clazzoop, 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;
 JNI_END
 
 
 DT_RETURN_MARK_DECL(NewObject, jobject
@@ -1017,18 +1043,26 @@
   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);
+  oop clazzoop = JNIHandles::resolve_non_null(clazz);
+  Klass* k = java_lang_Class::as_Klass(clazzoop);
+  if (k == nullptr) {
+    ResourceMark rm(THREAD);
+    THROW_(vmSymbols::java_lang_InstantiationException(), nullptr);
+  }
+
+  instanceOop i = InstanceKlass::allocate_instance(clazzoop, CHECK_NULL);
   obj = JNIHandles::make_local(THREAD, i);
   va_list args;
   va_start(args, methodID);
   JavaValue jvalue(T_VOID);
   JNI_ArgumentPusherVaArg ap(methodID, args);
   jni_invoke_nonstatic(env, &jvalue, obj, JNI_NONVIRTUAL, methodID, &ap, CHECK_NULL);
   va_end(args);
+
   return obj;
 JNI_END
 
 
 JNI_ENTRY(jclass, jni_GetObjectClass(JNIEnv *env, jobject obj))
@@ -1775,27 +1809,38 @@
     THROW_MSG_0(vmSymbols::java_lang_NoSuchFieldError(), err_msg("%s.%s %s", k->external_name(), name, sig));
   }
 
   // A jfieldID for a non-static field is simply the offset of the field within the instanceOop
   // It may also have hash bits for k, if VerifyJNIFields is turned on.
-  ret = jfieldIDWorkaround::to_instance_jfieldID(k, fd.offset());
+  ret = jfieldIDWorkaround::to_instance_jfieldID(k, fd.offset(), fd.is_flat());
   return ret;
 JNI_END
 
 
 JNI_ENTRY(jobject, jni_GetObjectField(JNIEnv *env, jobject obj, jfieldID fieldID))
   HOTSPOT_JNI_GETOBJECTFIELD_ENTRY(env, obj, (uintptr_t) fieldID);
   oop o = JNIHandles::resolve_non_null(obj);
   Klass* k = o->klass();
   int offset = jfieldIDWorkaround::from_instance_jfieldID(k, fieldID);
+  oop res = nullptr;
   // Keep JVMTI addition small and only check enabled flag here.
   // jni_GetField_probe() assumes that is okay to create handles.
   if (JvmtiExport::should_post_field_access()) {
     o = JvmtiExport::jni_GetField_probe(thread, obj, o, k, fieldID, false);
   }
-  oop loaded_obj = HeapAccess<ON_UNKNOWN_OOP_REF>::oop_load_at(o, offset);
-  jobject ret = JNIHandles::make_local(THREAD, loaded_obj);
+  if (!jfieldIDWorkaround::is_flat_jfieldID(fieldID)) {
+    res = HeapAccess<ON_UNKNOWN_OOP_REF>::oop_load_at(o, offset);
+  } else {
+    assert(k->is_instance_klass(), "Only instance can have flat fields");
+    InstanceKlass* ik = InstanceKlass::cast(k);
+    fieldDescriptor fd;
+    ik->find_field_from_offset(offset, false, &fd);  // performance bottleneck
+    InstanceKlass* holder = fd.field_holder();
+    InlineKlass* field_vklass = InlineKlass::cast(holder->get_inline_type_field_klass(fd.index()));
+    res = field_vklass->read_flat_field(o, ik->field_offset(fd.index()), CHECK_NULL);
+  }
+  jobject ret = JNIHandles::make_local(THREAD, res);
   HOTSPOT_JNI_GETOBJECTFIELD_RETURN(ret);
   return ret;
 JNI_END
 
 
@@ -1883,11 +1928,22 @@
   if (JvmtiExport::should_post_field_modification()) {
     jvalue field_value;
     field_value.l = value;
     o = JvmtiExport::jni_SetField_probe(thread, obj, o, k, fieldID, false, JVM_SIGNATURE_CLASS, (jvalue *)&field_value);
   }
-  HeapAccess<ON_UNKNOWN_OOP_REF>::oop_store_at(o, offset, JNIHandles::resolve(value));
+  if (!jfieldIDWorkaround::is_flat_jfieldID(fieldID)) {
+    HeapAccess<ON_UNKNOWN_OOP_REF>::oop_store_at(o, offset, JNIHandles::resolve(value));
+  } else {
+    assert(k->is_instance_klass(), "Only instances can have flat fields");
+    InstanceKlass* ik = InstanceKlass::cast(k);
+    fieldDescriptor fd;
+    ik->find_field_from_offset(offset, false, &fd);
+    InstanceKlass* holder = fd.field_holder();
+    InlineKlass* vklass = InlineKlass::cast(holder->get_inline_type_field_klass(fd.index()));
+    oop v = JNIHandles::resolve_non_null(value);
+    vklass->write_flat_field(o, offset, v, CHECK);
+  }
   HOTSPOT_JNI_SETOBJECTFIELD_RETURN();
 JNI_END
 
 // TODO: make this a template
 
@@ -2299,53 +2355,91 @@
 
 JNI_ENTRY(jobject, jni_GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index))
  HOTSPOT_JNI_GETOBJECTARRAYELEMENT_ENTRY(env, array, index);
   jobject ret = nullptr;
   DT_RETURN_MARK(GetObjectArrayElement, jobject, (const jobject&)ret);
-  objArrayOop a = objArrayOop(JNIHandles::resolve_non_null(array));
-  if (a->is_within_bounds(index)) {
-    ret = JNIHandles::make_local(THREAD, a->obj_at(index));
-    return ret;
+  oop res = nullptr;
+  arrayOop arr((arrayOop)JNIHandles::resolve_non_null(array));
+  if (arr->is_within_bounds(index)) {
+    if (arr->is_flatArray()) {
+      flatArrayOop a = flatArrayOop(JNIHandles::resolve_non_null(array));
+      flatArrayHandle vah(thread, a);
+      res = flatArrayOopDesc::value_alloc_copy_from_index(vah, index, CHECK_NULL);
+      assert(res != nullptr, "Must be set in one of two paths above");
+    } else {
+      assert(arr->is_objArray(), "If not a valueArray. must be an objArray");
+      objArrayOop a = objArrayOop(JNIHandles::resolve_non_null(array));
+      res = a->obj_at(index);
+    }
   } else {
     ResourceMark rm(THREAD);
     stringStream ss;
-    ss.print("Index %d out of bounds for length %d", index, a->length());
+    ss.print("Index %d out of bounds for length %d", index,arr->length());
     THROW_MSG_0(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), ss.as_string());
   }
+  ret = JNIHandles::make_local(THREAD, res);
+  return ret;
 JNI_END
 
 DT_VOID_RETURN_MARK_DECL(SetObjectArrayElement
                          , HOTSPOT_JNI_SETOBJECTARRAYELEMENT_RETURN());
 
 JNI_ENTRY(void, jni_SetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index, jobject value))
  HOTSPOT_JNI_SETOBJECTARRAYELEMENT_ENTRY(env, array, index, value);
   DT_VOID_RETURN_MARK(SetObjectArrayElement);
 
-  objArrayOop a = objArrayOop(JNIHandles::resolve_non_null(array));
-  oop v = JNIHandles::resolve(value);
-  if (a->is_within_bounds(index)) {
-    if (v == nullptr || v->is_a(ObjArrayKlass::cast(a->klass())->element_klass())) {
-      a->obj_at_put(index, v);
-    } else {
-      ResourceMark rm(THREAD);
-      stringStream ss;
-      Klass *bottom_kl = ObjArrayKlass::cast(a->klass())->bottom_klass();
-      ss.print("type mismatch: can not store %s to %s[%d]",
-               v->klass()->external_name(),
-               bottom_kl->is_typeArray_klass() ? type2name_tab[ArrayKlass::cast(bottom_kl)->element_type()] : bottom_kl->external_name(),
-               index);
-      for (int dims = ArrayKlass::cast(a->klass())->dimension(); dims > 1; --dims) {
-        ss.print("[]");
-      }
-      THROW_MSG(vmSymbols::java_lang_ArrayStoreException(), ss.as_string());
-    }
-  } else {
-    ResourceMark rm(THREAD);
-    stringStream ss;
-    ss.print("Index %d out of bounds for length %d", index, a->length());
-    THROW_MSG(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), ss.as_string());
-  }
+   bool oob = false;
+   int length = -1;
+   oop res = nullptr;
+   arrayOop arr((arrayOop)JNIHandles::resolve_non_null(array));
+   if (arr->is_within_bounds(index)) {
+     if (arr->is_flatArray()) {
+       flatArrayOop a = flatArrayOop(JNIHandles::resolve_non_null(array));
+       oop v = JNIHandles::resolve(value);
+       FlatArrayKlass* vaklass = FlatArrayKlass::cast(a->klass());
+       InlineKlass* element_vklass = vaklass->element_klass();
+       if (v != nullptr && v->is_a(element_vklass)) {
+         a->value_copy_to_index(v, index);
+       } else {
+         ResourceMark rm(THREAD);
+         stringStream ss;
+         Klass *kl = FlatArrayKlass::cast(a->klass());
+         ss.print("type mismatch: can not store %s to %s[%d]",
+             v->klass()->external_name(),
+             kl->external_name(),
+             index);
+         for (int dims = ArrayKlass::cast(a->klass())->dimension(); dims > 1; --dims) {
+           ss.print("[]");
+         }
+         THROW_MSG(vmSymbols::java_lang_ArrayStoreException(), ss.as_string());
+       }
+     } else {
+       assert(arr->is_objArray(), "If not a valueArray. must be an objArray");
+       objArrayOop a = objArrayOop(JNIHandles::resolve_non_null(array));
+       oop v = JNIHandles::resolve(value);
+       if (v == nullptr || v->is_a(ObjArrayKlass::cast(a->klass())->element_klass())) {
+         a->obj_at_put(index, v);
+       } else {
+         ResourceMark rm(THREAD);
+         stringStream ss;
+         Klass *bottom_kl = ObjArrayKlass::cast(a->klass())->bottom_klass();
+         ss.print("type mismatch: can not store %s to %s[%d]",
+             v->klass()->external_name(),
+             bottom_kl->is_typeArray_klass() ? type2name_tab[ArrayKlass::cast(bottom_kl)->element_type()] : bottom_kl->external_name(),
+                 index);
+         for (int dims = ArrayKlass::cast(a->klass())->dimension(); dims > 1; --dims) {
+           ss.print("[]");
+         }
+         THROW_MSG(vmSymbols::java_lang_ArrayStoreException(), ss.as_string());
+       }
+     }
+   } else {
+     ResourceMark rm(THREAD);
+     stringStream ss;
+     ss.print("Index %d out of bounds for length %d", index, arr->length());
+     THROW_MSG(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), ss.as_string());
+   }
 JNI_END
 
 
 
 #define DEFINE_NEWSCALARARRAY(Return,Allocator,Result \
diff a/src/hotspot/share/prims/jniCheck.cpp b/src/hotspot/share/prims/jniCheck.cpp
--- a/src/hotspot/share/prims/jniCheck.cpp
+++ b/src/hotspot/share/prims/jniCheck.cpp
@@ -254,11 +254,11 @@
 
   /* check for proper field type */
   if (!id->find_local_field(&fd))
     ReportJNIFatalError(thr, fatal_static_field_not_found);
   if ((fd.field_type() != ftype) &&
-      !(fd.field_type() == T_ARRAY && ftype == T_OBJECT)) {
+      !(fd.field_type() == T_ARRAY && ftype == T_OBJECT))  {
     ReportJNIFatalError(thr, fatal_static_field_mismatch);
   }
 }
 
 static inline void
@@ -342,13 +342,13 @@
     ReportJNIFatalError(thr, fatal_element_type_mismatch);
   }
 }
 
 static inline void
-check_is_obj_array(JavaThread* thr, jarray jArray) {
+check_is_obj_or_inline_array(JavaThread* thr, jarray jArray) {
   arrayOop aOop = check_is_array(thr, jArray);
-  if (!aOop->is_objArray()) {
+  if (!aOop->is_objArray() && !aOop->is_flatArray()) {
     ReportJNIFatalError(thr, fatal_object_array_expected);
   }
 }
 
 /*
@@ -1604,11 +1604,11 @@
   checked_jni_GetObjectArrayElement(JNIEnv *env,
                                     jobjectArray array,
                                     jsize index))
     functionEnter(thr);
     IN_VM(
-      check_is_obj_array(thr, array);
+      check_is_obj_or_inline_array(thr, array);
     )
     jobject result = UNCHECKED()->GetObjectArrayElement(env,array,index);
     functionExit(thr);
     return result;
 JNI_END
@@ -1618,11 +1618,11 @@
                                     jobjectArray array,
                                     jsize index,
                                     jobject val))
     functionEnter(thr);
     IN_VM(
-      check_is_obj_array(thr, array);
+      check_is_obj_or_inline_array(thr, array);
     )
     UNCHECKED()->SetObjectArrayElement(env,array,index,val);
     functionExit(thr);
 JNI_END
 
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
@@ -55,10 +55,11 @@
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
 #include "oops/access.inline.hpp"
 #include "oops/constantPool.hpp"
 #include "oops/fieldStreams.inline.hpp"
+#include "oops/flatArrayKlass.hpp"
 #include "oops/instanceKlass.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/method.hpp"
 #include "oops/recordComponent.hpp"
 #include "oops/objArrayKlass.hpp"
@@ -422,10 +423,33 @@
   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
 
+JVM_ENTRY(jarray, JVM_NewNullRestrictedArray(JNIEnv *env, jclass elmClass, jint len))
+  if (len < 0) {
+    THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Array length is negative");
+  }
+  oop mirror = JNIHandles::resolve_non_null(elmClass);
+  Klass* klass = java_lang_Class::as_Klass(mirror);
+  klass->initialize(CHECK_NULL);
+  if (klass->is_identity_class()) {
+    THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Element class is not a value class");
+  }
+  InstanceKlass* ik = InstanceKlass::cast(klass);
+  if (!ik->is_implicitly_constructible()) {
+    THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "Element class is not annotated with @ImplicitlyConstructible");
+  }
+  oop array = oopFactory::new_valueArray(ik, len, CHECK_NULL);
+  return (jarray) JNIHandles::make_local(THREAD, array);
+JVM_END
+
+
+JVM_ENTRY(jboolean, JVM_IsNullRestrictedArray(JNIEnv *env, jobject obj))
+  arrayOop oop = arrayOop(JNIHandles::resolve_non_null(obj));
+  return oop->is_null_free_array();
+JVM_END
 
 // java.lang.Runtime /////////////////////////////////////////////////////////////////////////
 
 extern volatile jint vm_created;
 
@@ -621,12 +645,32 @@
 // java.lang.Object ///////////////////////////////////////////////
 
 
 JVM_ENTRY(jint, 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)));
+  if (handle == nullptr) {
+    return 0;
+  }
+  oop obj = JNIHandles::resolve_non_null(handle);
+  if (EnableValhalla && obj->klass()->is_inline_klass()) {
+      JavaValue result(T_INT);
+      JavaCallArguments args;
+      Handle ho(THREAD, obj);
+      args.push_oop(ho);
+      methodHandle method(THREAD, Universe::value_object_hash_code_method());
+      JavaCalls::call(&result, method, &args, THREAD);
+      if (HAS_PENDING_EXCEPTION) {
+        if (!PENDING_EXCEPTION->is_a(vmClasses::Error_klass())) {
+          Handle e(THREAD, PENDING_EXCEPTION);
+          CLEAR_PENDING_EXCEPTION;
+          THROW_MSG_CAUSE_(vmSymbols::java_lang_InternalError(), "Internal error in hashCode", e, false);
+        }
+      }
+      return result.get_jint();
+  } else {
+    return checked_cast<jint>(ObjectSynchronizer::FastHashCode(THREAD, obj));
+  }
 JVM_END
 
 
 JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))
   Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
@@ -674,10 +718,11 @@
 
   // Check if class of obj supports the Cloneable interface.
   // All arrays are considered to be cloneable (See JLS 20.1.5).
   // All j.l.r.Reference classes are considered non-cloneable.
   if (!klass->is_cloneable() ||
+       klass->is_inline_klass() ||
       (klass->is_instance_klass() &&
        InstanceKlass::cast(klass)->reference_type() != REF_NONE)) {
     ResourceMark rm(THREAD);
     THROW_MSG_0(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name());
   }
@@ -1190,24 +1235,26 @@
 
   Klass* klass = java_lang_Class::as_Klass(mirror);
   // Figure size of result array
   int size;
   if (klass->is_instance_klass()) {
-    size = InstanceKlass::cast(klass)->local_interfaces()->length();
+    InstanceKlass* ik = InstanceKlass::cast(klass);
+    size = ik->local_interfaces()->length();
   } else {
-    assert(klass->is_objArray_klass() || klass->is_typeArray_klass(), "Illegal mirror klass");
+    assert(klass->is_objArray_klass() || klass->is_typeArray_klass() || klass->is_flatArray_klass(), "Illegal mirror klass");
     size = 2;
   }
 
   // Allocate result array
   objArrayOop r = oopFactory::new_objArray(vmClasses::Class_klass(), size, CHECK_NULL);
   objArrayHandle result (THREAD, r);
   // Fill in result
   if (klass->is_instance_klass()) {
     // Regular instance klass, fill in all local interfaces
     for (int index = 0; index < size; index++) {
-      Klass* k = InstanceKlass::cast(klass)->local_interfaces()->at(index);
+      InstanceKlass* ik = InstanceKlass::cast(klass);
+      Klass* k = ik->local_interfaces()->at(index);
       result->obj_at_put(index, k->java_mirror());
     }
   } else {
     // All arrays implement java.lang.Cloneable and java.io.Serializable
     result->obj_at_put(0, vmClasses::Cloneable_klass()->java_mirror());
@@ -1238,10 +1285,29 @@
   }
   Klass* k = java_lang_Class::as_Klass(mirror);
   return k->is_hidden();
 JVM_END
 
+JVM_ENTRY(jboolean, JVM_IsIdentityClass(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);
+  if (EnableValhalla) {
+    return k->is_array_klass() || k->is_identity_class();
+  } else {
+    return k->is_interface() ? JNI_FALSE : JNI_TRUE;
+  }
+JVM_END
+
+JVM_ENTRY(jboolean, JVM_IsImplicitlyConstructibleClass(JNIEnv *env, jclass cls))
+  oop mirror = JNIHandles::resolve_non_null(cls);
+  InstanceKlass* ik = InstanceKlass::cast(java_lang_Class::as_Klass(mirror));
+  return ik->is_implicitly_constructible();
+JVM_END
+
 JVM_ENTRY(jobjectArray, JVM_GetClassSigners(JNIEnv *env, jclass cls))
   JvmtiVMObjectAllocEventCollector oam;
   oop mirror = JNIHandles::resolve_non_null(cls);
   if (java_lang_Class::is_primitive(mirror)) {
     // There are no signers for primitive types
@@ -1852,14 +1918,17 @@
   return nullptr;
 }
 JVM_END
 
 static bool select_method(const methodHandle& method, bool want_constructor) {
+  bool is_ctor = (method->is_object_constructor());
   if (want_constructor) {
-    return (method->is_initializer() && !method->is_static());
+    return is_ctor;
   } else {
-    return  (!method->is_initializer() && !method->is_overpass());
+    return (!is_ctor &&
+            !method->is_class_initializer() &&
+            !method->is_overpass());
   }
 }
 
 static jobjectArray get_class_declared_methods_helper(
                                   JNIEnv *env,
@@ -1918,10 +1987,11 @@
       // Otherwise should probably put a method that throws NSME
       result->obj_at_put(i, nullptr);
     } else {
       oop m;
       if (want_constructor) {
+        assert(method->is_object_constructor(), "must be");
         m = Reflection::new_constructor(method, CHECK_NULL);
       } else {
         m = Reflection::new_method(method, false, CHECK_NULL);
       }
       result->obj_at_put(i, m);
@@ -2200,14 +2270,14 @@
   methodHandle m (THREAD, k->find_method(name, sig));
   if (m.is_null()) {
     THROW_MSG_0(vmSymbols::java_lang_RuntimeException(), "Unable to look up method in target class");
   }
   oop method;
-  if (!m->is_initializer() || m->is_static()) {
-    method = Reflection::new_method(m, true, CHECK_NULL);
-  } else {
+  if (m->is_object_constructor()) {
     method = Reflection::new_constructor(m, CHECK_NULL);
+  } else {
+    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))
@@ -2474,10 +2544,47 @@
   JvmtiVMObjectAllocEventCollector oam;
   oop asd = JavaAssertions::createAssertionStatusDirectives(CHECK_NULL);
   return JNIHandles::make_local(THREAD, asd);
 JVM_END
 
+// Arrays support /////////////////////////////////////////////////////////////
+
+JVM_ENTRY(jboolean, JVM_ArrayIsAccessAtomic(JNIEnv *env, jclass unused, jobject array))
+  oop o = JNIHandles::resolve(array);
+  Klass* k = o->klass();
+  if ((o == nullptr) || (!k->is_array_klass())) {
+    THROW_0(vmSymbols::java_lang_IllegalArgumentException());
+  }
+  return ArrayKlass::cast(k)->element_access_is_atomic();
+JVM_END
+
+JVM_ENTRY(jobject, JVM_ArrayEnsureAccessAtomic(JNIEnv *env, jclass unused, jobject array))
+  oop o = JNIHandles::resolve(array);
+  Klass* k = o->klass();
+  if ((o == nullptr) || (!k->is_array_klass())) {
+    THROW_0(vmSymbols::java_lang_IllegalArgumentException());
+  }
+  if (k->is_flatArray_klass()) {
+    FlatArrayKlass* vk = FlatArrayKlass::cast(k);
+    if (!vk->element_access_is_atomic()) {
+      /**
+       * Need to decide how to implement:
+       *
+       * 1) Change to objArrayOop layout, therefore oop->klass() differs so
+       * then "<atomic>[Qfoo;" klass needs to subclass "[Qfoo;" to pass through
+       * "checkcast" & "instanceof"
+       *
+       * 2) Use extra header in the flatArrayOop to flag atomicity required and
+       * possibly per instance lock structure. Said info, could be placed in
+       * "trailer" rather than disturb the current arrayOop
+       */
+      Unimplemented();
+    }
+  }
+  return array;
+JVM_END
+
 // Verification ////////////////////////////////////////////////////////////////////////////////
 
 // Reflection for the verifier /////////////////////////////////////////////////////////////////
 
 // RedefineClasses support: bug 6214132 caused verification to fail.
@@ -2603,11 +2710,11 @@
 
 
 JVM_ENTRY(jint, 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;
+  return InstanceKlass::cast(k)->field_access_flags(field_index);
 JVM_END
 
 
 JVM_ENTRY(jint, JVM_GetMethodIxLocalsCount(JNIEnv *env, jclass cls, int method_index))
   Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls));
@@ -2636,11 +2743,11 @@
 JVM_ENTRY(jboolean, 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();
+  return method->is_object_constructor();
 JVM_END
 
 
 JVM_ENTRY(jboolean, JVM_IsVMGeneratedMethodIx(JNIEnv *env, jclass cls, int method_index))
   ResourceMark rm(THREAD);
@@ -2793,11 +2900,11 @@
       Symbol* name      = cp->uncached_name_ref_at(cp_index);
       Symbol* signature = cp->uncached_signature_ref_at(cp_index);
       InstanceKlass* ik = InstanceKlass::cast(k_called);
       for (JavaFieldStream fs(ik); !fs.done(); fs.next()) {
         if (fs.name() == name && fs.signature() == signature) {
-          return fs.access_flags().as_short() & JVM_RECOGNIZED_FIELD_MODIFIERS;
+          return fs.access_flags().as_short();
         }
       }
       return -1;
     }
     default:
@@ -3473,10 +3580,14 @@
 
 JVM_LEAF(jboolean, JVM_IsPreviewEnabled(void))
   return Arguments::enable_preview() ? JNI_TRUE : JNI_FALSE;
 JVM_END
 
+JVM_LEAF(jboolean, JVM_IsValhallaEnabled(void))
+  return EnableValhalla ? JNI_TRUE : JNI_FALSE;
+JVM_END
+
 JVM_LEAF(jboolean, JVM_IsContinuationsSupported(void))
   return VMContinuations ? JNI_TRUE : JNI_FALSE;
 JVM_END
 
 JVM_LEAF(jboolean, JVM_IsForeignLinkerSupported(void))
@@ -3554,11 +3665,11 @@
 JVM_ENTRY(jobject, 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)));
+    objArrayHandle args = oopFactory::ensure_objArray(JNIHandles::resolve(args0), CHECK_NULL);
     oop result = Reflection::invoke_method(method_handle(), receiver, args, CHECK_NULL);
     jobject res = JNIHandles::make_local(THREAD, result);
     if (JvmtiExport::should_post_vm_object_alloc()) {
       oop ret_type = java_lang_reflect_Method::return_type(method_handle());
       assert(ret_type != nullptr, "sanity check: ret_type oop must not be null!");
@@ -3574,12 +3685,12 @@
   }
 JVM_END
 
 
 JVM_ENTRY(jobject, JVM_NewInstanceFromConstructor(JNIEnv *env, jobject c, jobjectArray args0))
+  objArrayHandle args = oopFactory::ensure_objArray(JNIHandles::resolve(args0), CHECK_NULL);
   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);
   }
diff a/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp b/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp
--- a/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp
+++ b/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp
@@ -22,10 +22,11 @@
  *
  */
 
 #include "precompiled.hpp"
 #include "classfile/symbolTable.hpp"
+#include "classfile/vmClasses.hpp"
 #include "interpreter/bytecodeStream.hpp"
 #include "memory/universe.hpp"
 #include "oops/constantPool.inline.hpp"
 #include "oops/fieldStreams.inline.hpp"
 #include "oops/instanceKlass.inline.hpp"
@@ -83,11 +84,11 @@
     // JVMSpec|         u2 descriptor_index;
     // JVMSpec|         u2 attributes_count;
     // JVMSpec|         attribute_info attributes[attributes_count];
     // JVMSpec|   }
 
-    write_u2(access_flags.get_flags() & JVM_RECOGNIZED_FIELD_MODIFIERS);
+    write_u2(access_flags.get_flags());
     write_u2(name_index);
     write_u2(signature_index);
     u2 attr_count = 0;
     if (initial_value_index != 0) {
       ++attr_count;
@@ -914,12 +915,11 @@
   // JVMSpec|           cp_info constant_pool[constant_pool_count-1];
   write_u2(checked_cast<u2>(cpool()->length()));
   copy_cpool_bytes(writeable_address(cpool_size()));
 
   // JVMSpec|           u2 access_flags;
-  write_u2(ik()->access_flags().get_flags() & JVM_RECOGNIZED_CLASS_MODIFIERS);
-
+  write_u2(ik()->access_flags().get_flags() & (JVM_RECOGNIZED_CLASS_MODIFIERS));
   // JVMSpec|           u2 this_class;
   // JVMSpec|           u2 super_class;
   write_u2(class_symbol_to_cpool_index(ik()->name()));
   Klass* super_class = ik()->super();
   write_u2(super_class == nullptr? 0 :  // zero for java.lang.Object
@@ -1025,11 +1025,11 @@
       bool is_wide = false;
       switch (code) {
       case Bytecodes::_getstatic       :  // fall through
       case Bytecodes::_putstatic       :  // fall through
       case Bytecodes::_getfield        :  // fall through
-      case Bytecodes::_putfield        :  {
+      case Bytecodes::_putfield        : {
         int field_index = Bytes::get_native_u2(bcp+1);
         u2 pool_index = mh->constants()->resolved_field_entry_at(field_index)->constant_pool_index();
         assert(pool_index < mh->constants()->length(), "sanity check");
         Bytes::put_Java_u2((address)(p+1), pool_index);     // java byte ordering
         break;
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
@@ -2771,13 +2771,13 @@
     Klass* k = java_lang_Class::as_Klass(k_mirror);
     NULL_CHECK(k, JVMTI_ERROR_INVALID_CLASS);
     result = k->compute_modifier_flags();
 
     // Reset the deleted  ACC_SUPER bit (deleted in compute_modifier_flags()).
-    if (k->is_super()) {
-      result |= JVM_ACC_SUPER;
-    }
+    // if (k->is_super()) {
+    //   result |= JVM_ACC_SUPER;
+    // }
   } else {
     result = (JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC);
   }
   *modifiers_ptr = result;
 
@@ -2904,11 +2904,12 @@
 
   // Allocate the result and fill it in.
   jfieldID* result_list = (jfieldID*)jvmtiMalloc(result_count * sizeof(jfieldID));
   for (int i = 0; i < result_count; i++, flds.next()) {
     result_list[i] = jfieldIDWorkaround::to_jfieldID(ik, flds.offset(),
-                                                     flds.access_flags().is_static());
+                                                     flds.access_flags().is_static(),
+                                                     flds.field_descriptor().is_flat());
   }
   assert(flds.done(), "just checking");
 
   // Fill in the results
   *field_count_ptr = result_count;
@@ -2942,12 +2943,13 @@
       *interface_count_ptr = 0;
       *interfaces_ptr = (jclass*) jvmtiMalloc(0 * sizeof(jclass));
       return JVMTI_ERROR_NONE;
     }
 
-    Array<InstanceKlass*>* interface_list = InstanceKlass::cast(k)->local_interfaces();
-    const int result_length = (interface_list == nullptr ? 0 : interface_list->length());
+    InstanceKlass* ik = InstanceKlass::cast(k);
+    Array<InstanceKlass*>* interface_list = ik->local_interfaces();
+    int result_length = (interface_list == nullptr ? 0 : interface_list->length());
     jclass* result_list = (jclass*) jvmtiMalloc(result_length * sizeof(jclass));
     for (int i_index = 0; i_index < result_length; i_index += 1) {
       InstanceKlass* klass_at = interface_list->at(i_index);
       assert(klass_at->is_klass(), "interfaces must be Klass*s");
       assert(klass_at->is_interface(), "interfaces must be interfaces");
diff a/src/hotspot/share/prims/jvmtiGetLoadedClasses.cpp b/src/hotspot/share/prims/jvmtiGetLoadedClasses.cpp
--- a/src/hotspot/share/prims/jvmtiGetLoadedClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiGetLoadedClasses.cpp
@@ -20,10 +20,11 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  *
  */
 
+#include <oops/inlineKlass.hpp>
 #include "precompiled.hpp"
 #include "classfile/classLoaderDataGraph.hpp"
 #include "classfile/dictionary.hpp"
 #include "classfile/javaClasses.hpp"
 #include "gc/shared/collectedHeap.hpp"
@@ -76,10 +77,11 @@
       // Collect array classes this way when walking the dictionary (because array classes are
       // not in the dictionary).
       for (Klass* l = k->array_klass_or_null(); l != nullptr; l = l->array_klass_or_null()) {
         _classStack.push((jclass) _env->jni_reference(Handle(_cur_thread, l->java_mirror())));
       }
+      // CMH flat arrays (InlineKlass)
     }
   }
 
   jvmtiError get_result(JvmtiEnv *env, jint* classCountPtr, jclass** classesPtr) {
     // Return results by extracting the collected contents into a list
diff a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp
--- a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp
@@ -607,12 +607,11 @@
     case JVM_CONSTANT_Invalid: // fall through
 
     // At this stage, String could be here, but not StringIndex
     case JVM_CONSTANT_StringIndex: // fall through
 
-    // At this stage JVM_CONSTANT_UnresolvedClassInError should not be
-    // here
+    // At this stage JVM_CONSTANT_UnresolvedClassInError should not be here
     case JVM_CONSTANT_UnresolvedClassInError: // fall through
 
     default:
     {
       // leave a breadcrumb
@@ -920,10 +919,22 @@
                                 the_class, scratch_class,
                                 the_class->permitted_subclasses(),
                                 scratch_class->permitted_subclasses());
 }
 
+static jvmtiError check_preload_attribute(InstanceKlass* the_class,
+                                          InstanceKlass* scratch_class) {
+  Thread* thread = Thread::current();
+  ResourceMark rm(thread);
+
+  // Check whether the class Preload attribute has been changed.
+  return check_attribute_arrays("Preload",
+                                the_class, scratch_class,
+                                the_class->preload_classes(),
+                                scratch_class->preload_classes());
+}
+
 static bool can_add_or_delete(Method* m) {
       // Compatibility mode
   return (AllowRedefinitionToAddDeleteMethods &&
           (m->is_private() && (m->is_static() || m->is_final())));
 }
@@ -999,10 +1010,16 @@
   err = check_permitted_subclasses_attribute(the_class, scratch_class);
   if (err != JVMTI_ERROR_NONE) {
     return err;
   }
 
+  // Check whether the Preload attribute has been changed.
+  err = check_preload_attribute(the_class, scratch_class);
+  if (err != JVMTI_ERROR_NONE) {
+    return err;
+  }
+
   // Check whether class modifiers are the same.
   jushort old_flags = (jushort) the_class->access_flags().get_flags();
   jushort new_flags = (jushort) scratch_class->access_flags().get_flags();
   if (old_flags != new_flags) {
     log_info(redefine, class, normalize)
@@ -1926,10 +1943,16 @@
   if (!rewrite_cp_refs_in_permitted_subclasses_attribute(scratch_class)) {
     // propagate failure back to caller
     return false;
   }
 
+  // rewrite constant pool references in the Preload attribute:
+  if (!rewrite_cp_refs_in_preload_attribute(scratch_class)) {
+    // propagate failure back to caller
+    return false;
+  }
+
   // rewrite constant pool references in the methods:
   if (!rewrite_cp_refs_in_methods(scratch_class)) {
     // propagate failure back to caller
     return false;
   }
@@ -2074,10 +2097,23 @@
     permitted_subclasses->at_put(i, find_new_index(cp_index));
   }
   return true;
 }
 
+// Rewrite constant pool references in the Preload attribute.
+bool VM_RedefineClasses::rewrite_cp_refs_in_preload_attribute(
+       InstanceKlass* scratch_class) {
+
+  Array<u2>* preload_classes = scratch_class->preload_classes();
+  assert(preload_classes != nullptr, "unexpected null preload_classes");
+  for (int i = 0; i < preload_classes->length(); i++) {
+    u2 cp_index = preload_classes->at(i);
+    preload_classes->at_put(i, find_new_index(cp_index));
+  }
+  return true;
+}
+
 // Rewrite constant pool references in the methods.
 bool VM_RedefineClasses::rewrite_cp_refs_in_methods(InstanceKlass* scratch_class) {
 
   Array<Method*>* methods = scratch_class->methods();
 
diff a/src/hotspot/share/prims/jvmtiRedefineClasses.hpp b/src/hotspot/share/prims/jvmtiRedefineClasses.hpp
--- a/src/hotspot/share/prims/jvmtiRedefineClasses.hpp
+++ b/src/hotspot/share/prims/jvmtiRedefineClasses.hpp
@@ -469,10 +469,11 @@
     AnnotationArray* type_annotations_typeArray, int &byte_i_ref);
   bool rewrite_cp_refs_in_fields_annotations(InstanceKlass* scratch_class);
   bool rewrite_cp_refs_in_nest_attributes(InstanceKlass* scratch_class);
   bool rewrite_cp_refs_in_record_attribute(InstanceKlass* scratch_class);
   bool rewrite_cp_refs_in_permitted_subclasses_attribute(InstanceKlass* scratch_class);
+  bool rewrite_cp_refs_in_preload_attribute(InstanceKlass* scratch_class);
 
   void rewrite_cp_refs_in_method(methodHandle method,
     methodHandle * new_method_p, TRAPS);
   bool rewrite_cp_refs_in_methods(InstanceKlass* scratch_class);
 
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
@@ -129,34 +129,36 @@
 
 // MemberName support
 
 // import java_lang_invoke_MemberName.*
 enum {
-  IS_METHOD            = java_lang_invoke_MemberName::MN_IS_METHOD,
-  IS_CONSTRUCTOR       = java_lang_invoke_MemberName::MN_IS_CONSTRUCTOR,
-  IS_FIELD             = java_lang_invoke_MemberName::MN_IS_FIELD,
-  IS_TYPE              = java_lang_invoke_MemberName::MN_IS_TYPE,
-  CALLER_SENSITIVE     = java_lang_invoke_MemberName::MN_CALLER_SENSITIVE,
-  TRUSTED_FINAL        = java_lang_invoke_MemberName::MN_TRUSTED_FINAL,
+  IS_METHOD             = java_lang_invoke_MemberName::MN_IS_METHOD,
+  IS_OBJECT_CONSTRUCTOR = java_lang_invoke_MemberName::MN_IS_OBJECT_CONSTRUCTOR,
+  IS_FIELD              = java_lang_invoke_MemberName::MN_IS_FIELD,
+  IS_TYPE               = java_lang_invoke_MemberName::MN_IS_TYPE,
+  CALLER_SENSITIVE      = java_lang_invoke_MemberName::MN_CALLER_SENSITIVE,
+  TRUSTED_FINAL         = java_lang_invoke_MemberName::MN_TRUSTED_FINAL,
   HIDDEN_MEMBER        = java_lang_invoke_MemberName::MN_HIDDEN_MEMBER,
-  REFERENCE_KIND_SHIFT = java_lang_invoke_MemberName::MN_REFERENCE_KIND_SHIFT,
-  REFERENCE_KIND_MASK  = java_lang_invoke_MemberName::MN_REFERENCE_KIND_MASK,
-  LM_UNCONDITIONAL     = java_lang_invoke_MemberName::MN_UNCONDITIONAL_MODE,
-  LM_MODULE            = java_lang_invoke_MemberName::MN_MODULE_MODE,
-  LM_TRUSTED           = java_lang_invoke_MemberName::MN_TRUSTED_MODE,
-  ALL_KINDS      = IS_METHOD | IS_CONSTRUCTOR | IS_FIELD | IS_TYPE
+  FLAT_FIELD            = java_lang_invoke_MemberName::MN_FLAT_FIELD,
+  NULL_RESTRICTED       = java_lang_invoke_MemberName::MN_NULL_RESTRICTED_FIELD,
+  REFERENCE_KIND_SHIFT  = java_lang_invoke_MemberName::MN_REFERENCE_KIND_SHIFT,
+  REFERENCE_KIND_MASK   = java_lang_invoke_MemberName::MN_REFERENCE_KIND_MASK,
+  LM_UNCONDITIONAL      = java_lang_invoke_MemberName::MN_UNCONDITIONAL_MODE,
+  LM_MODULE             = java_lang_invoke_MemberName::MN_MODULE_MODE,
+  LM_TRUSTED            = java_lang_invoke_MemberName::MN_TRUSTED_MODE,
+  ALL_KINDS      = IS_METHOD | IS_OBJECT_CONSTRUCTOR | IS_FIELD | IS_TYPE
 };
 
 int MethodHandles::ref_kind_to_flags(int ref_kind) {
   assert(ref_kind_is_valid(ref_kind), "%d", ref_kind);
   int flags = (ref_kind << REFERENCE_KIND_SHIFT);
   if (ref_kind_is_field(ref_kind)) {
     flags |= IS_FIELD;
   } else if (ref_kind_is_method(ref_kind)) {
     flags |= IS_METHOD;
   } else if (ref_kind == JVM_REF_newInvokeSpecial) {
-    flags |= IS_CONSTRUCTOR;
+    flags |= IS_OBJECT_CONSTRUCTOR;
   }
   return flags;
 }
 
 Handle MethodHandles::resolve_MemberName_type(Handle mname, Klass* caller, TRAPS) {
@@ -171,11 +173,11 @@
   }
   Handle resolved;
   int flags = java_lang_invoke_MemberName::flags(mname());
   switch (flags & ALL_KINDS) {
     case IS_METHOD:
-    case IS_CONSTRUCTOR:
+    case IS_OBJECT_CONSTRUCTOR:
       resolved = SystemDictionary::find_method_handle_type(signature, caller, CHECK_(empty));
       break;
     case IS_FIELD:
       resolved = SystemDictionary::find_field_handle_type(signature, caller, CHECK_(empty));
       break;
@@ -313,12 +315,12 @@
 
   case CallInfo::direct_call:
     vmindex = Method::nonvirtual_vtable_index;
     if (m->is_static()) {
       flags |= IS_METHOD      | (JVM_REF_invokeStatic  << REFERENCE_KIND_SHIFT);
-    } else if (m->is_initializer()) {
-      flags |= IS_CONSTRUCTOR | (JVM_REF_invokeSpecial << REFERENCE_KIND_SHIFT);
+    } else if (m->is_object_constructor()) {
+      flags |= IS_OBJECT_CONSTRUCTOR | (JVM_REF_invokeSpecial << REFERENCE_KIND_SHIFT);
     } else {
       // "special" reflects that this is a direct call, not that it
       // necessarily originates from an invokespecial. We can also do
       // direct calls for private and/or final non-static methods.
       flags |= IS_METHOD      | (JVM_REF_invokeSpecial << REFERENCE_KIND_SHIFT);
@@ -350,13 +352,15 @@
   return mname();
 }
 
 oop MethodHandles::init_field_MemberName(Handle mname, fieldDescriptor& fd, bool is_setter) {
   InstanceKlass* ik = fd.field_holder();
-  int flags = (jushort)( fd.access_flags().as_short() & JVM_RECOGNIZED_FIELD_MODIFIERS );
+  int flags = (jushort)( fd.access_flags().as_short());
   flags |= IS_FIELD | ((fd.is_static() ? JVM_REF_getStatic : JVM_REF_getField) << REFERENCE_KIND_SHIFT);
   if (fd.is_trusted_final()) flags |= TRUSTED_FINAL;
+  if (fd.is_flat()) flags |= FLAT_FIELD;
+  if (fd.is_null_free_inline_type()) flags |= NULL_RESTRICTED;
   if (is_setter)  flags += ((JVM_REF_putField - JVM_REF_getField) << REFERENCE_KIND_SHIFT);
   int vmindex        = fd.offset();  // determines the field uniquely when combined with static bit
 
   oop mname_oop = mname();
   java_lang_invoke_MemberName::set_flags  (mname_oop, flags);
@@ -806,17 +810,17 @@
       }
       result.set_resolved_method_name(CHECK_(empty));
       oop mname2 = init_method_MemberName(mname, result);
       return Handle(THREAD, mname2);
     }
-  case IS_CONSTRUCTOR:
+  case IS_OBJECT_CONSTRUCTOR:
     {
       CallInfo result;
       LinkInfo link_info(defc, name, type, caller, access_check, loader_constraint_check);
       {
         assert(!HAS_PENDING_EXCEPTION, "");
-        if (name == vmSymbols::object_initializer_name()) {
+        if (name == vmSymbols::object_initializer_name() && type->is_void_method_signature()) {
           LinkResolver::resolve_special_call(result, Handle(), link_info, THREAD);
         } else {
           break;                // will throw after end of switch
         }
         if (HAS_PENDING_EXCEPTION) {
@@ -875,11 +879,11 @@
 
   if (have_defc && have_name && have_type)  return;  // nothing needed
 
   switch (flags & ALL_KINDS) {
   case IS_METHOD:
-  case IS_CONSTRUCTOR:
+  case IS_OBJECT_CONSTRUCTOR:
     {
       Method* vmtarget = java_lang_invoke_MemberName::vmtarget(mname());
       if (vmtarget == nullptr) {
         THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "nothing to expand");
       }
@@ -997,16 +1001,17 @@
 //
 
 #ifndef PRODUCT
 #define EACH_NAMED_CON(template, requirement) \
     template(java_lang_invoke_MemberName,MN_IS_METHOD) \
-    template(java_lang_invoke_MemberName,MN_IS_CONSTRUCTOR) \
+    template(java_lang_invoke_MemberName,MN_IS_OBJECT_CONSTRUCTOR) \
     template(java_lang_invoke_MemberName,MN_IS_FIELD) \
     template(java_lang_invoke_MemberName,MN_IS_TYPE) \
     template(java_lang_invoke_MemberName,MN_CALLER_SENSITIVE) \
     template(java_lang_invoke_MemberName,MN_TRUSTED_FINAL) \
     template(java_lang_invoke_MemberName,MN_HIDDEN_MEMBER) \
+    template(java_lang_invoke_MemberName,MN_FLAT_FIELD) \
     template(java_lang_invoke_MemberName,MN_REFERENCE_KIND_SHIFT) \
     template(java_lang_invoke_MemberName,MN_REFERENCE_KIND_MASK) \
     template(java_lang_invoke_MemberName,MN_NESTMATE_CLASS) \
     template(java_lang_invoke_MemberName,MN_HIDDEN_CLASS) \
     template(java_lang_invoke_MemberName,MN_STRONG_LOADER_LINK) \
@@ -1140,11 +1145,11 @@
       return nullptr;
     }
     if ((flags & ALL_KINDS) == IS_FIELD) {
       THROW_MSG_NULL(vmSymbols::java_lang_NoSuchFieldError(), "field resolution failed");
     } else if ((flags & ALL_KINDS) == IS_METHOD ||
-               (flags & ALL_KINDS) == IS_CONSTRUCTOR) {
+               (flags & ALL_KINDS) == IS_OBJECT_CONSTRUCTOR) {
       THROW_MSG_NULL(vmSymbols::java_lang_NoSuchMethodError(), "method resolution failed");
     } else {
       THROW_MSG_NULL(vmSymbols::java_lang_LinkageError(), "resolution failed");
     }
   }
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
@@ -32,19 +32,25 @@
 #include "jfr/jfrEvents.hpp"
 #include "jni.h"
 #include "jvm.h"
 #include "memory/allocation.inline.hpp"
 #include "memory/resourceArea.hpp"
+#include "logging/log.hpp"
+#include "logging/logStream.hpp"
 #include "oops/access.inline.hpp"
 #include "oops/fieldStreams.inline.hpp"
+#include "oops/flatArrayKlass.hpp"
+#include "oops/flatArrayOop.inline.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "oops/instanceKlass.inline.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/objArrayOop.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/typeArrayOop.inline.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "prims/unsafe.hpp"
+#include "runtime/fieldDescriptor.inline.hpp"
 #include "runtime/globals.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/interfaceSupport.inline.hpp"
 #include "runtime/javaThread.inline.hpp"
 #include "runtime/jniHandles.inline.hpp"
@@ -169,11 +175,10 @@
 }
 jlong Unsafe_field_offset_from_byte_offset(jlong byte_offset) {
   return byte_offset;
 }
 
-
 ///// Data read/writes on the Java heap and in native (off-heap) memory
 
 /**
  * Helper class to wrap memory accesses in JavaThread::doing_unsafe_access()
  */
@@ -245,14 +250,14 @@
     return normalize_for_read(*addr());
   }
 
   void put(T x) {
     GuardUnsafeAccess guard(_thread);
+    assert(_obj == nullptr || !_obj->is_inline_type() || _obj->mark().is_larval_state(), "must be an object instance or a larval inline type");
     *addr() = normalize_for_write(x);
   }
 
-
   T get_volatile() {
     GuardUnsafeAccess guard(_thread);
     volatile T ret = RawAccess<MO_SEQ_CST>::load(addr());
     return normalize_for_read(ret);
   }
@@ -261,10 +266,72 @@
     GuardUnsafeAccess guard(_thread);
     RawAccess<MO_SEQ_CST>::store(addr(), normalize_for_write(x));
   }
 };
 
+#ifdef ASSERT
+/*
+ * Get the field descriptor of the field of the given object at the given offset.
+ */
+static bool get_field_descriptor(oop p, jlong offset, fieldDescriptor* fd) {
+  bool found = false;
+  Klass* k = p->klass();
+  if (k->is_instance_klass()) {
+    InstanceKlass* ik = InstanceKlass::cast(k);
+    found = ik->find_field_from_offset((int)offset, false, fd);
+    if (!found && ik->is_mirror_instance_klass()) {
+      Klass* k2 = java_lang_Class::as_Klass(p);
+      if (k2->is_instance_klass()) {
+        ik = InstanceKlass::cast(k2);
+        found = ik->find_field_from_offset((int)offset, true, fd);
+      }
+    }
+  }
+  return found;
+}
+#endif // ASSERT
+
+static void assert_and_log_unsafe_value_access(oop p, jlong offset, InlineKlass* vk) {
+  Klass* k = p->klass();
+#ifdef ASSERT
+  if (k->is_instance_klass()) {
+    assert_field_offset_sane(p, offset);
+    fieldDescriptor fd;
+    bool found = get_field_descriptor(p, offset, &fd);
+    if (found) {
+      assert(found, "value field not found");
+      assert(fd.is_flat(), "field not flat");
+    } else {
+      if (log_is_enabled(Trace, valuetypes)) {
+        log_trace(valuetypes)("not a field in %s at offset " UINT64_FORMAT_X,
+                              p->klass()->external_name(), (uint64_t)offset);
+      }
+    }
+  } else if (k->is_flatArray_klass()) {
+    FlatArrayKlass* vak = FlatArrayKlass::cast(k);
+    int index = (offset - vak->array_header_in_bytes()) / vak->element_byte_size();
+    address dest = (address)((flatArrayOop)p)->value_at_addr(index, vak->layout_helper());
+    assert(dest == (cast_from_oop<address>(p) + offset), "invalid offset");
+  } else {
+    ShouldNotReachHere();
+  }
+#endif // ASSERT
+  if (log_is_enabled(Trace, valuetypes)) {
+    if (k->is_flatArray_klass()) {
+      FlatArrayKlass* vak = FlatArrayKlass::cast(k);
+      int index = (offset - vak->array_header_in_bytes()) / vak->element_byte_size();
+      address dest = (address)((flatArrayOop)p)->value_at_addr(index, vak->layout_helper());
+      log_trace(valuetypes)("%s array type %s index %d element size %d offset " UINT64_FORMAT_X " at " INTPTR_FORMAT,
+                            p->klass()->external_name(), vak->external_name(),
+                            index, vak->element_byte_size(), (uint64_t)offset, p2i(dest));
+    } else {
+      log_trace(valuetypes)("%s field type %s at offset " UINT64_FORMAT_X,
+                            p->klass()->external_name(), vk->external_name(), (uint64_t)offset);
+    }
+  }
+}
+
 // These functions allow a null base pointer with an arbitrary address.
 // But if the base pointer is non-null, the offset should make some sense.
 // That is, it should be in the range [0, MAX_OBJECT_SIZE].
 UNSAFE_ENTRY(jobject, Unsafe_GetReference(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) {
   oop p = JNIHandles::resolve(obj);
@@ -275,13 +342,93 @@
 
 UNSAFE_ENTRY(void, Unsafe_PutReference(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject x_h)) {
   oop x = JNIHandles::resolve(x_h);
   oop p = JNIHandles::resolve(obj);
   assert_field_offset_sane(p, offset);
+  assert(!p->is_inline_type() || p->mark().is_larval_state(), "must be an object instance or a larval inline type");
   HeapAccess<ON_UNKNOWN_OOP_REF>::oop_store_at(p, offset, x);
 } UNSAFE_END
 
+UNSAFE_ENTRY(jlong, Unsafe_ValueHeaderSize(JNIEnv *env, jobject unsafe, jclass c)) {
+  Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(c));
+  InlineKlass* vk = InlineKlass::cast(k);
+  return vk->first_field_offset();
+} UNSAFE_END
+
+UNSAFE_ENTRY(jboolean, Unsafe_IsFlatField(JNIEnv *env, jobject unsafe, jobject o)) {
+  oop f = JNIHandles::resolve_non_null(o);
+  Klass* k = java_lang_Class::as_Klass(java_lang_reflect_Field::clazz(f));
+  int slot = java_lang_reflect_Field::slot(f);
+  return InstanceKlass::cast(k)->field_is_flat(slot);
+} UNSAFE_END
+
+UNSAFE_ENTRY(jboolean, Unsafe_HasNullMarker(JNIEnv *env, jobject unsage, jobject o)) {
+  oop f = JNIHandles::resolve_non_null(o);
+  Klass* k = java_lang_Class::as_Klass(java_lang_reflect_Field::clazz(f));
+  int slot = java_lang_reflect_Field::slot(f);
+  return InstanceKlass::cast(k)->field_has_null_marker(slot);
+} UNSAFE_END
+
+UNSAFE_ENTRY(jint, Unsafe_NullMarkerOffset(JNIEnv *env, jobject unsage, jobject o)) {
+  oop f = JNIHandles::resolve_non_null(o);
+  Klass* k = java_lang_Class::as_Klass(java_lang_reflect_Field::clazz(f));
+  int slot = java_lang_reflect_Field::slot(f);
+  return InstanceKlass::cast(k)->null_marker_offsets_array()->at(slot);
+} UNSAFE_END
+
+UNSAFE_ENTRY(jboolean, Unsafe_IsFlatArray(JNIEnv *env, jobject unsafe, jclass c)) {
+  Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(c));
+  return k->is_flatArray_klass();
+} UNSAFE_END
+
+UNSAFE_ENTRY(jobject, Unsafe_UninitializedDefaultValue(JNIEnv *env, jobject unsafe, jclass vc)) {
+  Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(vc));
+  InlineKlass* vk = InlineKlass::cast(k);
+  oop v = vk->default_value();
+  return JNIHandles::make_local(THREAD, v);
+} UNSAFE_END
+
+UNSAFE_ENTRY(jobject, Unsafe_GetValue(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jclass vc)) {
+  oop base = JNIHandles::resolve(obj);
+  Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(vc));
+  InlineKlass* vk = InlineKlass::cast(k);
+  assert_and_log_unsafe_value_access(base, offset, vk);
+  Handle base_h(THREAD, base);
+  oop v = vk->read_flat_field(base_h(), offset, CHECK_NULL);
+  return JNIHandles::make_local(THREAD, v);
+} UNSAFE_END
+
+UNSAFE_ENTRY(void, Unsafe_PutValue(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jclass vc, jobject value)) {
+  oop base = JNIHandles::resolve(obj);
+  Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(vc));
+  InlineKlass* vk = InlineKlass::cast(k);
+  assert(!base->is_inline_type() || base->mark().is_larval_state(), "must be an object instance or a larval inline type");
+  assert_and_log_unsafe_value_access(base, offset, vk);
+  oop v = JNIHandles::resolve(value);
+  vk->write_flat_field(base, offset, v, CHECK);
+} UNSAFE_END
+
+UNSAFE_ENTRY(jobject, Unsafe_MakePrivateBuffer(JNIEnv *env, jobject unsafe, jobject value)) {
+  oop v = JNIHandles::resolve_non_null(value);
+  assert(v->is_inline_type(), "must be an inline type instance");
+  Handle vh(THREAD, v);
+  InlineKlass* vk = InlineKlass::cast(v->klass());
+  instanceOop new_value = vk->allocate_instance_buffer(CHECK_NULL);
+  vk->inline_copy_oop_to_new_oop(vh(),  new_value);
+  markWord mark = new_value->mark();
+  new_value->set_mark(mark.enter_larval_state());
+  return JNIHandles::make_local(THREAD, new_value);
+} UNSAFE_END
+
+UNSAFE_ENTRY(jobject, Unsafe_FinishPrivateBuffer(JNIEnv *env, jobject unsafe, jobject value)) {
+  oop v = JNIHandles::resolve(value);
+  assert(v->mark().is_larval_state(), "must be a larval value");
+  markWord mark = v->mark();
+  v->set_mark(mark.exit_larval_state());
+  return JNIHandles::make_local(THREAD, v);
+} UNSAFE_END
+
 UNSAFE_ENTRY(jobject, Unsafe_GetReferenceVolatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) {
   oop p = JNIHandles::resolve(obj);
   assert_field_offset_sane(p, offset);
   oop v = HeapAccess<MO_SEQ_CST | ON_UNKNOWN_OOP_REF>::oop_load_at(p, offset);
   return JNIHandles::make_local(THREAD, v);
@@ -593,10 +740,15 @@
   } else if (k->is_typeArray_klass()) {
     TypeArrayKlass* tak = TypeArrayKlass::cast(k);
     base  = tak->array_header_in_bytes();
     assert(base == arrayOopDesc::base_offset_in_bytes(tak->element_type()), "array_header_size semantics ok");
     scale = (1 << tak->log2_element_size());
+  } else if (k->is_flatArray_klass()) {
+    FlatArrayKlass* vak = FlatArrayKlass::cast(k);
+    InlineKlass* vklass = vak->element_klass();
+    base = vak->array_header_in_bytes();
+    scale = vak->element_byte_size();
   } else {
     ShouldNotReachHere();
   }
 }
 
@@ -628,10 +780,16 @@
   // but requires it to be linear in byte offset.
   return field_offset_from_byte_offset(scale) - field_offset_from_byte_offset(0);
 } UNSAFE_END
 
 
+UNSAFE_ENTRY(jlong, Unsafe_GetObjectSize0(JNIEnv* env, jobject o, jobject obj))
+  oop p = JNIHandles::resolve(obj);
+  return p->size() * HeapWordSize;
+UNSAFE_END
+
+
 static inline void throw_new(JNIEnv *env, const char *ename) {
   jclass cls = env->FindClass(ename);
   if (env->ExceptionCheck()) {
     env->ExceptionClear();
     tty->print_cr("Unsafe: cannot throw %s because FindClass has failed", ename);
@@ -847,22 +1005,33 @@
 
 #define CC (char*)  /*cast a literal from (const char*)*/
 #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f)
 
 #define DECLARE_GETPUTOOP(Type, Desc) \
-    {CC "get" #Type,      CC "(" OBJ "J)" #Desc,       FN_PTR(Unsafe_Get##Type)}, \
-    {CC "put" #Type,      CC "(" OBJ "J" #Desc ")V",   FN_PTR(Unsafe_Put##Type)}, \
-    {CC "get" #Type "Volatile",      CC "(" OBJ "J)" #Desc,       FN_PTR(Unsafe_Get##Type##Volatile)}, \
-    {CC "put" #Type "Volatile",      CC "(" OBJ "J" #Desc ")V",   FN_PTR(Unsafe_Put##Type##Volatile)}
+    {CC "get"  #Type,      CC "(" OBJ "J)" #Desc,                 FN_PTR(Unsafe_Get##Type)}, \
+    {CC "put"  #Type,      CC "(" OBJ "J" #Desc ")V",             FN_PTR(Unsafe_Put##Type)}, \
+    {CC "get"  #Type "Volatile",      CC "(" OBJ "J)" #Desc,      FN_PTR(Unsafe_Get##Type##Volatile)}, \
+    {CC "put"  #Type "Volatile",      CC "(" OBJ "J" #Desc ")V",  FN_PTR(Unsafe_Put##Type##Volatile)}
 
 
 static JNINativeMethod jdk_internal_misc_Unsafe_methods[] = {
     {CC "getReference",         CC "(" OBJ "J)" OBJ "",   FN_PTR(Unsafe_GetReference)},
     {CC "putReference",         CC "(" OBJ "J" OBJ ")V",  FN_PTR(Unsafe_PutReference)},
     {CC "getReferenceVolatile", CC "(" OBJ "J)" OBJ,      FN_PTR(Unsafe_GetReferenceVolatile)},
     {CC "putReferenceVolatile", CC "(" OBJ "J" OBJ ")V",  FN_PTR(Unsafe_PutReferenceVolatile)},
 
+    {CC "isFlatArray", CC "(" CLS ")Z",                   FN_PTR(Unsafe_IsFlatArray)},
+    {CC "isFlatField0", CC "(" OBJ ")Z",                  FN_PTR(Unsafe_IsFlatField)},
+    {CC "hasNullMarker0"   , CC "(" OBJ ")Z",                    FN_PTR(Unsafe_HasNullMarker)},
+    {CC "nullMarkerOffset0", CC "(" OBJ ")I",                    FN_PTR(Unsafe_NullMarkerOffset)},
+    {CC "getValue",         CC "(" OBJ "J" CLS ")" OBJ,   FN_PTR(Unsafe_GetValue)},
+    {CC "putValue",         CC "(" OBJ "J" CLS OBJ ")V",  FN_PTR(Unsafe_PutValue)},
+    {CC "uninitializedDefaultValue", CC "(" CLS ")" OBJ,  FN_PTR(Unsafe_UninitializedDefaultValue)},
+    {CC "makePrivateBuffer",     CC "(" OBJ ")" OBJ,      FN_PTR(Unsafe_MakePrivateBuffer)},
+    {CC "finishPrivateBuffer",   CC "(" OBJ ")" OBJ,      FN_PTR(Unsafe_FinishPrivateBuffer)},
+    {CC "valueHeaderSize",       CC "(" CLS ")J",         FN_PTR(Unsafe_ValueHeaderSize)},
+
     {CC "getUncompressedObject", CC "(" ADR ")" OBJ,  FN_PTR(Unsafe_GetUncompressedObject)},
 
     DECLARE_GETPUTOOP(Boolean, Z),
     DECLARE_GETPUTOOP(Byte, B),
     DECLARE_GETPUTOOP(Short, S),
@@ -881,10 +1050,11 @@
     {CC "staticFieldOffset0", CC "(" FLD ")J",           FN_PTR(Unsafe_StaticFieldOffset0)},
     {CC "staticFieldBase0",   CC "(" FLD ")" OBJ,        FN_PTR(Unsafe_StaticFieldBase0)},
     {CC "ensureClassInitialized0", CC "(" CLS ")V",      FN_PTR(Unsafe_EnsureClassInitialized0)},
     {CC "arrayBaseOffset0",   CC "(" CLS ")I",           FN_PTR(Unsafe_ArrayBaseOffset0)},
     {CC "arrayIndexScale0",   CC "(" CLS ")I",           FN_PTR(Unsafe_ArrayIndexScale0)},
+    {CC "getObjectSize0",     CC "(Ljava/lang/Object;)J", FN_PTR(Unsafe_GetObjectSize0)},
 
     {CC "defineClass0",       CC "(" DC_Args ")" CLS,    FN_PTR(Unsafe_DefineClass0)},
     {CC "allocateInstance",   CC "(" CLS ")" OBJ,        FN_PTR(Unsafe_AllocateInstance)},
     {CC "throwException",     CC "(" THR ")V",           FN_PTR(Unsafe_ThrowException)},
     {CC "compareAndSetReference",CC "(" OBJ "J" OBJ "" OBJ ")Z", FN_PTR(Unsafe_CompareAndSetReference)},
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
@@ -49,25 +49,28 @@
 #include "gc/shared/genArguments.hpp"
 #include "jvmtifiles/jvmtiEnv.hpp"
 #include "logging/log.hpp"
 #include "memory/iterator.hpp"
 #include "memory/metadataFactory.hpp"
+#include "memory/iterator.inline.hpp"
 #include "memory/metaspace/testHelpers.hpp"
 #include "memory/metaspaceUtils.hpp"
 #include "memory/oopFactory.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
 #include "nmt/mallocSiteTable.hpp"
 #include "nmt/memTracker.hpp"
 #include "oops/array.hpp"
 #include "oops/compressedOops.hpp"
+#include "oops/compressedOops.inline.hpp"
 #include "oops/constantPool.inline.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/method.inline.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "oops/objArrayOop.inline.hpp"
 #include "oops/oop.inline.hpp"
+#include "oops/objArrayOop.inline.hpp"
 #include "oops/typeArrayOop.inline.hpp"
 #include "prims/jvmtiEnvBase.hpp"
 #include "prims/resolvedMethodTable.hpp"
 #include "prims/wbtestmethods/parserTests.hpp"
 #include "prims/whitebox.inline.hpp"
@@ -1876,10 +1879,97 @@
 
 WB_ENTRY(jint, WB_ConstantPoolEncodeIndyIndex(JNIEnv* env, jobject wb, jint index))
   return ConstantPool::encode_invokedynamic_index(index);
 WB_END
 
+WB_ENTRY(jobjectArray, WB_getObjectsViaKlassOopMaps(JNIEnv* env, jobject wb, jobject thing))
+  oop aoop = JNIHandles::resolve(thing);
+  if (!aoop->is_instance()) {
+    return nullptr;
+  }
+  instanceHandle ih(THREAD, (instanceOop) aoop);
+  InstanceKlass* klass = InstanceKlass::cast(ih->klass());
+  if (klass->nonstatic_oop_map_count() == 0) {
+    return nullptr;
+  }
+  const OopMapBlock* map = klass->start_of_nonstatic_oop_maps();
+  const OopMapBlock* const end = map + klass->nonstatic_oop_map_count();
+  int oop_count = 0;
+  while (map < end) {
+    oop_count += map->count();
+    map++;
+  }
+
+  objArrayHandle result_array =
+      oopFactory::new_objArray_handle(vmClasses::Object_klass(), oop_count, CHECK_NULL);
+  map = klass->start_of_nonstatic_oop_maps();
+  int index = 0;
+  while (map < end) {
+    int offset = map->offset();
+    for (unsigned int j = 0; j < map->count(); j++) {
+      result_array->obj_at_put(index++, ih->obj_field(offset));
+      offset += heapOopSize;
+    }
+    map++;
+  }
+  return (jobjectArray)JNIHandles::make_local(THREAD, result_array());
+WB_END
+
+class CollectOops : public BasicOopIterateClosure {
+ public:
+  GrowableArray<Handle>* array;
+
+  jobjectArray create_jni_result(JNIEnv* env, TRAPS) {
+    objArrayHandle result_array =
+        oopFactory::new_objArray_handle(vmClasses::Object_klass(), array->length(), CHECK_NULL);
+    for (int i = 0 ; i < array->length(); i++) {
+      result_array->obj_at_put(i, array->at(i)());
+    }
+    return (jobjectArray)JNIHandles::make_local(THREAD, result_array());
+  }
+
+  void add_oop(oop o) {
+    Handle oh = Handle(Thread::current(), o);
+    // Value might be oop, but JLS can't see as Object, just iterate through it...
+    if (oh != nullptr && oh->is_inline_type()) {
+      oh->oop_iterate(this);
+    } else {
+      array->append(oh);
+    }
+  }
+
+  void do_oop(oop* o) { add_oop(HeapAccess<>::oop_load(o)); }
+  void do_oop(narrowOop* v) { add_oop(HeapAccess<>::oop_load(v)); }
+};
+
+
+WB_ENTRY(jobjectArray, WB_getObjectsViaOopIterator(JNIEnv* env, jobject wb, jobject thing))
+  ResourceMark rm(thread);
+  Handle objh(thread, JNIHandles::resolve(thing));
+  GrowableArray<Handle>* array = new GrowableArray<Handle>(128);
+  CollectOops collectOops;
+  collectOops.array = array;
+  objh->oop_iterate(&collectOops);
+  return collectOops.create_jni_result(env, THREAD);
+WB_END
+
+WB_ENTRY(jobjectArray, WB_getObjectsViaFrameOopIterator(JNIEnv* env, jobject wb, jint depth))
+  ResourceMark rm(THREAD);
+  GrowableArray<Handle>* array = new GrowableArray<Handle>(128);
+  CollectOops collectOops;
+  collectOops.array = array;
+  StackFrameStream sfs(thread, false /* update */, true /* process_frames */);
+  while (depth > 0) { // Skip the native WB API frame
+    sfs.next();
+    frame* f = sfs.current();
+    f->oops_do(&collectOops, nullptr, sfs.register_map());
+    depth--;
+  }
+  return collectOops.create_jni_result(env, THREAD);
+WB_END
+
+
 WB_ENTRY(jint, WB_getFieldEntriesLength(JNIEnv* env, jobject wb, jclass klass))
   InstanceKlass* ik = InstanceKlass::cast(java_lang_Class::as_Klass(JNIHandles::resolve(klass)));
   ConstantPool* cp = ik->constants();
   if (cp->cache() == nullptr) {
     return -1;
@@ -2833,10 +2923,16 @@
   {CC"forceClassLoaderStatsSafepoint", CC"()V",       (void*)&WB_ForceClassLoaderStatsSafepoint },
   {CC"getConstantPool0",   CC"(Ljava/lang/Class;)J",  (void*)&WB_GetConstantPool    },
   {CC"getResolvedReferences0", CC"(Ljava/lang/Class;)[Ljava/lang/Object;", (void*)&WB_GetResolvedReferences},
   {CC"encodeConstantPoolIndyIndex0",
       CC"(I)I",                      (void*)&WB_ConstantPoolEncodeIndyIndex},
+  {CC"getObjectsViaKlassOopMaps0",
+      CC"(Ljava/lang/Object;)[Ljava/lang/Object;",    (void*)&WB_getObjectsViaKlassOopMaps},
+  {CC"getObjectsViaOopIterator0",
+          CC"(Ljava/lang/Object;)[Ljava/lang/Object;",(void*)&WB_getObjectsViaOopIterator},
+  {CC"getObjectsViaFrameOopIterator",
+      CC"(I)[Ljava/lang/Object;",                     (void*)&WB_getObjectsViaFrameOopIterator},
   {CC"getFieldEntriesLength0", CC"(Ljava/lang/Class;)I",  (void*)&WB_getFieldEntriesLength},
   {CC"getFieldCPIndex0",    CC"(Ljava/lang/Class;I)I", (void*)&WB_getFieldCPIndex},
   {CC"getMethodEntriesLength0", CC"(Ljava/lang/Class;)I",  (void*)&WB_getMethodEntriesLength},
   {CC"getMethodCPIndex0",    CC"(Ljava/lang/Class;I)I", (void*)&WB_getMethodCPIndex},
   {CC"getIndyInfoLength0", CC"(Ljava/lang/Class;)I",  (void*)&WB_getIndyInfoLength},
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
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
+ * 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.
@@ -72,10 +72,11 @@
 #if INCLUDE_JFR
 #include "jfr/jfr.hpp"
 #endif
 
 #include <limits>
+#include <string.h>
 
 static const char _default_java_launcher[] = "generic";
 
 #define DEFAULT_JAVA_LAUNCHER _default_java_launcher
 
@@ -1789,11 +1790,10 @@
 
 unsigned int addreads_count = 0;
 unsigned int addexports_count = 0;
 unsigned int addopens_count = 0;
 unsigned int addmods_count = 0;
-unsigned int patch_mod_count = 0;
 unsigned int enable_native_access_count = 0;
 
 // Check the consistency of vm_init_args
 bool Arguments::check_vm_args_consistency() {
   // Method for adding checks for flag consistency.
@@ -1836,10 +1836,24 @@
     FLAG_SET_CMDLINE(StackReservedPages, 0);
     warning("Reserved Stack Area not supported on this platform");
   }
 #endif
 
+  if (AMD64_ONLY(false &&) AARCH64_ONLY(false &&) !FLAG_IS_DEFAULT(InlineTypePassFieldsAsArgs)) {
+    FLAG_SET_CMDLINE(InlineTypePassFieldsAsArgs, false);
+    warning("InlineTypePassFieldsAsArgs is not supported on this platform");
+  }
+
+  if (AMD64_ONLY(false &&) AARCH64_ONLY(false &&) !FLAG_IS_DEFAULT(InlineTypeReturnedAsFields)) {
+    FLAG_SET_CMDLINE(InlineTypeReturnedAsFields, false);
+    warning("InlineTypeReturnedAsFields is not supported on this platform");
+  }
+
+  // Valhalla missing LM_LIGHTWEIGHT support just now
+  if (EnableValhalla && LockingMode != LM_LEGACY) {
+    FLAG_SET_CMDLINE(LockingMode, LM_LEGACY);
+  }
 #if !defined(X86) && !defined(AARCH64) && !defined(RISCV64) && !defined(ARM) && !defined(PPC64) && !defined(S390)
   if (LockingMode == LM_LIGHTWEIGHT) {
     FLAG_SET_CMDLINE(LockingMode, LM_LEGACY);
     warning("New lightweight locking not supported on this platform");
   }
@@ -1962,12 +1976,10 @@
 
 jint Arguments::parse_vm_init_args(const JavaVMInitArgs *vm_options_args,
                                    const JavaVMInitArgs *java_tool_options_args,
                                    const JavaVMInitArgs *java_options_args,
                                    const JavaVMInitArgs *cmd_line_args) {
-  bool patch_mod_javabase = false;
-
   // Save default settings for some mode flags
   Arguments::_AlwaysCompileLoopMethods = AlwaysCompileLoopMethods;
   Arguments::_UseOnStackReplacement    = UseOnStackReplacement;
   Arguments::_ClipInlining             = ClipInlining;
   Arguments::_BackgroundCompilation    = BackgroundCompilation;
@@ -1977,31 +1989,31 @@
 
   // Setup flags for mixed which is the default
   set_mode_flags(_mixed);
 
   // Parse args structure generated from java.base vm options resource
-  jint result = parse_each_vm_init_arg(vm_options_args, &patch_mod_javabase, JVMFlagOrigin::JIMAGE_RESOURCE);
+  jint result = parse_each_vm_init_arg(vm_options_args, JVMFlagOrigin::JIMAGE_RESOURCE);
   if (result != JNI_OK) {
     return result;
   }
 
   // Parse args structure generated from JAVA_TOOL_OPTIONS environment
   // variable (if present).
-  result = parse_each_vm_init_arg(java_tool_options_args, &patch_mod_javabase, JVMFlagOrigin::ENVIRON_VAR);
+  result = parse_each_vm_init_arg(java_tool_options_args, JVMFlagOrigin::ENVIRON_VAR);
   if (result != JNI_OK) {
     return result;
   }
 
   // Parse args structure generated from the command line flags.
-  result = parse_each_vm_init_arg(cmd_line_args, &patch_mod_javabase, JVMFlagOrigin::COMMAND_LINE);
+  result = parse_each_vm_init_arg(cmd_line_args, JVMFlagOrigin::COMMAND_LINE);
   if (result != JNI_OK) {
     return result;
   }
 
   // Parse args structure generated from the _JAVA_OPTIONS environment
   // variable (if present) (mimics classic VM)
-  result = parse_each_vm_init_arg(java_options_args, &patch_mod_javabase, JVMFlagOrigin::ENVIRON_VAR);
+  result = parse_each_vm_init_arg(java_options_args, JVMFlagOrigin::ENVIRON_VAR);
   if (result != JNI_OK) {
     return result;
   }
 
   // Disable CDS for exploded image
@@ -2018,11 +2030,11 @@
   os::init_container_support();
 
   SystemMemoryBarrier::initialize();
 
   // Do final processing now that all arguments have been parsed
-  result = finalize_vm_init_args(patch_mod_javabase);
+  result = finalize_vm_init_args();
   if (result != JNI_OK) {
     return result;
   }
 
   return JNI_OK;
@@ -2071,11 +2083,11 @@
   }
 
   return false;
 }
 
-int Arguments::process_patch_mod_option(const char* patch_mod_tail, bool* patch_mod_javabase) {
+int Arguments::process_patch_mod_option(const char* patch_mod_tail) {
   // --patch-module=<module>=<file>(<pathsep><file>)*
   assert(patch_mod_tail != nullptr, "Unexpected null patch-module value");
   // Find the equal sign between the module name and the path specification
   const char* module_equal = strchr(patch_mod_tail, '=');
   if (module_equal == nullptr) {
@@ -2087,22 +2099,83 @@
     char* module_name = NEW_C_HEAP_ARRAY_RETURN_NULL(char, module_len+1, mtArguments);
     if (module_name != nullptr) {
       memcpy(module_name, patch_mod_tail, module_len);
       *(module_name + module_len) = '\0';
       // The path piece begins one past the module_equal sign
-      add_patch_mod_prefix(module_name, module_equal + 1, patch_mod_javabase);
+      add_patch_mod_prefix(module_name, module_equal + 1, false /* no append */, false /* no cds */);
       FREE_C_HEAP_ARRAY(char, module_name);
-      if (!create_numbered_module_property("jdk.module.patch", patch_mod_tail, patch_mod_count++)) {
-        return JNI_ENOMEM;
-      }
     } else {
       return JNI_ENOMEM;
     }
   }
   return JNI_OK;
 }
 
+// VALUECLASS_STR must match string used in the build
+#define VALUECLASS_STR "valueclasses"
+#define VALUECLASS_JAR "-" VALUECLASS_STR ".jar"
+
+// Finalize --patch-module args and --enable-preview related to value class module patches.
+// Create all numbered properties passing module patches.
+int Arguments::finalize_patch_module() {
+  // If --enable-preview and EnableValhalla is true, each module may have value classes that
+  // are to be patched into the module.
+  // For each <module>-valueclasses.jar in <JAVA_HOME>/lib/valueclasses/
+  // appends the equivalent of --patch-module <module>=<JAVA_HOME>/lib/valueclasses/<module>-valueclasses.jar
+  if (enable_preview() && EnableValhalla) {
+    char * valueclasses_dir = AllocateHeap(JVM_MAXPATHLEN, mtArguments);
+    const char * fileSep = os::file_separator();
+
+    jio_snprintf(valueclasses_dir, JVM_MAXPATHLEN, "%s%slib%s" VALUECLASS_STR "%s",
+                 Arguments::get_java_home(), fileSep, fileSep, fileSep);
+    DIR* dir = os::opendir(valueclasses_dir);
+    if (dir != nullptr) {
+      char * module_name = AllocateHeap(JVM_MAXPATHLEN, mtArguments);
+      char * path = AllocateHeap(JVM_MAXPATHLEN, mtArguments);
+
+      for (dirent * entry = os::readdir(dir); entry != nullptr; entry = os::readdir(dir)) {
+        // Test if file ends-with "-valueclasses.jar"
+        int len = (int)strlen(entry->d_name) - (sizeof(VALUECLASS_JAR) - 1);
+        if (len <= 0 || strcmp(&entry->d_name[len], VALUECLASS_JAR) != 0) {
+          continue;         // too short or not the expected suffix
+        }
+
+        strcpy(module_name, entry->d_name);
+        module_name[len] = '\0';     // truncate to just module-name
+
+        jio_snprintf(path, JVM_MAXPATHLEN, "%s%s", valueclasses_dir, &entry->d_name);
+        add_patch_mod_prefix(module_name, path, true /* append */, true /* cds OK*/);
+        log_info(class)("--enable-preview appending value classes for module %s: %s", module_name, entry->d_name);
+      }
+      FreeHeap(module_name);
+      FreeHeap(path);
+      os::closedir(dir);
+    }
+    FreeHeap(valueclasses_dir);
+  }
+
+  // Create numbered properties for each module that has been patched either
+  // by --patch-module or --enable-preview
+  // Format is "jdk.module.patch.<n>=<module_name>=<path>"
+  if (_patch_mod_prefix != nullptr) {
+    char * prop_value = AllocateHeap(JVM_MAXPATHLEN + JVM_MAXPATHLEN + 1, mtArguments);
+    unsigned int patch_mod_count = 0;
+
+    for (GrowableArrayIterator<ModulePatchPath *> it = _patch_mod_prefix->begin();
+            it != _patch_mod_prefix->end(); ++it) {
+      jio_snprintf(prop_value, JVM_MAXPATHLEN + JVM_MAXPATHLEN + 1, "%s=%s",
+                   (*it)->module_name(), (*it)->path_string());
+      if (!create_numbered_module_property("jdk.module.patch", prop_value, patch_mod_count++)) {
+        FreeHeap(prop_value);
+        return JNI_ENOMEM;
+      }
+    }
+    FreeHeap(prop_value);
+  }
+  return JNI_OK;
+}
+
 // Parse -Xss memory string parameter and convert to ThreadStackSize in K.
 jint Arguments::parse_xss(const JavaVMOption* option, const char* tail, intx* out_ThreadStackSize) {
   // The min and max sizes match the values in globals.hpp, but scaled
   // with K. The values have been chosen so that alignment with page
   // size doesn't change the max value, which makes the conversions
@@ -2153,11 +2226,11 @@
   *out_ThreadStackSize = (intx)size_in_K;
 
   return JNI_OK;
 }
 
-jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_mod_javabase, JVMFlagOrigin origin) {
+jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, JVMFlagOrigin origin) {
   // For match_option to return remaining or value part of option string
   const char* tail;
 
   // iterate over arguments
   for (int index = 0; index < args->nOptions; index++) {
@@ -2276,11 +2349,11 @@
       if (!create_module_property("jdk.module.upgrade.path", tail, ExternalProperty)) {
         return JNI_ENOMEM;
       }
     } else if (match_option(option, "--patch-module=", &tail)) {
       // --patch-module=<module>=<file>(<pathsep><file>)*
-      int res = process_patch_mod_option(tail, patch_mod_javabase);
+      int res = process_patch_mod_option(tail);
       if (res != JNI_OK) {
         return res;
       }
     } else if (match_option(option, "--illegal-access=", &tail)) {
       char version[256];
@@ -2337,10 +2410,14 @@
       }
 #endif // !INCLUDE_JVMTI
     // --enable_preview
     } else if (match_option(option, "--enable-preview")) {
       set_enable_preview();
+      // --enable-preview enables Valhalla, EnableValhalla VM option will eventually be removed before integration
+      if (FLAG_SET_CMDLINE(EnableValhalla, true) != JVMFlag::SUCCESS) {
+        return JNI_EINVAL;
+      }
     // -Xnoclassgc
     } else if (match_option(option, "-Xnoclassgc")) {
       if (FLAG_SET_CMDLINE(ClassUnloading, false) != JVMFlag::SUCCESS) {
         return JNI_EINVAL;
       }
@@ -2816,29 +2893,41 @@
   fix_appclasspath();
 
   return JNI_OK;
 }
 
-void Arguments::add_patch_mod_prefix(const char* module_name, const char* path, bool* patch_mod_javabase) {
-  // For java.base check for duplicate --patch-module options being specified on the command line.
-  // This check is only required for java.base, all other duplicate module specifications
-  // will be checked during module system initialization.  The module system initialization
-  // will throw an ExceptionInInitializerError if this situation occurs.
-  if (strcmp(module_name, JAVA_BASE_NAME) == 0) {
-    if (*patch_mod_javabase) {
-      vm_exit_during_initialization("Cannot specify " JAVA_BASE_NAME " more than once to --patch-module");
-    } else {
-      *patch_mod_javabase = true;
+void Arguments::add_patch_mod_prefix(const char* module_name, const char* path, bool allow_append, bool allow_cds) {
+  if (!allow_cds) {
+    CDSConfig::set_module_patching_disables_cds();
+    if (strcmp(module_name, JAVA_BASE_NAME) == 0) {
+      CDSConfig::set_java_base_module_patching_disables_cds();
     }
   }
 
   // Create GrowableArray lazily, only if --patch-module has been specified
   if (_patch_mod_prefix == nullptr) {
     _patch_mod_prefix = new (mtArguments) GrowableArray<ModulePatchPath*>(10, mtArguments);
   }
 
-  _patch_mod_prefix->push(new ModulePatchPath(module_name, path));
+  // Scan patches for matching module
+  int i = _patch_mod_prefix->find_if([&](ModulePatchPath* patch) {
+    return (strcmp(module_name, patch->module_name()) == 0);
+  });
+  if (i == -1) {
+    _patch_mod_prefix->push(new ModulePatchPath(module_name, path));
+  } else {
+    if (allow_append) {
+      // append path to existing module entry
+      _patch_mod_prefix->at(i)->append_path(path);
+    } else {
+      if (strcmp(module_name, JAVA_BASE_NAME) == 0) {
+        vm_exit_during_initialization("Cannot specify " JAVA_BASE_NAME " more than once to --patch-module");
+      } else {
+        vm_exit_during_initialization("Cannot specify a module more than once to --patch-module", module_name);
+      }
+    }
+  }
 }
 
 // Remove all empty paths from the app classpath (if IgnoreEmptyClassPaths is enabled)
 //
 // This is necessary because some apps like to specify classpath like -cp foo.jar:${XYZ}:bar.jar
@@ -2877,11 +2966,11 @@
     _java_class_path->set_writeable_value(copy);
     FreeHeap(copy); // a copy was made by set_value, so don't need this anymore
   }
 }
 
-jint Arguments::finalize_vm_init_args(bool patch_mod_javabase) {
+jint Arguments::finalize_vm_init_args() {
   // check if the default lib/endorsed directory exists; if so, error
   char path[JVM_MAXPATHLEN];
   const char* fileSep = os::file_separator();
   jio_snprintf(path, JVM_MAXPATHLEN, "%s%slib%sendorsed", Arguments::get_java_home(), fileSep, fileSep);
 
@@ -2951,11 +3040,16 @@
 
   if (!check_vm_args_consistency()) {
     return JNI_ERR;
   }
 
-  if (!CDSConfig::check_vm_args_consistency(patch_mod_javabase, mode_flag_cmd_line)) {
+  // finalize --module-patch and related --enable-preview
+  if (finalize_patch_module() != JNI_OK) {
+    return JNI_ERR;
+  }
+
+  if (!CDSConfig::check_vm_args_consistency(mode_flag_cmd_line)) {
     return JNI_ERR;
   }
 
 #ifndef CAN_SHOW_REGISTERS_ON_ASSERT
   UNSUPPORTED_OPTION(ShowRegistersOnAssert);
@@ -3708,10 +3802,17 @@
   // verification is not as if both were enabled.
   if (BytecodeVerificationLocal && !BytecodeVerificationRemote) {
     log_info(verification)("Turning on remote verification because local verification is on");
     FLAG_SET_DEFAULT(BytecodeVerificationRemote, true);
   }
+  if (!EnableValhalla || (is_interpreter_only() && !CDSConfig::is_dumping_archive() && !UseSharedSpaces)) {
+    // Disable calling convention optimizations if inline types are not supported.
+    // Also these aren't useful in -Xint. However, don't disable them when dumping or using
+    // the CDS archive, as the values must match between dumptime and runtime.
+    InlineTypePassFieldsAsArgs = false;
+    InlineTypeReturnedAsFields = false;
+  }
 
 #ifndef PRODUCT
   if (!LogVMOutput && FLAG_IS_DEFAULT(LogVMOutput)) {
     if (use_vm_log()) {
       LogVMOutput = true;
diff a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp
--- a/src/hotspot/share/runtime/arguments.hpp
+++ b/src/hotspot/share/runtime/arguments.hpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
+ * 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.
@@ -89,10 +89,11 @@
   ModulePatchPath(const char* module_name, const char* path);
   ~ModulePatchPath();
 
   inline const char* module_name() const { return _module_name; }
   inline char* path_string() const { return _path->value(); }
+  inline void append_path(const char* path) { _path->append_value(path); }
 };
 
 // Element describing System and User (-Dkey=value flags) defined property.
 //
 // An internal SystemProperty is one that has been removed in
@@ -286,11 +287,11 @@
   // Basic properties are writeable as they operate as "last one wins" and will get overwritten.
   // Numbered properties are never writeable, and always internal.
   static bool create_module_property(const char* prop_name, const char* prop_value, PropertyInternal internal);
   static bool create_numbered_module_property(const char* prop_base_name, const char* prop_value, unsigned int count);
 
-  static int process_patch_mod_option(const char* patch_mod_tail, bool* patch_mod_javabase);
+  static int process_patch_mod_option(const char* patch_mod_tail);
 
   // Aggressive optimization flags.
   static jint set_aggressive_opts_flags();
 
   static jint set_aggressive_heap_flags();
@@ -321,12 +322,12 @@
 
   static jint parse_vm_init_args(const JavaVMInitArgs *vm_options_args,
                                  const JavaVMInitArgs *java_tool_options_args,
                                  const JavaVMInitArgs *java_options_args,
                                  const JavaVMInitArgs *cmd_line_args);
-  static jint parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_mod_javabase, JVMFlagOrigin origin);
-  static jint finalize_vm_init_args(bool patch_mod_javabase);
+  static jint parse_each_vm_init_arg(const JavaVMInitArgs* args, JVMFlagOrigin origin);
+  static jint finalize_vm_init_args();
   static bool is_bad_option(const JavaVMOption* option, jboolean ignore, const char* option_type);
 
   static bool is_bad_option(const JavaVMOption* option, jboolean ignore) {
     return is_bad_option(option, ignore, nullptr);
   }
@@ -467,11 +468,12 @@
   static void set_java_home(const char *value) { _java_home->set_value(value); }
   static void set_library_path(const char *value) { _java_library_path->set_value(value); }
   static void set_ext_dirs(char *value)     { _ext_dirs = os::strdup_check_oom(value); }
 
   // Set up the underlying pieces of the boot class path
-  static void add_patch_mod_prefix(const char *module_name, const char *path, bool* patch_mod_javabase);
+  static void add_patch_mod_prefix(const char *module_name, const char *path, bool allow_append, bool allow_cds);
+  static int finalize_patch_module();
   static void set_boot_class_path(const char *value, bool has_jimage) {
     // During start up, set by os::set_boot_path()
     assert(get_boot_class_path() == nullptr, "Boot class path previously set");
     _boot_class_path->set_value(value);
     _has_jimage = has_jimage;
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
@@ -47,15 +47,18 @@
 #include "memory/allocation.inline.hpp"
 #include "memory/oopFactory.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
 #include "oops/constantPool.hpp"
+#include "oops/flatArrayKlass.hpp"
+#include "oops/flatArrayOop.hpp"
 #include "oops/fieldStreams.inline.hpp"
 #include "oops/method.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "oops/objArrayOop.inline.hpp"
 #include "oops/oop.inline.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "oops/typeArrayOop.inline.hpp"
 #include "oops/verifyOopClosure.hpp"
 #include "prims/jvmtiDeferredUpdates.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "prims/jvmtiThreadState.hpp"
@@ -304,13 +307,17 @@
     Klass* k = java_lang_Class::as_Klass(sv->klass()->as_ConstantOopReadValue()->value()());
     Handle obj = sv->value();
 
     st.print("     object <" INTPTR_FORMAT "> of type ", p2i(sv->value()()));
     k->print_value_on(&st);
-    assert(obj.not_null() || realloc_failures, "reallocation was missed");
+    assert(obj.not_null() || k->is_inline_klass() || realloc_failures, "reallocation was missed");
     if (obj.is_null()) {
-      st.print(" allocation failed");
+      if (k->is_inline_klass()) {
+        st.print(" is null");
+      } else {
+        st.print(" allocation failed");
+      }
     } else {
       st.print(" allocated (" SIZE_FORMAT " bytes)", obj->size() * HeapWordSize);
     }
     st.cr();
 
@@ -340,46 +347,70 @@
   // It is not guaranteed that we can get such information here only
   // by analyzing bytecode in deoptimized frames. This is why this flag
   // is set during method compilation (see Compile::Process_OopMap_Node()).
   // If the previous frame was popped or if we are dispatching an exception,
   // we don't have an oop result.
-  bool save_oop_result = chunk->at(0)->scope()->return_oop() && !thread->popframe_forcing_deopt_reexecution() && (exec_mode == Deoptimization::Unpack_deopt);
-  Handle return_value;
+  ScopeDesc* scope = chunk->at(0)->scope();
+  bool save_oop_result = scope->return_oop() && !thread->popframe_forcing_deopt_reexecution() && (exec_mode == Deoptimization::Unpack_deopt);
+  // In case of the return of multiple values, we must take care
+  // of all oop return values.
+  GrowableArray<Handle> return_oops;
+  InlineKlass* vk = nullptr;
+  if (save_oop_result && scope->return_scalarized()) {
+    vk = InlineKlass::returned_inline_klass(map);
+    if (vk != nullptr) {
+      vk->save_oop_fields(map, return_oops);
+      save_oop_result = false;
+    }
+  }
   if (save_oop_result) {
     // Reallocation may trigger GC. If deoptimization happened on return from
     // call which returns oop we need to save it since it is not in oopmap.
     oop result = deoptee.saved_oop_result(&map);
     assert(oopDesc::is_oop_or_null(result), "must be oop");
-    return_value = Handle(thread, result);
+    return_oops.push(Handle(thread, result));
     assert(Universe::heap()->is_in_or_null(result), "must be heap pointer");
     if (TraceDeoptimization) {
       tty->print_cr("SAVED OOP RESULT " INTPTR_FORMAT " in thread " INTPTR_FORMAT, p2i(result), p2i(thread));
       tty->cr();
     }
   }
-  if (objects != nullptr) {
+  if (objects != nullptr || vk != nullptr) {
     if (exec_mode == Deoptimization::Unpack_none) {
       assert(thread->thread_state() == _thread_in_vm, "assumption");
       JavaThread* THREAD = thread; // For exception macros.
       // Clear pending OOM if reallocation fails and return true indicating allocation failure
-      realloc_failures = Deoptimization::realloc_objects(thread, &deoptee, &map, objects, CHECK_AND_CLEAR_(true));
+      if (vk != nullptr) {
+        realloc_failures = Deoptimization::realloc_inline_type_result(vk, map, return_oops, CHECK_AND_CLEAR_(true));
+      }
+      if (objects != nullptr) {
+        realloc_failures = realloc_failures || Deoptimization::realloc_objects(thread, &deoptee, &map, objects, CHECK_AND_CLEAR_(true));
+        bool skip_internal = (compiled_method != nullptr) && !compiled_method->is_compiled_by_jvmci();
+        Deoptimization::reassign_fields(&deoptee, &map, objects, realloc_failures, skip_internal, CHECK_AND_CLEAR_(true));
+      }
       deoptimized_objects = true;
     } else {
       JavaThread* current = thread; // For JRT_BLOCK
       JRT_BLOCK
-      realloc_failures = Deoptimization::realloc_objects(thread, &deoptee, &map, objects, THREAD);
+      if (vk != nullptr) {
+        realloc_failures = Deoptimization::realloc_inline_type_result(vk, map, return_oops, THREAD);
+      }
+      if (objects != nullptr) {
+        realloc_failures = realloc_failures || Deoptimization::realloc_objects(thread, &deoptee, &map, objects, THREAD);
+        bool skip_internal = (compiled_method != nullptr) && !compiled_method->is_compiled_by_jvmci();
+        Deoptimization::reassign_fields(&deoptee, &map, objects, realloc_failures, skip_internal, THREAD);
+      }
       JRT_END
     }
-    bool skip_internal = (compiled_method != nullptr) && !compiled_method->is_compiled_by_jvmci();
-    Deoptimization::reassign_fields(&deoptee, &map, objects, realloc_failures, skip_internal);
-    if (TraceDeoptimization) {
+    if (TraceDeoptimization && objects != nullptr) {
       print_objects(deoptee_thread, objects, realloc_failures);
     }
   }
-  if (save_oop_result) {
+  if (save_oop_result || vk != nullptr) {
     // Restore result.
-    deoptee.set_saved_oop_result(&map, return_value());
+    assert(return_oops.length() == 1, "no inline type");
+    deoptee.set_saved_oop_result(&map, return_oops.pop()());
   }
   return realloc_failures;
 }
 
 static void restore_eliminated_locks(JavaThread* thread, GrowableArray<compiledVFrame*>* chunk, bool realloc_failures,
@@ -710,11 +741,11 @@
     // non-parameter locals of the first unpacked interpreted frame.
     // Compute that adjustment.
     caller_adjustment = last_frame_adjust(callee_parameters, callee_locals);
   }
 
-  // If the sender is deoptimized the we must retrieve the address of the handler
+  // If the sender is deoptimized we must retrieve the address of the handler
   // since the frame will "magically" show the original pc before the deopt
   // and we'd undo the deopt.
 
   frame_pcs[0] = Continuation::is_cont_barrier_frame(deoptee) ? StubRoutines::cont_returnBarrier() : deopt_sender.raw_pc();
   if (Continuation::is_continuation_enterSpecial(deopt_sender)) {
@@ -1210,14 +1241,23 @@
   bool failures = false;
 
   for (int i = 0; i < objects->length(); i++) {
     assert(objects->at(i)->is_object(), "invalid debug information");
     ObjectValue* sv = (ObjectValue*) objects->at(i);
-
     Klass* k = java_lang_Class::as_Klass(sv->klass()->as_ConstantOopReadValue()->value()());
-    oop obj = nullptr;
 
+    // Check if the object may be null and has an additional is_init input that needs
+    // to be checked before using the field values. Skip re-allocation if it is null.
+    if (sv->maybe_null()) {
+      assert(k->is_inline_klass(), "must be an inline klass");
+      jint is_init = StackValue::create_stack_value(fr, reg_map, sv->is_init())->get_jint();
+      if (is_init == 0) {
+        continue;
+      }
+    }
+
+    oop obj = nullptr;
     bool cache_init_error = false;
     if (k->is_instance_klass()) {
 #if INCLUDE_JVMCI
       CompiledMethod* cm = fr->cb()->as_compiled_method_or_null();
       if (cm->is_compiled_by_jvmci() && sv->is_auto_box()) {
@@ -1244,10 +1284,14 @@
         }
 #else
         obj = ik->allocate_instance(THREAD);
 #endif // COMPILER2_OR_JVMCI
       }
+    } else if (k->is_flatArray_klass()) {
+      FlatArrayKlass* ak = FlatArrayKlass::cast(k);
+      // Inline type array must be zeroed because not all memory is reassigned
+      obj = ak->allocate(sv->field_size(), THREAD);
     } else if (k->is_typeArray_klass()) {
       TypeArrayKlass* ak = TypeArrayKlass::cast(k);
       assert(sv->field_size() % type2size[ak->element_type()] == 0, "non-integral array length");
       int len = sv->field_size() / type2size[ak->element_type()];
       obj = ak->allocate(len, THREAD);
@@ -1273,10 +1317,25 @@
   }
 
   return failures;
 }
 
+// We're deoptimizing at the return of a call, inline type fields are
+// in registers. When we go back to the interpreter, it will expect a
+// reference to an inline type instance. Allocate and initialize it from
+// the register values here.
+bool Deoptimization::realloc_inline_type_result(InlineKlass* vk, const RegisterMap& map, GrowableArray<Handle>& return_oops, TRAPS) {
+  oop new_vt = vk->realloc_result(map, return_oops, THREAD);
+  if (new_vt == nullptr) {
+    CLEAR_PENDING_EXCEPTION;
+    THROW_OOP_(Universe::out_of_memory_error_realloc_objects(), true);
+  }
+  return_oops.clear();
+  return_oops.push(Handle(THREAD, new_vt));
+  return false;
+}
+
 #if INCLUDE_JVMCI
 /**
  * For primitive types whose kind gets "erased" at runtime (shorts become stack ints),
  * we need to somehow be able to recover the actual kind to be able to write the correct
  * amount of bytes.
@@ -1438,45 +1497,63 @@
 
 class ReassignedField {
 public:
   int _offset;
   BasicType _type;
+  InstanceKlass* _klass;
+  bool _is_flat;
 public:
-  ReassignedField() {
-    _offset = 0;
-    _type = T_ILLEGAL;
-  }
+  ReassignedField() : _offset(0), _type(T_ILLEGAL), _klass(nullptr), _is_flat(false) { }
 };
 
 int compare(ReassignedField* left, ReassignedField* right) {
   return left->_offset - right->_offset;
 }
 
 // Restore fields of an eliminated instance object using the same field order
 // returned by HotSpotResolvedObjectTypeImpl.getInstanceFields(true)
-static int reassign_fields_by_klass(InstanceKlass* klass, frame* fr, RegisterMap* reg_map, ObjectValue* sv, int svIndex, oop obj, bool skip_internal) {
+static int reassign_fields_by_klass(InstanceKlass* klass, frame* fr, RegisterMap* reg_map, ObjectValue* sv, int svIndex, oop obj, bool skip_internal, int base_offset, TRAPS) {
   GrowableArray<ReassignedField>* fields = new GrowableArray<ReassignedField>();
   InstanceKlass* ik = klass;
   while (ik != nullptr) {
     for (AllFieldStream fs(ik); !fs.done(); fs.next()) {
       if (!fs.access_flags().is_static() && (!skip_internal || !fs.field_flags().is_injected())) {
         ReassignedField field;
         field._offset = fs.offset();
         field._type = Signature::basic_type(fs.signature());
+        if (fs.is_null_free_inline_type()) {
+          if (fs.is_flat()) {
+            field._is_flat = true;
+            // Resolve klass of flat inline type field
+            field._klass = InlineKlass::cast(klass->get_inline_type_field_klass(fs.index()));
+          } else {
+            field._type = T_OBJECT;  // Can be removed once Q-descriptors have been removed.
+          }
+        }
         fields->append(field);
       }
     }
     ik = ik->superklass();
   }
   fields->sort(compare);
   for (int i = 0; i < fields->length(); i++) {
+    BasicType type = fields->at(i)._type;
+    int offset = base_offset + fields->at(i)._offset;
+    // Check for flat inline type field before accessing the ScopeValue because it might not have any fields
+    if (fields->at(i)._is_flat) {
+      // Recursively re-assign flat inline type fields
+      InstanceKlass* vk = fields->at(i)._klass;
+      assert(vk != nullptr, "must be resolved");
+      offset -= InlineKlass::cast(vk)->first_field_offset(); // Adjust offset to omit oop header
+      svIndex = reassign_fields_by_klass(vk, fr, reg_map, sv, svIndex, obj, skip_internal, offset, CHECK_0);
+      continue; // Continue because we don't need to increment svIndex
+    }
     ScopeValue* scope_field = sv->field_at(svIndex);
     StackValue* value = StackValue::create_stack_value(fr, reg_map, scope_field);
-    int offset = fields->at(i)._offset;
-    BasicType type = fields->at(i)._type;
     switch (type) {
-      case T_OBJECT: case T_ARRAY:
+      case T_OBJECT:
+      case T_ARRAY:
         assert(value->type() == T_OBJECT, "Agreement.");
         obj->obj_field_put(offset, value->get_obj()());
         break;
 
       case T_INT: case T_FLOAT: { // 4 bytes.
@@ -1546,18 +1623,32 @@
     svIndex++;
   }
   return svIndex;
 }
 
+// restore fields of an eliminated inline type array
+void Deoptimization::reassign_flat_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, flatArrayOop obj, FlatArrayKlass* vak, bool skip_internal, TRAPS) {
+  InlineKlass* vk = vak->element_klass();
+  assert(vk->flat_array(), "should only be used for flat inline type arrays");
+  // Adjust offset to omit oop header
+  int base_offset = arrayOopDesc::base_offset_in_bytes(T_PRIMITIVE_OBJECT) - InlineKlass::cast(vk)->first_field_offset();
+  // Initialize all elements of the flat inline type array
+  for (int i = 0; i < sv->field_size(); i++) {
+    ScopeValue* val = sv->field_at(i);
+    int offset = base_offset + (i << Klass::layout_helper_log2_element_size(vak->layout_helper()));
+    reassign_fields_by_klass(vk, fr, reg_map, val->as_ObjectValue(), 0, (oop)obj, skip_internal, offset, CHECK);
+  }
+}
+
 // restore fields of all eliminated objects and arrays
-void Deoptimization::reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray<ScopeValue*>* objects, bool realloc_failures, bool skip_internal) {
+void Deoptimization::reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray<ScopeValue*>* objects, bool realloc_failures, bool skip_internal, TRAPS) {
   for (int i = 0; i < objects->length(); i++) {
     assert(objects->at(i)->is_object(), "invalid debug information");
     ObjectValue* sv = (ObjectValue*) objects->at(i);
     Klass* k = java_lang_Class::as_Klass(sv->klass()->as_ConstantOopReadValue()->value()());
     Handle obj = sv->value();
-    assert(obj.not_null() || realloc_failures, "reallocation was missed");
+    assert(obj.not_null() || realloc_failures || sv->maybe_null(), "reallocation was missed");
 #ifndef PRODUCT
     if (PrintDeoptimizationDetails) {
       tty->print_cr("reassign fields for object of type %s!", k->name()->as_C_string());
     }
 #endif // !PRODUCT
@@ -1593,11 +1684,14 @@
       // which could be restored after vector object allocation.
     }
 #endif /* !COMPILER2_OR_JVMCI */
     if (k->is_instance_klass()) {
       InstanceKlass* ik = InstanceKlass::cast(k);
-      reassign_fields_by_klass(ik, fr, reg_map, sv, 0, obj(), skip_internal);
+      reassign_fields_by_klass(ik, fr, reg_map, sv, 0, obj(), skip_internal, 0, CHECK);
+    } else if (k->is_flatArray_klass()) {
+      FlatArrayKlass* vak = FlatArrayKlass::cast(k);
+      reassign_flat_array_elements(fr, reg_map, sv, (flatArrayOop) obj(), vak, skip_internal, CHECK);
     } else if (k->is_typeArray_klass()) {
       TypeArrayKlass* ak = TypeArrayKlass::cast(k);
       reassign_type_array_elements(fr, reg_map, sv, (typeArrayOop) obj(), ak->element_type());
     } else if (k->is_objArray_klass()) {
       reassign_object_array_elements(fr, reg_map, sv, (objArrayOop) obj());
@@ -1763,11 +1857,11 @@
   // deopt the execution state and return to the interpreter.
   fr.deoptimize(thread);
 }
 
 void Deoptimization::deoptimize(JavaThread* thread, frame fr, DeoptReason reason) {
-  // Deoptimize only if the frame comes from compile code.
+  // Deoptimize only if the frame comes from compiled code.
   // Do not deoptimize the frame which is already patched
   // during the execution of the loops below.
   if (!fr.is_compiled_frame() || fr.is_deoptimized_frame()) {
     return;
   }
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
@@ -200,13 +200,15 @@
 
  public:
 
   // Support for restoring non-escaping objects
   static bool realloc_objects(JavaThread* thread, frame* fr, RegisterMap* reg_map, GrowableArray<ScopeValue*>* objects, TRAPS);
+  static bool realloc_inline_type_result(InlineKlass* vk, const RegisterMap& map, GrowableArray<Handle>& return_oops, TRAPS);
   static void reassign_type_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, typeArrayOop obj, BasicType type);
   static void reassign_object_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, objArrayOop obj);
-  static void reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray<ScopeValue*>* objects, bool realloc_failures, bool skip_internal);
+  static void reassign_flat_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, flatArrayOop obj, FlatArrayKlass* vak, bool skip_internal, TRAPS);
+  static void reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray<ScopeValue*>* objects, bool realloc_failures, bool skip_internal, TRAPS);
   static bool relock_objects(JavaThread* thread, GrowableArray<MonitorInfo*>* monitors,
                              JavaThread* deoptee_thread, frame& fr, int exec_mode, bool realloc_failures);
   static void pop_frames_failed_reallocs(JavaThread* thread, vframeArray* array);
 #endif // COMPILER2_OR_JVMCI
 
diff a/src/hotspot/share/runtime/fieldDescriptor.cpp b/src/hotspot/share/runtime/fieldDescriptor.cpp
--- a/src/hotspot/share/runtime/fieldDescriptor.cpp
+++ b/src/hotspot/share/runtime/fieldDescriptor.cpp
@@ -29,10 +29,11 @@
 #include "oops/constantPool.hpp"
 #include "oops/instanceKlass.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/fieldStreams.inline.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "runtime/fieldDescriptor.inline.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/signature.hpp"
 
 Symbol* fieldDescriptor::generic_signature() const {
@@ -42,11 +43,11 @@
   return _cp->symbol_at(_fieldinfo.generic_signature_index());
 }
 
 bool fieldDescriptor::is_trusted_final() const {
   InstanceKlass* ik = field_holder();
-  return is_final() && (is_static() || ik->is_hidden() || ik->is_record());
+  return is_final() && (is_static() || ik->is_hidden() || ik->is_record() || ik->is_inline_klass());
 }
 
 AnnotationArray* fieldDescriptor::annotations() const {
   InstanceKlass* ik = field_holder();
   Array<AnnotationArray*>* md = ik->fields_annotations();
@@ -123,14 +124,16 @@
 }
 
 void fieldDescriptor::print() const { print_on(tty); }
 
 void fieldDescriptor::print_on_for(outputStream* st, oop obj) {
-  print_on(st);
-  st->print(" ");
-
   BasicType ft = field_type();
+  if (!is_null_free_inline_type()) {
+    print_on(st);
+    st->print(" ");
+  }
+  jint as_int = 0;
   switch (ft) {
     case T_BYTE:
       st->print("%d", obj->byte_field(offset()));
       break;
     case T_CHAR:
@@ -156,17 +159,23 @@
       break;
     case T_BOOLEAN:
       st->print("%s", obj->bool_field(offset()) ? "true" : "false");
       break;
     case T_ARRAY:
-      if (obj->obj_field(offset()) != nullptr) {
-        obj->obj_field(offset())->print_value_on(st);
-      } else {
-        st->print("null");
-      }
-      break;
     case T_OBJECT:
+      if (is_flat()) { // only some inline types can be flat
+        assert(is_null_free_inline_type(), "Only null free inline type fields can be flat");
+        // Print fields of flat fields (recursively)
+        InlineKlass* vk = InlineKlass::cast(field_holder()->get_inline_type_field_klass(index()));
+        int field_offset = offset() - vk->first_field_offset();
+        obj = cast_to_oop(cast_from_oop<address>(obj) + field_offset);
+        st->print_cr("Flat inline type field '%s':", vk->name()->as_C_string());
+        FieldPrinter print_field(st, obj);
+        vk->do_nonstatic_fields(&print_field);
+        return; // Do not print underlying representation
+      }
+      // Not flat inline type field, fall through
       if (obj->obj_field(offset()) != nullptr) {
         obj->obj_field(offset())->print_value_on(st);
       } else {
         st->print("null");
       }
@@ -200,6 +209,7 @@
     default:
       ShouldNotReachHere();
       break;
     }
   }
+  st->cr();
 }
diff a/src/hotspot/share/runtime/fieldDescriptor.hpp b/src/hotspot/share/runtime/fieldDescriptor.hpp
--- a/src/hotspot/share/runtime/fieldDescriptor.hpp
+++ b/src/hotspot/share/runtime/fieldDescriptor.hpp
@@ -84,10 +84,14 @@
   bool is_static()                const    { return access_flags().is_static(); }
   bool is_final()                 const    { return access_flags().is_final(); }
   bool is_stable()                const    { return field_flags().is_stable(); }
   bool is_volatile()              const    { return access_flags().is_volatile(); }
   bool is_transient()             const    { return access_flags().is_transient(); }
+  inline bool is_flat()           const;
+  inline bool is_null_free_inline_type() const;
+  inline bool has_null_marker()   const;
+  inline bool has_internal_null_marker() const;
 
   bool is_synthetic()             const    { return access_flags().is_synthetic(); }
 
   bool is_field_access_watched()  const    { return field_status().is_access_watched(); }
   bool is_field_modification_watched() const
diff a/src/hotspot/share/runtime/fieldDescriptor.inline.hpp b/src/hotspot/share/runtime/fieldDescriptor.inline.hpp
--- a/src/hotspot/share/runtime/fieldDescriptor.inline.hpp
+++ b/src/hotspot/share/runtime/fieldDescriptor.inline.hpp
@@ -64,6 +64,11 @@
 
 inline BasicType fieldDescriptor::field_type() const {
   return Signature::basic_type(signature());
 }
 
+inline bool fieldDescriptor::is_flat()  const  { return field().field_flags().is_flat(); }
+inline bool fieldDescriptor::is_null_free_inline_type() const { return field().field_flags().is_null_free_inline_type(); }
+inline bool fieldDescriptor::has_null_marker() const { return field().field_flags().has_null_marker(); }
+inline bool fieldDescriptor::has_internal_null_marker() const { return field().field_flags().is_null_marker_internal(); }
+
 #endif // SHARE_RUNTIME_FIELDDESCRIPTOR_INLINE_HPP
diff a/src/hotspot/share/runtime/frame.cpp b/src/hotspot/share/runtime/frame.cpp
--- a/src/hotspot/share/runtime/frame.cpp
+++ b/src/hotspot/share/runtime/frame.cpp
@@ -38,10 +38,11 @@
 #include "memory/universe.hpp"
 #include "oops/markWord.hpp"
 #include "oops/method.inline.hpp"
 #include "oops/methodData.hpp"
 #include "oops/oop.inline.hpp"
+#include "oops/inlineKlass.hpp"
 #include "oops/stackChunkOop.inline.hpp"
 #include "oops/verifyOopClosure.hpp"
 #include "prims/methodHandles.hpp"
 #include "runtime/continuation.hpp"
 #include "runtime/continuationEntry.inline.hpp"
@@ -58,10 +59,13 @@
 #include "runtime/stubCodeGenerator.hpp"
 #include "runtime/stubRoutines.hpp"
 #include "utilities/debug.hpp"
 #include "utilities/decoder.hpp"
 #include "utilities/formatBuffer.hpp"
+#ifdef COMPILER1
+#include "c1/c1_Runtime1.hpp"
+#endif
 
 RegisterMap::RegisterMap(JavaThread *thread, UpdateMap update_map, ProcessFrames process_frames, WalkContinuation walk_cont) {
   _thread         = thread;
   _update_map     = update_map == UpdateMap::include;
   _process_frames = process_frames == ProcessFrames::include;
@@ -356,10 +360,29 @@
 
   NativePostCallNop* inst = nativePostCallNop_at(pc());
 
   // Save the original pc before we patch in the new one
   cm->set_original_pc(this, pc());
+
+#ifdef COMPILER1
+  if (cm->is_compiled_by_c1() && cm->method()->has_scalarized_args() &&
+      pc() < cm->verified_inline_entry_point()) {
+    // The VEP and VIEP(RO) of C1-compiled methods call into the runtime to buffer scalarized value
+    // type args. We can't deoptimize at that point because the buffers have not yet been initialized.
+    // Also, if the method is synchronized, we first need to acquire the lock.
+    // Don't patch the return pc to delay deoptimization until we enter the method body (the check
+    // added in LIRGenerator::do_Base will detect the pending deoptimization by checking the original_pc).
+#if defined ASSERT && !defined AARCH64   // Stub call site does not look like NativeCall on AArch64
+    NativeCall* call = nativeCall_before(this->pc());
+    address dest = call->destination();
+    assert(dest == Runtime1::entry_for(Runtime1::buffer_inline_args_no_receiver_id) ||
+           dest == Runtime1::entry_for(Runtime1::buffer_inline_args_id), "unexpected safepoint in entry point");
+#endif
+    return;
+  }
+#endif
+
   patch_pc(thread, deopt);
   assert(is_deoptimized_frame(), "must be");
 
 #ifdef ASSERT
   if (thread != nullptr) {
@@ -748,11 +771,11 @@
   int          _max_locals;
   int          _max_stack;
 
  public:
   InterpreterFrameClosure(const frame* fr, int max_locals, int max_stack,
-                          OopClosure* f) {
+                          OopClosure* f, BufferedValueClosure* bvt_f) {
     _fr         = fr;
     _max_locals = max_locals;
     _max_stack  = max_stack;
     _f          = f;
   }
@@ -760,11 +783,13 @@
   void offset_do(int offset) {
     oop* addr;
     if (offset < _max_locals) {
       addr = (oop*) _fr->interpreter_frame_local_at(offset);
       assert((intptr_t*)addr >= _fr->sp(), "must be inside the frame");
-      _f->do_oop(addr);
+      if (_f != nullptr) {
+        _f->do_oop(addr);
+      }
     } else {
       addr = (oop*) _fr->interpreter_frame_expression_stack_at((offset - _max_locals));
       // In case of exceptions, the expression stack is invalid and the esp will be reset to express
       // this condition. Therefore, we call f only if addr is 'inside' the stack (i.e., addr >= esp for Intel).
       bool in_stack;
@@ -772,11 +797,13 @@
         in_stack = (intptr_t*)addr <= _fr->interpreter_frame_tos_address();
       } else {
         in_stack = (intptr_t*)addr >= _fr->interpreter_frame_tos_address();
       }
       if (in_stack) {
-        _f->do_oop(addr);
+        if (_f != nullptr) {
+          _f->do_oop(addr);
+        }
       }
     }
   }
 };
 
@@ -947,11 +974,11 @@
         oops_interpreted_arguments_do(signature, has_receiver, f);
       }
     }
   }
 
-  InterpreterFrameClosure blk(this, max_locals, m->max_stack(), f);
+  InterpreterFrameClosure blk(this, max_locals, m->max_stack(), f, nullptr);
 
   // process locals & expression stack
   InterpreterOopMap mask;
   if (query_oop_map_cache) {
     m->mask_for(bci, &mask);
@@ -959,10 +986,27 @@
     OopMapCache::compute_one_oop_map(m, bci, &mask);
   }
   mask.iterate_oop(&blk);
 }
 
+void frame::buffered_values_interpreted_do(BufferedValueClosure* f) {
+  assert(is_interpreted_frame(), "Not an interpreted frame");
+  Thread *thread = Thread::current();
+  methodHandle m (thread, interpreter_frame_method());
+  jint      bci = interpreter_frame_bci();
+
+  assert(m->is_method(), "checking frame value");
+  assert(!m->is_native() && bci >= 0 && bci < m->code_size(),
+         "invalid bci value");
+
+  InterpreterFrameClosure blk(this, m->max_locals(), m->max_stack(), nullptr, f);
+
+  // process locals & expression stack
+  InterpreterOopMap mask;
+  m->mask_for(bci, &mask);
+  mask.iterate_oop(&blk);
+}
 
 void frame::oops_interpreted_arguments_do(Symbol* signature, bool has_receiver, OopClosure* f) const {
   InterpretedArgumentOopFinder finder(signature, has_receiver, this, f);
   finder.oops_do();
 }
@@ -1010,10 +1054,11 @@
   }
 
   virtual void handle_oop_offset() {
     // Extract low order register number from register array.
     // In LP64-land, the high-order bits are valid but unhelpful.
+    assert(_offset < _arg_size, "out of bounds");
     VMReg reg = _regs[_offset].first();
     oop *loc = _fr.oopmapreg_to_oop_location(reg, _reg_map);
   #ifdef ASSERT
     if (loc == nullptr) {
       if (_reg_map->should_skip_missing()) {
@@ -1036,15 +1081,11 @@
     _offset    = 0;
     _has_receiver = has_receiver;
     _has_appendix = has_appendix;
     _fr        = fr;
     _reg_map   = (RegisterMap*)reg_map;
-    _arg_size  = ArgumentSizeComputer(signature).size() + (has_receiver ? 1 : 0) + (has_appendix ? 1 : 0);
-
-    int arg_size;
-    _regs = SharedRuntime::find_callee_arguments(signature, has_receiver, has_appendix, &arg_size);
-    assert(arg_size == _arg_size, "wrong arg size");
+    _regs = SharedRuntime::find_callee_arguments(signature, has_receiver, has_appendix, &_arg_size);
   }
 
   void oops_do() {
     if (_has_receiver) {
       handle_oop_offset();
diff a/src/hotspot/share/runtime/frame.hpp b/src/hotspot/share/runtime/frame.hpp
--- a/src/hotspot/share/runtime/frame.hpp
+++ b/src/hotspot/share/runtime/frame.hpp
@@ -444,10 +444,11 @@
   oop* oopmapreg_to_oop_location(VMReg reg, const RegisterMapT* reg_map) const;
 
   // Oops-do's
   void oops_compiled_arguments_do(Symbol* signature, bool has_receiver, bool has_appendix, const RegisterMap* reg_map, OopClosure* f) const;
   void oops_interpreted_do(OopClosure* f, const RegisterMap* map, bool query_oop_map_cache = true) const;
+  void buffered_values_interpreted_do(BufferedValueClosure* f);
 
  private:
   void oops_interpreted_arguments_do(Symbol* signature, bool has_receiver, OopClosure* f) const;
 
   // Iteration of oops
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
@@ -811,13 +811,34 @@
           "Number of OutOfMemoryErrors preallocated with backtrace")        \
                                                                             \
   product(bool, UseXMMForArrayCopy, false,                                  \
           "Use SSE2 MOVQ instruction for Arraycopy")                        \
                                                                             \
-  notproduct(bool, PrintFieldLayout, false,                                 \
+  product(bool, PrintFieldLayout, false, DIAGNOSTIC,                        \
           "Print field layout for each class")                              \
                                                                             \
+  product(bool, PrintInlineLayout, false, DIAGNOSTIC,                       \
+          "Print field layout for each inline type or class with inline fields") \
+                                                                            \
+  notproduct(bool, PrintFlatArrayLayout, false,                             \
+          "Print array layout for each inline type array")                  \
+                                                                            \
+  product(intx, FlatArrayElementMaxSize, -1,                                \
+          "Max size for flattening inline array elements, <0 no limit")     \
+                                                                            \
+  product(intx, InlineFieldMaxFlatSize, 128,                                \
+          "Max size for flattening inline type fields, <0 no limit")        \
+                                                                            \
+  develop(bool, EnableNullableFieldFlattening, false,                       \
+          "Allow the JVM to flatten some nullable fields")                  \
+                                                                            \
+  product(intx, FlatArrayElementMaxOops, 4,                                 \
+          "Max nof embedded object references in an inline type to flatten, <0 no limit")  \
+                                                                            \
+  product(bool, InlineArrayAtomicAccess, false,                             \
+          "Atomic inline array accesses by-default, for all inline arrays") \
+                                                                            \
   /* Need to limit the extent of the padding to reasonable size.          */\
   /* 8K is well beyond the reasonable HW cache line size, even with       */\
   /* aggressive prefetching, while still leaving the room for segregating */\
   /* among the distinct pages.                                            */\
   product(int, ContendedPaddingWidth, 128,                                  \
@@ -1976,10 +1997,30 @@
           "Start flight recording with options"))                           \
                                                                             \
   product(bool, UseFastUnorderedTimeStamps, false, EXPERIMENTAL,            \
           "Use platform unstable time where supported for timestamps only") \
                                                                             \
+  product(bool, EnableValhalla, true,                                       \
+          "Enable experimental Valhalla features")                          \
+                                                                            \
+  product_pd(bool, InlineTypePassFieldsAsArgs,                              \
+          "Pass each inline type field as an argument at calls")            \
+                                                                            \
+  product_pd(bool, InlineTypeReturnedAsFields,                              \
+          "Return fields instead of an inline type reference")              \
+                                                                            \
+  develop(bool, StressCallingConvention, false,                             \
+          "Stress the scalarized calling convention.")                      \
+                                                                            \
+  product(bool, UseArrayMarkWordCheck, NOT_LP64(false) LP64_ONLY(true),     \
+          "Use bits in the mark word to check for flat/null-free arrays")   \
+                                                                            \
+  product(ccstrlist, ForceNonTearable, "", DIAGNOSTIC,                      \
+          "List of inline classes which are forced to be atomic "           \
+          "(whitespace and commas separate names, "                         \
+          "and leading and trailing stars '*' are wildcards)")              \
+                                                                            \
   product(bool, UseEmptySlotsInSupers, true,                                \
                 "Allow allocating fields in empty slots of super-classes")  \
                                                                             \
   product(bool, DeoptimizeNMethodBarriersALot, false, DIAGNOSTIC,           \
                 "Make nmethod barriers deoptimise a lot.")                  \
diff a/src/hotspot/share/runtime/handles.cpp b/src/hotspot/share/runtime/handles.cpp
--- a/src/hotspot/share/runtime/handles.cpp
+++ b/src/hotspot/share/runtime/handles.cpp
@@ -25,10 +25,12 @@
 #include "precompiled.hpp"
 #include "memory/allocation.inline.hpp"
 #include "oops/constantPool.hpp"
 #include "oops/method.hpp"
 #include "oops/oop.inline.hpp"
+#include "oops/inlineKlass.hpp"
+#include "runtime/atomic.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/javaThread.hpp"
 
 #ifdef ASSERT
 #define assert_handle_mark_nesting()                                                     \
diff a/src/hotspot/share/runtime/handles.hpp b/src/hotspot/share/runtime/handles.hpp
--- a/src/hotspot/share/runtime/handles.hpp
+++ b/src/hotspot/share/runtime/handles.hpp
@@ -27,10 +27,11 @@
 
 #include "memory/arena.hpp"
 #include "oops/oop.hpp"
 #include "oops/oopsHierarchy.hpp"
 
+class InlineKlass;
 class InstanceKlass;
 class Klass;
 class Thread;
 
 //------------------------------------------------------------------------------------------------------------------------
@@ -126,10 +127,11 @@
 DEF_HANDLE(instance         , is_instance_noinline         )
 DEF_HANDLE(stackChunk       , is_stackChunk_noinline       )
 DEF_HANDLE(array            , is_array_noinline            )
 DEF_HANDLE(objArray         , is_objArray_noinline         )
 DEF_HANDLE(typeArray        , is_typeArray_noinline        )
+DEF_HANDLE(flatArray        , is_flatArray_noinline        )
 
 //------------------------------------------------------------------------------------------------------------------------
 
 // Metadata Handles.  Unlike oop Handles these are needed to prevent metadata
 // from being reclaimed by RedefineClasses.
diff a/src/hotspot/share/runtime/handles.inline.hpp b/src/hotspot/share/runtime/handles.inline.hpp
--- a/src/hotspot/share/runtime/handles.inline.hpp
+++ b/src/hotspot/share/runtime/handles.inline.hpp
@@ -59,10 +59,11 @@
 
 DEF_HANDLE_CONSTR(instance , is_instance_noinline )
 DEF_HANDLE_CONSTR(array    , is_array_noinline    )
 DEF_HANDLE_CONSTR(objArray , is_objArray_noinline )
 DEF_HANDLE_CONSTR(typeArray, is_typeArray_noinline)
+DEF_HANDLE_CONSTR(flatArray, is_flatArray_noinline)
 
 // Constructor for metadata handles
 #define DEF_METADATA_HANDLE_FN(name, type) \
 inline name##Handle::name##Handle(Thread* thread, type* obj) : _value(obj), _thread(thread) { \
   if (obj != nullptr) {                                                   \
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
@@ -118,10 +118,11 @@
   bytecodes_init();
   classLoader_init1();
   compilationPolicy_init();
   codeCache_init();
   VM_Version_init();              // depends on codeCache_init for emitting code
+  VMRegImpl::set_regName();       // need this before generate_stubs (for printing oop maps).
   initial_stubs_init();
   jint status = universe_init();  // dependent on codeCache_init and
                                   // initial_stubs_init and metaspace_init.
   if (status != JNI_OK)
     return status;
@@ -139,11 +140,10 @@
   continuations_init();      // must precede continuation stub generation
   continuation_stubs_init(); // depends on continuations_init
   interpreter_init_stub();   // before methods get loaded
   accessFlags_init();
   InterfaceSupport_init();
-  VMRegImpl::set_regName();  // need this before generate_stubs (for printing oop maps).
   SharedRuntime::generate_stubs();
   return JNI_OK;
 }
 
 jint init_globals2() {
diff a/src/hotspot/share/runtime/javaCalls.cpp b/src/hotspot/share/runtime/javaCalls.cpp
--- a/src/hotspot/share/runtime/javaCalls.cpp
+++ b/src/hotspot/share/runtime/javaCalls.cpp
@@ -31,10 +31,11 @@
 #include "interpreter/interpreter.hpp"
 #include "interpreter/linkResolver.hpp"
 #include "memory/universe.hpp"
 #include "oops/method.inline.hpp"
 #include "oops/oop.inline.hpp"
+#include "oops/inlineKlass.hpp"
 #include "prims/jniCheck.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/interfaceSupport.inline.hpp"
 #include "runtime/javaCalls.hpp"
@@ -144,26 +145,27 @@
 
 
 // Helper methods
 static BasicType runtime_type_from(JavaValue* result) {
   switch (result->get_type()) {
-    case T_BOOLEAN: // fall through
-    case T_CHAR   : // fall through
-    case T_SHORT  : // fall through
-    case T_INT    : // fall through
+    case T_BOOLEAN  : // fall through
+    case T_CHAR     : // fall through
+    case T_SHORT    : // fall through
+    case T_INT      : // fall through
 #ifndef _LP64
-    case T_OBJECT : // fall through
-    case T_ARRAY  : // fall through
+    case T_OBJECT   : // fall through
+    case T_ARRAY    : // fall through
+    case T_PRIMITIVE_OBJECT: // fall through
 #endif
-    case T_BYTE   : // fall through
-    case T_VOID   : return T_INT;
-    case T_LONG   : return T_LONG;
-    case T_FLOAT  : return T_FLOAT;
-    case T_DOUBLE : return T_DOUBLE;
+    case T_BYTE     : // fall through
+    case T_VOID     : return T_INT;
+    case T_LONG     : return T_LONG;
+    case T_FLOAT    : return T_FLOAT;
+    case T_DOUBLE   : return T_DOUBLE;
 #ifdef _LP64
-    case T_ARRAY  : // fall through
-    case T_OBJECT:  return T_OBJECT;
+    case T_ARRAY    : // fall through
+    case T_OBJECT   : return T_OBJECT;
 #endif
     default:
       ShouldNotReachHere();
       return T_ILLEGAL;
   }
@@ -388,10 +390,22 @@
   } else {
     // Touch pages checked if the OS needs them to be touched to be mapped.
     os::map_stack_shadow_pages(sp);
   }
 
+  jobject value_buffer = nullptr;
+  if (InlineTypeReturnedAsFields && (result->get_type() == T_OBJECT)) {
+    // Pre allocate a buffered inline type in case the result is returned
+    // flattened by compiled code
+    InlineKlass* vk = method->returns_inline_type(thread);
+    if (vk != nullptr && vk->can_be_returned_as_fields()) {
+      oop instance = vk->allocate_instance(CHECK);
+      value_buffer = JNIHandles::make_local(thread, instance);
+      result->set_jobject(value_buffer);
+    }
+  }
+
   // do call
   { JavaCallWrapper link(method, receiver, result, CHECK);
     { HandleMark hm(thread);  // HandleMark used by HandleMarkCleaner
 
       // NOTE: if we move the computation of the result_val_address inside
@@ -438,10 +452,11 @@
 
   // Restore possible oop return
   if (oop_result_flag) {
     result->set_oop(thread->vm_result());
     thread->set_vm_result(nullptr);
+    JNIHandles::destroy_local(value_buffer);
   }
 }
 
 
 //--------------------------------------------------------------------------------------
@@ -586,11 +601,11 @@
 
 void JavaCallArguments::verify(const methodHandle& method, BasicType return_type) {
   guarantee(method->size_of_parameters() == size_of_parameters(), "wrong no. of arguments pushed");
 
   // Treat T_OBJECT and T_ARRAY as the same
-  if (is_reference_type(return_type)) return_type = T_OBJECT;
+  if (return_type == T_ARRAY) return_type = T_OBJECT;
 
   // Check that oop information is correct
   Symbol* signature = method->signature();
 
   SignatureChekker sc(signature,
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
@@ -46,10 +46,11 @@
 #include "logging/logStream.hpp"
 #include "memory/allocation.inline.hpp"
 #include "memory/iterator.hpp"
 #include "memory/universe.hpp"
 #include "oops/access.inline.hpp"
+#include "oops/inlineKlass.hpp"
 #include "oops/instanceKlass.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/oopHandle.inline.hpp"
 #include "oops/verifyOopClosure.hpp"
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
@@ -139,10 +139,11 @@
   Method*       _callee_target;
 
   // Used to pass back results to the interpreter or generated code running Java code.
   oop           _vm_result;    // oop result is GC-preserved
   Metadata*     _vm_result_2;  // non-oop result
+  oop           _return_buffered_value; // buffered value being returned
 
   // See ReduceInitialCardMarks: this holds the precise space interval of
   // the most recent slow path allocation for which compiled code has
   // elided card-marks for performance along the fast-path.
   MemRegion     _deferred_card_mark;
@@ -696,10 +697,13 @@
   oop  vm_result() const                         { return _vm_result; }
   void set_vm_result  (oop x)                    { _vm_result   = x; }
 
   void set_vm_result_2  (Metadata* x)            { _vm_result_2   = x; }
 
+  oop return_buffered_value() const              { return _return_buffered_value; }
+  void set_return_buffered_value(oop val)        { _return_buffered_value = val; }
+
   MemRegion deferred_card_mark() const           { return _deferred_card_mark; }
   void set_deferred_card_mark(MemRegion mr)      { _deferred_card_mark = mr;   }
 
 #if INCLUDE_JVMCI
   jlong pending_failed_speculation() const        { return _pending_failed_speculation; }
@@ -761,10 +765,11 @@
     return byte_offset_of(JavaThread, _anchor);
   }
   static ByteSize callee_target_offset()         { return byte_offset_of(JavaThread, _callee_target); }
   static ByteSize vm_result_offset()             { return byte_offset_of(JavaThread, _vm_result); }
   static ByteSize vm_result_2_offset()           { return byte_offset_of(JavaThread, _vm_result_2); }
+  static ByteSize return_buffered_value_offset() { return byte_offset_of(JavaThread, _return_buffered_value); }
   static ByteSize thread_state_offset()          { return byte_offset_of(JavaThread, _thread_state); }
   static ByteSize polling_word_offset()          { return byte_offset_of(JavaThread, _poll_data) + byte_offset_of(SafepointMechanism::ThreadData, _polling_word);}
   static ByteSize polling_page_offset()          { return byte_offset_of(JavaThread, _poll_data) + byte_offset_of(SafepointMechanism::ThreadData, _polling_page);}
   static ByteSize saved_exception_pc_offset()    { return byte_offset_of(JavaThread, _saved_exception_pc); }
   static ByteSize osthread_offset()              { return byte_offset_of(JavaThread, _osthread); }
diff a/src/hotspot/share/runtime/jfieldIDWorkaround.hpp b/src/hotspot/share/runtime/jfieldIDWorkaround.hpp
--- a/src/hotspot/share/runtime/jfieldIDWorkaround.hpp
+++ b/src/hotspot/share/runtime/jfieldIDWorkaround.hpp
@@ -46,29 +46,35 @@
   //
   // If the offset does not fit in 7 bits, or if the fieldID is
   // not checked, then the checked bit is zero and the rest of
   // the word (30 bits) contains only the offset.
   //
+
+  friend class JNI_FastGetField;
+
  private:
   enum {
     checked_bits           = 1,
     instance_bits          = 1,
-    address_bits           = BitsPerWord - checked_bits - instance_bits,
+    flat_bits              = 1,
+    address_bits           = BitsPerWord - checked_bits - instance_bits - flat_bits,
 
     large_offset_bits      = address_bits,  // unioned with address
     small_offset_bits      = 7,
     klass_bits             = address_bits - small_offset_bits,
 
     checked_shift          = 0,
     instance_shift         = checked_shift  + checked_bits,
-    address_shift          = instance_shift + instance_bits,
+    flat_shift             = instance_shift + instance_bits,
+    address_shift          = flat_shift + flat_bits,
 
     offset_shift           = address_shift,  // unioned with address
     klass_shift            = offset_shift + small_offset_bits,
 
     checked_mask_in_place  = right_n_bits(checked_bits)  << checked_shift,
     instance_mask_in_place = right_n_bits(instance_bits) << instance_shift,
+    flat_mask_in_place     = right_n_bits(flat_bits) << flat_shift,
 #ifndef _WIN64
     large_offset_mask      = right_n_bits(large_offset_bits),
     small_offset_mask      = right_n_bits(small_offset_bits),
     klass_mask             = right_n_bits(klass_bits)
 #endif
@@ -109,12 +115,21 @@
   static bool is_static_jfieldID(jfieldID id) {
     uintptr_t as_uint = (uintptr_t) id;
     return ((as_uint & instance_mask_in_place) == 0);
   }
 
-  static jfieldID to_instance_jfieldID(Klass* k, int offset) {
-    intptr_t as_uint = ((offset & large_offset_mask) << offset_shift) | instance_mask_in_place;
+  static bool is_flat_jfieldID(jfieldID id) {
+    uintptr_t as_uint = (uintptr_t) id;
+    return ((as_uint & flat_mask_in_place) != 0);
+  }
+
+  static jfieldID to_instance_jfieldID(Klass* k, int offset, bool is_flat) {
+    intptr_t as_uint = ((offset & large_offset_mask) << offset_shift) |
+                        instance_mask_in_place;
+    if (is_flat) {
+      as_uint |= flat_mask_in_place;
+    }
     if (VerifyJNIFields) {
       as_uint |= encode_klass_hash(k, offset);
     }
     jfieldID result = (jfieldID) as_uint;
 #ifndef ASSERT
@@ -152,17 +167,17 @@
     JNIid* result = (JNIid*) id;
     assert(result->is_static_field_id(), "to_JNIid, but not static field id");
     return result;
   }
 
-  static jfieldID to_jfieldID(InstanceKlass* k, int offset, bool is_static) {
+  static jfieldID to_jfieldID(InstanceKlass* k, int offset, bool is_static, bool is_flat) {
     if (is_static) {
       JNIid *id = k->jni_id_for(offset);
       debug_only(id->set_is_static_field_id());
       return jfieldIDWorkaround::to_static_jfieldID(id);
     } else {
-      return jfieldIDWorkaround::to_instance_jfieldID(k, offset);
+      return jfieldIDWorkaround::to_instance_jfieldID(k, offset, is_flat);
     }
   }
 };
 
 #endif // SHARE_RUNTIME_JFIELDIDWORKAROUND_HPP
diff a/src/hotspot/share/runtime/jniHandles.cpp b/src/hotspot/share/runtime/jniHandles.cpp
--- a/src/hotspot/share/runtime/jniHandles.cpp
+++ b/src/hotspot/share/runtime/jniHandles.cpp
@@ -21,19 +21,21 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "classfile/vmSymbols.hpp"
 #include "gc/shared/collectedHeap.hpp"
 #include "gc/shared/oopStorage.inline.hpp"
 #include "gc/shared/oopStorageSet.hpp"
 #include "logging/log.hpp"
 #include "memory/iterator.hpp"
 #include "memory/universe.hpp"
 #include "oops/access.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/handles.inline.hpp"
+#include "runtime/javaCalls.hpp"
 #include "runtime/javaThread.inline.hpp"
 #include "runtime/jniHandles.inline.hpp"
 #include "runtime/mutexLocker.hpp"
 #include "utilities/align.hpp"
 #include "utilities/debug.hpp"
@@ -281,10 +283,48 @@
   Thread* thread = Thread::current();
   return (thread->is_Java_thread() &&
           JavaThread::cast(thread)->thread_state() == _thread_in_native);
 }
 
+bool JNIHandles::is_same_object(jobject handle1, jobject handle2) {
+  oop obj1 = resolve_no_keepalive(handle1);
+  oop obj2 = resolve_no_keepalive(handle2);
+
+  bool ret = obj1 == obj2;
+
+  if (EnableValhalla) {
+    if (!ret && obj1 != nullptr && obj2 != nullptr && obj1->klass() == obj2->klass() && obj1->klass()->is_inline_klass()) {
+      // The two references are different, they are not null and they are both inline types,
+      // a full substitutability test is required, calling ValueObjectMethods.isSubstitutable()
+      // (similarly to InterpreterRuntime::is_substitutable)
+      JavaThread* THREAD = JavaThread::current();
+      Handle ha(THREAD, obj1);
+      Handle hb(THREAD, obj2);
+      JavaValue result(T_BOOLEAN);
+      JavaCallArguments args;
+      args.push_oop(ha);
+      args.push_oop(hb);
+      methodHandle method(THREAD, Universe::is_substitutable_method());
+      JavaCalls::call(&result, method, &args, THREAD);
+      if (HAS_PENDING_EXCEPTION) {
+        // Something really bad happened because isSubstitutable() should not throw exceptions
+        // If it is an error, just let it propagate
+        // If it is an exception, wrap it into an InternalError
+        if (!PENDING_EXCEPTION->is_a(vmClasses::Error_klass())) {
+          Handle e(THREAD, PENDING_EXCEPTION);
+          CLEAR_PENDING_EXCEPTION;
+          THROW_MSG_CAUSE_(vmSymbols::java_lang_InternalError(), "Internal error in substitutability test", e, false);
+        }
+      }
+      ret = result.get_jboolean();
+    }
+  }
+
+  return ret;
+}
+
+
 int JNIHandleBlock::_blocks_allocated = 0;
 
 static inline bool is_tagged_free_list(uintptr_t value) {
   return (value & 1u) != 0;
 }
diff a/src/hotspot/share/runtime/jniHandles.inline.hpp b/src/hotspot/share/runtime/jniHandles.inline.hpp
--- a/src/hotspot/share/runtime/jniHandles.inline.hpp
+++ b/src/hotspot/share/runtime/jniHandles.inline.hpp
@@ -102,16 +102,10 @@
     result = resolve_impl<AS_NO_KEEPALIVE, false /* external_guard */>(handle);
   }
   return result;
 }
 
-inline bool JNIHandles::is_same_object(jobject handle1, jobject handle2) {
-  oop obj1 = resolve_no_keepalive(handle1);
-  oop obj2 = resolve_no_keepalive(handle2);
-  return obj1 == obj2;
-}
-
 inline oop JNIHandles::resolve_non_null(jobject handle) {
   assert(handle != nullptr, "JNI handle should not be null");
   oop result = resolve_impl<DECORATORS_NONE, false /* external_guard */>(handle);
   assert(result != nullptr, "null read from jni handle");
   return result;
diff a/src/hotspot/share/runtime/perfMemory.hpp b/src/hotspot/share/runtime/perfMemory.hpp
--- a/src/hotspot/share/runtime/perfMemory.hpp
+++ b/src/hotspot/share/runtime/perfMemory.hpp
@@ -81,11 +81,11 @@
 
   jint entry_length;      // entry length in bytes
   jint name_offset;       // offset of the data item name
   jint vector_length;     // length of the vector. If 0, then scalar
   jbyte data_type;        // type of the data item -
-                          // 'B','Z','J','I','S','C','D','F','V','L','['
+                          // 'B','Z','J','I','S','C','D','F','V','L','Q','['
   jbyte flags;            // flags indicating misc attributes
   jbyte data_units;       // unit of measure for the data type
   jbyte data_variability; // variability classification of data type
   jint  data_offset;      // offset of the data item
 
diff a/src/hotspot/share/runtime/reflection.cpp b/src/hotspot/share/runtime/reflection.cpp
--- a/src/hotspot/share/runtime/reflection.cpp
+++ b/src/hotspot/share/runtime/reflection.cpp
@@ -35,10 +35,11 @@
 #include "jvm.h"
 #include "logging/log.hpp"
 #include "memory/oopFactory.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "oops/instanceKlass.inline.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "oops/objArrayOop.inline.hpp"
 #include "oops/oop.inline.hpp"
@@ -51,10 +52,11 @@
 #include "runtime/reflection.hpp"
 #include "runtime/reflectionUtils.hpp"
 #include "runtime/signature.hpp"
 #include "runtime/vframe.inline.hpp"
 #include "utilities/formatBuffer.hpp"
+#include "utilities/globalDefinitions.hpp"
 
 static void trace_class_resolution(oop mirror) {
   if (mirror == nullptr || java_lang_Class::is_primitive(mirror)) {
     return;
   }
@@ -270,13 +272,18 @@
 
 void Reflection::array_set(jvalue* value, arrayOop a, int index, BasicType value_type, TRAPS) {
   if (!a->is_within_bounds(index)) {
     THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException());
   }
+
   if (a->is_objArray()) {
     if (value_type == T_OBJECT) {
       oop obj = cast_to_oop(value->l);
+      if (a->is_null_free_array() && obj == nullptr) {
+         THROW_MSG(vmSymbols::java_lang_NullPointerException(), "null-restricted array");
+      }
+
       if (obj != nullptr) {
         Klass* element_klass = ObjArrayKlass::cast(a->klass())->element_klass();
         if (!obj->is_a(element_klass)) {
           THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "array element type mismatch");
         }
@@ -764,20 +771,16 @@
 }
 
 static Handle new_type(Symbol* signature, Klass* k, TRAPS) {
   ResolvingSignatureStream ss(signature, k, false);
   oop nt = ss.as_java_mirror(SignatureStream::NCDFError, CHECK_NH);
-  if (log_is_enabled(Debug, class, resolve)) {
-    trace_class_resolution(nt);
-  }
   return Handle(THREAD, nt);
 }
 
 oop Reflection::new_method(const methodHandle& method, bool for_constant_pool_access, TRAPS) {
   // Allow sun.reflect.ConstantPool to refer to <clinit> methods as java.lang.reflect.Methods.
-  assert(!method()->is_initializer() ||
-         (for_constant_pool_access && method()->is_static()),
+  assert(!method()->name()->starts_with('<') || for_constant_pool_access,
          "should call new_constructor instead");
   InstanceKlass* holder = method->method_holder();
   int slot = method->method_idnum();
 
   Symbol*  signature  = method->signature();
@@ -822,11 +825,12 @@
   return mh();
 }
 
 
 oop Reflection::new_constructor(const methodHandle& method, TRAPS) {
-  assert(method()->is_initializer(), "should call new_method instead");
+  assert(method()->is_object_constructor(),
+         "should call new_method instead");
 
   InstanceKlass* holder = method->method_holder();
   int slot = method->method_idnum();
 
   Symbol*  signature  = method->signature();
@@ -871,15 +875,23 @@
 
   java_lang_reflect_Field::set_clazz(rh(), fd->field_holder()->java_mirror());
   java_lang_reflect_Field::set_slot(rh(), fd->index());
   java_lang_reflect_Field::set_name(rh(), name());
   java_lang_reflect_Field::set_type(rh(), type());
+
+  int flags = 0;
   if (fd->is_trusted_final()) {
-    java_lang_reflect_Field::set_trusted_final(rh());
+    flags |= TRUSTED_FINAL;
+  }
+  if (fd->is_null_free_inline_type()) {
+    flags |= NULL_RESTRICTED;
   }
+  java_lang_reflect_Field::set_flags(rh(), flags);
+
   // Note the ACC_ANNOTATION bit, which is a per-class access flag, is never set here.
-  java_lang_reflect_Field::set_modifiers(rh(), fd->access_flags().as_int() & JVM_RECOGNIZED_FIELD_MODIFIERS);
+  int modifiers = fd->access_flags().as_int();
+  java_lang_reflect_Field::set_modifiers(rh(), modifiers);
   java_lang_reflect_Field::set_override(rh(), false);
   if (fd->has_generic_signature()) {
     Symbol*  gs = fd->generic_signature();
     Handle sig = java_lang_String::create_from_symbol(gs, CHECK_NULL);
     java_lang_reflect_Field::set_signature(rh(), sig());
@@ -991,11 +1003,12 @@
       THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "object is not an instance of declaring class");
     }
     // target klass is receiver's klass
     target_klass = receiver->klass();
     // no need to resolve if method is private or <init>
-    if (reflected_method->is_private() || reflected_method->name() == vmSymbols::object_initializer_name()) {
+    if (reflected_method->is_private() ||
+        reflected_method->name() == vmSymbols::object_initializer_name()) {
       method = reflected_method;
     } else {
       // resolve based on the receiver
       if (reflected_method->method_holder()->is_interface()) {
         // resolve interface call
@@ -1173,11 +1186,10 @@
   Method* m = klass->method_with_idnum(slot);
   if (m == nullptr) {
     THROW_MSG_0(vmSymbols::java_lang_InternalError(), "invoke");
   }
   methodHandle method(THREAD, m);
-  assert(method->name() == vmSymbols::object_initializer_name(), "invalid constructor");
 
   // Make sure klass gets initialize
   klass->initialize(CHECK_NULL);
 
   // Create new instance (the receiver)
diff a/src/hotspot/share/runtime/reflection.hpp b/src/hotspot/share/runtime/reflection.hpp
--- a/src/hotspot/share/runtime/reflection.hpp
+++ b/src/hotspot/share/runtime/reflection.hpp
@@ -48,10 +48,12 @@
   enum SomeConstants {
     PUBLIC            = 0,
     DECLARED          = 1,
     MEMBER_PUBLIC     = 0,
     MEMBER_DECLARED   = 1,
+    TRUSTED_FINAL     = 0x10,
+    NULL_RESTRICTED   = 0x20,
     MAX_DIM           = 255
   };
 
   // Results returned by verify_class_access()
   enum VerifyClassAccessResults {
diff a/src/hotspot/share/runtime/safepoint.cpp b/src/hotspot/share/runtime/safepoint.cpp
--- a/src/hotspot/share/runtime/safepoint.cpp
+++ b/src/hotspot/share/runtime/safepoint.cpp
@@ -44,10 +44,11 @@
 #include "logging/logStream.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/symbol.hpp"
+#include "oops/inlineKlass.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/deoptimization.hpp"
 #include "runtime/frame.inline.hpp"
 #include "runtime/globals.hpp"
 #include "runtime/handles.inline.hpp"
@@ -937,21 +938,38 @@
   // has already had the effect of causing the return to occur, so the execution
   // will continue immediately after the call. In addition, the oopmap at the
   // return point does not mark the return value as an oop (if it is), so
   // it needs a handle here to be updated.
   if( nm->is_at_poll_return(real_return_addr) ) {
+    ResourceMark rm;
     // See if return type is an oop.
-    bool return_oop = nm->method()->is_returning_oop();
+    Method* method = nm->method();
+    bool return_oop = method->is_returning_oop();
     HandleMark hm(self);
-    Handle return_value;
+    GrowableArray<Handle> return_values;
+    InlineKlass* vk = nullptr;
+    if (return_oop && InlineTypeReturnedAsFields &&
+        (method->result_type() == T_OBJECT)) {
+      // Check if an inline type is returned as fields
+      vk = InlineKlass::returned_inline_klass(map);
+      if (vk != nullptr) {
+        // We're at a safepoint at the return of a method that returns
+        // multiple values. We must make sure we preserve the oop values
+        // across the safepoint.
+        assert(vk == method->returns_inline_type(thread()), "bad inline klass");
+        vk->save_oop_fields(map, return_values);
+        return_oop = false;
+      }
+    }
+
     if (return_oop) {
       // The oop result has been saved on the stack together with all
       // the other registers. In order to preserve it over GCs we need
       // to keep it in a handle.
       oop result = caller_fr.saved_oop_result(&map);
       assert(oopDesc::is_oop_or_null(result), "must be oop");
-      return_value = Handle(self, result);
+      return_values.push(Handle(self, result));
       assert(Universe::heap()->is_in_or_null(result), "must be heap pointer");
     }
 
     // We get here if compiled return polls found a reason to call into the VM.
     // One condition for that is that the top frame is not yet safe to use.
@@ -961,11 +979,14 @@
     // Process pending operation
     SafepointMechanism::process_if_requested_with_exit_check(self, true /* check asyncs */);
 
     // restore oop result, if any
     if (return_oop) {
-      caller_fr.set_saved_oop_result(&map, return_value());
+      assert(return_values.length() == 1, "only one return value");
+      caller_fr.set_saved_oop_result(&map, return_values.pop()());
+    } else if (vk != nullptr) {
+      vk->restore_oop_results(map, return_values);
     }
   }
 
   // This is a safepoint poll. Verify the return address and block.
   else {
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
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
+ * 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.
@@ -42,18 +42,23 @@
 #include "interpreter/interpreter.hpp"
 #include "interpreter/interpreterRuntime.hpp"
 #include "jvm.h"
 #include "jfr/jfrEvents.hpp"
 #include "logging/log.hpp"
+#include "memory/oopFactory.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
+#include "oops/access.hpp"
+#include "oops/fieldStreams.inline.hpp"
 #include "metaprogramming/primitiveConversions.hpp"
 #include "oops/compiledICHolder.inline.hpp"
 #include "oops/klass.hpp"
 #include "oops/method.inline.hpp"
 #include "oops/objArrayKlass.hpp"
+#include "oops/objArrayOop.inline.hpp"
 #include "oops/oop.inline.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "prims/forte.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "prims/jvmtiThreadState.hpp"
 #include "prims/methodHandles.hpp"
 #include "prims/nativeLookup.hpp"
@@ -90,11 +95,10 @@
 RuntimeStub*        SharedRuntime::_wrong_method_abstract_blob;
 RuntimeStub*        SharedRuntime::_ic_miss_blob;
 RuntimeStub*        SharedRuntime::_resolve_opt_virtual_call_blob;
 RuntimeStub*        SharedRuntime::_resolve_virtual_call_blob;
 RuntimeStub*        SharedRuntime::_resolve_static_call_blob;
-address             SharedRuntime::_resolve_static_call_entry;
 
 DeoptimizationBlob* SharedRuntime::_deopt_blob;
 SafepointBlob*      SharedRuntime::_polling_page_vectors_safepoint_handler_blob;
 SafepointBlob*      SharedRuntime::_polling_page_safepoint_handler_blob;
 SafepointBlob*      SharedRuntime::_polling_page_return_handler_blob;
@@ -111,11 +115,10 @@
   _wrong_method_abstract_blob          = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::handle_wrong_method_abstract), "wrong_method_abstract_stub");
   _ic_miss_blob                        = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::handle_wrong_method_ic_miss),  "ic_miss_stub");
   _resolve_opt_virtual_call_blob       = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::resolve_opt_virtual_call_C),   "resolve_opt_virtual_call");
   _resolve_virtual_call_blob           = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::resolve_virtual_call_C),       "resolve_virtual_call");
   _resolve_static_call_blob            = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::resolve_static_call_C),        "resolve_static_call");
-  _resolve_static_call_entry           = _resolve_static_call_blob->entry_point();
 
   AdapterHandlerLibrary::initialize();
 
 #if COMPILER2_OR_JVMCI
   // Vectors are generated only by C2 and JVMCI.
@@ -1136,10 +1139,25 @@
     bc = Bytecodes::_invokestatic;
     LinkResolver::resolve_continuation_enter(callinfo, CHECK_NH);
     return receiver;
   }
 
+  // Substitutability test implementation piggy backs on static call resolution
+  Bytecodes::Code code = caller->java_code_at(bci);
+  if (code == Bytecodes::_if_acmpeq || code == Bytecodes::_if_acmpne) {
+    bc = Bytecodes::_invokestatic;
+    methodHandle attached_method(THREAD, extract_attached_method(vfst));
+    assert(attached_method.not_null(), "must have attached method");
+    vmClasses::ValueObjectMethods_klass()->initialize(CHECK_NH);
+    LinkResolver::resolve_invoke(callinfo, receiver, attached_method, bc, false, CHECK_NH);
+#ifdef ASSERT
+    Method* is_subst = vmClasses::ValueObjectMethods_klass()->find_method(vmSymbols::isSubstitutable_name(), vmSymbols::object_object_boolean_signature());
+    assert(callinfo.selected_method() == is_subst, "must be isSubstitutable method");
+#endif
+    return receiver;
+  }
+
   Bytecode_invoke bytecode(caller, bci);
   int bytecode_index = bytecode.index();
   bc = bytecode.invoke_code();
 
   methodHandle attached_method(current, extract_attached_method(vfst));
@@ -1171,18 +1189,25 @@
           }
           break;
         default:
           break;
       }
+    } else {
+      assert(attached_method->has_scalarized_args(), "invalid use of attached method");
+      if (!attached_method->method_holder()->is_inline_klass()) {
+        // Ignore the attached method in this case to not confuse below code
+        attached_method = methodHandle(current, nullptr);
+      }
     }
   }
 
   assert(bc != Bytecodes::_illegal, "not initialized");
 
   bool has_receiver = bc != Bytecodes::_invokestatic &&
                       bc != Bytecodes::_invokedynamic &&
                       bc != Bytecodes::_invokehandle;
+  bool check_null_and_abstract = true;
 
   // Find receiver for non-static call
   if (has_receiver) {
     // This register map must be update since we need to find the receiver for
     // compiled frames. The receiver might be in a register.
@@ -1192,39 +1217,50 @@
                          RegisterMap::WalkContinuation::skip);
     frame stubFrame   = current->last_frame();
     // Caller-frame is a compiled frame
     frame callerFrame = stubFrame.sender(&reg_map2);
 
-    if (attached_method.is_null()) {
-      Method* callee = bytecode.static_target(CHECK_NH);
+    Method* callee = attached_method();
+    if (callee == nullptr) {
+      callee = bytecode.static_target(CHECK_NH);
       if (callee == nullptr) {
         THROW_(vmSymbols::java_lang_NoSuchMethodException(), nullHandle);
       }
     }
-
-    // Retrieve from a compiled argument list
-    receiver = Handle(current, callerFrame.retrieve_receiver(&reg_map2));
-    assert(oopDesc::is_oop_or_null(receiver()), "");
-
-    if (receiver.is_null()) {
-      THROW_(vmSymbols::java_lang_NullPointerException(), nullHandle);
+    bool caller_is_c1 = callerFrame.is_compiled_frame() && callerFrame.cb()->is_compiled_by_c1();
+    if (!caller_is_c1 && callee->is_scalarized_arg(0)) {
+      // If the receiver is an inline type that is passed as fields, no oop is available
+      // Resolve the call without receiver null checking.
+      assert(!callee->mismatch(), "calls with inline type receivers should never mismatch");
+      assert(attached_method.not_null() && !attached_method->is_abstract(), "must have non-abstract attached method");
+      if (bc == Bytecodes::_invokeinterface) {
+        bc = Bytecodes::_invokevirtual; // C2 optimistically replaces interface calls by virtual calls
+      }
+      check_null_and_abstract = false;
+    } else {
+      // Retrieve from a compiled argument list
+      receiver = Handle(current, callerFrame.retrieve_receiver(&reg_map2));
+      assert(oopDesc::is_oop_or_null(receiver()), "");
+      if (receiver.is_null()) {
+        THROW_(vmSymbols::java_lang_NullPointerException(), nullHandle);
+      }
     }
   }
 
   // Resolve method
   if (attached_method.not_null()) {
     // Parameterized by attached method.
-    LinkResolver::resolve_invoke(callinfo, receiver, attached_method, bc, CHECK_NH);
+    LinkResolver::resolve_invoke(callinfo, receiver, attached_method, bc, check_null_and_abstract, CHECK_NH);
   } else {
     // Parameterized by bytecode.
     constantPoolHandle constants(current, caller->constants());
     LinkResolver::resolve_invoke(callinfo, receiver, constants, bytecode_index, bc, CHECK_NH);
   }
 
 #ifdef ASSERT
   // Check that the receiver klass is of the right subtype and that it is initialized for virtual calls
-  if (has_receiver) {
+  if (has_receiver && check_null_and_abstract) {
     assert(receiver.not_null(), "should have thrown exception");
     Klass* receiver_klass = receiver->klass();
     Klass* rk = nullptr;
     if (attached_method.not_null()) {
       // In case there's resolved method attached, use its holder during the check.
@@ -1248,11 +1284,11 @@
 #endif
 
   return receiver;
 }
 
-methodHandle SharedRuntime::find_callee_method(TRAPS) {
+methodHandle SharedRuntime::find_callee_method(bool is_optimized, bool& caller_is_c1, TRAPS) {
   JavaThread* current = THREAD;
   ResourceMark rm(current);
   // We need first to check if any Java activations (compiled, interpreted)
   // exist on the stack since last JavaCall.  If not, we need
   // to get the target method from the JavaCall wrapper.
@@ -1274,20 +1310,24 @@
     callee_method = methodHandle(current, fr.entry_frame_call_wrapper()->callee_method());
   } else {
     Bytecodes::Code bc;
     CallInfo callinfo;
     find_callee_info_helper(vfst, bc, callinfo, CHECK_(methodHandle()));
+    // Calls via mismatching methods are always non-scalarized
+    if (callinfo.resolved_method()->mismatch() && !is_optimized) {
+      caller_is_c1 = true;
+    }
     callee_method = methodHandle(current, callinfo.selected_method());
   }
   assert(callee_method()->is_method(), "must be");
   return callee_method;
 }
 
 // Resolves a call.
-methodHandle SharedRuntime::resolve_helper(bool is_virtual, bool is_optimized, TRAPS) {
+methodHandle SharedRuntime::resolve_helper(bool is_virtual, bool is_optimized, bool& caller_is_c1, TRAPS) {
   methodHandle callee_method;
-  callee_method = resolve_sub_helper(is_virtual, is_optimized, THREAD);
+  callee_method = resolve_sub_helper(is_virtual, is_optimized, caller_is_c1, THREAD);
   if (JvmtiExport::can_hotswap_or_post_breakpoint()) {
     int retry_count = 0;
     while (!HAS_PENDING_EXCEPTION && callee_method->is_old() &&
            callee_method->method_holder() != vmClasses::Object_klass()) {
       // If has a pending exception then there is no need to re-try to
@@ -1300,19 +1340,19 @@
       // in the middle of resolve. If it is looping here more than 100 times
       // means then there could be a bug here.
       guarantee((retry_count++ < 100),
                 "Could not resolve to latest version of redefined method");
       // method is redefined in the middle of resolve so re-try.
-      callee_method = resolve_sub_helper(is_virtual, is_optimized, THREAD);
+      callee_method = resolve_sub_helper(is_virtual, is_optimized, caller_is_c1, THREAD);
     }
   }
   return callee_method;
 }
 
 // This fails if resolution required refilling of IC stubs
 bool SharedRuntime::resolve_sub_helper_internal(methodHandle callee_method, const frame& caller_frame,
-                                                CompiledMethod* caller_nm, bool is_virtual, bool is_optimized,
+                                                CompiledMethod* caller_nm, bool is_virtual, bool is_optimized, bool& caller_is_c1,
                                                 Handle receiver, CallInfo& call_info, Bytecodes::Code invoke_code, TRAPS) {
   StaticCallInfo static_call_info;
   CompiledICInfo virtual_call_info;
 
   // Make sure the callee nmethod does not get deoptimized and removed before
@@ -1332,19 +1372,25 @@
 #endif
 
   bool is_nmethod = caller_nm->is_nmethod();
 
   if (is_virtual) {
-    assert(receiver.not_null() || invoke_code == Bytecodes::_invokehandle, "sanity check");
+    Klass* receiver_klass = nullptr;
+    if (!caller_is_c1 && callee_method->is_scalarized_arg(0)) {
+      // If the receiver is an inline type that is passed as fields, no oop is available
+      receiver_klass = callee_method->method_holder();
+    } else {
+      assert(receiver.not_null() || invoke_code == Bytecodes::_invokehandle, "sanity check");
+      receiver_klass = invoke_code == Bytecodes::_invokehandle ? nullptr : receiver->klass();
+    }
     bool static_bound = call_info.resolved_method()->can_be_statically_bound();
-    Klass* klass = invoke_code == Bytecodes::_invokehandle ? nullptr : receiver->klass();
-    CompiledIC::compute_monomorphic_entry(callee_method, klass,
-                     is_optimized, static_bound, is_nmethod, virtual_call_info,
+    CompiledIC::compute_monomorphic_entry(callee_method, receiver_klass,
+                     is_optimized, static_bound, is_nmethod, caller_is_c1, virtual_call_info,
                      CHECK_false);
   } else {
     // static call
-    CompiledStaticCall::compute_entry(callee_method, is_nmethod, static_call_info);
+    CompiledStaticCall::compute_entry(callee_method, caller_nm, static_call_info);
   }
 
   JFR_ONLY(bool patched_caller = false;)
   // grab lock, check for deoptimization and potentially patch caller
   {
@@ -1399,11 +1445,11 @@
   return true;
 }
 
 // Resolves a call.  The compilers generate code for calls that go here
 // and are patched with the real destination of the call.
-methodHandle SharedRuntime::resolve_sub_helper(bool is_virtual, bool is_optimized, TRAPS) {
+methodHandle SharedRuntime::resolve_sub_helper(bool is_virtual, bool is_optimized, bool& caller_is_c1, TRAPS) {
   JavaThread* current = THREAD;
   ResourceMark rm(current);
   RegisterMap cbl_map(current,
                       RegisterMap::UpdateMap::skip,
                       RegisterMap::ProcessFrames::include,
@@ -1419,10 +1465,14 @@
   //       b) an exception is thrown if receiver is null for non-static calls
   CallInfo call_info;
   Bytecodes::Code invoke_code = Bytecodes::_illegal;
   Handle receiver = find_callee_info(invoke_code, call_info, CHECK_(methodHandle()));
   methodHandle callee_method(current, call_info.selected_method());
+  // Calls via mismatching methods are always non-scalarized
+  if (caller_nm->is_compiled_by_c1() || (call_info.resolved_method()->mismatch() && !is_optimized)) {
+    caller_is_c1 = true;
+  }
 
   assert((!is_virtual && invoke_code == Bytecodes::_invokestatic ) ||
          (!is_virtual && invoke_code == Bytecodes::_invokespecial) ||
          (!is_virtual && invoke_code == Bytecodes::_invokehandle ) ||
          (!is_virtual && invoke_code == Bytecodes::_invokedynamic) ||
@@ -1483,11 +1533,11 @@
   // Patching IC caches may fail if we run out if transition stubs.
   // We refill the ic stubs then and try again.
   for (;;) {
     ICRefillVerifier ic_refill_verifier;
     bool successful = resolve_sub_helper_internal(callee_method, caller_frame, caller_nm,
-                                                  is_virtual, is_optimized, receiver,
+                                                  is_virtual, is_optimized, caller_is_c1, receiver,
                                                   call_info, invoke_code, CHECK_(methodHandle()));
     if (successful) {
       return callee_method;
     } else {
       InlineCacheBuffer::refill_ic_stubs();
@@ -1509,18 +1559,19 @@
   frame caller_frame = stub_frame.sender(&reg_map);
   assert(!caller_frame.is_interpreted_frame() && !caller_frame.is_entry_frame() && !caller_frame.is_upcall_stub_frame(), "unexpected frame");
 #endif /* ASSERT */
 
   methodHandle callee_method;
+  bool is_optimized = false;
+  bool caller_is_c1 = false;
   JRT_BLOCK
-    callee_method = SharedRuntime::handle_ic_miss_helper(CHECK_NULL);
+    callee_method = SharedRuntime::handle_ic_miss_helper(is_optimized, caller_is_c1, CHECK_NULL);
     // Return Method* through TLS
     current->set_vm_result_2(callee_method());
   JRT_BLOCK_END
   // return compiled code entry point after potential safepoints
-  assert(callee_method->verified_code_entry() != nullptr, " Jump to zero!");
-  return callee_method->verified_code_entry();
+  return entry_for_handle_wrong_method(callee_method, false, is_optimized, caller_is_c1);
 JRT_END
 
 
 // Handle call site that has been made non-entrant
 JRT_BLOCK_ENTRY(address, SharedRuntime::handle_wrong_method(JavaThread* current))
@@ -1557,24 +1608,30 @@
       // JVM upcalls may land here as well, but there's a proper check present in
       // LinkResolver::resolve_static_call (called from JavaCalls::call_static),
       // so bypassing it in c2i adapter is benign.
       return callee->get_c2i_no_clinit_check_entry();
     } else {
-      return callee->get_c2i_entry();
+      if (caller_frame.is_interpreted_frame()) {
+        return callee->get_c2i_inline_entry();
+      } else {
+        return callee->get_c2i_entry();
+      }
     }
   }
 
   // Must be compiled to compiled path which is safe to stackwalk
   methodHandle callee_method;
+  bool is_static_call = false;
+  bool is_optimized = false;
+  bool caller_is_c1 = false;
   JRT_BLOCK
     // Force resolving of caller (if we called from compiled frame)
-    callee_method = SharedRuntime::reresolve_call_site(CHECK_NULL);
+    callee_method = SharedRuntime::reresolve_call_site(is_static_call, is_optimized, caller_is_c1, CHECK_NULL);
     current->set_vm_result_2(callee_method());
   JRT_BLOCK_END
   // return compiled code entry point after potential safepoints
-  assert(callee_method->verified_code_entry() != nullptr, " Jump to zero!");
-  return callee_method->verified_code_entry();
+  return entry_for_handle_wrong_method(callee_method, is_static_call, is_optimized, caller_is_c1);
 JRT_END
 
 // Handle abstract method call
 JRT_BLOCK_ENTRY(address, SharedRuntime::handle_wrong_method_abstract(JavaThread* current))
   // Verbose error message for AbstractMethodError.
@@ -1611,13 +1668,14 @@
 
 
 // resolve a static call and patch code
 JRT_BLOCK_ENTRY(address, SharedRuntime::resolve_static_call_C(JavaThread* current ))
   methodHandle callee_method;
+  bool caller_is_c1 = false;
   bool enter_special = false;
   JRT_BLOCK
-    callee_method = SharedRuntime::resolve_helper(false, false, CHECK_NULL);
+    callee_method = SharedRuntime::resolve_helper(false, false, caller_is_c1, CHECK_NULL);
     current->set_vm_result_2(callee_method());
 
     if (current->is_interp_only_mode()) {
       RegisterMap reg_map(current,
                           RegisterMap::UpdateMap::skip,
@@ -1641,60 +1699,69 @@
     // interpreted version.
     return callee_method->get_c2i_entry();
   }
 
   // return compiled code entry point after potential safepoints
-  assert(callee_method->verified_code_entry() != nullptr, " Jump to zero!");
-  return callee_method->verified_code_entry();
+  address entry = caller_is_c1 ?
+    callee_method->verified_inline_code_entry() : callee_method->verified_code_entry();
+  assert(entry != nullptr, "Jump to zero!");
+  return entry;
 JRT_END
 
 
 // resolve virtual call and update inline cache to monomorphic
 JRT_BLOCK_ENTRY(address, SharedRuntime::resolve_virtual_call_C(JavaThread* current))
   methodHandle callee_method;
+  bool caller_is_c1 = false;
   JRT_BLOCK
-    callee_method = SharedRuntime::resolve_helper(true, false, CHECK_NULL);
+    callee_method = SharedRuntime::resolve_helper(true, false, caller_is_c1, CHECK_NULL);
     current->set_vm_result_2(callee_method());
   JRT_BLOCK_END
   // return compiled code entry point after potential safepoints
-  assert(callee_method->verified_code_entry() != nullptr, " Jump to zero!");
-  return callee_method->verified_code_entry();
+  address entry = caller_is_c1 ?
+    callee_method->verified_inline_code_entry() : callee_method->verified_inline_ro_code_entry();
+  assert(entry != nullptr, "Jump to zero!");
+  return entry;
 JRT_END
 
 
 // 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))
   methodHandle callee_method;
+  bool caller_is_c1 = false;
   JRT_BLOCK
-    callee_method = SharedRuntime::resolve_helper(true, true, CHECK_NULL);
+    callee_method = SharedRuntime::resolve_helper(true, true, caller_is_c1, CHECK_NULL);
     current->set_vm_result_2(callee_method());
   JRT_BLOCK_END
   // return compiled code entry point after potential safepoints
-  assert(callee_method->verified_code_entry() != nullptr, " Jump to zero!");
-  return callee_method->verified_code_entry();
+  address entry = caller_is_c1 ?
+    callee_method->verified_inline_code_entry() : callee_method->verified_code_entry();
+  assert(entry != nullptr, "Jump to zero!");
+  return entry;
 JRT_END
 
 // The handle_ic_miss_helper_internal function returns false if it failed due
 // to either running out of vtable stubs or ic stubs due to IC transitions
 // to transitional states. The needs_ic_stub_refill value will be set if
 // the failure was due to running out of IC stubs, in which case handle_ic_miss_helper
 // refills the IC stubs and tries again.
 bool SharedRuntime::handle_ic_miss_helper_internal(Handle receiver, CompiledMethod* caller_nm,
                                                    const frame& caller_frame, methodHandle callee_method,
                                                    Bytecodes::Code bc, CallInfo& call_info,
-                                                   bool& needs_ic_stub_refill, TRAPS) {
+                                                   bool& needs_ic_stub_refill, bool& is_optimized, bool caller_is_c1, TRAPS) {
   CompiledICLocker ml(caller_nm);
   CompiledIC* inline_cache = CompiledIC_before(caller_nm, caller_frame.pc());
   bool should_be_mono = false;
   if (inline_cache->is_optimized()) {
     if (TraceCallFixup) {
       ResourceMark rm(THREAD);
       tty->print("OPTIMIZED IC miss (%s) call to", Bytecodes::name(bc));
       callee_method->print_short_name(tty);
       tty->print_cr(" code: " INTPTR_FORMAT, p2i(callee_method->code()));
     }
+    is_optimized = true;
     should_be_mono = true;
   } else if (inline_cache->is_icholder_call()) {
     CompiledICHolder* ic_oop = inline_cache->cached_icholder();
     if (ic_oop != nullptr) {
       if (!ic_oop->is_loader_alive()) {
@@ -1728,19 +1795,20 @@
     Klass* receiver_klass = receiver()->klass();
     inline_cache->compute_monomorphic_entry(callee_method,
                                             receiver_klass,
                                             inline_cache->is_optimized(),
                                             false, caller_nm->is_nmethod(),
+                                            caller_is_c1,
                                             info, CHECK_false);
     if (!inline_cache->set_to_monomorphic(info)) {
       needs_ic_stub_refill = true;
       return false;
     }
   } else if (!inline_cache->is_megamorphic() && !inline_cache->is_clean()) {
     // Potential change to megamorphic
 
-    bool successful = inline_cache->set_to_megamorphic(&call_info, bc, needs_ic_stub_refill, CHECK_false);
+    bool successful = inline_cache->set_to_megamorphic(&call_info, bc, needs_ic_stub_refill, caller_is_c1, CHECK_false);
     if (needs_ic_stub_refill) {
       return false;
     }
     if (!successful) {
       if (!inline_cache->set_to_clean()) {
@@ -1752,11 +1820,11 @@
     // Either clean or megamorphic
   }
   return true;
 }
 
-methodHandle SharedRuntime::handle_ic_miss_helper(TRAPS) {
+methodHandle SharedRuntime::handle_ic_miss_helper(bool& is_optimized, bool& caller_is_c1, TRAPS) {
   JavaThread* current = THREAD;
   ResourceMark rm(current);
   CallInfo call_info;
   Bytecodes::Code bc;
 
@@ -1772,11 +1840,13 @@
   // plain ic_miss) and the site will be converted to an optimized virtual call site
   // never to miss again. I don't believe C2 will produce code like this but if it
   // did this would still be the correct thing to do for it too, hence no ifdef.
   //
   if (call_info.resolved_method()->can_be_statically_bound()) {
-    methodHandle callee_method = SharedRuntime::reresolve_call_site(CHECK_(methodHandle()));
+    bool is_static_call = false;
+    methodHandle callee_method = SharedRuntime::reresolve_call_site(is_static_call, is_optimized, caller_is_c1, CHECK_(methodHandle()));
+    assert(!is_static_call, "IC miss at static call?");
     if (TraceCallFixup) {
       RegisterMap reg_map(current,
                           RegisterMap::UpdateMap::skip,
                           RegisterMap::ProcessFrames::include,
                           RegisterMap::WalkContinuation::skip);
@@ -1831,16 +1901,20 @@
                       RegisterMap::ProcessFrames::include,
                       RegisterMap::WalkContinuation::skip);
   frame caller_frame = current->last_frame().sender(&reg_map);
   CodeBlob* cb = caller_frame.cb();
   CompiledMethod* caller_nm = cb->as_compiled_method();
+  // Calls via mismatching methods are always non-scalarized
+  if (caller_nm->is_compiled_by_c1() || call_info.resolved_method()->mismatch()) {
+    caller_is_c1 = true;
+  }
 
   for (;;) {
     ICRefillVerifier ic_refill_verifier;
     bool needs_ic_stub_refill = false;
     bool successful = handle_ic_miss_helper_internal(receiver, caller_nm, caller_frame, callee_method,
-                                                     bc, call_info, needs_ic_stub_refill, CHECK_(methodHandle()));
+                                                     bc, call_info, needs_ic_stub_refill, is_optimized, caller_is_c1, CHECK_(methodHandle()));
     if (successful || !needs_ic_stub_refill) {
       return callee_method;
     } else {
       InlineCacheBuffer::refill_ic_stubs();
     }
@@ -1868,20 +1942,23 @@
 // Resets a call-site in compiled code so it will get resolved again.
 // This routines handles both virtual call sites, optimized virtual call
 // sites, and static call sites. Typically used to change a call sites
 // destination from compiled to interpreted.
 //
-methodHandle SharedRuntime::reresolve_call_site(TRAPS) {
+methodHandle SharedRuntime::reresolve_call_site(bool& is_static_call, bool& is_optimized, bool& caller_is_c1, TRAPS) {
   JavaThread* current = THREAD;
   ResourceMark rm(current);
   RegisterMap reg_map(current,
                       RegisterMap::UpdateMap::skip,
                       RegisterMap::ProcessFrames::include,
                       RegisterMap::WalkContinuation::skip);
   frame stub_frame = current->last_frame();
   assert(stub_frame.is_runtime_frame(), "must be a runtimeStub");
   frame caller = stub_frame.sender(&reg_map);
+  if (caller.is_compiled_frame()) {
+    caller_is_c1 = caller.cb()->is_compiled_by_c1();
+  }
 
   // Do nothing if the frame isn't a live compiled frame.
   // nmethod could be deoptimized by the time we get here
   // so no update to the caller is needed.
 
@@ -1889,11 +1966,10 @@
       (caller.is_native_frame() && ((CompiledMethod*)caller.cb())->method()->is_continuation_enter_intrinsic())) {
 
     address pc = caller.pc();
 
     // Check for static or virtual call
-    bool is_static_call = false;
     CompiledMethod* caller_nm = CodeCache::find_compiled(pc);
 
     // Default call_addr is the location of the "basic" call.
     // Determine the address of the call we a reresolving. With
     // Inline Caches we will always find a recognizable call.
@@ -1927,17 +2003,19 @@
       // On x86 the logic for finding a call instruction is blindly checking for a call opcode 5
       // bytes back in the instruction stream so we must also check for reloc info.
       RelocIterator iter(caller_nm, call_addr, call_addr+1);
       bool ret = iter.next(); // Get item
       if (ret) {
-        bool is_static_call = false;
+        is_static_call = false;
+        is_optimized = false;
         switch (iter.type()) {
           case relocInfo::static_call_type:
             is_static_call = true;
 
           case relocInfo::virtual_call_type:
           case relocInfo::opt_virtual_call_type:
+            is_optimized = (iter.type() == relocInfo::opt_virtual_call_type);
             // Cleaning the inline cache will force a new resolve. This is more robust
             // than directly setting it to the new destination, since resolving of calls
             // is always done through the same code path. (experience shows that it
             // leads to very hard to track down bugs, if an inline cache gets updated
             // to a wrong method). It should not be performance critical, since the
@@ -1957,12 +2035,11 @@
         }
       }
     }
   }
 
-  methodHandle callee_method = find_callee_method(CHECK_(methodHandle()));
-
+  methodHandle callee_method = find_callee_method(is_optimized, caller_is_c1, CHECK_(methodHandle()));
 
 #ifndef PRODUCT
   Atomic::inc(&_wrong_method_ctr);
 
   if (TraceCallFixup) {
@@ -2130,11 +2207,11 @@
         if (ContinuationEntry::is_interpreted_call(call->instruction_address())) {
           return;
         }
       }
       address destination = call->destination();
-      address entry_point = callee->verified_entry_point();
+      address entry_point = cb->is_compiled_by_c1() ? callee->verified_inline_entry_point() : callee->verified_entry_point();
       if (should_fixup_call_destination(destination, entry_point, caller_pc, moop, cb)) {
         call->set_destination_mt_safe(entry_point);
       }
     }
   }
@@ -2442,11 +2519,11 @@
 // A simple wrapper class around the calling convention information
 // that allows sharing of adapters for the same calling convention.
 class AdapterFingerPrint : public CHeapObj<mtCode> {
  private:
   enum {
-    _basic_type_bits = 4,
+    _basic_type_bits = 5,
     _basic_type_mask = right_n_bits(_basic_type_bits),
     _basic_types_per_int = BitsPerInt / _basic_type_bits,
     _compact_int_count = 3
   };
   // TO DO:  Consider integrating this with a more global scheme for compressing signatures.
@@ -2460,17 +2537,17 @@
                // Otherwise _value._fingerprint is the array.
 
   // Remap BasicTypes that are handled equivalently by the adapters.
   // These are correct for the current system but someday it might be
   // necessary to make this mapping platform dependent.
-  static int adapter_encoding(BasicType in) {
+  static BasicType adapter_encoding(BasicType in) {
     switch (in) {
       case T_BOOLEAN:
       case T_BYTE:
       case T_SHORT:
       case T_CHAR:
-        // There are all promoted to T_INT in the calling convention
+        // They are all promoted to T_INT in the calling convention
         return T_INT;
 
       case T_OBJECT:
       case T_ARRAY:
         // In other words, we assume that any register good enough for
@@ -2493,13 +2570,14 @@
         return T_CONFLICT;
     }
   }
 
  public:
-  AdapterFingerPrint(int total_args_passed, BasicType* sig_bt) {
+  AdapterFingerPrint(const GrowableArray<SigEntry>* sig, bool has_ro_adapter = false) {
     // The fingerprint is based on the BasicType signature encoded
     // into an array of ints with eight entries per int.
+    int total_args_passed = (sig != nullptr) ? sig->length() : 0;
     int* ptr;
     int len = (total_args_passed + (_basic_types_per_int-1)) / _basic_types_per_int;
     if (len <= _compact_int_count) {
       assert(_compact_int_count == 3, "else change next line");
       _value._compact[0] = _value._compact[1] = _value._compact[2] = 0;
@@ -2513,19 +2591,45 @@
       ptr = _value._fingerprint;
     }
 
     // Now pack the BasicTypes with 8 per int
     int sig_index = 0;
+    BasicType prev_bt = T_ILLEGAL;
+    int vt_count = 0;
     for (int index = 0; index < len; index++) {
       int value = 0;
-      for (int byte = 0; sig_index < total_args_passed && byte < _basic_types_per_int; byte++) {
-        int bt = adapter_encoding(sig_bt[sig_index++]);
-        assert((bt & _basic_type_mask) == bt, "must fit in 4 bits");
-        value = (value << _basic_type_bits) | bt;
+      for (int byte = 0; byte < _basic_types_per_int; byte++) {
+        BasicType bt = T_ILLEGAL;
+        if (sig_index < total_args_passed) {
+          bt = sig->at(sig_index++)._bt;
+          if (bt == T_METADATA) {
+            // Found start of inline type in signature
+            assert(InlineTypePassFieldsAsArgs, "unexpected start of inline type");
+            if (sig_index == 1 && has_ro_adapter) {
+              // With a ro_adapter, replace receiver inline type delimiter by T_VOID to prevent matching
+              // with other adapters that have the same inline type as first argument and no receiver.
+              bt = T_VOID;
+            }
+            vt_count++;
+          } else if (bt == T_VOID && prev_bt != T_LONG && prev_bt != T_DOUBLE) {
+            // Found end of inline type in signature
+            assert(InlineTypePassFieldsAsArgs, "unexpected end of inline type");
+            vt_count--;
+            assert(vt_count >= 0, "invalid vt_count");
+          } else if (vt_count == 0) {
+            // Widen fields that are not part of a scalarized inline type argument
+            bt = adapter_encoding(bt);
+          }
+          prev_bt = bt;
+        }
+        int bt_val = (bt == T_ILLEGAL) ? 0 : bt;
+        assert((bt_val & _basic_type_mask) == bt_val, "must fit in 4 bits");
+        value = (value << _basic_type_bits) | bt_val;
       }
       ptr[index] = value;
     }
+    assert(vt_count == 0, "invalid vt_count");
   }
 
   ~AdapterFingerPrint() {
     if (_length > 0) {
       FREE_C_HEAP_ARRAY(int, _value._fingerprint);
@@ -2586,18 +2690,14 @@
           if (v == T_VOID) {
             st.print("J");
           } else {
             st.print("L");
           }
-        }
-        switch (v) {
-          case T_INT:    st.print("I");    break;
-          case T_LONG:   long_prev = true; break;
-          case T_FLOAT:  st.print("F");    break;
-          case T_DOUBLE: st.print("D");    break;
-          case T_VOID:   break;
-          default: ShouldNotReachHere();
+        } else if (v == T_LONG) {
+          long_prev = true;
+        } else if (v != T_VOID){
+          st.print("%c", type2char((BasicType)v));
         }
       }
     }
     if (long_prev) {
       st.print("L");
@@ -2641,14 +2741,14 @@
                   AdapterFingerPrint::compute_hash,
                   AdapterFingerPrint::equals>;
 static AdapterHandlerTable* _adapter_handler_table;
 
 // Find a entry with the same fingerprint if it exists
-static AdapterHandlerEntry* lookup(int total_args_passed, BasicType* sig_bt) {
+static AdapterHandlerEntry* lookup(const GrowableArray<SigEntry>* sig, bool has_ro_adapter = false) {
   NOT_PRODUCT(_lookups++);
   assert_lock_strong(AdapterHandlerLibrary_lock);
-  AdapterFingerPrint fp(total_args_passed, sig_bt);
+  AdapterFingerPrint fp(sig, has_ro_adapter);
   AdapterHandlerEntry** entry = _adapter_handler_table->get(&fp);
   if (entry != nullptr) {
 #ifndef PRODUCT
     if (fp.is_compact()) _compact++;
     _hits++;
@@ -2678,11 +2778,11 @@
 AdapterHandlerEntry* AdapterHandlerLibrary::_no_arg_handler = nullptr;
 AdapterHandlerEntry* AdapterHandlerLibrary::_int_arg_handler = nullptr;
 AdapterHandlerEntry* AdapterHandlerLibrary::_obj_arg_handler = nullptr;
 AdapterHandlerEntry* AdapterHandlerLibrary::_obj_int_arg_handler = nullptr;
 AdapterHandlerEntry* AdapterHandlerLibrary::_obj_obj_arg_handler = nullptr;
-const int AdapterHandlerLibrary_size = 16*K;
+const int AdapterHandlerLibrary_size = 48*K;
 BufferBlob* AdapterHandlerLibrary::_buffer = nullptr;
 
 BufferBlob* AdapterHandlerLibrary::buffer_blob() {
   return _buffer;
 }
@@ -2721,35 +2821,49 @@
     // are never compiled so an i2c entry is somewhat meaningless, but
     // throw AbstractMethodError just in case.
     // Pass wrong_method_abstract for the c2i transitions to return
     // AbstractMethodError for invalid invocations.
     address wrong_method_abstract = SharedRuntime::get_handle_wrong_method_abstract_stub();
-    _abstract_method_handler = AdapterHandlerLibrary::new_entry(new AdapterFingerPrint(0, nullptr),
+    _abstract_method_handler = AdapterHandlerLibrary::new_entry(new AdapterFingerPrint(nullptr),
                                                                 StubRoutines::throw_AbstractMethodError_entry(),
+                                                                wrong_method_abstract, wrong_method_abstract, wrong_method_abstract,
                                                                 wrong_method_abstract, wrong_method_abstract);
-
     _buffer = BufferBlob::create("adapters", AdapterHandlerLibrary_size);
-    _no_arg_handler = create_adapter(no_arg_blob, 0, nullptr, true);
 
-    BasicType obj_args[] = { T_OBJECT };
-    _obj_arg_handler = create_adapter(obj_arg_blob, 1, obj_args, true);
+    CompiledEntrySignature no_args;
+    no_args.compute_calling_conventions();
+    _no_arg_handler = create_adapter(no_arg_blob, no_args, true);
 
-    BasicType int_args[] = { T_INT };
-    _int_arg_handler = create_adapter(int_arg_blob, 1, int_args, true);
+    CompiledEntrySignature obj_args;
+    SigEntry::add_entry(obj_args.sig(), T_OBJECT, nullptr);
+    obj_args.compute_calling_conventions();
+    _obj_arg_handler = create_adapter(obj_arg_blob, obj_args, true);
 
-    BasicType obj_int_args[] = { T_OBJECT, T_INT };
-    _obj_int_arg_handler = create_adapter(obj_int_arg_blob, 2, obj_int_args, true);
+    CompiledEntrySignature int_args;
+    SigEntry::add_entry(int_args.sig(), T_INT, nullptr);
+    int_args.compute_calling_conventions();
+    _int_arg_handler = create_adapter(int_arg_blob, int_args, true);
 
-    BasicType obj_obj_args[] = { T_OBJECT, T_OBJECT };
-    _obj_obj_arg_handler = create_adapter(obj_obj_arg_blob, 2, obj_obj_args, true);
+    CompiledEntrySignature obj_int_args;
+    SigEntry::add_entry(obj_int_args.sig(), T_OBJECT, nullptr);
+    SigEntry::add_entry(obj_int_args.sig(), T_INT, nullptr);
+    obj_int_args.compute_calling_conventions();
+    _obj_int_arg_handler = create_adapter(obj_int_arg_blob, obj_int_args, true);
+
+    CompiledEntrySignature obj_obj_args;
+    SigEntry::add_entry(obj_obj_args.sig(), T_OBJECT, nullptr);
+    SigEntry::add_entry(obj_obj_args.sig(), T_OBJECT, nullptr);
+    obj_obj_args.compute_calling_conventions();
+    _obj_obj_arg_handler = create_adapter(obj_obj_arg_blob, obj_obj_args, true);
 
     assert(no_arg_blob != nullptr &&
           obj_arg_blob != nullptr &&
           int_arg_blob != nullptr &&
           obj_int_arg_blob != nullptr &&
           obj_obj_arg_blob != nullptr, "Initial adapters must be properly created");
   }
+  return;
 
   // Outside of the lock
   post_adapter_creation(no_arg_blob, _no_arg_handler);
   post_adapter_creation(obj_arg_blob, _obj_arg_handler);
   post_adapter_creation(int_arg_blob, _int_arg_handler);
@@ -2758,43 +2872,66 @@
 }
 
 AdapterHandlerEntry* AdapterHandlerLibrary::new_entry(AdapterFingerPrint* fingerprint,
                                                       address i2c_entry,
                                                       address c2i_entry,
+                                                      address c2i_inline_entry,
+                                                      address c2i_inline_ro_entry,
                                                       address c2i_unverified_entry,
+                                                      address c2i_unverified_inline_entry,
                                                       address c2i_no_clinit_check_entry) {
-  // Insert an entry into the table
-  return new AdapterHandlerEntry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry,
-                                 c2i_no_clinit_check_entry);
+  return new AdapterHandlerEntry(fingerprint, i2c_entry, c2i_entry, c2i_inline_entry, c2i_inline_ro_entry, c2i_unverified_entry,
+                              c2i_unverified_inline_entry, c2i_no_clinit_check_entry);
 }
 
 AdapterHandlerEntry* AdapterHandlerLibrary::get_simple_adapter(const methodHandle& method) {
   if (method->is_abstract()) {
-    return _abstract_method_handler;
+    return nullptr;
   }
   int total_args_passed = method->size_of_parameters(); // All args on stack
   if (total_args_passed == 0) {
     return _no_arg_handler;
   } else if (total_args_passed == 1) {
     if (!method->is_static()) {
+      if (InlineTypePassFieldsAsArgs && method->method_holder()->is_inline_klass()) {
+        return nullptr;
+      }
       return _obj_arg_handler;
     }
     switch (method->signature()->char_at(1)) {
-      case JVM_SIGNATURE_CLASS:
+      case JVM_SIGNATURE_CLASS: {
+        if (InlineTypePassFieldsAsArgs) {
+          SignatureStream ss(method->signature());
+          InlineKlass* vk = ss.as_inline_klass(method->method_holder());
+          if (vk != nullptr) {
+            return nullptr;
+          }
+        }
+        return _obj_arg_handler;
+      }
       case JVM_SIGNATURE_ARRAY:
         return _obj_arg_handler;
       case JVM_SIGNATURE_INT:
       case JVM_SIGNATURE_BOOLEAN:
       case JVM_SIGNATURE_CHAR:
       case JVM_SIGNATURE_BYTE:
       case JVM_SIGNATURE_SHORT:
         return _int_arg_handler;
     }
   } else if (total_args_passed == 2 &&
-             !method->is_static()) {
+             !method->is_static() && (!InlineTypePassFieldsAsArgs || !method->method_holder()->is_inline_klass())) {
     switch (method->signature()->char_at(1)) {
-      case JVM_SIGNATURE_CLASS:
+      case JVM_SIGNATURE_CLASS: {
+        if (InlineTypePassFieldsAsArgs) {
+          SignatureStream ss(method->signature());
+          InlineKlass* vk = ss.as_inline_klass(method->method_holder());
+          if (vk != nullptr) {
+            return nullptr;
+          }
+        }
+        return _obj_obj_arg_handler;
+      }
       case JVM_SIGNATURE_ARRAY:
         return _obj_obj_arg_handler;
       case JVM_SIGNATURE_INT:
       case JVM_SIGNATURE_BOOLEAN:
       case JVM_SIGNATURE_CHAR:
@@ -2804,51 +2941,233 @@
     }
   }
   return nullptr;
 }
 
-class AdapterSignatureIterator : public SignatureIterator {
- private:
-  BasicType stack_sig_bt[16];
-  BasicType* sig_bt;
-  int index;
+CompiledEntrySignature::CompiledEntrySignature(Method* method) :
+  _method(method), _num_inline_args(0), _has_inline_recv(false),
+  _regs(nullptr), _regs_cc(nullptr), _regs_cc_ro(nullptr),
+  _args_on_stack(0), _args_on_stack_cc(0), _args_on_stack_cc_ro(0),
+  _c1_needs_stack_repair(false), _c2_needs_stack_repair(false), _supers(nullptr) {
+  _sig = new GrowableArray<SigEntry>((method != nullptr) ? method->size_of_parameters() : 1);
+  _sig_cc = new GrowableArray<SigEntry>((method != nullptr) ? method->size_of_parameters() : 1);
+  _sig_cc_ro = new GrowableArray<SigEntry>((method != nullptr) ? method->size_of_parameters() : 1);
+}
 
- public:
-  AdapterSignatureIterator(Symbol* signature,
-                           fingerprint_t fingerprint,
-                           bool is_static,
-                           int total_args_passed) :
-    SignatureIterator(signature, fingerprint),
-    index(0)
-  {
-    sig_bt = (total_args_passed <= 16) ? stack_sig_bt : NEW_RESOURCE_ARRAY(BasicType, total_args_passed);
-    if (!is_static) { // Pass in receiver first
-      sig_bt[index++] = T_OBJECT;
-    }
-    do_parameters_on(this);
+// See if we can save space by sharing the same entry for VIEP and VIEP(RO),
+// or the same entry for VEP and VIEP(RO).
+CodeOffsets::Entries CompiledEntrySignature::c1_inline_ro_entry_type() const {
+  if (!has_scalarized_args()) {
+    // VEP/VIEP/VIEP(RO) all share the same entry. There's no packing.
+    return CodeOffsets::Verified_Entry;
+  }
+  if (_method->is_static()) {
+    // Static methods don't need VIEP(RO)
+    return CodeOffsets::Verified_Entry;
   }
 
-  BasicType* basic_types() {
-    return sig_bt;
+  if (has_inline_recv()) {
+    if (num_inline_args() == 1) {
+      // Share same entry for VIEP and VIEP(RO).
+      // This is quite common: we have an instance method in an InlineKlass that has
+      // no inline type args other than <this>.
+      return CodeOffsets::Verified_Inline_Entry;
+    } else {
+      assert(num_inline_args() > 1, "must be");
+      // No sharing:
+      //   VIEP(RO) -- <this> is passed as object
+      //   VEP      -- <this> is passed as fields
+      return CodeOffsets::Verified_Inline_Entry_RO;
+    }
   }
 
+  // Either a static method, or <this> is not an inline type
+  if (args_on_stack_cc() != args_on_stack_cc_ro()) {
+    // No sharing:
+    // Some arguments are passed on the stack, and we have inserted reserved entries
+    // into the VEP, but we never insert reserved entries into the VIEP(RO).
+    return CodeOffsets::Verified_Inline_Entry_RO;
+  } else {
+    // Share same entry for VEP and VIEP(RO).
+    return CodeOffsets::Verified_Entry;
+  }
+}
+
+// Returns all super methods (transitive) in classes and interfaces that are overridden by the current method.
+GrowableArray<Method*>* CompiledEntrySignature::get_supers() {
+  if (_supers != nullptr) {
+    return _supers;
+  }
+  _supers = new GrowableArray<Method*>();
+  // Skip private, static, and <init> methods
+  if (_method->is_private() || _method->is_static() || _method->is_object_constructor()) {
+    return _supers;
+  }
+  Symbol* name = _method->name();
+  Symbol* signature = _method->signature();
+  const Klass* holder = _method->method_holder()->super();
+  Symbol* holder_name = holder->name();
+  ThreadInVMfromUnknown tiv;
+  JavaThread* current = JavaThread::current();
+  HandleMark hm(current);
+  Handle loader(current, _method->method_holder()->class_loader());
+
+  // Walk up the class hierarchy and search for super methods
+  while (holder != nullptr) {
+    Method* super_method = holder->lookup_method(name, signature);
+    if (super_method == nullptr) {
+      break;
+    }
+    if (!super_method->is_static() && !super_method->is_private() &&
+        (!super_method->is_package_private() ||
+         super_method->method_holder()->is_same_class_package(loader(), holder_name))) {
+      _supers->push(super_method);
+    }
+    holder = super_method->method_holder()->super();
+  }
+  // Search interfaces for super methods
+  Array<InstanceKlass*>* interfaces = _method->method_holder()->transitive_interfaces();
+  for (int i = 0; i < interfaces->length(); ++i) {
+    Method* m = interfaces->at(i)->lookup_method(name, signature);
+    if (m != nullptr && !m->is_static() && m->is_public()) {
+      _supers->push(m);
+    }
+  }
+  return _supers;
+}
+
+// Iterate over arguments and compute scalarized and non-scalarized signatures
+void CompiledEntrySignature::compute_calling_conventions(bool init) {
+  bool has_scalarized = false;
+  if (_method != nullptr) {
+    InstanceKlass* holder = _method->method_holder();
+    int arg_num = 0;
+    if (!_method->is_static()) {
+      // We shouldn't scalarize 'this' in a value class constructor
+      if (holder->is_inline_klass() && InlineKlass::cast(holder)->can_be_passed_as_fields() && !_method->is_object_constructor() &&
+          (init || _method->is_scalarized_arg(arg_num))) {
+        _sig_cc->appendAll(InlineKlass::cast(holder)->extended_sig());
+        has_scalarized = true;
+        _has_inline_recv = true;
+        _num_inline_args++;
+      } else {
+        SigEntry::add_entry(_sig_cc, T_OBJECT, holder->name());
+      }
+      SigEntry::add_entry(_sig, T_OBJECT, holder->name());
+      SigEntry::add_entry(_sig_cc_ro, T_OBJECT, holder->name());
+      arg_num++;
+    }
+    for (SignatureStream ss(_method->signature()); !ss.at_return_type(); ss.next()) {
+      BasicType bt = ss.type();
+      if (bt == T_OBJECT) {
+        InlineKlass* vk = ss.as_inline_klass(holder);
+        if (vk != nullptr && vk->can_be_passed_as_fields() && (init || _method->is_scalarized_arg(arg_num))) {
+          // Check for a calling convention mismatch with super method(s)
+          bool scalar_super = false;
+          bool non_scalar_super = false;
+          GrowableArray<Method*>* supers = get_supers();
+          for (int i = 0; i < supers->length(); ++i) {
+            Method* super_method = supers->at(i);
+            if (super_method->is_scalarized_arg(arg_num)) {
+              scalar_super = true;
+            } else {
+              non_scalar_super = true;
+            }
+          }
 #ifdef ASSERT
-  int slots() {
-    return index;
-  }
+          // Randomly enable below code paths for stress testing
+          bool stress = init && StressCallingConvention;
+          if (stress && (os::random() & 1) == 1) {
+            non_scalar_super = true;
+            if ((os::random() & 1) == 1) {
+              scalar_super = true;
+            }
+          }
 #endif
+          if (non_scalar_super) {
+            // Found a super method with a non-scalarized argument. Fall back to the non-scalarized calling convention.
+            if (scalar_super) {
+              // Found non-scalar *and* scalar super methods. We can't handle both.
+              // Mark the scalar method as mismatch and re-compile call sites to use non-scalarized calling convention.
+              for (int i = 0; i < supers->length(); ++i) {
+                Method* super_method = supers->at(i);
+                if (super_method->is_scalarized_arg(arg_num) debug_only(|| (stress && (os::random() & 1) == 1))) {
+                  super_method->set_mismatch();
+                  MutexLocker ml(Compile_lock, Mutex::_safepoint_check_flag);
+                  JavaThread* thread = JavaThread::current();
+                  HandleMark hm(thread);
+                  methodHandle mh(thread, super_method);
+                  DeoptimizationScope deopt_scope;
+                  CodeCache::mark_for_deoptimization(&deopt_scope, mh());
+                  deopt_scope.deoptimize_marked();
+                }
+              }
+            }
+            // Fall back to non-scalarized calling convention
+            SigEntry::add_entry(_sig_cc, T_OBJECT, ss.as_symbol());
+            SigEntry::add_entry(_sig_cc_ro, T_OBJECT, ss.as_symbol());
+          } else {
+            _num_inline_args++;
+            has_scalarized = true;
+            int last = _sig_cc->length();
+            int last_ro = _sig_cc_ro->length();
+            _sig_cc->appendAll(vk->extended_sig());
+            _sig_cc_ro->appendAll(vk->extended_sig());
+            if (bt == T_OBJECT) {
+              // Nullable inline type argument, insert InlineTypeNode::IsInit field right after T_METADATA delimiter
+              _sig_cc->insert_before(last+1, SigEntry(T_BOOLEAN, -1, nullptr));
+              _sig_cc_ro->insert_before(last_ro+1, SigEntry(T_BOOLEAN, -1, nullptr));
+            }
+          }
+        } else {
+          SigEntry::add_entry(_sig_cc, T_OBJECT, ss.as_symbol());
+          SigEntry::add_entry(_sig_cc_ro, T_OBJECT, ss.as_symbol());
+        }
+        bt = T_OBJECT;
+      } else {
+        SigEntry::add_entry(_sig_cc, ss.type(), ss.as_symbol());
+        SigEntry::add_entry(_sig_cc_ro, ss.type(), ss.as_symbol());
+      }
+      SigEntry::add_entry(_sig, bt, ss.as_symbol());
+      if (bt != T_VOID) {
+        arg_num++;
+      }
+    }
+  }
 
- private:
+  // Compute the non-scalarized calling convention
+  _regs = NEW_RESOURCE_ARRAY(VMRegPair, _sig->length());
+  _args_on_stack = SharedRuntime::java_calling_convention(_sig, _regs);
+
+  // Compute the scalarized calling conventions if there are scalarized inline types in the signature
+  if (has_scalarized && !_method->is_native()) {
+    _regs_cc = NEW_RESOURCE_ARRAY(VMRegPair, _sig_cc->length());
+    _args_on_stack_cc = SharedRuntime::java_calling_convention(_sig_cc, _regs_cc);
+
+    _regs_cc_ro = NEW_RESOURCE_ARRAY(VMRegPair, _sig_cc_ro->length());
+    _args_on_stack_cc_ro = SharedRuntime::java_calling_convention(_sig_cc_ro, _regs_cc_ro);
 
-  friend class SignatureIterator;  // so do_parameters_on can call do_type
-  void do_type(BasicType type) {
-    sig_bt[index++] = type;
-    if (type == T_LONG || type == T_DOUBLE) {
-      sig_bt[index++] = T_VOID; // Longs & doubles take 2 Java slots
+    _c1_needs_stack_repair = (_args_on_stack_cc < _args_on_stack) || (_args_on_stack_cc_ro < _args_on_stack);
+    _c2_needs_stack_repair = (_args_on_stack_cc > _args_on_stack) || (_args_on_stack_cc > _args_on_stack_cc_ro);
+
+    // Upper bound on stack arguments to avoid hitting the argument limit and
+    // bailing out of compilation ("unsupported incoming calling sequence").
+    // TODO we need a reasonable limit (flag?) here
+    if (MAX2(_args_on_stack_cc, _args_on_stack_cc_ro) <= 60) {
+      return; // Success
     }
   }
-};
+
+  // No scalarized args
+  _sig_cc = _sig;
+  _regs_cc = _regs;
+  _args_on_stack_cc = _args_on_stack;
+
+  _sig_cc_ro = _sig;
+  _regs_cc_ro = _regs;
+  _args_on_stack_cc_ro = _args_on_stack;
+}
 
 AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter(const methodHandle& method) {
   // Use customized signature handler.  Need to lock around updates to
   // the _adapter_handler_table (it is not safe for concurrent readers
   // and a single writer: this could be fixed if it becomes a
@@ -2861,89 +3180,116 @@
   }
 
   ResourceMark rm;
   AdapterBlob* new_adapter = nullptr;
 
-  // Fill in the signature array, for the calling-convention call.
-  int total_args_passed = method->size_of_parameters(); // All args on stack
+  CompiledEntrySignature ces(method());
+  ces.compute_calling_conventions();
+  if (ces.has_scalarized_args()) {
+    if (!method->has_scalarized_args()) {
+      method->set_has_scalarized_args();
+    }
+    if (ces.c1_needs_stack_repair()) {
+      method->set_c1_needs_stack_repair();
+    }
+    if (ces.c2_needs_stack_repair() && !method->c2_needs_stack_repair()) {
+      method->set_c2_needs_stack_repair();
+    }
+  } else if (method->is_abstract()) {
+    return _abstract_method_handler;
+  }
 
-  AdapterSignatureIterator si(method->signature(), method->constMethod()->fingerprint(),
-                              method->is_static(), total_args_passed);
-  assert(si.slots() == total_args_passed, "");
-  BasicType* sig_bt = si.basic_types();
   {
     MutexLocker mu(AdapterHandlerLibrary_lock);
 
+    if (ces.has_scalarized_args() && method->is_abstract()) {
+      // Save a C heap allocated version of the signature for abstract methods with scalarized inline type arguments
+      address wrong_method_abstract = SharedRuntime::get_handle_wrong_method_abstract_stub();
+      entry = AdapterHandlerLibrary::new_entry(new AdapterFingerPrint(nullptr),
+                                               StubRoutines::throw_AbstractMethodError_entry(),
+                                               wrong_method_abstract, wrong_method_abstract, wrong_method_abstract,
+                                               wrong_method_abstract, wrong_method_abstract);
+      GrowableArray<SigEntry>* heap_sig = new (mtInternal) GrowableArray<SigEntry>(ces.sig_cc_ro()->length(), mtInternal);
+      heap_sig->appendAll(ces.sig_cc_ro());
+      entry->set_sig_cc(heap_sig);
+      return entry;
+    }
+
     // Lookup method signature's fingerprint
-    entry = lookup(total_args_passed, sig_bt);
+    entry = lookup(ces.sig_cc(), ces.has_inline_recv());
 
     if (entry != nullptr) {
 #ifdef ASSERT
       if (VerifyAdapterSharing) {
         AdapterBlob* comparison_blob = nullptr;
-        AdapterHandlerEntry* comparison_entry = create_adapter(comparison_blob, total_args_passed, sig_bt, false);
+        AdapterHandlerEntry* comparison_entry = create_adapter(comparison_blob, ces, false);
         assert(comparison_blob == nullptr, "no blob should be created when creating an adapter for comparison");
         assert(comparison_entry->compare_code(entry), "code must match");
         // Release the one just created and return the original
         delete comparison_entry;
       }
 #endif
       return entry;
     }
 
-    entry = create_adapter(new_adapter, total_args_passed, sig_bt, /* allocate_code_blob */ true);
+    entry = create_adapter(new_adapter, ces, /* allocate_code_blob */ true);
   }
 
   // Outside of the lock
   if (new_adapter != nullptr) {
     post_adapter_creation(new_adapter, entry);
   }
   return entry;
 }
 
 AdapterHandlerEntry* AdapterHandlerLibrary::create_adapter(AdapterBlob*& new_adapter,
-                                                           int total_args_passed,
-                                                           BasicType* sig_bt,
+                                                           CompiledEntrySignature& ces,
                                                            bool allocate_code_blob) {
 
   // StubRoutines::_final_stubs_code is initialized after this function can be called. As a result,
   // VerifyAdapterCalls and VerifyAdapterSharing can fail if we re-use code that generated prior
   // to all StubRoutines::_final_stubs_code being set. Checks refer to runtime range checks generated
   // in an I2C stub that ensure that an I2C stub is called from an interpreter frame or stubs.
   bool contains_all_checks = StubRoutines::final_stubs_code() != nullptr;
 
-  VMRegPair stack_regs[16];
-  VMRegPair* regs = (total_args_passed <= 16) ? stack_regs : NEW_RESOURCE_ARRAY(VMRegPair, total_args_passed);
-
-  // Get a description of the compiled java calling convention and the largest used (VMReg) stack slot usage
-  int comp_args_on_stack = SharedRuntime::java_calling_convention(sig_bt, regs, total_args_passed);
   BufferBlob* buf = buffer_blob(); // the temporary code buffer in CodeCache
   CodeBuffer buffer(buf);
   short buffer_locs[20];
   buffer.insts()->initialize_shared_locs((relocInfo*)buffer_locs,
                                           sizeof(buffer_locs)/sizeof(relocInfo));
 
   // Make a C heap allocated version of the fingerprint to store in the adapter
-  AdapterFingerPrint* fingerprint = new AdapterFingerPrint(total_args_passed, sig_bt);
+  AdapterFingerPrint* fingerprint = new AdapterFingerPrint(ces.sig_cc(), ces.has_inline_recv());
   MacroAssembler _masm(&buffer);
   AdapterHandlerEntry* entry = SharedRuntime::generate_i2c2i_adapters(&_masm,
-                                                total_args_passed,
-                                                comp_args_on_stack,
-                                                sig_bt,
-                                                regs,
-                                                fingerprint);
+                                                ces.args_on_stack(),
+                                                ces.sig(),
+                                                ces.regs(),
+                                                ces.sig_cc(),
+                                                ces.regs_cc(),
+                                                ces.sig_cc_ro(),
+                                                ces.regs_cc_ro(),
+                                                fingerprint,
+                                                new_adapter,
+                                                allocate_code_blob);
+
+  if (ces.has_scalarized_args()) {
+    // Save a C heap allocated version of the scalarized signature and store it in the adapter
+    GrowableArray<SigEntry>* heap_sig = new (mtInternal) GrowableArray<SigEntry>(ces.sig_cc()->length(), mtInternal);
+    heap_sig->appendAll(ces.sig_cc());
+    entry->set_sig_cc(heap_sig);
+  }
 
 #ifdef ASSERT
   if (VerifyAdapterSharing) {
     entry->save_code(buf->code_begin(), buffer.insts_size());
     if (!allocate_code_blob) {
       return entry;
     }
   }
 #endif
 
-  new_adapter = AdapterBlob::create(&buffer);
   NOT_PRODUCT(int insts_size = buffer.insts_size());
   if (new_adapter == nullptr) {
     // CodeCache is full, disable compilation
     // Ought to log this but compile log is only per compile thread
     // and we're some non descript Java thread.
@@ -2981,11 +3327,14 @@
 
 address AdapterHandlerEntry::base_address() {
   address base = _i2c_entry;
   if (base == nullptr)  base = _c2i_entry;
   assert(base <= _c2i_entry || _c2i_entry == nullptr, "");
+  assert(base <= _c2i_inline_entry || _c2i_inline_entry == nullptr, "");
+  assert(base <= _c2i_inline_ro_entry || _c2i_inline_ro_entry == nullptr, "");
   assert(base <= _c2i_unverified_entry || _c2i_unverified_entry == nullptr, "");
+  assert(base <= _c2i_unverified_inline_entry || _c2i_unverified_inline_entry == nullptr, "");
   assert(base <= _c2i_no_clinit_check_entry || _c2i_no_clinit_check_entry == nullptr, "");
   return base;
 }
 
 void AdapterHandlerEntry::relocate(address new_base) {
@@ -2994,20 +3343,29 @@
   ptrdiff_t delta = new_base - old_base;
   if (_i2c_entry != nullptr)
     _i2c_entry += delta;
   if (_c2i_entry != nullptr)
     _c2i_entry += delta;
+  if (_c2i_inline_entry != nullptr)
+    _c2i_inline_entry += delta;
+  if (_c2i_inline_ro_entry != nullptr)
+    _c2i_inline_ro_entry += delta;
   if (_c2i_unverified_entry != nullptr)
     _c2i_unverified_entry += delta;
+  if (_c2i_unverified_inline_entry != nullptr)
+    _c2i_unverified_inline_entry += delta;
   if (_c2i_no_clinit_check_entry != nullptr)
     _c2i_no_clinit_check_entry += delta;
   assert(base_address() == new_base, "");
 }
 
 
 AdapterHandlerEntry::~AdapterHandlerEntry() {
   delete _fingerprint;
+  if (_sig_cc != nullptr) {
+    delete _sig_cc;
+  }
 #ifdef ASSERT
   FREE_C_HEAP_ARRAY(unsigned char, _saved_code);
 #endif
 }
 
@@ -3090,18 +3448,28 @@
       MacroAssembler _masm(&buffer);
 
       // Fill in the signature array, for the calling-convention call.
       const int total_args_passed = method->size_of_parameters();
 
+      BasicType stack_sig_bt[16];
       VMRegPair stack_regs[16];
+      BasicType* sig_bt = (total_args_passed <= 16) ? stack_sig_bt : NEW_RESOURCE_ARRAY(BasicType, total_args_passed);
       VMRegPair* regs = (total_args_passed <= 16) ? stack_regs : NEW_RESOURCE_ARRAY(VMRegPair, total_args_passed);
 
-      AdapterSignatureIterator si(method->signature(), method->constMethod()->fingerprint(),
-                              method->is_static(), total_args_passed);
-      BasicType* sig_bt = si.basic_types();
-      assert(si.slots() == total_args_passed, "");
-      BasicType ret_type = si.return_type();
+      int i = 0;
+      if (!method->is_static()) {  // Pass in receiver first
+        sig_bt[i++] = T_OBJECT;
+      }
+      SignatureStream ss(method->signature());
+      for (; !ss.at_return_type(); ss.next()) {
+        sig_bt[i++] = ss.type();  // Collect remaining bits of signature
+        if (ss.type() == T_LONG || ss.type() == T_DOUBLE) {
+          sig_bt[i++] = T_VOID;   // Longs & doubles take 2 Java slots
+        }
+      }
+      assert(i == total_args_passed, "");
+      BasicType ret_type = ss.type();
 
       // Now get the compiled-Java arguments layout.
       SharedRuntime::java_calling_convention(sig_bt, regs, total_args_passed);
 
       // Generate the compiled-to-native wrapper code
@@ -3342,12 +3710,21 @@
     st->print(" i2c: " INTPTR_FORMAT, p2i(get_i2c_entry()));
   }
   if (get_c2i_entry() != nullptr) {
     st->print(" c2i: " INTPTR_FORMAT, p2i(get_c2i_entry()));
   }
+  if (get_c2i_entry() != nullptr) {
+    st->print(" c2iVE: " INTPTR_FORMAT, p2i(get_c2i_inline_entry()));
+  }
+  if (get_c2i_entry() != nullptr) {
+    st->print(" c2iVROE: " INTPTR_FORMAT, p2i(get_c2i_inline_ro_entry()));
+  }
   if (get_c2i_unverified_entry() != nullptr) {
-    st->print(" c2iUV: " INTPTR_FORMAT, p2i(get_c2i_unverified_entry()));
+    st->print(" c2iUE: " INTPTR_FORMAT, p2i(get_c2i_unverified_entry()));
+  }
+  if (get_c2i_unverified_entry() != nullptr) {
+    st->print(" c2iUVE: " INTPTR_FORMAT, p2i(get_c2i_unverified_inline_entry()));
   }
   if (get_c2i_no_clinit_check_entry() != nullptr) {
     st->print(" c2iNCI: " INTPTR_FORMAT, p2i(get_c2i_no_clinit_check_entry()));
   }
   st->cr();
@@ -3432,5 +3809,201 @@
   if (new_obj == nullptr) return;
 
   BarrierSet *bs = BarrierSet::barrier_set();
   bs->on_slowpath_allocation_exit(current, new_obj);
 }
+
+// We are at a compiled code to interpreter call. We need backing
+// buffers for all inline type arguments. Allocate an object array to
+// hold them (convenient because once we're done with it we don't have
+// to worry about freeing it).
+oop SharedRuntime::allocate_inline_types_impl(JavaThread* current, methodHandle callee, bool allocate_receiver, TRAPS) {
+  assert(InlineTypePassFieldsAsArgs, "no reason to call this");
+  ResourceMark rm;
+
+  int nb_slots = 0;
+  InstanceKlass* holder = callee->method_holder();
+  allocate_receiver &= !callee->is_static() && holder->is_inline_klass() && callee->is_scalarized_arg(0);
+  if (allocate_receiver) {
+    nb_slots++;
+  }
+  int arg_num = callee->is_static() ? 0 : 1;
+  for (SignatureStream ss(callee->signature()); !ss.at_return_type(); ss.next()) {
+    BasicType bt = ss.type();
+    if (bt == T_OBJECT && callee->is_scalarized_arg(arg_num)) {
+      nb_slots++;
+    }
+    if (bt != T_VOID) {
+      arg_num++;
+    }
+  }
+  objArrayOop array_oop = oopFactory::new_objectArray(nb_slots, CHECK_NULL);
+  objArrayHandle array(THREAD, array_oop);
+  arg_num = callee->is_static() ? 0 : 1;
+  int i = 0;
+  if (allocate_receiver) {
+    InlineKlass* vk = InlineKlass::cast(holder);
+    oop res = vk->allocate_instance(CHECK_NULL);
+    array->obj_at_put(i++, res);
+  }
+  for (SignatureStream ss(callee->signature()); !ss.at_return_type(); ss.next()) {
+    BasicType bt = ss.type();
+    if (bt == T_OBJECT && callee->is_scalarized_arg(arg_num)) {
+      InlineKlass* vk = ss.as_inline_klass(holder);
+      assert(vk != nullptr, "Unexpected klass");
+      oop res = vk->allocate_instance(CHECK_NULL);
+      array->obj_at_put(i++, res);
+    }
+    if (bt != T_VOID) {
+      arg_num++;
+    }
+  }
+  return array();
+}
+
+JRT_ENTRY(void, SharedRuntime::allocate_inline_types(JavaThread* current, Method* callee_method, bool allocate_receiver))
+  methodHandle callee(current, callee_method);
+  oop array = SharedRuntime::allocate_inline_types_impl(current, callee, allocate_receiver, CHECK);
+  current->set_vm_result(array);
+  current->set_vm_result_2(callee()); // TODO: required to keep callee live?
+JRT_END
+
+// We're returning from an interpreted method: load each field into a
+// register following the calling convention
+JRT_LEAF(void, SharedRuntime::load_inline_type_fields_in_regs(JavaThread* current, oopDesc* res))
+{
+  assert(res->klass()->is_inline_klass(), "only inline types here");
+  ResourceMark rm;
+  RegisterMap reg_map(current,
+                      RegisterMap::UpdateMap::include,
+                      RegisterMap::ProcessFrames::include,
+                      RegisterMap::WalkContinuation::skip);
+  frame stubFrame = current->last_frame();
+  frame callerFrame = stubFrame.sender(&reg_map);
+  assert(callerFrame.is_interpreted_frame(), "should be coming from interpreter");
+
+  InlineKlass* vk = InlineKlass::cast(res->klass());
+
+  const Array<SigEntry>* sig_vk = vk->extended_sig();
+  const Array<VMRegPair>* regs = vk->return_regs();
+
+  if (regs == nullptr) {
+    // The fields of the inline klass don't fit in registers, bail out
+    return;
+  }
+
+  int j = 1;
+  for (int i = 0; i < sig_vk->length(); i++) {
+    BasicType bt = sig_vk->at(i)._bt;
+    if (bt == T_METADATA) {
+      continue;
+    }
+    if (bt == T_VOID) {
+      if (sig_vk->at(i-1)._bt == T_LONG ||
+          sig_vk->at(i-1)._bt == T_DOUBLE) {
+        j++;
+      }
+      continue;
+    }
+    int off = sig_vk->at(i)._offset;
+    assert(off > 0, "offset in object should be positive");
+    VMRegPair pair = regs->at(j);
+    address loc = reg_map.location(pair.first(), nullptr);
+    switch(bt) {
+    case T_BOOLEAN:
+      *(jboolean*)loc = res->bool_field(off);
+      break;
+    case T_CHAR:
+      *(jchar*)loc = res->char_field(off);
+      break;
+    case T_BYTE:
+      *(jbyte*)loc = res->byte_field(off);
+      break;
+    case T_SHORT:
+      *(jshort*)loc = res->short_field(off);
+      break;
+    case T_INT: {
+      *(jint*)loc = res->int_field(off);
+      break;
+    }
+    case T_LONG:
+#ifdef _LP64
+      *(intptr_t*)loc = res->long_field(off);
+#else
+      Unimplemented();
+#endif
+      break;
+    case T_OBJECT:
+    case T_ARRAY: {
+      *(oop*)loc = res->obj_field(off);
+      break;
+    }
+    case T_FLOAT:
+      *(jfloat*)loc = res->float_field(off);
+      break;
+    case T_DOUBLE:
+      *(jdouble*)loc = res->double_field(off);
+      break;
+    default:
+      ShouldNotReachHere();
+    }
+    j++;
+  }
+  assert(j == regs->length(), "missed a field?");
+
+#ifdef ASSERT
+  VMRegPair pair = regs->at(0);
+  address loc = reg_map.location(pair.first(), nullptr);
+  assert(*(oopDesc**)loc == res, "overwritten object");
+#endif
+
+  current->set_vm_result(res);
+}
+JRT_END
+
+// We've returned to an interpreted method, the interpreter needs a
+// reference to an inline type instance. Allocate it and initialize it
+// from field's values in registers.
+JRT_BLOCK_ENTRY(void, SharedRuntime::store_inline_type_fields_to_buf(JavaThread* current, intptr_t res))
+{
+  ResourceMark rm;
+  RegisterMap reg_map(current,
+                      RegisterMap::UpdateMap::include,
+                      RegisterMap::ProcessFrames::include,
+                      RegisterMap::WalkContinuation::skip);
+  frame stubFrame = current->last_frame();
+  frame callerFrame = stubFrame.sender(&reg_map);
+
+#ifdef ASSERT
+  InlineKlass* verif_vk = InlineKlass::returned_inline_klass(reg_map);
+#endif
+
+  if (!is_set_nth_bit(res, 0)) {
+    // We're not returning with inline type fields in registers (the
+    // calling convention didn't allow it for this inline klass)
+    assert(!Metaspace::contains((void*)res), "should be oop or pointer in buffer area");
+    current->set_vm_result((oopDesc*)res);
+    assert(verif_vk == nullptr, "broken calling convention");
+    return;
+  }
+
+  clear_nth_bit(res, 0);
+  InlineKlass* vk = (InlineKlass*)res;
+  assert(verif_vk == vk, "broken calling convention");
+  assert(Metaspace::contains((void*)res), "should be klass");
+
+  // Allocate handles for every oop field so they are safe in case of
+  // a safepoint when allocating
+  GrowableArray<Handle> handles;
+  vk->save_oop_fields(reg_map, handles);
+
+  // It's unsafe to safepoint until we are here
+  JRT_BLOCK;
+  {
+    JavaThread* THREAD = current;
+    oop vt = vk->realloc_result(reg_map, handles, CHECK);
+    current->set_vm_result(vt);
+  }
+  JRT_BLOCK_END;
+}
+JRT_END
+
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
@@ -23,20 +23,23 @@
  */
 
 #ifndef SHARE_RUNTIME_SHAREDRUNTIME_HPP
 #define SHARE_RUNTIME_SHAREDRUNTIME_HPP
 
+#include "asm/codeBuffer.hpp"
 #include "code/codeBlob.hpp"
 #include "code/vmreg.hpp"
 #include "interpreter/linkResolver.hpp"
 #include "memory/allStatic.hpp"
 #include "memory/resourceArea.hpp"
+#include "runtime/signature.hpp"
 #include "utilities/macros.hpp"
 
 class AdapterHandlerEntry;
 class AdapterFingerPrint;
 class vframeStream;
+class SigEntry;
 
 // Runtime is the base class for various runtime interfaces
 // (InterpreterRuntime, CompilerRuntime, etc.). It provides
 // shared functionality such as exception forwarding (C++ to
 // Java exceptions), locking/unlocking mechanisms, statistical
@@ -45,23 +48,22 @@
 class SharedRuntime: AllStatic {
   friend class VMStructs;
 
  private:
   static bool resolve_sub_helper_internal(methodHandle callee_method, const frame& caller_frame,
-                                          CompiledMethod* caller_nm, bool is_virtual, bool is_optimized,
+                                          CompiledMethod* caller_nm, bool is_virtual, bool is_optimized, bool& caller_is_c1,
                                           Handle receiver, CallInfo& call_info, Bytecodes::Code invoke_code, TRAPS);
-  static methodHandle resolve_sub_helper(bool is_virtual, bool is_optimized, TRAPS);
+  static methodHandle resolve_sub_helper(bool is_virtual, bool is_optimized, bool& caller_is_c1, TRAPS);
 
   // Shared stub locations
 
   static RuntimeStub*        _wrong_method_blob;
   static RuntimeStub*        _wrong_method_abstract_blob;
   static RuntimeStub*        _ic_miss_blob;
   static RuntimeStub*        _resolve_opt_virtual_call_blob;
   static RuntimeStub*        _resolve_virtual_call_blob;
   static RuntimeStub*        _resolve_static_call_blob;
-  static address             _resolve_static_call_entry;
 
   static DeoptimizationBlob* _deopt_blob;
 
   static SafepointBlob*      _polling_page_vectors_safepoint_handler_blob;
   static SafepointBlob*      _polling_page_safepoint_handler_blob;
@@ -80,11 +82,10 @@
 
  private:
   enum { POLL_AT_RETURN,  POLL_AT_LOOP, POLL_AT_VECTOR_LOOP };
   static SafepointBlob* generate_handler_blob(address call_ptr, int poll_type);
   static RuntimeStub*   generate_resolve_blob(address destination, const char* name);
-
  public:
   static void generate_stubs(void);
 
   // max bytes for each dtrace string parameter
   enum { max_dtrace_string_size = 256 };
@@ -323,37 +324,50 @@
   //
   static char* generate_class_cast_message(Klass* caster_klass, Klass* target_klass, Symbol* target_klass_name = nullptr);
 
   // Resolves a call site- may patch in the destination of the call into the
   // compiled code.
-  static methodHandle resolve_helper(bool is_virtual, bool is_optimized, TRAPS);
+  static methodHandle resolve_helper(bool is_virtual, bool is_optimized, bool& caller_is_c1, TRAPS);
 
  private:
   // deopt blob
   static void generate_deopt_blob(void);
 
   static bool handle_ic_miss_helper_internal(Handle receiver, CompiledMethod* caller_nm, const frame& caller_frame,
                                              methodHandle callee_method, Bytecodes::Code bc, CallInfo& call_info,
-                                             bool& needs_ic_stub_refill, TRAPS);
+                                             bool& needs_ic_stub_refill, bool& is_optimized, bool caller_is_c1, TRAPS);
 
  public:
   static DeoptimizationBlob* deopt_blob(void)      { return _deopt_blob; }
 
   // Resets a call-site in compiled code so it will get resolved again.
-  static methodHandle reresolve_call_site(TRAPS);
+  static methodHandle reresolve_call_site(bool& is_static_call, bool& is_optimized, bool& caller_is_c1, TRAPS);
 
   // In the code prolog, if the klass comparison fails, the inline cache
   // misses and the call site is patched to megamorphic
-  static methodHandle handle_ic_miss_helper(TRAPS);
+  static methodHandle handle_ic_miss_helper(bool& is_optimized, bool& caller_is_c1, TRAPS);
 
   // Find the method that called us.
-  static methodHandle find_callee_method(TRAPS);
+  static methodHandle find_callee_method(bool is_optimized, bool& caller_is_c1, TRAPS);
 
   static void monitor_enter_helper(oopDesc* obj, BasicLock* lock, JavaThread* thread);
 
   static void monitor_exit_helper(oopDesc* obj, BasicLock* lock, JavaThread* current);
 
+  static address entry_for_handle_wrong_method(methodHandle callee_method, bool is_static_call, bool is_optimized, bool caller_is_c1) {
+    assert(callee_method->verified_code_entry() != nullptr, "Jump to zero!");
+    assert(callee_method->verified_inline_code_entry() != nullptr, "Jump to zero!");
+    assert(callee_method->verified_inline_ro_code_entry() != nullptr, "Jump to zero!");
+    if (caller_is_c1) {
+      return callee_method->verified_inline_code_entry();
+    } else if (is_static_call || is_optimized) {
+      return callee_method->verified_code_entry();
+    } else {
+      return callee_method->verified_inline_ro_code_entry();
+    }
+  }
+
  private:
   static Handle find_callee_info(Bytecodes::Code& bc, CallInfo& callinfo, TRAPS);
   static Handle find_callee_info_helper(vframeStream& vfst, Bytecodes::Code& bc, CallInfo& callinfo, TRAPS);
 
   static Method* extract_attached_method(vframeStream& vfst);
@@ -372,10 +386,18 @@
   // based off of the window top.  SharedInfo::stack0 refers to the first usable
   // slot in the bottom of the frame. SharedInfo::stack0+1 refers to the memory word
   // 4-bytes higher.
   // return value is the maximum number of VMReg stack slots the convention will use.
   static int java_calling_convention(const BasicType* sig_bt, VMRegPair* regs, int total_args_passed);
+  static int java_calling_convention(const GrowableArray<SigEntry>* sig, VMRegPair* regs) {
+    BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, sig->length());
+    int total_args_passed = SigEntry::fill_sig_bt(sig, sig_bt);
+    return java_calling_convention(sig_bt, regs, total_args_passed);
+  }
+  static int java_return_convention(const BasicType* sig_bt, VMRegPair* regs, int total_args_passed);
+  static const uint java_return_convention_max_int;
+  static const uint java_return_convention_max_float;
 
   static void check_member_name_argument_is_last_argument(const methodHandle& method,
                                                           const BasicType* sig_bt,
                                                           const VMRegPair* regs) NOT_DEBUG_RETURN;
 
@@ -418,21 +440,25 @@
   // that the interpreter before it does any call dispatch will record the current
   // stack pointer in the interpreter frame. On return it will restore the stack
   // pointer as needed. This means the i2c adapter code doesn't need any special
   // handshaking path with compiled code to keep the stack walking correct.
 
-  static AdapterHandlerEntry* generate_i2c2i_adapters(MacroAssembler *_masm,
-                                                      int total_args_passed,
-                                                      int max_arg,
-                                                      const BasicType *sig_bt,
-                                                      const VMRegPair *regs,
-                                                      AdapterFingerPrint* fingerprint);
+  static AdapterHandlerEntry* generate_i2c2i_adapters(MacroAssembler *masm,
+                                                      int comp_args_on_stack,
+                                                      const GrowableArray<SigEntry>* sig,
+                                                      const VMRegPair* regs,
+                                                      const GrowableArray<SigEntry>* sig_cc,
+                                                      const VMRegPair* regs_cc,
+                                                      const GrowableArray<SigEntry>* sig_cc_ro,
+                                                      const VMRegPair* regs_cc_ro,
+                                                      AdapterFingerPrint* fingerprint,
+                                                      AdapterBlob*& new_adapter,
+                                                      bool allocate_code_blob);
 
   static void gen_i2c_adapter(MacroAssembler *_masm,
-                              int total_args_passed,
                               int comp_args_on_stack,
-                              const BasicType *sig_bt,
+                              const GrowableArray<SigEntry>* sig,
                               const VMRegPair *regs);
 
   // OSR support
 
   // OSR_migration_begin will extract the jvm state from an interpreter
@@ -501,23 +527,29 @@
   // Resolving of calls
   static address resolve_static_call_C     (JavaThread* current);
   static address resolve_virtual_call_C    (JavaThread* current);
   static address resolve_opt_virtual_call_C(JavaThread* current);
 
+  static void load_inline_type_fields_in_regs(JavaThread* current, oopDesc* res);
+  static void store_inline_type_fields_to_buf(JavaThread* current, intptr_t res);
+
   // arraycopy, the non-leaf version.  (See StubRoutines for all the leaf calls.)
   static void slow_arraycopy_C(oopDesc* src,  jint src_pos,
                                oopDesc* dest, jint dest_pos,
                                jint length, JavaThread* thread);
 
   // handle ic miss with caller being compiled code
   // wrong method handling (inline cache misses)
   static address handle_wrong_method(JavaThread* current);
   static address handle_wrong_method_abstract(JavaThread* current);
   static address handle_wrong_method_ic_miss(JavaThread* current);
+  static void allocate_inline_types(JavaThread* current, Method* callee, bool allocate_receiver);
+  static oop allocate_inline_types_impl(JavaThread* current, methodHandle callee, bool allocate_receiver, TRAPS);
 
   static address handle_unsafe_access(JavaThread* thread, address next_pc);
 
+  static BufferedInlineTypeBlob* generate_buffered_inline_type_adapter(const InlineKlass* vk);
 #ifndef PRODUCT
 
   // Collect and print inline cache miss statistics
  private:
   enum { maxICmiss_count = 100 };
@@ -620,44 +652,62 @@
 
  private:
   AdapterFingerPrint* _fingerprint;
   address _i2c_entry;
   address _c2i_entry;
+  address _c2i_inline_entry;
+  address _c2i_inline_ro_entry;
   address _c2i_unverified_entry;
+  address _c2i_unverified_inline_entry;
   address _c2i_no_clinit_check_entry;
 
+  // Support for scalarized inline type calling convention
+  const GrowableArray<SigEntry>* _sig_cc;
+
 #ifdef ASSERT
   // Captures code and signature used to generate this adapter when
   // verifying adapter equivalence.
   unsigned char* _saved_code;
   int            _saved_code_length;
 #endif
 
   AdapterHandlerEntry(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry,
-                      address c2i_unverified_entry,
+                      address c2i_inline_entry, address c2i_inline_ro_entry,
+                      address c2i_unverified_entry, address c2i_unverified_inline_entry,
                       address c2i_no_clinit_check_entry) :
     _fingerprint(fingerprint),
     _i2c_entry(i2c_entry),
     _c2i_entry(c2i_entry),
+    _c2i_inline_entry(c2i_inline_entry),
+    _c2i_inline_ro_entry(c2i_inline_ro_entry),
     _c2i_unverified_entry(c2i_unverified_entry),
-    _c2i_no_clinit_check_entry(c2i_no_clinit_check_entry)
+    _c2i_unverified_inline_entry(c2i_unverified_inline_entry),
+    _c2i_no_clinit_check_entry(c2i_no_clinit_check_entry),
+    _sig_cc(nullptr)
 #ifdef ASSERT
     , _saved_code_length(0)
 #endif
   { }
 
   ~AdapterHandlerEntry();
 
  public:
-  address get_i2c_entry()                  const { return _i2c_entry; }
-  address get_c2i_entry()                  const { return _c2i_entry; }
-  address get_c2i_unverified_entry()       const { return _c2i_unverified_entry; }
-  address get_c2i_no_clinit_check_entry()  const { return _c2i_no_clinit_check_entry; }
+  address get_i2c_entry()                   const { return _i2c_entry; }
+  address get_c2i_entry()                   const { return _c2i_entry; }
+  address get_c2i_inline_entry()            const { return _c2i_inline_entry; }
+  address get_c2i_inline_ro_entry()         const { return _c2i_inline_ro_entry; }
+  address get_c2i_unverified_entry()        const { return _c2i_unverified_entry; }
+  address get_c2i_unverified_inline_entry() const { return _c2i_unverified_inline_entry; }
+  address get_c2i_no_clinit_check_entry()   const { return _c2i_no_clinit_check_entry; }
 
   address base_address();
   void relocate(address new_base);
 
+  // Support for scalarized inline type calling convention
+  void set_sig_cc(const GrowableArray<SigEntry>* sig)  { _sig_cc = sig; }
+  const GrowableArray<SigEntry>* get_sig_cc()    const { return _sig_cc; }
+
   AdapterFingerPrint* fingerprint() const { return _fingerprint; }
 
 #ifdef ASSERT
   // Used to verify that code generated for shared adapters is equivalent
   void save_code   (unsigned char* code, int length);
@@ -666,10 +716,12 @@
 
   //virtual void print_on(outputStream* st) const;  DO NOT USE
   void print_adapter_on(outputStream* st) const;
 };
 
+class CompiledEntrySignature;
+
 class AdapterHandlerLibrary: public AllStatic {
   friend class SharedRuntime;
  private:
   static BufferBlob* _buffer; // the temporary code buffer in CodeCache
   static AdapterHandlerEntry* _abstract_method_handler;
@@ -680,21 +732,18 @@
   static AdapterHandlerEntry* _obj_obj_arg_handler;
 
   static BufferBlob* buffer_blob();
   static void initialize();
   static AdapterHandlerEntry* create_adapter(AdapterBlob*& new_adapter,
-                                             int total_args_passed,
-                                             BasicType* sig_bt,
+                                             CompiledEntrySignature& ces,
                                              bool allocate_code_blob);
   static AdapterHandlerEntry* get_simple_adapter(const methodHandle& method);
  public:
 
   static AdapterHandlerEntry* new_entry(AdapterFingerPrint* fingerprint,
-                                        address i2c_entry,
-                                        address c2i_entry,
-                                        address c2i_unverified_entry,
-                                        address c2i_no_clinit_check_entry = nullptr);
+                                        address i2c_entry, address c2i_entry, address c2i_inline_entry, address c2i_inline_ro_entry,
+                                        address c2i_unverified_entry, address c2i_unverified_inline_entry, address c2i_no_clinit_check_entry = nullptr);
   static void create_native_wrapper(const methodHandle& method);
   static AdapterHandlerEntry* get_adapter(const methodHandle& method);
 
   static void print_handler(const CodeBlob* b) { print_handler_on(tty, b); }
   static void print_handler_on(outputStream* st, const CodeBlob* b);
@@ -703,6 +752,65 @@
   static void print_statistics();
 #endif // PRODUCT
 
 };
 
+// Utility class for computing the calling convention of the 3 types
+// of compiled method entries:
+//     Method::_from_compiled_entry               - sig_cc
+//     Method::_from_compiled_inline_ro_entry     - sig_cc_ro
+//     Method::_from_compiled_inline_entry        - sig
+class CompiledEntrySignature : public StackObj {
+  Method* _method;
+  int  _num_inline_args;
+  bool _has_inline_recv;
+  GrowableArray<SigEntry>* _sig;
+  GrowableArray<SigEntry>* _sig_cc;
+  GrowableArray<SigEntry>* _sig_cc_ro;
+  VMRegPair* _regs;
+  VMRegPair* _regs_cc;
+  VMRegPair* _regs_cc_ro;
+
+  int _args_on_stack;
+  int _args_on_stack_cc;
+  int _args_on_stack_cc_ro;
+
+  bool _c1_needs_stack_repair;
+  bool _c2_needs_stack_repair;
+
+  GrowableArray<Method*>* _supers;
+
+public:
+  Method* method()                     const { return _method; }
+
+  // Used by Method::_from_compiled_inline_entry
+  GrowableArray<SigEntry>* sig()       const { return _sig; }
+
+  // Used by Method::_from_compiled_entry
+  GrowableArray<SigEntry>* sig_cc()    const { return _sig_cc; }
+
+  // Used by Method::_from_compiled_inline_ro_entry
+  GrowableArray<SigEntry>* sig_cc_ro() const { return _sig_cc_ro; }
+
+  VMRegPair* regs()                    const { return _regs; }
+  VMRegPair* regs_cc()                 const { return _regs_cc; }
+  VMRegPair* regs_cc_ro()              const { return _regs_cc_ro; }
+
+  int args_on_stack()                  const { return _args_on_stack; }
+  int args_on_stack_cc()               const { return _args_on_stack_cc; }
+  int args_on_stack_cc_ro()            const { return _args_on_stack_cc_ro; }
+
+  int  num_inline_args()               const { return _num_inline_args; }
+  bool has_inline_recv()               const { return _has_inline_recv; }
+
+  bool has_scalarized_args()           const { return _sig != _sig_cc; }
+  bool c1_needs_stack_repair()         const { return _c1_needs_stack_repair; }
+  bool c2_needs_stack_repair()         const { return _c2_needs_stack_repair; }
+  CodeOffsets::Entries c1_inline_ro_entry_type() const;
+
+  GrowableArray<Method*>* get_supers();
+
+  CompiledEntrySignature(Method* method = nullptr);
+  void compute_calling_conventions(bool init = true);
+};
+
 #endif // SHARE_RUNTIME_SHAREDRUNTIME_HPP
diff a/src/hotspot/share/runtime/signature.cpp b/src/hotspot/share/runtime/signature.cpp
--- a/src/hotspot/share/runtime/signature.cpp
+++ b/src/hotspot/share/runtime/signature.cpp
@@ -33,12 +33,14 @@
 #include "oops/instanceKlass.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/symbol.hpp"
 #include "oops/typeArrayKlass.hpp"
+#include "oops/inlineKlass.inline.hpp"
 #include "runtime/fieldDescriptor.inline.hpp"
 #include "runtime/handles.inline.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
 #include "runtime/safepointVerifiers.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/signature.hpp"
 #include "runtime/sharedRuntime.hpp"
 
@@ -47,11 +49,11 @@
 // Signature syntax:
 //
 // Signature  = "(" {Parameter} ")" ReturnType.
 // Parameter  = FieldType.
 // ReturnType = FieldType | "V".
-// FieldType  = "B" | "C" | "D" | "F" | "I" | "J" | "S" | "Z" | "L" ClassName ";" | "[" FieldType.
+// FieldType  = "B" | "C" | "D" | "F" | "I" | "J" | "S" | "Z" | "L" ClassName ";" | "Q" ValueClassName ";" | "[" FieldType.
 // ClassName  = string.
 
 // The ClassName string can be any JVM-style UTF8 string except:
 //  - an empty string (the empty string is never a name of any kind)
 //  - a string which begins or ends with slash '/' (the package separator)
@@ -499,10 +501,25 @@
   }
   _previous_name = name;
   return name;
 }
 
+InlineKlass* SignatureStream::as_inline_klass(InstanceKlass* holder) {
+  ThreadInVMfromUnknown tiv;
+  JavaThread* THREAD = JavaThread::current();
+  HandleMark hm(THREAD);
+  Handle class_loader(THREAD, holder->class_loader());
+  Handle protection_domain(THREAD, holder->protection_domain());
+  Klass* k = as_klass(class_loader, protection_domain, SignatureStream::CachedOrNull, THREAD);
+  assert(!HAS_PENDING_EXCEPTION, "Should never throw");
+  if (k != nullptr && k->is_inline_klass()) {
+    return InlineKlass::cast(k);
+  } else {
+    return nullptr;
+  }
+}
+
 Klass* SignatureStream::as_klass(Handle class_loader, Handle protection_domain,
                                  FailureMode failure_mode, TRAPS) {
   if (!is_reference()) {
     return nullptr;
   }
@@ -578,11 +595,10 @@
   _class_loader = Handle(current, _load_origin->class_loader());
   _protection_domain = Handle(current, _load_origin->protection_domain());
 }
 
 #ifdef ASSERT
-
 extern bool signature_constants_sane(); // called from basic_types_init()
 
 bool signature_constants_sane() {
   // for the lookup table, test every 8-bit code point, and then some:
   for (int i = -256; i <= 256; i++) {
@@ -597,11 +613,11 @@
     assert(btc == btcode, "misconfigured table: %d => %d not %d", i, btc, btcode);
   }
   return true;
 }
 
-bool SignatureVerifier::is_valid_method_signature(Symbol* sig) {
+bool SignatureVerifier::is_valid_method_signature(const Symbol* sig) {
   const char* method_sig = (const char*)sig->bytes();
   ssize_t len = sig->utf8_length();
   ssize_t index = 0;
   if (method_sig != nullptr && len > 1 && method_sig[index] == JVM_SIGNATURE_FUNC) {
     ++index;
@@ -620,11 +636,11 @@
     }
   }
   return false;
 }
 
-bool SignatureVerifier::is_valid_type_signature(Symbol* sig) {
+bool SignatureVerifier::is_valid_type_signature(const Symbol* sig) {
   const char* type_sig = (const char*)sig->bytes();
   ssize_t len = sig->utf8_length();
   return (type_sig != nullptr && len >= 1 &&
           (is_valid_type(type_sig, len) == len));
 }
@@ -667,5 +683,58 @@
   }
   return -1;
 }
 
 #endif // ASSERT
+
+// Adds an argument to the signature
+void SigEntry::add_entry(GrowableArray<SigEntry>* sig, BasicType bt, Symbol* symbol, int offset) {
+  sig->append(SigEntry(bt, offset, symbol));
+  if (bt == T_LONG || bt == T_DOUBLE) {
+    sig->append(SigEntry(T_VOID, offset, symbol)); // Longs and doubles take two stack slots
+  }
+}
+
+// Returns true if the argument at index 'i' is not an inline type delimiter
+bool SigEntry::skip_value_delimiters(const GrowableArray<SigEntry>* sig, int i) {
+  return (sig->at(i)._bt != T_METADATA &&
+          (sig->at(i)._bt != T_VOID || sig->at(i-1)._bt == T_LONG || sig->at(i-1)._bt == T_DOUBLE));
+}
+
+// Fill basic type array from signature array
+int SigEntry::fill_sig_bt(const GrowableArray<SigEntry>* sig, BasicType* sig_bt) {
+  int count = 0;
+  for (int i = 0; i < sig->length(); i++) {
+    if (skip_value_delimiters(sig, i)) {
+      sig_bt[count++] = sig->at(i)._bt;
+    }
+  }
+  return count;
+}
+
+// Create a temporary symbol from the signature array
+TempNewSymbol SigEntry::create_symbol(const GrowableArray<SigEntry>* sig) {
+  ResourceMark rm;
+  int length = sig->length();
+  char* sig_str = NEW_RESOURCE_ARRAY(char, 2*length + 3);
+  int idx = 0;
+  sig_str[idx++] = '(';
+  for (int i = 0; i < length; i++) {
+    BasicType bt = sig->at(i)._bt;
+    if (bt == T_METADATA || bt == T_VOID) {
+      // Ignore
+    } else {
+      if (bt == T_ARRAY) {
+        bt = T_OBJECT; // We don't know the element type, treat as Object
+      }
+      sig_str[idx++] = type2char(bt);
+      if (bt == T_OBJECT) {
+        sig_str[idx++] = ';';
+      }
+    }
+  }
+  sig_str[idx++] = ')';
+  // Add a dummy return type. It won't be used but SignatureStream needs it.
+  sig_str[idx++] = 'V';
+  sig_str[idx++] = '\0';
+  return SymbolTable::new_symbol(sig_str);
+}
diff a/src/hotspot/share/runtime/signature.hpp b/src/hotspot/share/runtime/signature.hpp
--- a/src/hotspot/share/runtime/signature.hpp
+++ b/src/hotspot/share/runtime/signature.hpp
@@ -24,10 +24,11 @@
  */
 
 #ifndef SHARE_RUNTIME_SIGNATURE_HPP
 #define SHARE_RUNTIME_SIGNATURE_HPP
 
+#include "classfile/symbolTable.hpp"
 #include "memory/allocation.hpp"
 #include "oops/method.hpp"
 
 
 // Static routines and parsing loops for processing field and method
@@ -558,14 +559,67 @@
     return skip_whole_array_prefix();
   }
 
   // free-standing lookups (bring your own CL/PD pair)
   enum FailureMode { ReturnNull, NCDFError, CachedOrNull };
+
   Klass* as_klass(Handle class_loader, Handle protection_domain, FailureMode failure_mode, TRAPS);
+  InlineKlass* as_inline_klass(InstanceKlass* holder);
   oop as_java_mirror(Handle class_loader, Handle protection_domain, FailureMode failure_mode, TRAPS);
 };
 
+class SigEntryFilter;
+typedef GrowableArrayFilterIterator<SigEntry, SigEntryFilter> ExtendedSignature;
+
+// Used for adapter generation. One SigEntry is used per element of
+// the signature of the method. Inline type arguments are treated
+// specially. See comment for InlineKlass::collect_fields().
+class SigEntry {
+ public:
+  BasicType _bt;
+  int _offset;
+  Symbol* _symbol;
+
+  SigEntry()
+    : _bt(T_ILLEGAL), _offset(-1), _symbol(NULL) {}
+
+  SigEntry(BasicType bt, int offset, Symbol* symbol)
+    : _bt(bt), _offset(offset), _symbol(symbol) {}
+
+  static int compare(SigEntry* e1, SigEntry* e2) {
+    if (e1->_offset != e2->_offset) {
+      return e1->_offset - e2->_offset;
+    }
+    assert((e1->_bt == T_LONG && (e2->_bt == T_LONG || e2->_bt == T_VOID)) ||
+           (e1->_bt == T_DOUBLE && (e2->_bt == T_DOUBLE || e2->_bt == T_VOID)) ||
+           e1->_bt == T_METADATA || e2->_bt == T_METADATA || e1->_bt == T_VOID || e2->_bt == T_VOID, "bad bt");
+    if (e1->_bt == e2->_bt) {
+      assert(e1->_bt == T_METADATA || e1->_bt == T_VOID, "only ones with duplicate offsets");
+      return 0;
+    }
+    if (e1->_bt == T_VOID ||
+        e2->_bt == T_METADATA) {
+      return 1;
+    }
+    if (e1->_bt == T_METADATA ||
+        e2->_bt == T_VOID) {
+      return -1;
+    }
+    ShouldNotReachHere();
+    return 0;
+  }
+  static void add_entry(GrowableArray<SigEntry>* sig, BasicType bt, Symbol* symbol, int offset = -1);
+  static bool skip_value_delimiters(const GrowableArray<SigEntry>* sig, int i);
+  static int fill_sig_bt(const GrowableArray<SigEntry>* sig, BasicType* sig_bt);
+  static TempNewSymbol create_symbol(const GrowableArray<SigEntry>* sig);
+};
+
+class SigEntryFilter {
+public:
+  bool operator()(const SigEntry& entry) { return entry._bt != T_METADATA && entry._bt != T_VOID; }
+};
+
 // Specialized SignatureStream: used for invoking SystemDictionary to either find
 //                              or resolve the underlying type when iterating over a
 //                              Java descriptor (or parts of it).
 class ResolvingSignatureStream : public SignatureStream {
   Klass*       _load_origin;
@@ -627,15 +681,15 @@
       callback->do_type(type);
     }
   }
 }
 
- #ifdef ASSERT
+#ifdef ASSERT
  class SignatureVerifier : public StackObj {
   public:
-    static bool is_valid_method_signature(Symbol* sig);
-    static bool is_valid_type_signature(Symbol* sig);
+    static bool is_valid_method_signature(const Symbol* sig);
+    static bool is_valid_type_signature(const Symbol* sig);
   private:
     static ssize_t is_valid_type(const char*, ssize_t);
 };
 #endif
 #endif // SHARE_RUNTIME_SIGNATURE_HPP
diff a/src/hotspot/share/runtime/signature_cc.hpp b/src/hotspot/share/runtime/signature_cc.hpp
--- /dev/null
+++ b/src/hotspot/share/runtime/signature_cc.hpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2019, 2020, 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_SIGNATURE_CC_HPP
+#define SHARE_RUNTIME_SIGNATURE_CC_HPP
+
+#include "runtime/signature.hpp"
+
+// Stream that iterates over a scalarized signature
+class ScalarizedInlineArgsStream : public StackObj {
+  const GrowableArray<SigEntry>* _sig;
+  int _sig_idx;
+  const VMRegPair* _regs;
+  int _regs_count;
+  int _regs_idx;
+  int _depth;
+  int _step;
+  DEBUG_ONLY(bool _finished);
+
+public:
+  ScalarizedInlineArgsStream(const GrowableArray<SigEntry>* sig, int sig_idx, VMRegPair* regs, int regs_count, int regs_idx, int step = 1)
+    : _sig(sig), _sig_idx(sig_idx), _regs(regs), _regs_count(regs_count), _regs_idx(regs_idx), _step(step) {
+    reset(sig_idx, regs_idx);
+  }
+
+  bool next(VMReg& reg, BasicType& bt) {
+    assert(!_finished, "sanity");
+    do {
+      _sig_idx += _step;
+      bt = _sig->at(_sig_idx)._bt;
+      if (bt == T_METADATA) {
+        _depth += _step;
+      } else if (bt == T_VOID &&
+                 _sig->at(_sig_idx-1)._bt != T_LONG &&
+                 _sig->at(_sig_idx-1)._bt != T_DOUBLE) {
+        _depth -= _step;
+      } else {
+        assert(_regs_idx >= 0 && _regs_idx < _regs_count, "out of bounds");
+        const VMRegPair pair = _regs[_regs_idx];
+        _regs_idx += _step;
+        reg = pair.first();
+        if (!reg->is_valid()) {
+          assert(!pair.second()->is_valid(), "must be invalid");
+        } else {
+          return true;
+        }
+      }
+    } while (_depth != 0);
+
+    DEBUG_ONLY(_finished = true);
+    return false;
+  }
+
+  void reset(int sig_idx, int regs_idx) {
+    _sig_idx = sig_idx;
+    _regs_idx = regs_idx;
+    assert(_sig->at(_sig_idx)._bt == (_step > 0) ? T_METADATA : T_VOID, "should be at inline type delimiter");
+    _depth = 1;
+    DEBUG_ONLY(_finished = false);
+  }
+
+  int sig_index()  { return _sig_idx;  }
+  int regs_index() { return _regs_idx; }
+};
+
+#endif // SHARE_RUNTIME_SIGNATURE_CC_HPP
diff a/src/hotspot/share/runtime/stackValue.cpp b/src/hotspot/share/runtime/stackValue.cpp
--- a/src/hotspot/share/runtime/stackValue.cpp
+++ b/src/hotspot/share/runtime/stackValue.cpp
@@ -39,17 +39,10 @@
 #endif
 
 class RegisterMap;
 class SmallRegisterMap;
 
-template StackValue* StackValue::create_stack_value(const frame* fr, const RegisterMap* reg_map, ScopeValue* sv);
-template StackValue* StackValue::create_stack_value(const frame* fr, const SmallRegisterMap* reg_map, ScopeValue* sv);
-
-template<typename RegisterMapT>
-StackValue* StackValue::create_stack_value(const frame* fr, const RegisterMapT* reg_map, ScopeValue* sv) {
-  return create_stack_value(sv, stack_value_address(fr, reg_map, sv), reg_map);
-}
 
 static oop oop_from_oop_location(stackChunkOop chunk, void* addr) {
   if (addr == nullptr) {
     return nullptr;
   }
@@ -146,12 +139,17 @@
          p2i(addr), chunk != nullptr, chunk != nullptr && chunk->has_bitmap() && UseCompressedOops);
   Handle h(Thread::current(), val); // Wrap a handle around the oop
   return new StackValue(h);
 }
 
+
+template StackValue* StackValue::create_stack_value(const frame* fr, const RegisterMap* reg_map, ScopeValue* sv);
+template StackValue* StackValue::create_stack_value(const frame* fr, const SmallRegisterMap* reg_map, ScopeValue* sv);
+
 template<typename RegisterMapT>
-StackValue* StackValue::create_stack_value(ScopeValue* sv, address value_addr, const RegisterMapT* reg_map) {
+StackValue* StackValue::create_stack_value(const frame* fr, const RegisterMapT* reg_map, ScopeValue* sv) {
+  address value_addr = stack_value_address(fr, reg_map, sv);
   stackChunkOop chunk = reg_map->stack_chunk()();
   if (sv->is_location()) {
     // Stack or register value
     Location loc = ((LocationValue *)sv)->location();
 
@@ -246,12 +244,18 @@
     value.p = (intptr_t) CONST64(0xDEADDEAFDEADDEAF);
     value.jl = ((ConstantLongValue *)sv)->value();
     return new StackValue(value.p);
 #endif
   } else if (sv->is_object()) { // Scalar replaced object in compiled frame
-    Handle ov = ((ObjectValue *)sv)->value();
-    return new StackValue(ov, (ov.is_null()) ? 1 : 0);
+    ObjectValue* ov = ((ObjectValue *)sv);
+    bool scalar_replaced = ov->value().is_null();
+    if (ov->maybe_null()) {
+      // Don't treat inline type as scalar replaced if it is null
+      jint is_init = StackValue::create_stack_value(fr, reg_map, ov->is_init())->get_jint();
+      scalar_replaced &= (is_init != 0);
+    }
+    return new StackValue(ov->value(), scalar_replaced ? 1 : 0);
   } else if (sv->is_marker()) {
     // Should never need to directly construct a marker.
     ShouldNotReachHere();
   }
   // Unknown ScopeValue type
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
@@ -168,10 +168,13 @@
 address StubRoutines::_dlibm_sin_cos_huge = nullptr;
 address StubRoutines::_dlibm_reduce_pi04l = nullptr;
 address StubRoutines::_dlibm_tan_cot_huge = nullptr;
 address StubRoutines::_dtan = nullptr;
 
+address StubRoutines::_load_inline_type_fields_in_regs = nullptr;
+address StubRoutines::_store_inline_type_fields_to_buf = nullptr;
+
 address StubRoutines::_f2hf = nullptr;
 address StubRoutines::_hf2f = nullptr;
 
 address StubRoutines::_vector_f_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_SVML_OP] = {{nullptr}, {nullptr}};
 address StubRoutines::_vector_d_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_SVML_OP] = {{nullptr}, {nullptr}};
diff a/src/hotspot/share/runtime/stubRoutines.hpp b/src/hotspot/share/runtime/stubRoutines.hpp
--- a/src/hotspot/share/runtime/stubRoutines.hpp
+++ b/src/hotspot/share/runtime/stubRoutines.hpp
@@ -265,10 +265,13 @@
   JFR_ONLY(static RuntimeStub* _jfr_write_checkpoint_stub;)
   JFR_ONLY(static address _jfr_write_checkpoint;)
   JFR_ONLY(static RuntimeStub* _jfr_return_lease_stub;)
   JFR_ONLY(static address _jfr_return_lease;)
 
+  static address _load_inline_type_fields_in_regs;
+  static address _store_inline_type_fields_to_buf;
+
   // Vector Math Routines
   static address _vector_f_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_SVML_OP];
   static address _vector_d_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_SVML_OP];
 
   static address _upcall_stub_exception_handler;
@@ -493,8 +496,11 @@
   static void arrayof_jshort_copy    (HeapWord* src, HeapWord* dest, size_t count);
   static void arrayof_jint_copy      (HeapWord* src, HeapWord* dest, size_t count);
   static void arrayof_jlong_copy     (HeapWord* src, HeapWord* dest, size_t count);
   static void arrayof_oop_copy       (HeapWord* src, HeapWord* dest, size_t count);
   static void arrayof_oop_copy_uninit(HeapWord* src, HeapWord* dest, size_t count);
+
+  static address load_inline_type_fields_in_regs() { return _load_inline_type_fields_in_regs; }
+  static address store_inline_type_fields_to_buf() { return _store_inline_type_fields_to_buf; }
 };
 
 #endif // SHARE_RUNTIME_STUBROUTINES_HPP
diff a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp
--- a/src/hotspot/share/runtime/synchronizer.cpp
+++ b/src/hotspot/share/runtime/synchronizer.cpp
@@ -302,10 +302,24 @@
 bool volatile ObjectSynchronizer::_is_final_audit = false;
 jlong ObjectSynchronizer::_last_async_deflation_time_ns = 0;
 static uintx _no_progress_cnt = 0;
 static bool _no_progress_skip_increment = false;
 
+#define CHECK_THROW_NOSYNC_IMSE(obj)  \
+  if (EnableValhalla && (obj)->mark().is_inline_type()) {  \
+    JavaThread* THREAD = current;           \
+    ResourceMark rm(THREAD);                \
+    THROW_MSG(vmSymbols::java_lang_IllegalMonitorStateException(), obj->klass()->external_name()); \
+  }
+
+#define CHECK_THROW_NOSYNC_IMSE_0(obj)  \
+  if (EnableValhalla && (obj)->mark().is_inline_type()) {  \
+    JavaThread* THREAD = current;             \
+    ResourceMark rm(THREAD);                  \
+    THROW_MSG_0(vmSymbols::java_lang_IllegalMonitorStateException(), obj->klass()->external_name()); \
+  }
+
 // =====================> Quick functions
 
 // The quick_* forms are special fast-path variants used to improve
 // performance.  In the simplest case, a "quick_*" implementation could
 // simply return false, in which case the caller will perform the necessary
@@ -328,10 +342,11 @@
 
 bool ObjectSynchronizer::quick_notify(oopDesc* obj, JavaThread* current, bool all) {
   assert(current->thread_state() == _thread_in_Java, "invariant");
   NoSafepointVerifier nsv;
   if (obj == nullptr) return false;  // slow-path for invalid obj
+  assert(!EnableValhalla || !obj->klass()->is_inline_klass(), "monitor op on inline type");
   const markWord mark = obj->mark();
 
   if (LockingMode == LM_LIGHTWEIGHT) {
     if (mark.is_fast_locked() && current->lock_stack().contains(cast_to_oop(obj))) {
       // Degenerate notify
@@ -384,10 +399,11 @@
 bool ObjectSynchronizer::quick_enter(oop obj, JavaThread* current,
                                      BasicLock * lock) {
   assert(current->thread_state() == _thread_in_Java, "invariant");
   NoSafepointVerifier nsv;
   if (obj == nullptr) return false;       // Need to throw NPE
+  assert(!EnableValhalla || !obj->klass()->is_inline_klass(), "monitor op on inline type");
 
   if (obj->klass()->is_value_based()) {
     return false;
   }
 
@@ -505,10 +521,11 @@
 // The interpreter and compiler assembly code tries to lock using the fast path
 // of this algorithm. Make sure to update that code if the following function is
 // changed. The implementation is extremely sensitive to race condition. Be careful.
 
 void ObjectSynchronizer::enter(Handle obj, BasicLock* lock, JavaThread* current) {
+  CHECK_THROW_NOSYNC_IMSE(obj);
   if (obj->klass()->is_value_based()) {
     handle_sync_on_value_based_class(obj, current);
   }
 
   current->inc_held_monitor_count();
@@ -576,10 +593,13 @@
 void ObjectSynchronizer::exit(oop object, BasicLock* lock, JavaThread* current) {
   current->dec_held_monitor_count();
 
   if (!useHeavyMonitors()) {
     markWord mark = object->mark();
+    if (EnableValhalla && mark.is_inline_type()) {
+      return;
+    }
     if (LockingMode == LM_LIGHTWEIGHT) {
       // Fast-locking does not use the 'lock' argument.
       while (mark.is_fast_locked()) {
         // Retry until a lock state change has been observed.  cas_set_mark() may collide with non lock bits modifications.
         const markWord unlocked_mark = mark.set_unlocked();
@@ -647,10 +667,11 @@
 // NOTE: must use heavy weight monitor to handle jni monitor enter
 void ObjectSynchronizer::jni_enter(Handle obj, JavaThread* current) {
   if (obj->klass()->is_value_based()) {
     handle_sync_on_value_based_class(obj, current);
   }
+  CHECK_THROW_NOSYNC_IMSE(obj);
 
   // the current locking is from JNI instead of Java code
   current->set_current_pending_monitor_is_from_java(false);
   // An async deflation can race after the inflate() call and before
   // enter() can make the ObjectMonitor busy. enter() returns false if
@@ -666,10 +687,11 @@
 }
 
 // NOTE: must use heavy weight monitor to handle jni monitor exit
 void ObjectSynchronizer::jni_exit(oop obj, TRAPS) {
   JavaThread* current = THREAD;
+  CHECK_THROW_NOSYNC_IMSE(obj);
 
   // The ObjectMonitor* can't be async deflated until ownership is
   // dropped inside exit() and the ObjectMonitor* must be !is_busy().
   ObjectMonitor* monitor = inflate(current, obj, inflate_cause_jni_exit);
   // If this thread has locked the object, exit the monitor. We
@@ -704,10 +726,11 @@
 // -----------------------------------------------------------------------------
 //  Wait/Notify/NotifyAll
 // NOTE: must use heavy weight monitor to handle wait()
 int ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) {
   JavaThread* current = THREAD;
+  CHECK_THROW_NOSYNC_IMSE_0(obj);
   if (millis < 0) {
     THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
   }
   // The ObjectMonitor* can't be async deflated because the _waiters
   // field is incremented before ownership is dropped and decremented
@@ -725,10 +748,11 @@
   return ret_code;
 }
 
 void ObjectSynchronizer::notify(Handle obj, TRAPS) {
   JavaThread* current = THREAD;
+  CHECK_THROW_NOSYNC_IMSE(obj);
 
   markWord mark = obj->mark();
   if (LockingMode == LM_LIGHTWEIGHT) {
     if ((mark.is_fast_locked() && current->lock_stack().contains(obj()))) {
       // Not inflated so there can't be any waiters to notify.
@@ -747,10 +771,11 @@
 }
 
 // NOTE: see comment of notify()
 void ObjectSynchronizer::notifyall(Handle obj, TRAPS) {
   JavaThread* current = THREAD;
+  CHECK_THROW_NOSYNC_IMSE(obj);
 
   markWord mark = obj->mark();
   if (LockingMode == LM_LIGHTWEIGHT) {
     if ((mark.is_fast_locked() && current->lock_stack().contains(obj()))) {
       // Not inflated so there can't be any waiters to notify.
@@ -901,10 +926,14 @@
   assert(value != markWord::no_hash, "invariant");
   return value;
 }
 
 intptr_t ObjectSynchronizer::FastHashCode(Thread* current, oop obj) {
+  if (EnableValhalla && obj->klass()->is_inline_klass()) {
+    // VM should be calling bootstrap method
+    ShouldNotReachHere();
+  }
 
   while (true) {
     ObjectMonitor* monitor = nullptr;
     markWord temp, test;
     intptr_t hash;
@@ -1018,10 +1047,13 @@
   }
 }
 
 bool ObjectSynchronizer::current_thread_holds_lock(JavaThread* current,
                                                    Handle h_obj) {
+  if (EnableValhalla && h_obj->mark().is_inline_type()) {
+    return false;
+  }
   assert(current == JavaThread::current(), "Can only be called on current thread");
   oop obj = h_obj();
 
   markWord mark = read_stable_mark(obj);
 
@@ -1296,10 +1328,14 @@
   return thread->is_Java_thread() ? JavaThread::cast(thread)->lock_stack().contains(obj) : false;
 }
 
 ObjectMonitor* ObjectSynchronizer::inflate(Thread* current, oop object,
                                            const InflateCause cause) {
+  if (EnableValhalla) {
+    guarantee(!object->klass()->is_inline_klass(), "Attempt to inflate inline type");
+  }
+
   EventJavaMonitorInflate event;
 
   for (;;) {
     const markWord mark = object->mark_acquire();
 
diff a/src/hotspot/share/runtime/vmOperation.hpp b/src/hotspot/share/runtime/vmOperation.hpp
--- a/src/hotspot/share/runtime/vmOperation.hpp
+++ b/src/hotspot/share/runtime/vmOperation.hpp
@@ -112,10 +112,11 @@
   template(ICBufferFull)                          \
   template(PrintMetadata)                         \
   template(GTestExecuteAtSafepoint)               \
   template(GTestStopSafepoint)                    \
   template(JFROldObject)                          \
+  template(ClassPrintLayout)                      \
   template(JvmtiPostObjectFree)                   \
   template(RendezvousGCThreads)
 
 class Thread;
 class outputStream;
diff a/src/hotspot/share/runtime/vmOperations.cpp b/src/hotspot/share/runtime/vmOperations.cpp
--- a/src/hotspot/share/runtime/vmOperations.cpp
+++ b/src/hotspot/share/runtime/vmOperations.cpp
@@ -615,6 +615,10 @@
 
 #if INCLUDE_SERVICES
 void VM_PrintClassHierarchy::doit() {
   KlassHierarchy::print_class_hierarchy(_out, _print_interfaces, _print_subclasses, _classname);
 }
+
+void VM_PrintClassLayout::doit() {
+  PrintClassLayout::print_class_layout(_out, _class_name);
+}
 #endif
diff a/src/hotspot/share/runtime/vmOperations.hpp b/src/hotspot/share/runtime/vmOperations.hpp
--- a/src/hotspot/share/runtime/vmOperations.hpp
+++ b/src/hotspot/share/runtime/vmOperations.hpp
@@ -257,10 +257,20 @@
   VM_PrintCompileQueue(outputStream* st) : _out(st) {}
   VMOp_Type type() const { return VMOp_PrintCompileQueue; }
   void doit();
 };
 
+class VM_PrintClassLayout: public VM_Operation {
+ private:
+  outputStream* _out;
+  char* _class_name;
+ public:
+  VM_PrintClassLayout(outputStream* st, char* class_name): _out(st), _class_name(class_name) {}
+  VMOp_Type type() const { return VMOp_PrintClassHierarchy; }
+  void doit();
+};
+
 #if INCLUDE_SERVICES
 class VM_PrintClassHierarchy: public VM_Operation {
  private:
   outputStream* _out;
   bool _print_interfaces;
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
@@ -66,10 +66,12 @@
 #include "oops/compiledICHolder.hpp"
 #include "oops/constMethod.hpp"
 #include "oops/constantPool.hpp"
 #include "oops/cpCache.hpp"
 #include "oops/fieldInfo.hpp"
+#include "oops/flatArrayKlass.hpp"
+#include "oops/inlineKlass.hpp"
 #include "oops/instanceClassLoaderKlass.hpp"
 #include "oops/instanceKlass.hpp"
 #include "oops/instanceMirrorKlass.hpp"
 #include "oops/instanceOop.hpp"
 #include "oops/instanceStackChunkKlass.hpp"
@@ -230,11 +232,11 @@
   nonstatic_field(ResolvedFieldEntry,          _cpool_index,                                  u2)                                    \
   nonstatic_field(ConstantPoolCache,           _resolved_method_entries,                      Array<ResolvedMethodEntry>*)           \
   nonstatic_field(ResolvedMethodEntry,         _cpool_index,                                  u2)                                    \
   nonstatic_field(ConstantPoolCache,           _resolved_indy_entries,                        Array<ResolvedIndyEntry>*)             \
   nonstatic_field(ResolvedIndyEntry,           _cpool_index,                                  u2)                                    \
-  volatile_nonstatic_field(InstanceKlass,      _array_klasses,                                ObjArrayKlass*)                        \
+  volatile_nonstatic_field(InstanceKlass,      _array_klasses,                                ArrayKlass*)                        \
   nonstatic_field(InstanceKlass,               _methods,                                      Array<Method*>*)                       \
   nonstatic_field(InstanceKlass,               _default_methods,                              Array<Method*>*)                       \
   nonstatic_field(InstanceKlass,               _local_interfaces,                             Array<InstanceKlass*>*)                \
   nonstatic_field(InstanceKlass,               _transitive_interfaces,                        Array<InstanceKlass*>*)                \
   nonstatic_field(InstanceKlass,               _fieldinfo_stream,                             Array<u1>*)                            \
@@ -1165,13 +1167,15 @@
   declare_toplevel_type(CompiledICHolder)                                 \
   declare_toplevel_type(MetaspaceObj)                                     \
     declare_type(Metadata, MetaspaceObj)                                  \
     declare_type(Klass, Metadata)                                         \
            declare_type(ArrayKlass, Klass)                                \
+           declare_type(FlatArrayKlass, ArrayKlass)                       \
            declare_type(ObjArrayKlass, ArrayKlass)                        \
            declare_type(TypeArrayKlass, ArrayKlass)                       \
       declare_type(InstanceKlass, Klass)                                  \
+        declare_type(InlineKlass, InstanceKlass)                          \
         declare_type(InstanceClassLoaderKlass, InstanceKlass)             \
         declare_type(InstanceMirrorKlass, InstanceKlass)                  \
         declare_type(InstanceRefKlass, InstanceKlass)                     \
         declare_type(InstanceStackChunkKlass, InstanceKlass)              \
     declare_type(ConstantPool, Metadata)                                  \
@@ -1546,10 +1550,11 @@
   declare_c2_type(MachNode, Node)                                         \
   declare_c2_type(MachIdealNode, MachNode)                                \
   declare_c2_type(MachTypeNode, MachNode)                                 \
   declare_c2_type(MachBreakpointNode, MachIdealNode)                      \
   declare_c2_type(MachUEPNode, MachIdealNode)                             \
+  declare_c2_type(MachVEPNode, MachIdealNode)                             \
   declare_c2_type(MachPrologNode, MachIdealNode)                          \
   declare_c2_type(MachEpilogNode, MachIdealNode)                          \
   declare_c2_type(MachNopNode, MachIdealNode)                             \
   declare_c2_type(MachSpillCopyNode, MachIdealNode)                       \
   declare_c2_type(MachNullCheckNode, MachIdealNode)                       \
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
@@ -116,10 +116,11 @@
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassHierarchyDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassesDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<SymboltableDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<StringtableDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<metaspace::MetaspaceDCmd>(full_export, true, false));
+  DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<PrintClassLayoutDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<EventLogDCmd>(full_export, true, false));
 #if INCLUDE_JVMTI // Both JVMTI and SERVICES have to be enabled to have this dcmd
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JVMTIAgentLoadDCmd>(full_export, true, false));
 #endif // INCLUDE_JVMTI
 #endif // INCLUDE_SERVICES
@@ -154,11 +155,10 @@
   uint32_t jmx_agent_export_flags = DCmd_Source_Internal | DCmd_Source_AttachAPI;
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JMXStartRemoteDCmd>(jmx_agent_export_flags, true,false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JMXStartLocalDCmd>(jmx_agent_export_flags, true,false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JMXStopRemoteDCmd>(jmx_agent_export_flags, true,false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JMXStatusDCmd>(jmx_agent_export_flags, true,false));
-
   // Debug on cmd (only makes sense with JVMTI since the agentlib needs it).
 #if INCLUDE_JVMTI
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<DebugOnCmdStartDCmd>(full_export, true, true));
 #endif // INCLUDE_JVMTI
 
@@ -951,11 +951,35 @@
 void ClassHierarchyDCmd::execute(DCmdSource source, TRAPS) {
   VM_PrintClassHierarchy printClassHierarchyOp(output(), _print_interfaces.value(),
                                                _print_subclasses.value(), _classname.value());
   VMThread::execute(&printClassHierarchyOp);
 }
-#endif
+
+PrintClassLayoutDCmd::PrintClassLayoutDCmd(outputStream* output, bool heap) :
+                                       DCmdWithParser(output, heap),
+  _classname("classname", "Name of class whose layout should be printed. ",
+             "STRING", true) {
+  _dcmdparser.add_dcmd_argument(&_classname);
+}
+
+void PrintClassLayoutDCmd::execute(DCmdSource source, TRAPS) {
+  VM_PrintClassLayout printClassLayoutOp(output(), _classname.value());
+  VMThread::execute(&printClassLayoutOp);
+}
+
+int PrintClassLayoutDCmd::num_arguments() {
+  ResourceMark rm;
+  PrintClassLayoutDCmd* dcmd = new PrintClassLayoutDCmd(nullptr, false);
+  if (dcmd != nullptr) {
+    DCmdMark mark(dcmd);
+    return dcmd->_dcmdparser.num_arguments();
+  } else {
+    return 0;
+  }
+}
+
+#endif // INCLUDE_SERVICES
 
 ClassesDCmd::ClassesDCmd(outputStream* output, bool heap) :
                                      DCmdWithParser(output, heap),
   _verbose("-verbose",
            "Dump the detailed content of a Java class. "
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,35 @@
     return p;
   }
   virtual void execute(DCmdSource source, TRAPS);
 };
 
+class PrintClassLayoutDCmd : public DCmdWithParser {
+protected:
+  DCmdArgument<char*> _classname; // lass name whose layout should be printed.
+public:
+  PrintClassLayoutDCmd(outputStream* output, bool heap);
+  static const char* name() {
+    return "VM.class_print_layout";
+  }
+  static const char* description() {
+    return "Print the layout of an instance of a class, including flat fields. "
+           "The name of each class is followed by the ClassLoaderData* of its ClassLoader, "
+           "or \"null\" if loaded by the bootstrap class loader.";
+  }
+  static const char* impact() {
+      return "Medium: Depends on number of loaded classes.";
+  }
+  static const JavaPermission permission() {
+    JavaPermission p = {"java.lang.management.ManagementPermission",
+                        "monitor", nullptr};
+    return p;
+  }
+  static int num_arguments();
+  virtual void execute(DCmdSource source, TRAPS);
+};
+
 #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/heapDumper.cpp b/src/hotspot/share/services/heapDumper.cpp
--- a/src/hotspot/share/services/heapDumper.cpp
+++ b/src/hotspot/share/services/heapDumper.cpp
@@ -40,13 +40,16 @@
 #include "memory/universe.hpp"
 #include "oops/fieldStreams.inline.hpp"
 #include "oops/klass.inline.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "oops/objArrayOop.inline.hpp"
+#include "oops/flatArrayKlass.hpp"
+#include "oops/flatArrayOop.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/typeArrayOop.inline.hpp"
 #include "runtime/continuationWrapper.inline.hpp"
+#include "runtime/fieldDescriptor.inline.hpp"
 #include "runtime/frame.inline.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/javaCalls.hpp"
 #include "runtime/javaThread.inline.hpp"
 #include "runtime/jniHandles.hpp"
@@ -314,10 +317,32 @@
  *
  *                u4        0x00000001: alloc traces on/off
  *                          0x00000002: cpu sampling on/off
  *                u2        stack trace depth
  *
+ * HPROF_FLAT_ARRAYS        list of flat arrays
+ *
+ *               [flat array sub-records]*
+ *
+ *               HPROF_FLAT_ARRAY      flat array
+ *
+ *                          id         array object ID (dumped as HPROF_GC_PRIM_ARRAY_DUMP)
+ *                          id         element class ID (dumped by HPROF_GC_CLASS_DUMP)
+ *
+ * HPROF_INLINED_FIELDS     decribes inlined fields
+ *
+ *               [class with inlined fields sub-records]*
+ *
+ *               HPROF_CLASS_WITH_INLINED_FIELDS
+ *
+ *                          id         class ID (dumped as HPROF_GC_CLASS_DUMP)
+ *
+ *                          u2         number of instance inlined fields (not including super)
+ *                          [u2,       inlined field index,
+ *                           u2,       synthetic field count,
+ *                           id,       original field name,
+ *                           id]*      inlined field class ID (dumped by HPROF_GC_CLASS_DUMP)
  *
  * When the header is "JAVA PROFILE 1.0.2" a heap dump can optionally
  * be generated as a sequence of heap dump segments. This sequence is
  * terminated by an end record. The additional tags allowed by format
  * "JAVA PROFILE 1.0.2" are:
@@ -351,10 +376,17 @@
 
   // 1.0.2 record types
   HPROF_HEAP_DUMP_SEGMENT       = 0x1C,
   HPROF_HEAP_DUMP_END           = 0x2C,
 
+  // inlined object support
+  HPROF_FLAT_ARRAYS             = 0x12,
+  HPROF_INLINED_FIELDS          = 0x13,
+  // inlined object subrecords
+  HPROF_FLAT_ARRAY                  = 0x01,
+  HPROF_CLASS_WITH_INLINED_FIELDS   = 0x01,
+
   // field types
   HPROF_ARRAY_OBJECT            = 0x01,
   HPROF_NORMAL_OBJECT           = 0x02,
   HPROF_BOOLEAN                 = 0x04,
   HPROF_CHAR                    = 0x05,
@@ -385,10 +417,75 @@
 enum {
   STACK_TRACE_ID = 1,
   INITIAL_CLASS_COUNT = 200
 };
 
+
+class AbstractDumpWriter;
+
+class InlinedObjects {
+
+  struct ClassInlinedFields {
+    const Klass *klass;
+    uintx base_index;   // base index of the inlined field names (1st field has index base_index+1).
+    ClassInlinedFields(const Klass *klass = nullptr, uintx base_index = 0) : klass(klass), base_index(base_index) {}
+
+    // For GrowableArray::find_sorted().
+    static int compare(const ClassInlinedFields& a, const ClassInlinedFields& b) {
+      return a.klass - b.klass;
+    }
+    // For GrowableArray::sort().
+    static int compare(ClassInlinedFields* a, ClassInlinedFields* b) {
+      return compare(*a, *b);
+    }
+  };
+
+  uintx _min_string_id;
+  uintx _max_string_id;
+
+  GrowableArray<ClassInlinedFields> *_inlined_field_map;
+
+  // counters for classes with inlined fields and for the fields
+  int _classes_count;
+  int _inlined_fields_count;
+
+  static InlinedObjects *_instance;
+
+  static void inlined_field_names_callback(InlinedObjects* _this, const Klass *klass, uintx base_index, int count);
+
+  GrowableArray<oop> *_flat_arrays;
+
+public:
+  InlinedObjects()
+    : _min_string_id(0), _max_string_id(0),
+    _inlined_field_map(nullptr),
+    _classes_count(0), _inlined_fields_count(0),
+    _flat_arrays(nullptr) {
+  }
+
+  static InlinedObjects* get_instance() {
+    return _instance;
+  }
+
+  void init();
+  void release();
+
+  void dump_inlined_field_names(AbstractDumpWriter *writer);
+
+  uintx get_base_index_for(Klass* k);
+  uintx get_next_string_id(uintx id);
+
+  void dump_classed_with_inlined_fields(AbstractDumpWriter* writer);
+
+  void add_flat_array(oop array);
+  void dump_flat_arrays(AbstractDumpWriter* writer);
+
+};
+
+InlinedObjects *InlinedObjects::_instance = nullptr;
+
+
 // Supports I/O operations for a dump
 // Base class for dump and parallel dump
 class AbstractDumpWriter : public CHeapObj<mtInternal> {
  protected:
   enum {
@@ -737,11 +834,11 @@
   // returns hprof tag for the given basic type
   static hprofTag type2tag(BasicType type);
   // Returns the size of the data to write.
   static u4 sig2size(Symbol* sig);
 
-  // returns the size of the instance of the given class
+  // calculates the total size of the all fields of the given class.
   static u4 instance_size(InstanceKlass* ik, DumperClassCacheTableEntry* class_cache_entry = nullptr);
 
   // dump a jfloat
   static void dump_float(AbstractDumpWriter* writer, jfloat f);
   // dump a jdouble
@@ -750,32 +847,43 @@
   static void dump_field_value(AbstractDumpWriter* writer, char type, oop obj, int offset);
   // returns the size of the static fields; also counts the static fields
   static u4 get_static_fields_size(InstanceKlass* ik, u2& field_count);
   // dumps static fields of the given class
   static void dump_static_fields(AbstractDumpWriter* writer, Klass* k);
-  // dump the raw values of the instance fields of the given object
-  static void dump_instance_fields(AbstractDumpWriter* writer, oop o, DumperClassCacheTableEntry* class_cache_entry);
+  // dump the raw values of the instance fields of the given identity or inlined object;
+  // for identity objects offset is 0 and 'klass' is o->klass(),
+  // for inlined objects offset is the offset in the holder object, 'klass' is inlined object class
+  static void dump_instance_fields(AbstractDumpWriter* writer, oop o, int offset, DumperClassCacheTable* class_cache, DumperClassCacheTableEntry* class_cache_entry);
+  // dump the raw values of the instance fields of the given inlined object;
+  // dump_instance_fields wrapper for inlined objects
+  static void dump_inlined_object_fields(AbstractDumpWriter* writer, oop o, int offset, DumperClassCacheTable* class_cache, DumperClassCacheTableEntry* class_cache_entry);
+
   // get the count of the instance fields for a given class
   static u2 get_instance_fields_count(InstanceKlass* ik);
   // dumps the definition of the instance fields for a given class
-  static void dump_instance_field_descriptors(AbstractDumpWriter* writer, Klass* k);
+  static void dump_instance_field_descriptors(AbstractDumpWriter* writer, InstanceKlass* k, uintx *inlined_fields_index = nullptr);
   // creates HPROF_GC_INSTANCE_DUMP record for the given object
   static void dump_instance(AbstractDumpWriter* writer, oop o, DumperClassCacheTable* class_cache);
   // creates HPROF_GC_CLASS_DUMP record for the given instance class
   static void dump_instance_class(AbstractDumpWriter* writer, Klass* k);
   // creates HPROF_GC_CLASS_DUMP record for a given array class
   static void dump_array_class(AbstractDumpWriter* writer, Klass* k);
 
   // creates HPROF_GC_OBJ_ARRAY_DUMP record for the given object array
   static void dump_object_array(AbstractDumpWriter* writer, objArrayOop array);
+  // creates HPROF_GC_PRIM_ARRAY_DUMP record for the given flat array
+  static void dump_flat_array(AbstractDumpWriter* writer, flatArrayOop array, DumperClassCacheTable* class_cache);
   // creates HPROF_GC_PRIM_ARRAY_DUMP record for the given type array
   static void dump_prim_array(AbstractDumpWriter* writer, typeArrayOop array);
   // create HPROF_FRAME record for the given method and bci
   static void dump_stack_frame(AbstractDumpWriter* writer, int frame_serial_num, int class_serial_num, Method* m, int bci);
 
   // check if we need to truncate an array
   static int calculate_array_max_length(AbstractDumpWriter* writer, arrayOop array, short header_size);
+  // extended version to dump flat arrays as primitive arrays;
+  // type_size specifies size of the inlined objects.
+  static int calculate_array_max_length(AbstractDumpWriter* writer, arrayOop array, int type_size, short header_size);
 
   // fixes up the current dump record and writes HPROF_HEAP_DUMP_END record
   static void end_of_dump(AbstractDumpWriter* writer);
 
   static oop mask_dormant_archived_object(oop o, oop ref_obj) {
@@ -787,10 +895,20 @@
     } else {
       return o;
     }
   }
 
+  // helper methods for inlined fields.
+  static bool is_inlined_field(const fieldDescriptor& fld) {
+    return fld.is_flat();
+  }
+  static InlineKlass* get_inlined_field_klass(const fieldDescriptor& fld) {
+    assert(is_inlined_field(fld), "must be inlined field");
+    InstanceKlass* holder_klass = fld.field_holder();
+    return InlineKlass::cast(holder_klass->get_inline_type_field_klass(fld.index()));
+  }
+
   static void report_dormant_archived_object(oop o, oop ref_obj) {
     if (log_is_enabled(Trace, cds, heap)) {
       ResourceMark rm;
       if (ref_obj != nullptr) {
         log_trace(cds, heap)("skipped dormant archived object " INTPTR_FORMAT " (%s) referenced by " INTPTR_FORMAT " (%s)",
@@ -811,18 +929,22 @@
 class DumperClassCacheTableEntry : public CHeapObj<mtServiceability> {
   friend class DumperClassCacheTable;
 private:
   GrowableArray<char> _sigs_start;
   GrowableArray<int> _offsets;
+  GrowableArray<InlineKlass*> _inline_klasses;
   u4 _instance_size;
   int _entries;
 
 public:
   DumperClassCacheTableEntry() : _instance_size(0), _entries(0) {};
 
   int field_count()             { return _entries; }
   char sig_start(int field_idx) { return _sigs_start.at(field_idx); }
+  void push_sig_start_inlined() { _sigs_start.push('Q'); }
+  bool is_inlined(int field_idx){ return _sigs_start.at(field_idx) == 'Q'; }
+  InlineKlass* inline_klass(int field_idx) { assert(is_inlined(field_idx), "Not inlined"); return _inline_klasses.at(field_idx); }
   int offset(int field_idx)     { return _offsets.at(field_idx); }
   u4 instance_size()            { return _instance_size; }
 };
 
 class DumperClassCacheTable {
@@ -867,15 +989,23 @@
     DumperClassCacheTableEntry** from_cache = _ptrs->get(ik);
     if (from_cache == nullptr) {
       entry = new DumperClassCacheTableEntry();
       for (HierarchicalFieldStream<JavaFieldStream> fld(ik); !fld.done(); fld.next()) {
         if (!fld.access_flags().is_static()) {
-          Symbol* sig = fld.signature();
-          entry->_sigs_start.push(sig->char_at(0));
+          InlineKlass* inlineKlass = nullptr;
+          if (DumperSupport::is_inlined_field(fld.field_descriptor())) {
+            inlineKlass = DumperSupport::get_inlined_field_klass(fld.field_descriptor());
+            entry->push_sig_start_inlined();
+            entry->_instance_size += DumperSupport::instance_size(inlineKlass);
+          } else {
+            Symbol* sig = fld.signature();
+            entry->_sigs_start.push(sig->char_at(0));
+            entry->_instance_size += DumperSupport::sig2size(sig);
+          }
+          entry->_inline_klasses.push(inlineKlass);
           entry->_offsets.push(fld.offset());
           entry->_entries++;
-          entry->_instance_size += DumperSupport::sig2size(sig);
         }
       }
 
       if (_ptrs->number_of_entries() >= CACHE_TOP) {
         // We do not track the individual hit rates for table entries.
@@ -981,10 +1111,11 @@
   } else {
     writer->write_u8(bit_cast<u8>(d));
   }
 }
 
+
 // dumps the raw value of the given field
 void DumperSupport::dump_field_value(AbstractDumpWriter* writer, char type, oop obj, int offset) {
   switch (type) {
     case JVM_SIGNATURE_CLASS :
     case JVM_SIGNATURE_ARRAY : {
@@ -1039,19 +1170,23 @@
       break;
     }
   }
 }
 
-// returns the size of the instance of the given class
+// calculates the total size of the all fields of the given class.
 u4 DumperSupport::instance_size(InstanceKlass* ik, DumperClassCacheTableEntry* class_cache_entry) {
   if (class_cache_entry != nullptr) {
     return class_cache_entry->instance_size();
   } else {
     u4 size = 0;
     for (HierarchicalFieldStream<JavaFieldStream> fld(ik); !fld.done(); fld.next()) {
       if (!fld.access_flags().is_static()) {
-        size += sig2size(fld.signature());
+        if (is_inlined_field(fld.field_descriptor())) {
+          size += instance_size(get_inlined_field_klass(fld.field_descriptor()));
+        } else {
+          size += sig2size(fld.signature());
+        }
       }
     }
     return size;
   }
 }
@@ -1060,10 +1195,12 @@
   field_count = 0;
   u4 size = 0;
 
   for (JavaFieldStream fldc(ik); !fldc.done(); fldc.next()) {
     if (fldc.access_flags().is_static()) {
+      assert(!is_inlined_field(fldc.field_descriptor()), "static fields cannot be inlined");
+
       field_count++;
       size += sig2size(fldc.signature());
     }
   }
 
@@ -1094,10 +1231,12 @@
   InstanceKlass* ik = InstanceKlass::cast(k);
 
   // dump the field descriptors and raw values
   for (JavaFieldStream fld(ik); !fld.done(); fld.next()) {
     if (fld.access_flags().is_static()) {
+      assert(!is_inlined_field(fld.field_descriptor()), "static fields cannot be inlined");
+
       Symbol* sig = fld.signature();
 
       writer->write_symbolID(fld.name());   // name
       writer->write_u1(sig2tag(sig));       // type
 
@@ -1122,40 +1261,90 @@
       prev = prev->previous_versions();
     }
   }
 }
 
-// dump the raw values of the instance fields of the given object
-void DumperSupport::dump_instance_fields(AbstractDumpWriter* writer, oop o, DumperClassCacheTableEntry* class_cache_entry) {
+// dump the raw values of the instance fields of the given identity or inlined object;
+// for identity objects offset is 0 and 'klass' is o->klass(),
+// for inlined objects offset is the offset in the holder object, 'klass' is inlined object class.
+void DumperSupport::dump_instance_fields(AbstractDumpWriter* writer, oop o, int offset, DumperClassCacheTable* class_cache, DumperClassCacheTableEntry* class_cache_entry) {
   assert(class_cache_entry != nullptr, "Pre-condition: must be provided");
   for (int idx = 0; idx < class_cache_entry->field_count(); idx++) {
-    dump_field_value(writer, class_cache_entry->sig_start(idx), o, class_cache_entry->offset(idx));
+    if (class_cache_entry->is_inlined(idx)) {
+      InlineKlass* field_klass = class_cache_entry->inline_klass(idx);
+      int fields_offset = offset + (class_cache_entry->offset(idx) - field_klass->first_field_offset());
+      DumperClassCacheTableEntry* inline_class_cache_entry = class_cache->lookup_or_create(field_klass);
+      dump_inlined_object_fields(writer, o, fields_offset, class_cache, inline_class_cache_entry);
+    } else {
+      dump_field_value(writer, class_cache_entry->sig_start(idx), o, class_cache_entry->offset(idx));
+    }
   }
 }
 
-// dumps the definition of the instance fields for a given class
+void DumperSupport::dump_inlined_object_fields(AbstractDumpWriter* writer, oop o, int offset, DumperClassCacheTable* class_cache, DumperClassCacheTableEntry* class_cache_entry) {
+  // the object is inlined, so all its fields are stored without headers.
+  dump_instance_fields(writer, o, offset, class_cache, class_cache_entry);
+}
+
+// gets the count of the instance fields for a given class
 u2 DumperSupport::get_instance_fields_count(InstanceKlass* ik) {
   u2 field_count = 0;
 
   for (JavaFieldStream fldc(ik); !fldc.done(); fldc.next()) {
-    if (!fldc.access_flags().is_static()) field_count++;
+    if (!fldc.access_flags().is_static()) {
+      if (is_inlined_field(fldc.field_descriptor())) {
+        // add "synthetic" fields for inlined fields.
+        field_count += get_instance_fields_count(get_inlined_field_klass(fldc.field_descriptor()));
+      } else {
+        field_count++;
+      }
+    }
   }
 
   return field_count;
 }
 
 // dumps the definition of the instance fields for a given class
-void DumperSupport::dump_instance_field_descriptors(AbstractDumpWriter* writer, Klass* k) {
-  InstanceKlass* ik = InstanceKlass::cast(k);
+// inlined_fields_id is not-nullptr for inlined fields (to get synthetic field name IDs
+// by using InlinedObjects::get_next_string_id()).
+void DumperSupport::dump_instance_field_descriptors(AbstractDumpWriter* writer, InstanceKlass* ik, uintx* inlined_fields_id) {
+  // inlined_fields_id != nullptr means ik is a class of inlined field.
+  // Inlined field id pointer for this class; lazyly initialized
+  // if the class has inlined field(s) and the caller didn't provide inlined_fields_id.
+  uintx *this_klass_inlined_fields_id = inlined_fields_id;
+  uintx inlined_id = 0;
 
   // dump the field descriptors
   for (JavaFieldStream fld(ik); !fld.done(); fld.next()) {
     if (!fld.access_flags().is_static()) {
-      Symbol* sig = fld.signature();
+      if (is_inlined_field(fld.field_descriptor())) {
+        // dump "synthetic" fields for inlined fields.
+        if (this_klass_inlined_fields_id == nullptr) {
+          inlined_id = InlinedObjects::get_instance()->get_base_index_for(ik);
+          this_klass_inlined_fields_id = &inlined_id;
+        }
+        dump_instance_field_descriptors(writer, get_inlined_field_klass(fld.field_descriptor()), this_klass_inlined_fields_id);
+      } else {
+        Symbol* sig = fld.signature();
+        Symbol* name = nullptr;
+        // Use inlined_fields_id provided by caller.
+        if (inlined_fields_id != nullptr) {
+          uintx name_id = InlinedObjects::get_instance()->get_next_string_id(*inlined_fields_id);
+
+          // name_id == 0 is returned on error. use original field signature.
+          if (name_id != 0) {
+            *inlined_fields_id = name_id;
+            name = reinterpret_cast<Symbol*>(name_id);
+          }
+        }
+        if (name == nullptr) {
+          name = fld.name();
+        }
 
-      writer->write_symbolID(fld.name());   // name
-      writer->write_u1(sig2tag(sig));       // type
+        writer->write_symbolID(name);         // name
+        writer->write_u1(sig2tag(sig));       // type
+      }
     }
   }
 }
 
 // creates HPROF_GC_INSTANCE_DUMP record for the given object
@@ -1176,11 +1365,11 @@
 
   // number of bytes that follow
   writer->write_u4(is);
 
   // field values
-  dump_instance_fields(writer, o, cache_entry);
+  dump_instance_fields(writer, o, 0, class_cache, cache_entry);
 
   writer->end_sub_record();
 }
 
 // creates HPROF_GC_CLASS_DUMP record for the given instance class
@@ -1221,11 +1410,11 @@
   // reserved
   writer->write_objectID(oop(nullptr));
   writer->write_objectID(oop(nullptr));
 
   // instance size
-  writer->write_u4(DumperSupport::instance_size(ik));
+  writer->write_u4(HeapWordSize * ik->size_helper());
 
   // size of constant pool - ignored by HAT 1.1
   writer->write_u2(0);
 
   // static fields
@@ -1275,36 +1464,43 @@
 
 }
 
 // Hprof uses an u4 as record length field,
 // which means we need to truncate arrays that are too long.
-int DumperSupport::calculate_array_max_length(AbstractDumpWriter* writer, arrayOop array, short header_size) {
-  BasicType type = ArrayKlass::cast(array->klass())->element_type();
-  assert(type >= T_BOOLEAN && type <= T_OBJECT, "invalid array element type");
-
+int DumperSupport::calculate_array_max_length(AbstractDumpWriter* writer, arrayOop array, int type_size, short header_size) {
   int length = array->length();
 
-  int type_size;
-  if (type == T_OBJECT) {
-    type_size = sizeof(address);
-  } else {
-    type_size = type2aelembytes(type);
-  }
-
   size_t length_in_bytes = (size_t)length * type_size;
   uint max_bytes = max_juint - header_size;
 
   if (length_in_bytes > max_bytes) {
     length = max_bytes / type_size;
     length_in_bytes = (size_t)length * type_size;
 
+    BasicType type = ArrayKlass::cast(array->klass())->element_type();
     warning("cannot dump array of type %s[] with length %d; truncating to length %d",
             type2name_tab[type], array->length(), length);
   }
   return length;
 }
 
+int DumperSupport::calculate_array_max_length(AbstractDumpWriter* writer, arrayOop array, short header_size) {
+  BasicType type = ArrayKlass::cast(array->klass())->element_type();
+  assert((type >= T_BOOLEAN && type <= T_OBJECT) || type == T_PRIMITIVE_OBJECT, "invalid array element type");
+  int type_size;
+  if (type == T_OBJECT) {
+    type_size = sizeof(address);
+  } else if (type == T_PRIMITIVE_OBJECT) {
+      // TODO: FIXME
+      fatal("Not supported yet"); // FIXME: JDK-8325678
+  } else {
+    type_size = type2aelembytes(type);
+  }
+
+  return calculate_array_max_length(writer, array, type_size, header_size);
+}
+
 // creates HPROF_GC_OBJ_ARRAY_DUMP record for the given object array
 void DumperSupport::dump_object_array(AbstractDumpWriter* writer, objArrayOop array) {
   // sizeof(u1) + 2 * sizeof(u4) + sizeof(objectID) + sizeof(classID)
   short header_size = 1 + 2 * 4 + 2 * sizeof(address);
   int length = calculate_array_max_length(writer, array, header_size);
@@ -1326,10 +1522,51 @@
   }
 
   writer->end_sub_record();
 }
 
+// creates HPROF_GC_PRIM_ARRAY_DUMP record for the given flat array
+void DumperSupport::dump_flat_array(AbstractDumpWriter* writer, flatArrayOop array, DumperClassCacheTable* class_cache) {
+  FlatArrayKlass* array_klass = FlatArrayKlass::cast(array->klass());
+  InlineKlass* element_klass = array_klass->element_klass();
+  int element_size = instance_size(element_klass);
+  /*                          id         array object ID
+   *                          u4         stack trace serial number
+   *                          u4         number of elements
+   *                          u1         element type
+   */
+  short header_size = 1 + sizeof(address) + 2 * 4 + 1;
+
+  // TODO: use T_SHORT/T_INT/T_LONG if needed to avoid truncation
+  BasicType type = T_BYTE;
+  int type_size = type2aelembytes(type);
+  int length = calculate_array_max_length(writer, array, element_size, header_size);
+  u4 length_in_bytes = (u4)(length * element_size);
+  u4 size = header_size + length_in_bytes;
+
+  writer->start_sub_record(HPROF_GC_PRIM_ARRAY_DUMP, size);
+  writer->write_objectID(array);
+  writer->write_u4(STACK_TRACE_ID);
+  // TODO: round up array length for T_SHORT/T_INT/T_LONG
+  writer->write_u4(length * element_size);
+  writer->write_u1(type2tag(type));
+
+  for (int index = 0; index < length; index++) {
+    // need offset in the holder to read inlined object. calculate it from flatArrayOop::value_at_addr()
+    int offset = (int)((address)array->value_at_addr(index, array_klass->layout_helper())
+                  - cast_from_oop<address>(array));
+    DumperClassCacheTableEntry* class_cache_entry = class_cache->lookup_or_create(element_klass);
+    dump_inlined_object_fields(writer, array, offset, class_cache, class_cache_entry);
+  }
+
+  // TODO: write padding bytes for T_SHORT/T_INT/T_LONG
+
+  InlinedObjects::get_instance()->add_flat_array(array);
+
+  writer->end_sub_record();
+}
+
 #define WRITE_ARRAY(Array, Type, Size, Length) \
   for (int i = 0; i < Length; i++) { writer->write_##Size((Size)Array->Type##_at(i)); }
 
 // creates HPROF_GC_PRIM_ARRAY_DUMP record for the given type array
 void DumperSupport::dump_prim_array(AbstractDumpWriter* writer, typeArrayOop array) {
@@ -1447,10 +1684,274 @@
   writer->write_u4(class_serial_num);               // class serial number
   writer->write_u4((u4) line_number);               // line number
 }
 
 
+class InlinedFieldNameDumper : public LockedClassesDo {
+public:
+  typedef void (*Callback)(InlinedObjects *owner, const Klass *klass, uintx base_index, int count);
+
+private:
+  AbstractDumpWriter* _writer;
+  InlinedObjects *_owner;
+  Callback       _callback;
+  uintx _index;
+
+  void dump_inlined_field_names(GrowableArray<Symbol*>* super_names, Symbol* field_name, InlineKlass* klass) {
+    super_names->push(field_name);
+    for (HierarchicalFieldStream<JavaFieldStream> fld(klass); !fld.done(); fld.next()) {
+      if (!fld.access_flags().is_static()) {
+        if (DumperSupport::is_inlined_field(fld.field_descriptor())) {
+          dump_inlined_field_names(super_names, fld.name(), DumperSupport::get_inlined_field_klass(fld.field_descriptor()));
+        } else {
+          // get next string ID.
+          uintx next_index = _owner->get_next_string_id(_index);
+          if (next_index == 0) {
+            // something went wrong (overflow?)
+            // stop generation; the rest of inlined objects will have original field names.
+            return;
+          }
+          _index = next_index;
+
+          // Calculate length.
+          int len = fld.name()->utf8_length();
+          for (GrowableArrayIterator<Symbol*> it = super_names->begin(); it != super_names->end(); ++it) {
+            len += (*it)->utf8_length() + 1;    // +1 for ".".
+          }
+
+          DumperSupport::write_header(_writer, HPROF_UTF8, oopSize + len);
+          _writer->write_symbolID(reinterpret_cast<Symbol*>(_index));
+          // Write the string value.
+          // 1) super_names.
+          for (GrowableArrayIterator<Symbol*> it = super_names->begin(); it != super_names->end(); ++it) {
+            _writer->write_raw((*it)->bytes(), (*it)->utf8_length());
+            _writer->write_u1('.');
+          }
+          // 2) field name.
+          _writer->write_raw(fld.name()->bytes(), fld.name()->utf8_length());
+        }
+      }
+    }
+    super_names->pop();
+  }
+
+  void dump_inlined_field_names(Symbol* field_name, InlineKlass* field_klass) {
+    GrowableArray<Symbol*> super_names(4, mtServiceability);
+    dump_inlined_field_names(&super_names, field_name, field_klass);
+  }
+
+public:
+  InlinedFieldNameDumper(AbstractDumpWriter* writer, InlinedObjects* owner, Callback callback)
+    : _writer(writer), _owner(owner), _callback(callback), _index(0)  {
+  }
+
+  void do_klass(Klass* k) {
+    if (!k->is_instance_klass()) {
+      return;
+    }
+    InstanceKlass* ik = InstanceKlass::cast(k);
+    // if (ik->has_inline_type_fields()) {
+    //   return;
+    // }
+
+    uintx base_index = _index;
+    int count = 0;
+
+    for (HierarchicalFieldStream<JavaFieldStream> fld(ik); !fld.done(); fld.next()) {
+      if (!fld.access_flags().is_static()) {
+        if (DumperSupport::is_inlined_field(fld.field_descriptor())) {
+          dump_inlined_field_names(fld.name(), DumperSupport::get_inlined_field_klass(fld.field_descriptor()));
+          count++;
+        }
+      }
+    }
+
+    if (count != 0) {
+      _callback(_owner, k, base_index, count);
+    }
+  }
+};
+
+class InlinedFieldsDumper : public LockedClassesDo {
+private:
+  AbstractDumpWriter* _writer;
+
+public:
+  InlinedFieldsDumper(AbstractDumpWriter* writer) : _writer(writer) {}
+
+  void do_klass(Klass* k) {
+    if (!k->is_instance_klass()) {
+      return;
+    }
+    InstanceKlass* ik = InstanceKlass::cast(k);
+    // if (ik->has_inline_type_fields()) {
+    //   return;
+    // }
+
+    // We can be at a point where java mirror does not exist yet.
+    // So we need to check that the class is at least loaded, to avoid crash from a null mirror.
+    if (!ik->is_loaded()) {
+      return;
+    }
+
+    u2 inlined_count = 0;
+    for (HierarchicalFieldStream<JavaFieldStream> fld(ik); !fld.done(); fld.next()) {
+      if (!fld.access_flags().is_static()) {
+        if (DumperSupport::is_inlined_field(fld.field_descriptor())) {
+          inlined_count++;
+        }
+      }
+    }
+    if (inlined_count != 0) {
+      _writer->write_u1(HPROF_CLASS_WITH_INLINED_FIELDS);
+
+      // class ID
+      _writer->write_classID(ik);
+      // number of inlined fields
+      _writer->write_u2(inlined_count);
+      u2 index = 0;
+      for (HierarchicalFieldStream<JavaFieldStream> fld(ik); !fld.done(); fld.next()) {
+        if (!fld.access_flags().is_static()) {
+          if (DumperSupport::is_inlined_field(fld.field_descriptor())) {
+            // inlined field index
+            _writer->write_u2(index);
+            // synthetic field count
+            u2 field_count = DumperSupport::get_instance_fields_count(DumperSupport::get_inlined_field_klass(fld.field_descriptor()));
+            _writer->write_u2(field_count);
+            // original field name
+            _writer->write_symbolID(fld.name());
+            // inlined field class ID
+            _writer->write_classID(DumperSupport::get_inlined_field_klass(fld.field_descriptor()));
+
+            index += field_count;
+          } else {
+            index++;
+          }
+        }
+      }
+    }
+  }
+};
+
+
+void InlinedObjects::init() {
+  _instance = this;
+
+  struct Closure : public SymbolClosure {
+    uintx _min_id = max_uintx;
+    uintx _max_id = 0;
+    Closure() : _min_id(max_uintx), _max_id(0) {}
+
+    void do_symbol(Symbol** p) {
+      uintx val = reinterpret_cast<uintx>(*p);
+      if (val < _min_id) {
+        _min_id = val;
+      }
+      if (val > _max_id) {
+        _max_id = val;
+      }
+    }
+  } closure;
+
+  SymbolTable::symbols_do(&closure);
+
+  _min_string_id = closure._min_id;
+  _max_string_id = closure._max_id;
+}
+
+void InlinedObjects::release() {
+  _instance = nullptr;
+
+  if (_inlined_field_map != nullptr) {
+    delete _inlined_field_map;
+    _inlined_field_map = nullptr;
+  }
+  if (_flat_arrays != nullptr) {
+    delete _flat_arrays;
+    _flat_arrays = nullptr;
+  }
+}
+
+void InlinedObjects::inlined_field_names_callback(InlinedObjects* _this, const Klass* klass, uintx base_index, int count) {
+  if (_this->_inlined_field_map == nullptr) {
+    _this->_inlined_field_map = new (mtServiceability) GrowableArray<ClassInlinedFields>(100, mtServiceability);
+  }
+  _this->_inlined_field_map->append(ClassInlinedFields(klass, base_index));
+
+  // counters for dumping classes with inlined fields
+  _this->_classes_count++;
+  _this->_inlined_fields_count += count;
+}
+
+void InlinedObjects::dump_inlined_field_names(AbstractDumpWriter* writer) {
+  InlinedFieldNameDumper nameDumper(writer, this, inlined_field_names_callback);
+  ClassLoaderDataGraph::classes_do(&nameDumper);
+
+  if (_inlined_field_map != nullptr) {
+    // prepare the map for  get_base_index_for().
+    _inlined_field_map->sort(ClassInlinedFields::compare);
+  }
+}
+
+uintx InlinedObjects::get_base_index_for(Klass* k) {
+  if (_inlined_field_map != nullptr) {
+    bool found = false;
+    int idx = _inlined_field_map->find_sorted<ClassInlinedFields, ClassInlinedFields::compare>(ClassInlinedFields(k, 0), found);
+    if (found) {
+        return _inlined_field_map->at(idx).base_index;
+    }
+  }
+
+  // return max_uintx, so get_next_string_id returns 0.
+  return max_uintx;
+}
+
+uintx InlinedObjects::get_next_string_id(uintx id) {
+  if (++id == _min_string_id) {
+    return _max_string_id + 1;
+  }
+  return id;
+}
+
+void InlinedObjects::dump_classed_with_inlined_fields(AbstractDumpWriter* writer) {
+  if (_classes_count != 0) {
+    // Record for each class contains tag(u1), class ID and count(u2)
+    // for each inlined field index(u2), synthetic fields count(u2), original field name and class ID
+    int size = _classes_count * (1 + sizeof(address) + 2)
+             + _inlined_fields_count * (2 + 2 + sizeof(address) + sizeof(address));
+    DumperSupport::write_header(writer, HPROF_INLINED_FIELDS, (u4)size);
+
+    InlinedFieldsDumper dumper(writer);
+    ClassLoaderDataGraph::classes_do(&dumper);
+  }
+}
+
+void InlinedObjects::add_flat_array(oop array) {
+  if (_flat_arrays == nullptr) {
+    _flat_arrays = new (mtServiceability) GrowableArray<oop>(100, mtServiceability);
+  }
+  _flat_arrays->append(array);
+}
+
+void InlinedObjects::dump_flat_arrays(AbstractDumpWriter* writer) {
+  if (_flat_arrays != nullptr) {
+    // For each flat array the record contains tag (u1), object ID and class ID.
+    int size = _flat_arrays->length() * (1 + sizeof(address) + sizeof(address));
+
+    DumperSupport::write_header(writer, HPROF_FLAT_ARRAYS, (u4)size);
+    for (GrowableArrayIterator<oop> it = _flat_arrays->begin(); it != _flat_arrays->end(); ++it) {
+      flatArrayOop array = flatArrayOop(*it);
+      FlatArrayKlass* array_klass = FlatArrayKlass::cast(array->klass());
+      InlineKlass* element_klass = array_klass->element_klass();
+      writer->write_u1(HPROF_FLAT_ARRAY);
+      writer->write_objectID(array);
+      writer->write_classID(element_klass);
+    }
+  }
+}
+
+
 // Support class used to generate HPROF_UTF8 records from the entries in the
 // SymbolTable.
 
 class SymbolTableDumper : public SymbolClosure {
  private:
@@ -1938,10 +2439,12 @@
       _vthread_dumper->dump_vthread(o, writer());
     }
   } else if (o->is_objArray()) {
     // create a HPROF_GC_OBJ_ARRAY_DUMP record for each object array
     DumperSupport::dump_object_array(writer(), objArrayOop(o));
+  } else if (o->is_flatArray()) {
+    DumperSupport::dump_flat_array(writer(), flatArrayOop(o), &_class_cache);
   } else if (o->is_typeArray()) {
     // create a HPROF_GC_PRIM_ARRAY_DUMP record for each type array
     DumperSupport::dump_prim_array(writer(), typeArrayOop(o));
   }
 }
@@ -2017,22 +2520,24 @@
 
 // DumpMerger merges separate dump files into a complete one
 class DumpMerger : public StackObj {
 private:
   DumpWriter* _writer;
+  InlinedObjects*  _inlined_objects;
   const char* _path;
   bool _has_error;
   int _dump_seq;
 
 private:
   void merge_file(const char* path);
   void merge_done();
   void set_error(const char* msg);
 
 public:
-  DumpMerger(const char* path, DumpWriter* writer, int dump_seq) :
+  DumpMerger(const char* path, DumpWriter* writer, InlinedObjects* inlined_objects, int dump_seq) :
     _writer(writer),
+    _inlined_objects(inlined_objects),
     _path(path),
     _has_error(_writer->has_error()),
     _dump_seq(dump_seq) {}
 
   void do_merge();
@@ -2060,11 +2565,13 @@
 
 void DumpMerger::merge_done() {
   // Writes the HPROF_HEAP_DUMP_END record.
   if (!_has_error) {
     DumperSupport::end_of_dump(_writer);
+    _inlined_objects->dump_flat_arrays(_writer);
     _writer->flush();
+    _inlined_objects->release();
   }
   _dump_seq = 0; //reset
 }
 
 void DumpMerger::set_error(const char* msg) {
@@ -2195,10 +2702,14 @@
   int                     _thread_dumpers_count;
   volatile int            _thread_serial_num;
   volatile int            _frame_serial_num;
 
   volatile int            _dump_seq;
+
+  // Inlined object support.
+  InlinedObjects          _inlined_objects;
+
   // parallel heap dump support
   uint                    _num_dumper_threads;
   DumperController*       _dumper_controller;
   ParallelObjectIterator* _poi;
 
@@ -2295,10 +2806,12 @@
   }
   int dump_seq()           { return _dump_seq; }
   bool is_parallel_dump()  { return _num_dumper_threads > 1; }
   void prepare_parallel_dump(WorkerThreads* workers);
 
+  InlinedObjects* inlined_objects() { return &_inlined_objects; }
+
   VMOp_Type type() const { return VMOp_HeapDumper; }
   virtual bool doit_prologue();
   void doit();
   void work(uint worker_id);
 
@@ -2469,10 +2982,17 @@
     writer()->write_u8(os::javaTimeMillis());
     // HPROF_UTF8 records
     SymbolTableDumper sym_dumper(writer());
     SymbolTable::symbols_do(&sym_dumper);
 
+    // HPROF_UTF8 records for inlined field names.
+    inlined_objects()->init();
+    inlined_objects()->dump_inlined_field_names(writer());
+
+    // HPROF_INLINED_FIELDS
+    inlined_objects()->dump_classed_with_inlined_fields(writer());
+
     // write HPROF_LOAD_CLASS records
     {
       LockedClassesDo locked_load_classes(&do_load_class);
       ClassLoaderDataGraph::classes_do(&locked_load_classes);
     }
@@ -2656,11 +3176,11 @@
   //          This is done by VM_HeapDumper, which is performed within safepoint.
   //
   // Phase 2: Merge multiple heap files into one complete heap dump file.
   //          This is done by DumpMerger, which is performed outside safepoint
 
-  DumpMerger merger(path, &writer, dumper.dump_seq());
+  DumpMerger merger(path, &writer, dumper.inlined_objects(), dumper.dump_seq());
   Thread* current_thread = Thread::current();
   if (current_thread->is_AttachListener_thread()) {
     // perform heapdump file merge operation in the current thread prevents us
     // from occupying the VM Thread, which in turn affects the occurrence of
     // GC and other VM operations.
diff a/src/hotspot/share/utilities/accessFlags.cpp b/src/hotspot/share/utilities/accessFlags.cpp
--- a/src/hotspot/share/utilities/accessFlags.cpp
+++ b/src/hotspot/share/utilities/accessFlags.cpp
@@ -40,10 +40,12 @@
   if (is_transient   ()) st->print("transient "   );
   if (is_native      ()) st->print("native "      );
   if (is_interface   ()) st->print("interface "   );
   if (is_abstract    ()) st->print("abstract "    );
   if (is_synthetic   ()) st->print("synthetic "   );
+  if (is_identity_class()) st->print("identity "  );
+  if (!is_identity_class()) st->print("value "    );
 }
 
 #endif // !PRODUCT || INCLUDE_JVMTI
 
 void accessFlags_init() {
diff a/src/hotspot/share/utilities/accessFlags.hpp b/src/hotspot/share/utilities/accessFlags.hpp
--- a/src/hotspot/share/utilities/accessFlags.hpp
+++ b/src/hotspot/share/utilities/accessFlags.hpp
@@ -64,17 +64,18 @@
   bool is_public      () const         { return (_flags & JVM_ACC_PUBLIC      ) != 0; }
   bool is_private     () const         { return (_flags & JVM_ACC_PRIVATE     ) != 0; }
   bool is_protected   () const         { return (_flags & JVM_ACC_PROTECTED   ) != 0; }
   bool is_static      () const         { return (_flags & JVM_ACC_STATIC      ) != 0; }
   bool is_final       () const         { return (_flags & JVM_ACC_FINAL       ) != 0; }
+  bool is_strict      () const         { return (_flags & JVM_ACC_STRICT      ) != 0; }
   bool is_synchronized() const         { return (_flags & JVM_ACC_SYNCHRONIZED) != 0; }
-  bool is_super       () const         { return (_flags & JVM_ACC_SUPER       ) != 0; }
   bool is_volatile    () const         { return (_flags & JVM_ACC_VOLATILE    ) != 0; }
   bool is_transient   () const         { return (_flags & JVM_ACC_TRANSIENT   ) != 0; }
   bool is_native      () const         { return (_flags & JVM_ACC_NATIVE      ) != 0; }
   bool is_interface   () const         { return (_flags & JVM_ACC_INTERFACE   ) != 0; }
   bool is_abstract    () const         { return (_flags & JVM_ACC_ABSTRACT    ) != 0; }
+  bool is_identity_class  () const     { return (_flags & JVM_ACC_IDENTITY    ) != 0; }
 
   // Attribute flags
   bool is_synthetic   () const         { return (_flags & JVM_ACC_SYNTHETIC   ) != 0; }
 
   // Klass* flags
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
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2018, 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.
@@ -33,11 +33,11 @@
 }
 
 #endif // PRODUCT
 
 BasicType constantTag::basic_type() const {
-  switch (_tag) {
+  switch (value()) {
     case JVM_CONSTANT_Integer :
       return T_INT;
     case JVM_CONSTANT_Float :
       return T_FLOAT;
     case JVM_CONSTANT_Long :
@@ -67,27 +67,27 @@
   }
 }
 
 
 jbyte constantTag::non_error_value() const {
-  switch (_tag) {
+  switch (value()) {
   case JVM_CONSTANT_UnresolvedClassInError:
     return JVM_CONSTANT_UnresolvedClass;
   case JVM_CONSTANT_MethodHandleInError:
     return JVM_CONSTANT_MethodHandle;
   case JVM_CONSTANT_MethodTypeInError:
     return JVM_CONSTANT_MethodType;
   case JVM_CONSTANT_DynamicInError:
     return JVM_CONSTANT_Dynamic;
   default:
-    return _tag;
+    return value();
   }
 }
 
 
 jbyte constantTag::error_value() const {
-  switch (_tag) {
+  switch (value()) {
   case JVM_CONSTANT_UnresolvedClass:
     return JVM_CONSTANT_UnresolvedClassInError;
   case JVM_CONSTANT_MethodHandle:
     return JVM_CONSTANT_MethodHandleInError;
   case JVM_CONSTANT_MethodType:
@@ -99,11 +99,11 @@
     return JVM_CONSTANT_Invalid;
   }
 }
 
 const char* constantTag::internal_name() const {
-  switch (_tag) {
+  switch (value()) {
     case JVM_CONSTANT_Invalid :
       return "Invalid index";
     case JVM_CONSTANT_Class :
       return "Class";
     case JVM_CONSTANT_Fieldref :
diff a/src/hotspot/share/utilities/constantTag.hpp b/src/hotspot/share/utilities/constantTag.hpp
--- a/src/hotspot/share/utilities/constantTag.hpp
+++ b/src/hotspot/share/utilities/constantTag.hpp
@@ -37,25 +37,24 @@
   // NOTE: replicated in SA in vm/agent/sun/jvm/hotspot/utilities/ConstantTag.java
   // Hotspot specific tags
   JVM_CONSTANT_Invalid                  = 0,    // For bad value initialization
   JVM_CONSTANT_InternalMin              = 100,  // First implementation tag (aside from bad value of course)
   JVM_CONSTANT_UnresolvedClass          = 100,  // Temporary tag until actual use
-  JVM_CONSTANT_ClassIndex               = 101,  // Temporary tag while constructing constant pool
-  JVM_CONSTANT_StringIndex              = 102,  // Temporary tag while constructing constant pool
+  JVM_CONSTANT_ClassIndex               = 101,  // Temporary tag while constructing constant pool, class redefinition
+  JVM_CONSTANT_StringIndex              = 102,  // Temporary tag while constructing constant pool, class redefinition
   JVM_CONSTANT_UnresolvedClassInError   = 103,  // Error tag due to resolution error
   JVM_CONSTANT_MethodHandleInError      = 104,  // Error tag due to resolution error
   JVM_CONSTANT_MethodTypeInError        = 105,  // Error tag due to resolution error
   JVM_CONSTANT_DynamicInError           = 106,  // Error tag due to resolution error
-  JVM_CONSTANT_InternalMax              = 106   // Last implementation tag
+  JVM_CONSTANT_InternalMax              = 106,  // Last implementation tag
 };
 
-
 class constantTag {
  private:
   jbyte _tag;
  public:
-  bool is_klass() const             { return _tag == JVM_CONSTANT_Class; }
+  bool is_klass() const             { return value() == JVM_CONSTANT_Class; }
   bool is_field () const            { return _tag == JVM_CONSTANT_Fieldref; }
   bool is_method() const            { return _tag == JVM_CONSTANT_Methodref; }
   bool is_interface_method() const  { return _tag == JVM_CONSTANT_InterfaceMethodref; }
   bool is_string() const            { return _tag == JVM_CONSTANT_String; }
   bool is_int() const               { return _tag == JVM_CONSTANT_Integer; }
@@ -66,15 +65,15 @@
   bool is_utf8() const              { return _tag == JVM_CONSTANT_Utf8; }
 
   bool is_invalid() const           { return _tag == JVM_CONSTANT_Invalid; }
 
   bool is_unresolved_klass() const {
-    return _tag == JVM_CONSTANT_UnresolvedClass || _tag == JVM_CONSTANT_UnresolvedClassInError;
+    return value() == JVM_CONSTANT_UnresolvedClass || value() == JVM_CONSTANT_UnresolvedClassInError;
   }
 
   bool is_unresolved_klass_in_error() const {
-    return _tag == JVM_CONSTANT_UnresolvedClassInError;
+    return value() == JVM_CONSTANT_UnresolvedClassInError;
   }
 
   bool is_method_handle_in_error() const {
     return _tag == JVM_CONSTANT_MethodHandleInError;
   }
@@ -147,10 +146,11 @@
         return JVM_CONSTANT_Invalid;
     }
   }
 
   jbyte value() const                { return _tag; }
+  jbyte tag() const                  { return _tag; }
   jbyte error_value() const;
   jbyte non_error_value() const;
 
   BasicType basic_type() const;        // if used with ldc, what kind of value gets pushed?
 
diff a/src/hotspot/share/utilities/exceptions.hpp b/src/hotspot/share/utilities/exceptions.hpp
--- a/src/hotspot/share/utilities/exceptions.hpp
+++ b/src/hotspot/share/utilities/exceptions.hpp
@@ -250,10 +250,12 @@
 // The THROW... macros should be used to throw an exception. They require a THREAD variable to be
 // visible within the scope containing the THROW. Usually this is achieved by declaring the function
 // with a TRAPS argument.
 
 #define THREAD_AND_LOCATION                      THREAD, __FILE__, __LINE__
+#define THREAD_AND_LOCATION_DECL                 TRAPS, const char* file, int line
+#define THREAD_AND_LOCATION_ARGS                 THREAD, file, line
 
 #define THROW_OOP(e)                                \
   { Exceptions::_throw_oop(THREAD_AND_LOCATION, e);                             return;  }
 
 #define THROW_HANDLE(e)                                \
diff a/src/hotspot/share/utilities/globalDefinitions.cpp b/src/hotspot/share/utilities/globalDefinitions.cpp
--- a/src/hotspot/share/utilities/globalDefinitions.cpp
+++ b/src/hotspot/share/utilities/globalDefinitions.cpp
@@ -114,11 +114,11 @@
       assert(char2type(type2char((BasicType)i)) == i, "proper inverses");
       assert(Signature::basic_type(type2char((BasicType)i)) == i, "proper inverses");
       num_type_chars++;
     }
   }
-  assert(num_type_chars == 11, "must have tested the right number of mappings");
+  assert(num_type_chars == 12, "must have tested the right number of mappings");
   assert(char2type(0) == T_ILLEGAL, "correct illegality");
 
   {
     for (int i = T_BOOLEAN; i <= T_CONFLICT; i++) {
       BasicType vt = (BasicType)i;
@@ -197,10 +197,11 @@
     BytesPerHeapOop    = BytesPerWord;
     BitsPerHeapOop     = BitsPerWord;
   }
   _type2aelembytes[T_OBJECT] = heapOopSize;
   _type2aelembytes[T_ARRAY]  = heapOopSize;
+  _type2aelembytes[T_PRIMITIVE_OBJECT]  = heapOopSize;
 }
 
 
 // Map BasicType to signature character
 char type2char_tab[T_CONFLICT+1] = {
@@ -208,12 +209,12 @@
   JVM_SIGNATURE_BOOLEAN, JVM_SIGNATURE_CHAR,
   JVM_SIGNATURE_FLOAT,   JVM_SIGNATURE_DOUBLE,
   JVM_SIGNATURE_BYTE,    JVM_SIGNATURE_SHORT,
   JVM_SIGNATURE_INT,     JVM_SIGNATURE_LONG,
   JVM_SIGNATURE_CLASS,   JVM_SIGNATURE_ARRAY,
-  JVM_SIGNATURE_VOID,    0,
-  0, 0, 0, 0
+  JVM_SIGNATURE_PRIMITIVE_OBJECT, JVM_SIGNATURE_VOID,
+  0, 0, 0, 0, 0
 };
 
 // Map BasicType to Java type name
 const char* type2name_tab[T_CONFLICT+1] = {
   nullptr, nullptr, nullptr, nullptr,
@@ -225,10 +226,11 @@
   "short",
   "int",
   "long",
   "object",
   "array",
+  "inline_type",
   "void",
   "*address*",
   "*narrowoop*",
   "*metadata*",
   "*narrowklass*",
@@ -255,11 +257,11 @@
   }
   return T_ILLEGAL;
 }
 
 // Map BasicType to size in words
-int type2size[T_CONFLICT+1]={ -1, 0, 0, 0, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 0, 1, 1, 1, 1, -1};
+int type2size[T_CONFLICT+1]={ -1, 0, 0, 0, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 0, 1, 1, 1, 1, -1};
 
 BasicType type2field[T_CONFLICT+1] = {
   (BasicType)0,            // 0,
   (BasicType)0,            // 1,
   (BasicType)0,            // 2,
@@ -272,16 +274,17 @@
   T_SHORT,                 // T_SHORT    =  9,
   T_INT,                   // T_INT      = 10,
   T_LONG,                  // T_LONG     = 11,
   T_OBJECT,                // T_OBJECT   = 12,
   T_OBJECT,                // T_ARRAY    = 13,
-  T_VOID,                  // T_VOID     = 14,
-  T_ADDRESS,               // T_ADDRESS  = 15,
-  T_NARROWOOP,             // T_NARROWOOP= 16,
-  T_METADATA,              // T_METADATA = 17,
-  T_NARROWKLASS,           // T_NARROWKLASS = 18,
-  T_CONFLICT               // T_CONFLICT = 19,
+  T_OBJECT,                // T_PRIMITIVE_OBJECT = 14,
+  T_VOID,                  // T_VOID     = 15,
+  T_ADDRESS,               // T_ADDRESS  = 16,
+  T_NARROWOOP,             // T_NARROWOOP= 17,
+  T_METADATA,              // T_METADATA = 18,
+  T_NARROWKLASS,           // T_NARROWKLASS = 19,
+  T_CONFLICT               // T_CONFLICT = 20
 };
 
 
 BasicType type2wfield[T_CONFLICT+1] = {
   (BasicType)0,            // 0,
@@ -296,16 +299,17 @@
   T_INT,     // T_SHORT    =  9,
   T_INT,     // T_INT      = 10,
   T_LONG,    // T_LONG     = 11,
   T_OBJECT,  // T_OBJECT   = 12,
   T_OBJECT,  // T_ARRAY    = 13,
-  T_VOID,    // T_VOID     = 14,
-  T_ADDRESS, // T_ADDRESS  = 15,
-  T_NARROWOOP, // T_NARROWOOP  = 16,
-  T_METADATA,  // T_METADATA   = 17,
-  T_NARROWKLASS, // T_NARROWKLASS  = 18,
-  T_CONFLICT // T_CONFLICT = 19,
+  T_OBJECT,  // T_PRIMITIVE_OBJECT = 14,
+  T_VOID,    // T_VOID     = 15,
+  T_ADDRESS, // T_ADDRESS  = 16,
+  T_NARROWOOP, // T_NARROWOOP  = 17,
+  T_METADATA,  // T_METADATA   = 18,
+  T_NARROWKLASS, // T_NARROWKLASS  = 19,
+  T_CONFLICT // T_CONFLICT = 20
 };
 
 
 int _type2aelembytes[T_CONFLICT+1] = {
   0,                         // 0
@@ -320,16 +324,17 @@
   T_SHORT_aelem_bytes,       // T_SHORT    =  9,
   T_INT_aelem_bytes,         // T_INT      = 10,
   T_LONG_aelem_bytes,        // T_LONG     = 11,
   T_OBJECT_aelem_bytes,      // T_OBJECT   = 12,
   T_ARRAY_aelem_bytes,       // T_ARRAY    = 13,
-  0,                         // T_VOID     = 14,
-  T_OBJECT_aelem_bytes,      // T_ADDRESS  = 15,
-  T_NARROWOOP_aelem_bytes,   // T_NARROWOOP= 16,
-  T_OBJECT_aelem_bytes,      // T_METADATA = 17,
-  T_NARROWKLASS_aelem_bytes, // T_NARROWKLASS= 18,
-  0                          // T_CONFLICT = 19,
+  T_PRIMITIVE_OBJECT_aelem_bytes, // T_PRIMITIVE_OBJECT = 14,
+  0,                         // T_VOID     = 15,
+  T_OBJECT_aelem_bytes,      // T_ADDRESS  = 16,
+  T_NARROWOOP_aelem_bytes,   // T_NARROWOOP= 17,
+  T_OBJECT_aelem_bytes,      // T_METADATA = 18,
+  T_NARROWKLASS_aelem_bytes, // T_NARROWKLASS= 19,
+  0                          // T_CONFLICT = 20
 };
 
 #ifdef ASSERT
 int type2aelembytes(BasicType t, bool allow_address) {
   assert((allow_address || t != T_ADDRESS) && t <= T_CONFLICT, "unexpected basic type");
diff a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp
--- a/src/hotspot/share/utilities/globalDefinitions.hpp
+++ b/src/hotspot/share/utilities/globalDefinitions.hpp
@@ -618,10 +618,19 @@
 // used to silence compiler warnings
 
 #define Unused_Variable(var) var
 
 
+//----------------------------------------------------------------------------------------------------
+// Prototyping
+// "Code Missing Here" macro, un-define when integrating back from prototyping stage and break
+// compilation on purpose (i.e. "forget me not")
+#define PROTOTYPE
+#ifdef PROTOTYPE
+#define CMH(m)
+#endif
+
 //----------------------------------------------------------------------------------------------------
 // Miscellaneous
 
 // 6302670 Eliminate Hotspot __fabsf dependency
 // All fabs() callers should call this function instead, which will implicitly
@@ -703,16 +712,17 @@
   // T_ADDRESS, T_METADATA, T_NARROWOOP, T_NARROWKLASS describe
   // internal references within the JVM as if they were Java
   // types in their own right.
   T_OBJECT      = 12,
   T_ARRAY       = 13,
-  T_VOID        = 14,
-  T_ADDRESS     = 15,
-  T_NARROWOOP   = 16,
-  T_METADATA    = 17,
-  T_NARROWKLASS = 18,
-  T_CONFLICT    = 19, // for stack value type with conflicting contents
+  T_PRIMITIVE_OBJECT = 14, // Not a true BasicType, only use in headers of flat arrays
+  T_VOID        = 15,
+  T_ADDRESS     = 16,
+  T_NARROWOOP   = 17,
+  T_METADATA    = 18,
+  T_NARROWKLASS = 19,
+  T_CONFLICT    = 20, // for stack value type with conflicting contents
   T_ILLEGAL     = 99
 };
 
 #define SIGNATURE_TYPES_DO(F, N)                \
     F(JVM_SIGNATURE_BOOLEAN, T_BOOLEAN, N)      \
@@ -723,10 +733,11 @@
     F(JVM_SIGNATURE_SHORT,   T_SHORT,   N)      \
     F(JVM_SIGNATURE_INT,     T_INT,     N)      \
     F(JVM_SIGNATURE_LONG,    T_LONG,    N)      \
     F(JVM_SIGNATURE_CLASS,   T_OBJECT,  N)      \
     F(JVM_SIGNATURE_ARRAY,   T_ARRAY,   N)      \
+    F(JVM_SIGNATURE_PRIMITIVE_OBJECT, T_PRIMITIVE_OBJECT, N) \
     F(JVM_SIGNATURE_VOID,    T_VOID,    N)      \
     /*end*/
 
 inline bool is_java_type(BasicType t) {
   return T_BOOLEAN <= t && t <= T_VOID;
@@ -752,11 +763,12 @@
 inline bool is_double_word_type(BasicType t) {
   return (t == T_DOUBLE || t == T_LONG);
 }
 
 inline bool is_reference_type(BasicType t, bool include_narrow_oop = false) {
-  return (t == T_OBJECT || t == T_ARRAY || (include_narrow_oop && t == T_NARROWOOP));
+  // TODO 8325106 Remove the last occurences of T_PRIMITIVE_OBJECT
+  return (t == T_OBJECT || t == T_ARRAY || t == T_PRIMITIVE_OBJECT || (include_narrow_oop && t == T_NARROWOOP));
 }
 
 inline bool is_integral_type(BasicType t) {
   return is_subword_type(t) || t == T_INT || t == T_LONG;
 }
@@ -810,11 +822,12 @@
   T_LONG_size        = 2,
   T_OBJECT_size      = 1,
   T_ARRAY_size       = 1,
   T_NARROWOOP_size   = 1,
   T_NARROWKLASS_size = 1,
-  T_VOID_size        = 0
+  T_VOID_size        = 0,
+  T_PRIMITIVE_OBJECT_size = 1
 };
 
 // this works on valid parameter types but not T_VOID, T_CONFLICT, etc.
 inline int parameter_type_word_count(BasicType t) {
   if (is_double_word_type(t))  return 2;
@@ -840,13 +853,15 @@
   T_INT_aelem_bytes         = 4,
   T_LONG_aelem_bytes        = 8,
 #ifdef _LP64
   T_OBJECT_aelem_bytes      = 8,
   T_ARRAY_aelem_bytes       = 8,
+  T_PRIMITIVE_OBJECT_aelem_bytes = 8,
 #else
   T_OBJECT_aelem_bytes      = 4,
   T_ARRAY_aelem_bytes       = 4,
+  T_PRIMITIVE_OBJECT_aelem_bytes = 4,
 #endif
   T_NARROWOOP_aelem_bytes   = 4,
   T_NARROWKLASS_aelem_bytes = 4,
   T_VOID_aelem_bytes        = 0
 };
@@ -935,11 +950,11 @@
   itos = 4,             // int tos cached
   ltos = 5,             // long tos cached
   ftos = 6,             // float tos cached
   dtos = 7,             // double tos cached
   atos = 8,             // object cached
-  vtos = 9,             // tos not cached
+  vtos = 9,             // tos not cached,
   number_of_states,
   ilgl                  // illegal state: should not occur
 };
 
 
@@ -952,11 +967,11 @@
     case T_INT    : return itos;
     case T_LONG   : return ltos;
     case T_FLOAT  : return ftos;
     case T_DOUBLE : return dtos;
     case T_VOID   : return vtos;
-    case T_ARRAY  : // fall through
+    case T_ARRAY  :   // fall through
     case T_OBJECT : return atos;
     default       : return ilgl;
   }
 }
 
@@ -1323,10 +1338,16 @@
 
 template<typename K> bool primitive_equals(const K& k0, const K& k1) {
   return k0 == k1;
 }
 
+// TEMP!!!!
+// This should be removed after LW2 arrays are implemented (JDK-8220790).
+// It's an alias to (EnableValhalla && (FlatArrayElementMaxSize != 0)),
+// which is actually not 100% correct, but works for the current set of C1/C2
+// implementation and test cases.
+#define UseFlatArray (EnableValhalla && (FlatArrayElementMaxSize != 0))
 template<typename K> int primitive_compare(const K& k0, const K& k1) {
   return ((k0 < k1) ? -1 : (k0 == k1) ? 0 : 1);
 }
 
 //----------------------------------------------------------------------------------------------------
diff a/src/hotspot/share/utilities/growableArray.hpp b/src/hotspot/share/utilities/growableArray.hpp
--- a/src/hotspot/share/utilities/growableArray.hpp
+++ b/src/hotspot/share/utilities/growableArray.hpp
@@ -24,10 +24,12 @@
 
 #ifndef SHARE_UTILITIES_GROWABLEARRAY_HPP
 #define SHARE_UTILITIES_GROWABLEARRAY_HPP
 
 #include "memory/allocation.hpp"
+#include "oops/array.hpp"
+#include "oops/oop.hpp"
 #include "memory/iterator.hpp"
 #include "utilities/debug.hpp"
 #include "utilities/globalDefinitions.hpp"
 #include "utilities/ostream.hpp"
 #include "utilities/powerOfTwo.hpp"
@@ -468,10 +470,16 @@
     for (int i = 0; i < l->length(); i++) {
       this->at_put_grow(this->_len, l->at(i), E());
     }
   }
 
+  void appendAll(const Array<E>* l) {
+    for (int i = 0; i < l->length(); i++) {
+      this->at_put_grow(this->_len, l->at(i), E());
+    }
+  }
+
   // Binary search and insertion utility.  Search array for element
   // matching key according to the static compare function.  Insert
   // that element if not already in the list.  Assumes the list is
   // already sorted according to compare function.
   template <int compare(const E&, const E&)> E insert_sorted(const E& key) {
@@ -882,23 +890,23 @@
   const GrowableArrayView<E>* _array; // GrowableArray we iterate over
   int _position;                      // Current position in the GrowableArray
   UnaryPredicate _predicate;          // Unary predicate the elements of the GrowableArray should satisfy
 
  public:
-  GrowableArrayFilterIterator(const GrowableArrayIterator<E>& begin, UnaryPredicate filter_predicate) :
-      _array(begin._array), _position(begin._position), _predicate(filter_predicate) {
+  GrowableArrayFilterIterator(const GrowableArray<E>* array, UnaryPredicate filter_predicate) :
+      _array(array), _position(0), _predicate(filter_predicate) {
     // Advance to first element satisfying the predicate
-    while(_position != _array->length() && !_predicate(_array->at(_position))) {
+    while(!at_end() && !_predicate(_array->at(_position))) {
       ++_position;
     }
   }
 
   GrowableArrayFilterIterator<E, UnaryPredicate>& operator++() {
     do {
       // Advance to next element satisfying the predicate
       ++_position;
-    } while(_position != _array->length() && !_predicate(_array->at(_position)));
+    } while(!at_end() && !_predicate(_array->at(_position)));
     return *this;
   }
 
   E operator*() { return _array->at(_position); }
 
@@ -919,10 +927,14 @@
 
   bool operator!=(const GrowableArrayFilterIterator<E, UnaryPredicate>& rhs)  {
     assert(_array == rhs._array, "iterator belongs to different array");
     return _position != rhs._position;
   }
+
+  bool at_end() const {
+    return _array == nullptr || _position == _array->end()._position;
+  }
 };
 
 // Arrays for basic types
 typedef GrowableArray<int> intArray;
 typedef GrowableArray<int> intStack;
diff a/src/hotspot/share/utilities/stringUtils.cpp b/src/hotspot/share/utilities/stringUtils.cpp
--- a/src/hotspot/share/utilities/stringUtils.cpp
+++ b/src/hotspot/share/utilities/stringUtils.cpp
@@ -23,10 +23,11 @@
  */
 
 #include "precompiled.hpp"
 #include "jvm_io.h"
 #include "utilities/debug.hpp"
+#include "utilities/ostream.hpp"
 #include "utilities/stringUtils.hpp"
 
 #include <ctype.h>
 #include <string.h>
 
@@ -68,10 +69,225 @@
   }
 
   return 2.0 * (double) hit / (double) total;
 }
 
+class StringMatcher {
+ public:
+  typedef int getc_function_t(const char* &source, const char* limit);
+
+ private:
+  // These do not get properly inlined.
+  // For full performance, this should be a template class
+  // parameterized by two function arguments.
+  getc_function_t* _pattern_getc;
+  getc_function_t* _string_getc;
+
+ public:
+  StringMatcher(getc_function_t pattern_getc,
+                getc_function_t string_getc)
+    : _pattern_getc(pattern_getc),
+      _string_getc(string_getc)
+  { }
+
+  enum {  // special results from _pattern_getc
+    string_match_comma  = -0x100 + ',',
+    string_match_star   = -0x100 + '*',
+    string_match_eos    = -0x100 + '\0'
+  };
+
+ private:
+  const char*
+  skip_anchor_word(const char* match,
+                   const char* match_end,
+                   int anchor_length,
+                   const char* pattern,
+                   const char* pattern_end) {
+    assert(pattern < pattern_end && anchor_length > 0, "");
+    const char* begp = pattern;
+    int ch1 = _pattern_getc(begp, pattern_end);
+    // note that begp is now advanced over ch1
+    assert(ch1 > 0, "regular char only");
+    const char* matchp = match;
+    const char* limitp = match_end - anchor_length;
+    while (matchp <= limitp) {
+      int mch = _string_getc(matchp, match_end);
+      if (mch == ch1) {
+        const char* patp = begp;
+        const char* anchorp = matchp;
+        while (patp < pattern_end) {
+          char ch = _pattern_getc(patp, pattern_end);
+          char mch = _string_getc(anchorp, match_end);
+          if (mch != ch) {
+            anchorp = nullptr;
+            break;
+          }
+        }
+        if (anchorp != nullptr) {
+          return anchorp;  // Found a full copy of the anchor.
+        }
+        // That did not work, so restart the search for ch1.
+      }
+    }
+    return nullptr;
+  }
+
+ public:
+  bool string_match(const char* pattern,
+                    const char* string) {
+    return string_match(pattern, pattern + strlen(pattern),
+                        string, string + strlen(string));
+  }
+  bool string_match(const char* pattern, const char* pattern_end,
+                    const char* string, const char* string_end) {
+    const char* patp = pattern;
+    switch (_pattern_getc(patp, pattern_end)) {
+    case string_match_eos:
+      return false;  // Empty pattern is always false.
+    case string_match_star:
+      if (patp == pattern_end) {
+        return true;   // Lone star pattern is always true.
+      }
+      break;
+    }
+    patp = pattern;  // Reset after lookahead.
+    const char* matchp = string;  // nullptr if failing
+    for (;;) {
+      int ch = _pattern_getc(patp, pattern_end);
+      switch (ch) {
+      case string_match_eos:
+      case string_match_comma:
+        // End of a list item; see if it's a match.
+        if (matchp == string_end) {
+          return true;
+        }
+        if (ch == string_match_comma) {
+          // Get ready to match the next item.
+          matchp = string;
+          continue;
+        }
+        return false;  // End of all items.
+
+      case string_match_star:
+        if (matchp != nullptr) {
+          // Wildcard:  Parse out following anchor word and look for it.
+          const char* begp = patp;
+          const char* endp = patp;
+          int anchor_len = 0;
+          for (;;) {
+            // get as many following regular characters as possible
+            endp = patp;
+            ch = _pattern_getc(patp, pattern_end);
+            if (ch <= 0) {
+              break;
+            }
+            anchor_len += 1;
+          }
+          // Anchor word [begp..endp) does not contain ch, so back up.
+          // Now do an eager match to the anchor word, and commit to it.
+          patp = endp;
+          if (ch == string_match_eos ||
+              ch == string_match_comma) {
+            // Anchor word is at end of pattern, so treat it as a fixed pattern.
+            const char* limitp = string_end - anchor_len;
+            matchp = limitp;
+            patp = begp;
+            // Resume normal scanning at the only possible match position.
+            continue;
+          }
+          // Find a floating occurrence of the anchor and continue matching.
+          // Note:  This is greedy; there is no backtrack here.  Good enough.
+          matchp = skip_anchor_word(matchp, string_end, anchor_len, begp, endp);
+        }
+        continue;
+      }
+      // Normal character.
+      if (matchp != nullptr) {
+        int mch = _string_getc(matchp, string_end);
+        if (mch != ch) {
+          matchp = nullptr;
+        }
+      }
+    }
+  }
+};
+
+// Match a wildcarded class list to a proposed class name (in internal form).
+// Commas or newlines separate multiple possible matches; stars are shell-style wildcards.
+class ClassListMatcher : public StringMatcher {
+ public:
+  ClassListMatcher()
+    : StringMatcher(pattern_list_getc, class_name_getc)
+  { }
+
+ private:
+  static int pattern_list_getc(const char* &pattern_ptr,
+                               const char* pattern_end) {
+    if (pattern_ptr == pattern_end) {
+      return string_match_eos;
+    }
+    int ch = (unsigned char) *pattern_ptr++;
+    switch (ch) {
+    case ' ': case '\t': case '\n': case '\r':
+    case ',':
+      // End of list item.
+      for (;;) {
+        switch (*pattern_ptr) {
+        case ' ': case '\t': case '\n': case '\r':
+        case ',':
+          pattern_ptr += 1;  // Collapse multiple commas or spaces.
+          continue;
+        }
+        break;
+      }
+      return string_match_comma;
+
+    case '*':
+      // Wildcard, matching any number of chars.
+      while (*pattern_ptr == '*') {
+        pattern_ptr += 1;  // Collapse multiple stars.
+      }
+      return string_match_star;
+
+    case '.':
+      ch = '/';   // Look for internal form of package separator
+      break;
+
+    case '\\':
+      // Superquote in pattern escapes * , whitespace, and itself.
+      if (pattern_ptr < pattern_end) {
+        ch = (unsigned char) *pattern_ptr++;
+      }
+      break;
+    }
+
+    assert(ch > 0, "regular char only");
+    return ch;
+  }
+
+  static int class_name_getc(const char* &name_ptr,
+                             const char* name_end) {
+    if (name_ptr == name_end) {
+      return string_match_eos;
+    }
+    int ch = (unsigned char) *name_ptr++;
+    if (ch == '.') {
+      ch = '/';   // Normalize to internal form of package separator
+    }
+    return ch;  // plain character
+  }
+};
+
+bool StringUtils::class_list_match(const char* class_pattern_list,
+                                   const char* class_name) {
+  if (class_pattern_list == nullptr || class_name == nullptr || class_name[0] == '\0')
+    return false;
+  ClassListMatcher clm;
+  return clm.string_match(class_pattern_list, class_name);
+}
+
+
 const char* StringUtils::strstr_nocase(const char* haystack, const char* needle) {
   if (needle[0] == '\0') {
     return haystack; // empty needle matches with anything
   }
   for (size_t i = 0; haystack[i] != '\0'; i++) {
diff a/src/hotspot/share/utilities/stringUtils.hpp b/src/hotspot/share/utilities/stringUtils.hpp
--- a/src/hotspot/share/utilities/stringUtils.hpp
+++ b/src/hotspot/share/utilities/stringUtils.hpp
@@ -39,10 +39,14 @@
   static int replace_no_expand(char* string, const char* from, const char* to);
 
   // Compute string similarity based on Dice's coefficient
   static double similarity(const char* str1, size_t len1, const char* str2, size_t len2);
 
+  // Match a wildcarded class list to a proposed class name (in internal form).
+  // Commas separate multiple possible matches; stars are shell-style wildcards.
+  static bool class_list_match(const char* class_list, const char* class_name);
+
   // Find needle in haystack, case insensitive.
   // Custom implementation of strcasestr, as it is not available on windows.
   static const char* strstr_nocase(const char* haystack, const char* needle);
 
   // Check if str matches the star_pattern.
diff a/src/java.base/share/classes/java/io/Externalizable.java b/src/java.base/share/classes/java/io/Externalizable.java
--- a/src/java.base/share/classes/java/io/Externalizable.java
+++ b/src/java.base/share/classes/java/io/Externalizable.java
@@ -49,13 +49,19 @@
  * ObjectOutputStream. <br> When an Externalizable object is
  * reconstructed, an instance is created using the public no-arg
  * constructor, then the readExternal method called.  Serializable
  * objects are restored by reading them from an ObjectInputStream.<br>
  *
- * An Externalizable instance can designate a substitution object via
+ * An Externalizable object can designate a substitution object via
  * the writeReplace and readResolve methods documented in the Serializable
- * interface.<br>
+ * interface. To control the serialized form of Records and Externalizable value classes
+ * the record or class uses {@link Serializable} {@code writeReplace}
+ * to delegate to another serializable or externalizable class and that delegate
+ * uses {@code readResolve} to supply the replacement on deserialization.
+ * Value classes implementing {@link Externalizable}
+ * and not using {@code writeReplace} are not supported.
+ * <br>
  *
  * @see java.io.ObjectOutputStream
  * @see java.io.ObjectInputStream
  * @see java.io.ObjectOutput
  * @see java.io.ObjectInput
diff a/src/java.base/share/classes/java/io/ObjectInputStream.java b/src/java.base/share/classes/java/io/ObjectInputStream.java
--- a/src/java.base/share/classes/java/io/ObjectInputStream.java
+++ b/src/java.base/share/classes/java/io/ObjectInputStream.java
@@ -215,10 +215,12 @@
  * form.  The methods of the Externalizable interface, writeExternal and
  * readExternal, are called to save and restore the objects state.  When
  * implemented by a class they can write and read their own state using all of
  * the methods of ObjectOutput and ObjectInput.  It is the responsibility of
  * the objects to handle any versioning that occurs.
+ * Value objects cannot be `java.io.Externalizable` because value objects are
+ * immutable and `Externalizable.readExternal` is unable to modify the fields of the value.
  *
  * <p>Enum constants are deserialized differently than ordinary serializable or
  * externalizable objects.  The serialized form of an enum constant consists
  * solely of its name; field values of the constant are not transmitted.  To
  * deserialize an enum constant, ObjectInputStream reads the constant name from
@@ -240,10 +242,15 @@
  * as readObject and writeObject, are ignored for serializable records. See
  * <a href="{@docRoot}/../specs/serialization/serial-arch.html#serialization-of-records">
  * <cite>Java Object Serialization Specification,</cite> Section 1.13,
  * "Serialization of Records"</a> for additional information.
  *
+ * <p>Value objects are deserialized differently than ordinary serializable objects or records.
+ * See <a href="{@docRoot}/../specs/serialization/serial-arch.html#serialization-of-value-objects">
+ * <cite>Java Object Serialization Specification,</cite> Section 1.14,
+ * "Serialization of Value Objects"</a> for additional information.
+ *
  * @spec serialization/index.html Java Object Serialization Specification
  * @author      Mike Warres
  * @author      Roger Riggs
  * @see java.io.DataInput
  * @see java.io.ObjectOutputStream
@@ -2249,11 +2256,12 @@
         } catch (Exception ex) {
             throw new InvalidClassException(desc.forClass().getName(),
                                             "unable to create instance", ex);
         }
 
-        passHandle = handles.assign(unshared ? unsharedMarker : obj);
+        // Assign the handle and initially set to null or the unsharedMarker
+        passHandle = handles.assign(unshared ? unsharedMarker : null);
         ClassNotFoundException resolveEx = desc.getResolveException();
         if (resolveEx != null) {
             handles.markException(passHandle, resolveEx);
         }
 
@@ -2262,12 +2270,28 @@
             assert obj == null;
             obj = readRecord(desc);
             if (!unshared)
                 handles.setObject(passHandle, obj);
         } else if (desc.isExternalizable()) {
+            if (desc.isValue()) {
+                throw new NotSerializableException("Externalizable not valid for value class "
+                        + cl.getName());
+            }
+            if (!unshared)
+                handles.setObject(passHandle, obj);
             readExternalData((Externalizable) obj, desc);
+        } else if (desc.isValue()) {
+            // For value objects, read the fields and finish the buffer before publishing the ref
+            assert obj != null : "obj == null: " + desc;
+            readSerialData(obj, desc);
+            obj = desc.finishValue(obj);
+            if (!unshared)
+                handles.setObject(passHandle, obj);
         } else {
+            // For all other objects, publish the ref and then read the data
+            if (!unshared)
+                handles.setObject(passHandle, obj);
             readSerialData(obj, desc);
         }
 
         handles.finish(passHandle);
 
diff a/src/java.base/share/classes/java/io/ObjectOutputStream.java b/src/java.base/share/classes/java/io/ObjectOutputStream.java
--- a/src/java.base/share/classes/java/io/ObjectOutputStream.java
+++ b/src/java.base/share/classes/java/io/ObjectOutputStream.java
@@ -125,10 +125,16 @@
  * form.  The methods of the Externalizable interface, writeExternal and
  * readExternal, are called to save and restore the objects state.  When
  * implemented by a class they can write and read their own state using all of
  * the methods of ObjectOutput and ObjectInput.  It is the responsibility of
  * the objects to handle any versioning that occurs.
+ * Value classes implementing {@link Externalizable} cannot be serialized
+ * or deserialized, the value object is immutable and the state cannot be restored.
+ * Use {@link Serializable} {@code writeReplace} to delegate to another serializable
+ * object such as a record.
+ *
+ * Value objects cannot be {@code java.io.Externalizable}.
  *
  * <p>Enum constants are serialized differently than ordinary serializable or
  * externalizable objects.  The serialized form of an enum constant consists
  * solely of its name; field values of the constant are not transmitted.  To
  * serialize an enum constant, ObjectOutputStream writes the string returned by
@@ -1444,10 +1450,13 @@
             handles.assign(unshared ? null : obj);
 
             if (desc.isRecord()) {
                 writeRecordData(obj, desc);
             } else if (desc.isExternalizable() && !desc.isProxy()) {
+                if (desc.forClass().isValue())
+                    throw new NotSerializableException("Externalizable not valid for value class "
+                            + desc.forClass().getName());
                 writeExternalData((Externalizable) obj);
             } else {
                 writeSerialData(obj, desc);
             }
         } finally {
diff a/src/java.base/share/classes/java/io/ObjectStreamClass.java b/src/java.base/share/classes/java/io/ObjectStreamClass.java
--- a/src/java.base/share/classes/java/io/ObjectStreamClass.java
+++ b/src/java.base/share/classes/java/io/ObjectStreamClass.java
@@ -28,10 +28,11 @@
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
+import java.lang.reflect.InaccessibleObjectException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.RecordComponent;
 import java.lang.reflect.UndeclaredThrowableException;
 import java.lang.reflect.Member;
 import java.lang.reflect.Method;
@@ -69,11 +70,11 @@
  * serialVersionUID of the class.  The ObjectStreamClass for a specific class
  * loaded in this Java VM can be found/created using the lookup method.
  *
  * <p>The algorithm to compute the SerialVersionUID is described in
  * <a href="{@docRoot}/../specs/serialization/class.html#stream-unique-identifiers">
- *    <cite>Java Object Serialization Specification,</cite> Section 4.6, "Stream Unique Identifiers"</a>.
+ *    <cite>Java Object Serialization Specification</cite>, Section 4.6, "Stream Unique Identifiers"</a>.
  *
  * @spec serialization/index.html Java Object Serialization Specification
  * @author      Mike Warres
  * @author      Roger Riggs
  * @see ObjectStreamField
@@ -133,10 +134,12 @@
     private boolean isProxy;
     /** true if represents enum type */
     private boolean isEnum;
     /** true if represents record type */
     private boolean isRecord;
+    /** true if represents a value class */
+    private boolean isValue;
     /** true if represented class implements Serializable */
     private boolean serializable;
     /** true if represented class implements Externalizable */
     private boolean externalizable;
     /** true if desc has data written by class-defined writeObject method */
@@ -372,10 +375,11 @@
         this.cl = cl;
         name = cl.getName();
         isProxy = Proxy.isProxyClass(cl);
         isEnum = Enum.class.isAssignableFrom(cl);
         isRecord = cl.isRecord();
+        isValue = cl.isValue();
         serializable = Serializable.class.isAssignableFrom(cl);
         externalizable = Externalizable.class.isAssignableFrom(cl);
 
         Class<?> superCl = cl.getSuperclass();
         superDesc = (superCl != null) ? lookup(superCl, false) : null;
@@ -405,10 +409,13 @@
                     }
 
                     if (isRecord) {
                         canonicalCtr = canonicalRecordCtr(cl);
                         deserializationCtrs = new DeserializationConstructorsCache();
+                    } else if (isValue) {
+                        // Value objects are created using Unsafe.
+                        cons = null;
                     } else if (externalizable) {
                         cons = getExternalizableConstructor(cl);
                     } else {
                         cons = getSerializableConstructor(cl);
                         writeObjectMethod = getPrivateMethod(cl, "writeObject",
@@ -442,11 +449,11 @@
         }
 
         if (deserializeEx == null) {
             if (isEnum) {
                 deserializeEx = new ExceptionInfo(name, "enum type");
-            } else if (cons == null && !isRecord) {
+            } else if (cons == null && !(isRecord | isValue)) {
                 deserializeEx = new ExceptionInfo(name, "no valid constructor");
             }
         }
         if (isRecord && canonicalCtr == null) {
             deserializeEx = new ExceptionInfo(name, "record canonical constructor not found");
@@ -636,10 +643,11 @@
         numObjFields = model.numObjFields;
 
         if (osc != null) {
             localDesc = osc;
             isRecord = localDesc.isRecord;
+            isValue = localDesc.isValue;
             // canonical record constructor is shared
             canonicalCtr = localDesc.canonicalCtr;
             // cache of deserialization constructors is shared
             deserializationCtrs = localDesc.deserializationCtrs;
             writeObjectMethod = localDesc.writeObjectMethod;
@@ -917,10 +925,18 @@
     boolean isSerializable() {
         requireInitialized();
         return serializable;
     }
 
+    /**
+     * {@return {code true} if the class is a value class, {@code false} otherwise}
+     */
+    boolean isValue() {
+        requireInitialized();
+        return isValue;
+    }
+
     /**
      * Returns true if class descriptor represents externalizable class that
      * has written its data in 1.2 (block data) format, false otherwise.
      */
     boolean hasBlockExternalData() {
@@ -939,17 +955,18 @@
     }
 
     /**
      * Returns true if represented class is serializable/externalizable and can
      * be instantiated by the serialization runtime--i.e., if it is
-     * externalizable and defines a public no-arg constructor, or if it is
+     * externalizable and defines a public no-arg constructor, if it is
      * non-externalizable and its first non-serializable superclass defines an
-     * accessible no-arg constructor.  Otherwise, returns false.
+     * accessible no-arg constructor, or if the class is a value class.
+     * Otherwise, returns false.
      */
     boolean isInstantiable() {
         requireInitialized();
-        return (cons != null);
+        return (cons != null | isValue);
     }
 
     /**
      * Returns true if represented class is serializable (but not
      * externalizable) and defines a conformant writeObject method.  Otherwise,
@@ -1056,15 +1073,27 @@
             } catch (InstantiationError err) {
                 var ex = new InstantiationException();
                 ex.initCause(err);
                 throw ex;
             }
-        } else {
+        } else if (isValue) {
+            // Start with a buffered default value.
+            return FieldReflector.newValueInstance(cl);
+        }  else {
             throw new UnsupportedOperationException();
         }
     }
 
+    /**
+     * Finish the initialization of a value object.
+     * @param obj an object (larval if a value object)
+     * @return the finished object
+     */
+    Object finishValue(Object obj) {
+        return (isValue) ? FieldReflector.finishValueInstance(obj) : obj;
+    }
+
     /**
      * Invokes the writeObject method of the represented serializable class.
      * Throws UnsupportedOperationException if this class descriptor is not
      * associated with a class, or if the class is externalizable,
      * non-serializable or does not define writeObject.
@@ -1435,11 +1464,11 @@
         try {
             Constructor<?> cons = cl.getDeclaredConstructor((Class<?>[]) null);
             cons.setAccessible(true);
             return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ?
                 cons : null;
-        } catch (NoSuchMethodException ex) {
+        } catch (NoSuchMethodException | InaccessibleObjectException ex) {
             return null;
         }
     }
 
     /**
@@ -1928,10 +1957,33 @@
         /** field type codes */
         private final char[] typeCodes;
         /** field types */
         private final Class<?>[] types;
 
+        /**
+         * Return a new instance of the class using Unsafe.uninitializedDefaultValue
+         * and buffer it.
+         * @param clazz The value class
+         * @return a buffered default value
+         */
+        static Object newValueInstance(Class<?> clazz) throws InstantiationException{
+            assert clazz.isValue() : "Should be a value class";
+            // may not be implicitly constructible; so allocate with Unsafe
+            Object obj = UNSAFE.uninitializedDefaultValue(clazz);
+            return UNSAFE.makePrivateBuffer(obj);
+        }
+
+        /**
+         * Finish a value object, clear the larval state and returning the value object.
+         * @param obj a buffered value object in a larval state
+         * @return the finished value object
+         */
+        static Object finishValueInstance(Object obj) {
+            assert (obj.getClass().isValue()) : "Should be a value class";
+            return UNSAFE.finishPrivateBuffer(obj);
+        }
+
         /**
          * Constructs FieldReflector capable of setting/getting values from the
          * subset of fields whose ObjectStreamFields contain non-null
          * reflective Field objects.  ObjectStreamFields with null Fields are
          * treated as filler, for which get operations return default values
@@ -2048,12 +2100,16 @@
             /* assuming checkDefaultSerialize() has been called on the class
              * descriptor this FieldReflector was obtained from, no field keys
              * in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
              */
             for (int i = numPrimFields; i < fields.length; i++) {
+                Field f = fields[i].getField();
                 vals[offsets[i]] = switch (typeCodes[i]) {
-                    case 'L', '[' -> UNSAFE.getReference(obj, readKeys[i]);
+                    case 'L', '[' ->
+                            UNSAFE.isFlatField(f)
+                                    ? UNSAFE.getValue(obj, readKeys[i], f.getType())
+                                    : UNSAFE.getReference(obj, readKeys[i]);
                     default       -> throw new InternalError();
                 };
             }
         }
 
@@ -2086,25 +2142,30 @@
                 if (key == Unsafe.INVALID_FIELD_OFFSET) {
                     continue;           // discard value
                 }
                 switch (typeCodes[i]) {
                     case 'L', '[' -> {
+                        Field f = fields[i].getField();
                         Object val = vals[offsets[i]];
                         if (val != null &&
                             !types[i - numPrimFields].isInstance(val))
                         {
-                            Field f = fields[i].getField();
                             throw new ClassCastException(
                                 "cannot assign instance of " +
                                 val.getClass().getName() + " to field " +
                                 f.getDeclaringClass().getName() + "." +
                                 f.getName() + " of type " +
                                 f.getType().getName() + " in instance of " +
                                 obj.getClass().getName());
                         }
-                        if (!dryRun)
-                            UNSAFE.putReference(obj, key, val);
+                        if (!dryRun) {
+                            if (UNSAFE.isFlatField(f)) {
+                                UNSAFE.putValue(obj, key, f.getType(), val);
+                            } else {
+                                UNSAFE.putReference(obj, key, val);
+                            }
+                        }
                     }
                     default -> throw new InternalError();
                 }
             }
         }
diff a/src/java.base/share/classes/java/io/Serializable.java b/src/java.base/share/classes/java/io/Serializable.java
--- a/src/java.base/share/classes/java/io/Serializable.java
+++ b/src/java.base/share/classes/java/io/Serializable.java
@@ -141,11 +141,20 @@
  *
  * Record classes can implement {@code Serializable} and receive treatment defined
  * by the <a href="{@docRoot}/../specs/serialization/serial-arch.html#serialization-of-records">
  * <cite>Java Object Serialization Specification,</cite> Section 1.13,
  * "Serialization of Records"</a>. Any declarations of the special
- * handling methods discussed above are ignored for record types.<p>
+ * handling methods discussed above, except {@code writeReplace},
+ * are ignored for record types.<p>
+ *
+ * Value classes can implement {@code Serializble} and receive the treatment defined
+ * by the <a href="{@docRoot}/../specs/serialization/serial-arch.html#serialization-of-value-objects">
+ * <cite>Java Object Serialization Specification,</cite> Section 1.14,
+ * "Serialization of Value Objects"</a>. Any declarations of the special
+ * handling methods discussed above, except {@code writeReplace},
+ * are ignored for value classes. Value classes implementing {@link Externalizable}
+ * and not using {@code writeReplace} are not supported.<p>
  *
  * The serialization runtime associates with each serializable class a version
  * number, called a serialVersionUID, which is used during deserialization to
  * verify that the sender and receiver of a serialized object have loaded
  * classes for that object that are compatible with respect to serialization.
diff a/src/java.base/share/classes/java/lang/Character.java b/src/java.base/share/classes/java/lang/Character.java
--- a/src/java.base/share/classes/java/lang/Character.java
+++ b/src/java.base/share/classes/java/lang/Character.java
@@ -178,12 +178,11 @@
  * @author  Martin Buchholz
  * @author  Ulf Zibis
  * @since   1.0
  */
 @jdk.internal.ValueBased
-public final
-class Character implements java.io.Serializable, Comparable<Character>, Constable {
+public final class Character implements java.io.Serializable, Comparable<Character>, Constable {
     /**
      * The minimum radix available for conversion to and from strings.
      * The constant value of this field is the smallest value permitted
      * for the radix argument in radix-conversion methods such as the
      * {@code digit} method, the {@code forDigit} method, and the
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
@@ -37,10 +37,11 @@
 import java.io.ObjectStreamField;
 import java.lang.reflect.AnnotatedElement;
 import java.lang.reflect.AnnotatedType;
 import java.lang.reflect.AccessFlag;
 import java.lang.reflect.Array;
+import java.lang.reflect.ClassFileFormatVersion;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Executable;
 import java.lang.reflect.Field;
 import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.GenericDeclaration;
@@ -219,13 +220,13 @@
                               GenericDeclaration,
                               Type,
                               AnnotatedElement,
                               TypeDescriptor.OfField<Class<?>>,
                               Constable {
-    private static final int ANNOTATION= 0x00002000;
-    private static final int ENUM      = 0x00004000;
-    private static final int SYNTHETIC = 0x00001000;
+    private static final int ANNOTATION = 0x00002000;
+    private static final int ENUM       = 0x00004000;
+    private static final int SYNTHETIC  = 0x00001000;
 
     private static native void registerNatives();
     static {
         registerNatives();
     }
@@ -308,10 +309,12 @@
                 } while (component.isArray());
                 sb.append(component.getName());
             } else {
                 // Class modifiers are a superset of interface modifiers
                 int modifiers = getModifiers() & Modifier.classModifiers();
+                // Modifier.toString() below mis-interprets SYNCHRONIZED, STRICT, and VOLATILE bits
+                modifiers &= ~(Modifier.SYNCHRONIZED | Modifier.STRICT | Modifier.VOLATILE);
                 if (modifiers != 0) {
                     sb.append(Modifier.toString(modifiers));
                     sb.append(' ');
                 }
 
@@ -321,10 +324,13 @@
                 addSealingInfo(modifiers, sb);
 
                 if (isAnnotation()) {
                     sb.append('@');
                 }
+                if (isValue()) {
+                    sb.append("value ");
+                }
                 if (isInterface()) { // Note: all annotation interfaces are interfaces
                     sb.append("interface");
                 } else {
                     if (isEnum())
                         sb.append("enum");
@@ -670,10 +676,43 @@
         } else {
             return BootLoader.loadClass(module, name);
         }
     }
 
+    /**
+     * {@return {@code true} if this {@code Class} object represents an identity
+     * class or interface; otherwise {@code false}}
+     *
+     * If this {@code Class} object represents an array type, then this method
+     * returns {@code true}.
+     * If this {@code Class} object represents a primitive type, or {@code void},
+     * then this method returns {@code false}.
+     *
+     * @since Valhalla
+     */
+    @PreviewFeature(feature = PreviewFeature.Feature.VALUE_OBJECTS, reflective=true)
+    public native boolean isIdentity();
+
+    /**
+     * {@return {@code true} if this {@code Class} object represents a value
+     * class; otherwise {@code false}}
+     *
+     * If this {@code Class} object represents an array type, an interface,
+     * a primitive type, or {@code void}, then this method returns {@code false}.
+     *
+     * @since Valhalla
+     */
+    @PreviewFeature(feature = PreviewFeature.Feature.VALUE_OBJECTS, reflective=true)
+    public boolean isValue() {
+        if (!PreviewFeatures.isEnabled()) {
+            return false;
+        }
+         if (isPrimitive() || isArray() || isInterface())
+             return false;
+        return ((getModifiers() & Modifier.IDENTITY) == 0);
+    }
+
     /**
      * {@return the {@code Class} object associated with the
      * {@linkplain #isPrimitive() primitive type} of the given name}
      * If the argument is not the name of a primitive type, {@code
      * null} is returned.
@@ -1461,13 +1500,14 @@
      * @jvms 4.1 The {@code ClassFile} Structure
      */
     @IntrinsicCandidate
     public native int getModifiers();
 
-    /**
+   /**
      * {@return an unmodifiable set of the {@linkplain AccessFlag access
      * flags} for this class, possibly empty}
+     * The {@code AccessFlags} may depend on the class file format version of the class.
      *
      * <p> If the underlying class is an array class:
      * <ul>
      * <li> its {@code PUBLIC}, {@code PRIVATE} and {@code PROTECTED}
      *      access flags are the same as those of its component type
@@ -1493,33 +1533,38 @@
         // Use getClassAccessFlagsRaw to expose SUPER status.
         var location = (isMemberClass() || isLocalClass() ||
                         isAnonymousClass() || isArray()) ?
             AccessFlag.Location.INNER_CLASS :
             AccessFlag.Location.CLASS;
-        return AccessFlag.maskToAccessFlags((location == AccessFlag.Location.CLASS) ?
-                                            getClassAccessFlagsRaw() :
-                                            getModifiers(),
-                                            location);
+        int accessFlags = (location == AccessFlag.Location.CLASS) ?
+                getClassAccessFlagsRaw() : getModifiers();
+        var cffv = ClassFileFormatVersion.fromMajor(getClassFileVersion() & 0xffff);
+        if (cffv.compareTo(ClassFileFormatVersion.latest()) >= 0) {
+            // Ignore unspecified (0x0800) access flag for current version
+            accessFlags &= ~0x0800;
+        }
+        if (!PreviewFeatures.isEnabled() && location == AccessFlag.Location.INNER_CLASS) {
+            accessFlags &= ~Modifier.IDENTITY; // drop ACC_IDENTITY bit in inner class if not in preview
+        }
+        return AccessFlag.maskToAccessFlags(accessFlags, location, cffv);
     }
 
-    /**
+   /**
      * Gets the signers of this class.
      *
      * @return  the signers of this class, or null if there are no signers.  In
      *          particular, this method returns null if this {@code Class} object represents
      *          a primitive type or void.
      * @since   1.1
      */
     public native Object[] getSigners();
 
-
     /**
      * Set the signers of this class.
      */
     native void setSigners(Object[] signers);
 
-
     /**
      * If this {@code Class} object represents a local or anonymous
      * class within a method, returns a {@link
      * java.lang.reflect.Method Method} object representing the
      * immediately enclosing method of the underlying class. Returns
@@ -4428,11 +4473,11 @@
      *
      * @return an array representing the superinterfaces
      * @since 1.8
      */
     public AnnotatedType[] getAnnotatedInterfaces() {
-         return TypeAnnotationParser.buildAnnotatedInterfaces(getRawTypeAnnotations(), getConstantPool(), this);
+        return TypeAnnotationParser.buildAnnotatedInterfaces(getRawTypeAnnotations(), getConstantPool(), this);
     }
 
     private native Class<?> getNestHost0();
 
     /**
@@ -4829,11 +4874,12 @@
      *
      * If the class is an array type then the class file version of its element
      * type is returned.  If the class is a primitive type then the latest class
      * file major version is returned and zero is returned for the minor version.
      */
-    private int getClassFileVersion() {
+    /* package-private */
+    int getClassFileVersion() {
         Class<?> c = isArray() ? elementType() : this;
         return c.getClassFileVersion0();
     }
 
     private native int getClassFileVersion0();
diff a/src/java.base/share/classes/java/lang/IdentityException.java b/src/java.base/share/classes/java/lang/IdentityException.java
--- /dev/null
+++ b/src/java.base/share/classes/java/lang/IdentityException.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2022, 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 java.lang;
+
+
+import jdk.internal.javac.PreviewFeature;
+
+/**
+ * Thrown when an identity object is required but a value object is supplied.
+ * <p>
+ * Identity objects are required for synchronization and locking.
+ * <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">Value-based</a>
+ * objects do not have identity and cannot be used for synchronization or locking.
+ *
+ * @since Valhalla
+ */
+@PreviewFeature(feature = PreviewFeature.Feature.VALUE_OBJECTS)
+public class IdentityException extends RuntimeException {
+    @java.io.Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Create an {@code IdentityException} with no message.
+     */
+    public IdentityException() {
+    }
+
+    /**
+     * Create an {@code IdentityException} with the class name and default message.
+     *
+     * @param clazz the class of the object
+     */
+    public IdentityException(Class<?> clazz) {
+        super(clazz.getName() + " is not an identity class");
+    }
+
+    /**
+     * Create an {@code IdentityException} with a message.
+     *
+     * @param  message the detail message; can be {@code null}
+     */
+    public IdentityException(String message) {
+        super(message);
+    }
+
+    /**
+     * Create an {@code IdentityException} with a cause.
+     *
+     * @param  cause the cause; {@code null} is permitted, and indicates
+     *               that the cause is nonexistent or unknown.
+     */
+    public IdentityException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Create an {@code IdentityException} with a message and cause.
+     *
+     * @param  message the detail message; can be {@code null}
+     * @param  cause the cause; {@code null} is permitted, and indicates
+     *               that the cause is nonexistent or unknown.
+     */
+    public IdentityException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff a/src/java.base/share/classes/java/lang/Object.java b/src/java.base/share/classes/java/lang/Object.java
--- a/src/java.base/share/classes/java/lang/Object.java
+++ b/src/java.base/share/classes/java/lang/Object.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1994, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 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.  Oracle designates this
@@ -30,10 +30,14 @@
 
 /**
  * Class {@code Object} is the root of the class hierarchy.
  * Every class has {@code Object} as a superclass. All objects,
  * including arrays, implement the methods of this class.
+ * <p>
+ * Subclasses of {@code java.lang.Object} can be either an {@linkplain Class#isIdentity identity class}
+ * or a {@linkplain Class#isValue value class}.
+ * See {@jls The Java Language Specification 8.1.1.5 value Classes}.
  *
  * @see     java.lang.Class
  * @since   1.0
  */
 public class Object {
diff a/src/java.base/share/classes/java/lang/Record.java b/src/java.base/share/classes/java/lang/Record.java
--- a/src/java.base/share/classes/java/lang/Record.java
+++ b/src/java.base/share/classes/java/lang/Record.java
@@ -85,10 +85,11 @@
  *
  * @spec serialization/index.html Java Object Serialization Specification
  * @jls 8.10 Record Types
  * @since 16
  */
+@jdk.internal.ValueBased
 public abstract class Record {
     /**
      * Constructor for record classes to call.
      */
     protected Record() {}
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
@@ -38,10 +38,11 @@
 import java.lang.foreign.MemorySegment;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodType;
 import java.lang.invoke.StringConcatFactory;
 import java.lang.module.ModuleDescriptor;
+import java.lang.reflect.ClassFileFormatVersion;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Executable;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.net.URI;
@@ -2672,10 +2673,14 @@
                                                       ContinuationScope contScope,
                                                       Continuation continuation) {
                 return StackWalker.newInstance(options, null, contScope, continuation);
             }
 
+            public int classFileFormatVersion(Class<?> clazz) {
+                return clazz.getClassFileVersion();
+            }
+
             public String getLoaderNameID(ClassLoader loader) {
                 return loader != null ? loader.nameAndId() : "null";
             }
 
             @Override
diff a/src/java.base/share/classes/java/lang/classfile/Attribute.java b/src/java.base/share/classes/java/lang/classfile/Attribute.java
--- a/src/java.base/share/classes/java/lang/classfile/Attribute.java
+++ b/src/java.base/share/classes/java/lang/classfile/Attribute.java
@@ -45,10 +45,11 @@
 import java.lang.classfile.attribute.ModuleResolutionAttribute;
 import java.lang.classfile.attribute.ModuleTargetAttribute;
 import java.lang.classfile.attribute.NestHostAttribute;
 import java.lang.classfile.attribute.NestMembersAttribute;
 import java.lang.classfile.attribute.PermittedSubclassesAttribute;
+import java.lang.classfile.attribute.PreloadAttribute;
 import java.lang.classfile.attribute.RecordAttribute;
 import java.lang.classfile.attribute.RuntimeInvisibleAnnotationsAttribute;
 import java.lang.classfile.attribute.RuntimeInvisibleParameterAnnotationsAttribute;
 import java.lang.classfile.attribute.RuntimeInvisibleTypeAnnotationsAttribute;
 import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
@@ -87,11 +88,11 @@
                 ExceptionsAttribute, InnerClassesAttribute, LineNumberTableAttribute,
                 LocalVariableTableAttribute, LocalVariableTypeTableAttribute,
                 MethodParametersAttribute, ModuleAttribute, ModuleHashesAttribute,
                 ModuleMainClassAttribute, ModulePackagesAttribute, ModuleResolutionAttribute,
                 ModuleTargetAttribute, NestHostAttribute, NestMembersAttribute,
-                PermittedSubclassesAttribute,
+                PermittedSubclassesAttribute, PreloadAttribute,
                 RecordAttribute, RuntimeInvisibleAnnotationsAttribute,
                 RuntimeInvisibleParameterAnnotationsAttribute, RuntimeInvisibleTypeAnnotationsAttribute,
                 RuntimeVisibleAnnotationsAttribute, RuntimeVisibleParameterAnnotationsAttribute,
                 RuntimeVisibleTypeAnnotationsAttribute, SignatureAttribute,
                 SourceDebugExtensionAttribute, SourceFileAttribute, SourceIDAttribute,
diff a/src/java.base/share/classes/java/lang/classfile/Attributes.java b/src/java.base/share/classes/java/lang/classfile/Attributes.java
--- a/src/java.base/share/classes/java/lang/classfile/Attributes.java
+++ b/src/java.base/share/classes/java/lang/classfile/Attributes.java
@@ -62,10 +62,11 @@
 import java.lang.classfile.attribute.ModuleResolutionAttribute;
 import java.lang.classfile.attribute.ModuleTargetAttribute;
 import java.lang.classfile.attribute.NestHostAttribute;
 import java.lang.classfile.attribute.NestMembersAttribute;
 import java.lang.classfile.attribute.PermittedSubclassesAttribute;
+import java.lang.classfile.attribute.PreloadAttribute;
 import java.lang.classfile.attribute.RecordAttribute;
 import java.lang.classfile.attribute.RecordComponentInfo;
 import java.lang.classfile.attribute.RuntimeInvisibleAnnotationsAttribute;
 import java.lang.classfile.attribute.RuntimeInvisibleParameterAnnotationsAttribute;
 import java.lang.classfile.attribute.RuntimeInvisibleTypeAnnotationsAttribute;
@@ -163,10 +164,13 @@
     public static final String NAME_NEST_MEMBERS = "NestMembers";
 
     /** PermittedSubclasses */
     public static final String NAME_PERMITTED_SUBCLASSES = "PermittedSubclasses";
 
+    /** Preload */
+    public static final String NAME_PRELOAD = "Preload";
+
     /** Record */
     public static final String NAME_RECORD = "Record";
 
     /** RuntimeInvisibleAnnotations */
     public static final String NAME_RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations";
@@ -718,10 +722,29 @@
                 public AttributeMapper.AttributeStability stability() {
                     return AttributeStability.CP_REFS;
                 }
             };
 
+    /** Attribute mapper for the {@code Preload} attribute */
+    public static final AttributeMapper<PreloadAttribute>
+            PRELOAD = new AbstractAttributeMapper<>(NAME_PRELOAD) {
+                @Override
+                public PreloadAttribute readAttribute(AttributedElement e, ClassReader cf, int p) {
+                    return new BoundAttribute.BoundPreloadAttribute(cf, this, p);
+                }
+
+                @Override
+                protected void writeBody(BufWriter buf, PreloadAttribute attr) {
+                    buf.writeListIndices(attr.preloads());
+                }
+
+                @Override
+                public AttributeMapper.AttributeStability stability() {
+                    return AttributeStability.CP_REFS;
+                }
+            };
+
     /** Attribute mapper for the {@code Record} attribute */
     public static final AttributeMapper<RecordAttribute>
             RECORD = new AbstractAttributeMapper<>(NAME_RECORD) {
                 @Override
                 public RecordAttribute readAttribute(AttributedElement e, ClassReader cf, int p) {
@@ -1014,10 +1037,11 @@
             MODULE_RESOLUTION,
             MODULE_TARGET,
             NEST_HOST,
             NEST_MEMBERS,
             PERMITTED_SUBCLASSES,
+            PRELOAD,
             RECORD,
             RUNTIME_INVISIBLE_ANNOTATIONS,
             RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS,
             RUNTIME_INVISIBLE_TYPE_ANNOTATIONS,
             RUNTIME_VISIBLE_ANNOTATIONS,
diff a/src/java.base/share/classes/java/lang/classfile/ClassElement.java b/src/java.base/share/classes/java/lang/classfile/ClassElement.java
--- a/src/java.base/share/classes/java/lang/classfile/ClassElement.java
+++ b/src/java.base/share/classes/java/lang/classfile/ClassElement.java
@@ -35,10 +35,11 @@
 import java.lang.classfile.attribute.ModuleResolutionAttribute;
 import java.lang.classfile.attribute.ModuleTargetAttribute;
 import java.lang.classfile.attribute.NestHostAttribute;
 import java.lang.classfile.attribute.NestMembersAttribute;
 import java.lang.classfile.attribute.PermittedSubclassesAttribute;
+import java.lang.classfile.attribute.PreloadAttribute;
 import java.lang.classfile.attribute.RecordAttribute;
 import java.lang.classfile.attribute.RuntimeInvisibleAnnotationsAttribute;
 import java.lang.classfile.attribute.RuntimeInvisibleTypeAnnotationsAttribute;
 import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
 import java.lang.classfile.attribute.RuntimeVisibleTypeAnnotationsAttribute;
@@ -63,11 +64,11 @@
                 FieldModel, MethodModel,
                 CustomAttribute, CompilationIDAttribute, DeprecatedAttribute,
                 EnclosingMethodAttribute, InnerClassesAttribute,
                 ModuleAttribute, ModuleHashesAttribute, ModuleMainClassAttribute,
                 ModulePackagesAttribute, ModuleResolutionAttribute, ModuleTargetAttribute,
-                NestHostAttribute, NestMembersAttribute, PermittedSubclassesAttribute,
+                NestHostAttribute, NestMembersAttribute, PermittedSubclassesAttribute, PreloadAttribute,
                 RecordAttribute,
                 RuntimeInvisibleAnnotationsAttribute, RuntimeInvisibleTypeAnnotationsAttribute,
                 RuntimeVisibleAnnotationsAttribute, RuntimeVisibleTypeAnnotationsAttribute,
                 SignatureAttribute, SourceDebugExtensionAttribute,
                 SourceFileAttribute, SourceIDAttribute, SyntheticAttribute, UnknownAttribute {
diff a/src/java.base/share/classes/java/lang/classfile/ClassFile.java b/src/java.base/share/classes/java/lang/classfile/ClassFile.java
--- a/src/java.base/share/classes/java/lang/classfile/ClassFile.java
+++ b/src/java.base/share/classes/java/lang/classfile/ClassFile.java
@@ -1137,10 +1137,13 @@
     int ACC_ANNOTATION = 0x2000;
 
     /** The value of SUPER access and property modifier. */
     int ACC_SUPER = 0x0020;
 
+    /** The value of IDENTITY access and property modifier. */
+    int ACC_IDENTITY = 0x0020;
+
     /** The value of ABSTRACT access and property modifier. */
     int ACC_ABSTRACT = 0x0400;
 
     /** The value of VOLATILE access and property modifier. */
     int ACC_VOLATILE = 0x0040;
diff a/src/java.base/share/classes/java/lang/classfile/attribute/PreloadAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/PreloadAttribute.java
--- /dev/null
+++ b/src/java.base/share/classes/java/lang/classfile/attribute/PreloadAttribute.java
@@ -0,0 +1,86 @@
+/*
+ * 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.  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.
+ */
+package java.lang.classfile.attribute;
+
+import java.lang.constant.ClassDesc;
+import java.util.Arrays;
+import java.util.List;
+
+import java.lang.classfile.Attribute;
+import java.lang.classfile.ClassElement;
+import java.lang.classfile.constantpool.ClassEntry;
+import jdk.internal.classfile.impl.BoundAttribute;
+import jdk.internal.classfile.impl.UnboundAttribute;
+import jdk.internal.classfile.impl.Util;
+import jdk.internal.javac.PreviewFeature;
+
+/**
+ *  <p><b>This is NOT part of any supported API.
+ *  If you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ */
+@PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API)
+public sealed interface PreloadAttribute
+        extends Attribute<PreloadAttribute>, ClassElement
+        permits BoundAttribute.BoundPreloadAttribute, UnboundAttribute.UnboundPreloadAttribute {
+
+    /**
+     * {@return the list of preload classes}
+     */
+    List<ClassEntry> preloads();
+
+    /**
+     * {@return a {@code Preload} attribute}
+     * @param preloads the preload classes
+     */
+    static PreloadAttribute of(List<ClassEntry> preloads) {
+        return new UnboundAttribute.UnboundPreloadAttribute(preloads);
+    }
+
+    /**
+     * {@return a {@code Preload} attribute}
+     * @param preloads the preload classes
+     */
+    static PreloadAttribute of(ClassEntry... preloads) {
+        return of(List.of(preloads));
+    }
+
+    /**
+     * {@return a {@code Preload} attribute}
+     * @param preloads the preload classes
+     */
+    static PreloadAttribute ofSymbols(List<ClassDesc> preloads) {
+        return of(Util.entryList(preloads));
+    }
+
+    /**
+     * {@return a {@code Preload} attribute}
+     * @param preloads the preload classes
+     */
+    static PreloadAttribute ofSymbols(ClassDesc... preloads) {
+        return ofSymbols(Arrays.asList(preloads));
+    }
+}
diff a/src/java.base/share/classes/java/lang/classfile/package-info.java b/src/java.base/share/classes/java/lang/classfile/package-info.java
--- a/src/java.base/share/classes/java/lang/classfile/package-info.java
+++ b/src/java.base/share/classes/java/lang/classfile/package-info.java
@@ -452,10 +452,11 @@
  *     | NestMembersAttribute?(List<ClassEntry> nestMembers)
  *     | RecordAttribute?(List<RecordComponent> components)
  *     | EnclosingMethodAttribute?(ClassEntry className, NameAndTypeEntry method)
  *     | InnerClassesAttribute?(List<InnerClassInfo> classes)
  *     | PermittedSubclassesAttribute?(List<ClassEntry> permittedSubclasses)
+ *     | PreloadAttribute?(List<ClassEntry> preloads)
  *     | DeclarationElement*
  * }
  *<p>
  * where {@code DeclarationElement} are the elements that are common to all declarations
  * (classes,  methods, fields) and so are factored out:
diff a/src/java.base/share/classes/java/lang/constant/ClassDesc.java b/src/java.base/share/classes/java/lang/constant/ClassDesc.java
--- a/src/java.base/share/classes/java/lang/constant/ClassDesc.java
+++ b/src/java.base/share/classes/java/lang/constant/ClassDesc.java
@@ -56,11 +56,11 @@
  */
 public sealed interface ClassDesc
         extends ConstantDesc,
                 TypeDescriptor.OfField<ClassDesc>
         permits PrimitiveClassDescImpl,
-                ReferenceClassDescImpl {
+                ClassDescImpl {
 
     /**
      * Returns a {@linkplain ClassDesc} for a class or interface type,
      * given the name of the class or interface, such as {@code "java.lang.String"}.
      * (To create a descriptor for an array type, either use {@link #ofDescriptor(String)}
@@ -169,11 +169,11 @@
                     "Cannot create an array type descriptor with more than " +
                     ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS + " dimensions");
         }
         return (descriptor.length() == 1)
                ? new PrimitiveClassDescImpl(descriptor)
-               : new ReferenceClassDescImpl(descriptor);
+               : new ClassDescImpl(descriptor);
     }
 
     /**
      * Returns a {@linkplain ClassDesc} for an array type whose component type
      * is described by this {@linkplain ClassDesc}.
diff a/src/java.base/share/classes/java/lang/constant/ClassDescImpl.java b/src/java.base/share/classes/java/lang/constant/ClassDescImpl.java
--- /dev/null
+++ b/src/java.base/share/classes/java/lang/constant/ClassDescImpl.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2018, 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.
+ */
+package java.lang.constant;
+
+import java.lang.invoke.MethodHandles;
+
+import static java.lang.constant.ConstantUtils.*;
+import static java.util.Objects.requireNonNull;
+
+/**
+ * A <a href="package-summary.html#nominal">nominal descriptor</a> for a class,
+ * interface, or array type.  A {@linkplain ClassDescImpl} corresponds to a
+ * {@code Constant_Class_info} entry in the constant pool of a classfile.
+ */
+final class ClassDescImpl implements ClassDesc {
+    private final String descriptor;
+
+    /**
+     * Creates a {@linkplain ClassDesc} from a descriptor string for a class or
+     * interface type or an array type.
+     *
+     * @param descriptor a field descriptor string for a class or interface type
+     * @throws IllegalArgumentException if the descriptor string is not a valid
+     * field descriptor string, or does not describe a class or interface type
+     * @jvms 4.3.2 Field Descriptors
+     */
+    ClassDescImpl(String descriptor) {
+        requireNonNull(descriptor);
+        int len = ConstantUtils.skipOverFieldSignature(descriptor, 0, descriptor.length(), false);
+        if (len == 0 || len == 1
+            || len != descriptor.length())
+            throw new IllegalArgumentException(String.format("not a valid reference type descriptor: %s", descriptor));
+        this.descriptor = descriptor;
+    }
+
+    @Override
+    public String descriptorString() {
+        return descriptor;
+    }
+
+    @Override
+    public Class<?> resolveConstantDesc(MethodHandles.Lookup lookup)
+            throws ReflectiveOperationException {
+        if (isArray()) {
+            if (isPrimitiveArray()) {
+                return lookup.findClass(descriptor);
+            }
+            // Class.forName is slow on class or interface arrays
+            int depth = ConstantUtils.arrayDepth(descriptor);
+            Class<?> clazz = lookup.findClass(internalToBinary(descriptor.substring(depth + 1, descriptor.length() - 1)));
+            for (int i = 0; i < depth; i++)
+                clazz = clazz.arrayType();
+            return clazz;
+        }
+        return lookup.findClass(internalToBinary(dropFirstAndLastChar(descriptor)));
+    }
+
+    /**
+     * Whether the descriptor is one of a primitive array, given this is
+     * already a valid reference type descriptor.
+     */
+    private boolean isPrimitiveArray() {
+        // All L-type descriptors must end with a semicolon; same for reference
+        // arrays, leaving primitive arrays the only ones without a final semicolon
+        return descriptor.charAt(descriptor.length() - 1) != ';';
+    }
+
+    /**
+     * Returns {@code true} if this {@linkplain ClassDescImpl} is
+     * equal to another {@linkplain ClassDescImpl}.  Equality is
+     * determined by the two class descriptors having equal class descriptor
+     * strings.
+     *
+     * @param o the {@code ClassDesc} to compare to this
+     *       {@code ClassDesc}
+     * @return {@code true} if the specified {@code ClassDesc}
+     *      is equal to this {@code ClassDesc}.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        ClassDesc constant = (ClassDesc) o;
+        return descriptor.equals(constant.descriptorString());
+    }
+
+    @Override
+    public int hashCode() {
+        return descriptor.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return String.format("ClassDesc[%s]", displayName());
+    }
+}
diff a/src/java.base/share/classes/java/lang/constant/ReferenceClassDescImpl.java b/src/java.base/share/classes/java/lang/constant/ReferenceClassDescImpl.java
--- a/src/java.base/share/classes/java/lang/constant/ReferenceClassDescImpl.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (c) 2018, 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.
- */
-package java.lang.constant;
-
-import java.lang.invoke.MethodHandles;
-
-import static java.lang.constant.ConstantUtils.*;
-import static java.util.Objects.requireNonNull;
-
-/**
- * A <a href="package-summary.html#nominal">nominal descriptor</a> for a class,
- * interface, or array type.  A {@linkplain ReferenceClassDescImpl} corresponds to a
- * {@code Constant_Class_info} entry in the constant pool of a classfile.
- */
-final class ReferenceClassDescImpl implements ClassDesc {
-    private final String descriptor;
-
-    /**
-     * Creates a {@linkplain ClassDesc} from a descriptor string for a class or
-     * interface type or an array type.
-     *
-     * @param descriptor a field descriptor string for a class or interface type
-     * @throws IllegalArgumentException if the descriptor string is not a valid
-     * field descriptor string, or does not describe a class or interface type
-     * @jvms 4.3.2 Field Descriptors
-     */
-    ReferenceClassDescImpl(String descriptor) {
-        requireNonNull(descriptor);
-        int len = ConstantUtils.skipOverFieldSignature(descriptor, 0, descriptor.length(), false);
-        if (len == 0 || len == 1
-            || len != descriptor.length())
-            throw new IllegalArgumentException(String.format("not a valid reference type descriptor: %s", descriptor));
-        this.descriptor = descriptor;
-    }
-
-    @Override
-    public String descriptorString() {
-        return descriptor;
-    }
-
-    @Override
-    public Class<?> resolveConstantDesc(MethodHandles.Lookup lookup)
-            throws ReflectiveOperationException {
-        if (isArray()) {
-            if (isPrimitiveArray()) {
-                return lookup.findClass(descriptor);
-            }
-            // Class.forName is slow on class or interface arrays
-            int depth = ConstantUtils.arrayDepth(descriptor);
-            Class<?> clazz = lookup.findClass(internalToBinary(descriptor.substring(depth + 1, descriptor.length() - 1)));
-            for (int i = 0; i < depth; i++)
-                clazz = clazz.arrayType();
-            return clazz;
-        }
-        return lookup.findClass(internalToBinary(dropFirstAndLastChar(descriptor)));
-    }
-
-    /**
-     * Whether the descriptor is one of a primitive array, given this is
-     * already a valid reference type descriptor.
-     */
-    private boolean isPrimitiveArray() {
-        // All L-type descriptors must end with a semicolon; same for reference
-        // arrays, leaving primitive arrays the only ones without a final semicolon
-        return descriptor.charAt(descriptor.length() - 1) != ';';
-    }
-
-    /**
-     * Returns {@code true} if this {@linkplain ReferenceClassDescImpl} is
-     * equal to another {@linkplain ReferenceClassDescImpl}.  Equality is
-     * determined by the two class descriptors having equal class descriptor
-     * strings.
-     *
-     * @param o the {@code ClassDesc} to compare to this
-     *       {@code ClassDesc}
-     * @return {@code true} if the specified {@code ClassDesc}
-     *      is equal to this {@code ClassDesc}.
-     */
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        ClassDesc constant = (ClassDesc) o;
-        return descriptor.equals(constant.descriptorString());
-    }
-
-    @Override
-    public int hashCode() {
-        return descriptor.hashCode();
-    }
-
-    @Override
-    public String toString() {
-        return String.format("ClassDesc[%s]", displayName());
-    }
-}
diff a/src/java.base/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java b/src/java.base/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java
--- a/src/java.base/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java
+++ b/src/java.base/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2022, 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
@@ -269,11 +269,11 @@
 
             // check receiver type
             if (!implClass.isAssignableFrom(receiverClass)) {
                 throw new LambdaConversionException(
                         String.format("Invalid receiver type %s; not a subtype of implementation type %s",
-                                      receiverClass, implClass));
+                                      receiverClass.descriptorString(), implClass.descriptorString()));
             }
         } else {
             // no receiver
             capturedStart = 0;
             samStart = capturedArity;
@@ -369,11 +369,11 @@
                 } else {
                     // must be convertible to primitive
                     return !strict;
                 }
             } else {
-                // both are reference types: fromType should be a superclass of toType.
+                // fromType should be a superclass of toType
                 return !strict || toType.isAssignableFrom(fromType);
             }
         }
     }
 
diff a/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java b/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
--- a/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
+++ b/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
@@ -130,13 +130,13 @@
         if (member.isConstructor())
             return makeAllocator(member.getDeclaringClass(), member);
         return make(member.getDeclaringClass(), member);
     }
     static DirectMethodHandle makeAllocator(Class<?> instanceClass, MemberName ctor) {
-        assert(ctor.isConstructor() && ctor.getName().equals("<init>"));
+        assert(ctor.isConstructor()) : ctor;
         ctor = ctor.asConstructor();
-        assert(ctor.isConstructor() && ctor.getReferenceKind() == REF_newInvokeSpecial) : ctor;
+        assert(ctor.getReferenceKind() == REF_newInvokeSpecial) : ctor;
         MethodType mtype = ctor.getMethodType().changeReturnType(instanceClass);
         LambdaForm lform = preparedLambdaForm(ctor);
         MemberName init = ctor.asSpecial();
         assert(init.getMethodType().returnType() == void.class);
         return new Constructor(mtype, lform, ctor, true, init, instanceClass);
@@ -596,10 +596,25 @@
     /*non-public*/
     static Object checkCast(Object mh, Object obj) {
         return ((DirectMethodHandle) mh).checkCast(obj);
     }
 
+    @ForceInline
+    /*non-public*/ static Class<?> fieldType(Object accessorObj) {
+        return ((Accessor) accessorObj).fieldType;
+    }
+
+    @ForceInline
+    /*non-public*/ static Class<?> staticFieldType(Object accessorObj) {
+        return ((StaticAccessor) accessorObj).fieldType;
+    }
+
+    @ForceInline
+    /*non-public*/ static Object zeroInstanceIfNull(Class<?> fieldType, Object obj) {
+        return obj != null ? obj : UNSAFE.uninitializedDefaultValue(fieldType);
+    }
+
     Object checkCast(Object obj) {
         return member.getMethodType().returnType().cast(obj);
     }
 
     // Caching machinery for field accessors:
@@ -610,77 +625,81 @@
             AF_PUTSTATIC       = 3,
             AF_GETSTATIC_INIT  = 4,
             AF_PUTSTATIC_INIT  = 5,
             AF_LIMIT           = 6;
     // Enumerate the different field kinds using Wrapper,
-    // with an extra case added for checked references.
+    // with an extra case added for checked references and value field access
     static final int
-            FT_LAST_WRAPPER    = Wrapper.COUNT-1,
-            FT_UNCHECKED_REF   = Wrapper.OBJECT.ordinal(),
-            FT_CHECKED_REF     = FT_LAST_WRAPPER+1,
-            FT_LIMIT           = FT_LAST_WRAPPER+2;
-    private static int afIndex(byte formOp, boolean isVolatile, int ftypeKind) {
+            FT_LAST_WRAPPER     = Wrapper.COUNT-1,
+            FT_UNCHECKED_REF    = Wrapper.OBJECT.ordinal(),
+            FT_CHECKED_REF      = FT_LAST_WRAPPER+1,
+            FT_CHECKED_VALUE    = FT_LAST_WRAPPER+2,  // flat vs non-flat x null value vs null-restricted value
+            FT_LIMIT            = FT_LAST_WRAPPER+6;
+    private static int afIndex(byte formOp, boolean isVolatile, boolean isFlat, boolean isNullRestricted, int ftypeKind) {
         return ((formOp * FT_LIMIT * 2)
                 + (isVolatile ? FT_LIMIT : 0)
+                + (isFlat ? 1 : 0)
+                + (isNullRestricted ? 1 : 0)
                 + ftypeKind);
     }
     @Stable
     private static final LambdaForm[] ACCESSOR_FORMS
-            = new LambdaForm[afIndex(AF_LIMIT, false, 0)];
+            = new LambdaForm[afIndex(AF_LIMIT, false, false, false, 0)];
     static int ftypeKind(Class<?> ftype) {
         if (ftype.isPrimitive()) {
             return Wrapper.forPrimitiveType(ftype).ordinal();
         } else if (ftype.isInterface() || ftype.isAssignableFrom(Object.class)) {
             // retyping can be done without a cast
             return FT_UNCHECKED_REF;
         } else {
-            return FT_CHECKED_REF;
+            return ftype.isValue() ? FT_CHECKED_VALUE : FT_CHECKED_REF;
         }
     }
 
     /**
      * Create a LF which can access the given field.
      * Cache and share this structure among all fields with
      * the same basicType and refKind.
      */
     private static LambdaForm preparedFieldLambdaForm(MemberName m) {
         Class<?> ftype = m.getFieldType();
-        boolean isVolatile = m.isVolatile();
         byte formOp = switch (m.getReferenceKind()) {
             case REF_getField  -> AF_GETFIELD;
             case REF_putField  -> AF_PUTFIELD;
             case REF_getStatic -> AF_GETSTATIC;
             case REF_putStatic -> AF_PUTSTATIC;
             default -> throw new InternalError(m.toString());
         };
         if (shouldBeInitialized(m)) {
             // precompute the barrier-free version:
-            preparedFieldLambdaForm(formOp, isVolatile, ftype);
+            preparedFieldLambdaForm(formOp, m.isVolatile(), m.isFlat(), m.isNullRestricted(), ftype);
             assert((AF_GETSTATIC_INIT - AF_GETSTATIC) ==
                    (AF_PUTSTATIC_INIT - AF_PUTSTATIC));
             formOp += (AF_GETSTATIC_INIT - AF_GETSTATIC);
         }
-        LambdaForm lform = preparedFieldLambdaForm(formOp, isVolatile, ftype);
+        LambdaForm lform = preparedFieldLambdaForm(formOp, m.isVolatile(), m.isFlat(), m.isNullRestricted(), ftype);
         maybeCompile(lform, m);
         assert(lform.methodType().dropParameterTypes(0, 1)
                 .equals(m.getInvocationType().basicType()))
                 : Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType());
         return lform;
     }
-    private static LambdaForm preparedFieldLambdaForm(byte formOp, boolean isVolatile, Class<?> ftype) {
+
+    private static LambdaForm preparedFieldLambdaForm(byte formOp, boolean isVolatile,
+                                                      boolean isFlat, boolean isNullRestricted, Class<?> ftype) {
         int ftypeKind = ftypeKind(ftype);
-        int afIndex = afIndex(formOp, isVolatile, ftypeKind);
+        int afIndex = afIndex(formOp, isVolatile, isFlat, isNullRestricted, ftypeKind);
         LambdaForm lform = ACCESSOR_FORMS[afIndex];
         if (lform != null)  return lform;
-        lform = makePreparedFieldLambdaForm(formOp, isVolatile, ftypeKind);
+        lform = makePreparedFieldLambdaForm(formOp, isVolatile,isFlat, isNullRestricted, ftypeKind);
         ACCESSOR_FORMS[afIndex] = lform;  // don't bother with a CAS
         return lform;
     }
 
     private static final Wrapper[] ALL_WRAPPERS = Wrapper.values();
 
-    private static Kind getFieldKind(boolean isGetter, boolean isVolatile, Wrapper wrapper) {
+    private static Kind getFieldKind(boolean isGetter, boolean isVolatile, boolean isFlat, Wrapper wrapper) {
         if (isGetter) {
             if (isVolatile) {
                 switch (wrapper) {
                     case BOOLEAN: return GET_BOOLEAN_VOLATILE;
                     case BYTE:    return GET_BYTE_VOLATILE;
@@ -688,11 +707,11 @@
                     case CHAR:    return GET_CHAR_VOLATILE;
                     case INT:     return GET_INT_VOLATILE;
                     case LONG:    return GET_LONG_VOLATILE;
                     case FLOAT:   return GET_FLOAT_VOLATILE;
                     case DOUBLE:  return GET_DOUBLE_VOLATILE;
-                    case OBJECT:  return GET_REFERENCE_VOLATILE;
+                    case OBJECT:  return isFlat ? GET_VALUE_VOLATILE : GET_REFERENCE_VOLATILE;
                 }
             } else {
                 switch (wrapper) {
                     case BOOLEAN: return GET_BOOLEAN;
                     case BYTE:    return GET_BYTE;
@@ -700,11 +719,11 @@
                     case CHAR:    return GET_CHAR;
                     case INT:     return GET_INT;
                     case LONG:    return GET_LONG;
                     case FLOAT:   return GET_FLOAT;
                     case DOUBLE:  return GET_DOUBLE;
-                    case OBJECT:  return GET_REFERENCE;
+                    case OBJECT:  return isFlat ? GET_VALUE : GET_REFERENCE;
                 }
             }
         } else {
             if (isVolatile) {
                 switch (wrapper) {
@@ -714,11 +733,11 @@
                     case CHAR:    return PUT_CHAR_VOLATILE;
                     case INT:     return PUT_INT_VOLATILE;
                     case LONG:    return PUT_LONG_VOLATILE;
                     case FLOAT:   return PUT_FLOAT_VOLATILE;
                     case DOUBLE:  return PUT_DOUBLE_VOLATILE;
-                    case OBJECT:  return PUT_REFERENCE_VOLATILE;
+                    case OBJECT:  return isFlat ? PUT_VALUE_VOLATILE : PUT_REFERENCE_VOLATILE;
                 }
             } else {
                 switch (wrapper) {
                     case BOOLEAN: return PUT_BOOLEAN;
                     case BYTE:    return PUT_BYTE;
@@ -726,34 +745,45 @@
                     case CHAR:    return PUT_CHAR;
                     case INT:     return PUT_INT;
                     case LONG:    return PUT_LONG;
                     case FLOAT:   return PUT_FLOAT;
                     case DOUBLE:  return PUT_DOUBLE;
-                    case OBJECT:  return PUT_REFERENCE;
+                    case OBJECT:  return isFlat ? PUT_VALUE : PUT_REFERENCE;
                 }
             }
         }
         throw new AssertionError("Invalid arguments");
     }
 
-    static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftypeKind) {
+    /** invoked by GenerateJLIClassesHelper */
+    static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftype) {
+        return makePreparedFieldLambdaForm(formOp, isVolatile, false, false, ftype);
+    }
+
+    private static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile,
+                                                          boolean isFlat, boolean isNullRestricted, int ftypeKind) {
         boolean isGetter  = (formOp & 1) == (AF_GETFIELD & 1);
         boolean isStatic  = (formOp >= AF_GETSTATIC);
         boolean needsInit = (formOp >= AF_GETSTATIC_INIT);
-        boolean needsCast = (ftypeKind == FT_CHECKED_REF);
+        boolean needsCast = (ftypeKind == FT_CHECKED_REF || ftypeKind == FT_CHECKED_VALUE);
         Wrapper fw = (needsCast ? Wrapper.OBJECT : ALL_WRAPPERS[ftypeKind]);
         Class<?> ft = fw.primitiveType();
-        assert(ftypeKind(needsCast ? String.class : ft) == ftypeKind);
+        assert(needsCast ? true : ftypeKind(ft) == ftypeKind);
 
         // getObject, putIntVolatile, etc.
-        Kind kind = getFieldKind(isGetter, isVolatile, fw);
+        Kind kind = getFieldKind(isGetter, isVolatile, isFlat, fw);
 
         MethodType linkerType;
-        if (isGetter)
-            linkerType = MethodType.methodType(ft, Object.class, long.class);
-        else
-            linkerType = MethodType.methodType(void.class, Object.class, long.class, ft);
+        if (isGetter) {
+            linkerType = isFlat
+                            ? MethodType.methodType(ft, Object.class, long.class, Class.class)
+                            : MethodType.methodType(ft, Object.class, long.class);
+        } else {
+            linkerType = isFlat
+                            ? MethodType.methodType(void.class, Object.class, long.class, Class.class, ft)
+                            : MethodType.methodType(void.class, Object.class, long.class, ft);
+        }
         MemberName linker = new MemberName(Unsafe.class, kind.methodName, linkerType, REF_invokeVirtual);
         try {
             linker = IMPL_NAMES.resolveOrFail(REF_invokeVirtual, linker, null, LM_TRUSTED,
                                               NoSuchMethodException.class);
         } catch (ReflectiveOperationException ex) {
@@ -780,36 +810,58 @@
         final int F_HOLDER  = (isStatic ? nameCursor++ : -1);  // static base if any
         final int F_OFFSET  = nameCursor++;  // Either static offset or field offset.
         final int OBJ_CHECK = (OBJ_BASE >= 0 ? nameCursor++ : -1);
         final int U_HOLDER  = nameCursor++;  // UNSAFE holder
         final int INIT_BAR  = (needsInit ? nameCursor++ : -1);
+        final int VALUE_TYPE = (isFlat ? nameCursor++ : -1);
+        final int NULL_CHECK  = (isNullRestricted && !isGetter ? nameCursor++ : -1);
         final int PRE_CAST  = (needsCast && !isGetter ? nameCursor++ : -1);
         final int LINKER_CALL = nameCursor++;
+        final int FIELD_TYPE = (isNullRestricted && isGetter ? nameCursor++ : -1);
+        final int ZERO_INSTANCE = (isNullRestricted && isGetter ? nameCursor++ : -1);
         final int POST_CAST = (needsCast && isGetter ? nameCursor++ : -1);
-        final int RESULT    = nameCursor-1;  // either the call or the cast
+        final int RESULT    = nameCursor-1;  // either the call, zero instance, or the cast
         Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
         if (needsInit)
             names[INIT_BAR] = new Name(getFunction(NF_ensureInitialized), names[DMH_THIS]);
-        if (needsCast && !isGetter)
-            names[PRE_CAST] = new Name(getFunction(NF_checkCast), names[DMH_THIS], names[SET_VALUE]);
+        if (!isGetter) {
+            if (isNullRestricted)
+                names[NULL_CHECK] = new Name(getFunction(NF_nullCheck), names[SET_VALUE]);
+            if (needsCast)
+                names[PRE_CAST] = new Name(getFunction(NF_checkCast), names[DMH_THIS], names[SET_VALUE]);
+        }
         Object[] outArgs = new Object[1 + linkerType.parameterCount()];
-        assert(outArgs.length == (isGetter ? 3 : 4));
+        assert (outArgs.length == (isGetter ? 3 : 4) + (isFlat ? 1 : 0));
         outArgs[0] = names[U_HOLDER] = new Name(getFunction(NF_UNSAFE));
         if (isStatic) {
             outArgs[1] = names[F_HOLDER]  = new Name(getFunction(NF_staticBase), names[DMH_THIS]);
             outArgs[2] = names[F_OFFSET]  = new Name(getFunction(NF_staticOffset), names[DMH_THIS]);
         } else {
             outArgs[1] = names[OBJ_CHECK] = new Name(getFunction(NF_checkBase), names[OBJ_BASE]);
             outArgs[2] = names[F_OFFSET]  = new Name(getFunction(NF_fieldOffset), names[DMH_THIS]);
         }
+        int x = 3;
+        if (isFlat) {
+            outArgs[x++] = names[VALUE_TYPE] = isStatic ? new Name(getFunction(NF_staticFieldType), names[DMH_THIS])
+                                                        : new Name(getFunction(NF_fieldType), names[DMH_THIS]);
+        }
         if (!isGetter) {
-            outArgs[3] = (needsCast ? names[PRE_CAST] : names[SET_VALUE]);
+            outArgs[x] = (needsCast ? names[PRE_CAST] : names[SET_VALUE]);
         }
         for (Object a : outArgs)  assert(a != null);
         names[LINKER_CALL] = new Name(linker, outArgs);
-        if (needsCast && isGetter)
-            names[POST_CAST] = new Name(getFunction(NF_checkCast), names[DMH_THIS], names[LINKER_CALL]);
+        if (isGetter) {
+            int argIndex = LINKER_CALL;
+            if (isNullRestricted) {
+                names[FIELD_TYPE] = isStatic ? new Name(getFunction(NF_staticFieldType), names[DMH_THIS])
+                                             : new Name(getFunction(NF_fieldType), names[DMH_THIS]);
+                names[ZERO_INSTANCE] = new Name(getFunction(NF_zeroInstance), names[FIELD_TYPE], names[LINKER_CALL]);
+                argIndex = ZERO_INSTANCE;
+            }
+            if (needsCast)
+                names[POST_CAST] = new Name(getFunction(NF_checkCast), names[DMH_THIS], names[argIndex]);
+        }
         for (Name n : names)  assert(n != null);
 
         LambdaForm form;
         if (needsCast || needsInit) {
             // can't use the pre-generated form when casting and/or initializing
@@ -851,11 +903,15 @@
             NF_checkCast = 7,
             NF_allocateInstance = 8,
             NF_constructorMethod = 9,
             NF_UNSAFE = 10,
             NF_checkReceiver = 11,
-            NF_LIMIT = 12;
+            NF_fieldType = 12,
+            NF_staticFieldType = 13,
+            NF_zeroInstance = 14,
+            NF_nullCheck = 15,
+            NF_LIMIT = 16;
 
     private static final @Stable NamedFunction[] NFS = new NamedFunction[NF_LIMIT];
 
     private static NamedFunction getFunction(byte func) {
         NamedFunction nf = NFS[func];
@@ -866,10 +922,12 @@
         nf = NFS[func] = createFunction(func);
         assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf));
         return nf;
     }
 
+    private static final MethodType CLS_OBJ_TYPE = MethodType.methodType(Class.class, Object.class);
+
     private static final MethodType OBJ_OBJ_TYPE = MethodType.methodType(Object.class, Object.class);
 
     private static final MethodType LONG_OBJ_TYPE = MethodType.methodType(long.class, Object.class);
 
     private static NamedFunction createFunction(byte func) {
@@ -905,10 +963,18 @@
                     member = new MemberName(DirectMethodHandle.class, "checkReceiver", OBJ_OBJ_TYPE, REF_invokeVirtual);
                     return new NamedFunction(
                             MemberName.getFactory().resolveOrFail(REF_invokeVirtual, member,
                                                                   DirectMethodHandle.class, LM_TRUSTED,
                                                                   NoSuchMethodException.class));
+                case NF_fieldType:
+                    return getNamedFunction("fieldType", CLS_OBJ_TYPE);
+                case NF_staticFieldType:
+                    return getNamedFunction("staticFieldType", CLS_OBJ_TYPE);
+                case NF_zeroInstance:
+                    return getNamedFunction("zeroInstanceIfNull", MethodType.methodType(Object.class, Class.class, Object.class));
+                case NF_nullCheck:
+                    return getNamedFunction("nullCheck", OBJ_OBJ_TYPE);
                 default:
                     throw newInternalError("Unknown function: " + func);
             }
         } catch (ReflectiveOperationException ex) {
             throw newInternalError(ex);
diff a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
--- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
+++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
@@ -23,20 +23,22 @@
  * questions.
  */
 
 package java.lang.invoke;
 
+import jdk.internal.misc.PreviewFeatures;
 import jdk.internal.misc.CDS;
 import jdk.internal.org.objectweb.asm.*;
 import jdk.internal.util.ClassFileDumper;
 import sun.invoke.util.BytecodeDescriptor;
 import sun.invoke.util.VerifyAccess;
 import sun.security.action.GetBooleanAction;
 
 import java.io.Serializable;
 import java.lang.constant.ConstantDescs;
 import java.lang.reflect.Modifier;
+import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.Set;
 
 import static java.lang.invoke.MethodHandleStatics.CLASSFILE_VERSION;
 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
@@ -312,11 +314,12 @@
                 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(i);
             }
             interfaceNames = itfs.toArray(new String[itfs.size()]);
         }
 
-        cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
+        int version = CLASSFILE_VERSION | (PreviewFeatures.isEnabled() ? Opcodes.V_PREVIEW : 0);
+        cw.visit(version, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
                  lambdaClassName, null,
                  JAVA_LANG_OBJECT, interfaceNames);
 
         // Generate final fields to be filled in by constructor
         for (int i = 0; i < argDescs.length; i++) {
@@ -350,10 +353,20 @@
         if (isSerializable)
             generateSerializationFriendlyMethods();
         else if (accidentallySerializable)
             generateSerializationHostileMethods();
 
+        // generate Preload attribute if it references any value class
+        PreloadAttributeBuilder builder = new PreloadAttributeBuilder(targetClass);
+        builder.add(factoryType)
+               .add(interfaceMethodType)
+               .add(implMethodType)
+               .add(dynamicMethodType)
+               .add(altMethods);
+        if (!builder.isEmpty())
+            cw.visitAttribute(builder.build());
+
         cw.visitEnd();
 
         // Define the generated class in this VM.
 
         final byte[] classBytes = cw.toByteArray();
@@ -564,10 +577,76 @@
                 default -> throw new InternalError("Unexpected invocation kind: " + implKind);
             };
         }
     }
 
+    /*
+     * Preload attribute builder
+     */
+    static class PreloadAttributeBuilder {
+        private final Set<Class<?>> preloadClasses = new HashSet<>();
+        PreloadAttributeBuilder(Class<?> targetClass) {
+            if (requiresPreload(targetClass)) {
+                preloadClasses.add(targetClass);
+            }
+        }
+
+        /*
+         * Add the value types referenced in the given MethodType.
+         */
+        PreloadAttributeBuilder add(MethodType mt) {
+            // parameter types
+            for (Class<?> paramType : mt.ptypes()) {
+                if (requiresPreload(paramType)) {
+                    preloadClasses.add(paramType);
+                }
+            }
+            // return type
+            if (requiresPreload(mt.returnType())) {
+                preloadClasses.add(mt.returnType());
+            }
+            return this;
+        }
+
+        PreloadAttributeBuilder add(MethodType... mtypes) {
+            for (MethodType mt : mtypes) {
+                add(mt);
+            }
+            return this;
+        }
+
+        boolean requiresPreload(Class<?> cls) {
+            Class<?> c = cls;
+            while (c.isArray()) {
+                c = c.getComponentType();
+            }
+            return c.isValue();
+        }
+
+        boolean isEmpty() {
+            return preloadClasses.isEmpty();
+        }
+
+        Attribute build() {
+            return new Attribute("Preload") {
+                @Override
+                protected ByteVector write(ClassWriter cw,
+                                           byte[] code,
+                                           int len,
+                                           int maxStack,
+                                           int maxLocals) {
+                    ByteVector attr = new ByteVector();
+                    attr.putShort(preloadClasses.size());
+                    for (Class<?> c : preloadClasses) {
+                        attr.putShort(cw.newClass(Type.getInternalName(c)));
+                    }
+                    return attr;
+                }
+            };
+        }
+    }
+
     static int getParameterSize(Class<?> c) {
         if (c == Void.TYPE) {
             return 0;
         } else if (c == Long.TYPE || c == Double.TYPE) {
             return 2;
diff a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
--- a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
+++ b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
@@ -259,10 +259,14 @@
         DIRECT_INVOKE_STATIC_INIT("DMH.invokeStaticInit", "invokeStaticInit"),
         GET_REFERENCE("getReference"),
         PUT_REFERENCE("putReference"),
         GET_REFERENCE_VOLATILE("getReferenceVolatile"),
         PUT_REFERENCE_VOLATILE("putReferenceVolatile"),
+        GET_VALUE("getValue"),
+        PUT_VALUE("putValue"),
+        GET_VALUE_VOLATILE("getValueVolatile"),
+        PUT_VALUE_VOLATILE("putValueVolatile"),
         GET_INT("getInt"),
         PUT_INT("putInt"),
         GET_INT_VOLATILE("getIntVolatile"),
         PUT_INT_VOLATILE("putIntVolatile"),
         GET_BOOLEAN("getBoolean"),
diff a/src/java.base/share/classes/java/lang/invoke/MemberName.java b/src/java.base/share/classes/java/lang/invoke/MemberName.java
--- a/src/java.base/share/classes/java/lang/invoke/MemberName.java
+++ b/src/java.base/share/classes/java/lang/invoke/MemberName.java
@@ -23,10 +23,13 @@
  * questions.
  */
 
 package java.lang.invoke;
 
+import jdk.internal.value.CheckedType;
+import jdk.internal.value.NormalCheckedType;
+import jdk.internal.value.NullRestrictedCheckedType;
 import sun.invoke.util.VerifyAccess;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Member;
@@ -222,10 +225,18 @@
             assert type instanceof Class<?> : "bad field type " + type;
         }
         return (Class<?>) type;
     }
 
+    /**
+     * Return {@code CheckedType} representing the type of this member.
+     */
+    public CheckedType getCheckedFieldType() {
+        return isNullRestricted() ? NullRestrictedCheckedType.of(getFieldType())
+                                  : NormalCheckedType.of(getFieldType());
+    }
+
     /** Utility method to produce either the method type or field type of this member. */
     public Object getType() {
         return (isInvocable() ? getMethodType() : getFieldType());
     }
 
@@ -406,15 +417,16 @@
         return Modifier.isNative(flags);
     }
     // let the rest (native, volatile, transient, etc.) be tested via Modifier.isFoo
 
     // unofficial modifier flags, used by HotSpot:
-    static final int BRIDGE    = 0x00000040;
-    static final int VARARGS   = 0x00000080;
-    static final int SYNTHETIC = 0x00001000;
-    static final int ANNOTATION= 0x00002000;
-    static final int ENUM      = 0x00004000;
+    static final int BRIDGE      = 0x00000040;
+    static final int VARARGS     = 0x00000080;
+    static final int SYNTHETIC   = 0x00001000;
+    static final int ANNOTATION  = 0x00002000;
+    static final int ENUM        = 0x00004000;
+
     /** Utility method to query the modifier flags of this member; returns false if the member is not a method. */
     public boolean isBridge() {
         return allFlagsSet(IS_METHOD | BRIDGE);
     }
     /** Utility method to query the modifier flags of this member; returns false if the member is not a method. */
@@ -424,23 +436,29 @@
     /** Utility method to query the modifier flags of this member; returns false if the member is not a method. */
     public boolean isSynthetic() {
         return allFlagsSet(SYNTHETIC);
     }
 
-    static final String CONSTRUCTOR_NAME = "<init>";  // the ever-popular
+    /** Query whether this member is a flat field */
+    public boolean isFlat() { return (flags & MN_FLAT_FIELD) == MN_FLAT_FIELD; }
+
+    /** Query whether this member is a null-restricted field */
+    public boolean isNullRestricted() { return (flags & MN_NULL_RESTRICTED) == MN_NULL_RESTRICTED; }
+
+    static final String CONSTRUCTOR_NAME = "<init>";
 
     // modifiers exported by the JVM:
     static final int RECOGNIZED_MODIFIERS = 0xFFFF;
 
     // private flags, not part of RECOGNIZED_MODIFIERS:
     static final int
-            IS_METHOD        = MN_IS_METHOD,        // method (not constructor)
-            IS_CONSTRUCTOR   = MN_IS_CONSTRUCTOR,   // constructor
-            IS_FIELD         = MN_IS_FIELD,         // field
-            IS_TYPE          = MN_IS_TYPE,          // nested type
-            CALLER_SENSITIVE = MN_CALLER_SENSITIVE, // @CallerSensitive annotation detected
-            TRUSTED_FINAL    = MN_TRUSTED_FINAL;    // trusted final field
+            IS_METHOD             = MN_IS_METHOD,              // method (not constructor)
+            IS_CONSTRUCTOR        = MN_IS_CONSTRUCTOR,         // constructor
+            IS_FIELD              = MN_IS_FIELD,               // field
+            IS_TYPE               = MN_IS_TYPE,                // nested type
+            CALLER_SENSITIVE      = MN_CALLER_SENSITIVE,       // @CallerSensitive annotation detected
+            TRUSTED_FINAL         = MN_TRUSTED_FINAL;          // trusted final field
 
     static final int ALL_ACCESS = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED;
     static final int ALL_KINDS = IS_METHOD | IS_CONSTRUCTOR | IS_FIELD | IS_TYPE;
     static final int IS_INVOCABLE = IS_METHOD | IS_CONSTRUCTOR;
 
@@ -753,11 +771,11 @@
      *  The declaring class may be supplied as null if this is to be a bare name and type.
      *  The last argument is optional, a boolean which requests REF_invokeSpecial.
      *  The resulting name will in an unresolved state.
      */
     public MemberName(Class<?> defClass, String name, MethodType type, byte refKind) {
-        int initFlags = (name != null && name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD);
+        int initFlags = CONSTRUCTOR_NAME.equals(name) ? IS_CONSTRUCTOR : IS_METHOD;
         init(defClass, name, type, flagsMods(initFlags, 0, refKind));
         initResolved(false);
     }
     /** Create a method, constructor, or field name from the given components:
      *  Reference kind, declaring class, name, type.
diff a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
@@ -1561,10 +1561,16 @@
 
             public boolean isHiddenMember(int flags) {
                 return (flags & MN_HIDDEN_MEMBER) == MN_HIDDEN_MEMBER;
             }
 
+            public boolean isNullRestrictedField(MethodHandle mh) {
+                var memberName = mh.internalMemberName();
+                assert memberName.isField();
+                return memberName.isNullRestricted();
+            }
+
             @Override
             public Map<String, byte[]> generateHolderClasses(Stream<String> traces) {
                 return GenerateJLIClassesHelper.generateHolderClasses(traces);
             }
 
diff a/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java
@@ -108,19 +108,21 @@
      */
     static class Constants {
         Constants() { } // static only
 
         static final int
-            MN_IS_METHOD           = 0x00010000, // method (not constructor)
-            MN_IS_CONSTRUCTOR      = 0x00020000, // constructor
-            MN_IS_FIELD            = 0x00040000, // field
-            MN_IS_TYPE             = 0x00080000, // nested type
-            MN_CALLER_SENSITIVE    = 0x00100000, // @CallerSensitive annotation detected
-            MN_TRUSTED_FINAL       = 0x00200000, // trusted final field
-            MN_HIDDEN_MEMBER       = 0x00400000, // members defined in a hidden class or with @Hidden
-            MN_REFERENCE_KIND_SHIFT = 24, // refKind
-            MN_REFERENCE_KIND_MASK = 0x0F000000 >> MN_REFERENCE_KIND_SHIFT;
+            MN_IS_METHOD             = 0x00010000, // method (not object constructor)
+            MN_IS_CONSTRUCTOR        = 0x00020000, // object constructor
+            MN_IS_FIELD              = 0x00040000, // field
+            MN_IS_TYPE               = 0x00080000, // nested type
+            MN_CALLER_SENSITIVE      = 0x00100000, // @CallerSensitive annotation detected
+            MN_TRUSTED_FINAL         = 0x00200000, // trusted final field
+            MN_HIDDEN_MEMBER         = 0x00400000, // members defined in a hidden class or with @Hidden
+            MN_FLAT_FIELD            = 0x00800000, // flat field
+            MN_NULL_RESTRICTED       = 0x01000000, // null-restricted field
+            MN_REFERENCE_KIND_SHIFT  = 26, // refKind
+            MN_REFERENCE_KIND_MASK   = 0x3C000000 >> MN_REFERENCE_KIND_SHIFT;
 
         /**
          * Constant pool reference-kind codes, as used by CONSTANT_MethodHandle CP entries.
          */
         static final byte
diff a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 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.  Oracle designates this
@@ -50,10 +50,11 @@
 import jdk.internal.access.SharedSecrets;
 import java.lang.classfile.ClassHierarchyResolver;
 import java.lang.classfile.ClassFile;
 import java.lang.classfile.CodeBuilder;
 import java.lang.classfile.TypeKind;
+import jdk.internal.misc.PreviewFeatures;
 import jdk.internal.module.Modules;
 import jdk.internal.reflect.CallerSensitive;
 import jdk.internal.reflect.Reflection;
 import jdk.internal.util.ClassFileDumper;
 import sun.reflect.misc.ReflectUtil;
@@ -368,11 +369,11 @@
     private static byte[] createTemplate(ClassLoader loader, ClassDesc proxyDesc, ClassDesc ifaceDesc,
                                          String methodName, List<MethodInfo> methods) {
         return ClassFile.of(ClassHierarchyResolverOption.of(ClassHierarchyResolver.ofClassLoading(loader)))
                         .build(proxyDesc, clb -> {
             clb.withSuperclass(CD_Object);
-            clb.withFlags(ACC_FINAL | ACC_SYNTHETIC);
+            clb.withFlags((PreviewFeatures.isEnabled() ? ACC_IDENTITY  : 0) | ACC_FINAL | ACC_SYNTHETIC);
             clb.withInterfaceSymbols(ifaceDesc);
 
             // static and instance fields
             clb.withField(TYPE_NAME, CD_Class, ACC_PRIVATE | ACC_STATIC | ACC_FINAL);
             clb.withField(TARGET_NAME, CD_MethodHandle, ACC_PRIVATE | ACC_FINAL);
diff a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
@@ -2801,10 +2801,12 @@
   ProcessBuilder.class, methodType(void.class, String[].class));
 ProcessBuilder pb = (ProcessBuilder)
   MH_newProcessBuilder.invoke("x", "y", "z");
 assertEquals("[x, y, z]", pb.command().toString());
          * }
+         *
+         *
          * @param refc the class or interface from which the method is accessed
          * @param type the type of the method, with the receiver argument omitted, and a void return type
          * @return the desired method handle
          * @throws NoSuchMethodException if the constructor does not exist
          * @throws IllegalAccessException if access checking fails
@@ -2816,10 +2818,13 @@
          */
         public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoSuchMethodException, IllegalAccessException {
             if (refc.isArray()) {
                 throw new NoSuchMethodException("no constructor for array class: " + refc.getName());
             }
+            if (type.returnType() != void.class) {
+                throw new NoSuchMethodException("Constructors must have void return type: " + refc.getName());
+            }
             String name = ConstantDescs.INIT_NAME;
             MemberName ctor = resolveOrFail(REF_newInvokeSpecial, refc, name, type);
             return getDirectConstructor(refc, ctor);
         }
 
@@ -4084,11 +4089,10 @@
             checkMethod(refKind, refc, method);
             // Optionally check with the security manager; this isn't needed for unreflect* calls.
             if (checkSecurity)
                 checkSecurityManager(refc, method);
             assert(!method.isMethodHandleInvoke());
-
             if (refKind == REF_invokeSpecial &&
                 refc != lookupClass() &&
                 !refc.isInterface() && !lookupClass().isInterface() &&
                 refc != lookupClass().getSuperclass() &&
                 refc.isAssignableFrom(lookupClass())) {
diff a/src/java.base/share/classes/java/lang/invoke/MethodType.java b/src/java.base/share/classes/java/lang/invoke/MethodType.java
--- a/src/java.base/share/classes/java/lang/invoke/MethodType.java
+++ b/src/java.base/share/classes/java/lang/invoke/MethodType.java
@@ -26,24 +26,20 @@
 package java.lang.invoke;
 
 import java.lang.constant.ClassDesc;
 import java.lang.constant.Constable;
 import java.lang.constant.MethodTypeDesc;
-import java.lang.ref.Reference;
-import java.lang.ref.ReferenceQueue;
-import java.lang.ref.WeakReference;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.function.Supplier;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.StringJoiner;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
 import java.util.stream.Stream;
 
 import jdk.internal.util.ReferencedKeySet;
 import jdk.internal.util.ReferenceKey;
 import jdk.internal.vm.annotation.Stable;
diff a/src/java.base/share/classes/java/lang/invoke/VarHandle.java b/src/java.base/share/classes/java/lang/invoke/VarHandle.java
--- a/src/java.base/share/classes/java/lang/invoke/VarHandle.java
+++ b/src/java.base/share/classes/java/lang/invoke/VarHandle.java
@@ -504,11 +504,14 @@
              VarHandleReferences.Array,
              VarHandleReferences.FieldInstanceReadOnly,
              VarHandleReferences.FieldStaticReadOnly,
              VarHandleShorts.Array,
              VarHandleShorts.FieldInstanceReadOnly,
-             VarHandleShorts.FieldStaticReadOnly {
+             VarHandleShorts.FieldStaticReadOnly,
+             VarHandleValues.Array,
+             VarHandleValues.FieldInstanceReadOnly,
+             VarHandleValues.FieldStaticReadOnly {
     final VarForm vform;
     final boolean exact;
 
     VarHandle(VarForm vform) {
         this(vform, false);
diff a/src/java.base/share/classes/java/lang/invoke/VarHandles.java b/src/java.base/share/classes/java/lang/invoke/VarHandles.java
--- a/src/java.base/share/classes/java/lang/invoke/VarHandles.java
+++ b/src/java.base/share/classes/java/lang/invoke/VarHandles.java
@@ -57,13 +57,19 @@
     static VarHandle makeFieldHandle(MemberName f, Class<?> refc, boolean isWriteAllowedOnFinalFields) {
         if (!f.isStatic()) {
             long foffset = MethodHandleNatives.objectFieldOffset(f);
             Class<?> type = f.getFieldType();
             if (!type.isPrimitive()) {
-                return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
-                       ? new VarHandleReferences.FieldInstanceReadOnly(refc, foffset, type)
-                       : new VarHandleReferences.FieldInstanceReadWrite(refc, foffset, type));
+                if (f.isFlat()) {
+                    return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
+                        ? new VarHandleValues.FieldInstanceReadOnly(refc, foffset, type, f.getCheckedFieldType())
+                        : new VarHandleValues.FieldInstanceReadWrite(refc, foffset, type, f.getCheckedFieldType()));
+                } else {
+                    return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
+                       ? new VarHandleReferences.FieldInstanceReadOnly(refc, foffset, type, f.getCheckedFieldType())
+                       : new VarHandleReferences.FieldInstanceReadWrite(refc, foffset, type, f.getCheckedFieldType()));
+                }
             }
             else if (type == boolean.class) {
                 return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
                        ? new VarHandleBooleans.FieldInstanceReadOnly(refc, foffset)
                        : new VarHandleBooleans.FieldInstanceReadWrite(refc, foffset));
@@ -119,13 +125,19 @@
     static VarHandle makeStaticFieldVarHandle(Class<?> decl, MemberName f, boolean isWriteAllowedOnFinalFields) {
         Object base = MethodHandleNatives.staticFieldBase(f);
         long foffset = MethodHandleNatives.staticFieldOffset(f);
         Class<?> type = f.getFieldType();
         if (!type.isPrimitive()) {
-            return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
-                    ? new VarHandleReferences.FieldStaticReadOnly(decl, base, foffset, type)
-                    : new VarHandleReferences.FieldStaticReadWrite(decl, base, foffset, type));
+            if (f.isFlat()) {
+                return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
+                        ? new VarHandleValues.FieldStaticReadOnly(decl, base, foffset, type, f.getCheckedFieldType())
+                        : new VarHandleValues.FieldStaticReadWrite(decl, base, foffset, type, f.getCheckedFieldType()));
+            } else {
+                return f.isFinal() && !isWriteAllowedOnFinalFields
+                        ? new VarHandleReferences.FieldStaticReadOnly(decl, base, foffset, type, f.getCheckedFieldType())
+                        : new VarHandleReferences.FieldStaticReadWrite(decl, base, foffset, type, f.getCheckedFieldType());
+            }
         }
         else if (type == boolean.class) {
             return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields
                     ? new VarHandleBooleans.FieldStaticReadOnly(decl, base, foffset)
                     : new VarHandleBooleans.FieldStaticReadWrite(decl, base, foffset));
@@ -209,11 +221,13 @@
         int aoffset = UNSAFE.arrayBaseOffset(arrayClass);
         int ascale = UNSAFE.arrayIndexScale(arrayClass);
         int ashift = 31 - Integer.numberOfLeadingZeros(ascale);
 
         if (!componentType.isPrimitive()) {
-            return maybeAdapt(new VarHandleReferences.Array(aoffset, ashift, arrayClass));
+            return maybeAdapt(UNSAFE.isFlatArray(arrayClass)
+                ? new VarHandleValues.Array(aoffset, ashift, arrayClass)
+                : new VarHandleReferences.Array(aoffset, ashift, arrayClass));
         }
         else if (componentType == boolean.class) {
             return maybeAdapt(new VarHandleBooleans.Array(aoffset, ashift));
         }
         else if (componentType == byte.class) {
diff a/src/java.base/share/classes/java/lang/invoke/X-VarHandle.java.template b/src/java.base/share/classes/java/lang/invoke/X-VarHandle.java.template
--- a/src/java.base/share/classes/java/lang/invoke/X-VarHandle.java.template
+++ b/src/java.base/share/classes/java/lang/invoke/X-VarHandle.java.template
@@ -22,10 +22,15 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
 package java.lang.invoke;
 
+#if[Object]
+import jdk.internal.value.CheckedType;
+import jdk.internal.value.NullRestrictedCheckedType;
+import jdk.internal.value.ValueClass;
+#end[Object]
 import jdk.internal.util.Preconditions;
 import jdk.internal.vm.annotation.ForceInline;
 
 import java.lang.invoke.VarHandle.VarHandleDesc;
 import java.util.Objects;
@@ -40,38 +45,40 @@
     static sealed class FieldInstanceReadOnly extends VarHandle {
         final long fieldOffset;
         final Class<?> receiverType;
 #if[Object]
         final Class<?> fieldType;
+        final CheckedType checkedFieldType;
 #end[Object]
 
-        FieldInstanceReadOnly(Class<?> receiverType, long fieldOffset{#if[Object]?, Class<?> fieldType}) {
-            this(receiverType, fieldOffset{#if[Object]?, fieldType}, FieldInstanceReadOnly.FORM, false);
+        FieldInstanceReadOnly(Class<?> receiverType, long fieldOffset{#if[Object]?, Class<?> fieldType, CheckedType checkedFieldType}) {
+            this(receiverType, fieldOffset{#if[Object]?, fieldType, checkedFieldType}, FieldInstanceReadOnly.FORM, false);
         }
 
-        protected FieldInstanceReadOnly(Class<?> receiverType, long fieldOffset{#if[Object]?, Class<?> fieldType},
+        protected FieldInstanceReadOnly(Class<?> receiverType, long fieldOffset{#if[Object]?, Class<?> fieldType, CheckedType checkedFieldType},
                                         VarForm form, boolean exact) {
             super(form, exact);
             this.fieldOffset = fieldOffset;
             this.receiverType = receiverType;
 #if[Object]
             this.fieldType = fieldType;
+            this.checkedFieldType = checkedFieldType;
 #end[Object]
         }
 
         @Override
         public FieldInstanceReadOnly withInvokeExactBehavior() {
             return hasInvokeExactBehavior()
                 ? this
-                : new FieldInstanceReadOnly(receiverType, fieldOffset{#if[Object]?, fieldType}, vform, true);
+                : new FieldInstanceReadOnly(receiverType, fieldOffset{#if[Object]?, fieldType, checkedFieldType}, vform, true);
         }
 
         @Override
         public FieldInstanceReadOnly withInvokeBehavior() {
             return !hasInvokeExactBehavior()
                 ? this
-                : new FieldInstanceReadOnly(receiverType, fieldOffset{#if[Object]?, fieldType}, vform, false);
+                : new FieldInstanceReadOnly(receiverType, fieldOffset{#if[Object]?, fieldType, checkedFieldType}, vform, false);
         }
 
         @Override
         final MethodType accessModeTypeUncached(AccessType at) {
             return at.accessModeType(receiverType, {#if[Object]?fieldType:$type$.class});
@@ -91,190 +98,220 @@
         }
 
         @ForceInline
         static $type$ get(VarHandle ob, Object holder) {
             FieldInstanceReadOnly handle = (FieldInstanceReadOnly)ob;
-            return UNSAFE.get$Type$(Objects.requireNonNull(handle.receiverType.cast(holder)),
-                                 handle.fieldOffset);
+            $type$ value = UNSAFE.get$Type$(Objects.requireNonNull(handle.receiverType.cast(holder)),
+                                 handle.fieldOffset{#if[Value]?, handle.fieldType});
+#if[Object]
+            if (value == null && handle.checkedFieldType instanceof NullRestrictedCheckedType) {
+                return ValueClass.zeroInstance(handle.fieldType);
+            }
+#end[Object]
+            return value;
         }
 
         @ForceInline
         static $type$ getVolatile(VarHandle ob, Object holder) {
             FieldInstanceReadOnly handle = (FieldInstanceReadOnly)ob;
-            return UNSAFE.get$Type$Volatile(Objects.requireNonNull(handle.receiverType.cast(holder)),
-                                 handle.fieldOffset);
+            $type$ value = UNSAFE.get$Type$Volatile(Objects.requireNonNull(handle.receiverType.cast(holder)),
+                                 handle.fieldOffset{#if[Value]?, handle.fieldType});
+#if[Object]
+            if (value == null && handle.checkedFieldType instanceof NullRestrictedCheckedType) {
+                return ValueClass.zeroInstance(handle.fieldType);
+            }
+#end[Object]
+            return value;
         }
 
         @ForceInline
         static $type$ getOpaque(VarHandle ob, Object holder) {
             FieldInstanceReadOnly handle = (FieldInstanceReadOnly)ob;
-            return UNSAFE.get$Type$Opaque(Objects.requireNonNull(handle.receiverType.cast(holder)),
-                                 handle.fieldOffset);
+            $type$ value = UNSAFE.get$Type$Opaque(Objects.requireNonNull(handle.receiverType.cast(holder)),
+                                 handle.fieldOffset{#if[Value]?, handle.fieldType});
+#if[Object]
+            if (value == null && handle.checkedFieldType instanceof NullRestrictedCheckedType) {
+                return ValueClass.zeroInstance(handle.fieldType);
+            }
+#end[Object]
+            return value;
         }
 
         @ForceInline
         static $type$ getAcquire(VarHandle ob, Object holder) {
             FieldInstanceReadOnly handle = (FieldInstanceReadOnly)ob;
-            return UNSAFE.get$Type$Acquire(Objects.requireNonNull(handle.receiverType.cast(holder)),
-                                 handle.fieldOffset);
+            $type$ value = UNSAFE.get$Type$Acquire(Objects.requireNonNull(handle.receiverType.cast(holder)),
+                                 handle.fieldOffset{#if[Value]?, handle.fieldType});
+#if[Object]
+            if (value == null && handle.checkedFieldType instanceof NullRestrictedCheckedType) {
+                return ValueClass.zeroInstance(handle.fieldType);
+            }
+#end[Object]
+            return value;
         }
 
         static final VarForm FORM = new VarForm(FieldInstanceReadOnly.class, Object.class, $type$.class);
     }
 
     static final class FieldInstanceReadWrite extends FieldInstanceReadOnly {
-
-        FieldInstanceReadWrite(Class<?> receiverType, long fieldOffset{#if[Object]?, Class<?> fieldType}) {
-            this(receiverType, fieldOffset{#if[Object]?, fieldType}, false);
+        FieldInstanceReadWrite(Class<?> receiverType, long fieldOffset{#if[Object]?, Class<?> fieldType, CheckedType checkedFieldType}) {
+            this(receiverType, fieldOffset{#if[Object]?, fieldType, checkedFieldType}, false);
         }
 
-        private FieldInstanceReadWrite(Class<?> receiverType, long fieldOffset{#if[Object]?, Class<?> fieldType},
+        private FieldInstanceReadWrite(Class<?> receiverType, long fieldOffset{#if[Object]?, Class<?> fieldType, CheckedType checkedFieldType},
                                        boolean exact) {
-            super(receiverType, fieldOffset{#if[Object]?, fieldType}, FieldInstanceReadWrite.FORM, exact);
+            super(receiverType, fieldOffset{#if[Object]?, fieldType, checkedFieldType}, FieldInstanceReadWrite.FORM, exact);
         }
 
         @Override
         public FieldInstanceReadWrite withInvokeExactBehavior() {
             return hasInvokeExactBehavior()
                 ? this
-                : new FieldInstanceReadWrite(receiverType, fieldOffset{#if[Object]?, fieldType}, true);
+                : new FieldInstanceReadWrite(receiverType, fieldOffset{#if[Object]?, fieldType, checkedFieldType}, true);
         }
 
         @Override
         public FieldInstanceReadWrite withInvokeBehavior() {
             return !hasInvokeExactBehavior()
                 ? this
-                : new FieldInstanceReadWrite(receiverType, fieldOffset{#if[Object]?, fieldType}, false);
+                : new FieldInstanceReadWrite(receiverType, fieldOffset{#if[Object]?, fieldType, checkedFieldType}, false);
+        }
+
+#if[Object]
+        @ForceInline
+        static Object checkCast(FieldInstanceReadWrite handle, $type$ value) {
+            return handle.checkedFieldType.cast(value);
         }
+#end[Object]
 
         @ForceInline
         static void set(VarHandle ob, Object holder, $type$ value) {
             FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
             UNSAFE.put$Type$(Objects.requireNonNull(handle.receiverType.cast(holder)),
-                             handle.fieldOffset,
-                             {#if[Object]?handle.fieldType.cast(value):value});
+                             handle.fieldOffset{#if[Value]?, handle.fieldType},
+                             {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static void setVolatile(VarHandle ob, Object holder, $type$ value) {
             FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
             UNSAFE.put$Type$Volatile(Objects.requireNonNull(handle.receiverType.cast(holder)),
-                                     handle.fieldOffset,
-                                     {#if[Object]?handle.fieldType.cast(value):value});
+                                     handle.fieldOffset{#if[Value]?, handle.fieldType},
+                                     {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static void setOpaque(VarHandle ob, Object holder, $type$ value) {
             FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
             UNSAFE.put$Type$Opaque(Objects.requireNonNull(handle.receiverType.cast(holder)),
-                                   handle.fieldOffset,
-                                   {#if[Object]?handle.fieldType.cast(value):value});
+                                   handle.fieldOffset{#if[Value]?, handle.fieldType},
+                                   {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static void setRelease(VarHandle ob, Object holder, $type$ value) {
             FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
             UNSAFE.put$Type$Release(Objects.requireNonNull(handle.receiverType.cast(holder)),
-                                    handle.fieldOffset,
-                                    {#if[Object]?handle.fieldType.cast(value):value});
+                                    handle.fieldOffset{#if[Value]?, handle.fieldType},
+                                    {#if[Object]?checkCast(handle, value):value});
         }
 #if[CAS]
 
         @ForceInline
         static boolean compareAndSet(VarHandle ob, Object holder, $type$ expected, $type$ value) {
             FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
             return UNSAFE.compareAndSet$Type$(Objects.requireNonNull(handle.receiverType.cast(holder)),
-                                               handle.fieldOffset,
-                                               {#if[Object]?handle.fieldType.cast(expected):expected},
-                                               {#if[Object]?handle.fieldType.cast(value):value});
+                                               handle.fieldOffset{#if[Object]?, handle.fieldType},
+                                               {#if[Object]?checkCast(handle, expected):expected},
+                                               {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static $type$ compareAndExchange(VarHandle ob, Object holder, $type$ expected, $type$ value) {
             FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
             return UNSAFE.compareAndExchange$Type$(Objects.requireNonNull(handle.receiverType.cast(holder)),
-                                               handle.fieldOffset,
-                                               {#if[Object]?handle.fieldType.cast(expected):expected},
-                                               {#if[Object]?handle.fieldType.cast(value):value});
+                                               handle.fieldOffset{#if[Object]?, handle.fieldType},
+                                               {#if[Object]?checkCast(handle, expected):expected},
+                                               {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static $type$ compareAndExchangeAcquire(VarHandle ob, Object holder, $type$ expected, $type$ value) {
             FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
             return UNSAFE.compareAndExchange$Type$Acquire(Objects.requireNonNull(handle.receiverType.cast(holder)),
-                                               handle.fieldOffset,
-                                               {#if[Object]?handle.fieldType.cast(expected):expected},
-                                               {#if[Object]?handle.fieldType.cast(value):value});
+                                               handle.fieldOffset{#if[Object]?, handle.fieldType},
+                                               {#if[Object]?checkCast(handle, expected):expected},
+                                               {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static $type$ compareAndExchangeRelease(VarHandle ob, Object holder, $type$ expected, $type$ value) {
             FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
             return UNSAFE.compareAndExchange$Type$Release(Objects.requireNonNull(handle.receiverType.cast(holder)),
-                                               handle.fieldOffset,
-                                               {#if[Object]?handle.fieldType.cast(expected):expected},
-                                               {#if[Object]?handle.fieldType.cast(value):value});
+                                               handle.fieldOffset{#if[Object]?, handle.fieldType},
+                                               {#if[Object]?checkCast(handle, expected):expected},
+                                               {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static boolean weakCompareAndSetPlain(VarHandle ob, Object holder, $type$ expected, $type$ value) {
             FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
             return UNSAFE.weakCompareAndSet$Type$Plain(Objects.requireNonNull(handle.receiverType.cast(holder)),
-                                               handle.fieldOffset,
-                                               {#if[Object]?handle.fieldType.cast(expected):expected},
-                                               {#if[Object]?handle.fieldType.cast(value):value});
+                                               handle.fieldOffset{#if[Object]?, handle.fieldType},
+                                               {#if[Object]?checkCast(handle, expected):expected},
+                                               {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static boolean weakCompareAndSet(VarHandle ob, Object holder, $type$ expected, $type$ value) {
             FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
             return UNSAFE.weakCompareAndSet$Type$(Objects.requireNonNull(handle.receiverType.cast(holder)),
-                                               handle.fieldOffset,
-                                               {#if[Object]?handle.fieldType.cast(expected):expected},
-                                               {#if[Object]?handle.fieldType.cast(value):value});
+                                               handle.fieldOffset{#if[Object]?, handle.fieldType},
+                                               {#if[Object]?checkCast(handle, expected):expected},
+                                               {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static boolean weakCompareAndSetAcquire(VarHandle ob, Object holder, $type$ expected, $type$ value) {
             FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
             return UNSAFE.weakCompareAndSet$Type$Acquire(Objects.requireNonNull(handle.receiverType.cast(holder)),
-                                               handle.fieldOffset,
-                                               {#if[Object]?handle.fieldType.cast(expected):expected},
-                                               {#if[Object]?handle.fieldType.cast(value):value});
+                                               handle.fieldOffset{#if[Object]?, handle.fieldType},
+                                               {#if[Object]?checkCast(handle, expected):expected},
+                                               {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static boolean weakCompareAndSetRelease(VarHandle ob, Object holder, $type$ expected, $type$ value) {
             FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
             return UNSAFE.weakCompareAndSet$Type$Release(Objects.requireNonNull(handle.receiverType.cast(holder)),
-                                               handle.fieldOffset,
-                                               {#if[Object]?handle.fieldType.cast(expected):expected},
-                                               {#if[Object]?handle.fieldType.cast(value):value});
+                                               handle.fieldOffset{#if[Object]?, handle.fieldType},
+                                               {#if[Object]?checkCast(handle, expected):expected},
+                                               {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static $type$ getAndSet(VarHandle ob, Object holder, $type$ value) {
             FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
             return UNSAFE.getAndSet$Type$(Objects.requireNonNull(handle.receiverType.cast(holder)),
-                                          handle.fieldOffset,
-                                          {#if[Object]?handle.fieldType.cast(value):value});
+                                          handle.fieldOffset{#if[Value]?, handle.fieldType},
+                                          {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static $type$ getAndSetAcquire(VarHandle ob, Object holder, $type$ value) {
             FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
             return UNSAFE.getAndSet$Type$Acquire(Objects.requireNonNull(handle.receiverType.cast(holder)),
-                                          handle.fieldOffset,
-                                          {#if[Object]?handle.fieldType.cast(value):value});
+                                          handle.fieldOffset{#if[Value]?, handle.fieldType},
+                                          {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static $type$ getAndSetRelease(VarHandle ob, Object holder, $type$ value) {
             FieldInstanceReadWrite handle = (FieldInstanceReadWrite)ob;
             return UNSAFE.getAndSet$Type$Release(Objects.requireNonNull(handle.receiverType.cast(holder)),
-                                          handle.fieldOffset,
-                                          {#if[Object]?handle.fieldType.cast(value):value});
+                                          handle.fieldOffset{#if[Value]?, handle.fieldType},
+                                          {#if[Object]?checkCast(handle, value):value});
         }
 #end[CAS]
 #if[AtomicAdd]
 
         @ForceInline
@@ -385,39 +422,41 @@
         final Class<?> declaringClass;
         final Object base;
         final long fieldOffset;
 #if[Object]
         final Class<?> fieldType;
+        final CheckedType checkedFieldType;
 #end[Object]
 
-        FieldStaticReadOnly(Class<?> declaringClass, Object base, long fieldOffset{#if[Object]?, Class<?> fieldType}) {
-            this(declaringClass, base, fieldOffset{#if[Object]?, fieldType}, FieldStaticReadOnly.FORM, false);
+        FieldStaticReadOnly(Class<?> declaringClass, Object base, long fieldOffset{#if[Object]?, Class<?> fieldType, CheckedType checkedFieldType}) {
+            this(declaringClass, base, fieldOffset{#if[Object]?, fieldType, checkedFieldType}, FieldStaticReadOnly.FORM, false);
         }
 
-        protected FieldStaticReadOnly(Class<?> declaringClass, Object base, long fieldOffset{#if[Object]?, Class<?> fieldType},
+        protected FieldStaticReadOnly(Class<?> declaringClass, Object base, long fieldOffset{#if[Object]?, Class<?> fieldType, CheckedType checkedFieldType},
                                       VarForm form, boolean exact) {
             super(form, exact);
             this.declaringClass = declaringClass;
             this.base = base;
             this.fieldOffset = fieldOffset;
 #if[Object]
             this.fieldType = fieldType;
+            this.checkedFieldType = checkedFieldType;
 #end[Object]
         }
 
         @Override
         public FieldStaticReadOnly withInvokeExactBehavior() {
             return hasInvokeExactBehavior()
                 ? this
-                : new FieldStaticReadOnly(declaringClass, base, fieldOffset{#if[Object]?, fieldType}, vform, true);
+                : new FieldStaticReadOnly(declaringClass, base, fieldOffset{#if[Object]?, fieldType, checkedFieldType}, vform, true);
         }
 
         @Override
         public FieldStaticReadOnly withInvokeBehavior() {
             return !hasInvokeExactBehavior()
                 ? this
-                : new FieldStaticReadOnly(declaringClass, base, fieldOffset{#if[Object]?, fieldType}, vform, false);
+                : new FieldStaticReadOnly(declaringClass, base, fieldOffset{#if[Object]?, fieldType, checkedFieldType}, vform, false);
         }
 
         @Override
         public Optional<VarHandleDesc> describeConstable() {
             var fieldTypeRef = {#if[Object]?fieldType:$type$.class}.describeConstable();
@@ -439,191 +478,222 @@
         }
 
         @ForceInline
         static $type$ get(VarHandle ob) {
             FieldStaticReadOnly handle = (FieldStaticReadOnly) ob.target();
-            return UNSAFE.get$Type$(handle.base,
-                                 handle.fieldOffset);
+            $type$ value = UNSAFE.get$Type$(handle.base,
+                                 handle.fieldOffset{#if[Value]?, handle.fieldType});
+#if[Object]
+            if (value == null && handle.checkedFieldType instanceof NullRestrictedCheckedType) {
+                return ValueClass.zeroInstance(handle.fieldType);
+            }
+#end[Object]
+            return value;
         }
 
         @ForceInline
         static $type$ getVolatile(VarHandle ob) {
             FieldStaticReadOnly handle = (FieldStaticReadOnly) ob.target();
-            return UNSAFE.get$Type$Volatile(handle.base,
-                                 handle.fieldOffset);
+            $type$ value = UNSAFE.get$Type$Volatile(handle.base,
+                                 handle.fieldOffset{#if[Value]?, handle.fieldType});
+#if[Object]
+            if (value == null && handle.checkedFieldType instanceof NullRestrictedCheckedType) {
+                return ValueClass.zeroInstance(handle.fieldType);
+            }
+#end[Object]
+            return value;
         }
 
         @ForceInline
         static $type$ getOpaque(VarHandle ob) {
             FieldStaticReadOnly handle = (FieldStaticReadOnly) ob.target();
-            return UNSAFE.get$Type$Opaque(handle.base,
-                                 handle.fieldOffset);
+            $type$ value = UNSAFE.get$Type$Opaque(handle.base,
+                                 handle.fieldOffset{#if[Value]?, handle.fieldType});
+#if[Object]
+            if (value == null && handle.checkedFieldType instanceof NullRestrictedCheckedType) {
+                return ValueClass.zeroInstance(handle.fieldType);
+            }
+#end[Object]
+            return value;
         }
 
         @ForceInline
         static $type$ getAcquire(VarHandle ob) {
             FieldStaticReadOnly handle = (FieldStaticReadOnly) ob.target();
-            return UNSAFE.get$Type$Acquire(handle.base,
-                                 handle.fieldOffset);
+            $type$ value = UNSAFE.get$Type$Acquire(handle.base,
+                                 handle.fieldOffset{#if[Value]?, handle.fieldType});
+#if[Object]
+            if (value == null && handle.checkedFieldType instanceof NullRestrictedCheckedType) {
+                return ValueClass.zeroInstance(handle.fieldType);
+            }
+#end[Object]
+            return value;
         }
 
         static final VarForm FORM = new VarForm(FieldStaticReadOnly.class, null, $type$.class);
     }
 
     static final class FieldStaticReadWrite extends FieldStaticReadOnly {
 
-        FieldStaticReadWrite(Class<?> declaringClass, Object base, long fieldOffset{#if[Object]?, Class<?> fieldType}) {
-            this(declaringClass, base, fieldOffset{#if[Object]?, fieldType}, false);
+        FieldStaticReadWrite(Class<?> declaringClass, Object base, long fieldOffset{#if[Object]?, Class<?> fieldType, CheckedType checkedFieldType}) {
+            this(declaringClass, base, fieldOffset{#if[Object]?, fieldType, checkedFieldType}, false);
         }
 
-        private FieldStaticReadWrite(Class<?> declaringClass, Object base, long fieldOffset{#if[Object]?, Class<?> fieldType},
+        private FieldStaticReadWrite(Class<?> declaringClass, Object base, long fieldOffset{#if[Object]?, Class<?> fieldType, CheckedType checkedFieldType},
                                      boolean exact) {
-            super(declaringClass, base, fieldOffset{#if[Object]?, fieldType}, FieldStaticReadWrite.FORM, exact);
+            super(declaringClass, base, fieldOffset{#if[Object]?, fieldType, checkedFieldType}, FieldStaticReadWrite.FORM, exact);
         }
 
         @Override
         public FieldStaticReadWrite withInvokeExactBehavior() {
             return hasInvokeExactBehavior()
                 ? this
-                : new FieldStaticReadWrite(declaringClass, base, fieldOffset{#if[Object]?, fieldType}, true);
+                : new FieldStaticReadWrite(declaringClass, base, fieldOffset{#if[Object]?, fieldType, checkedFieldType}, true);
         }
 
         @Override
         public FieldStaticReadWrite withInvokeBehavior() {
             return !hasInvokeExactBehavior()
                 ? this
-                : new FieldStaticReadWrite(declaringClass, base, fieldOffset{#if[Object]?, fieldType}, false);
+                : new FieldStaticReadWrite(declaringClass, base, fieldOffset{#if[Object]?, fieldType, checkedFieldType}, false);
+        }
+
+#if[Object]
+        @ForceInline
+        static Object checkCast(FieldStaticReadWrite handle, $type$ value) {
+            return handle.checkedFieldType.cast(value);
         }
+#end[Object]
 
         @ForceInline
         static void set(VarHandle ob, $type$ value) {
             FieldStaticReadWrite handle = (FieldStaticReadWrite) ob.target();
             UNSAFE.put$Type$(handle.base,
-                             handle.fieldOffset,
-                             {#if[Object]?handle.fieldType.cast(value):value});
+                             handle.fieldOffset{#if[Value]?, handle.fieldType},
+                             {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static void setVolatile(VarHandle ob, $type$ value) {
             FieldStaticReadWrite handle = (FieldStaticReadWrite) ob.target();
             UNSAFE.put$Type$Volatile(handle.base,
-                                     handle.fieldOffset,
-                                     {#if[Object]?handle.fieldType.cast(value):value});
+                                     handle.fieldOffset{#if[Value]?, handle.fieldType},
+                                     {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static void setOpaque(VarHandle ob, $type$ value) {
             FieldStaticReadWrite handle = (FieldStaticReadWrite) ob.target();
             UNSAFE.put$Type$Opaque(handle.base,
-                                   handle.fieldOffset,
-                                   {#if[Object]?handle.fieldType.cast(value):value});
+                                   handle.fieldOffset{#if[Value]?, handle.fieldType},
+                                   {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static void setRelease(VarHandle ob, $type$ value) {
             FieldStaticReadWrite handle = (FieldStaticReadWrite) ob.target();
             UNSAFE.put$Type$Release(handle.base,
-                                    handle.fieldOffset,
-                                    {#if[Object]?handle.fieldType.cast(value):value});
+                                    handle.fieldOffset{#if[Value]?, handle.fieldType},
+                                    {#if[Object]?checkCast(handle, value):value});
         }
 #if[CAS]
 
         @ForceInline
         static boolean compareAndSet(VarHandle ob, $type$ expected, $type$ value) {
             FieldStaticReadWrite handle = (FieldStaticReadWrite) ob.target();
             return UNSAFE.compareAndSet$Type$(handle.base,
-                                               handle.fieldOffset,
-                                               {#if[Object]?handle.fieldType.cast(expected):expected},
-                                               {#if[Object]?handle.fieldType.cast(value):value});
+                                               handle.fieldOffset{#if[Object]?, handle.fieldType},
+                                               {#if[Object]?checkCast(handle, expected):expected},
+                                               {#if[Object]?checkCast(handle, value):value});
         }
 
 
         @ForceInline
         static $type$ compareAndExchange(VarHandle ob, $type$ expected, $type$ value) {
             FieldStaticReadWrite handle = (FieldStaticReadWrite) ob.target();
             return UNSAFE.compareAndExchange$Type$(handle.base,
-                                               handle.fieldOffset,
-                                               {#if[Object]?handle.fieldType.cast(expected):expected},
-                                               {#if[Object]?handle.fieldType.cast(value):value});
+                                               handle.fieldOffset{#if[Object]?, handle.fieldType},
+                                               {#if[Object]?checkCast(handle, expected):expected},
+                                               {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static $type$ compareAndExchangeAcquire(VarHandle ob, $type$ expected, $type$ value) {
             FieldStaticReadWrite handle = (FieldStaticReadWrite) ob.target();
             return UNSAFE.compareAndExchange$Type$Acquire(handle.base,
-                                               handle.fieldOffset,
-                                               {#if[Object]?handle.fieldType.cast(expected):expected},
-                                               {#if[Object]?handle.fieldType.cast(value):value});
+                                               handle.fieldOffset{#if[Object]?, handle.fieldType},
+                                               {#if[Object]?checkCast(handle, expected):expected},
+                                               {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static $type$ compareAndExchangeRelease(VarHandle ob, $type$ expected, $type$ value) {
             FieldStaticReadWrite handle = (FieldStaticReadWrite) ob.target();
             return UNSAFE.compareAndExchange$Type$Release(handle.base,
-                                               handle.fieldOffset,
-                                               {#if[Object]?handle.fieldType.cast(expected):expected},
-                                               {#if[Object]?handle.fieldType.cast(value):value});
+                                               handle.fieldOffset{#if[Object]?, handle.fieldType},
+                                               {#if[Object]?checkCast(handle, expected):expected},
+                                               {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static boolean weakCompareAndSetPlain(VarHandle ob, $type$ expected, $type$ value) {
             FieldStaticReadWrite handle = (FieldStaticReadWrite) ob.target();
             return UNSAFE.weakCompareAndSet$Type$Plain(handle.base,
-                                               handle.fieldOffset,
-                                               {#if[Object]?handle.fieldType.cast(expected):expected},
-                                               {#if[Object]?handle.fieldType.cast(value):value});
+                                               handle.fieldOffset{#if[Object]?, handle.fieldType},
+                                               {#if[Object]?checkCast(handle, expected):expected},
+                                               {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static boolean weakCompareAndSet(VarHandle ob, $type$ expected, $type$ value) {
             FieldStaticReadWrite handle = (FieldStaticReadWrite) ob.target();
             return UNSAFE.weakCompareAndSet$Type$(handle.base,
-                                               handle.fieldOffset,
-                                               {#if[Object]?handle.fieldType.cast(expected):expected},
-                                               {#if[Object]?handle.fieldType.cast(value):value});
+                                               handle.fieldOffset{#if[Object]?, handle.fieldType},
+                                               {#if[Object]?checkCast(handle, expected):expected},
+                                               {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static boolean weakCompareAndSetAcquire(VarHandle ob, $type$ expected, $type$ value) {
             FieldStaticReadWrite handle = (FieldStaticReadWrite) ob.target();
             return UNSAFE.weakCompareAndSet$Type$Acquire(handle.base,
-                                               handle.fieldOffset,
-                                               {#if[Object]?handle.fieldType.cast(expected):expected},
-                                               {#if[Object]?handle.fieldType.cast(value):value});
+                                               handle.fieldOffset{#if[Object]?, handle.fieldType},
+                                               {#if[Object]?checkCast(handle, expected):expected},
+                                               {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static boolean weakCompareAndSetRelease(VarHandle ob, $type$ expected, $type$ value) {
             FieldStaticReadWrite handle = (FieldStaticReadWrite) ob.target();
             return UNSAFE.weakCompareAndSet$Type$Release(handle.base,
-                                               handle.fieldOffset,
-                                               {#if[Object]?handle.fieldType.cast(expected):expected},
-                                               {#if[Object]?handle.fieldType.cast(value):value});
+                                               handle.fieldOffset{#if[Object]?, handle.fieldType},
+                                               {#if[Object]?checkCast(handle, expected):expected},
+                                               {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static $type$ getAndSet(VarHandle ob, $type$ value) {
             FieldStaticReadWrite handle = (FieldStaticReadWrite) ob.target();
             return UNSAFE.getAndSet$Type$(handle.base,
-                                          handle.fieldOffset,
-                                          {#if[Object]?handle.fieldType.cast(value):value});
+                                          handle.fieldOffset{#if[Value]?, handle.fieldType},
+                                          {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static $type$ getAndSetAcquire(VarHandle ob, $type$ value) {
             FieldStaticReadWrite handle = (FieldStaticReadWrite) ob.target();
             return UNSAFE.getAndSet$Type$Acquire(handle.base,
-                                          handle.fieldOffset,
-                                          {#if[Object]?handle.fieldType.cast(value):value});
+                                          handle.fieldOffset{#if[Value]?, handle.fieldType},
+                                          {#if[Object]?checkCast(handle, value):value});
         }
 
         @ForceInline
         static $type$ getAndSetRelease(VarHandle ob, $type$ value) {
             FieldStaticReadWrite handle = (FieldStaticReadWrite) ob.target();
             return UNSAFE.getAndSet$Type$Release(handle.base,
-                                          handle.fieldOffset,
-                                          {#if[Object]?handle.fieldType.cast(value):value});
+                                          handle.fieldOffset{#if[Value]?, handle.fieldType},
+                                          {#if[Object]?checkCast(handle, value):value});
         }
 #end[CAS]
 #if[AtomicAdd]
 
         @ForceInline
@@ -726,11 +796,10 @@
 #end[Bitwise]
 
         static final VarForm FORM = new VarForm(FieldStaticReadWrite.class, null, $type$.class);
     }
 
-
     static final class Array extends VarHandle {
         final int abase;
         final int ashift;
 #if[Object]
         final Class<{#if[Object]??:$type$[]}> arrayType;
@@ -774,29 +843,29 @@
             return Optional.of(VarHandleDesc.ofArray(arrayTypeRef.get()));
         }
 
         @Override
         final MethodType accessModeTypeUncached(AccessType at) {
-            return at.accessModeType({#if[Object]?arrayType:$type$[].class}, {#if[Object]?arrayType.getComponentType():$type$.class}, int.class);
+            return at.accessModeType({#if[Object]?arrayType:$type$[].class}, {#if[Object]?componentType:$type$.class}, int.class);
         }
 
 #if[Object]
         @ForceInline
         static Object runtimeTypeCheck(Array handle, Object[] oarray, Object value) {
             if (handle.arrayType == oarray.getClass()) {
                 // Fast path: static array type same as argument array type
-                return handle.componentType.cast(value);
+                return {#if[Value]?ValueClass.componentCheckedType(oarray):handle.componentType}.cast(value);
             } else {
-                // Slow path: check value against argument array component type
+                // Slow path: check value against argument array component checked type
                 return reflectiveTypeCheck(oarray, value);
             }
         }
 
         @ForceInline
         static Object reflectiveTypeCheck(Object[] oarray, Object value) {
             try {
-                return oarray.getClass().getComponentType().cast(value);
+                return ValueClass.componentCheckedType(oarray).cast(value);
             } catch (ClassCastException e) {
                 throw new ArrayStoreException();
             }
         }
 #end[Object]
@@ -818,35 +887,61 @@
 #if[Object]
             Object[] array = (Object[]) handle.arrayType.cast(oarray);
 #else[Object]
             $type$[] array = ($type$[]) oarray;
 #end[Object]
-            array[index] = {#if[Object]?handle.componentType.cast(value):value};
+#if[Reference]
+            Class<?> arrayType = oarray.getClass();
+            if (handle.arrayType != arrayType && UNSAFE.isFlatArray(arrayType)) {
+                // delegate to VarHandle of flat array
+                VarHandle vh = VarHandleValues.flatArrayVarHandle(arrayType);
+                vh.set(oarray, index, value);
+                return;
+            }
+#end[Reference]
+            array[index] = {#if[Object]?runtimeTypeCheck(handle, array, value):value};
         }
 
         @ForceInline
         static $type$ getVolatile(VarHandle ob, Object oarray, int index) {
             Array handle = (Array)ob;
 #if[Object]
             Object[] array = (Object[]) handle.arrayType.cast(oarray);
 #else[Object]
             $type$[] array = ($type$[]) oarray;
 #end[Object]
+#if[Reference]
+            Class<?> arrayType = oarray.getClass();
+            if (handle.arrayType != arrayType && UNSAFE.isFlatArray(arrayType)) {
+                // delegate to VarHandle of flat array
+                VarHandle vh = VarHandleValues.flatArrayVarHandle(arrayType);
+                return vh.getVolatile(oarray, index);
+            }
+#end[Reference]
             return UNSAFE.get$Type$Volatile(array,
-                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase);
+                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase{#if[Value]?, handle.componentType});
         }
 
         @ForceInline
         static void setVolatile(VarHandle ob, Object oarray, int index, $type$ value) {
             Array handle = (Array)ob;
 #if[Object]
             Object[] array = (Object[]) handle.arrayType.cast(oarray);
 #else[Object]
             $type$[] array = ($type$[]) oarray;
 #end[Object]
+#if[Reference]
+            Class<?> arrayType = oarray.getClass();
+            if (handle.arrayType != arrayType && UNSAFE.isFlatArray(arrayType)) {
+                // delegate to VarHandle of flat array
+                VarHandle vh = VarHandleValues.flatArrayVarHandle(arrayType);
+                vh.setVolatile(oarray, index, value);
+                return;
+            }
+#end[Reference]
             UNSAFE.put$Type$Volatile(array,
-                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase,
+                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase{#if[Value]?, handle.componentType},
                     {#if[Object]?runtimeTypeCheck(handle, array, value):value});
         }
 
         @ForceInline
         static $type$ getOpaque(VarHandle ob, Object oarray, int index) {
@@ -854,24 +949,41 @@
 #if[Object]
             Object[] array = (Object[]) handle.arrayType.cast(oarray);
 #else[Object]
             $type$[] array = ($type$[]) oarray;
 #end[Object]
+#if[Reference]
+            Class<?> arrayType = oarray.getClass();
+            if (handle.arrayType != arrayType && UNSAFE.isFlatArray(arrayType)) {
+                // delegate to VarHandle of flat array
+                VarHandle vh = VarHandleValues.flatArrayVarHandle(arrayType);
+                return vh.getOpaque(oarray, index);
+            }
+#end[Reference]
             return UNSAFE.get$Type$Opaque(array,
-                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase);
+                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase{#if[Value]?, handle.componentType});
         }
 
         @ForceInline
         static void setOpaque(VarHandle ob, Object oarray, int index, $type$ value) {
             Array handle = (Array)ob;
 #if[Object]
             Object[] array = (Object[]) handle.arrayType.cast(oarray);
 #else[Object]
             $type$[] array = ($type$[]) oarray;
 #end[Object]
+#if[Reference]
+            Class<?> arrayType = oarray.getClass();
+            if (handle.arrayType != arrayType && UNSAFE.isFlatArray(arrayType)) {
+                // delegate to VarHandle of flat array
+                VarHandle vh = VarHandleValues.flatArrayVarHandle(arrayType);
+                vh.setOpaque(oarray, index, value);
+                return;
+            }
+#end[Reference]
             UNSAFE.put$Type$Opaque(array,
-                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase,
+                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase{#if[Value]?, handle.componentType},
                     {#if[Object]?runtimeTypeCheck(handle, array, value):value});
         }
 
         @ForceInline
         static $type$ getAcquire(VarHandle ob, Object oarray, int index) {
@@ -879,24 +991,41 @@
 #if[Object]
             Object[] array = (Object[]) handle.arrayType.cast(oarray);
 #else[Object]
             $type$[] array = ($type$[]) oarray;
 #end[Object]
+#if[Reference]
+            Class<?> arrayType = oarray.getClass();
+            if (handle.arrayType != arrayType && UNSAFE.isFlatArray(arrayType)) {
+                // delegate to VarHandle of flat array
+                VarHandle vh = VarHandleValues.flatArrayVarHandle(arrayType);
+                return vh.getAcquire(oarray, index);
+            }
+#end[Reference]
             return UNSAFE.get$Type$Acquire(array,
-                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase);
+                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase{#if[Value]?, handle.componentType});
         }
 
         @ForceInline
         static void setRelease(VarHandle ob, Object oarray, int index, $type$ value) {
             Array handle = (Array)ob;
 #if[Object]
             Object[] array = (Object[]) handle.arrayType.cast(oarray);
 #else[Object]
             $type$[] array = ($type$[]) oarray;
 #end[Object]
+#if[Reference]
+            Class<?> arrayType = oarray.getClass();
+            if (handle.arrayType != arrayType && UNSAFE.isFlatArray(arrayType)) {
+                // delegate to VarHandle of flat array
+                VarHandle vh = VarHandleValues.flatArrayVarHandle(arrayType);
+                vh.setRelease(oarray, index, value);
+                return;
+            }
+#end[Reference]
             UNSAFE.put$Type$Release(array,
-                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase,
+                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase{#if[Value]?, handle.componentType},
                     {#if[Object]?runtimeTypeCheck(handle, array, value):value});
         }
 #if[CAS]
 
         @ForceInline
@@ -905,12 +1034,20 @@
 #if[Object]
             Object[] array = (Object[]) handle.arrayType.cast(oarray);
 #else[Object]
             $type$[] array = ($type$[]) oarray;
 #end[Object]
+#if[Reference]
+            Class<?> arrayType = oarray.getClass();
+            if (handle.arrayType != arrayType && UNSAFE.isFlatArray(arrayType)) {
+                // delegate to VarHandle of flat array
+                VarHandle vh = VarHandleValues.flatArrayVarHandle(arrayType);
+                return vh.compareAndSet(oarray, index, expected, value);
+            }
+#end[Reference]
             return UNSAFE.compareAndSet$Type$(array,
-                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase,
+                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase{#if[Object]?, handle.componentType},
                     {#if[Object]?handle.componentType.cast(expected):expected},
                     {#if[Object]?runtimeTypeCheck(handle, array, value):value});
         }
 
         @ForceInline
@@ -919,12 +1056,20 @@
 #if[Object]
             Object[] array = (Object[]) handle.arrayType.cast(oarray);
 #else[Object]
             $type$[] array = ($type$[]) oarray;
 #end[Object]
+#if[Reference]
+            Class<?> arrayType = oarray.getClass();
+            if (handle.arrayType != arrayType && UNSAFE.isFlatArray(arrayType)) {
+                // delegate to VarHandle of flat array
+                VarHandle vh = VarHandleValues.flatArrayVarHandle(arrayType);
+                return vh.compareAndExchange(oarray, index, expected, value);
+            }
+#end[Reference]
             return UNSAFE.compareAndExchange$Type$(array,
-                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase,
+                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase{#if[Object]?, handle.componentType},
                     {#if[Object]?handle.componentType.cast(expected):expected},
                     {#if[Object]?runtimeTypeCheck(handle, array, value):value});
         }
 
         @ForceInline
@@ -933,12 +1078,20 @@
 #if[Object]
             Object[] array = (Object[]) handle.arrayType.cast(oarray);
 #else[Object]
             $type$[] array = ($type$[]) oarray;
 #end[Object]
+#if[Reference]
+            Class<?> arrayType = oarray.getClass();
+            if (handle.arrayType != arrayType && UNSAFE.isFlatArray(arrayType)) {
+                // delegate to VarHandle of flat array
+                VarHandle vh = VarHandleValues.flatArrayVarHandle(arrayType);
+                return vh.compareAndExchangeAcquire(oarray, index, expected, value);
+            }
+#end[Reference]
             return UNSAFE.compareAndExchange$Type$Acquire(array,
-                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase,
+                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase{#if[Object]?, handle.componentType},
                     {#if[Object]?handle.componentType.cast(expected):expected},
                     {#if[Object]?runtimeTypeCheck(handle, array, value):value});
         }
 
         @ForceInline
@@ -947,12 +1100,20 @@
 #if[Object]
             Object[] array = (Object[]) handle.arrayType.cast(oarray);
 #else[Object]
             $type$[] array = ($type$[]) oarray;
 #end[Object]
+#if[Reference]
+            Class<?> arrayType = oarray.getClass();
+            if (handle.arrayType != arrayType && UNSAFE.isFlatArray(arrayType)) {
+                // delegate to VarHandle of flat array
+                VarHandle vh = VarHandleValues.flatArrayVarHandle(arrayType);
+                return vh.compareAndExchangeRelease(oarray, index, expected, value);
+            }
+#end[Reference]
             return UNSAFE.compareAndExchange$Type$Release(array,
-                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase,
+                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase{#if[Object]?, handle.componentType},
                     {#if[Object]?handle.componentType.cast(expected):expected},
                     {#if[Object]?runtimeTypeCheck(handle, array, value):value});
         }
 
         @ForceInline
@@ -961,12 +1122,20 @@
 #if[Object]
             Object[] array = (Object[]) handle.arrayType.cast(oarray);
 #else[Object]
             $type$[] array = ($type$[]) oarray;
 #end[Object]
+#if[Reference]
+            Class<?> arrayType = oarray.getClass();
+            if (handle.arrayType != arrayType && UNSAFE.isFlatArray(arrayType)) {
+                // delegate to VarHandle of flat array
+                VarHandle vh = VarHandleValues.flatArrayVarHandle(arrayType);
+                return vh.weakCompareAndSetPlain(oarray, index, expected, value);
+            }
+#end[Reference]
             return UNSAFE.weakCompareAndSet$Type$Plain(array,
-                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase,
+                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase{#if[Object]?, handle.componentType},
                     {#if[Object]?handle.componentType.cast(expected):expected},
                     {#if[Object]?runtimeTypeCheck(handle, array, value):value});
         }
 
         @ForceInline
@@ -975,12 +1144,20 @@
 #if[Object]
             Object[] array = (Object[]) handle.arrayType.cast(oarray);
 #else[Object]
             $type$[] array = ($type$[]) oarray;
 #end[Object]
+#if[Reference]
+            Class<?> arrayType = oarray.getClass();
+            if (handle.arrayType != arrayType && UNSAFE.isFlatArray(arrayType)) {
+                // delegate to VarHandle of flat array
+                VarHandle vh = VarHandleValues.flatArrayVarHandle(arrayType);
+                return vh.weakCompareAndSet(oarray, index, expected, value);
+            }
+#end[Reference]
             return UNSAFE.weakCompareAndSet$Type$(array,
-                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase,
+                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase{#if[Object]?, handle.componentType},
                     {#if[Object]?handle.componentType.cast(expected):expected},
                     {#if[Object]?runtimeTypeCheck(handle, array, value):value});
         }
 
         @ForceInline
@@ -989,12 +1166,20 @@
 #if[Object]
             Object[] array = (Object[]) handle.arrayType.cast(oarray);
 #else[Object]
             $type$[] array = ($type$[]) oarray;
 #end[Object]
+#if[Reference]
+            Class<?> arrayType = oarray.getClass();
+            if (handle.arrayType != arrayType && UNSAFE.isFlatArray(arrayType)) {
+                // delegate to VarHandle of flat array
+                VarHandle vh = VarHandleValues.flatArrayVarHandle(arrayType);
+                return vh.weakCompareAndSetAcquire(oarray, index, expected, value);
+            }
+#end[Reference]
             return UNSAFE.weakCompareAndSet$Type$Acquire(array,
-                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase,
+                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase{#if[Object]?, handle.componentType},
                     {#if[Object]?handle.componentType.cast(expected):expected},
                     {#if[Object]?runtimeTypeCheck(handle, array, value):value});
         }
 
         @ForceInline
@@ -1003,12 +1188,20 @@
 #if[Object]
             Object[] array = (Object[]) handle.arrayType.cast(oarray);
 #else[Object]
             $type$[] array = ($type$[]) oarray;
 #end[Object]
+#if[Reference]
+            Class<?> arrayType = oarray.getClass();
+            if (handle.arrayType != arrayType && UNSAFE.isFlatArray(arrayType)) {
+                // delegate to VarHandle of flat array
+                VarHandle vh = VarHandleValues.flatArrayVarHandle(arrayType);
+                return vh.weakCompareAndSetRelease(oarray, index, expected, value);
+            }
+#end[Reference]
             return UNSAFE.weakCompareAndSet$Type$Release(array,
-                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase,
+                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase{#if[Object]?, handle.componentType},
                     {#if[Object]?handle.componentType.cast(expected):expected},
                     {#if[Object]?runtimeTypeCheck(handle, array, value):value});
         }
 
         @ForceInline
@@ -1017,12 +1210,20 @@
 #if[Object]
             Object[] array = (Object[]) handle.arrayType.cast(oarray);
 #else[Object]
             $type$[] array = ($type$[]) oarray;
 #end[Object]
+#if[Reference]
+            Class<?> arrayType = oarray.getClass();
+            if (handle.arrayType != arrayType && UNSAFE.isFlatArray(arrayType)) {
+                // delegate to VarHandle of flat array
+                VarHandle vh = VarHandleValues.flatArrayVarHandle(arrayType);
+                return vh.getAndSet(oarray, index, value);
+            }
+#end[Reference]
             return UNSAFE.getAndSet$Type$(array,
-                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase,
+                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase{#if[Value]?, handle.componentType},
                     {#if[Object]?runtimeTypeCheck(handle, array, value):value});
         }
 
         @ForceInline
         static $type$ getAndSetAcquire(VarHandle ob, Object oarray, int index, $type$ value) {
@@ -1030,12 +1231,20 @@
 #if[Object]
             Object[] array = (Object[]) handle.arrayType.cast(oarray);
 #else[Object]
             $type$[] array = ($type$[]) oarray;
 #end[Object]
+#if[Reference]
+            Class<?> arrayType = oarray.getClass();
+            if (handle.arrayType != arrayType && UNSAFE.isFlatArray(arrayType)) {
+                // delegate to VarHandle of flat array
+                VarHandle vh = VarHandleValues.flatArrayVarHandle(arrayType);
+                return vh.getAndSetAcquire(oarray, index, value);
+            }
+#end[Reference]
             return UNSAFE.getAndSet$Type$Acquire(array,
-                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase,
+                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase{#if[Value]?, handle.componentType},
                     {#if[Object]?runtimeTypeCheck(handle, array, value):value});
         }
 
         @ForceInline
         static $type$ getAndSetRelease(VarHandle ob, Object oarray, int index, $type$ value) {
@@ -1043,12 +1252,20 @@
 #if[Object]
             Object[] array = (Object[]) handle.arrayType.cast(oarray);
 #else[Object]
             $type$[] array = ($type$[]) oarray;
 #end[Object]
+#if[Reference]
+            Class<?> arrayType = oarray.getClass();
+            if (handle.arrayType != arrayType && UNSAFE.isFlatArray(arrayType)) {
+                // delegate to VarHandle of flat array
+                VarHandle vh = VarHandleValues.flatArrayVarHandle(arrayType);
+                return vh.getAndSetRelease(oarray, index, value);
+            }
+#end[Reference]
             return UNSAFE.getAndSet$Type$Release(array,
-                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase,
+                    (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase{#if[Value]?, handle.componentType},
                     {#if[Object]?runtimeTypeCheck(handle, array, value):value});
         }
 #end[CAS]
 #if[AtomicAdd]
 
@@ -1160,9 +1377,22 @@
             return UNSAFE.getAndBitwiseXor$Type$Acquire(array,
                                        (((long) Preconditions.checkIndex(index, array.length, Preconditions.AIOOBE_FORMATTER)) << handle.ashift) + handle.abase,
                                        value);
         }
 #end[Bitwise]
-
         static final VarForm FORM = new VarForm(Array.class, {#if[Object]?Object[].class:$type$[].class}, {#if[Object]?Object.class:$type$.class}, int.class);
     }
+#if[Value]
+    static final ClassValue<Array> flatArrayVarHandles = new ClassValue<>() {
+        @Override protected Array computeValue(Class<?> arrayClass) {
+            assert UNSAFE.isFlatArray(arrayClass);
+            int aoffset = UNSAFE.arrayBaseOffset(arrayClass);
+            int ascale = UNSAFE.arrayIndexScale(arrayClass);
+            int ashift = 31 - Integer.numberOfLeadingZeros(ascale);
+            return new Array(aoffset, ashift, arrayClass);
+        }
+    };
+    static VarHandle flatArrayVarHandle(Class<?> arrayClass) {
+        return flatArrayVarHandles.get(arrayClass);
+    }
+#end[Value]
 }
diff a/src/java.base/share/classes/java/lang/ref/PhantomReference.java b/src/java.base/share/classes/java/lang/ref/PhantomReference.java
--- a/src/java.base/share/classes/java/lang/ref/PhantomReference.java
+++ b/src/java.base/share/classes/java/lang/ref/PhantomReference.java
@@ -23,16 +23,19 @@
  * questions.
  */
 
 package java.lang.ref;
 
+import java.util.Objects;
 import jdk.internal.vm.annotation.IntrinsicCandidate;
 
 /**
  * Phantom reference objects, which are enqueued after the collector
  * determines that their referents may otherwise be reclaimed.  Phantom
  * references are most often used to schedule post-mortem cleanup actions.
+ * <p>
+ * The referent must be an {@linkplain Objects#isIdentityObject(Object) identity object}.
  *
  * <p> Suppose the garbage collector determines at a certain point in time
  * that an object is <a href="package-summary.html#reachability">
  * phantom reachable</a>.  At that time it will atomically clear
  * all phantom references to that object and all phantom references to
@@ -85,10 +88,12 @@
      * queue.  Such a reference will never be enqueued.
      *
      * @param referent the object the new phantom reference will refer to
      * @param q the queue with which the reference is to be registered,
      *          or {@code null} if registration is not required
+     * @throws IdentityException if the referent is not an
+     *         {@link java.util.Objects#isIdentityObject(Object) identity object}
      */
     public PhantomReference(T referent, ReferenceQueue<? super T> q) {
         super(referent, q);
     }
 
diff a/src/java.base/share/classes/java/lang/ref/Reference.java b/src/java.base/share/classes/java/lang/ref/Reference.java
--- a/src/java.base/share/classes/java/lang/ref/Reference.java
+++ b/src/java.base/share/classes/java/lang/ref/Reference.java
@@ -30,15 +30,21 @@
 import jdk.internal.vm.annotation.IntrinsicCandidate;
 import jdk.internal.access.JavaLangRefAccess;
 import jdk.internal.access.SharedSecrets;
 import jdk.internal.ref.Cleaner;
 
+import java.util.Objects;
+
 /**
  * Abstract base class for reference objects.  This class defines the
  * operations common to all reference objects.  Because reference objects are
  * implemented in close cooperation with the garbage collector, this class may
  * not be subclassed directly.
+ * <p>
+ * The referent must be an {@linkplain Objects#isIdentityObject(Object) identity object}.
+ * Attempts to create a reference to a {@linkplain Objects#isValueObject value object}
+ * results in an {@link IdentityException}.
  * @param <T> the type of the referent
  *
  * @author   Mark Reinhold
  * @since    1.2
  * @sealedGraph
@@ -504,10 +510,13 @@
     Reference(T referent) {
         this(referent, null);
     }
 
     Reference(T referent, ReferenceQueue<? super T> queue) {
+        if (referent != null) {
+            Objects.requireIdentity(referent);
+        }
         this.referent = referent;
         this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
     }
 
     /**
diff a/src/java.base/share/classes/java/lang/ref/SoftReference.java b/src/java.base/share/classes/java/lang/ref/SoftReference.java
--- a/src/java.base/share/classes/java/lang/ref/SoftReference.java
+++ b/src/java.base/share/classes/java/lang/ref/SoftReference.java
@@ -24,14 +24,18 @@
  */
 
 package java.lang.ref;
 
 
+import java.util.Objects;
+
 /**
  * Soft reference objects, which are cleared at the discretion of the garbage
  * collector in response to memory demand.  Soft references are most often used
  * to implement memory-sensitive caches.
+ * <p>
+ * The referent must be an {@linkplain Objects#isIdentityObject(Object) identity object}.
  *
  * <p> Suppose that the garbage collector determines at a certain point in time
  * that an object is <a href="package-summary.html#reachability">softly
  * reachable</a>.  At that time it may choose to clear atomically all soft
  * references to that object and all soft references to any other
@@ -79,10 +83,12 @@
     /**
      * Creates a new soft reference that refers to the given object.  The new
      * reference is not registered with any queue.
      *
      * @param referent object the new soft reference will refer to
+     * @throws IdentityException if the referent is not an
+     *         {@link java.util.Objects#isIdentityObject(Object) identity object}
      */
     public SoftReference(T referent) {
         super(referent);
         this.timestamp = clock;
     }
@@ -92,11 +98,12 @@
      * registered with the given queue.
      *
      * @param referent object the new soft reference will refer to
      * @param q the queue with which the reference is to be registered,
      *          or {@code null} if registration is not required
-     *
+     * @throws IdentityException if the referent is not an
+     *         {@link java.util.Objects#isIdentityObject(Object) identity object}
      */
     public SoftReference(T referent, ReferenceQueue<? super T> q) {
         super(referent, q);
         this.timestamp = clock;
     }
diff a/src/java.base/share/classes/java/lang/ref/WeakReference.java b/src/java.base/share/classes/java/lang/ref/WeakReference.java
--- a/src/java.base/share/classes/java/lang/ref/WeakReference.java
+++ b/src/java.base/share/classes/java/lang/ref/WeakReference.java
@@ -24,14 +24,18 @@
  */
 
 package java.lang.ref;
 
 
+import java.util.Objects;
+
 /**
  * Weak reference objects, which do not prevent their referents from being
  * made finalizable, finalized, and then reclaimed.  Weak references are most
  * often used to implement canonicalizing mappings.
+ * <p>
+ * The referent must be an {@linkplain Objects#isIdentityObject(Object) identity object}.
  *
  * <p> Suppose that the garbage collector determines at a certain point in time
  * that an object is <a href="package-summary.html#reachability">weakly
  * reachable</a>.  At that time it will atomically clear all weak references to
  * that object and all weak references to any other weakly-reachable objects
@@ -51,10 +55,12 @@
     /**
      * Creates a new weak reference that refers to the given object.  The new
      * reference is not registered with any queue.
      *
      * @param referent object the new weak reference will refer to
+     * @throws IdentityException if the referent is not an
+     *         {@link java.util.Objects#isIdentityObject(Object) identity object}
      */
     public WeakReference(T referent) {
         super(referent);
     }
 
@@ -63,10 +69,12 @@
      * registered with the given queue.
      *
      * @param referent object the new weak reference will refer to
      * @param q the queue with which the reference is to be registered,
      *          or {@code null} if registration is not required
+     * @throws IdentityException if the referent is not an
+     *         {@link java.util.Objects#isIdentityObject(Object) identity object}
      */
     public WeakReference(T referent, ReferenceQueue<? super T> q) {
         super(referent, q);
     }
 
diff a/src/java.base/share/classes/java/lang/reflect/AccessFlag.java b/src/java.base/share/classes/java/lang/reflect/AccessFlag.java
--- a/src/java.base/share/classes/java/lang/reflect/AccessFlag.java
+++ b/src/java.base/share/classes/java/lang/reflect/AccessFlag.java
@@ -23,10 +23,13 @@
  * questions.
  */
 
 package java.lang.reflect;
 
+import jdk.internal.javac.PreviewFeature;
+import jdk.internal.misc.PreviewFeatures;
+
 import java.util.Collections;
 import java.util.Objects;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Function;
@@ -183,26 +186,56 @@
      * 0x0020}.
      *
      * @apiNote
      * In Java SE 8 and above, the JVM treats the {@code ACC_SUPER}
      * flag as set in every class file (JVMS {@jvms 4.1}).
+     * If preview feature is enabled,
+     * the {@code 0x0020} access flag bit is {@linkplain #IDENTITY IDENTITY access flag}.
+     */
+    SUPER(0x0000_0020, false,
+            PreviewFeatures.isEnabled() ? Location.EMPTY_SET : Location.SET_CLASS,
+            new Function<ClassFileFormatVersion, Set<Location>>() {
+            @Override
+            public Set<Location> apply(ClassFileFormatVersion cffv) {
+                return (cffv.compareTo(ClassFileFormatVersion.RELEASE_22) >= 0) &&
+                        PreviewFeatures.isEnabled() ? Location.EMPTY_SET : Location.SET_CLASS;
+            }
+        }),
+
+    /**
+     * The access flag {@code ACC_IDENTITY}, corresponding to the
+     * source modifier {@link Modifier#IDENTITY identity}, with a mask
+     * value of <code>{@value "0x%04x" Modifier#IDENTITY}</code>.
+     * @jvms 4.1 -B. Class access and property modifiers
+     *
+     * @since Valhalla
      */
-    SUPER(0x0000_0020, false, Location.SET_CLASS, null),
+    @PreviewFeature(feature = PreviewFeature.Feature.VALUE_OBJECTS, reflective=true)
+    IDENTITY(Modifier.IDENTITY, false,
+            PreviewFeatures.isEnabled() ? Location.SET_CLASS_INNER_CLASS : Location.EMPTY_SET,
+            new Function<ClassFileFormatVersion, Set<Location>>() {
+                @Override
+                public Set<Location> apply(ClassFileFormatVersion cffv) {
+                    return (cffv.compareTo(ClassFileFormatVersion.RELEASE_22) >= 0
+                            && PreviewFeatures.isEnabled())
+                            ? Location.SET_CLASS_INNER_CLASS : Location.EMPTY_SET;
+                }
+            }),
 
     /**
      * The module flag {@code ACC_OPEN} with a mask value of {@code
      * 0x0020}.
      * @see java.lang.module.ModuleDescriptor#isOpen
      */
-        OPEN(0x0000_0020, false, Location.SET_MODULE,
-             new Function<ClassFileFormatVersion, Set<Location>>() {
-                 @Override
-                 public Set<Location> apply(ClassFileFormatVersion cffv) {
-                     return (cffv.compareTo(ClassFileFormatVersion.RELEASE_9) >= 0 ) ?
-                         Location.SET_MODULE:
-                         Location.EMPTY_SET;}
-             }),
+    OPEN(0x0000_0020, false, Location.SET_MODULE,
+         new Function<ClassFileFormatVersion, Set<Location>>() {
+             @Override
+             public Set<Location> apply(ClassFileFormatVersion cffv) {
+                 return (cffv.compareTo(ClassFileFormatVersion.RELEASE_9) >= 0 ) ?
+                     Location.SET_MODULE:
+                     Location.EMPTY_SET;}
+         }),
 
     /**
      * The module requires flag {@code ACC_TRANSITIVE} with a mask
      * value of {@code 0x0020}.
      * @see java.lang.module.ModuleDescriptor.Requires.Modifier#TRANSITIVE
@@ -235,11 +268,11 @@
                          return (cffv.compareTo(ClassFileFormatVersion.RELEASE_9) >= 0 ) ?
                              Location.SET_MODULE_REQUIRES:
                              Location.EMPTY_SET;}
                  }),
 
-   /**
+    /**
      * The access flag {@code ACC_VOLATILE}, corresponding to the
      * source modifier {@link Modifier#VOLATILE volatile}, with a mask
      * value of <code>{@value "0x%04x" Modifier#VOLATILE}</code>.
      */
     VOLATILE(Modifier.VOLATILE, true, Location.SET_FIELD, null),
@@ -484,29 +517,68 @@
      * appropriate for the location in question}
      *
      * @param mask bit mask of access flags
      * @param location context to interpret mask value
      * @throws IllegalArgumentException if the mask contains bit
-     * positions not support for the location in question
+     * positions not supported for the location in question
      */
     public static Set<AccessFlag> maskToAccessFlags(int mask, Location location) {
         Set<AccessFlag> result = java.util.EnumSet.noneOf(AccessFlag.class);
         for (var accessFlag : LocationToFlags.locationToFlags.get(location)) {
             int accessMask = accessFlag.mask();
-            if ((mask &  accessMask) != 0) {
+            if ((mask & accessMask) != 0) {
                 result.add(accessFlag);
                 mask = mask & ~accessMask;
+                if (mask == 0) {
+                    break;      // no more mask bits
+                }
             }
         }
         if (mask != 0) {
             throw new IllegalArgumentException("Unmatched bit position 0x" +
                                                Integer.toHexString(mask) +
                                                " for location " + location);
         }
         return Collections.unmodifiableSet(result);
     }
 
+    /**
+     * {@return an unmodifiable set of access flags for the given mask value
+     * appropriate for the location in question}
+     *
+     * @param mask bit mask of access flags
+     * @param location context to interpret mask value
+     * @param cffv the class file format version
+     * @throws IllegalArgumentException if the mask contains bit
+     * positions not supported for the location in question
+     */
+    public static Set<AccessFlag> maskToAccessFlags(int mask, Location location,
+                                                    ClassFileFormatVersion cffv) {
+        Set<AccessFlag> result = java.util.EnumSet.noneOf(AccessFlag.class);
+        for (var accessFlag : AccessFlag.values()) {
+            int accessMask = accessFlag.mask();
+            if ((mask & accessMask) != 0) {
+                var locations = accessFlag.locations(cffv);
+                if (locations.contains(location)) {
+                    result.add(accessFlag);
+                    mask = mask & ~accessMask;
+                    if (mask == 0) {
+                        break;      // no more mask bits
+                    }
+                }
+            }
+        }
+        if (mask != 0) {
+            throw new IllegalArgumentException("Unmatched bit position 0x" +
+                                               Integer.toHexString(mask) +
+                                               " for location " + location +
+                                               " for class file format version " + cffv);
+        }
+        return Collections.unmodifiableSet(result);
+    }
+
+
     /**
      * A location within a class file where flags can be applied.
      *
      * Note that since these locations represent class file structures
      * rather than language structures many language structures, such
@@ -621,25 +693,25 @@
     }
 
     private static class LocationToFlags {
         private static Map<Location, Set<AccessFlag>> locationToFlags =
             Map.ofEntries(entry(Location.CLASS,
-                                Set.of(PUBLIC, FINAL, SUPER,
+                                Set.of(PUBLIC, FINAL, (PreviewFeatures.isEnabled() ? IDENTITY : SUPER),
                                        INTERFACE, ABSTRACT,
                                        SYNTHETIC, ANNOTATION,
                                        ENUM, AccessFlag.MODULE)),
                           entry(Location.FIELD,
                                 Set.of(PUBLIC, PRIVATE, PROTECTED,
                                        STATIC, FINAL, VOLATILE,
-                                       TRANSIENT, SYNTHETIC, ENUM)),
+                                       TRANSIENT, SYNTHETIC, ENUM, STRICT)),
                           entry(Location.METHOD,
                                 Set.of(PUBLIC, PRIVATE, PROTECTED,
                                        STATIC, FINAL, SYNCHRONIZED,
                                        BRIDGE, VARARGS, NATIVE,
                                        ABSTRACT, STRICT, SYNTHETIC)),
                           entry(Location.INNER_CLASS,
-                                Set.of(PUBLIC, PRIVATE, PROTECTED,
+                                Set.of(PUBLIC, PRIVATE, PROTECTED, IDENTITY,
                                        STATIC, FINAL, INTERFACE, ABSTRACT,
                                        SYNTHETIC, ANNOTATION, ENUM)),
                           entry(Location.METHOD_PARAMETER,
                                 Set.of(FINAL, SYNTHETIC, MANDATED)),
                           entry(Location.MODULE,
diff a/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java b/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java
--- a/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java
+++ b/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java
@@ -186,10 +186,11 @@
      * access to a <em>non-modifiable</em> final field.  The following fields
      * are non-modifiable:
      * <ul>
      * <li>static final fields declared in any class or interface</li>
      * <li>final fields declared in a {@linkplain Class#isHidden() hidden class}</li>
+     * <li>fields declared in a {@linkplain Class#isValue() value class}</li>
      * <li>final fields declared in a {@linkplain Class#isRecord() record}</li>
      * </ul>
      * <p> The {@code accessible} flag when {@code true} suppresses Java language access
      * control checks to only enable {@linkplain Field#get <em>read</em>} access to
      * these non-modifiable final fields.
diff a/src/java.base/share/classes/java/lang/reflect/Array.java b/src/java.base/share/classes/java/lang/reflect/Array.java
--- a/src/java.base/share/classes/java/lang/reflect/Array.java
+++ b/src/java.base/share/classes/java/lang/reflect/Array.java
@@ -138,12 +138,22 @@
      * an array
      * @throws    ArrayIndexOutOfBoundsException If the specified {@code index}
      * argument is negative, or if it is greater than or equal to the
      * length of the specified array
      */
-    public static native Object get(Object array, int index)
-        throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
+    public static Object get(Object array, int index)
+        throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+        Class<?> componentType = array.getClass().getComponentType();
+        if (componentType != null && !componentType.isPrimitive()) {
+            Object[] objArray = (Object[]) array.getClass().cast(array);
+            return objArray[index];
+        } else {
+            return getReferenceOrPrimitive(array, index);
+        }
+    }
+
+    private static native Object getReferenceOrPrimitive(Object array, int index);
 
     /**
      * Returns the value of the indexed component in the specified
      * array object, as a {@code boolean}.
      *
@@ -310,12 +320,22 @@
      * an unwrapping conversion fails
      * @throws    ArrayIndexOutOfBoundsException If the specified {@code index}
      * argument is negative, or if it is greater than or equal to
      * the length of the specified array
      */
-    public static native void set(Object array, int index, Object value)
-        throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
+    public static void set(Object array, int index, Object value)
+        throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
+        Class<?> componentType = array.getClass().getComponentType();
+        if (componentType != null && !componentType.isPrimitive()) {
+            Object[] objArray = (Object[]) array.getClass().cast(array);
+            objArray[index] = componentType.cast(value);
+        } else {
+            setReferenceOrPrimitive(array, index, value);
+        }
+    }
+
+    private static native void setReferenceOrPrimitive(Object array, int index, Object value);
 
     /**
      * Sets the value of the indexed component of the specified array
      * object to the specified {@code boolean} value.
      * @param array the array
diff a/src/java.base/share/classes/java/lang/reflect/Executable.java b/src/java.base/share/classes/java/lang/reflect/Executable.java
--- a/src/java.base/share/classes/java/lang/reflect/Executable.java
+++ b/src/java.base/share/classes/java/lang/reflect/Executable.java
@@ -212,19 +212,23 @@
 
     /**
      * {@return an unmodifiable set of the {@linkplain AccessFlag
      * access flags} for the executable represented by this object,
      * possibly empty}
+     * The {@code AccessFlags} may depend on the class file format version of the class.
      *
      * @see #getModifiers()
      * @jvms 4.6 Methods
      * @since 20
      */
     @Override
     public Set<AccessFlag> accessFlags() {
+        int major = SharedSecrets.getJavaLangAccess().classFileFormatVersion(getDeclaringClass()) & 0xffff;
+        var cffv = ClassFileFormatVersion.fromMajor(major);
         return AccessFlag.maskToAccessFlags(getModifiers(),
-                                            AccessFlag.Location.METHOD);
+                                            AccessFlag.Location.METHOD,
+                                            cffv);
     }
 
     /**
      * Returns an array of {@code TypeVariable} objects that represent the
      * type variables declared by the generic declaration represented by this
diff a/src/java.base/share/classes/java/lang/reflect/Field.java b/src/java.base/share/classes/java/lang/reflect/Field.java
--- a/src/java.base/share/classes/java/lang/reflect/Field.java
+++ b/src/java.base/share/classes/java/lang/reflect/Field.java
@@ -71,11 +71,11 @@
     // This is guaranteed to be interned by the VM in the 1.4
     // reflection implementation
     private final String              name;
     private final Class<?>            type;
     private final int                 modifiers;
-    private final boolean             trustedFinal;
+    private final int                 flags;
     // Generics and annotations support
     private final transient String    signature;
     // generic info repository; lazily initialized
     private transient volatile FieldRepository genericInfo;
     private final byte[]              annotations;
@@ -124,20 +124,20 @@
     @SuppressWarnings("deprecation")
     Field(Class<?> declaringClass,
           String name,
           Class<?> type,
           int modifiers,
-          boolean trustedFinal,
+          int flags,
           int slot,
           String signature,
           byte[] annotations)
     {
         this.clazz = declaringClass;
         this.name = name;
         this.type = type;
         this.modifiers = modifiers;
-        this.trustedFinal = trustedFinal;
+        this.flags = flags;
         this.slot = slot;
         this.signature = signature;
         this.annotations = annotations;
     }
 
@@ -155,11 +155,11 @@
         // objects be fabricated for each reflective call on Class
         // objects.)
         if (this.root != null)
             throw new IllegalArgumentException("Can not copy a non-root Field");
 
-        Field res = new Field(clazz, name, type, modifiers, trustedFinal, slot, signature, annotations);
+        Field res = new Field(clazz, name, type, modifiers, flags, slot, signature, annotations);
         res.root = this;
         // Might as well eagerly propagate this if already present
         res.fieldAccessor = fieldAccessor;
         res.overrideFieldAccessor = overrideFieldAccessor;
 
@@ -214,17 +214,21 @@
     }
 
     /**
      * {@return an unmodifiable set of the {@linkplain AccessFlag
      * access flags} for this field, possibly empty}
+     * The {@code AccessFlags} may depend on the class file format version of the class.
+     *
      * @see #getModifiers()
      * @jvms 4.5 Fields
      * @since 20
      */
     @Override
     public Set<AccessFlag> accessFlags() {
-        return AccessFlag.maskToAccessFlags(getModifiers(), AccessFlag.Location.FIELD);
+        int major = SharedSecrets.getJavaLangAccess().classFileFormatVersion(getDeclaringClass()) & 0xffff;
+        var cffv = ClassFileFormatVersion.fromMajor(major);
+        return AccessFlag.maskToAccessFlags(getModifiers(), AccessFlag.Location.FIELD, cffv);
     }
 
     /**
      * Returns {@code true} if this field represents an element of
      * an enumerated class; returns {@code false} otherwise.
@@ -771,11 +775,13 @@
      * <ul>
      * <li>{@link #setAccessible(boolean) setAccessible(true)} has succeeded for
      *     this {@code Field} object;</li>
      * <li>the field is non-static; and</li>
      * <li>the field's declaring class is not a {@linkplain Class#isHidden()
-     *     hidden class}; and</li>
+     *     hidden class};</li>
+     * <li>the field's declaring class is not a {@linkplain Class#isValue()
+     *     value class}; and</li>
      * <li>the field's declaring class is not a {@linkplain Class#isRecord()
      *     record class}.</li>
      * </ul>
      * If any of the above checks is not met, this method throws an
      * {@code IllegalAccessException}.
@@ -1228,12 +1234,19 @@
     @Override
     /* package-private */ Field getRoot() {
         return root;
     }
 
+    private static final int TRUST_FINAL     = 0x0010;
+    private static final int NULL_RESTRICTED = 0x0020;
+
     /* package-private */ boolean isTrustedFinal() {
-        return trustedFinal;
+        return (flags & TRUST_FINAL) == TRUST_FINAL;
+    }
+
+    /* package-private */ boolean isNullRestricted() {
+        return (flags & NULL_RESTRICTED) == NULL_RESTRICTED;
     }
 
     /**
      * {@inheritDoc}
      *
diff a/src/java.base/share/classes/java/lang/reflect/Modifier.java b/src/java.base/share/classes/java/lang/reflect/Modifier.java
--- a/src/java.base/share/classes/java/lang/reflect/Modifier.java
+++ b/src/java.base/share/classes/java/lang/reflect/Modifier.java
@@ -23,16 +23,19 @@
  * questions.
  */
 
 package java.lang.reflect;
 
+import jdk.internal.javac.PreviewFeature;
+
 import java.util.StringJoiner;
 
 /**
  * The Modifier class provides {@code static} methods and
- * constants to decode class and member access modifiers.  The sets of
- * modifiers are represented as integers with distinct bit positions
+ * constants to decode class and member access modifiers.
+ * The {@link AccessFlag} class should be used instead of this class.
+ * The sets of modifiers are represented as integers with non-distinct bit positions
  * representing different modifiers.  The values for the constants
  * representing the modifiers are taken from the tables in sections
  * {@jvms 4.1}, {@jvms 4.4}, {@jvms 4.5}, and {@jvms 4.7} of
  * <cite>The Java Virtual Machine Specification</cite>.
  *
@@ -121,18 +124,39 @@
 
     /**
      * Return {@code true} if the integer argument includes the
      * {@code synchronized} modifier, {@code false} otherwise.
      *
+     * @apiNote {@code isSynchronized} should only be called with the modifiers
+     * of a {@linkplain Method#getModifiers() method}.
+     *
      * @param   mod a set of modifiers
      * @return {@code true} if {@code mod} includes the
      * {@code synchronized} modifier; {@code false} otherwise.
      */
     public static boolean isSynchronized(int mod) {
         return (mod & SYNCHRONIZED) != 0;
     }
 
+    /**
+     * Return {@code true} if the integer argument includes the
+     * {@code identity} modifier, {@code false} otherwise.
+     *
+     * @apiNote {@code isIdentity} should only be called with the modifiers
+     * of a {@linkplain Class#getModifiers() class}.
+     *
+     * @param   mod a set of modifiers
+     * @return {@code true} if {@code mod} includes the
+     * {@code identity} modifier; {@code false} otherwise.
+     *
+     * @since Valhalla
+     */
+    @PreviewFeature(feature = PreviewFeature.Feature.VALUE_OBJECTS, reflective=true)
+    public static boolean isIdentity(int mod) {
+        return (mod & IDENTITY) != 0;
+    }
+
     /**
      * Return {@code true} if the integer argument includes the
      * {@code volatile} modifier, {@code false} otherwise.
      *
      * @param   mod a set of modifiers
@@ -317,10 +341,19 @@
      * modifier.
      * @see AccessFlag#SYNCHRONIZED
      */
     public static final int SYNCHRONIZED     = 0x00000020;
 
+    /**
+     * The {@code int} value representing the {@code ACC_IDENTITY}
+     * modifier.
+     *
+     * @since Valhalla
+     */
+    @PreviewFeature(feature = PreviewFeature.Feature.VALUE_OBJECTS, reflective=true)
+    public static final int IDENTITY         = 0x00000020;
+
     /**
      * The {@code int} value representing the {@code volatile}
      * modifier.
      * @see AccessFlag#VOLATILE
      */
@@ -394,11 +427,11 @@
      * @jls 8.1.1 Class Modifiers
      */
     private static final int CLASS_MODIFIERS =
         Modifier.PUBLIC         | Modifier.PROTECTED    | Modifier.PRIVATE |
         Modifier.ABSTRACT       | Modifier.STATIC       | Modifier.FINAL   |
-        Modifier.STRICT;
+        Modifier.IDENTITY       | Modifier.STRICT;
 
     /**
      * The Java source modifiers that can be applied to an interface.
      * @jls 9.1.1 Interface Modifiers
      */
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
@@ -516,11 +516,11 @@
                         // All proxy superinterfaces are public, must be in named dynamic module
                         throw new InternalError("public proxy in unnamed module: " + module);
                     }
                 }
 
-                if ((accessFlags & ~Modifier.PUBLIC) != 0) {
+                if ((accessFlags & ~(Modifier.PUBLIC | Modifier.IDENTITY)) != 0) {
                     throw new InternalError("proxy access flags must be Modifier.PUBLIC or 0");
                 }
             }
         }
 
@@ -538,11 +538,11 @@
 
             /*
              * Generate the specified proxy class.
              */
             byte[] proxyClassFile = ProxyGenerator.generateProxyClass(loader, proxyName, interfaces,
-                                                                      context.accessFlags() | Modifier.FINAL);
+                                                                      context.accessFlags() | Modifier.FINAL | Modifier.IDENTITY);
             try {
                 Class<?> pc = JLA.defineClass(loader, proxyName, proxyClassFile,
                                               null, "__dynamic_proxy__");
                 reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
                 return pc;
diff a/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java b/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java
--- a/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java
+++ b/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java
@@ -451,10 +451,16 @@
     }
 
     /**
      * Generate a class file for the proxy class.  This method drives the
      * class file generation process.
+     *
+     * If a proxy interface references any value classes, the value classes
+     * are listed in the preload attribute of the interface class.  The
+     * classes that are referenced by the proxy interface have already
+     * been loaded before the proxy class.  Hence the proxy class is
+     * generated with no preload attributes as it essentially has no effect.
      */
     private byte[] generateClassFile() {
         visit(CLASSFILE_VERSION, accessFlags, dotToSlash(className), null,
                 JLR_PROXY, typeNames(interfaces));
 
diff a/src/java.base/share/classes/java/lang/reflect/ReflectAccess.java b/src/java.base/share/classes/java/lang/reflect/ReflectAccess.java
--- a/src/java.base/share/classes/java/lang/reflect/ReflectAccess.java
+++ b/src/java.base/share/classes/java/lang/reflect/ReflectAccess.java
@@ -124,10 +124,14 @@
 
     public boolean isTrustedFinalField(Field f) {
         return f.isTrustedFinal();
     }
 
+    public boolean isNullRestrictedField(Field f) {
+        return f.isNullRestricted();
+    }
+
     public <T> T newInstance(Constructor<T> ctor, Object[] args, Class<?> caller)
         throws IllegalAccessException, InstantiationException, InvocationTargetException
     {
         return ctor.newInstanceWithCaller(args, true, caller);
     }
diff a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java
--- a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java
+++ b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java
@@ -70,11 +70,13 @@
     private static final MethodHandle OBJECT_HASHCODE;
     private static final MethodHandle OBJECT_TO_STRING;
     private static final MethodHandle STRING_FORMAT;
     private static final MethodHandle HASH_COMBINER;
 
-    private static final HashMap<Class<?>, MethodHandle> primitiveEquals = new HashMap<>();
+    /* package-private */
+    static final HashMap<Class<?>, MethodHandle> primitiveEquals = new HashMap<>();
+
     private static final HashMap<Class<?>, MethodHandle> primitiveHashers = new HashMap<>();
     private static final HashMap<Class<?>, MethodHandle> primitiveToString = new HashMap<>();
 
     static {
         try {
@@ -410,18 +412,26 @@
         requireNonNull(recordClass);
         requireNonNull(names);
         requireNonNull(getters);
         Arrays.stream(getters).forEach(Objects::requireNonNull);
         MethodType methodType;
-        if (type instanceof MethodType mt)
+        if (type instanceof MethodType mt) {
             methodType = mt;
-        else {
+            if (mt.parameterType(0) != recordClass) {
+                throw new IllegalArgumentException("Bad method type: " + mt);
+            }
+        } else {
             methodType = null;
             if (!MethodHandle.class.equals(type))
                 throw new IllegalArgumentException(type.toString());
         }
         List<MethodHandle> getterList = List.of(getters);
+        for (MethodHandle getter : getterList) {
+            if (getter.type().parameterType(0) != recordClass) {
+                throw new IllegalArgumentException("Bad receiver type: " + getter);
+            }
+        }
         MethodHandle handle = switch (methodName) {
             case "equals"   -> {
                 if (methodType != null && !methodType.equals(MethodType.methodType(boolean.class, recordClass, Object.class)))
                     throw new IllegalArgumentException("Bad method type: " + methodType);
                 yield makeEquals(recordClass, getterList);
diff a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java
--- a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java
+++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java
@@ -48,10 +48,11 @@
 import jdk.internal.vm.annotation.Stable;
 
 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG;
 import static java.util.Objects.requireNonNull;
+import jdk.internal.misc.PreviewFeatures;
 
 /**
  * Bootstrap methods for linking {@code invokedynamic} call sites that implement
  * the selection functionality of the {@code switch} statement.  The bootstraps
  * take additional static arguments corresponding to the {@code case} labels
@@ -385,11 +386,11 @@
     private static MethodHandle generateInnerClass(MethodHandles.Lookup caller, Object[] labels) {
         List<EnumDesc<?>> enumDescs = new ArrayList<>();
         List<Class<?>> extraClassLabels = new ArrayList<>();
 
         byte[] classBytes = ClassFile.of().build(ClassDesc.of(typeSwitchClassName(caller.lookupClass())), clb -> {
-            clb.withFlags(AccessFlag.FINAL, AccessFlag.SUPER, AccessFlag.SYNTHETIC)
+            clb.withFlags(AccessFlag.FINAL, (PreviewFeatures.isEnabled())  ? AccessFlag.IDENTITY : AccessFlag.SUPER, AccessFlag.SYNTHETIC)
                .withMethodBody("typeSwitch",
                                TYPES_SWITCH_DESCRIPTOR,
                                ClassFile.ACC_FINAL | ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC,
                                cb -> {
                     cb.aload(0);
diff a/src/java.base/share/classes/java/lang/runtime/ValueObjectMethods.java b/src/java.base/share/classes/java/lang/runtime/ValueObjectMethods.java
--- /dev/null
+++ b/src/java.base/share/classes/java/lang/runtime/ValueObjectMethods.java
@@ -0,0 +1,1415 @@
+/*
+ * Copyright (c) 2018, 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.  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.
+ */
+
+package java.lang.runtime;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.VarHandle;
+import java.lang.reflect.Modifier;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+
+import jdk.internal.access.JavaLangInvokeAccess;
+import jdk.internal.access.SharedSecrets;
+import sun.invoke.util.Wrapper;
+import sun.security.action.GetIntegerAction;
+import sun.security.action.GetPropertyAction;
+
+import static java.lang.invoke.MethodHandles.constant;
+import static java.lang.invoke.MethodHandles.dropArguments;
+import static java.lang.invoke.MethodHandles.filterArguments;
+import static java.lang.invoke.MethodHandles.foldArguments;
+import static java.lang.invoke.MethodHandles.guardWithTest;
+import static java.lang.invoke.MethodHandles.permuteArguments;
+import static java.lang.invoke.MethodType.methodType;
+import static java.lang.runtime.ObjectMethods.primitiveEquals;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toMap;
+
+/**
+ * Implementation for Object::equals and Object::hashCode for value objects.
+ *
+ * ValueObjectMethods::isSubstitutable and valueObjectHashCode are
+ * private entry points called by VM.
+ */
+final class ValueObjectMethods {
+    private ValueObjectMethods() {}
+    private static final boolean VERBOSE =
+            GetPropertyAction.privilegedGetProperty("value.bsm.debug") != null;
+    private static final int MAX_NODE_VISITS =
+            GetIntegerAction.privilegedGetProperty("jdk.value.recursion.threshold", Integer.MAX_VALUE);
+    private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess();
+
+    static class MethodHandleBuilder {
+        private static final HashMap<Class<?>, MethodHandle> primitiveSubstitutable = new HashMap<>();
+
+        static {
+            primitiveSubstitutable.putAll(primitiveEquals); // adopt all the primitive eq methods
+            primitiveSubstitutable.put(float.class,
+                    findStatic("eqValue", methodType(boolean.class, float.class, float.class)));
+            primitiveSubstitutable.put(double.class,
+                    findStatic("eqValue", methodType(boolean.class, double.class, double.class)));
+        }
+
+        static Stream<MethodHandle> getterStream(Class<?> type, Comparator<MethodHandle> comparator) {
+            // filter static fields
+            Stream<MethodHandle> s = Arrays.stream(type.getDeclaredFields())
+                .filter(f -> !Modifier.isStatic(f.getModifiers()))
+                .map(f -> {
+                    try {
+                        return JLIA.unreflectField(f, false);
+                    } catch (IllegalAccessException e) {
+                        throw newLinkageError(e);
+                    }
+                });
+            if (comparator != null) {
+                s = s.sorted(comparator);
+            }
+            return s;
+        }
+
+        static MethodHandle hashCodeForType(Class<?> type) {
+            if (type.isPrimitive()) {
+                int index = Wrapper.forPrimitiveType(type).ordinal();
+                return HASHCODE[index];
+            } else {
+                return HASHCODE[Wrapper.OBJECT.ordinal()].asType(methodType(int.class, type));
+            }
+        }
+
+        static MethodHandle builtinPrimitiveSubstitutable(Class<?> type) {
+            return primitiveSubstitutable.get(type);
+        }
+
+        /*
+         * Produces a MethodHandle that returns boolean if two instances
+         * of the given reference type are substitutable.
+         *
+         * Two values of reference type are substitutable i== iff
+         * 1. if o1 and o2 are both reference objects then o1 r== o2; or
+         * 2. if o1 and o2 are both values then o1 v== o2
+         *
+         * At invocation time, it needs a dynamic check on the objects and
+         * do the substitutability test if they are of a value class.
+         */
+        static MethodHandle referenceTypeEquals(Class<?> type) {
+            return OBJECT_EQUALS.asType(methodType(boolean.class, type, type));
+        }
+
+        static Class<?> fieldType(MethodHandle getter) {
+            Class<?> ftype = getter.type().returnType();
+            return ftype;
+        }
+
+        private static List<Class<?>> valueTypeFields(Class<?> type) {
+            List<Class<?>> result = new ArrayList<>();
+            Arrays.stream(type.getDeclaredFields())
+                  .filter(f -> !Modifier.isStatic(f.getModifiers()))
+                  .map(f -> f.getType())
+                  .filter(ft -> ft.isValue() && !result.contains(ft))
+                  .forEach(result::add);
+            return result;
+        }
+
+        /*
+         * Produces a MethodHandle that returns boolean if two value objects
+         * are substitutable.  The method type is (V, V)boolean.
+         */
+        static MethodHandle valueTypeEquals(Class<?> type) {
+            var builder = METHOD_HANDLE_BUILDERS.get(type);
+            if (builder == null) {
+                builder = newBuilder(type);
+            }
+            return builder.equalsTarget();
+        }
+
+        /*
+         * Produces a MethodHandle that computes the hash code of a value object.
+         * The method type of the return MethodHandle is (V)int.
+         */
+        static MethodHandle valueTypeHashCode(Class<?> type) {
+            var builder = METHOD_HANDLE_BUILDERS.get(type);
+            if (builder == null) {
+                builder = newBuilder(type);
+            }
+            return builder.hashCodeTarget();
+        }
+
+        /*
+         * Produces a MethodHandle that returns boolean if the given non-recursively typed
+         * fields of the two value objects are substitutable. The method type is (V, V)boolean
+         */
+        static MethodHandle valueTypeEquals(Class<?> type, List<MethodHandle> getters) {
+            assert type.isValue();
+
+            MethodType mt = methodType(boolean.class, type, type);
+            MethodHandle instanceTrue = dropArguments(TRUE, 0, type, Object.class).asType(mt);
+            MethodHandle instanceFalse = dropArguments(FALSE, 0, type, Object.class).asType(mt);
+            MethodHandle accumulator = dropArguments(TRUE, 0, type, type);
+            for (MethodHandle getter : getters) {
+                Class<?> ftype = fieldType(getter);
+                var eq = substitutableInvoker(ftype).asType(methodType(boolean.class, ftype, ftype));
+                var thisFieldEqual = filterArguments(eq, 0, getter, getter);
+                accumulator = guardWithTest(thisFieldEqual, accumulator, instanceFalse);
+            }
+
+            // if both arguments are null, return true;
+            // otherwise return accumulator;
+            return guardWithTest(IS_NULL.asType(mt),
+                                 instanceTrue,
+                                 guardWithTest(IS_SAME_VALUE_CLASS.asType(mt),
+                                               accumulator,
+                                               instanceFalse));
+        }
+
+        /*
+         * Produces a MethodHandle that returns the hash code computed from
+         * the given non-recursively-typed fields of a value object.
+         * The method type is (V)int.
+         */
+        static MethodHandle valueTypeHashCode(Class<?> type, List<MethodHandle> getters) {
+            assert type.isValue();
+
+            MethodHandle target = dropArguments(constant(int.class, SALT), 0, type);
+            MethodHandle classHasher = dropArguments(hashCodeForType(Class.class).bindTo(type), 0, type);
+            MethodHandle hashCombiner = dropArguments(HASH_COMBINER, 2, type);
+            MethodHandle accumulator = foldArguments(foldArguments(hashCombiner, 1, classHasher), 0, target);
+            for (MethodHandle getter : getters) {
+                Class<?> ft = fieldType(getter);
+                // For primitive types or reference types, this calls Objects::hashCode.
+                // For value objects and the hashCode method is not overridden,
+                // VM will call valueObjectHashCode to compute the hash code.
+                var hasher = hashCodeForType(ft);
+                var hashThisField = filterArguments(hasher, 0, getter);    // (R)I
+                var combineHashes = foldArguments(hashCombiner, 1, hashThisField);
+                accumulator = foldArguments(combineHashes, 0, accumulator);
+            }
+            return accumulator;
+        }
+
+        // ------ utility methods ------
+        private static boolean eq(Object a, Object b)   {
+            if (a == null && b == null) return true;
+            if (a == null || b == null) return false;
+            if (a.getClass() != b.getClass()) return false;
+            return a.getClass().isValue() ? valueEquals(a, b) : (a == b);
+        }
+
+        /*
+         * Returns true if two value objects are substitutable.
+         */
+        private static boolean valueEquals(Object a, Object b) {
+            assert a != null && b != null && isSameValueClass(a, b);
+            try {
+                Class<?> type = a.getClass();
+                return (boolean) substitutableInvoker(type).invoke(type.cast(a), type.cast(b));
+            } catch (Error|RuntimeException e) {
+                throw e;
+            } catch (Throwable e) {
+                throw new InternalError(e);
+            }
+        }
+
+        private static boolean isNull(Object a, Object b) {
+            // avoid acmp that will call isSubstitutable
+            if (a != null) return false;
+            if (b != null) return false;
+            return true;
+        }
+
+        /*
+         * Returns true if the given objects are of the same value class.
+         *
+         * Two objects are of the same value class iff:
+         * 1. a != null and b != null
+         * 2. the declaring class of a and b is the same value class
+         */
+        private static boolean isSameValueClass(Object a, Object b) {
+            if (a == null || b == null) return false;
+
+            return a.getClass().isValue() && a.getClass() == b.getClass();
+        }
+
+        private static int hashCombiner(int accumulator, int value) {
+            return accumulator * 31 + value;
+        }
+
+        private static int[] newCounter(int[] counter) {
+            return new int[] { counter[0] };
+        }
+
+        private static boolean recurValueEq(MethodHandle target, Object o1, Object o2, int[] counter) {
+            assert counter[0] > 0;
+
+            if (o1 == null && o2 == null) return true;
+            if (o1 == null || o2 == null) return false;
+            if (o1.getClass() != o2.getClass()) return false;
+
+            if (--counter[0] == 0) {
+                throw new StackOverflowError("fail to evaluate == for value class " + o1.getClass().getName());
+            }
+
+            try {
+                return (boolean) target.invoke(o1, o2, counter);
+            } catch (Error|RuntimeException e) {
+                throw e;
+            } catch (Throwable e) {
+                throw new InternalError(e);
+            }
+        }
+
+        private static int recurValueHashCode(MethodHandle target, Object o, int[] counter) {
+            assert counter[0] > 0;
+
+            if (o == null) return 0;
+
+            if (--counter[0] == 0) {
+                throw new StackOverflowError("fail to evaluate hashCode for value class " + o.getClass().getName());
+            }
+
+            try {
+                return (int) target.invoke(o, counter);
+            } catch (Error|RuntimeException e) {
+                throw e;
+            } catch (Throwable e) {
+                throw new InternalError(e);
+            }
+        }
+
+        private static final MethodHandle FALSE = constant(boolean.class, false);
+        private static final MethodHandle TRUE = constant(boolean.class, true);
+        // Substitutability test for float
+        private static boolean eqValue(float a, float b) {
+            return Float.floatToRawIntBits(a) == Float.floatToRawIntBits(b);
+        }
+        // Substitutability test for double
+        private static boolean eqValue(double a, double b) {
+            return Double.doubleToRawLongBits(a) == Double.doubleToRawLongBits(b);
+        }
+        private static final MethodHandle OBJECT_EQUALS =
+                findStatic("eq", methodType(boolean.class, Object.class, Object.class));
+        private static final MethodHandle IS_SAME_VALUE_CLASS =
+                findStatic("isSameValueClass", methodType(boolean.class, Object.class, Object.class));
+        private static final MethodHandle IS_NULL =
+                findStatic("isNull", methodType(boolean.class, Object.class, Object.class));
+        private static final MethodHandle HASH_COMBINER =
+                findStatic("hashCombiner", methodType(int.class, int.class, int.class));
+        private static final MethodHandle[] HASHCODE = initHashCode();
+        private static final MethodHandle RECUR_VALUE_EQ =
+                findStatic("recurValueEq", methodType(boolean.class, MethodHandle.class, Object.class, Object.class, int[].class));
+        private static final MethodHandle RECUR_VALUE_HASHCODE =
+                findStatic("recurValueHashCode", methodType(int.class, MethodHandle.class, Object.class, int[].class));
+        private static final MethodHandle NEW_COUNTER =
+                findStatic("newCounter", methodType(int[].class, int[].class));
+
+        private static MethodHandle[] initHashCode() {
+            MethodHandle[] mhs = new MethodHandle[Wrapper.COUNT];
+            for (Wrapper wrapper : Wrapper.values()) {
+                if (wrapper == Wrapper.VOID) continue;
+                Class<?> cls = wrapper == Wrapper.OBJECT ? Objects.class : wrapper.wrapperType();
+                mhs[wrapper.ordinal()] = findStatic(cls, "hashCode",
+                                                    methodType(int.class, wrapper.primitiveType()));
+            }
+            return mhs;
+        }
+
+        private static MethodHandle findStatic(String name, MethodType methodType) {
+            return findStatic(MethodHandleBuilder.class, name, methodType);
+        }
+        private static MethodHandle findStatic(Class<?> cls, String name, MethodType methodType) {
+            try {
+                return JLIA.findStatic(cls, name, methodType);
+            } catch (IllegalAccessException e) {
+                throw newLinkageError(e);
+            }
+        }
+
+        /**
+         * A "salt" value used for this internal hashcode implementation that
+         * needs to vary sufficiently from one run to the next so that
+         * the default hashcode for value classes will vary between JVM runs.
+         */
+        static final int SALT;
+        static {
+            long nt = System.nanoTime();
+            int value = (int)((nt >>> 32) ^ nt);
+            SALT = GetIntegerAction.privilegedGetProperty("value.bsm.salt", value);
+        }
+
+        static MethodHandleBuilder newBuilder(Class<?> type) {
+            assert type.isValue();
+
+            Deque<Class<?>> deque = new ArrayDeque<>();
+            deque.add(type);
+            Map<Class<?>, MethodHandleBuilder> visited = new HashMap<>();
+            var builder = new MethodHandleBuilder(type, deque, visited);
+            visited.put(type, builder);
+            return builder;
+        }
+
+        enum Status {
+            NOT_START,
+            IN_PROGRESS,
+            TRAVERSAL_DONE,
+            READY
+        }
+
+        final Class<?> type;
+        final List<Class<?>> fieldValueTypes;
+        // a map of the type of a field T to a cycle of T -> ... -> V
+        // where V is this builder's value type
+        final Map<Class<?>, List<Class<?>>> cyclicMembers = new HashMap<>();
+        // recursively-typed fields declared in this builder's value type
+        final Set<Class<?>> recurFieldTypes = new HashSet<>();
+        final Deque<Class<?>> path;
+        final Map<Class<?>, MethodHandleBuilder> visited;;
+        volatile Status status = Status.NOT_START;
+        volatile MethodHandle equalsTarget;
+        volatile MethodHandle hashCodeTarget;
+
+        static final VarHandle STATUS_HANDLE;
+        static {
+            VarHandle vh = null;
+            try {
+                vh = MethodHandles.lookup().findVarHandle(MethodHandleBuilder.class, "status", Status.class);
+            } catch (ReflectiveOperationException e) {
+                throw new InternalError(e);
+            }
+            STATUS_HANDLE = vh;
+        }
+
+        /**
+         * Constructs a new MethodHandleBuilder for the given value type.
+         *
+         * @param type a value class
+         * @param path the graph traversal
+         * @param visited a map of a visited type to a builder
+         */
+        private MethodHandleBuilder(Class<?> type, Deque<Class<?>> path, Map<Class<?>, MethodHandleBuilder> visited) {
+            this.type = type;
+            this.fieldValueTypes = valueTypeFields(type);
+            this.path = path;
+            this.visited = visited;
+            if (VERBOSE) {
+                System.out.println("New builder for " + type.getName() + " " + path);
+            }
+        }
+
+        /*
+         * Returns a method handle that implements equals method for this builder's value class.
+         */
+        MethodHandle equalsTarget() {
+            if (status != Status.READY)
+                throw new IllegalStateException(type.getName() + " not ready");
+
+            var mh = equalsTarget;
+            if (mh != null) return mh;
+
+            generateMethodHandle();
+            return equalsTarget;
+        }
+
+        /*
+         * Returns a method handle that implements hashCode method for this builder's value class.
+         */
+        MethodHandle hashCodeTarget() {
+            if (status != Status.READY)
+                throw new IllegalStateException(type.getName() + " not ready");
+
+            var mh = hashCodeTarget;
+            if (mh != null) return mh;
+
+            generateMethodHandle();
+            return hashCodeTarget;
+        }
+
+        /*
+         * Build the graph for this builder's value type.  Detect all cycles.
+         * This builder after this method returns is in DONE_TRAVERSAL or READY status.
+         *
+         * A builder for type V will change to READY status when the entire graph for V
+         * is traversed (i.e. all builders in this graph are in DONE_TRAVERSAL or READY
+         * status).
+         */
+        MethodHandleBuilder build() {
+            if (status == Status.READY) return this;
+
+            if (!STATUS_HANDLE.compareAndSet(this, Status.NOT_START, Status.IN_PROGRESS)) {
+                throw new RuntimeException(type.getName() + " in progress");
+            }
+
+            // traverse the graph and find all cycles
+            detectCycles();
+
+            if (!STATUS_HANDLE.compareAndSet(this, Status.IN_PROGRESS, Status.TRAVERSAL_DONE)) {
+                throw new RuntimeException(type.getName() + " failed to set done traversal. " + status);
+            }
+
+            // Check if this node V is ready for building equals/hashCode method handles.
+            // V is ready if the types of all its fields are done traversal.
+            if (ready()) {
+                // Do a pass on all the cycles containing V.  V is ready.
+                // If a node N in the cycle has completed the traversal (i.e. cycles are detected),
+                // call ready() on N to update its status if ready.
+                for (List<Class<?>> cycle : cyclicMembers.values()) {
+                    cycle.stream().filter(c -> c != type)
+                                  .map(visited::get)
+                                  .filter(b -> b.status == Status.TRAVERSAL_DONE)
+                                  .forEach(MethodHandleBuilder::ready);
+                }
+            }
+            return this;
+        }
+
+        /*
+         * Traverses the graph and finds all cycles.
+         */
+        private void detectCycles() {
+            LinkedList<Class<?>> deque = new LinkedList<>();
+            deque.addAll(fieldValueTypes);
+            while (!deque.isEmpty()) {
+                Class<?> n = deque.pop();
+                // detect cyclic membership
+                if (path.contains(n)) {
+                    List<Class<?>> cycle = new ArrayList<>();
+                    Iterator<Class<?>> iter = path.iterator();
+                    while (iter.hasNext()) {
+                        Class<?> c = iter.next();
+                        cycle.add(c);
+                        if (c == n) break;
+                    }
+                    cyclicMembers.put(n, cycle);
+                    path.pop();
+                    continue;
+                }
+
+                try {
+                    path.push(n);
+                    if (!visited.containsKey(n)) {
+                        // Duplicate the path and pass it to an unvisited node
+                        Deque<Class<?>> newPath = new ArrayDeque<>();
+                        newPath.addAll(path);
+                        visited.computeIfAbsent(n, c -> new MethodHandleBuilder(n, newPath, visited));
+                    }
+
+                    var builder = visited.get(n);
+                    switch (builder.status) {
+                        case NOT_START -> builder.build();
+                        case TRAVERSAL_DONE -> builder.ready();
+                    }
+                } finally {
+                    path.pop();
+                }
+            }
+
+            // propagate the cycles to the recursively-typed value classes
+            // For each cycle A -> B -> C -> A, the cycle is recorded in all
+            // the nodes (A, B, and C) in this cycle.
+            for (Map.Entry<Class<?>, List<Class<?>>> e : cyclicMembers.entrySet()) {
+                Class<?> c = e.getKey();
+                List<Class<?>> cycle = e.getValue();
+                var builder = visited.get(c);
+                for (Class<?> ft : cycle) {
+                    if (ft != c && builder.fieldValueTypes.contains(ft)) {
+                        var v = builder.cyclicMembers.put(ft, cycle);
+                        assert v == null || cycle.equals(v) : "mismatched cycle: " + v + " vs " + cycle;
+                    }
+                }
+            }
+        }
+
+        /*
+         * Tests if this builder is ready for generating equals and hashCode
+         * method handles for the value class.
+         *
+         * This builder is ready if and only if the type graph of all its fields
+         * are traversed and all cycles are detected.
+         *
+         * Before setting to READY, the recursively-typed fields are recorded
+         * that includes all types in the cycles and the field types which
+         * references recursive types
+         */
+        private boolean ready() {
+            if (status == Status.READY) return true;
+
+            boolean inProgress = fieldValueTypes.stream().map(visited::get)
+                                                .anyMatch(b -> b.status == Status.IN_PROGRESS);
+            if (inProgress)
+                return false;
+
+            // collect the recursively-typed value classes required by this method handle
+            // all types in the cycles and the field types which references recursive types
+            recurFieldTypes.addAll(cyclicMembers.keySet());
+            for (Class<?> c : fieldValueTypes) {
+                if (c == type) continue;
+
+                // if this field type references a recursive type
+                var b = visited.get(c);
+                if (b.cyclicMembers.size() > 0 || b.recurFieldTypes.size() > 0)
+                    recurFieldTypes.add(c);
+            };
+
+            // Finished recording recursively-typed fields.  Set to READY.
+            if (!STATUS_HANDLE.compareAndSet(this, Status.TRAVERSAL_DONE, Status.READY)) {
+                throw new RuntimeException(type.getName() + " failed to set READY. " + status);
+            }
+            return true;
+        }
+
+        void generateMethodHandle() {
+            if (status != Status.READY)
+                throw new IllegalStateException(type.getName() + " not ready");
+
+            // non-recursive value type
+            if (recurFieldTypes.isEmpty()) {
+                if (cyclicMembers.size() > 0)
+                    throw new RuntimeException(type.getName() + " should not reach here");
+
+                this.equalsTarget = valueTypeEquals(type, getterStream(type, TYPE_SORTER).toList());
+                this.hashCodeTarget = valueTypeHashCode(type, getterStream(type, null).toList());
+                return;
+            }
+
+            if (VERBOSE) {
+                System.out.println(debugString());
+            }
+
+            // generate the base function for each recursive type
+            // boolean base1(MethodHandle entry, MethodHandle base1, MethodHandle base2,....., Object o1, Object o2, int[] counter)
+            // :
+            // boolean baseN(MethodHandle entry, MethodHandle base1, MethodHandle base2,....., Object o1, Object o2, int[] counter)
+            //
+            List<Class<?>> recursiveTypes = aggregateRecursiveTypes();
+            Map<Class<?>, MethodHandle> bases = new LinkedHashMap<>();
+            Map<Class<?>, MethodHandle> hashCodeBases = new LinkedHashMap<>();
+            for (Class<?> c : recursiveTypes) {
+                bases.put(c, visited.get(c).generateSubstBase(recursiveTypes));
+                hashCodeBases.put(c, visited.get(c).generateHashCodeBase(recursiveTypes));
+            }
+
+            var handles = bases.values().stream().toArray(MethodHandle[]::new);
+            var hashCodeHandles = hashCodeBases.values().stream().toArray(MethodHandle[]::new);
+
+            // The entry point for equals for this value type T looks like this:
+            //
+            // boolean entry(MethodHandle entry, MethodHandle base1, MethodHandle base2,....., Object o1, Object o2, int[] counter) {
+            //    int[] newCounter = new int[] { counter[0] } ;
+            //    return baseT(o1, o2, newCounter);
+            // }
+            this.equalsTarget = newValueEquals(recursiveTypes, handles);
+            this.hashCodeTarget = newValueHashCode(recursiveTypes, hashCodeHandles);
+
+            // Precompute the method handles for all recursive data types in the cycles
+            // They share the generated base method handles.
+            var cycles = cyclicMembers.values().stream().flatMap(List::stream)
+                                .filter(c -> c != type)
+                                .collect(toMap(Function.identity(), visited::get));
+            for (Class<?> n : cycles.keySet()) {
+                var builder = cycles.get(n);
+                if (builder.status != Status.READY) {
+                    throw new InternalError(type.getName() + " is not ready: " + status);
+                }
+
+                var mh = builder.equalsTarget;
+                var mh2 = builder.hashCodeTarget;
+                if (mh != null && mh2 != null) {
+                    continue;
+                }
+
+                // precompute the method handles for each recursive type in the cycle
+                if (mh == null) {
+                    builder.equalsTarget = builder.newValueEquals(recursiveTypes, handles);
+                }
+                if (mh2 == null) {
+                    builder.hashCodeTarget = builder.newValueHashCode(recursiveTypes, hashCodeHandles);
+                }
+            }
+
+            // cache the builders with precomputed method handles in the cache
+            synchronized (CACHED_METHOD_HANDLE_BUILDERS) {
+                for (Class<?> n : cycles.keySet()) {
+                    try {
+                        // the builder is added to the builder cache and propapate to
+                        // the class value
+                        CACHED_METHOD_HANDLE_BUILDERS.computeIfAbsent(n, cycles::get);
+                        METHOD_HANDLE_BUILDERS.get(n);
+                    } finally {
+                        // Remove it from the builder cache once it's in class value
+                        CACHED_METHOD_HANDLE_BUILDERS.remove(n);
+                    }
+                }
+            }
+
+            // equals and hashCode are generated.  Clear the path and visited builders.
+            clear();
+        }
+
+        private void clear() {
+            path.clear();
+            visited.clear();
+        }
+
+        /*
+         * Aggregates all recursive data types for this builder's value types.
+         * The result is used in generating a recursive method handle
+         * for this builder's value type.
+         *
+         * A graph of V:
+         * V -> P -> V
+         *   -> N -> N (self-recursive)
+         *   -> E -> F -> E
+         *
+         * V, P, N, E, F are the mutual recursive types for V. The recursive method handle
+         * for V is created with the base functions for V, P, N, E, F and it can mutually
+         * call the recursive method handle for these types.  Specifically, MH_V calls
+         * MH_P which calls MH_V, MH_N which calls itself, and MH_E which calls MH_F.
+         */
+        private List<Class<?>> aggregateRecursiveTypes() {
+            boolean ready = true;
+            for (List<Class<?>> cycle : cyclicMembers.values()) {
+                // ensure all nodes in all cycles that are done traversal and ready for
+                // method handle generation
+                cycle.stream().filter(c -> c != type)
+                              .map(visited::get)
+                              .filter(b -> b.status == Status.TRAVERSAL_DONE)
+                              .forEach(MethodHandleBuilder::ready);
+
+                // check the status
+                ready = ready && cycle.stream().filter(c -> c != type)
+                                      .map(visited::get)
+                                      .allMatch(b -> b.status == Status.READY);
+            }
+
+            if (!ready) {
+                throw new IllegalStateException(type.getName() + " " + status);
+            }
+
+            /*
+             * Traverse the graph for V to find all mutual recursive types for V.
+             *
+             * Node T is a mutual recursive type for V if any of the following:
+             * 1. T is a recursively-typed field in V
+             * 2. T is a type involved the cycles from V ... -> T ... -> V
+             * 3. T is a mutual recursive type for N where N is a mutual recursive type for V.
+             */
+            Deque<Class<?>> deque = new ArrayDeque<>();
+            List<Class<?>> recurTypes = new ArrayList<>();
+            recurTypes.add(type);
+            Stream.concat(recurFieldTypes.stream(),
+                          cyclicMembers.values().stream().flatMap(List::stream))
+                  .filter(Predicate.not(deque::contains)).forEach(deque::add);
+            while (!deque.isEmpty()) {
+                Class<?> c = deque.pop();
+                if (recurTypes.contains(c)) continue;
+
+                recurTypes.add(c);
+
+                var builder = visited.get(c);
+                Stream.concat(builder.recurFieldTypes.stream(),
+                              builder.cyclicMembers.values().stream().flatMap(List::stream))
+                      .filter(n -> !recurTypes.contains(n) && !deque.contains(n))
+                      .forEach(deque::push);
+            }
+            return recurTypes;
+        }
+
+        /*
+         * Create a new method handle that implements equals(T, Object) for value class T
+         * for this builder using the given base method handles.  The return method handle
+         * is capable of recursively calling itself for value class T whose entry point:
+         *   boolean entry(MethodHandle entry, MethodHandle base1, MethodHandle base2, ..., Object o1, Object o2, int[] counter) {
+         *       int[] newCounter = new int[] { counter[0] };
+         *       return baseT(o1, o2, newCounter);
+         *   }
+         *
+         * The counter is used to keep of node visits and throw StackOverflowError
+         * if the counter gets "unreasonably" large of a cyclic value graph
+         * (regardless of how many real stack frames were consumed.)
+         */
+        MethodHandle newValueEquals(List<Class<?>> recursiveTypes, MethodHandle[] bases) {
+            var entry = equalsEntry(recursiveTypes);
+            var mh = MethodHandles.insertArguments(recursive(entry, bases), 2, new int[] {MAX_NODE_VISITS});
+            return mh.asType(methodType(boolean.class, type, type));
+        }
+
+        /*
+         * Create a new method handle that implements hashCode(T) for value class T
+         * for this builder using the given base method handles.  The return method handle
+         * is capable of recursively calling itself for value class T whose entry point:
+         *   boolean entry(MethodHandle entry, MethodHandle base1, MethodHandle base2, ..., Object o, int[] counter) {
+         *       int[] newCounter = new int[] { counter[0] };
+         *       return baseT(o, newCounter);
+         *   }
+         *
+         * The counter is used to keep of node visits and throw StackOverflowError
+         * if the counter gets "unreasonably" large of a cyclic value graph
+         * (regardless of how many real stack frames were consumed.)
+         */
+        MethodHandle newValueHashCode(List<Class<?>> recursiveTypes, MethodHandle[] bases) {
+            var entry = hashCodeEntry(recursiveTypes);
+            var mh = MethodHandles.insertArguments(recursive(entry, bases), 1, new int[] {MAX_NODE_VISITS});
+            return mh.asType(methodType(int.class, type));
+        }
+
+        /*
+         * Create a method handle where the first N+1 parameters are MethodHandle and
+         * N is the number of the recursive value types and followed with
+         * Object, Object and int[] parameters.  The pseudo code looks like this:
+         *
+         * boolean eq(MethodHandle entry, MethodHandle base1, MethodHandle base2, ..., Object o1, Object o2, int[] counter) {
+         *    if (o1 == null && o2 == null) return true;
+         *    if (o1 == null || o2 == null) return false;
+         *    if (o1.getClass() != o2. getClass()) return false;
+         *
+         *    int[] newCounter = new int[] { counter[0]; }
+         *    return (boolean) baseT.invoke(o1, o2, newCounter);
+         * }
+         */
+        MethodHandle equalsEntry(List<Class<?>> recursiveTypes) {
+            List<Class<?>> leadingMHParams = new ArrayList<>();
+            // the first MethodHandle parameter is this entry point
+            // followed with MethodHandle parameter for each mutual exclusive value class
+            int mhParamCount = recursiveTypes.size()+1;
+            for (int i=0; i < mhParamCount; i++) {
+                leadingMHParams.add(MethodHandle.class);
+            }
+
+            MethodType mt = methodType(boolean.class, Stream.concat(leadingMHParams.stream(), Stream.of(type, type, int[].class))
+                    .collect(toList()));
+            var allParameters = mt.parameterList();
+            MethodHandle instanceTrue = dropArguments(TRUE, 0, allParameters).asType(mt);
+            MethodHandle instanceFalse = dropArguments(FALSE, 0, allParameters).asType(mt);
+            MethodHandle isNull = dropArguments(dropArguments(IS_NULL, 0, leadingMHParams), mhParamCount+2, int[].class).asType(mt);
+            MethodHandle isSameValueType = dropArguments(dropArguments(IS_SAME_VALUE_CLASS, 0, leadingMHParams), mhParamCount+2, int[].class).asType(mt);
+
+            int index = recursiveTypes.indexOf(type);
+            var mtype = methodType(boolean.class, Stream.concat(leadingMHParams.stream(), Stream.of(type, type, int[].class)).collect(toList()));
+            var recurEq = RECUR_VALUE_EQ.asType(methodType(boolean.class, MethodHandle.class, type, type, int[].class));
+            var eq = permuteArguments(recurEq, mtype, index+1, mhParamCount, mhParamCount+1, mhParamCount+2);
+            eq = filterArguments(eq, mhParamCount+2, NEW_COUNTER);
+
+            // if both arguments are null, return true;
+            // otherwise return the method handle corresponding to this type
+            return guardWithTest(isNull,
+                                 instanceTrue,
+                                 guardWithTest(isSameValueType, eq, instanceFalse));
+        }
+
+        /*
+         * A base method for substitutability test for a recursive data type,
+         * a value class with cyclic membership.
+         *
+         * The signature of this base method is:
+         *    boolean base(MethodHandle entry, MethodHandle base1, MethodHandle base2, ..., V o1, V o2, int[] counter)
+         *
+         * where the first N+1 parameters are MethodHandle and N is the number of
+         * the recursive value types and followed with Object, Object and int[] parameters.
+         *
+         * This method first calls the method handle that tests the substitutability
+         * of all fields that are not recursively-typed, if any, and then test
+         * the substitutability of the fields that are of each recursive value type.
+         * The pseudo code looks like this:
+         *
+         * boolean base(MethodHandle entry, MethodHandle base1, MethodHandle base2, ..., V o1, V o2, int[] counter) {
+         *    if (o1 == null && o2 == null) return true;
+         *    if (o1 == null || o2 == null) return false;
+         *    if (o1.getClass() != o2. getClass()) return false;
+         *
+         *    for each non-recursively-typed field {
+         *        if (field value of o1 != field value of o2) return false;
+         *    }
+         *
+         *    for each recursively-typed field of type T {
+         *        if (--counter[0] == 0) throw new StackOverflowError();
+         *        // baseT is the method handle corresponding to the recursive type T
+         *        boolean rc = (boolean) baseT.invoke(o1, o2, counter);
+         *        if (!rc) return false;
+         *    }
+         *    return true;
+         * }
+         */
+        MethodHandle generateSubstBase(List<Class<?>> recursiveTypes) {
+            List<MethodHandle> nonRecurGetters = new ArrayList<>();
+            Map<Class<?>, List<MethodHandle>> recurGetters = new LinkedHashMap<>();
+            getterStream(type, TYPE_SORTER).forEach(mh -> {
+                Class<?> ft = fieldType(mh);
+                if (!this.recurFieldTypes.contains(ft)) {
+                    nonRecurGetters.add(mh);
+                } else {
+                    assert recursiveTypes.contains(ft);
+                    recurGetters.computeIfAbsent(ft, t -> new ArrayList<>()).add(mh);
+                }
+            });
+
+            // The first parameter is the method handle of the entry point
+            // followed with one MethodHandle for each recursive value type
+            List<Class<?>> leadingMHParams = new ArrayList<>();
+            int mhParamCount = recursiveTypes.size()+1;
+            for (int i=0; i < mhParamCount; i++) {
+                leadingMHParams.add(MethodHandle.class);
+            }
+
+            MethodType mt = methodType(boolean.class,
+                                       Stream.concat(leadingMHParams.stream(), Stream.of(type, type, int[].class)).collect(toList()));
+            var allParameters = mt.parameterList();
+
+            var instanceTrue = dropArguments(TRUE, 0, allParameters).asType(mt);
+            var instanceFalse = dropArguments(FALSE, 0, allParameters).asType(mt);
+            var accumulator = dropArguments(TRUE, 0, allParameters).asType(mt);
+            var isNull = dropArguments(dropArguments(IS_NULL, 0, leadingMHParams), mhParamCount+2, int[].class).asType(mt);
+            var isSameValueType = dropArguments(dropArguments(IS_SAME_VALUE_CLASS, 0, leadingMHParams), mhParamCount+2, int[].class).asType(mt);
+
+            // This value class contains cyclic membership.
+            // Create a method handle that first calls the method handle that tests
+            // the substitutability of all fields that are not recursively-typed, if any,
+            // and then test the substitutability of the fields that are of each recursive
+            // value type.
+            //
+            // Method handle for the substitutability test for recursive types is built
+            // before that for non-recursive types.
+            for (Map.Entry<Class<?>, List<MethodHandle>> e : recurGetters.entrySet()) {
+                Class<?> ft = e.getKey();
+                int index = recursiveTypes.indexOf(ft);
+                var mtype = methodType(boolean.class,
+                                Stream.concat(leadingMHParams.stream(), Stream.of(ft, ft, int[].class)).collect(toList()));
+                var recurEq = RECUR_VALUE_EQ.asType(methodType(boolean.class, MethodHandle.class, ft, ft, int[].class));
+                var eq = permuteArguments(recurEq, mtype, index+1, mhParamCount, mhParamCount+1, mhParamCount+2);
+                for (MethodHandle getter : e.getValue()) {
+                    assert ft == fieldType(getter);
+                    var thisFieldEqual = filterArguments(eq, mhParamCount, getter, getter);
+                    accumulator = guardWithTest(thisFieldEqual, accumulator, instanceFalse);
+                }
+            }
+
+            if (nonRecurGetters.isEmpty()) {
+                // if both arguments are null, return true;
+                // otherwise return accumulator;
+                return guardWithTest(isNull,
+                                     instanceTrue,
+                                     guardWithTest(isSameValueType, accumulator, instanceFalse));
+            } else {
+                // method handle for substitutability test of the non-recursive-typed fields
+                var mh = valueTypeEquals(type, nonRecurGetters);
+                mh = dropArguments(dropArguments(mh, 0, leadingMHParams), mhParamCount+2, int[].class).asType(mt);
+                return guardWithTest(mh, accumulator, instanceFalse);
+            }
+        }
+
+        /*
+         * Create a method handle where the first N+1 parameters are MethodHandle and
+         * N is the number of the recursive value types and followed with
+         * Object and int[] parameters.  The pseudo code looks like this:
+         *
+         * int hashCode(MethodHandle entry, MethodHandle base1, MethodHandle base2, ..., Object o, int[] counter) {
+         *    int[] newCounter = new int[] { counter[0]; }
+         *    return (int) baseT.invoke(o, newCounter);
+         * }
+         */
+        MethodHandle hashCodeEntry(List<Class<?>> recursiveTypes) {
+            List<Class<?>> leadingMHParams = new ArrayList<>();
+            int mhParamCount = recursiveTypes.size()+1;
+            // the first MethodHandle parameter is this entry point
+            // followed with MethodHandle parameter for each mutual exclusive value class
+            for (int i=0; i < mhParamCount; i++) {
+                leadingMHParams.add(MethodHandle.class);
+            }
+
+            int index = recursiveTypes.indexOf(type);
+            var mtype = methodType(int.class, Stream.concat(leadingMHParams.stream(), Stream.of(type, int[].class)).collect(toList()));
+            var recurHashCode = RECUR_VALUE_HASHCODE.asType(methodType(int.class, MethodHandle.class, type, int[].class));
+            var mh = permuteArguments(recurHashCode, mtype, index+1, mhParamCount, mhParamCount+1);
+            return filterArguments(mh, mhParamCount+1, NEW_COUNTER);
+        }
+
+        /**
+         * A base method for computing the hashcode for a recursive data type,
+         * a value class with cyclic membership.
+         *
+         * The signature of this base method is:
+         *    int base(MethodHandle entry, MethodHandle base1, MethodHandle base2, ..., V o, int[] counter)
+         *
+         * where the first N+1 parameters are MethodHandle and N is the number of
+         * the recursive value types and followed with Object and int[] parameters.
+         *
+         * This method will first invoke a method handle to compute the hash code
+         * of the not recursively-typed fields.  Then compute the hash code of the
+         * remaining recursively-typed fields.
+         */
+        MethodHandle generateHashCodeBase(List<Class<?>> recursiveTypes) {
+            assert status == Status.READY;
+
+            List<MethodHandle> nonRecurGetters = new ArrayList<>();
+            Map<Class<?>, List<MethodHandle>> recurGetters = new LinkedHashMap<>();
+            getterStream(type, null).forEach(mh -> {
+                Class<?> ft = fieldType(mh);
+                if (!this.recurFieldTypes.contains(ft)) {
+                    nonRecurGetters.add(mh);
+                } else {
+                    assert recursiveTypes.contains(ft);
+                    recurGetters.computeIfAbsent(ft, t -> new ArrayList<>()).add(mh);
+                }
+            });
+
+            int mhParamCount = recursiveTypes.size()+1;
+            List<Class<?>> leadingMHParams = new ArrayList<>();
+            for (int i=0; i < mhParamCount; i++) {    // include entry point
+                leadingMHParams.add(MethodHandle.class);
+            }
+
+            MethodType mt = methodType(int.class,
+                                       Stream.concat(leadingMHParams.stream(), Stream.of(type, int[].class)).collect(toList()));
+            var allParameters = mt.parameterList();
+            var hashCombiner = dropArguments(HASH_COMBINER, 2, allParameters);
+            var salt = dropArguments(constant(int.class, SALT), 0, allParameters);
+            var classHasher = dropArguments(hashCodeForType(Class.class).bindTo(type), 0, allParameters);
+            var accumulator = foldArguments(foldArguments(hashCombiner, 1, classHasher), 0, salt);
+            for (MethodHandle getter : nonRecurGetters) {
+                Class<?> ft = fieldType(getter);
+                var hasher = dropArguments(hashCodeForType(ft), 0, leadingMHParams);
+                var hashThisField = filterArguments(hasher, mhParamCount, getter);
+                var combineHashes = foldArguments(hashCombiner, 1, hashThisField);
+                accumulator = foldArguments(combineHashes, 0, accumulator);
+            }
+
+            for (Map.Entry<Class<?>, List<MethodHandle>> e : recurGetters.entrySet()) {
+                Class<?> ft = e.getKey();
+                int index = recursiveTypes.indexOf(ft);
+                var mtype = methodType(int.class, Stream.concat(leadingMHParams.stream(), Stream.of(ft, int[].class)).collect(toList()));
+                var recurHashCode = RECUR_VALUE_HASHCODE.asType(methodType(int.class, MethodHandle.class, ft, int[].class));
+                var hasher = permuteArguments(recurHashCode, mtype, index + 1, mhParamCount, mhParamCount + 1);
+                for (MethodHandle getter : e.getValue()) {
+                    assert ft == fieldType(getter);
+                    var hashThisField = filterArguments(hasher, mhParamCount, getter);
+                    var combineHashes = foldArguments(hashCombiner, 1, hashThisField);
+                    accumulator = foldArguments(combineHashes, 0, accumulator);
+                }
+            }
+            return accumulator;
+        }
+
+        private String debugString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(type.getName()).append(" ").append(status).append("\n");
+            sb.append(fieldValueTypes.stream().filter(c -> !recurFieldTypes.contains(c))
+                            .map(Class::getName)
+                            .collect(joining(" ", "  non-recursive types: ", "\n")));
+            sb.append(recurFieldTypes.stream().map(Class::getName)
+                            .collect(joining(" ", "  recursive types: ", "\n")));
+            for (var n : cyclicMembers.keySet()) {
+                List<Class<?>> cycle = cyclicMembers.get(n);
+                sb.append("  cycle: ");
+                int start = cycle.indexOf(n);
+                for (int i=start; i < cycle.size(); i++ ) {
+                    sb.append(cycle.get(i).getName()).append(" -> ");
+                }
+                for (int i=0; i < start; i++) {
+                    sb.append(cycle.get(i).getName()).append(" -> ");
+                }
+                sb.append(n.getName()).append("\n");
+            };
+            return sb.toString();
+        }
+    }
+
+    private static LinkageError newLinkageError(Throwable e) {
+        return (LinkageError) new LinkageError().initCause(e);
+    }
+
+    /**
+     * Returns {@code true} if the arguments are <em>substitutable</em> to each
+     * other and {@code false} otherwise.
+     * <em>Substitutability</em> means that they cannot be distinguished from
+     * each other in any data-dependent way, meaning that it is safe to substitute
+     * one for the other.
+     *
+     * <ul>
+     * <li>If {@code a} and {@code b} are both {@code null}, this method returns
+     *     {@code true}.
+     * <li>If {@code a} and {@code b} are both instances of the same value class
+     *     {@code V}, this method returns {@code true} if, for all fields {@code f}
+     *      declared in {@code V}, {@code a.f} and {@code b.f} are substitutable.
+     * <li>If {@code a} and {@code b} are both values of the same builtin primitive type,
+     *     this method returns {@code a == b} with the following exception:
+     *     <ul>
+     *     <li> For primitive types {@code float} and {@code double} the
+     *          comparison uses the raw bits corresponding to {@link Float#floatToRawIntBits(float)}
+     *          and {@link Double#doubleToRawLongBits(double)} respectively.
+     *     </ul>
+     * <li>If {@code a} and {@code b} are both instances of the same reference type,
+     *     this method returns {@code a == b}.
+     * <li>Otherwise this method returns {@code false}.
+     * </ul>
+     *
+     * <p>For example,
+     * <pre>{@code interface Number { ... }
+     * // ordinary reference class
+     * class IntNumber implements Number { ... }
+     * // value class
+     * value class IntValue implements Number {
+     *     int i;
+     *     :
+     *     public static IntValue of(int i) {...}     // IntValue::of creates a new value instance
+     * }
+     * // value class with an Object field
+     * value class RefValue {
+     *     Object o;
+     *     :
+     * }
+     *
+     * var val1 = IntValue.of(10);
+     * var val2 = IntValue.of(10);                    // val1 and val2 have the same value
+     * var ref1 = new IntNumber(10);                  // ref1 and ref2 are two reference instances
+     * var ref2 = new IntNumber(10);
+     * assertTrue(isSubstitutable(val1, val2));       // val1 and val2 are both value instances of IntValue
+     * assertFalse(isSubstitutable(ref1, ref2));      // ref1 and ref2 are two reference instances that are not substitutable
+     * assertTrue(isSubstitutable(ref1, ref1));       // a reference instance is substitutable with itself
+     *
+     * var rval1 = RefValue.of(List.of("list"));      // rval1.o and rval2.o both contain a list of one-single element "list"
+     * var rval2 = RefValue.of(List.of("list");
+     * var rval3 = RefValue.of(rval1.o);
+     *
+     * assertFalse(isSubstitutable(rval1, rval2));    // rval1.o and rval2.o are two different List instances and hence not substitutable
+     * assertTrue(isSubstitutable(rval1, rval3 ));    // rval1.o and rval3.o are the same reference instance
+     * }</pre>
+     *
+     * @param a an object
+     * @param b an object to be compared with {@code a} for substitutability
+     * @return {@code true} if the arguments are substitutable to each other;
+     *         {@code false} otherwise.
+     * @param <T> type
+     * @see Float#floatToRawIntBits(float)
+     * @see Double#doubleToRawLongBits(double)
+     */
+    private static <T> boolean isSubstitutable(T a, Object b) {
+        if (VERBOSE) {
+            System.out.println("substitutable " + a.getClass() + ": " + a + " vs " + b);
+        }
+
+        // Called directly from the VM.
+        //
+        // DO NOT use "==" or "!=" on args "a" and "b", with this code or any of
+        // its callees. Could be inside of if_acmp<eq|ne> bytecode implementation.
+
+        if (a == null && b == null) return true;
+        if (a == null || b == null) return false;
+        if (a.getClass() != b.getClass()) return false;
+
+        try {
+            Class<?> type = a.getClass();
+            return (boolean) substitutableInvoker(type).invoke(a, b);
+        } catch (Error|RuntimeException e) {
+            if (VERBOSE) e.printStackTrace();
+            throw e;
+        } catch (Throwable e) {
+            if (VERBOSE) e.printStackTrace();
+            throw new InternalError(e);
+        }
+    }
+
+    /**
+     * Produces a method handle which tests if two arguments are
+     * {@linkplain #isSubstitutable(Object, Object) substitutable}.
+     *
+     * <ul>
+     * <li>If {@code T} is a non-floating point primitive type, this method
+     *     returns a method handle testing the two arguments are the same value,
+     *     i.e. {@code a == b}.
+     * <li>If {@code T} is {@code float} or {@code double}, this method
+     *     returns a method handle representing {@link Float#floatToRawIntBits(float)} or
+     *     {@link Double#doubleToRawLongBits(double)} respectively.
+     * <li>If {@code T} is a reference type that is not {@code Object} and not an
+     *     interface, this method returns a method handle testing
+     *     the two arguments are the same reference, i.e. {@code a == b}.
+     * <li>If {@code T} is a value class, this method returns
+     *     a method handle that returns {@code true} if
+     *     for all fields {@code f} declared in {@code T}, where {@code U} is
+     *     the type of {@code f}, if {@code a.f} and {@code b.f} are substitutable
+     *     with respect to {@code U}.
+     * <li>If {@code T} is an interface or {@code Object}, and
+     *     {@code a} and {@code b} are of the same value class {@code V},
+     *     this method returns a method handle that returns {@code true} if
+     *     {@code a} and {@code b} are substitutable with respect to {@code V}.
+     * </ul>
+     *
+     * @param type class type
+     * @param <T> type
+     * @return a method handle for substitutability test
+     */
+    private static <T> MethodHandle substitutableInvoker(Class<T> type) {
+        if (type.isPrimitive()) {
+            return MethodHandleBuilder.builtinPrimitiveSubstitutable(type);
+        }
+        if (type.isValue()) {
+            return SUBST_TEST_METHOD_HANDLES.get(type);
+        }
+        return MethodHandleBuilder.referenceTypeEquals(type);
+    }
+
+    private static final ClassValue<MethodHandleBuilder> METHOD_HANDLE_BUILDERS = new ClassValue<>() {
+        @Override protected MethodHandleBuilder computeValue(Class<?> type) {
+            var builder = CACHED_METHOD_HANDLE_BUILDERS.get(type);
+            if (builder == null) {
+                builder = MethodHandleBuilder.newBuilder(type).build();
+            }
+            return builder;
+        }
+    };
+
+    // This cache is only used to propagate the builders of mutual recursive types
+    // A -> B -> C -> A as method handles for equals/hashCode for A, B, C are
+    // all precomputed.  This map should only be non-empty only during the short
+    // window propagating to the method handle builder class value.
+    private static Map<Class<?>, MethodHandleBuilder> CACHED_METHOD_HANDLE_BUILDERS = new ConcurrentHashMap<>();
+
+    // store the method handle for value classes in ClassValue
+    private static final ClassValue<MethodHandle> SUBST_TEST_METHOD_HANDLES = new ClassValue<>() {
+        @Override protected MethodHandle computeValue(Class<?> type) {
+            return MethodHandleBuilder.valueTypeEquals(type);
+        }
+    };
+
+    /**
+     * Invoke the hashCode method for the given value object.
+     * @param o the instance to hash.
+     * @return the hash code of the given value object.
+     */
+    private static int valueObjectHashCode(Object o) {
+        Class<?> type = o.getClass();
+        try {
+            // Note: javac disallows user to call super.hashCode if user implemented
+            // risk for recursion for experts crafting byte-code
+            if (!type.isValue())
+                throw new InternalError("must be value class: " + type.getName());
+
+            return (int) HASHCODE_METHOD_HANDLES.get(type).invoke(o);
+        } catch (Error|RuntimeException e) {
+            throw e;
+        } catch (Throwable e) {
+            if (VERBOSE) e.printStackTrace();
+            throw new InternalError(e);
+        }
+    }
+
+    private static final ClassValue<MethodHandle> HASHCODE_METHOD_HANDLES = new ClassValue<>() {
+        @Override protected MethodHandle computeValue(Class<?> type) {
+            return MethodHandleBuilder.valueTypeHashCode(type);
+        }
+    };
+
+    private static final Comparator<MethodHandle> TYPE_SORTER = (mh1, mh2) -> {
+        // sort the getters with the return type
+        Class<?> t1 = mh1.type().returnType();
+        Class<?> t2 = mh2.type().returnType();
+        if (t1 == t2) return 0;
+
+        if (t1.isPrimitive()) {
+            if (!t2.isPrimitive()) {
+                return 1;
+            }
+        } else {
+            if (t2.isPrimitive()) {
+                return -1;
+            }
+        }
+        return -1;
+    };
+
+
+    /**
+     * Constructs a method handle that is capable of recursively
+     * calling itself, whose behavior is determined by a non-recursive
+     * base method handle which gets both the original arguments and a
+     * reference to the recursive method handle.
+     * <p>
+     * Here is pseudocode for the resulting loop handle, plus a sketch
+     * of the behavior of the base function. The symbols {@code A},
+     * {@code a}, and {@code R} represent arguments and return value
+     * for both the recursive function and the base function.
+     *
+     * <blockquote><pre>{@code
+     * R recursive(A... a) {
+     *   MethodHandle recur = &recursive;
+     *   return base(recur, a...);
+     * }
+     * R base(MethodHandle recur, A... a) {
+     *   ... if (no recursion)  return f(a);  ...
+     *   var r2 = recur.invokeExact(a2...);
+     *   var r3 = recur.invokeExact(a3...);
+     *   ... do stuff with r2, r3, etc. ...
+     * }
+     * }</pre></blockquote>
+     * <p>
+     * To make several functions mutually recursive, additional base
+     * arguments can be passed to this combinator.  For each base
+     * function, a recursive adapter is formed (like {@code recur}
+     * above).  The sequence of recursive adapters is passed as
+     * initial arguments to each base function.  Here is pseudocode
+     * that corresponds to three base functions:
+     * <blockquote><pre>{@code
+     * R recursive(A... a) {
+     *   return base(&recursive, &recursive2, &recursive3, a...);
+     * }
+     * R2 recursive2(A2... a2) {
+     *   return base2(&recursive, &recursive2, &recursive3, a2...);
+     * }
+     * R3 recursive3(A3... a3) {
+     *   return base3(&recursive, &recursive2, &recursive3, a3...);
+     * }
+     * R base(MethodHandle recur, MethodHandle recur2,
+     *        MethodHandle recur3, A... a) {
+     *   ... if (no recursion)  return f(a);  ...
+     *   var r2 = recur2.invokeExact(a2...);
+     *   var r3 = recur3.invokeExact(a3...);
+     *   ... do stuff with r2, r3, etc. ...
+     * }
+     * R2 base2(MethodHandle recur, MethodHandle recur2,
+     *        MethodHandle recur3, A2... a2) { ... }
+     * R3 base3(MethodHandle recur, MethodHandle recur2,
+     *        MethodHandle recur3, A3... a3) { ... }
+     * }</pre></blockquote>
+     *
+     * @apiNote Example:
+     * {@snippet lang="java" :
+     * // classic recursive implementation of the factorial function
+     * static int base(MethodHandle recur, int k) throws Throwable {
+     *   if (k <= 1)  return 1;
+     *   return k * (int) recur.invokeExact(k - 1);
+     * }
+     * // assume MH_base is a handle to the above method
+     * MethodHandle recur = MethodHandles.recursive(MH_base);
+     * assertEquals(120, recur.invoke(5));
+     * }
+     * <p>
+     * A constructed recursive method handle is made varargs
+     * if its corresponding base method handle is varargs.
+     * @implSpec
+     * For a single base function, this produces a result equivalent to:
+     * <pre>{@code
+     * class Holder {
+     *   final MethodHandle recur;
+     *   static final MH_recur = ...;  //field getter
+     *   Holder(MethodHandle base) {
+     *     recur = filterArguments(base, 0, MH_recur).bindTo(this);
+     *   }
+     * }
+     * return new Holder(base).recur;
+     * }</pre>
+     * @param base the logic of the function to make recursive
+     * @param moreBases additional base functions to be made mutually recursive
+     * @throws NullPointerException if any argument is null
+     * @throws IllegalArgumentException if any base function does not accept
+     *          the required leading arguments of type {@code MethodHandle}
+     *
+     * @return a method handle which invokes the (first) base function
+     *         on the incoming arguments, with recursive versions of the
+     *         base function (or functions) prepended as extra arguments
+     *
+     * @since Valhalla
+     */
+    static MethodHandle recursive(MethodHandle base, MethodHandle... moreBases) {
+        // freeze the varargs and check for nulls:
+        List<MethodHandle> bases2 = List.of(moreBases);
+        int baseCount = 1 + bases2.size();
+        recursiveChecks(base, baseCount);
+        for (var base2 : bases2) { recursiveChecks(base2, baseCount); }
+        class Holder {
+            final MethodHandle recur;
+            final List<MethodHandle> recurs2;
+            MethodHandle recurs2(int i) { return recurs2.get(i); }
+            Holder() {
+                // Feed the first baseCount parameters of each base
+                // with a fetch of each recur, so we can bind to this:
+                var fetchers = new MethodHandle[baseCount];
+                fetchers[0] = MH_recur;
+                for (int pos = 1; pos < fetchers.length; pos++) {
+                    int i = pos-1;  // index into recurs2
+                    fetchers[pos] = MethodHandles.insertArguments(MH_recurs2, 1, i);
+                }
+                this.recur = makeRecur(base, fetchers);
+                if (baseCount == 1) {
+                    this.recurs2 = List.of();
+                } else {
+                    var recurs2 = new MethodHandle[baseCount-1];
+                    for (int i = 0; i < recurs2.length; i++) {
+                        recurs2[i] = makeRecur(bases2.get(i), fetchers);
+                    }
+                    this.recurs2 = List.of(recurs2);
+                }
+            }
+            MethodHandle makeRecur(MethodHandle base, MethodHandle[] fetchers) {
+                var adapt = filterArguments(base, 0, fetchers);
+                for (int pos = 0; pos < fetchers.length; pos++) {
+                    adapt = adapt.bindTo(this);
+                }
+                return adapt.withVarargs(base.isVarargsCollector());
+            }
+            static final MethodHandle MH_recur, MH_recurs2;
+            static {
+                try {
+                    MH_recur = MethodHandles.lookup()
+                            .findGetter(Holder.class, "recur", MethodHandle.class);
+                    MH_recurs2 = MethodHandles.lookup()
+                            .findVirtual(Holder.class, "recurs2",
+                                    methodType(MethodHandle.class, int.class));
+                } catch (ReflectiveOperationException ex) {
+                    throw new InternalError(ex);
+                }
+            }
+        }
+        return new Holder().recur;
+    }
+
+    private static void recursiveChecks(MethodHandle base, int baseCount) {
+        MethodType mt = base.type();  // implicit null check
+        boolean wrong = (mt.parameterCount() < baseCount);
+        for (int i = 0; i < baseCount && !wrong; i++) {
+            if (mt.parameterType(i) != MethodHandle.class) {
+                wrong = true;
+            }
+        }
+        if (!wrong)  return;
+        throw new IllegalArgumentException("missing leading MethodHandle parameters: " + mt);
+    }
+
+}
diff a/src/java.base/share/classes/java/util/Arrays.java b/src/java.base/share/classes/java/util/Arrays.java
--- a/src/java.base/share/classes/java/util/Arrays.java
+++ b/src/java.base/share/classes/java/util/Arrays.java
@@ -24,10 +24,11 @@
  */
 
 package java.util;
 
 import jdk.internal.util.ArraysSupport;
+import jdk.internal.value.ValueClass;
 import jdk.internal.vm.annotation.IntrinsicCandidate;
 
 import java.io.Serializable;
 import java.lang.reflect.Array;
 import java.util.concurrent.ForkJoinPool;
@@ -3505,14 +3506,17 @@
      *     an array of class {@code newType}
      * @since 1.6
      */
     @IntrinsicCandidate
     public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
+        Class<?> componentType = newType.getComponentType();
         @SuppressWarnings("unchecked")
         T[] copy = ((Object)newType == (Object)Object[].class)
             ? (T[]) new Object[newLength]
-            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
+            : (original.getClass() == newType && componentType.isValue() && ValueClass.isNullRestrictedArray(original)
+                    ? (T[]) ValueClass.newNullRestrictedArray(newType.getComponentType(), newLength)
+                    : (T[]) Array.newInstance(componentType, newLength));
         System.arraycopy(original, 0, copy, 0,
                          Math.min(original.length, newLength));
         return copy;
     }
 
@@ -3804,14 +3808,17 @@
     public static <T,U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {
         int newLength = to - from;
         if (newLength < 0) {
             throw new IllegalArgumentException(from + " > " + to);
         }
+        Class<?> componentType = newType.getComponentType();
         @SuppressWarnings("unchecked")
         T[] copy = ((Object)newType == (Object)Object[].class)
             ? (T[]) new Object[newLength]
-            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
+            : (original.getClass() == newType && componentType.isValue() && ValueClass.isNullRestrictedArray(original)
+                    ? (T[]) ValueClass.newNullRestrictedArray(componentType, newLength)
+                    : (T[]) Array.newInstance(componentType, newLength));
         System.arraycopy(original, from, copy, 0,
                          Math.min(original.length - from, newLength));
         return copy;
     }
 
diff a/src/java.base/share/classes/java/util/Objects.java b/src/java.base/share/classes/java/util/Objects.java
--- a/src/java.base/share/classes/java/util/Objects.java
+++ b/src/java.base/share/classes/java/util/Objects.java
@@ -23,12 +23,14 @@
  * questions.
  */
 
 package java.util;
 
+import jdk.internal.javac.PreviewFeature;
 import jdk.internal.util.Preconditions;
 import jdk.internal.vm.annotation.ForceInline;
+import jdk.internal.misc.Unsafe;
 
 import java.util.function.Supplier;
 
 /**
  * This class consists of {@code static} utility methods for operating
@@ -175,10 +177,101 @@
     public static String toIdentityString(Object o) {
         requireNonNull(o);
         return o.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(o));
     }
 
+   /**
+    * {@return {@code true} if the specified object reference is an identity object,
+    * otherwise {@code false}}
+    *
+    * @param obj an object
+    * @throws NullPointerException if {@code obj} is {@code null}
+    */
+   @PreviewFeature(feature = PreviewFeature.Feature.VALUE_OBJECTS)
+//    @IntrinsicCandidate
+    public static boolean isIdentityObject(Object obj) {
+        requireNonNull(obj);
+        return obj.getClass().isIdentity() ||  // Before Valhalla all classes are identity classes
+                obj.getClass() == Object.class;
+    }
+
+    /**
+     * Checks that the specified object reference is an identity object.
+     *
+     * @param obj the object reference to check for identity
+     * @param <T> the type of the reference
+     * @return {@code obj} if {@code obj} is an identity object
+     * @throws NullPointerException if {@code obj} is {@code null}
+     * @throws IdentityException if {@code obj} is not an identity object
+     * @since Valhalla
+     */
+    @PreviewFeature(feature = PreviewFeature.Feature.VALUE_OBJECTS)
+    @ForceInline
+    public static <T> T requireIdentity(T obj) {
+        Objects.requireNonNull(obj);
+        if (!isIdentityObject(obj))
+            throw new IdentityException(obj.getClass());
+        return obj;
+    }
+
+    /**
+     * Checks that the specified object reference is an identity object.
+     *
+     * @param obj the object reference to check for identity
+     * @param message detail message to be used in the event that an
+     *        {@code IdentityException} is thrown; may be null
+     * @param <T> the type of the reference
+     * @return {@code obj} if {@code obj} is an identity object
+     * @throws NullPointerException if {@code obj} is {@code null}
+     * @throws IdentityException if {@code obj} is not an identity object
+     * @since Valhalla
+     */
+    @PreviewFeature(feature = PreviewFeature.Feature.VALUE_OBJECTS)
+    @ForceInline
+    public static <T> T requireIdentity(T obj, String message) {
+        Objects.requireNonNull(obj);
+        if (!isIdentityObject(obj))
+            throw new IdentityException(message);
+        return obj;
+    }
+
+    /**
+     * Checks that the specified object reference is an identity object.
+     *
+     * @param obj the object reference to check for identity
+     * @param messageSupplier supplier of the detail message to be
+     *        used in the event that an {@code IdentityException} is thrown; may be null
+     * @param <T> the type of the reference
+     * @return {@code obj} if {@code obj} is an identity object
+     * @throws NullPointerException if {@code obj} is {@code null}
+     * @throws IdentityException if {@code obj} is not an identity object
+     * @since Valhalla
+     */
+    @PreviewFeature(feature = PreviewFeature.Feature.VALUE_OBJECTS)
+    @ForceInline
+    public static <T> T requireIdentity(T obj, Supplier<String> messageSupplier) {
+        Objects.requireNonNull(obj);
+        if (!isIdentityObject(obj))
+            throw new IdentityException(messageSupplier == null ?
+                    null : messageSupplier.get());
+        return obj;
+    }
+
+   /**
+    * {@return {@code true} if the specified object is a {@linkplain Class#isValue value object},
+    * otherwise {@code false}}
+    *
+    * @param obj an object
+    * @throws NullPointerException if {@code obj} is {@code null}
+    */
+   @PreviewFeature(feature = PreviewFeature.Feature.VALUE_OBJECTS)
+//    @IntrinsicCandidate
+    public static boolean isValueObject(Object obj) {
+        requireNonNull(obj, "obj");
+        return obj.getClass().isValue();
+    }
+
     /**
      * {@return 0 if the arguments are identical and {@code
      * c.compare(a, b)} otherwise}
      * Consequently, if both arguments are {@code null} 0
      * is returned.
@@ -415,10 +508,21 @@
     public static
     int checkFromIndexSize(int fromIndex, int size, int length) {
         return Preconditions.checkFromIndexSize(fromIndex, size, length, null);
     }
 
+    /**
+     * Return the size of the object in the heap.
+     *
+     * @param o an object
+     * @return the objects's size
+     * @since Valhalla
+     */
+    public static long getObjectSize(Object o) {
+        return Unsafe.getUnsafe().getObjectSize(o);
+    }
+
     /**
      * Checks if the {@code index} is within the bounds of the range from
      * {@code 0} (inclusive) to {@code length} (exclusive).
      *
      * <p>The {@code index} is defined to be out of bounds if any of the
diff a/src/java.base/share/classes/java/util/WeakHashMap.java b/src/java.base/share/classes/java/util/WeakHashMap.java
--- a/src/java.base/share/classes/java/util/WeakHashMap.java
+++ b/src/java.base/share/classes/java/util/WeakHashMap.java
@@ -23,10 +23,13 @@
  * questions.
  */
 
 package java.util;
 
+import sun.security.action.GetPropertyAction;
+
+import java.lang.ref.SoftReference;
 import java.lang.ref.WeakReference;
 import java.lang.ref.ReferenceQueue;
 import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
@@ -34,37 +37,58 @@
 
 /**
  * Hash table based implementation of the {@code Map} interface, with
  * <em>weak keys</em>.
  * An entry in a {@code WeakHashMap} will automatically be removed when
- * its key is no longer in ordinary use.  More precisely, the presence of a
+ * its key is no longer in ordinary use.
+ * More precisely, for keys that are identity objects, the presence of a
  * mapping for a given key will not prevent the key from being discarded by the
  * garbage collector, that is, made finalizable, finalized, and then reclaimed.
+ * For keys that are {@linkplain Class#isValue() value objects}, the retention of the
+ * key and value depends on the {@link ValuePolicy} for the {@code WeakHashMap} and
+ * the garbage collection handling of objects that are linked by {@link SoftReference}.
  * When a key has been discarded its entry is effectively removed from the map,
  * so this class behaves somewhat differently from other {@code Map}
  * implementations.
  *
+ * <p>
+ * Keys that are {@linkplain Class#isValue() value objects} do not have identity and cannot be
+ * the referent in any {@link java.lang.ref.Reference} including {@link WeakReference}.
+ * The retention of entries with keys that are value objects is selected
+ * using {@link ValuePolicy} when the {@code WeakHashMap} is created.
+ * The default {@code ValuePolicy} is {@link ValuePolicy#defaultValuePolicy()}.
+ * The retention modes implemented by {@link #put(Object, Object) WeakHashMap.put(k,v)} are:
+ * <UL>
+ *     <LI> {@linkplain ValuePolicy#STRONG SOFT} - entries have a lifetime similar to
+ *          referents of {@link SoftReference},
+ *     <LI> {@linkplain ValuePolicy#STRONG STRONG} - entries are retained until removed,
+ *     <LI> {@linkplain ValuePolicy#STRONG DISCARD} - entries are discarded and not put in the map,
+ *     <LI> {@linkplain ValuePolicy#STRONG THROW} - entries are not inserted and
+ *          {@link #put(Object, Object) put(k,v)} throws {@link IdentityException}
+ * </UL>
+ *
  * <p> Both null values and the null key are supported. This class has
  * performance characteristics similar to those of the {@code HashMap}
  * class, and has the same efficiency parameters of <em>initial capacity</em>
  * and <em>load factor</em>.
  *
  * <p> Like most collection classes, this class is not synchronized.
  * A synchronized {@code WeakHashMap} may be constructed using the
  * {@link Collections#synchronizedMap Collections.synchronizedMap}
  * method.
  *
- * <p> This class is intended primarily for use with key objects whose
+ * <p> <i>Update needed for Value Objects:
+ * <br>This class is intended primarily for use with key objects whose
  * {@code equals} methods test for object identity using the
  * {@code ==} operator.  Once such a key is discarded it can never be
  * recreated, so it is impossible to do a lookup of that key in a
  * {@code WeakHashMap} at some later time and be surprised that its entry
  * has been removed.  This class will work perfectly well with key objects
  * whose {@code equals} methods are not based upon object identity, such
  * as {@code String} instances.  With such recreatable key objects,
  * however, the automatic removal of {@code WeakHashMap} entries whose
- * keys have been discarded may prove to be confusing.
+ * keys have been discarded may prove to be confusing.</i>
  *
  * <p> The behavior of the {@code WeakHashMap} class depends in part upon
  * the actions of the garbage collector, so several familiar (though not
  * required) {@code Map} invariants do not hold for this class.  Because
  * the garbage collector may discard keys at any time, a
@@ -187,18 +211,23 @@
      *
      * @see ConcurrentModificationException
      */
     int modCount;
 
+    // Current policy with regard to keys that are Value classes.
+    private final ValuePolicy valuePolicy;
+
     @SuppressWarnings("unchecked")
     private Entry<K,V>[] newTable(int n) {
         return (Entry<K,V>[]) new Entry<?,?>[n];
     }
 
     /**
      * Constructs a new, empty {@code WeakHashMap} with the given initial
      * capacity and the given load factor.
+     * The {@code WeakHashMap} is created using the {@linkplain ValuePolicy#defaultValuePolicy()
+     * default policy for value objects}.
      *
      * @apiNote
      * To create a {@code WeakHashMap} with an initial capacity that accommodates
      * an expected number of mappings, use {@link #newWeakHashMap(int) newWeakHashMap}.
      *
@@ -206,28 +235,46 @@
      * @param  loadFactor      The load factor of the {@code WeakHashMap}
      * @throws IllegalArgumentException if the initial capacity is negative,
      *         or if the load factor is nonpositive.
      */
     public WeakHashMap(int initialCapacity, float loadFactor) {
+        this(initialCapacity, loadFactor, ValuePolicy.DEFAULT_VALUE_POLICY);
+    }
+
+    /**
+     * Constructs a new, empty {@code WeakHashMap} with the given initial
+     * capacity, the given load factor, and value policy.
+     *
+     * @param  initialCapacity The initial capacity of the {@code WeakHashMap}
+     * @param  loadFactor      The load factor of the {@code WeakHashMap}
+     * @param  valuePolicy     The {@link ValuePolicy} for keys that are value objects
+     * @throws IllegalArgumentException if the initial capacity is negative,
+     *         or if the load factor is nonpositive.
+     * @throws NullPointerException if {@code valuePolicy} is null
+     */
+    public WeakHashMap(int initialCapacity, float loadFactor, ValuePolicy valuePolicy) {
         if (initialCapacity < 0)
             throw new IllegalArgumentException("Illegal Initial Capacity: "+
                                                initialCapacity);
         if (initialCapacity > MAXIMUM_CAPACITY)
             initialCapacity = MAXIMUM_CAPACITY;
 
         if (loadFactor <= 0 || Float.isNaN(loadFactor))
             throw new IllegalArgumentException("Illegal Load factor: "+
                                                loadFactor);
+        this.valuePolicy = Objects.requireNonNull(valuePolicy, "valuePolicy");
         int capacity = HashMap.tableSizeFor(initialCapacity);
         table = newTable(capacity);
         this.loadFactor = loadFactor;
         threshold = (int)(capacity * loadFactor);
     }
 
     /**
      * Constructs a new, empty {@code WeakHashMap} with the given initial
      * capacity and the default load factor (0.75).
+     * The {@code WeakHashMap} is created using the {@linkplain ValuePolicy#defaultValuePolicy()
+     * default policy for value objects}.
      *
      * @apiNote
      * To create a {@code WeakHashMap} with an initial capacity that accommodates
      * an expected number of mappings, use {@link #newWeakHashMap(int) newWeakHashMap}.
      *
@@ -239,20 +286,35 @@
     }
 
     /**
      * Constructs a new, empty {@code WeakHashMap} with the default initial
      * capacity (16) and load factor (0.75).
+     * The {@code WeakHashMap} is created using the {@linkplain ValuePolicy#defaultValuePolicy()
+     * default policy for value objects}.
      */
     public WeakHashMap() {
         this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
     }
 
+    /**
+     * Constructs a new, empty {@code WeakHashMap} with the {@link ValuePolicy},
+     * the default initial capacity (16) and load factor (0.75).
+     *
+     * @param  valuePolicy     The {@link ValuePolicy} for keys that are value objects; non-null
+     * @throws NullPointerException if {@code valuePolicy} is null
+     */
+    public WeakHashMap(ValuePolicy valuePolicy) {
+        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, valuePolicy);
+    }
+
     /**
      * Constructs a new {@code WeakHashMap} with the same mappings as the
      * specified map.  The {@code WeakHashMap} is created with the default
      * load factor (0.75) and an initial capacity sufficient to hold the
      * mappings in the specified map.
+     * The {@code WeakHashMap} is created using the {@linkplain ValuePolicy#defaultValuePolicy()
+     * default policy for value objects}.
      *
      * @param   m the map whose mappings are to be placed in this map
      * @throws  NullPointerException if the specified map is null
      * @since   1.3
      */
@@ -261,10 +323,17 @@
                 DEFAULT_INITIAL_CAPACITY),
              DEFAULT_LOAD_FACTOR);
         putAll(m);
     }
 
+    /**
+     * {@return the {@link ValuePolicy} for this WeakHashMap.}
+     */
+    public ValuePolicy valuePolicy() {
+        return valuePolicy;
+    }
+
     // internal utilities
 
     /**
      * Value representing null keys inside tables.
      */
@@ -289,11 +358,12 @@
      * default uses Object.equals.
      */
     private boolean matchesKey(Entry<K,V> e, Object key) {
         // check if the given entry refers to the given key without
         // keeping a strong reference to the entry's referent
-        if (e.refersTo(key)) return true;
+        // only identity objects can be compared to a reference
+        if (Objects.isIdentityObject(key) && e.refersTo(key)) return true;
 
         // then check for equality if the referent is not cleared
         Object k = e.get();
         return k != null && key.equals(k);
     }
@@ -453,13 +523,20 @@
      * @param value value to be associated with the specified key.
      * @return the previous value associated with {@code key}, or
      *         {@code null} if there was no mapping for {@code key}.
      *         (A {@code null} return can also indicate that the map
      *         previously associated {@code null} with {@code key}.)
+     * @throws IdentityException if {@code key} is a value object
+     *         and the {@link #valuePolicy() valuePolicy} is {@link ValuePolicy#THROW}.
      */
     public V put(K key, V value) {
         Object k = maskNull(key);
+        final boolean isValue = Objects.isValueObject(k);
+        if (isValue && valuePolicy == ValuePolicy.DISCARD) {
+            // put of a value object key with value policy DISCARD is more like remove(key)
+            return remove(key);
+        }
         int h = hash(k);
         Entry<K,V>[] tab = getTable();
         int i = indexFor(h, tab.length);
 
         for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
@@ -469,18 +546,49 @@
                     e.value = value;
                 return oldValue;
             }
         }
 
-        modCount++;
         Entry<K,V> e = tab[i];
-        tab[i] = new Entry<>(k, value, queue, h, e);
+        e = (isValue) ? newValueEntry(k, value, queue, h, e) : new Entry<>(k, value, queue, h, e);
+
+        modCount++;
+        tab[i] = e;
         if (++size > threshold)
             resize(tab.length * 2);
         return null;
     }
 
+    /**
+     * Return a new entry for keys that are value objects.
+     * The {@link ValuePolicy} for this WeakHashMap determines what entry is returned.
+     * <ul>
+     *     <li> THROW - Throws an IdentityException</li>
+     *     <li> STRONG - a StrongEntry </li>
+     *     <li> SOFT - a SoftEntry</li>
+     *     <li> DISCARD - null</li>
+     * </ul>
+     *
+     * @param key key with which the specified value is to be associated; non-null
+     * @param value value to be associated with the specified key
+     * @param queue queue
+     * @param hash hash
+     * @param next next
+     * @return a new entry or null to discard
+     * @throws IdentityException if the valuePolicy is {@link ValuePolicy#THROW}
+     */
+    private Entry<K, V> newValueEntry(Object key, V value,
+                                      ReferenceQueue<Object> queue,
+                                      int hash, Entry<K,V> next) {
+        return switch (valuePolicy) {
+            case THROW -> throw new IdentityException(key.getClass());
+            case STRONG -> StrongEntry.newStrongEntry(key, value, queue, hash,  next);
+            case SOFT ->  SoftEntry.newSoftEntry(key, value, queue, hash,  next);
+            case DISCARD -> null;
+        };
+    }
+
     /**
      * Rehashes the contents of this map into a new array with a
      * larger capacity.  This method is called automatically when the
      * number of keys in this map reaches its threshold.
      *
@@ -545,10 +653,12 @@
      * These mappings will replace any mappings that this map had for any
      * of the keys currently in the specified map.
      *
      * @param m mappings to be stored in this map.
      * @throws  NullPointerException if the specified map is null.
+     * @throws  IdentityException if any of the {@code keys} is a value object
+     *         and the {@link #valuePolicy() valuePolicy} is {@link ValuePolicy#THROW}.
      */
     public void putAll(Map<? extends K, ? extends V> m) {
         int numKeysToBeAdded = m.size();
         if (numKeysToBeAdded == 0)
             return;
@@ -575,10 +685,28 @@
 
         for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
             put(e.getKey(), e.getValue());
     }
 
+    /**
+     * {@inheritDoc}
+     * @param key {@inheritDoc}
+     * @param value {@inheritDoc}
+     * @return {@inheritDoc}
+     *
+     * @throws  IdentityException if {@code key} is a value object
+     *         and the {@link #valuePolicy() valuePolicy} is {@link ValuePolicy#THROW}.
+     */
+    public V putIfAbsent(K key, V value) {
+        V v = get(key);
+        if (v == null) {
+            v = put(key, value);
+        }
+
+        return v;
+    }
+
     /**
      * Removes the mapping for a key from this weak hash map if it is present.
      * More formally, if this map contains a mapping from key {@code k} to
      * value {@code v} such that <code>(key==null ?  k==null :
      * key.equals(k))</code>, that mapping is removed.  (The map can contain
@@ -764,10 +892,100 @@
         public String toString() {
             return getKey() + "=" + getValue();
         }
     }
 
+    /**
+     * A SoftEntry is used for value class keys in which the entries are retained
+     * until there is some memory pressure.  An anchor object is used as the referent
+     * of a SoftReference and also as the referent of the WeakReference.
+     * After the SoftReference is cleared, due to GC pressure, the WeakReference is cleared too.
+     *
+     * @param <K> key
+     * @param <V> value
+     */
+    private static class SoftEntry<K, V> extends Entry<K, V> {
+        final Object realKey;
+        // SoftReference to the anchor to keep it alive until GC clears the SoftReference
+        private final SoftReference<Object> softAnchor;
+
+        static <K, V> SoftEntry<K, V> newSoftEntry(Object key, V value,
+                                      ReferenceQueue<Object> queue,
+                                      int hash, Entry<K, V> next) {
+            // Select a new anchor object; the entry will be retained until the anchor is collected
+            Object anchor = new Object();
+            return new SoftEntry<>(anchor, key, value, queue, hash, next);
+        }
+
+        private SoftEntry(Object anchor, Object key, V value,
+                    ReferenceQueue<Object> queue,
+                    int hash, Entry<K,V> next) {
+            super(anchor, value, queue, hash, next);
+            this.realKey = key;
+            this.softAnchor = new SoftReference<>(anchor);
+        }
+
+        /**
+         * The real key is not the referent.
+         * {{@inheritDoc}}
+         */
+        @Override
+        @SuppressWarnings("unchecked")
+        public K get() {
+            return (K) realKey;
+        }
+
+        @SuppressWarnings("unchecked")
+        public K getKey() {
+            return (K) realKey;
+        }
+    }
+
+    /**
+     * A StrongEntry is used for value class keys in which the entries are retained
+     * until removed.  A singleton instance is used as the referent of the WeakReference.
+     * Since the anchor is never reclaimed, the Entry is retained forever.
+     *
+     * @param <K> key
+     * @param <V> value
+     */
+    private static class StrongEntry<K, V> extends Entry<K, V> {
+        final Object realKey;
+
+        // A permanent strong reference to an Object
+        private static final Object STRONG_ANCHOR = new Object();
+
+        static <K, V> StrongEntry<K, V> newStrongEntry(Object key, V value,
+                                      ReferenceQueue<Object> queue,
+                                      int hash, Entry<K, V> next) {
+            return new StrongEntry<>(STRONG_ANCHOR, key, value, queue, hash, next);
+        }
+
+        private StrongEntry(Object anchor, Object key, V value,
+                    ReferenceQueue<Object> queue,
+                    int hash, Entry<K,V> next) {
+            super(anchor, value, queue, hash, next);
+            this.realKey = key;
+        }
+
+        /**
+         * The real key is not the referent.
+         * {{@inheritDoc}}
+         */
+        @Override
+        @SuppressWarnings("unchecked")
+        public K get() {
+            return (K) realKey;
+        }
+
+
+        @SuppressWarnings("unchecked")
+        public K getKey() {
+            return (K) realKey;
+        }
+    }
+
     private abstract class HashIterator<T> implements Iterator<T> {
         private int index;
         private Entry<K,V> entry;
         private Entry<K,V> lastReturned;
         private int expectedModCount = modCount;
@@ -1361,6 +1579,73 @@
             throw new IllegalArgumentException("Negative number of mappings: " + numMappings);
         }
         return new WeakHashMap<>(HashMap.calculateHashMapCapacity(numMappings));
     }
 
+    /**
+     * Enum for the ValuePolicy; when putting a key and value into a WeakHashMap
+     * determines how keys that are value objects are retained (or not).
+     * The default {@code ValuePolicy} is {@link ValuePolicy#SOFT}.
+     * @since Valhalla
+     */
+    public enum ValuePolicy {
+        /**
+         * If the key is a value object, retain the key and value until removed or
+         * there is memory pressure that causes soft references to be cleared.
+         */
+        SOFT,
+        /**
+         * If the key is a value object, retain the key and value until removed.
+         */
+        STRONG,
+        /**
+         * If the key is a value object, discard the key and value immediately;
+         * such keys and values are not retained.
+         */
+        DISCARD,
+        /**
+         * If the key is a value object, throw {@link IdentityException};
+         * such keys and values are not retained.
+         */
+        THROW;
+
+        /**
+         * {@return the default ValuePolicy}
+         *
+         * The default {@code ValuePolicy} is {@link ValuePolicy#SOFT} unless overridden by
+         * the system property {@systemProperty java.util.WeakHashMap.valueKeyRetention}.
+         * If the property is set to the name of a {@code ValuePolicy} enum,
+         * the default {@code ValuePolicy} is set using {@link ValuePolicy#valueOf(String)}.
+         * If the property value is absent or not valid, the policy is set to {@link ValuePolicy#SOFT}.
+         */
+        public static ValuePolicy defaultValuePolicy() {
+            return DEFAULT_VALUE_POLICY;
+        }
+
+        // System property name for the default ValuePolicy
+        private static final String WEAK_HASH_MAP_VALUE_KEY_RETENTION =
+                "java.util.WeakHashMap.valueKeyRetention";
+
+        // Default WeakHashMap ValuePolicy for keys that are value objects
+        private static final ValuePolicy DEFAULT_VALUE_POLICY = initDefaultValuePolicy();
+
+        /**
+         * {@return the default policy for retention of keys that are value classes}
+         * If the system property "java.util.WeakHashMap.valueKeyRetention"
+         * is the name of a {@link ValuePolicy} enum return it,
+         * otherwise return {@link ValuePolicy#SOFT}.
+         */
+        private static ValuePolicy initDefaultValuePolicy() {
+            try {
+                String p = GetPropertyAction
+                        .privilegedGetProperty(WEAK_HASH_MAP_VALUE_KEY_RETENTION);
+                if (p != null) {
+                    return ValuePolicy.valueOf(p);
+                }
+            } catch (IllegalArgumentException ex) {
+            }
+
+            return SOFT;  // hardcoded default if property not set
+        }
+    }
+
 }
diff a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java
--- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java
+++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java
@@ -29,10 +29,11 @@
 import java.lang.annotation.Annotation;
 import java.lang.foreign.MemorySegment;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodType;
 import java.lang.module.ModuleDescriptor;
+import java.lang.reflect.ClassFileFormatVersion;
 import java.lang.reflect.Executable;
 import java.lang.reflect.Method;
 import java.net.URI;
 import java.nio.charset.CharacterCodingException;
 import java.nio.charset.Charset;
@@ -569,10 +570,16 @@
      * Creates a new StackWalker
      */
     StackWalker newStackWalkerInstance(Set<StackWalker.Option> options,
                                        ContinuationScope contScope,
                                        Continuation continuation);
+
+    /**
+     * Returns the class file format version of the class.
+     */
+    int classFileFormatVersion(Class<?> klass);
+
     /**
      * Returns '<loader-name>' @<id> if classloader has a name
      * explicitly set otherwise <qualified-class-name> @<id>
      */
     String getLoaderNameID(ClassLoader loader);
diff a/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java
--- a/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java
+++ b/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java
@@ -60,10 +60,16 @@
     /**
      * Returns true if the given flags has MN_HIDDEN_MEMBER flag set.
      */
     boolean isHiddenMember(int flags);
 
+    /**
+     * Returns true if the member of the given method handle is a null-restricted
+     * field.
+     */
+    boolean isNullRestrictedField(MethodHandle mh);
+
     /**
      * Returns a map of class name in internal forms to its corresponding
      * class bytes per the given stream of LF_RESOLVE and SPECIES_RESOLVE
      * trace logs. Used by GenerateJLIClassesPlugin to enable generation
      * of such classes during the jlink phase.
diff a/src/java.base/share/classes/jdk/internal/access/JavaLangReflectAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangReflectAccess.java
--- a/src/java.base/share/classes/jdk/internal/access/JavaLangReflectAccess.java
+++ b/src/java.base/share/classes/jdk/internal/access/JavaLangReflectAccess.java
@@ -100,9 +100,13 @@
     public <T extends AccessibleObject> T getRoot(T obj);
 
     /** Tests if this is a trusted final field */
     public boolean isTrustedFinalField(Field f);
 
+
+    /** Tests if this is a null-restricted field */
+    public boolean isNullRestrictedField(Field f);
+
     /** Returns a new instance created by the given constructor with access check */
     public <T> T newInstance(Constructor<T> ctor, Object[] args, Class<?> caller)
         throws IllegalAccessException, InstantiationException, InvocationTargetException;
 }
diff a/src/java.base/share/classes/jdk/internal/classfile/impl/BoundAttribute.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BoundAttribute.java
--- a/src/java.base/share/classes/jdk/internal/classfile/impl/BoundAttribute.java
+++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BoundAttribute.java
@@ -933,10 +933,27 @@
             }
             return permittedSubclasses;
         }
     }
 
+    public static final class BoundPreloadAttribute extends BoundAttribute<PreloadAttribute>
+            implements PreloadAttribute {
+        private List<ClassEntry> preloads = null;
+
+        public BoundPreloadAttribute(ClassReader cf, AttributeMapper<PreloadAttribute> mapper, int pos) {
+            super(cf, mapper, pos);
+        }
+
+        @Override
+        public List<ClassEntry> preloads() {
+            if (preloads == null) {
+                preloads = readEntryList(payloadStart);
+            }
+            return preloads;
+        }
+    }
+
     public static abstract sealed class BoundCodeAttribute
             extends BoundAttribute<CodeAttribute>
             implements CodeAttribute
             permits CodeImpl {
         protected final int codeStart;
diff a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassPrinterImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassPrinterImpl.java
--- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassPrinterImpl.java
+++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassPrinterImpl.java
@@ -899,10 +899,13 @@
                     nodes.add(list("nest members", "member", nma.nestMembers().stream()
                             .map(mp -> mp.name().stringValue())));
                 case PermittedSubclassesAttribute psa ->
                     nodes.add(list("permitted subclasses", "subclass", psa.permittedSubclasses().stream()
                             .map(e -> e.name().stringValue())));
+                case PreloadAttribute pa ->
+                    nodes.add(list("preload classes", "class", pa.preloads().stream()
+                            .map(e -> e.name().stringValue())));
                 default -> {}
             }
             if (verbosity == Verbosity.TRACE_ALL) switch (attr) {
                 case EnclosingMethodAttribute ema ->
                     nodes.add(map("enclosing method",
diff a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassRemapperImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassRemapperImpl.java
--- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassRemapperImpl.java
+++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassRemapperImpl.java
@@ -54,10 +54,11 @@
 import java.lang.classfile.attribute.InnerClassesAttribute;
 import java.lang.classfile.attribute.ModuleAttribute;
 import java.lang.classfile.attribute.ModuleProvideInfo;
 import java.lang.classfile.attribute.NestHostAttribute;
 import java.lang.classfile.attribute.NestMembersAttribute;
+import java.lang.classfile.attribute.PreloadAttribute;
 import java.lang.classfile.attribute.PermittedSubclassesAttribute;
 import java.lang.classfile.attribute.RecordAttribute;
 import java.lang.classfile.attribute.RecordComponentInfo;
 import java.lang.classfile.attribute.RuntimeInvisibleAnnotationsAttribute;
 import java.lang.classfile.attribute.RuntimeInvisibleParameterAnnotationsAttribute;
@@ -140,10 +141,14 @@
                         .map(nm -> map(nm.asSymbol())).toList()));
             case PermittedSubclassesAttribute psa ->
                 clb.with(PermittedSubclassesAttribute.ofSymbols(
                         psa.permittedSubclasses().stream().map(ps ->
                                 map(ps.asSymbol())).toList()));
+            case PreloadAttribute pa ->
+                clb.with(PreloadAttribute.ofSymbols(
+                        pa.preloads().stream().map(pc ->
+                                map(pc.asSymbol())).toList()));
             case RuntimeVisibleAnnotationsAttribute aa ->
                 clb.with(RuntimeVisibleAnnotationsAttribute.of(
                         mapAnnotations(aa.annotations())));
             case RuntimeInvisibleAnnotationsAttribute aa ->
                 clb.with(RuntimeInvisibleAnnotationsAttribute.of(
diff a/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java b/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java
--- a/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java
+++ b/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java
@@ -70,10 +70,11 @@
 import java.lang.classfile.attribute.ModuleResolutionAttribute;
 import java.lang.classfile.attribute.ModuleTargetAttribute;
 import java.lang.classfile.attribute.NestHostAttribute;
 import java.lang.classfile.attribute.NestMembersAttribute;
 import java.lang.classfile.attribute.PermittedSubclassesAttribute;
+import java.lang.classfile.attribute.PreloadAttribute;
 import java.lang.classfile.attribute.RecordAttribute;
 import java.lang.classfile.attribute.RecordComponentInfo;
 import java.lang.classfile.attribute.RuntimeInvisibleAnnotationsAttribute;
 import java.lang.classfile.attribute.RuntimeInvisibleParameterAnnotationsAttribute;
 import java.lang.classfile.attribute.RuntimeInvisibleTypeAnnotationsAttribute;
@@ -427,10 +428,26 @@
         public List<ClassEntry> permittedSubclasses() {
             return permittedSubclasses;
         }
     }
 
+    public static final class UnboundPreloadAttribute
+            extends UnboundAttribute<PreloadAttribute>
+            implements PreloadAttribute {
+        private final List<ClassEntry> preloads;
+
+        public UnboundPreloadAttribute(List<ClassEntry> preloads) {
+            super(Attributes.PRELOAD);
+            this.preloads = List.copyOf(preloads);
+        }
+
+        @Override
+        public List<ClassEntry> preloads() {
+            return preloads;
+        }
+    }
+
     public static final class UnboundNestMembersAttribute
             extends UnboundAttribute<NestMembersAttribute>
             implements NestMembersAttribute {
         private final List<ClassEntry> memberEntries;
 
diff a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java
--- a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java
+++ b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java
@@ -65,10 +65,13 @@
      */
     public enum Feature {
         // not used, but required for interim javac to not warn.
         VIRTUAL_THREADS,
         FOREIGN,
+        @JEP(number=401, title="Value Classes and Objects", status = "Preview")
+        VALUE_OBJECTS,
+
         @JEP(number=459, title="String Templates", status="Second Preview")
         STRING_TEMPLATES,
         @JEP(number=445, title="Unnamed Classes and Instance Main Methods", status="Deprecated")
         UNNAMED_CLASSES,
         @JEP(number=463, title="Implicitly Declared Classes and Instance Main Methods", status="Preview")
diff a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java
--- a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java
+++ b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2022, 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
@@ -175,19 +175,72 @@
      *         {@link NullPointerException}
      */
     @IntrinsicCandidate
     public native void putInt(Object o, long offset, int x);
 
+
+    /**
+     * Returns true if the given field is flattened.
+     */
+    public boolean isFlatField(Field f) {
+        if (f == null) {
+            throw new NullPointerException();
+        }
+        return isFlatField0(f);
+    }
+
+    private native boolean isFlatField0(Object o);
+
+    /* Returns true if the given field has a null marker
+     * <p>
+     * Nullable flat fields are stored in a flattened representation
+     * and have an associated null marker to indicate if the the field value is
+     * null or the one stored with the flat representation
+     */
+
+     public boolean hasNullMarker(Field f) {
+        if (f == null) {
+            throw new NullPointerException();
+        }
+        return hasNullMarker0(f);
+     }
+
+     private native boolean hasNullMarker0(Object o);
+
+     /* Returns the offset of the null marker of the field,
+      * or -1 if the field doesn't have a null marker
+      */
+
+     public int nullMarkerOffset(Field f) {
+        if (f == null) {
+            throw new NullPointerException();
+        }
+        return nullMarkerOffset0(f);
+     }
+
+     private native int nullMarkerOffset0(Object o);
+
+    /**
+     * Returns true if the given class is a flattened array.
+     */
+    @IntrinsicCandidate
+    public native boolean isFlatArray(Class<?> arrayClass);
+
     /**
      * Fetches a reference value from a given Java variable.
+     * This method can return a reference to either an object or value
+     * or a null reference.
+     *
      * @see #getInt(Object, long)
      */
     @IntrinsicCandidate
     public native Object getReference(Object o, long offset);
 
     /**
      * Stores a reference value into a given Java variable.
+     * This method can store a reference to either an object or value
+     * or a null reference.
      * <p>
      * Unless the reference {@code x} being stored is either null
      * or matches the field type, the results are undefined.
      * If the reference {@code o} is non-null, card marks or
      * other store barriers for that object (if the VM requires them)
@@ -195,10 +248,83 @@
      * @see #putInt(Object, long, int)
      */
     @IntrinsicCandidate
     public native void putReference(Object o, long offset, Object x);
 
+    /**
+     * Fetches a value of type {@code <V>} from a given Java variable.
+     * More specifically, fetches a field or array element within the given
+     * {@code o} object at the given offset, or (if {@code o} is null)
+     * from the memory address whose numerical value is the given offset.
+     *
+     * @param o Java heap object in which the variable resides, if any, else
+     *        null
+     * @param offset indication of where the variable resides in a Java heap
+     *        object, if any, else a memory address locating the variable
+     *        statically
+     * @param valueType value type
+     * @param <V> the type of a value
+     * @return the value fetched from the indicated Java variable
+     * @throws RuntimeException No defined exceptions are thrown, not even
+     *         {@link NullPointerException}
+     */
+    @IntrinsicCandidate
+    public native <V> V getValue(Object o, long offset, Class<?> valueType);
+
+    /**
+     * Stores the given value into a given Java variable.
+     *
+     * Unless the reference {@code o} being stored is either null
+     * or matches the field type, the results are undefined.
+     *
+     * @param o Java heap object in which the variable resides, if any, else
+     *        null
+     * @param offset indication of where the variable resides in a Java heap
+     *        object, if any, else a memory address locating the variable
+     *        statically
+     * @param valueType value type
+     * @param v the value to store into the indicated Java variable
+     * @param <V> the type of a value
+     * @throws RuntimeException No defined exceptions are thrown, not even
+     *         {@link NullPointerException}
+     */
+    @IntrinsicCandidate
+    public native <V> void putValue(Object o, long offset, Class<?> valueType, V v);
+
+    /**
+     * Returns an uninitialized default instance of the given value class.
+     */
+    public native <V> V uninitializedDefaultValue(Class<?> type);
+
+    /**
+     * Returns an object instance with a private buffered value whose layout
+     * and contents is exactly the given value instance.  The return object
+     * is in the larval state that can be updated using the unsafe put operation.
+     *
+     * @param value a value instance
+     * @param <V> the type of the given value instance
+     */
+    @IntrinsicCandidate
+    public native <V> V makePrivateBuffer(V value);
+
+    /**
+     * Exits the larval state and returns a value instance.
+     *
+     * @param value a value instance
+     * @param <V> the type of the given value instance
+     */
+    @IntrinsicCandidate
+    public native <V> V finishPrivateBuffer(V value);
+
+    /**
+     * Returns the header size of the given value type.
+     *
+     * @param valueType value type
+     * @return the header size of the value type
+     */
+    public native <V> long valueHeaderSize(Class<V> valueType);
+
     /** @see #getInt(Object, long) */
     @IntrinsicCandidate
     public native boolean getBoolean(Object o, long offset);
 
     /** @see #putInt(Object, long, int) */
@@ -1232,10 +1358,21 @@
         }
 
         return arrayIndexScale0(arrayClass);
     }
 
+    /**
+     * Return the size of the object in the heap.
+     * @param o an object
+     * @return the objects's size
+     * @since Valhalla
+     */
+    public long getObjectSize(Object o) {
+        if (o == null)
+            throw new NullPointerException();
+        return getObjectSize0(o);
+    }
 
     /** The value of {@code arrayIndexScale(boolean[].class)} */
     public static final int ARRAY_BOOLEAN_INDEX_SCALE
             = theUnsafe.arrayIndexScale(boolean[].class);
 
@@ -1410,57 +1547,241 @@
     @IntrinsicCandidate
     public final native boolean compareAndSetReference(Object o, long offset,
                                                        Object expected,
                                                        Object x);
 
+    private final boolean isValueObject(Object o) {
+        return o != null && o.getClass().isValue();
+    }
+
+    /*
+     * For value type, CAS should do substitutability test as opposed
+     * to two pointers comparison.
+     *
+     * TODO: replace global lock workaround with the proper support for
+     * atomic access to value objects and loosely consistent values.
+     */
+    public final <V> boolean compareAndSetReference(Object o, long offset,
+                                                    Class<?> type,
+                                                    V expected,
+                                                    V x) {
+        if (type.isValue() || isValueObject(expected)) {
+            synchronized (valueLock) {
+                Object witness = getReference(o, offset);
+                if (witness == expected) {
+                    putReference(o, offset, x);
+                    return true;
+                } else {
+                    return false;
+                }
+            }
+        } else {
+            return compareAndSetReference(o, offset, expected, x);
+        }
+    }
+
+    @ForceInline
+    public final <V> boolean compareAndSetValue(Object o, long offset,
+                                                Class<?> valueType,
+                                                V expected,
+                                                V x) {
+        synchronized (valueLock) {
+            Object witness = getValue(o, offset, valueType);
+            if (witness == expected) {
+                putValue(o, offset, valueType, x);
+                return true;
+            }
+            else {
+                return false;
+            }
+        }
+    }
+
     @IntrinsicCandidate
     public final native Object compareAndExchangeReference(Object o, long offset,
                                                            Object expected,
                                                            Object x);
 
+    public final <V> Object compareAndExchangeReference(Object o, long offset,
+                                                        Class<?> valueType,
+                                                        V expected,
+                                                        V x) {
+        if (valueType.isValue() || isValueObject(expected)) {
+            synchronized (valueLock) {
+                Object witness = getReference(o, offset);
+                if (witness == expected) {
+                    putReference(o, offset, x);
+                }
+                return witness;
+            }
+        } else {
+            return compareAndExchangeReference(o, offset, expected, x);
+        }
+    }
+
+    @ForceInline
+    public final <V> Object compareAndExchangeValue(Object o, long offset,
+                                                    Class<?> valueType,
+                                                    V expected,
+                                                    V x) {
+        synchronized (valueLock) {
+            Object witness = getValue(o, offset, valueType);
+            if (witness == expected) {
+                putValue(o, offset, valueType, x);
+            }
+            return witness;
+        }
+    }
+
     @IntrinsicCandidate
     public final Object compareAndExchangeReferenceAcquire(Object o, long offset,
                                                            Object expected,
                                                            Object x) {
         return compareAndExchangeReference(o, offset, expected, x);
     }
 
+    public final <V> Object compareAndExchangeReferenceAcquire(Object o, long offset,
+                                                               Class<?> valueType,
+                                                               V expected,
+                                                               V x) {
+        return compareAndExchangeReference(o, offset, valueType, expected, x);
+    }
+
+    @ForceInline
+    public final <V> Object compareAndExchangeValueAcquire(Object o, long offset,
+                                                           Class<?> valueType,
+                                                           V expected,
+                                                           V x) {
+        return compareAndExchangeValue(o, offset, valueType, expected, x);
+    }
+
     @IntrinsicCandidate
     public final Object compareAndExchangeReferenceRelease(Object o, long offset,
                                                            Object expected,
                                                            Object x) {
         return compareAndExchangeReference(o, offset, expected, x);
     }
 
+    public final <V> Object compareAndExchangeReferenceRelease(Object o, long offset,
+                                                               Class<?> valueType,
+                                                               V expected,
+                                                               V x) {
+        return compareAndExchangeReference(o, offset, valueType, expected, x);
+    }
+
+    @ForceInline
+    public final <V> Object compareAndExchangeValueRelease(Object o, long offset,
+                                                           Class<?> valueType,
+                                                           V expected,
+                                                           V x) {
+        return compareAndExchangeValue(o, offset, valueType, expected, x);
+    }
+
     @IntrinsicCandidate
     public final boolean weakCompareAndSetReferencePlain(Object o, long offset,
                                                          Object expected,
                                                          Object x) {
         return compareAndSetReference(o, offset, expected, x);
     }
 
+    public final <V> boolean weakCompareAndSetReferencePlain(Object o, long offset,
+                                                             Class<?> valueType,
+                                                             V expected,
+                                                             V x) {
+        if (valueType.isValue() || isValueObject(expected)) {
+            return compareAndSetReference(o, offset, valueType, expected, x);
+        } else {
+            return weakCompareAndSetReferencePlain(o, offset, expected, x);
+        }
+    }
+
+    @ForceInline
+    public final <V> boolean weakCompareAndSetValuePlain(Object o, long offset,
+                                                         Class<?> valueType,
+                                                         V expected,
+                                                         V x) {
+        return compareAndSetValue(o, offset, valueType, expected, x);
+    }
+
     @IntrinsicCandidate
     public final boolean weakCompareAndSetReferenceAcquire(Object o, long offset,
                                                            Object expected,
                                                            Object x) {
         return compareAndSetReference(o, offset, expected, x);
     }
 
+    public final <V> boolean weakCompareAndSetReferenceAcquire(Object o, long offset,
+                                                               Class<?> valueType,
+                                                               V expected,
+                                                               V x) {
+        if (valueType.isValue() || isValueObject(expected)) {
+            return compareAndSetReference(o, offset, valueType, expected, x);
+        } else {
+            return weakCompareAndSetReferencePlain(o, offset, expected, x);
+        }
+    }
+
+    @ForceInline
+    public final <V> boolean weakCompareAndSetValueAcquire(Object o, long offset,
+                                                           Class<?> valueType,
+                                                           V expected,
+                                                           V x) {
+        return compareAndSetValue(o, offset, valueType, expected, x);
+    }
+
     @IntrinsicCandidate
     public final boolean weakCompareAndSetReferenceRelease(Object o, long offset,
                                                            Object expected,
                                                            Object x) {
         return compareAndSetReference(o, offset, expected, x);
     }
 
+    public final <V> boolean weakCompareAndSetReferenceRelease(Object o, long offset,
+                                                               Class<?> valueType,
+                                                               V expected,
+                                                               V x) {
+        if (valueType.isValue() || isValueObject(expected)) {
+            return compareAndSetReference(o, offset, valueType, expected, x);
+        } else {
+            return weakCompareAndSetReferencePlain(o, offset, expected, x);
+        }
+    }
+
+    @ForceInline
+    public final <V> boolean weakCompareAndSetValueRelease(Object o, long offset,
+                                                           Class<?> valueType,
+                                                           V expected,
+                                                           V x) {
+        return compareAndSetValue(o, offset, valueType, expected, x);
+    }
+
     @IntrinsicCandidate
     public final boolean weakCompareAndSetReference(Object o, long offset,
                                                     Object expected,
                                                     Object x) {
         return compareAndSetReference(o, offset, expected, x);
     }
 
+    public final <V> boolean weakCompareAndSetReference(Object o, long offset,
+                                                        Class<?> valueType,
+                                                        V expected,
+                                                        V x) {
+        if (valueType.isValue() || isValueObject(expected)) {
+            return compareAndSetReference(o, offset, valueType, expected, x);
+        } else {
+            return weakCompareAndSetReferencePlain(o, offset, expected, x);
+        }
+    }
+
+    @ForceInline
+    public final <V> boolean weakCompareAndSetValue(Object o, long offset,
+                                                    Class<?> valueType,
+                                                    V expected,
+                                                    V x) {
+        return compareAndSetValue(o, offset, valueType, expected, x);
+    }
+
     /**
      * Atomically updates Java variable to {@code x} if it is currently
      * holding {@code expected}.
      *
      * <p>This operation has memory semantics of a {@code volatile} read
@@ -2072,17 +2393,36 @@
      * load semantics. Otherwise identical to {@link #getReference(Object, long)}
      */
     @IntrinsicCandidate
     public native Object getReferenceVolatile(Object o, long offset);
 
+    /**
+     * Global lock for atomic and volatile strength access to any value of
+     * a value type.  This is a temporary workaround until better localized
+     * atomic access mechanisms are supported for value class and primitive class.
+     */
+    private static final Object valueLock = new Object();
+
+    public final <V> Object getValueVolatile(Object base, long offset, Class<?> valueType) {
+        synchronized (valueLock) {
+            return getValue(base, offset, valueType);
+        }
+    }
+
     /**
      * Stores a reference value into a given Java variable, with
      * volatile store semantics. Otherwise identical to {@link #putReference(Object, long, Object)}
      */
     @IntrinsicCandidate
     public native void putReferenceVolatile(Object o, long offset, Object x);
 
+    public final <V> void putValueVolatile(Object o, long offset, Class<?> valueType, V x) {
+        synchronized (valueLock) {
+            putValue(o, offset, valueType, x);
+        }
+    }
+
     /** Volatile version of {@link #getInt(Object, long)}  */
     @IntrinsicCandidate
     public native int     getIntVolatile(Object o, long offset);
 
     /** Volatile version of {@link #putInt(Object, long, int)}  */
@@ -2151,10 +2491,14 @@
     @IntrinsicCandidate
     public final Object getReferenceAcquire(Object o, long offset) {
         return getReferenceVolatile(o, offset);
     }
 
+    public final <V> Object getValueAcquire(Object base, long offset, Class<?> valueType) {
+        return getValueVolatile(base, offset, valueType);
+    }
+
     /** Acquire version of {@link #getBooleanVolatile(Object, long)} */
     @IntrinsicCandidate
     public final boolean getBooleanAcquire(Object o, long offset) {
         return getBooleanVolatile(o, offset);
     }
@@ -2215,10 +2559,14 @@
     @IntrinsicCandidate
     public final void putReferenceRelease(Object o, long offset, Object x) {
         putReferenceVolatile(o, offset, x);
     }
 
+    public final <V> void putValueRelease(Object o, long offset, Class<?> valueType, V x) {
+        putValueVolatile(o, offset, valueType, x);
+    }
+
     /** Release version of {@link #putBooleanVolatile(Object, long, boolean)} */
     @IntrinsicCandidate
     public final void putBooleanRelease(Object o, long offset, boolean x) {
         putBooleanVolatile(o, offset, x);
     }
@@ -2271,10 +2619,14 @@
     @IntrinsicCandidate
     public final Object getReferenceOpaque(Object o, long offset) {
         return getReferenceVolatile(o, offset);
     }
 
+    public final <V> Object getValueOpaque(Object base, long offset, Class<?> valueType) {
+        return getValueVolatile(base, offset, valueType);
+    }
+
     /** Opaque version of {@link #getBooleanVolatile(Object, long)} */
     @IntrinsicCandidate
     public final boolean getBooleanOpaque(Object o, long offset) {
         return getBooleanVolatile(o, offset);
     }
@@ -2325,10 +2677,14 @@
     @IntrinsicCandidate
     public final void putReferenceOpaque(Object o, long offset, Object x) {
         putReferenceVolatile(o, offset, x);
     }
 
+    public final <V> void putValueOpaque(Object o, long offset, Class<?> valueType, V x) {
+        putValueVolatile(o, offset, valueType, x);
+    }
+
     /** Opaque version of {@link #putBooleanVolatile(Object, long, boolean)} */
     @IntrinsicCandidate
     public final void putBooleanOpaque(Object o, long offset, boolean x) {
         putBooleanVolatile(o, offset, x);
     }
@@ -2759,28 +3115,47 @@
             v = getReferenceVolatile(o, offset);
         } while (!weakCompareAndSetReference(o, offset, v, newValue));
         return v;
     }
 
+    @SuppressWarnings("unchecked")
+    public final <V> Object getAndSetValue(Object o, long offset, Class<?> valueType, V newValue) {
+        synchronized (valueLock) {
+            Object oldValue = getValue(o, offset, valueType);
+            putValue(o, offset, valueType, newValue);
+            return oldValue;
+        }
+    }
+
     @ForceInline
     public final Object getAndSetReferenceRelease(Object o, long offset, Object newValue) {
         Object v;
         do {
             v = getReference(o, offset);
         } while (!weakCompareAndSetReferenceRelease(o, offset, v, newValue));
         return v;
     }
 
+    @ForceInline
+    public final <V> Object getAndSetValueRelease(Object o, long offset, Class<?> valueType, V newValue) {
+        return getAndSetValue(o, offset, valueType, newValue);
+    }
+
     @ForceInline
     public final Object getAndSetReferenceAcquire(Object o, long offset, Object newValue) {
         Object v;
         do {
             v = getReferenceAcquire(o, offset);
         } while (!weakCompareAndSetReferenceAcquire(o, offset, v, newValue));
         return v;
     }
 
+    @ForceInline
+    public final <V> Object getAndSetValueAcquire(Object o, long offset, Class<?> valueType, V newValue) {
+        return getAndSetValue(o, offset, valueType, newValue);
+    }
+
     @IntrinsicCandidate
     public final byte getAndSetByte(Object o, long offset, byte newValue) {
         byte v;
         do {
             v = getByteVolatile(o, offset);
@@ -3833,10 +4208,11 @@
     private native Object staticFieldBase0(Field f);
     private native boolean shouldBeInitialized0(Class<?> c);
     private native void ensureClassInitialized0(Class<?> c);
     private native int arrayBaseOffset0(Class<?> arrayClass);
     private native int arrayIndexScale0(Class<?> arrayClass);
+    private native long getObjectSize0(Object o);
     private native int getLoadAverage0(double[] loadavg, int nelems);
 
 
     /**
      * Invokes the given direct byte buffer's cleaner, if any.
diff a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Constants.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Constants.java
--- a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Constants.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Constants.java
@@ -248,7 +248,6 @@
         if (minorVersion != 0xFFFF) {
             throw new IllegalStateException(
                     "ASM9_EXPERIMENTAL can only be used by classes compiled with --enable-preview");
         }
     }
-}
-
+}
diff a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Opcodes.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Opcodes.java
--- a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Opcodes.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Opcodes.java
@@ -332,10 +332,11 @@
     int ACC_PRIVATE = 0x0002; // class, field, method
     int ACC_PROTECTED = 0x0004; // class, field, method
     int ACC_STATIC = 0x0008; // field, method
     int ACC_FINAL = 0x0010; // class, field, method, parameter
     int ACC_SUPER = 0x0020; // class
+    int ACC_IDENTITY = 0x0020; // class
     int ACC_SYNCHRONIZED = 0x0020; // method
     int ACC_OPEN = 0x0020; // module
     int ACC_TRANSITIVE = 0x0020; // module requires
     int ACC_VOLATILE = 0x0040; // field
     int ACC_BRIDGE = 0x0040; // method
diff a/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java b/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java
--- a/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java
+++ b/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java
@@ -267,10 +267,13 @@
 
     public final Constructor<?> newConstructorForExternalization(Class<?> cl) {
         if (!Externalizable.class.isAssignableFrom(cl)) {
             return null;
         }
+        if (cl.isValue()) {
+            throw new UnsupportedOperationException("newConstructorForExternalization does not support value classes");
+        }
         try {
             Constructor<?> cons = cl.getConstructor();
             cons.setAccessible(true);
             return cons;
         } catch (NoSuchMethodException ex) {
@@ -283,10 +286,13 @@
     {
         if (constructorToCall.getDeclaringClass() == cl) {
             constructorToCall.setAccessible(true);
             return constructorToCall;
         }
+        if (cl.isValue()) {
+            throw new UnsupportedOperationException("newConstructorForSerialization does not support value classes");
+        }
         return generateConstructor(cl, constructorToCall);
     }
 
     /**
      * Given a class, determines whether its superclass has
@@ -342,10 +348,14 @@
      *
      * @param cl the class for which a constructor is to be found
      * @return the generated constructor, or null if none is available
      */
     public final Constructor<?> newConstructorForSerialization(Class<?> cl) {
+        if (cl.isValue()) {
+            throw new UnsupportedOperationException("newConstructorForSerialization does not support value classes");
+        }
+
         Class<?> initCl = cl;
         while (Serializable.class.isAssignableFrom(initCl)) {
             Class<?> prev = initCl;
             if ((initCl = initCl.getSuperclass()) == null ||
                 (!disableSerialConstructorChecks() && !superHasAccessibleConstructor(prev))) {
diff a/src/java.base/share/classes/jdk/internal/value/CheckedType.java b/src/java.base/share/classes/jdk/internal/value/CheckedType.java
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/value/CheckedType.java
@@ -0,0 +1,32 @@
+/*
+ * 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.  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.
+ */
+
+package jdk.internal.value;
+
+public sealed interface CheckedType permits NormalCheckedType, NullRestrictedCheckedType {
+    Object cast(Object obj);
+    boolean canCast(Object obj);
+    Class<?> boundingClass();
+}
diff a/src/java.base/share/classes/jdk/internal/value/NormalCheckedType.java b/src/java.base/share/classes/jdk/internal/value/NormalCheckedType.java
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/value/NormalCheckedType.java
@@ -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.  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.
+ */
+
+package jdk.internal.value;
+
+public final class NormalCheckedType implements CheckedType {
+    private final Class<?> type;
+    private NormalCheckedType(Class<?> cls) {
+        this.type = cls;
+    }
+
+    @Override
+    public Object cast(Object obj) {
+        return type.cast(obj);
+    }
+
+    @Override
+    public boolean canCast(Object obj) {
+        return type.isAssignableFrom(obj.getClass());
+    }
+
+    @Override
+    public Class<?> boundingClass() {
+        return type;
+    }
+
+    /**
+     * {@returns a {@linkplain CheckedType checked type} for the given class if it is a checked type}
+     *
+     * A primitive type and {@code void} is not a checked type.
+     *
+     * @param cls {@code Class} object
+     * @throws IllegalArgumentException if the given class is a primitive type
+     */
+    public static CheckedType of(Class<?> cls) {
+        if (cls.isPrimitive())
+            throw new IllegalArgumentException(cls.getName() + " not a checked type");
+        return new NormalCheckedType(cls);
+    }
+}
diff a/src/java.base/share/classes/jdk/internal/value/NullRestrictedCheckedType.java b/src/java.base/share/classes/jdk/internal/value/NullRestrictedCheckedType.java
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/value/NullRestrictedCheckedType.java
@@ -0,0 +1,71 @@
+/*
+ * 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.  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.
+ */
+
+package jdk.internal.value;
+
+public final class NullRestrictedCheckedType implements CheckedType {
+    private final Class<?> type;
+    private NullRestrictedCheckedType(Class<?> cls) {
+        this.type = cls;
+    }
+
+    @Override
+    public Object cast(Object obj) {
+        if (obj == null) {
+            throw new NullPointerException("null not allowed for null-restricted type " + type.getName());
+        }
+        return type.cast(obj);
+    }
+
+    @Override
+    public boolean canCast(Object obj) {
+        if (obj == null) return false;
+        return type.isAssignableFrom(obj.getClass());
+    }
+
+    @Override
+    public Class<?> boundingClass() {
+        return type;
+    }
+
+    /**
+     * {@returns a {@linkplain NullRestrictedCheckedType null-restricted checked type}
+     * for the given class if it is a checked type}
+     *
+     * A primitive type and {@code void} is not a checked type.
+     *
+     * @param cls {@code Class} object
+     * @throws IllegalArgumentException if the given class is a primitive type
+     * @throws IllegalArgumentException if the given class is not a value class
+     */
+    public static NullRestrictedCheckedType of(Class<?> cls) {
+        if (cls.isPrimitive())
+            throw new IllegalArgumentException(cls.getName() + " not a checked type");
+        if (!cls.isValue()) {
+            throw new IllegalArgumentException(cls.getName() + " not a value class");
+        }
+        return new NullRestrictedCheckedType(cls);
+    }
+}
diff a/src/java.base/share/classes/jdk/internal/value/ValueClass.java b/src/java.base/share/classes/jdk/internal/value/ValueClass.java
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/value/ValueClass.java
@@ -0,0 +1,126 @@
+/*
+ * 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.  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.
+ */
+
+package jdk.internal.value;
+
+import jdk.internal.access.JavaLangReflectAccess;
+import jdk.internal.access.SharedSecrets;
+import jdk.internal.misc.Unsafe;
+import jdk.internal.vm.annotation.IntrinsicCandidate;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+
+/**
+ * Utilities to access
+ */
+public class ValueClass {
+    private static final Unsafe UNSAFE = Unsafe.getUnsafe();
+    private static final JavaLangReflectAccess JLRA = SharedSecrets.getJavaLangReflectAccess();
+
+    /**
+     * {@return true if the given {@code Class} object is implicitly constructible}
+     */
+    public static native boolean isImplicitlyConstructible(Class<?> cls);
+
+    /**
+     * {@return the default value of the given value class type}
+     *
+     * @throws IllegalArgumentException if {@code cls} is not a
+     *         value class type or is not annotated with
+     *         {@link jdk.internal.vm.annotation.ImplicitlyConstructible}
+     */
+    public static <T> T zeroInstance(Class<T> cls) {
+        if (!cls.isValue()) {
+            throw new IllegalArgumentException(cls.getName() + " not a value class");
+        }
+        if (!isImplicitlyConstructible(cls)) {
+            throw new IllegalArgumentException(cls.getName() + " not implicitly constructible");
+        }
+        UNSAFE.ensureClassInitialized(cls);
+        return UNSAFE.uninitializedDefaultValue(cls);
+    }
+
+    /**
+     * {@return {@code CheckedType} representing the type of the given field}
+     */
+    public static CheckedType checkedType(Field f) {
+        return JLRA.isNullRestrictedField(f) ? NullRestrictedCheckedType.of(f.getType())
+                                             : NormalCheckedType.of(f.getType());
+    }
+
+    /**
+     * {@return {@code CheckedType} representing the component type of the given array}
+     */
+    public static CheckedType componentCheckedType(Object array) {
+        Class<?> componentType = array.getClass().getComponentType();
+        return isNullRestrictedArray(array) ? NullRestrictedCheckedType.of(componentType)
+                                            : NormalCheckedType.of(componentType);
+    }
+
+    /**
+     * Allocate an array of a value class type with components that behave in
+     * the same way as a {@link jdk.internal.vm.annotation.NullRestricted}
+     * field.
+     * <p>
+     * Because these behaviors are not specified by Java SE, arrays created with
+     * this method should only be used by internal JDK code for experimental
+     * purposes and should not affect user-observable outcomes.
+     *
+     * @throws IllegalArgumentException if {@code componentType} is not a
+     *         value class type or is not annotated with
+     *         {@link jdk.internal.vm.annotation.ImplicitlyConstructible}
+     */
+    @SuppressWarnings("unchecked")
+    public static Object[] newArrayInstance(CheckedType componentType, int length) {
+        if (componentType instanceof NullRestrictedCheckedType) {
+            return newNullRestrictedArray(componentType.boundingClass(), length);
+        } else {
+            return (Object[]) Array.newInstance(componentType.boundingClass(), length);
+        }
+    }
+
+    /**
+     * Allocate an array of a value class type with components that behave in
+     * the same way as a {@link jdk.internal.vm.annotation.NullRestricted}
+     * field.
+     * <p>
+     * Because these behaviors are not specified by Java SE, arrays created with
+     * this method should only be used by internal JDK code for experimental
+     * purposes and should not affect user-observable outcomes.
+     *
+     * @throws IllegalArgumentException if {@code componentType} is not a
+     *         value class type or is not annotated with
+     *         {@link jdk.internal.vm.annotation.ImplicitlyConstructible}
+     */
+    @IntrinsicCandidate
+    public static native Object[] newNullRestrictedArray(Class<?> componentType,
+                                                         int length);
+
+    /**
+     * {@return true if the given array is a null-restricted array}
+     */
+    public static native boolean isNullRestrictedArray(Object array);
+}
diff a/src/java.base/share/classes/jdk/internal/vm/annotation/ImplicitlyConstructible.java b/src/java.base/share/classes/jdk/internal/vm/annotation/ImplicitlyConstructible.java
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/vm/annotation/ImplicitlyConstructible.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+package jdk.internal.vm.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * An implicitly-constructible value class is a class that authorizes the JVM
+ * to create its <em>zero instance</em>, where all instance fields are set to
+ * their default values (0, {@code null}, etc.), without any code execution.
+ * Any superclasses other than {@code Object} are also expected to be marked
+ * {@code @ImplicitlyConstructible}.
+ * <p>
+ * The HotSpot VM depends on this annotation being present to properly
+ * initialize {@link NullRestricted} fields and arrays of a value class type.
+ * <p>
+ * Because these behaviors are not specified by Java SE, this annotation should
+ * only be used by internal JDK code for experimental purposes and should not
+ * affect user-observable outcomes.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ImplicitlyConstructible {
+}
diff a/src/java.base/share/classes/jdk/internal/vm/annotation/LooselyConsistentValue.java b/src/java.base/share/classes/jdk/internal/vm/annotation/LooselyConsistentValue.java
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/vm/annotation/LooselyConsistentValue.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package jdk.internal.vm.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * A loosely-consistent value class is a class that is willing to tolerate
+ * data corruption when fields or arrays storing instances of the class are
+ * updated under race. Specifically, a value object read from such a field may
+ * contain combinations of field values that were never set by a previous
+ * constructor invocation.
+ * <p>
+ * Users of a class with this annotation take responsibility for ensuring the
+ * integrity of their data by avoiding race conditions.
+ * <p>
+ * The HotSpot VM uses this annotation to enable non-atomic strategies for
+ * reading and writing to flattened fields and arrays of the annotated class's
+ * type.
+ * <p>
+ * Because these behaviors are not specified by Java SE, this annotation should
+ * only be used by internal JDK code for experimental purposes and should not
+ * affect user-observable outcomes.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface LooselyConsistentValue {
+}
diff a/src/java.base/share/classes/jdk/internal/vm/annotation/NullRestricted.java b/src/java.base/share/classes/jdk/internal/vm/annotation/NullRestricted.java
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/vm/annotation/NullRestricted.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+package jdk.internal.vm.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * A null-restricted field is a field that does not store {@code null}.
+ * The type of the field is expected to be a value class type with the
+ * {@link ImplicitlyConstructible} annotation. The initial value of the field
+ * is the zero instance of the given class, and attempts to write {@code null}
+ * to the field will throw an exception.
+ * <p>
+ * The HotSpot VM uses this annotation to enable flattened encodings for the
+ * field that would otherwise be impossible.
+ * <p>
+ * Because these behaviors are not specified by Java SE, this annotation should
+ * only be used by internal JDK classes for experimental purposes and should not
+ * affect user-observable outcomes.
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NullRestricted {
+}
diff a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java
--- a/src/java.base/share/classes/module-info.java
+++ b/src/java.base/share/classes/module-info.java
@@ -134,11 +134,10 @@
     exports javax.security.auth.login;
     exports javax.security.auth.spi;
     exports javax.security.auth.x500;
     exports javax.security.cert;
 
-
     // additional qualified exports may be inserted at build time
     // see make/gensrc/GenModuleInfo.gmk
 
     exports com.sun.crypto.provider to
         jdk.crypto.cryptoki;
@@ -268,10 +267,12 @@
         jdk.jfr;
     exports jdk.internal.util.xml.impl to
         jdk.jfr;
     exports jdk.internal.util.random to
         jdk.random;
+    exports jdk.internal.value to  // Needed by Unsafe
+        jdk.unsupported;
     exports jdk.internal.util to
         java.desktop,
         java.prefs,
         java.security.jgss,
         java.smartcardio,
diff a/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java b/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java
--- a/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java
+++ b/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java
@@ -199,13 +199,14 @@
         if (isPublic(mods)) {
 
             Module lookupModule = lookupClass.getModule();
             Module refModule = refc.getModule();
 
-            // early VM startup case, java.base not defined
-            if (lookupModule == null) {
-                assert refModule == null;
+            // early VM startup case, java.base not defined or
+            // module system is not fully initialized and exports are not set up
+            if (lookupModule == null || !jdk.internal.misc.VM.isModuleSystemInited()) {
+                assert lookupModule == refModule;
                 return true;
             }
 
             // allow access to public types in all unconditionally exported packages
             if ((allowedModes & UNCONDITIONAL_ALLOWED) != 0) {
@@ -225,16 +226,12 @@
             // 1. refc is in different module from lookupModule, or
             // 2. refc is in lookupModule and a different module from prevLookupModule
             Module prevLookupModule = prevLookupClass != null ? prevLookupClass.getModule()
                                                               : null;
             assert refModule != lookupModule || refModule != prevLookupModule;
-            if (isModuleAccessible(refc, lookupModule, prevLookupModule))
-                return true;
 
-            // not exported but allow access during VM initialization
-            // because java.base does not have its exports setup
-            if (!jdk.internal.misc.VM.isModuleSystemInited())
+            if (isModuleAccessible(refc, lookupModule, prevLookupModule))
                 return true;
 
             // public class not accessible to lookupClass
             return false;
         }
diff a/src/java.base/share/native/include/classfile_constants.h.template b/src/java.base/share/native/include/classfile_constants.h.template
--- a/src/java.base/share/native/include/classfile_constants.h.template
+++ b/src/java.base/share/native/include/classfile_constants.h.template
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2022, 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
@@ -42,17 +42,19 @@
     JVM_ACC_PROTECTED     = 0x0004,
     JVM_ACC_STATIC        = 0x0008,
     JVM_ACC_FINAL         = 0x0010,
     JVM_ACC_SYNCHRONIZED  = 0x0020,
     JVM_ACC_SUPER         = 0x0020,
+    JVM_ACC_IDENTITY      = 0x0020,
     JVM_ACC_VOLATILE      = 0x0040,
     JVM_ACC_BRIDGE        = 0x0040,
     JVM_ACC_TRANSIENT     = 0x0080,
     JVM_ACC_VARARGS       = 0x0080,
     JVM_ACC_NATIVE        = 0x0100,
     JVM_ACC_INTERFACE     = 0x0200,
     JVM_ACC_ABSTRACT      = 0x0400,
+    JVM_ACC_PRIMITIVE     = 0x0800,
     JVM_ACC_STRICT        = 0x0800,
     JVM_ACC_SYNTHETIC     = 0x1000,
     JVM_ACC_ANNOTATION    = 0x2000,
     JVM_ACC_ENUM          = 0x4000,
     JVM_ACC_MODULE        = 0x8000
@@ -109,11 +111,11 @@
     JVM_CONSTANT_MethodType             = 16,  // JSR 292
     JVM_CONSTANT_Dynamic                = 17,
     JVM_CONSTANT_InvokeDynamic          = 18,
     JVM_CONSTANT_Module                 = 19,
     JVM_CONSTANT_Package                = 20,
-    JVM_CONSTANT_ExternalMax            = 20 
+    JVM_CONSTANT_ExternalMax            = 20
 };
 
 /* JVM_CONSTANT_MethodHandle subtypes */
 enum {
     JVM_REF_getField                = 1,
@@ -150,10 +152,11 @@
     JVM_SIGNATURE_ENDSPECIAL    = '>',
     JVM_SIGNATURE_ARRAY         = '[',
     JVM_SIGNATURE_BYTE          = 'B',
     JVM_SIGNATURE_CHAR          = 'C',
     JVM_SIGNATURE_CLASS         = 'L',
+    JVM_SIGNATURE_PRIMITIVE_OBJECT = 'Q',
     JVM_SIGNATURE_ENDCLASS      = ';',
     JVM_SIGNATURE_ENUM          = 'E',
     JVM_SIGNATURE_FLOAT         = 'F',
     JVM_SIGNATURE_DOUBLE        = 'D',
     JVM_SIGNATURE_FUNC          = '(',
diff a/src/java.base/share/native/libjava/Array.c b/src/java.base/share/native/libjava/Array.c
--- a/src/java.base/share/native/libjava/Array.c
+++ b/src/java.base/share/native/libjava/Array.c
@@ -44,12 +44,12 @@
 
 /*
  *
  */
 JNIEXPORT jobject JNICALL
-Java_java_lang_reflect_Array_get(JNIEnv *env, jclass ignore, jobject arr,
-                                 jint index)
+Java_java_lang_reflect_Array_getReferenceOrPrimitive(JNIEnv *env, jclass ignore, jobject arr,
+                                                     jint index)
 {
     return JVM_GetArrayElement(env, arr, index);
 }
 
 JNIEXPORT jboolean JNICALL
@@ -110,12 +110,12 @@
 
 /*
  *
  */
 JNIEXPORT void JNICALL
-Java_java_lang_reflect_Array_set(JNIEnv *env, jclass ignore, jobject arr,
-                                 jint index, jobject val)
+Java_java_lang_reflect_Array_setReferenceOrPrimitive(JNIEnv *env, jclass ignore, jobject arr,
+                                                     jint index, jobject val)
 {
     JVM_SetArrayElement(env, arr, index, val);
 }
 
 JNIEXPORT void JNICALL
diff a/src/java.base/share/native/libjava/Class.c b/src/java.base/share/native/libjava/Class.c
--- a/src/java.base/share/native/libjava/Class.c
+++ b/src/java.base/share/native/libjava/Class.c
@@ -60,10 +60,11 @@
     {"isInterface",      "()Z",             (void *)&JVM_IsInterface},
     {"getSigners",       "()[" OBJ,         (void *)&JVM_GetClassSigners},
     {"setSigners",       "([" OBJ ")V",     (void *)&JVM_SetClassSigners},
     {"isArray",          "()Z",             (void *)&JVM_IsArrayClass},
     {"isHidden",         "()Z",             (void *)&JVM_IsHiddenClass},
+    {"isIdentity",       "()Z",             (void *)&JVM_IsIdentityClass},
     {"isPrimitive",      "()Z",             (void *)&JVM_IsPrimitiveClass},
     {"getModifiers",     "()I",             (void *)&JVM_GetClassModifiers},
     {"getDeclaredFields0","(Z)[" FLD,       (void *)&JVM_GetClassDeclaredFields},
     {"getDeclaredMethods0","(Z)[" MHD,      (void *)&JVM_GetClassDeclaredMethods},
     {"getDeclaredConstructors0","(Z)[" CTR, (void *)&JVM_GetClassDeclaredConstructors},
diff a/src/java.base/share/native/libjava/VM.c b/src/java.base/share/native/libjava/VM.c
--- a/src/java.base/share/native/libjava/VM.c
+++ b/src/java.base/share/native/libjava/VM.c
@@ -52,6 +52,6 @@
 }
 
 JNIEXPORT jobjectArray JNICALL
 Java_jdk_internal_misc_VM_getRuntimeArguments(JNIEnv *env, jclass cls) {
     return JVM_GetVmArguments(env);
-}
+}
diff a/src/java.base/share/native/libjava/ValueClass.c b/src/java.base/share/native/libjava/ValueClass.c
--- /dev/null
+++ b/src/java.base/share/native/libjava/ValueClass.c
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#include "jni.h"
+#include "jvm.h"
+
+#include "jdk_internal_value_ValueClass.h"
+
+JNIEXPORT jboolean JNICALL
+Java_jdk_internal_value_ValueClass_isImplicitlyConstructible(JNIEnv *env, jclass dummy, jclass cls) {
+    return JVM_IsImplicitlyConstructibleClass(env, cls);
+}
+
+JNIEXPORT jarray JNICALL
+Java_jdk_internal_value_ValueClass_newNullRestrictedArray(JNIEnv *env, jclass cls, jclass elmClass, jint len)
+{
+    return JVM_NewNullRestrictedArray(env, elmClass, len);
+}
+
+JNIEXPORT jboolean JNICALL
+Java_jdk_internal_value_ValueClass_isNullRestrictedArray(JNIEnv *env, jclass cls, jobject obj)
+{
+    return JVM_IsNullRestrictedArray(env, obj);
+}
+
diff a/src/java.compiler/share/classes/javax/lang/model/element/Modifier.java b/src/java.compiler/share/classes/javax/lang/model/element/Modifier.java
--- a/src/java.compiler/share/classes/javax/lang/model/element/Modifier.java
+++ b/src/java.compiler/share/classes/javax/lang/model/element/Modifier.java
@@ -75,10 +75,17 @@
     NON_SEALED {
         public String toString() {
             return "non-sealed";
         }
     },
+
+    /**
+     * The modifier {@code value}
+     * @since 18
+     */
+    VALUE,
+
     /** The modifier {@code final} */           FINAL,
     /** The modifier {@code transient} */       TRANSIENT,
     /** The modifier {@code volatile} */        VOLATILE,
     /** The modifier {@code synchronized} */    SYNCHRONIZED,
     /** The modifier {@code native} */          NATIVE,
diff a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java
--- a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java
@@ -235,13 +235,11 @@
     public R visitWhileLoop(WhileLoopTree node, P p) {
         return defaultAction(node, p);
     }
 
     /**
-     * {@inheritDoc}
-     *
-     * @implSpec This implementation calls {@code defaultAction}.
+     * {@inheritDoc} This implementation calls {@code defaultAction}.
      *
      * @param node {@inheritDoc}
      * @param p {@inheritDoc}
      * @return  the result of {@code defaultAction}
      */
diff a/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java b/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java
--- a/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java
@@ -303,13 +303,11 @@
         r = scanAndReduce(node.getStatement(), p, r);
         return r;
     }
 
     /**
-     * {@inheritDoc}
-     *
-     * @implSpec This implementation scans the children in left to right order.
+     * {@inheritDoc} This implementation scans the children in left to right order.
      *
      * @param node  {@inheritDoc}
      * @param p  {@inheritDoc}
      * @return the result of scanning
      */
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java
@@ -103,13 +103,14 @@
 
     // Because the following access flags are overloaded with other
     // bit positions, we translate them when reading and writing class
     // files into unique bits positions: ACC_SYNTHETIC <-> SYNTHETIC,
     // for example.
-    public static final int ACC_SUPER    = 0x0020;
+    public static final int ACC_IDENTITY = 0x0020;
     public static final int ACC_BRIDGE   = 0x0040;
     public static final int ACC_VARARGS  = 0x0080;
+    public static final int ACC_STRICT   = 0x0800;
     public static final int ACC_MODULE   = 0x8000;
 
     /*****************************************
      * Internal compiler flags (no bits in the lower 16).
      *****************************************/
@@ -121,19 +122,29 @@
     /** Flag is set for a variable symbol if the variable's definition
      *  has an initializer part.
      */
     public static final int HASINIT          = 1<<18;
 
+    /** Flag is set for a class or interface whose instances have identity
+     * i.e. any concrete class not declared with the modifier `value'
+     * (a) abstract class not declared `value'
+     * (b) older class files with ACC_SUPER bit set
+     */
+    public static final int IDENTITY_TYPE            = 1<<19;
+
     /** Class is an implicitly declared top level class.
      */
-    public static final int IMPLICIT_CLASS    = 1<<19;
+    public static final int IMPLICIT_CLASS    = 1<<23;
 
     /** Flag is set for compiler-generated anonymous method symbols
      *  that `own' an initializer block.
      */
     public static final int BLOCK            = 1<<20;
 
+    /** Marks a type as a value class */
+    public static final int VALUE_CLASS      = 1<<20;
+
     /** Flag is set for ClassSymbols that are being compiled from source.
      */
     public static final int FROM_SOURCE      = 1<<21; //ClassSymbols
 
     /** Flag is set for nested classes that do not access instance members
@@ -397,10 +408,15 @@
     /**
      * Flag to indicate that the class/interface was declared with the non-sealed modifier.
      */
     public static final long NON_SEALED = 1L<<63; // ClassSymbols
 
+    /**
+     * Flag to indicate that a field is strict
+     */
+    public static final long STRICT = 1L<<53; // VarSymbols
+
     /**
      * Describe modifier flags as they might appear in source code, i.e.,
      * separated by spaces and in the order suggested by JLS 8.1.1.
      */
     public static String toSource(long flags) {
@@ -411,12 +427,12 @@
 
     /** Modifier masks.
      */
     public static final int
         AccessFlags                       = PUBLIC | PROTECTED | PRIVATE,
-        LocalClassFlags                   = FINAL | ABSTRACT | STRICTFP | ENUM | SYNTHETIC,
-        StaticLocalFlags                  = LocalClassFlags | STATIC | INTERFACE,
+        LocalClassFlags                   = FINAL | ABSTRACT | STRICTFP | ENUM | SYNTHETIC | IDENTITY_TYPE,
+        StaticLocalClassFlags             = LocalClassFlags | STATIC | INTERFACE,
         MemberClassFlags                  = LocalClassFlags | INTERFACE | AccessFlags,
         MemberStaticClassFlags            = MemberClassFlags | STATIC,
         ClassFlags                        = LocalClassFlags | INTERFACE | PUBLIC | ANNOTATION,
         InterfaceVarFlags                 = FINAL | STATIC | PUBLIC,
         VarFlags                          = AccessFlags | FINAL | STATIC |
@@ -426,15 +442,18 @@
         MethodFlags                       = AccessFlags | ABSTRACT | STATIC | NATIVE |
                                             SYNCHRONIZED | FINAL | STRICTFP,
         RecordMethodFlags                 = AccessFlags | ABSTRACT | STATIC |
                                             SYNCHRONIZED | FINAL | STRICTFP;
     public static final long
-        ExtendedStandardFlags             = (long)StandardFlags | DEFAULT | SEALED | NON_SEALED,
-        ExtendedMemberClassFlags          = (long)MemberClassFlags | SEALED | NON_SEALED,
-        ExtendedMemberStaticClassFlags    = (long) MemberStaticClassFlags | SEALED | NON_SEALED,
-        ExtendedClassFlags                = (long)ClassFlags | SEALED | NON_SEALED,
-        ModifierFlags                     = ((long)StandardFlags & ~INTERFACE) | DEFAULT | SEALED | NON_SEALED,
+        ExtendedStandardFlags             = (long)StandardFlags | DEFAULT | SEALED | NON_SEALED | VALUE_CLASS,
+        ExtendedMemberClassFlags          = (long)MemberClassFlags | SEALED | NON_SEALED | VALUE_CLASS,
+        ExtendedMemberStaticClassFlags    = (long) MemberStaticClassFlags | SEALED | NON_SEALED | VALUE_CLASS,
+        ExtendedClassFlags                = (long)ClassFlags | SEALED | NON_SEALED | VALUE_CLASS,
+        ExtendedLocalClassFlags           = (long) LocalClassFlags | VALUE_CLASS,
+        ExtendedStaticLocalClassFlags     = (long) StaticLocalClassFlags | VALUE_CLASS,
+        ExtendedVarFlags                  = (long) VarFlags | STRICT,
+        ModifierFlags                     = ((long)StandardFlags & ~INTERFACE) | DEFAULT | SEALED | NON_SEALED | VALUE_CLASS,
         InterfaceMethodMask               = ABSTRACT | PRIVATE | STATIC | PUBLIC | STRICTFP | DEFAULT,
         AnnotationTypeElementMask         = ABSTRACT | PUBLIC,
         LocalVarFlags                     = FINAL | PARAMETER,
         ReceiverParamFlags                = PARAMETER;
 
@@ -456,10 +475,11 @@
             if (0 != (flags & SYNCHRONIZED))
                                           modifiers.add(Modifier.SYNCHRONIZED);
             if (0 != (flags & NATIVE))    modifiers.add(Modifier.NATIVE);
             if (0 != (flags & STRICTFP))  modifiers.add(Modifier.STRICTFP);
             if (0 != (flags & DEFAULT))   modifiers.add(Modifier.DEFAULT);
+            if (0 != (flags & VALUE_CLASS))     modifiers.add(Modifier.VALUE);
             modifiers = Collections.unmodifiableSet(modifiers);
             modifierSets.put(flags, modifiers);
         }
         return modifiers;
     }
@@ -477,11 +497,10 @@
 
     public static boolean isConstant(Symbol.VarSymbol symbol) {
         return symbol.getConstValue() != null;
     }
 
-
     public enum Flag {
         PUBLIC(Flags.PUBLIC),
         PRIVATE(Flags.PRIVATE),
         PROTECTED(Flags.PROTECTED),
         STATIC(Flags.STATIC),
@@ -497,10 +516,17 @@
         BRIDGE(Flags.BRIDGE),
         SYNTHETIC(Flags.SYNTHETIC),
         ANNOTATION(Flags.ANNOTATION),
         DEPRECATED(Flags.DEPRECATED),
         HASINIT(Flags.HASINIT),
+        IDENTITY_TYPE(Flags.IDENTITY_TYPE) {
+            @Override
+            public String toString() {
+                return "identity";
+            }
+        },
+        VALUE(Flags.VALUE_CLASS),
         IMPLICIT_CLASS(Flags.IMPLICIT_CLASS),
         BLOCK(Flags.BLOCK),
         FROM_SOURCE(Flags.FROM_SOURCE),
         ENUM(Flags.ENUM),
         MANDATED(Flags.MANDATED),
@@ -548,11 +574,12 @@
         NON_SEALED(Flags.NON_SEALED) {
             @Override
             public String toString() {
                 return "non-sealed";
             }
-        };
+        },
+        STRICT(Flags.STRICT);
 
         Flag(long flag) {
             this.value = flag;
             this.lowercaseName = StringUtils.toLowerCase(name());
         }
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java
@@ -234,10 +234,15 @@
         /**
          * Warn about module system related issues.
          */
         MODULE("module"),
 
+        /**
+         * Warn about issues related to migration of JDK classes.
+         */
+        MIGRATION("migration"),
+
         /**
          * Warn about issues regarding module opens.
          */
         OPENS("opens"),
 
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java
@@ -208,10 +208,11 @@
     public boolean isPreview(Feature feature) {
         return switch (feature) {
             case STRING_TEMPLATES -> true;
             case IMPLICIT_CLASSES -> true;
             case SUPER_INIT -> true;
+            case VALUE_CLASSES -> true;
             //Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing).
             //When real preview features will be added, this method can be implemented to return 'true'
             //for those selected features, and 'false' for all the others.
             default -> forcePreview;
         };
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java
@@ -191,10 +191,23 @@
 
     public boolean isSupported() {
         return this.compareTo(MIN) >= 0;
     }
 
+    public static boolean isSupported(Feature feature, int majorVersion) {
+        Source source = null;
+        for (Target target : Target.values()) {
+            if (majorVersion == target.majorVersion) {
+                source = lookup(target.name);
+            }
+        }
+        if (source != null) {
+            return feature.allowedInSource(source);
+        }
+        return false;
+    }
+
     public Target requiredTarget() {
         return switch(this) {
         case JDK23  -> Target.JDK1_23;
         case JDK22  -> Target.JDK1_22;
         case JDK21  -> Target.JDK1_21;
@@ -253,10 +266,11 @@
         STRING_TEMPLATES(JDK21, Fragments.FeatureStringTemplates, DiagKind.PLURAL),
         IMPLICIT_CLASSES(JDK21, Fragments.FeatureImplicitClasses, DiagKind.PLURAL),
         WARN_ON_ILLEGAL_UTF8(MIN, JDK21),
         UNNAMED_VARIABLES(JDK22, Fragments.FeatureUnnamedVariables, DiagKind.PLURAL),
         SUPER_INIT(JDK22, Fragments.FeatureSuperInit, DiagKind.NORMAL),
+        VALUE_CLASSES(JDK22, Fragments.FeatureValueClasses, DiagKind.PLURAL),
         ;
 
         enum DiagKind {
             NORMAL,
             PLURAL;
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java
@@ -28,11 +28,10 @@
 import java.lang.annotation.Annotation;
 import java.lang.annotation.Inherited;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.EnumSet;
-import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Callable;
 import java.util.function.Supplier;
 import java.util.function.Predicate;
@@ -403,21 +402,29 @@
             (owner.flags() & INTERFACE) != 0 && kind != MTH &&
              name != name.table.names._this;
     }
 
     public boolean isInterface() {
-        return (flags() & INTERFACE) != 0;
+        return (flags_field & INTERFACE) != 0;
     }
 
     public boolean isAbstract() {
         return (flags_field & ABSTRACT) != 0;
     }
 
     public boolean isPrivate() {
         return (flags_field & Flags.AccessFlags) == PRIVATE;
     }
 
+    public boolean isValueClass() {
+        return (flags_field & VALUE_CLASS) != 0;
+    }
+
+    public boolean isIdentityClass() {
+        return !isInterface() && (flags_field & IDENTITY_TYPE) != 0;
+    }
+
     public boolean isPublic() {
         return (flags_field & Flags.AccessFlags) == PUBLIC;
     }
 
     public boolean isEnum() {
@@ -1323,11 +1330,11 @@
 
         public ClassSymbol(long flags, Name name, Symbol owner) {
             this(
                 flags,
                 name,
-                new ClassType(Type.noType, null, null),
+                new ClassType(Type.noType, null, null, List.nil()),
                 owner);
             this.type.tsym = this;
         }
 
         public void addPermittedSubclass(ClassSymbol csym, int pos) {
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java
@@ -229,10 +229,25 @@
         Assert.checkNonNull(metadata);
         this.tsym = tsym;
         this.metadata = metadata;
     }
 
+    public boolean isValueClass() {
+        return false;
+    }
+
+    public boolean isIdentityClass() {
+        return false;
+    }
+
+    // Does this type need to be preloaded in the context of the referring class ??
+    public boolean requiresPreload(Symbol referringClass) {
+        if (this.tsym == referringClass)
+            return false; // pointless
+        return this.isValueClass();
+    }
+
     /**
      * A subclass of {@link Types.TypeMapping} which applies a mapping recursively to the subterms
      * of a given type expression. This mapping returns the original type is no changes occurred
      * when recursively mapping the original type's subterms.
      */
@@ -1031,11 +1046,11 @@
         public int poolTag() {
             return ClassFile.CONSTANT_Class;
         }
 
         @Override
-        protected ClassType cloneWithMetadata(List<TypeMetadata> md) {
+        public ClassType cloneWithMetadata(List<TypeMetadata> md) {
             return new ClassType(outer_field, typarams_field, tsym, md) {
                 @Override
                 public Type baseType() { return ClassType.this.baseType(); }
             };
         }
@@ -1168,10 +1183,20 @@
         @Override
         public boolean isReference() {
             return true;
         }
 
+        @Override
+        public boolean isValueClass() {
+            return tsym != null && tsym.isValueClass();
+        }
+
+        @Override
+        public boolean isIdentityClass() {
+            return tsym != null && tsym.isIdentityClass();
+        }
+
         @Override
         public boolean isNullOrReference() {
             return true;
         }
 
@@ -2327,12 +2352,11 @@
             c.kind = ERR;
             c.members_field = new Scope.ErrorScope(c);
         }
 
         public ErrorType(Type originalType, TypeSymbol tsym) {
-            super(noType, List.nil(), null);
-            this.tsym = tsym;
+            super(noType, List.nil(), tsym, List.nil());
             this.originalType = (originalType == null ? noType : originalType);
         }
 
         private ErrorType(Type originalType, TypeSymbol tsym,
                           List<TypeMetadata> metadata) {
@@ -2340,11 +2364,11 @@
             this.tsym = tsym;
             this.originalType = (originalType == null ? noType : originalType);
         }
 
         @Override
-        protected ErrorType cloneWithMetadata(List<TypeMetadata> md) {
+        public ErrorType cloneWithMetadata(List<TypeMetadata> md) {
             return new ErrorType(originalType, tsym, md) {
                 @Override
                 public Type baseType() { return ErrorType.this.baseType(); }
             };
         }
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
@@ -31,10 +31,11 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.WeakHashMap;
+import java.util.function.BiFunction;
 import java.util.function.BiPredicate;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Collector;
 
@@ -47,11 +48,10 @@
 import com.sun.tools.javac.code.TypeMetadata.Annotations;
 import com.sun.tools.javac.comp.AttrContext;
 import com.sun.tools.javac.comp.Check;
 import com.sun.tools.javac.comp.Enter;
 import com.sun.tools.javac.comp.Env;
-import com.sun.tools.javac.comp.LambdaToMethod;
 import com.sun.tools.javac.jvm.ClassFile;
 import com.sun.tools.javac.util.*;
 
 import static com.sun.tools.javac.code.BoundKind.*;
 import static com.sun.tools.javac.code.Flags.*;
@@ -117,11 +117,16 @@
         chk = Check.instance(context);
         enter = Enter.instance(context);
         capturedName = names.fromString("<captured wildcard>");
         messages = JavacMessages.instance(context);
         diags = JCDiagnostic.Factory.instance(context);
-        noWarnings = new Warner(null);
+        noWarnings = new Warner(null) {
+            @Override
+            public String toString() {
+                return "NO_WARNINGS";
+            }
+        };
     }
     // </editor-fold>
 
     // <editor-fold defaultstate="collapsed" desc="bounds">
     /**
@@ -1680,10 +1685,13 @@
                 if (!pairsSeen.add(newPair))
                     return false;
                 if (isSubtype(erasure(ts.type), erasure(ss.type))) {
                     return false;
                 }
+                if (isSubtype(erasure(ts.type), erasure(ss.type))) {
+                    return false;
+                }
                 // if both are classes or both are interfaces, shortcut
                 if (ts.isInterface() == ss.isInterface() && isSubtype(erasure(ss.type), erasure(ts.type))) {
                     return false;
                 }
                 if (ts.isInterface() && !ss.isInterface()) {
@@ -2128,14 +2136,22 @@
      *
      * @param t The component type of the ArrayType
      * @return the ArrayType for the given component
      */
     public ArrayType makeArrayType(Type t) {
+        return makeArrayType(t, 1);
+    }
+
+    public ArrayType makeArrayType(Type t, int dimensions) {
         if (t.hasTag(VOID) || t.hasTag(PACKAGE)) {
             Assert.error("Type t must not be a VOID or PACKAGE type, " + t.toString());
         }
-        return new ArrayType(t, syms.arrayClass);
+        ArrayType result = new ArrayType(t, syms.arrayClass);
+        for (int i = 1; i < dimensions; i++) {
+            result = new ArrayType(result, syms.arrayClass);
+        }
+        return result;
     }
     // </editor-fold>
 
     // <editor-fold defaultstate="collapsed" desc="asSuper">
     /**
@@ -3897,11 +3913,11 @@
             }
             Assert.check(act1.isEmpty() && act2.isEmpty() && typarams.isEmpty());
             // There is no spec detailing how type annotations are to
             // be inherited.  So set it to noAnnotations for now
             return new ClassType(class1.getEnclosingType(), merged.toList(),
-                                 class1.tsym);
+                                 class1.tsym, List.nil());
         }
 
     /**
      * Return the minimum type of a closure, a compound type if no
      * unique minimum exists.
@@ -4868,24 +4884,34 @@
      * A wrapper for a type that allows use in sets.
      */
     public static class UniqueType {
         public final Type type;
         final Types types;
+        private boolean encodeTypeSig;
 
-        public UniqueType(Type type, Types types) {
+        public UniqueType(Type type, Types types, boolean encodeTypeSig) {
             this.type = type;
             this.types = types;
+            this.encodeTypeSig = encodeTypeSig;
+        }
+
+        public UniqueType(Type type, Types types) {
+            this(type, types, true);
         }
 
         public int hashCode() {
             return types.hashCode(type);
         }
 
         public boolean equals(Object obj) {
             return (obj instanceof UniqueType uniqueType) &&
                     types.isSameType(type, uniqueType.type);
         }
+
+        public boolean encodeTypeSig() {
+            return encodeTypeSig;
+        }
 
         public String toString() {
             return type.toString();
         }
 
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
@@ -303,10 +303,21 @@
                 log.error(pos, Errors.TryResourceMayNotBeAssigned(v));
             } else {
                 log.error(pos, Errors.CantAssignValToVar(Flags.toSource(v.flags() & (STATIC | FINAL)), v));
             }
         }
+
+        if (!env.info.ctorPrologue &&
+                v.owner.isValueClass() &&
+                !env.info.instanceInitializerBlock && // it is OK instance initializer blocks will go after super() anyways
+                v.owner.kind == TYP &&
+                v.owner == env.enclClass.sym &&
+                (v.flags() & STATIC) == 0 &&
+                (base == null ||
+                        TreeInfo.isExplicitThisReference(types, (ClassType)env.enclClass.type, base))) {
+            log.error(pos, Errors.CantRefAfterCtorCalled(v));
+        }
     }
 
     /** Does tree represent a static reference to an identifier?
      *  It is assumed that tree is either a SELECT or an IDENT.
      *  We have to weed out selects from non-type names here.
@@ -1187,11 +1198,15 @@
                 // or we are compiling class java.lang.Object.
                 if (isConstructor && owner.type != syms.objectType) {
                     if (!TreeInfo.hasAnyConstructorCall(tree)) {
                         JCStatement supCall = make.at(tree.body.pos).Exec(make.Apply(List.nil(),
                                 make.Ident(names._super), make.Idents(List.nil())));
-                        tree.body.stats = tree.body.stats.prepend(supCall);
+                        if (owner.isValueClass()) {
+                            tree.body.stats = tree.body.stats.append(supCall);
+                        } else {
+                            tree.body.stats = tree.body.stats.prepend(supCall);
+                        }
                     } else if ((env.enclClass.sym.flags() & ENUM) != 0 &&
                             (tree.mods.flags & GENERATEDCONSTR) == 0 &&
                             TreeInfo.hasConstructorCall(tree, names._super)) {
                         // enum constructors are not allowed to call super
                         // directly, so make sure there aren't any super calls
@@ -1329,11 +1344,12 @@
         for (Symbol s : syms.objectType.tsym.members().getSymbolsByName(name, s -> s.kind == MTH)) {
             if (s.type.getParameterTypes().isEmpty()) {
                 return true;
             }
         }
-        return false;
+        // isValueObject is not included in Object yet so we need a work around
+        return name == names.isValueObject;
     }
 
     Fragment canInferLocalVarType(JCVariableDecl tree) {
         LocalInitScanner lis = new LocalInitScanner();
         lis.scan(tree.init);
@@ -1413,11 +1429,15 @@
                     env.info.scope.owner.flags() & STRICTFP, names.empty, null,
                     env.info.scope.owner);
             final Env<AttrContext> localEnv =
                 env.dup(tree, env.info.dup(env.info.scope.dupUnshared(fakeOwner)));
 
-            if ((tree.flags & STATIC) != 0) localEnv.info.staticLevel++;
+            if ((tree.flags & STATIC) != 0) {
+                localEnv.info.staticLevel++;
+            } else {
+                localEnv.info.instanceInitializerBlock = true;
+            }
             // Attribute all type annotations in the block
             annotate.queueScanTreeAndTypeAnnotate(tree, localEnv, localEnv.info.scope.owner, null);
             annotate.flush();
             attribStats(tree.stats, localEnv);
 
@@ -1908,11 +1928,11 @@
         }
         return null;
     }
 
     public void visitSynchronized(JCSynchronized tree) {
-        chk.checkRefType(tree.pos(), attribExpr(tree.lock, env));
+        chk.checkIdentityType(tree.pos(), attribExpr(tree.lock, env));
         if (env.info.lint.isEnabled(LintCategory.SYNCHRONIZATION) && isValueBased(tree.lock.type)) {
             log.warning(LintCategory.SYNCHRONIZATION, tree.pos(), Warnings.AttemptToSynchronizeOnInstanceOfValueBasedClass);
         }
         attribStat(tree.body, env);
         result = null;
@@ -4361,10 +4381,11 @@
                 skind = KindSelector.of(skind, KindSelector.VAL, KindSelector.TYP);
         }
 
         // Attribute the qualifier expression, and determine its symbol (if any).
         Type site = attribTree(tree.selected, env, new ResultInfo(skind, Type.noType));
+        Assert.check(site == tree.selected.type);
         if (!pkind().contains(KindSelector.TYP_PCK))
             site = capture(site); // Capture field access
 
         // don't allow T.class T[].class, etc
         if (skind == KindSelector.TYP) {
@@ -5460,11 +5481,11 @@
 
                         if (!hasErrorSuper) {
                             log.error(TreeInfo.diagnosticPositionFor(c, env.tree), Errors.NonSealedWithNoSealedSupertype(c));
                         }
                     }
-                } else {
+                } else if ((c.flags_field & Flags.COMPOUND) == 0) {
                     if (c.isDirectlyOrIndirectlyLocal() && !c.isEnum()) {
                         log.error(TreeInfo.diagnosticPositionFor(c, env.tree), Errors.LocalClassesCantExtendSealed(c.isAnonymous() ? Fragments.Anonymous : Fragments.Local));
                     }
 
                     if (!c.type.isCompound()) {
@@ -5498,10 +5519,15 @@
 
                 if (rs.isSerializable(c.type)) {
                     env.info.isSerializable = true;
                 }
 
+                if (c.isValueClass()) {
+                    Assert.check(env.tree.hasTag(CLASSDEF));
+                    chk.checkConstraintsOfValueClass((JCClassDecl) env.tree, c);
+                }
+
                 attribClassBody(env, c);
 
                 chk.checkDeprecatedAnnotation(env.tree.pos(), c);
                 chk.checkClassOverrideEqualsAndHashIfNeeded(env.tree.pos(), c);
                 chk.checkFunctionalInterface((JCClassDecl) env.tree, c);
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java
@@ -124,10 +124,12 @@
      *  but this may not be true during the window a method is looked up in enclosing
      *  contexts (JDK-8145466)
      */
     JCTree preferredTreeForDiagnostics;
 
+    boolean instanceInitializerBlock = false;
+
     /** Duplicate this context, replacing scope field and copying all others.
      */
     AttrContext dup(WriteableScope scope) {
         AttrContext info = new AttrContext();
         info.scope = scope;
@@ -147,10 +149,11 @@
         info.isAnonymousDiamond = isAnonymousDiamond;
         info.isNewClass = isNewClass;
         info.preferredTreeForDiagnostics = preferredTreeForDiagnostics;
         info.visitingServiceImplementation = visitingServiceImplementation;
         info.allowProtectedAccess = allowProtectedAccess;
+        info.instanceInitializerBlock = instanceInitializerBlock;
         return info;
     }
 
     /** Duplicate this context, copying all fields.
      */
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
@@ -758,10 +758,35 @@
             return (t.hasTag(TYPEVAR))
                                     ? diags.fragment(Fragments.TypeParameter(t))
                                     : t;
         }
 
+    void checkConstraintsOfValueClass(JCClassDecl tree, ClassSymbol c) {
+        DiagnosticPosition pos = tree.pos();
+        for (Type st : types.closure(c.type)) {
+            if (st == null || st.tsym == null || st.tsym.kind == ERR)
+                continue;
+            if  (st.tsym == syms.objectType.tsym || st.tsym == syms.recordType.tsym || st.isInterface())
+                continue;
+            if (!st.tsym.isAbstract()) {
+                if (c != st.tsym) {
+                    log.error(pos, Errors.ConcreteSupertypeForValueClass(c, st));
+                }
+                continue;
+            }
+            // dealing with an abstract value or value super class below.
+            for (Symbol s : st.tsym.members().getSymbols(NON_RECURSIVE)) {
+                if (s.kind == MTH) {
+                    if ((s.flags() & (SYNCHRONIZED | STATIC)) == SYNCHRONIZED) {
+                        log.error(pos, Errors.SuperClassMethodCannotBeSynchronized(s, c, st));
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
     /** Check that type is a valid qualifier for a constructor reference expression
      */
     Type checkConstructorRefType(DiagnosticPosition pos, Type t) {
         t = checkClassOrArrayType(pos, t);
         if (t.hasTag(CLASS)) {
@@ -815,10 +840,32 @@
             return typeTagError(pos,
                                 diags.fragment(Fragments.TypeReqRef),
                                 t);
     }
 
+    /** Check that type is an identity type, i.e. not a value type.
+     *  When not discernible statically, give it the benefit of doubt
+     *  and defer to runtime.
+     *
+     *  @param pos           Position to be used for error reporting.
+     *  @param t             The type to be checked.
+     */
+    void checkIdentityType(DiagnosticPosition pos, Type t) {
+        if (t.hasTag(TYPEVAR)) {
+            t = types.skipTypeVars(t, false);
+        }
+        if (t.isIntersection()) {
+            IntersectionClassType ict = (IntersectionClassType)t;
+            for (Type component : ict.getExplicitComponents()) {
+                checkIdentityType(pos, component);
+            }
+            return;
+        }
+        if (t.isPrimitive() || (t.isValueClass() && !t.tsym.isAbstract()))
+            typeTagError(pos, diags.fragment(Fragments.TypeReqIdentity), t);
+    }
+
     /** Check that each type is a reference type, i.e. a class, interface or array type
      *  or a type variable.
      *  @param trees         Original trees, used for error reporting.
      *  @param types         The types to be checked.
      */
@@ -1205,12 +1252,17 @@
                 mask = ReceiverParamFlags;
             else if (sym.owner.kind != TYP)
                 mask = LocalVarFlags;
             else if ((sym.owner.flags_field & INTERFACE) != 0)
                 mask = implicit = InterfaceVarFlags;
-            else
-                mask = VarFlags;
+            else {
+                boolean isInstanceFieldOfValueClass = sym.owner.type.isValueClass() && (flags & STATIC) == 0;
+                mask = !isInstanceFieldOfValueClass ? VarFlags : ExtendedVarFlags;
+                if (isInstanceFieldOfValueClass) {
+                    implicit |= FINAL | STRICT;
+                }
+            }
             break;
         case MTH:
             if (sym.name == names.init) {
                 if ((sym.owner.flags_field & ENUM) != 0) {
                     // enum constructors cannot be declared public or
@@ -1232,13 +1284,16 @@
                     }
                 } else {
                     mask = implicit = InterfaceMethodFlags;
                 }
             } else if ((sym.owner.flags_field & RECORD) != 0) {
-                mask = RecordMethodFlags;
+                mask = ((sym.owner.flags_field & VALUE_CLASS) != 0 && (flags & Flags.STATIC) == 0) ?
+                        RecordMethodFlags & ~SYNCHRONIZED : RecordMethodFlags;
             } else {
-                mask = MethodFlags;
+                // value objects do not have an associated monitor/lock
+                mask = ((sym.owner.flags_field & VALUE_CLASS) != 0 && (flags & Flags.STATIC) == 0) ?
+                        MethodFlags & ~SYNCHRONIZED : MethodFlags;
             }
             if ((flags & STRICTFP) != 0) {
                 warnOnExplicitStrictfp(pos);
             }
             // Imply STRICTFP if owner has STRICTFP set.
@@ -1251,11 +1306,11 @@
                     (sym.isDirectlyOrIndirectlyLocal() && (flags & ANNOTATION) != 0)) {
                 boolean implicitlyStatic = !sym.isAnonymous() &&
                         ((flags & RECORD) != 0 || (flags & ENUM) != 0 || (flags & INTERFACE) != 0);
                 boolean staticOrImplicitlyStatic = (flags & STATIC) != 0 || implicitlyStatic;
                 // local statics are allowed only if records are allowed too
-                mask = staticOrImplicitlyStatic && allowRecords && (flags & ANNOTATION) == 0 ? StaticLocalFlags : LocalClassFlags;
+                mask = staticOrImplicitlyStatic && allowRecords && (flags & ANNOTATION) == 0 ? ExtendedStaticLocalClassFlags : ExtendedLocalClassFlags;
                 implicit = implicitlyStatic ? STATIC : implicit;
             } else if (sym.owner.kind == TYP) {
                 // statics in inner classes are allowed only if records are allowed too
                 mask = ((flags & STATIC) != 0) && allowRecords && (flags & ANNOTATION) == 0 ? ExtendedMemberStaticClassFlags : ExtendedMemberClassFlags;
                 if (sym.owner.owner.kind == PCK ||
@@ -1270,13 +1325,17 @@
                 mask = ExtendedClassFlags;
             }
             // Interfaces are always ABSTRACT
             if ((flags & INTERFACE) != 0) implicit |= ABSTRACT;
 
+            if ((flags & (INTERFACE | VALUE_CLASS)) == 0) {
+                implicit |= IDENTITY_TYPE;
+            }
+
             if ((flags & ENUM) != 0) {
-                // enums can't be declared abstract, final, sealed or non-sealed
-                mask &= ~(ABSTRACT | FINAL | SEALED | NON_SEALED);
+                // enums can't be declared abstract, final, sealed or non-sealed or value
+                mask &= ~(ABSTRACT | FINAL | SEALED | NON_SEALED | VALUE_CLASS);
                 implicit |= implicitEnumFinalFlag(tree);
             }
             if ((flags & RECORD) != 0) {
                 // records can't be declared abstract
                 mask &= ~ABSTRACT;
@@ -1285,10 +1344,18 @@
             if ((flags & STRICTFP) != 0) {
                 warnOnExplicitStrictfp(pos);
             }
             // Imply STRICTFP if owner has STRICTFP set.
             implicit |= sym.owner.flags_field & STRICTFP;
+
+            // concrete value classes are implicitly final
+            if ((flags & (ABSTRACT | INTERFACE | VALUE_CLASS)) == VALUE_CLASS) {
+                implicit |= FINAL;
+            }
+
+            // TYPs can't be declared synchronized
+            mask &= ~SYNCHRONIZED;
             break;
         default:
             throw new AssertionError();
         }
         long illegal = flags & ExtendedStandardFlags & ~mask;
@@ -1315,19 +1382,23 @@
                  &&
                  checkDisjoint(pos, flags,
                                ABSTRACT | INTERFACE,
                                FINAL | NATIVE | SYNCHRONIZED)
                  &&
+                 checkDisjoint(pos, flags,
+                        INTERFACE,
+                        VALUE_CLASS)
+                 &&
                  checkDisjoint(pos, flags,
                                PUBLIC,
                                PRIVATE | PROTECTED)
                  &&
                  checkDisjoint(pos, flags,
                                PRIVATE,
                                PUBLIC | PROTECTED)
                  &&
-                 checkDisjoint(pos, flags,
+                 checkDisjoint(pos, (flags | implicit), // complain against volatile & implcitly final entities too.
                                FINAL,
                                VOLATILE)
                  &&
                  (sym.kind == TYP ||
                   checkDisjoint(pos, flags,
@@ -1339,11 +1410,20 @@
                  && checkDisjoint(pos, flags,
                                 SEALED,
                            FINAL | NON_SEALED)
                  && checkDisjoint(pos, flags,
                                 SEALED,
-                                ANNOTATION)) {
+                                ANNOTATION)
+                && checkDisjoint(pos, flags,
+                                VALUE_CLASS,
+                                ANNOTATION)
+                && checkDisjoint(pos, flags,
+                                VALUE_CLASS,
+                                NON_SEALED)
+                && checkDisjoint(pos, flags,
+                                VALUE_CLASS,
+                                INTERFACE) ) {
             // skip
         }
         return flags & (mask | ~ExtendedStandardFlags) | implicit;
     }
 
@@ -2584,10 +2664,25 @@
             for (List<Type> m = supertypes; m != l; m = m.tail)
                 if (!checkCompatibleAbstracts(pos, l.head, m.head, c))
                     return;
         }
         checkCompatibleConcretes(pos, c);
+
+        boolean cIsValue = (c.tsym.flags() & VALUE_CLASS) != 0;
+        Type identitySuper = null, valueSuper = null;
+        for (Type t : types.closure(c)) {
+            if (t != c) {
+                if ((t.tsym.flags() & IDENTITY_TYPE) != 0 && (t.tsym.flags() & VALUE_BASED) == 0)
+                    identitySuper = t;
+                else if ((t.tsym.flags() & VALUE_CLASS) != 0)
+                    valueSuper = t;
+                if (cIsValue && identitySuper != null && identitySuper.tsym != syms.objectType.tsym) { // Object is special
+                    log.error(pos, Errors.ValueTypeHasIdentitySuperType(c, identitySuper));
+                    break;
+                }
+            }
+        }
     }
 
     /** Check that all non-override equivalent methods accessible from 'site'
      *  are mutually compatible (JLS 8.4.8/9.4.1).
      *
@@ -4058,13 +4153,16 @@
                 if (initCall != null) {
                     log.error(apply.pos(), Errors.RedundantSuperclassInit);
                     break;
                 }
 
+                // valhalla is using this feature so commenting this code for now so that the
+                // build doesn't depend on preview code
                 // If super()/this() isn't first, require "statements before super()" feature
-                if (!firstStatement)
+                /*if (!firstStatement) {
                     preview.checkSourceLevel(apply.pos(), Feature.SUPER_INIT);
+                }*/
 
                 // We found a legitimate super()/this() call; remember it
                 initCall = methodName;
             } while (false);
 
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
@@ -188,10 +188,14 @@
      * This is required when a capturing local class is created from a lambda (in which
      * case the captured symbols should be replaced with the translated lambda symbols).
      */
     Map<Symbol, Symbol> lambdaTranslationMap = null;
 
+    /** A hash table mapping local classes to a set of outer this fields
+     */
+    public Map<ClassSymbol, Set<JCExpression>> initializerOuterThis = new WeakHashMap<>();
+
     /** A navigator class for assembling a mapping from local class symbols
      *  to class definition trees.
      *  There is only one case; all other cases simply traverse down the tree.
      */
     class ClassMap extends TreeScanner {
@@ -904,11 +908,11 @@
         for (List<ClassSymbol> l = accessConstrTags; l.nonEmpty(); l = l.tail) {
             ClassSymbol c = l.head;
             if (isTranslatedClassAvailable(c))
                 continue;
             // Create class definition tree.
-            JCClassDecl cdec = makeEmptyClass(STATIC | SYNTHETIC,
+            JCClassDecl cdec = makeEmptyClass(STATIC | SYNTHETIC | IDENTITY_TYPE,
                     c.outermostClass(), c.flatname, false);
             swapAccessConstructorTag(c, cdec.sym);
             translated.append(cdec);
         }
     }
@@ -1388,11 +1392,11 @@
             Name flatname = names.fromString("" + topClass.getQualifiedName() +
                                             target.syntheticNameChar() +
                                             i);
             ClassSymbol ctag = chk.getCompiled(topModle, flatname);
             if (ctag == null)
-                ctag = makeEmptyClass(STATIC | SYNTHETIC, topClass).sym;
+                ctag = makeEmptyClass(STATIC | SYNTHETIC | IDENTITY_TYPE, topClass).sym;
             else if (!ctag.isAnonymous())
                 continue;
             // keep a record of all tags, to verify that all are generated as required
             accessConstrTags = accessConstrTags.prepend(ctag);
             return ctag;
@@ -1544,18 +1548,15 @@
         }
         return proxyName;
     }
 
     /** Proxy definitions for all free variables in given list, in reverse order.
-     *  @param pos        The source code position of the definition.
-     *  @param freevars   The free variables.
-     *  @param owner      The class in which the definitions go.
+     *  @param pos               The source code position of the definition.
+     *  @param freevars          The free variables.
+     *  @param owner             The class in which the definitions go.
+     *  @param additionalFlags   Any additional flags
      */
-    List<JCVariableDecl> freevarDefs(int pos, List<VarSymbol> freevars, Symbol owner) {
-        return freevarDefs(pos, freevars, owner, 0);
-    }
-
     List<JCVariableDecl> freevarDefs(int pos, List<VarSymbol> freevars, Symbol owner,
             long additionalFlags) {
         long flags = FINAL | SYNTHETIC | additionalFlags;
         List<JCVariableDecl> defs = List.nil();
         Set<Name> proxyNames = new HashSet<>();
@@ -1632,11 +1633,11 @@
     /** Definition for this$n field.
      *  @param pos        The source code position of the definition.
      *  @param owner      The class in which the definition goes.
      */
     JCVariableDecl outerThisDef(int pos, ClassSymbol owner) {
-        VarSymbol outerThis = makeOuterThisVarSymbol(owner, FINAL | SYNTHETIC);
+        VarSymbol outerThis = makeOuterThisVarSymbol(owner, FINAL | SYNTHETIC | (owner.isValueClass() ? STRICT : 0));
         return makeOuterThisVarDecl(pos, outerThis);
     }
 
     /** Return a list of trees that load the free variables in given list,
      *  in reverse order.
@@ -1965,11 +1966,11 @@
         Scope s = clazz.members();
         for (Symbol sym : s.getSymbols(NON_RECURSIVE))
             if (sym.kind == TYP &&
                 sym.name == names.empty &&
                 (sym.flags() & INTERFACE) == 0) return (ClassSymbol) sym;
-        return makeEmptyClass(STATIC | SYNTHETIC, clazz).sym;
+        return makeEmptyClass(STATIC | SYNTHETIC | IDENTITY_TYPE, clazz).sym;
     }
 
     /** Create an attributed tree of the form left.name(). */
     private JCMethodInvocation makeCall(JCExpression left, Name name, List<JCExpression> args) {
         Assert.checkNonNull(left.type);
@@ -2017,11 +2018,11 @@
     /**Used to create an auxiliary class to hold $assertionsDisabled for interfaces.
      */
     private ClassSymbol assertionsDisabledClass() {
         if (assertionsDisabledClassCache != null) return assertionsDisabledClassCache;
 
-        assertionsDisabledClassCache = makeEmptyClass(STATIC | SYNTHETIC, outermostClassDef.sym).sym;
+        assertionsDisabledClassCache = makeEmptyClass(STATIC | SYNTHETIC | IDENTITY_TYPE, outermostClassDef.sym).sym;
 
         return assertionsDisabledClassCache;
     }
 
     // This code is not particularly robust if the user has
@@ -2305,11 +2306,11 @@
         if (currentClass.hasOuterInstance())
             otdef = outerThisDef(tree.pos, currentClass);
 
         // If this is a local class, define proxies for all its free variables.
         List<JCVariableDecl> fvdefs = freevarDefs(
-            tree.pos, freevars(currentClass), currentClass);
+            tree.pos, freevars(currentClass), currentClass, currentClass.isValueClass() ? STRICT : 0);
 
         // Recursively translate superclass, interfaces.
         tree.extending = translate(tree.extending);
         tree.implementing = translate(tree.implementing);
 
@@ -2882,23 +2883,29 @@
             ListBuffer<VarSymbol> fields = new ListBuffer<>();
             for (Symbol sym : currentClass.getEnclosedElements()) {
                 if (sym.kind == Kinds.Kind.VAR && ((sym.flags() & RECORD) != 0))
                     fields.append((VarSymbol) sym);
             }
+            ListBuffer<JCStatement> initializers = new ListBuffer<>();
             for (VarSymbol field: fields) {
                 if ((field.flags_field & Flags.UNINITIALIZED_FIELD) != 0) {
                     VarSymbol param = tree.params.stream().filter(p -> p.name == field.name).findFirst().get().sym;
                     make.at(tree.pos);
-                    tree.body.stats = tree.body.stats.append(
-                            make.Exec(
-                                    make.Assign(
-                                            make.Select(make.This(field.owner.erasure(types)), field),
-                                            make.Ident(param)).setType(field.erasure(types))));
-                    // we don't need the flag at the field anymore
+                    initializers.add(make.Exec(
+                            make.Assign(
+                                    make.Select(make.This(field.owner.erasure(types)), field),
+                                    make.Ident(param)).setType(field.erasure(types))));
                     field.flags_field &= ~Flags.UNINITIALIZED_FIELD;
                 }
             }
+            if (initializers.nonEmpty()) {
+                if (tree.sym.owner.isValueClass()) {
+                    TreeInfo.mapSuperCalls(tree.body, supercall -> make.Block(0, initializers.toList().append(supercall)));
+                } else {
+                    tree.body.stats = tree.body.stats.appendList(initializers);
+                }
+            }
         }
         result = tree;
     }
     //where
         private Map<Symbol, Symbol> makeTranslationMap(JCMethodDecl tree) {
@@ -2956,10 +2963,21 @@
                 // local class
                 thisArg = makeThis(tree.pos(), c.type.getEnclosingType().tsym);
             } else {
                 // nested class
                 thisArg = makeOwnerThis(tree.pos(), c, false);
+                if (currentMethodSym != null &&
+                        ((currentMethodSym.flags_field & (STATIC | BLOCK)) == BLOCK) &&
+                        currentMethodSym.owner.isValueClass()) {
+                    // instance initializer in a value class
+                    Set<JCExpression> outerThisSet = initializerOuterThis.get(currentClass);
+                    if (outerThisSet == null) {
+                        outerThisSet = new HashSet<>();
+                    }
+                    outerThisSet.add(thisArg);
+                    initializerOuterThis.put(currentClass, outerThisSet);
+                }
             }
             tree.args = tree.args.prepend(thisArg);
         }
         tree.encl = null;
 
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java
@@ -222,11 +222,11 @@
         if (lastParam != null && (lastParam.mods.flags & Flags.VARARGS) != 0)
             m.flags_field |= Flags.VARARGS;
 
         localEnv.info.scope.leave();
         if (chk.checkUnique(tree.pos(), m, enclScope)) {
-        enclScope.enter(m);
+            enclScope.enter(m);
         }
 
         annotate.annotateLater(tree.mods.annotations, localEnv, m, tree.pos());
         // Visit the signature of the method. Note that
         // TypeAnnotate doesn't descend into the body.
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java
@@ -413,47 +413,52 @@
         if (env.info.visitingServiceImplementation &&
             env.toplevel.modle == sym.packge().modle) {
             return true;
         }
 
-        switch ((short)(sym.flags() & AccessFlags)) {
-        case PRIVATE:
-            return
-                (env.enclClass.sym == sym.owner // fast special case
-                 ||
-                 env.enclClass.sym.outermostClass() ==
-                 sym.owner.outermostClass())
-                &&
-                sym.isInheritedIn(site.tsym, types);
-        case 0:
-            return
-                (env.toplevel.packge == sym.owner.owner // fast special case
-                 ||
-                 env.toplevel.packge == sym.packge())
-                &&
-                isAccessible(env, site, checkInner)
-                &&
-                sym.isInheritedIn(site.tsym, types)
-                &&
-                notOverriddenIn(site, sym);
-        case PROTECTED:
-            return
-                (env.toplevel.packge == sym.owner.owner // fast special case
-                 ||
-                 env.toplevel.packge == sym.packge()
-                 ||
-                 isProtectedAccessible(sym, env.enclClass.sym, site)
-                 ||
-                 // OK to select instance method or field from 'super' or type name
-                 // (but type names should be disallowed elsewhere!)
-                 env.info.selectSuper && (sym.flags() & STATIC) == 0 && sym.kind != TYP)
-                &&
-                isAccessible(env, site, checkInner)
-                &&
-                notOverriddenIn(site, sym);
-        default: // this case includes erroneous combinations as well
-            return isAccessible(env, site, checkInner) && notOverriddenIn(site, sym);
+        ClassSymbol enclosingCsym = env.enclClass.sym;
+        try {
+            switch ((short)(sym.flags() & AccessFlags)) {
+                case PRIVATE:
+                    return
+                            (env.enclClass.sym == sym.owner // fast special case
+                                    ||
+                                    env.enclClass.sym.outermostClass() ==
+                                            sym.owner.outermostClass())
+                                    &&
+                                    sym.isInheritedIn(site.tsym, types);
+                case 0:
+                    return
+                            (env.toplevel.packge == sym.owner.owner // fast special case
+                                    ||
+                                    env.toplevel.packge == sym.packge())
+                                    &&
+                                    isAccessible(env, site, checkInner)
+                                    &&
+                                    sym.isInheritedIn(site.tsym, types)
+                                    &&
+                                    notOverriddenIn(site, sym);
+                case PROTECTED:
+                    return
+                            (env.toplevel.packge == sym.owner.owner // fast special case
+                                    ||
+                                    env.toplevel.packge == sym.packge()
+                                    ||
+                                    isProtectedAccessible(sym, env.enclClass.sym, site)
+                                    ||
+                                    // OK to select instance method or field from 'super' or type name
+                                    // (but type names should be disallowed elsewhere!)
+                                    env.info.selectSuper && (sym.flags() & STATIC) == 0 && sym.kind != TYP)
+                                    &&
+                                    isAccessible(env, site, checkInner)
+                                    &&
+                                    notOverriddenIn(site, sym);
+                default: // this case includes erroneous combinations as well
+                    return isAccessible(env, site, checkInner) && notOverriddenIn(site, sym);
+            }
+        } finally {
+            env.enclClass.sym = enclosingCsym;
         }
     }
     //where
     /* `sym' is accessible only if not overridden by
      * another symbol which is a member of `site'
@@ -1506,12 +1511,15 @@
                 if (sym.kind == VAR &&
                         sym.owner.kind == TYP &&
                         (sym.flags() & STATIC) == 0) {
                     if (staticOnly)
                         return new StaticError(sym);
-                    if (env1.info.ctorPrologue && (sym.flags_field & SYNTHETIC) == 0)
-                        return new RefBeforeCtorCalledError(sym);
+                    if (env1.info.ctorPrologue && (sym.flags_field & SYNTHETIC) == 0) {
+                        if (!env.tree.hasTag(ASSIGN) || !TreeInfo.isIdentOrThisDotIdent(((JCAssign)env.tree).lhs)) {
+                            return new RefBeforeCtorCalledError(sym);
+                        }
+                    }
                 }
                 return sym;
             } else {
                 bestSoFar = bestOf(bestSoFar, sym);
             }
@@ -4205,11 +4213,10 @@
                     return diags.create(dkind, log.currentSource(), pos,
                               "cant.apply.symbol.noargs",
                               rewriter,
                               kindName(ws),
                               ws.name == names.init ? ws.owner.name : ws.name,
-                              kindName(ws.owner),
                               ws.owner.type,
                               c.snd);
                 default:
                     // Avoid saying "constructor Array in class Array"
                     if (ws.owner == syms.arrayClass && ws.name == names.init) {
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java
@@ -772,11 +772,11 @@
         boolean selectedDirectRef = refs.remove(ExprRef.direct(depth));
         boolean selectedIndirectRef = refs.remove(ExprRef.indirect(depth));
 
         // Explicit 'this' reference?
         Type.ClassType currentClassType = (Type.ClassType)methodClass.sym.type;
-        if (isExplicitThisReference(types, currentClassType, tree)) {
+        if (TreeInfo.isExplicitThisReference(types, currentClassType, tree)) {
             refs.mapInto(refs, ThisRef.class, direct -> new ExprRef(depth, direct));
             return;
         }
 
         // Explicit outer 'this' reference?
@@ -1160,47 +1160,10 @@
         return sym != null &&
             sym.kind == VAR &&
             (sym.owner.kind == MTH || sym.owner.kind == VAR);
     }
 
-    /** Check if the given tree is an explicit reference to the 'this' instance of the
-     *  class currently being compiled. This is true if tree is:
-     *  - An unqualified 'this' identifier
-     *  - A 'super' identifier qualified by a class name whose type is 'currentClass' or a supertype
-     *  - A 'this' identifier qualified by a class name whose type is 'currentClass' or a supertype
-     *    but also NOT an enclosing outer class of 'currentClass'.
-     */
-    private boolean isExplicitThisReference(Types types, Type.ClassType currentClass, JCTree tree) {
-        switch (tree.getTag()) {
-            case PARENS:
-                return isExplicitThisReference(types, currentClass, TreeInfo.skipParens(tree));
-            case IDENT:
-            {
-                JCIdent ident = (JCIdent)tree;
-                Names names = ident.name.table.names;
-                return ident.name == names._this;
-            }
-            case SELECT:
-            {
-                JCFieldAccess select = (JCFieldAccess)tree;
-                Type selectedType = types.erasure(select.selected.type);
-                if (!selectedType.hasTag(CLASS))
-                    return false;
-                ClassSymbol currentClassSym = (ClassSymbol)((Type.ClassType)types.erasure(currentClass)).tsym;
-                ClassSymbol selectedClassSym = (ClassSymbol)((Type.ClassType)selectedType).tsym;
-                Names names = select.name.table.names;
-                return currentClassSym.isSubClass(selectedClassSym, types) &&
-                        (select.name == names._super ||
-                        (select.name == names._this &&
-                            (currentClassSym == selectedClassSym ||
-                            !currentClassSym.isEnclosedBy(selectedClassSym))));
-            }
-            default:
-                return false;
-        }
-    }
-
     // When scanning nodes we can be in one of two modes:
     //  (a) Looking for constructors - we do not recurse into any code blocks
     //  (b) Analyzing a constructor - we are tracing its possible execution paths
     private boolean isAnalyzing() {
         return targetClass != null;
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java
@@ -32,11 +32,10 @@
 import javax.tools.JavaFileObject;
 
 import com.sun.tools.javac.code.*;
 import com.sun.tools.javac.code.Lint.LintCategory;
 import com.sun.tools.javac.code.Scope.ImportFilter;
-import com.sun.tools.javac.code.Scope.ImportScope;
 import com.sun.tools.javac.code.Scope.NamedImportScope;
 import com.sun.tools.javac.code.Scope.StarImportScope;
 import com.sun.tools.javac.code.Scope.WriteableScope;
 import com.sun.tools.javac.code.Source.Feature;
 import com.sun.tools.javac.comp.Annotate.AnnotationTypeMetadata;
@@ -51,15 +50,15 @@
 import com.sun.tools.javac.resources.CompilerProperties.Errors;
 import com.sun.tools.javac.tree.JCTree.*;
 
 import static com.sun.tools.javac.code.Flags.*;
 import static com.sun.tools.javac.code.Flags.ANNOTATION;
+import static com.sun.tools.javac.code.Flags.SYNCHRONIZED;
 import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
 import static com.sun.tools.javac.code.Kinds.Kind.*;
 import static com.sun.tools.javac.code.TypeTag.CLASS;
 import static com.sun.tools.javac.code.TypeTag.ERROR;
-import com.sun.tools.javac.resources.CompilerProperties.Fragments;
 
 import static com.sun.tools.javac.code.TypeTag.*;
 import static com.sun.tools.javac.tree.JCTree.Tag.*;
 
 import com.sun.tools.javac.util.Dependencies.CompletionCause;
@@ -1137,10 +1136,14 @@
             }
             if (tree.sym.isAnnotationType()) {
                 Assert.check(tree.sym.isCompleted());
                 tree.sym.setAnnotationTypeMetadata(new AnnotationTypeMetadata(tree.sym, annotate.annotationTypeSourceCompleter()));
             }
+
+            if ((tree.sym.flags() & (INTERFACE | VALUE_CLASS)) == 0) {
+                tree.sym.flags_field |= IDENTITY_TYPE;
+            }
         }
 
         private void addAccessor(JCVariableDecl tree, Env<AttrContext> env) {
             MethodSymbol implSym = lookupMethod(env.enclClass.sym, tree.sym.name, List.nil());
             RecordComponent rec = ((ClassSymbol) tree.sym.owner).getRecordComponent(tree.sym);
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java
@@ -60,12 +60,14 @@
 import com.sun.tools.javac.file.BaseFileManager;
 import com.sun.tools.javac.file.PathFileObject;
 import com.sun.tools.javac.jvm.ClassFile.Version;
 import com.sun.tools.javac.jvm.PoolConstant.NameAndType;
 import com.sun.tools.javac.main.Option;
+import com.sun.tools.javac.resources.CompilerProperties;
 import com.sun.tools.javac.resources.CompilerProperties.Fragments;
 import com.sun.tools.javac.resources.CompilerProperties.Warnings;
+import com.sun.tools.javac.tree.JCTree;
 import com.sun.tools.javac.util.*;
 import com.sun.tools.javac.util.ByteBuffer.UnderflowException;
 import com.sun.tools.javac.util.DefinedBy.Api;
 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
 import com.sun.tools.javac.util.JCDiagnostic.Fragment;
@@ -73,10 +75,11 @@
 import static com.sun.tools.javac.code.Flags.*;
 import static com.sun.tools.javac.code.Kinds.Kind.*;
 
 import com.sun.tools.javac.code.Scope.LookupKind;
 
+import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
 import static com.sun.tools.javac.code.TypeTag.ARRAY;
 import static com.sun.tools.javac.code.TypeTag.CLASS;
 import static com.sun.tools.javac.code.TypeTag.TYPEVAR;
 import static com.sun.tools.javac.jvm.ClassFile.*;
 import static com.sun.tools.javac.jvm.ClassFile.Version.*;
@@ -108,10 +111,14 @@
 
     /** Switch: allow modules.
      */
     boolean allowModules;
 
+    /** Switch: allow value classes.
+     */
+    boolean allowValueClasses;
+
     /** Switch: allow sealed
      */
     boolean allowSealedTypes;
 
     /** Switch: allow records
@@ -282,10 +289,12 @@
         verbose         = options.isSet(Option.VERBOSE);
 
         Source source = Source.instance(context);
         preview = Preview.instance(context);
         allowModules     = Feature.MODULES.allowedInSource(source);
+        allowValueClasses = (!preview.isPreview(Feature.VALUE_CLASSES) || preview.isEnabled()) &&
+                Feature.VALUE_CLASSES.allowedInSource(source);
         allowRecords = Feature.RECORDS.allowedInSource(source);
         allowSealedTypes = Feature.SEALED_CLASSES.allowedInSource(source);
         warnOnIllegalUtf8 = Feature.WARN_ON_ILLEGAL_UTF8.allowedInSource(source);
 
         saveParameterNames = options.isSet(PARAMETERS);
@@ -564,10 +573,11 @@
     Type classSigToType() {
         if (signature[sigp] != 'L')
             throw badClassFile("bad.class.signature", quoteBadSignature());
         sigp++;
         Type outer = Type.noType;
+        Name name;
         int startSbp = sbp;
 
         while (true) {
             final byte c = signature[sigp++];
             switch (c) {
@@ -576,23 +586,25 @@
                 ClassSymbol t = enterClass(readName(signatureBuffer,
                                                          startSbp,
                                                          sbp - startSbp));
 
                 try {
-                    return (outer == Type.noType) ?
-                            t.erasure(types) :
-                        new ClassType(outer, List.nil(), t);
+                    if (outer == Type.noType) {
+                        ClassType et = (ClassType) t.erasure(types);
+                        return new ClassType(et.getEnclosingType(), List.nil(), et.tsym, et.getMetadata());
+                    }
+                    return new ClassType(outer, List.nil(), t, List.nil());
                 } finally {
                     sbp = startSbp;
                 }
             }
 
             case '<':           // generic arguments
                 ClassSymbol t = enterClass(readName(signatureBuffer,
                                                          startSbp,
                                                          sbp - startSbp));
-                outer = new ClassType(outer, sigToTypes('>'), t) {
+                outer = new ClassType(outer, sigToTypes('>'), t, List.nil()) {
                         boolean completed = false;
                         @Override @DefinedBy(Api.LANGUAGE_MODEL)
                         public Type getEnclosingType() {
                             if (!completed) {
                                 completed = true;
@@ -651,11 +663,11 @@
                 //we have seen an enclosing non-generic class
                 if (outer != Type.noType) {
                     t = enterClass(readName(signatureBuffer,
                                                  startSbp,
                                                  sbp - startSbp));
-                    outer = new ClassType(outer, List.nil(), t);
+                    outer = new ClassType(outer, List.nil(), t, List.nil());
                 }
                 signatureBuffer[sbp++] = (byte)'$';
                 continue;
             case '/':
                 signatureBuffer[sbp++] = (byte)'.';
@@ -3125,10 +3137,15 @@
 /************************************************************************
  * Adjusting flags
  ***********************************************************************/
 
     long adjustFieldFlags(long flags) {
+        boolean previewClassFile = minorVersion == ClassFile.PREVIEW_MINOR_VERSION;
+        if (allowValueClasses && previewClassFile && (flags & ACC_STRICT) != 0) {
+            flags &= ~ACC_STRICT;
+            flags |= STRICT;
+        }
         return flags;
     }
 
     long adjustMethodFlags(long flags) {
         if ((flags & ACC_BRIDGE) != 0) {
@@ -3141,15 +3158,22 @@
         }
         return flags;
     }
 
     long adjustClassFlags(long flags) {
+        boolean previewClassFile = minorVersion == ClassFile.PREVIEW_MINOR_VERSION;
         if ((flags & ACC_MODULE) != 0) {
             flags &= ~ACC_MODULE;
             flags |= MODULE;
         }
-        return flags & ~ACC_SUPER; // SUPER and SYNCHRONIZED bits overloaded
+        if ((flags & ACC_IDENTITY) != 0 || (majorVersion < V66.major && (flags & INTERFACE) == 0)) {
+            flags |= IDENTITY_TYPE;
+        } else if ((flags & INTERFACE) == 0 && allowValueClasses && previewClassFile && majorVersion >= V66.major) {
+            flags |= VALUE_CLASS;
+        }
+        flags &= ~ACC_IDENTITY; // ACC_IDENTITY and SYNCHRONIZED bits overloaded
+        return flags;
     }
 
     /**
      * A subclass of JavaFileObject for the sourcefile attribute found in a classfile.
      * The attribute is only the last component of the original filename, so is unlikely
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java
@@ -220,11 +220,11 @@
     public static String flagNames(long flags) {
         StringBuilder sbuf = new StringBuilder();
         int i = 0;
         long f = flags & StandardFlags;
         while (f != 0) {
-            if ((f & 1) != 0) {
+            if ((f & 1) != 0 && flagName[i] != "") {
                 sbuf.append(" ");
                 sbuf.append(flagName[i]);
             }
             f = f >> 1;
             i++;
@@ -232,11 +232,12 @@
         return sbuf.toString();
     }
     //where
         private static final String[] flagName = {
             "PUBLIC", "PRIVATE", "PROTECTED", "STATIC", "FINAL",
-            "SUPER", "VOLATILE", "TRANSIENT", "NATIVE", "INTERFACE",
+            // the empty position should be for synchronized but right now we don't have any test checking it
+            "", "VOLATILE", "TRANSIENT", "NATIVE", "INTERFACE",
             "ABSTRACT", "STRICTFP"};
 
 /******************************************************************
  * Output routines
  ******************************************************************/
@@ -832,13 +833,12 @@
     void writeInnerClasses() {
         int alenIdx = writeAttr(names.InnerClasses);
         databuf.appendChar(poolWriter.innerClasses.size());
         for (ClassSymbol inner : poolWriter.innerClasses) {
             inner.markAbstractIfNeeded(types);
-            int flags = adjustFlags(inner.flags_field);
+            int flags = adjustFlags(inner, inner.flags_field);
             if ((flags & INTERFACE) != 0) flags |= ABSTRACT; // Interfaces are always ABSTRACT
-            flags &= ~STRICTFP; //inner classes should not have the strictfp flag set.
             if (dumpInnerClassModifiers) {
                 PrintWriter pw = log.getWriter(Log.WriterKind.ERROR);
                 pw.println("INNERCLASS  " + inner.name);
                 pw.println("---" + flagNames(flags));
             }
@@ -850,10 +850,21 @@
             databuf.appendChar(flags);
         }
         endAttr(alenIdx);
     }
 
+     /** Write out "Preload" attribute by enumerating the value classes encountered in field/method descriptors during this compilation.
+      */
+     void writePreloadAttribute() {
+        int alenIdx = writeAttr(names.Preload);
+        databuf.appendChar(poolWriter.preloadClasses.size());
+        for (ClassSymbol c : poolWriter.preloadClasses) {
+            databuf.appendChar(poolWriter.putClass(c));
+        }
+        endAttr(alenIdx);
+     }
+
     int writeRecordAttribute(ClassSymbol csym) {
         int alenIdx = writeAttr(names.Record);
         Scope s = csym.members();
         databuf.appendChar(csym.getRecordComponents().size());
         for (VarSymbol v: csym.getRecordComponents()) {
@@ -963,19 +974,26 @@
     }
 
     /** Write field symbol, entering all references into constant pool.
      */
     void writeField(VarSymbol v) {
-        int flags = adjustFlags(v.flags());
+        int flags = adjustFlags(v, v.flags());
         databuf.appendChar(flags);
         if (dumpFieldModifiers) {
             PrintWriter pw = log.getWriter(Log.WriterKind.ERROR);
             pw.println("FIELD  " + v.name);
             pw.println("---" + flagNames(v.flags()));
         }
         databuf.appendChar(poolWriter.putName(v.name));
         databuf.appendChar(poolWriter.putDescriptor(v));
+        Type fldType = v.erasure(types);
+        if (fldType.requiresPreload(v.owner)) {
+            poolWriter.enterPreloadClass((ClassSymbol) fldType.tsym);
+            if (preview.isPreview(Source.Feature.VALUE_CLASSES)) {
+                preview.markUsesPreview(null);
+            }
+        }
         int acountIdx = beginAttrs();
         int acount = 0;
         if (v.getConstValue() != null) {
             int alenIdx = writeAttr(names.ConstantValue);
             databuf.appendChar(poolWriter.putConstant(v.getConstValue()));
@@ -988,19 +1006,29 @@
     }
 
     /** Write method symbol, entering all references into constant pool.
      */
     void writeMethod(MethodSymbol m) {
-        int flags = adjustFlags(m.flags());
+        int flags = adjustFlags(m, m.flags());
         databuf.appendChar(flags);
         if (dumpMethodModifiers) {
             PrintWriter pw = log.getWriter(Log.WriterKind.ERROR);
             pw.println("METHOD  " + m.name);
             pw.println("---" + flagNames(m.flags()));
         }
         databuf.appendChar(poolWriter.putName(m.name));
         databuf.appendChar(poolWriter.putDescriptor(m));
+        MethodType mtype = (MethodType) m.externalType(types);
+        for (Type t : mtype.getParameterTypes()) {
+            if (t.requiresPreload(m.owner)) {
+                poolWriter.enterPreloadClass((ClassSymbol) t.tsym);
+            }
+        }
+        Type returnType = mtype.getReturnType();
+        if (returnType.requiresPreload(m.owner)) {
+            poolWriter.enterPreloadClass((ClassSymbol) returnType.tsym);
+        }
         int acountIdx = beginAttrs();
         int acount = 0;
         if (m.code != null) {
             int alenIdx = writeAttr(names.Code);
             writeCode(m.code);
@@ -1574,14 +1602,15 @@
 
         int flags;
         if (c.owner.kind == MDL) {
             flags = ACC_MODULE;
         } else {
-            flags = adjustFlags(c.flags() & ~DEFAULT);
+            long originalFlags = c.flags();
+            flags = adjustFlags(c, c.flags() & ~(DEFAULT | STRICTFP));
             if ((flags & PROTECTED) != 0) flags |= PUBLIC;
-            flags = flags & ClassFlags & ~STRICTFP;
-            if ((flags & INTERFACE) == 0) flags |= ACC_SUPER;
+            flags = flags & ClassFlags;
+            flags |= (originalFlags & IDENTITY_TYPE) != 0 ? ACC_IDENTITY : flags;
         }
 
         if (dumpClassModifiers) {
             PrintWriter pw = log.getWriter(Log.WriterKind.ERROR);
             pw.println();
@@ -1703,10 +1732,15 @@
         if (!poolWriter.innerClasses.isEmpty()) {
             writeInnerClasses();
             acount++;
         }
 
+        if (!poolWriter.preloadClasses.isEmpty()) {
+            writePreloadAttribute();
+            acount++;
+        }
+
         endAttrs(acountIdx, acount);
 
         out.write(poolbuf.elems, 0, poolbuf.length);
 
         poolWriter.writePool(out);
@@ -1733,11 +1767,11 @@
             i += hook.applyAsInt(sym);
         }
         return i;
     }
 
-    int adjustFlags(final long flags) {
+    int adjustFlags(Symbol sym, final long flags) {
         int result = (int)flags;
 
         // Elide strictfp bit in class files
         if (target.obsoleteAccStrict())
             result &= ~STRICTFP;
@@ -1746,10 +1780,18 @@
             result |= ACC_BRIDGE;
         if ((flags & VARARGS) != 0)
             result |= ACC_VARARGS;
         if ((flags & DEFAULT) != 0)
             result &= ~ABSTRACT;
+        if ((flags & IDENTITY_TYPE) != 0) {
+            result |= ACC_IDENTITY;
+        }
+        if (sym.kind == VAR) {
+            if ((flags & STRICT) != 0) {
+                result |= ACC_STRICT;
+            }
+        }
         return result;
     }
 
     long getLastModified(FileObject filename) {
         long mod = 0;
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java
@@ -422,10 +422,12 @@
      *  @param defs         The list of class member declarations.
      *  @param c            The enclosing class.
      */
     List<JCTree> normalizeDefs(List<JCTree> defs, ClassSymbol c) {
         ListBuffer<JCStatement> initCode = new ListBuffer<>();
+        // only used for value classes
+        ListBuffer<JCStatement> initBlocks = new ListBuffer<>();
         ListBuffer<Attribute.TypeCompound> initTAs = new ListBuffer<>();
         ListBuffer<JCStatement> clinitCode = new ListBuffer<>();
         ListBuffer<Attribute.TypeCompound> clinitTAs = new ListBuffer<>();
         ListBuffer<JCTree> methodDefs = new ListBuffer<>();
         // Sort definitions into three listbuffers:
@@ -437,12 +439,17 @@
             switch (def.getTag()) {
             case BLOCK:
                 JCBlock block = (JCBlock)def;
                 if ((block.flags & STATIC) != 0)
                     clinitCode.append(block);
-                else if ((block.flags & SYNTHETIC) == 0)
-                    initCode.append(block);
+                else if ((block.flags & SYNTHETIC) == 0) {
+                    if (c.isValueClass()) {
+                        initBlocks.append(block);
+                    } else {
+                        initCode.append(block);
+                    }
+                }
                 break;
             case METHODDEF:
                 methodDefs.append(def);
                 break;
             case VARDEF:
@@ -477,16 +484,15 @@
             default:
                 Assert.error();
             }
         }
         // Insert any instance initializers into all constructors.
-        if (initCode.length() != 0) {
-            List<JCStatement> inits = initCode.toList();
+        if (initCode.length() != 0 || initBlocks.length() != 0) {
             initTAs.addAll(c.getInitTypeAttributes());
             List<Attribute.TypeCompound> initTAlist = initTAs.toList();
             for (JCTree t : methodDefs) {
-                normalizeMethod((JCMethodDecl)t, inits, initTAlist);
+                normalizeMethod((JCMethodDecl)t, initCode.toList(), initBlocks.toList(), initTAlist);
             }
         }
         // If there are class initializers, create a <clinit> method
         // that contains them as its body.
         if (clinitCode.length() != 0) {
@@ -545,23 +551,62 @@
      *  @param md        The tree potentially representing a
      *                   constructor's definition.
      *  @param initCode  The list of instance initializer statements.
      *  @param initTAs  Type annotations from the initializer expression.
      */
-    void normalizeMethod(JCMethodDecl md, List<JCStatement> initCode, List<TypeCompound> initTAs) {
+    void normalizeMethod(JCMethodDecl md, List<JCStatement> initCode, List<JCStatement> initBlocks,  List<TypeCompound> initTAs) {
         if (TreeInfo.isConstructor(md) && TreeInfo.hasConstructorCall(md, names._super)) {
             // We are seeing a constructor that has a super() call.
             // Find the super() invocation and append the given initializer code.
-            TreeInfo.mapSuperCalls(md.body, supercall -> make.Block(0, initCode.prepend(supercall)));
+            if (md.sym.owner.isValueClass()) {
+                rewriteInitializersIfNeeded(md, initCode);
+                TreeInfo.mapSuperCalls(md.body, supercall -> make.Block(0, initCode.append(supercall).appendList(initBlocks)));
+            } else {
+                TreeInfo.mapSuperCalls(md.body, supercall -> make.Block(0, initCode.prepend(supercall)));
+            }
 
             if (md.body.endpos == Position.NOPOS)
                 md.body.endpos = TreeInfo.endPos(md.body.stats.last());
 
             md.sym.appendUniqueTypeAttributes(initTAs);
         }
     }
 
+    void rewriteInitializersIfNeeded(JCMethodDecl md, List<JCStatement> initCode) {
+        if (lower.initializerOuterThis.containsKey(md.sym.owner)) {
+            InitializerVisitor initializerVisitor = new InitializerVisitor(md, lower.initializerOuterThis.get(md.sym.owner));
+            for (JCStatement init : initCode) {
+                initializerVisitor.scan(init);
+            }
+        }
+    }
+
+    class InitializerVisitor extends TreeScanner {
+        JCMethodDecl md;
+        Set<JCExpression> exprSet;
+
+        InitializerVisitor(JCMethodDecl md, Set<JCExpression> exprSet) {
+            this.md = md;
+            this.exprSet = exprSet;
+        }
+
+        @Override
+        public void visitTree(JCTree tree) {}
+
+        @Override
+        public void visitIdent(JCIdent tree) {
+            if (exprSet.contains(tree)) {
+                for (JCVariableDecl param: md.params) {
+                    if (param.name == tree.name &&
+                            ((param.sym.flags_field & (MANDATED | NOOUTERTHIS)) == (MANDATED | NOOUTERTHIS))) {
+                        tree.sym = param.sym;
+                    }
+                }
+            }
+        }
+    }
+
 /* ************************************************************************
  * Traversal methods
  *************************************************************************/
 
     /** Visitor argument: The current environment.
@@ -1066,10 +1111,14 @@
             }
         } else {
             code.newLocal(v);
         }
         checkDimension(tree.pos(), v.type);
+        Type localType = v.erasure(types);
+        if (localType.requiresPreload(env.enclClass.sym)) {
+            poolWriter.enterPreloadClass((ClassSymbol) localType.tsym);
+        }
     }
 
     public void visitSkip(JCSkip tree) {
     }
 
@@ -2333,11 +2382,11 @@
 
         if (tree.name == names._class) {
             code.emitLdc((LoadableConstant)checkDimension(tree.pos(), tree.selected.type));
             result = items.makeStackItem(pt);
             return;
-       }
+        }
 
         Symbol ssym = TreeInfo.symbol(tree.selected);
 
         // Are we selecting via super?
         boolean selectSuper =
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java
@@ -49,13 +49,15 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.ArrayDeque;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.Map;
+import java.util.Set;
 
 import static com.sun.tools.javac.code.Kinds.Kind.TYP;
 import static com.sun.tools.javac.code.TypeTag.ARRAY;
 import static com.sun.tools.javac.code.TypeTag.CLASS;
 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Class;
@@ -92,10 +94,12 @@
     final SharedSignatureGenerator signatureGen;
 
     /** The inner classes to be written, as an ordered set (enclosing first). */
     LinkedHashSet<ClassSymbol> innerClasses = new LinkedHashSet<>();
 
+    Set<ClassSymbol> preloadClasses = new HashSet<>();
+
     /** The list of entries in the BootstrapMethods attribute. */
     Map<BsmKey, Integer> bootstrapMethods = new LinkedHashMap<>();
 
     public PoolWriter(Types types, Names names) {
         this.types = types;
@@ -230,10 +234,20 @@
             enterInner(c.owner.enclClass());
             innerClasses.add(c);
         }
     }
 
+    /** Enter a value class into the `preloadClasses' set.
+     */
+    void enterPreloadClass(ClassSymbol c) {
+        if (c.type.isCompound()) {
+            throw new AssertionError("Unexpected intersection type: " + c.type);
+        }
+        c.complete();
+        preloadClasses.add(c);
+    }
+
     /**
      * Create a new Utf8 entry representing a descriptor for given (member) symbol.
      */
     private Type descriptorType(Symbol s) {
         return s.kind == Kind.MTH ? s.externalType(types) : s.erasure(types);
@@ -510,9 +524,10 @@
         return signatureGen.toName();
     }
 
     void reset() {
         innerClasses.clear();
+        preloadClasses.clear();
         bootstrapMethods.clear();
         pool.reset();
     }
 }
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java
@@ -35,10 +35,11 @@
 import com.sun.tools.javac.util.*;
 
 import static com.sun.tools.javac.code.Kinds.Kind.MTH;
 import static com.sun.tools.javac.code.TypeTag.*;
 import static com.sun.tools.javac.jvm.ByteCodes.*;
+import static com.sun.tools.javac.tree.JCTree.Tag.LITERAL;
 import static com.sun.tools.javac.tree.JCTree.Tag.PLUS;
 import com.sun.tools.javac.jvm.Items.*;
 
 import java.util.HashMap;
 import java.util.Map;
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java
@@ -212,10 +212,16 @@
      */
     public boolean hasSealedClasses() {
         return compareTo(JDK1_15) >= 0;
     }
 
+    /** Does the target VM support value classes
+     */
+    public boolean hasValueClasses() {
+        return compareTo(JDK1_19) >= 0;
+    }
+
     /** Is the ACC_STRICT bit redundant and obsolete
      */
     public boolean obsoleteAccStrict() {
         return compareTo(JDK1_17) >= 0;
     }
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java
@@ -28,21 +28,22 @@
 import java.io.*;
 import java.nio.file.FileSystemNotFoundException;
 import java.nio.file.InvalidPathException;
 import java.nio.file.ReadOnlyFileSystemException;
 import java.util.Collection;
-import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.Map;
 import java.util.MissingResourceException;
 import java.util.Queue;
 import java.util.ResourceBundle;
 import java.util.Set;
 import java.util.function.Function;
+import java.util.function.ToIntFunction;
 
 import javax.annotation.processing.Processor;
 import javax.lang.model.SourceVersion;
 import javax.lang.model.element.ElementVisitor;
 import javax.tools.DiagnosticListener;
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
@@ -35,13 +35,15 @@
 import com.sun.source.tree.CaseTree;
 import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
 import com.sun.source.tree.ModuleTree.ModuleKind;
 
 import com.sun.tools.javac.code.*;
+import com.sun.tools.javac.code.Flags.Flag;
 import com.sun.tools.javac.code.Source.Feature;
 import com.sun.tools.javac.file.PathFileObject;
 import com.sun.tools.javac.parser.Tokens.*;
+import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle;
 import com.sun.tools.javac.resources.CompilerProperties.Errors;
 import com.sun.tools.javac.resources.CompilerProperties.Fragments;
 import com.sun.tools.javac.resources.CompilerProperties.Warnings;
 import com.sun.tools.javac.tree.*;
 import com.sun.tools.javac.tree.JCTree.*;
@@ -49,18 +51,20 @@
 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
 import com.sun.tools.javac.util.JCDiagnostic.Error;
 import com.sun.tools.javac.util.JCDiagnostic.Fragment;
 import com.sun.tools.javac.util.List;
 
+import static com.sun.tools.javac.code.Flags.asFlagSet;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.*;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.ASSERT;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.CASE;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.CATCH;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.EQ;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.GT;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.IMPORT;
 import static com.sun.tools.javac.parser.Tokens.TokenKind.LT;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.SYNCHRONIZED;
 import static com.sun.tools.javac.tree.JCTree.Tag.*;
 import static com.sun.tools.javac.resources.CompilerProperties.Fragments.ImplicitAndExplicitNotAllowed;
 import static com.sun.tools.javac.resources.CompilerProperties.Fragments.VarAndExplicitNotAllowed;
 import static com.sun.tools.javac.resources.CompilerProperties.Fragments.VarAndImplicitNotAllowed;
 import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition;
@@ -189,10 +193,12 @@
         this.errorTree = F.Erroneous();
         this.endPosTable = newEndPosTable(keepEndPositions);
         this.allowYieldStatement = Feature.SWITCH_EXPRESSION.allowedInSource(source);
         this.allowRecords = Feature.RECORDS.allowedInSource(source);
         this.allowSealedTypes = Feature.SEALED_CLASSES.allowedInSource(source);
+        this.allowValueClasses = (!preview.isPreview(Feature.VALUE_CLASSES) || preview.isEnabled()) &&
+                Feature.VALUE_CLASSES.allowedInSource(source);
     }
 
     /** Construct a parser from an existing parser, with minimal overhead.
      */
     @SuppressWarnings("this-escape")
@@ -212,10 +218,11 @@
         this.errorTree = F.Erroneous();
         this.endPosTable = newEndPosTable(false);
         this.allowYieldStatement = Feature.SWITCH_EXPRESSION.allowedInSource(source);
         this.allowRecords = Feature.RECORDS.allowedInSource(source);
         this.allowSealedTypes = Feature.SEALED_CLASSES.allowedInSource(source);
+        this.allowValueClasses = parser.allowValueClasses;
     }
 
     protected AbstractEndPosTable newEndPosTable(boolean keepEndPositions) {
         return  keepEndPositions
                 ? new SimpleEndPosTable(this)
@@ -249,10 +256,14 @@
 
     /** Switch: are records allowed in this source level?
      */
     boolean allowRecords;
 
+    /** Switch: are value classes allowed in this source level?
+     */
+    boolean allowValueClasses;
+
     /** Switch: are sealed types allowed in this source level?
      */
     boolean allowSealedTypes;
 
     /** The type of the method receiver, as specified by a first "this" parameter.
@@ -1572,12 +1583,12 @@
                             // Don't return here -- error recovery attempt
                             illegal(annos.head.pos);
                         }
                         break loop;
                     case LT:
-                        if (!isMode(TYPE) && isUnboundMemberRef()) {
-                            //this is an unbound method reference whose qualifier
+                        if (!isMode(TYPE) && isParameterizedTypePrefix()) {
+                            //this is either an unbound method reference whose qualifier
                             //is a generic type i.e. A<S>::m
                             int pos1 = token.pos;
                             accept(LT);
                             ListBuffer<JCExpression> args = new ListBuffer<>();
                             args.append(typeArgument());
@@ -1821,11 +1832,11 @@
      * If we see an identifier followed by a '&lt;' it could be an unbound
      * method reference or a binary expression. To disambiguate, look for a
      * matching '&gt;' and see if the subsequent terminal is either '.' or '::'.
      */
     @SuppressWarnings("fallthrough")
-    boolean isUnboundMemberRef() {
+    boolean isParameterizedTypePrefix() {
         int pos = 0, depth = 0;
         outer: for (Token t = S.token(pos) ; ; t = S.token(++pos)) {
             switch (t.kind) {
                 case IDENTIFIER: case UNDERSCORE: case QUES: case EXTENDS: case SUPER:
                 case DOT: case RBRACKET: case LBRACKET: case COMMA:
@@ -2490,15 +2501,18 @@
     }
 
     /** Creator = [Annotations] Qualident [TypeArguments] ( ArrayCreatorRest | ClassCreatorRest )
      */
     JCExpression creator(int newpos, List<JCExpression> typeArgs) {
-        List<JCAnnotation> newAnnotations = typeAnnotationsOpt();
-
+        final JCModifiers mods = modifiersOpt();
+        List<JCAnnotation> newAnnotations = mods.annotations;
         switch (token.kind) {
         case BYTE: case SHORT: case CHAR: case INT: case LONG: case FLOAT:
         case DOUBLE: case BOOLEAN:
+            if (mods.flags != 0) {
+                log.error(token.pos, Errors.ModNotAllowedHere(asFlagSet(mods.flags)));
+            }
             if (typeArgs == null) {
                 if (newAnnotations.isEmpty()) {
                     return arrayCreatorRest(newpos, basicType());
                 } else {
                     return arrayCreatorRest(newpos, toP(F.at(newAnnotations.head.pos).AnnotatedType(newAnnotations, basicType())));
@@ -2567,11 +2581,15 @@
         } else if (token.kind == LPAREN) {
             // handle type annotations for instantiations and anonymous classes
             if (newAnnotations.nonEmpty()) {
                 t = insertAnnotationsToMostInner(t, newAnnotations, false);
             }
-            return classCreatorRest(newpos, null, typeArgs, t);
+            JCNewClass newClass = classCreatorRest(newpos, null, typeArgs, t, mods.flags);
+            if ((newClass.def == null) && (mods.flags != 0)) {
+                log.error(newClass.pos, Errors.ModNotAllowedHere(asFlagSet(mods.flags)));
+            }
+            return newClass;
         } else {
             setErrorEndPos(token.pos);
             reportSyntaxError(token.pos, Errors.Expected2(LPAREN, LBRACKET));
             t = toP(F.at(newpos).NewClass(null, typeArgs, t, List.nil(), null));
             return toP(F.at(newpos).Erroneous(List.<JCTree>of(t)));
@@ -2592,11 +2610,11 @@
         if (token.kind == LT) {
             int prevmode = mode;
             t = typeArguments(t, true);
             setMode(prevmode);
         }
-        return classCreatorRest(newpos, encl, typeArgs, t);
+        return classCreatorRest(newpos, encl, typeArgs, t, 0);
     }
 
     /** ArrayCreatorRest = [Annotations] "[" ( "]" BracketsOpt ArrayInitializer
      *                         | Expression "]" {[Annotations]  "[" Expression "]"} BracketsOpt )
      */
@@ -2670,18 +2688,19 @@
     /** ClassCreatorRest = Arguments [ClassBody]
      */
     JCNewClass classCreatorRest(int newpos,
                                   JCExpression encl,
                                   List<JCExpression> typeArgs,
-                                  JCExpression t)
+                                  JCExpression t,
+                                  long flags)
     {
         List<JCExpression> args = arguments();
         JCClassDecl body = null;
         if (token.kind == LBRACE) {
             int pos = token.pos;
             List<JCTree> defs = classInterfaceOrRecordBody(names.empty, false, false);
-            JCModifiers mods = F.at(Position.NOPOS).Modifiers(0);
+            JCModifiers mods = F.at(Position.NOPOS).Modifiers(flags);
             body = toP(F.at(pos).AnonymousClassDef(mods, defs));
         }
         return toP(F.at(newpos).NewClass(encl, typeArgs, t, args, body));
     }
 
@@ -2912,10 +2931,15 @@
                     nextToken();
                     return List.of(classOrRecordOrInterfaceOrEnumDeclaration(modifiersOpt(), token.docComment()));
                 }
             }
         }
+        if ((isValueModifier()) && allowValueClasses) {
+            checkSourceLevel(Feature.VALUE_CLASSES);
+            dc = token.docComment();
+            return List.of(classOrRecordOrInterfaceOrEnumDeclaration(modifiersOpt(), dc));
+        }
         if (isRecordStart() && allowRecords) {
             dc = token.docComment();
             return List.of(recordDeclaration(F.at(pos).Modifiers(0), dc));
         } else {
             Token prevToken = token;
@@ -3407,11 +3431,14 @@
         if (token.kind == FINAL || token.kind == MONKEYS_AT) {
             return variableDeclarators(optFinal(0), parseType(true), stats, true).toList();
         } else {
             JCExpression t = term(EXPR | TYPE);
             if (wasTypeMode() && LAX_IDENTIFIER.test(token.kind)) {
-                return variableDeclarators(modifiersOpt(), t, stats, true).toList();
+                pos = token.pos;
+                JCModifiers mods = F.at(Position.NOPOS).Modifiers(0);
+                F.at(pos);
+                return variableDeclarators(mods, t, stats, true).toList();
             } else if (wasTypeMode() && token.kind == COLON) {
                 log.error(DiagnosticFlag.SYNTAX, pos, Errors.BadInitializer("for-loop"));
                 return List.of((JCStatement)F.at(pos).VarDef(modifiersOpt(), names.error, t, null));
             } else {
                 return moreStatementExpressions(pos, t, stats).toList();
@@ -3504,10 +3531,15 @@
                 if (isSealedClassStart(false)) {
                     checkSourceLevel(Feature.SEALED_CLASSES);
                     flag = Flags.SEALED;
                     break;
                 }
+                if (isValueModifier()) {
+                    checkSourceLevel(Feature.VALUE_CLASSES);
+                    flag = Flags.VALUE_CLASS;
+                    break;
+                }
                 break loop;
             }
             default: break loop;
             }
             if ((flags & flag) != 0) log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.RepeatedModifier);
@@ -3770,10 +3802,17 @@
                 return Source.JDK14;
             } else if (shouldWarn) {
                 log.warning(pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK14));
             }
         }
+        if (name == names.value) {
+            if (allowValueClasses) {
+                return Source.JDK22;
+            } else if (shouldWarn) {
+                log.warning(pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK22));
+            }
+        }
         if (name == names.sealed) {
             if (allowSealedTypes) {
                 return Source.JDK15;
             } else if (shouldWarn) {
                 log.warning(pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK15));
@@ -4836,10 +4875,36 @@
             }
         }
         return false;
     }
 
+    protected boolean isValueModifier() {
+        if (token.kind == IDENTIFIER && token.name() == names.value) {
+            boolean isValueModifier = false;
+            Token next = S.token(1);
+            switch (next.kind) {
+                case PRIVATE: case PROTECTED: case PUBLIC: case STATIC: case TRANSIENT:
+                case FINAL: case ABSTRACT: case NATIVE: case VOLATILE: case SYNCHRONIZED:
+                case STRICTFP: case MONKEYS_AT: case DEFAULT: case BYTE: case SHORT:
+                case CHAR: case INT: case LONG: case FLOAT: case DOUBLE: case BOOLEAN: case VOID:
+                case CLASS: case INTERFACE: case ENUM:
+                    isValueModifier = true;
+                    break;
+                case IDENTIFIER: // value record R || value value || new value Comparable() {} ??
+                    if (next.name() == names.record || next.name() == names.value
+                            || (mode & EXPR) != 0)
+                        isValueModifier = true;
+                    break;
+            }
+            if (isValueModifier) {
+                checkSourceLevel(Feature.VALUE_CLASSES);
+                return true;
+            }
+        }
+        return false;
+    }
+
     protected boolean isSealedClassStart(boolean local) {
         if (token.name() == names.sealed) {
             Token next = S.token(1);
             if (allowedAfterSealedOrNonSealed(next, local, false)) {
                 checkSourceLevel(Feature.SEALED_CLASSES);
@@ -4863,11 +4928,13 @@
                 case MONKEYS_AT -> {
                     Token afterNext = S.token(2);
                     yield afterNext.kind != INTERFACE || currentIsNonSealed;
                 }
                 case PUBLIC, PROTECTED, PRIVATE, ABSTRACT, STATIC, FINAL, STRICTFP, CLASS, INTERFACE, ENUM -> true;
-                case IDENTIFIER -> isNonSealedIdentifier(next, currentIsNonSealed ? 3 : 1) || next.name() == names.sealed;
+                case IDENTIFIER -> isNonSealedIdentifier(next, currentIsNonSealed ? 3 : 1) ||
+                        next.name() == names.sealed ||
+                        allowValueClasses && next.name() == names.value;
                 default -> false;
             };
     }
 
     /** MethodDeclaratorRest =
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java
@@ -1338,11 +1338,11 @@
                 for (ClassSymbol cs : symtab.getAllClasses()) {
                     if (cs.classfile != null || cs.kind == ERR) {
                         Kinds.Kind symKind = cs.kind;
                         cs.reset();
                         if (symKind == ERR) {
-                            cs.type = new ClassType(cs.type.getEnclosingType(), null, cs);
+                            cs.type = new ClassType(cs.type.getEnclosingType(), null, cs, List.nil());
                         }
                         if (cs.isCompleted()) {
                             cs.completer = initialCompleter;
                         }
                     }
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
@@ -760,11 +760,11 @@
 # 0: symbol
 compiler.err.import.requires.canonical=\
     import requires canonical name for {0}
 
 compiler.err.improperly.formed.type.param.missing=\
-    improperly formed type, some parameters are missing
+    improperly formed type, some parameters are missing or misplaced
 
 compiler.err.improperly.formed.type.inner.raw.param=\
     improperly formed type, type arguments given on a raw type
 
 # 0: type, 1: type
@@ -2720,10 +2720,13 @@
     reference
 
 compiler.misc.type.req.exact=\
     class or interface without bounds
 
+compiler.misc.type.req.identity=\
+    a type with identity
+
 # 0: type
 compiler.misc.type.parameter=\
     type parameter {0}
 
 #####
@@ -3731,10 +3734,13 @@
     cannot resolve modules
 
 compiler.misc.bad.requires.flag=\
     bad requires flag: {0}
 
+compiler.misc.bad.access.flags=\
+    bad access flags combination: {0}
+
 # 0: string
 compiler.err.invalid.module.specifier=\
     module specifier not allowed: {0}
 
 # 0: symbol
@@ -4064,10 +4070,29 @@
     (preview language features are only supported for release {1})
 
 compiler.err.preview.without.source.or.release=\
     --enable-preview must be used with either -source or --release
 
+compiler.misc.feature.value.classes=\
+    value classes
+
+# 0: type, 1: type
+compiler.err.value.type.has.identity.super.type=\
+    The identity type {1} cannot be a supertype of the value type {0}
+
+# 0: symbol, 1: type
+compiler.err.concrete.supertype.for.value.class=\
+    The concrete class {1} is not allowed to be a super class of the value class {0} either directly or indirectly
+
+# 0: symbol, 1: symbol, 2: type
+compiler.err.super.class.method.cannot.be.synchronized=\
+    The method {0} in the super class {2} of the value class {1} is synchronized. This is disallowed
+
+# 0: symbol or name
+compiler.err.cant.ref.after.ctor.called=\
+    cannot reference {0} after supertype constructor has been called
+
 # 0: symbol
 compiler.err.deconstruction.pattern.only.records=\
     deconstruction patterns can only be applied to records, {0} is not a record
 
 compiler.err.deconstruction.pattern.var.not.allowed=\
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties
@@ -220,10 +220,13 @@
     Warn about possible lossy conversions in compound assignment.
 
 javac.opt.Xlint.desc.module=\
     Warn about module system related issues.
 
+javac.opt.Xlint.desc.migration=\
+    Warn about issues related to migration of JDK classes.
+
 javac.opt.Xlint.desc.opens=\
     Warn about issues regarding module opens.
 
 javac.opt.Xlint.desc.options=\
     Warn about issues relating to use of command line options.
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java
@@ -180,10 +180,45 @@
             default:
                 return false;
         }
     }
 
+    /** Check if the given tree is an explicit reference to the 'this' instance of the
+     *  class currently being compiled. This is true if tree is:
+     *  - An unqualified 'this' identifier
+     *  - A 'super' identifier qualified by a class name whose type is 'currentClass' or a supertype
+     *  - A 'this' identifier qualified by a class name whose type is 'currentClass' or a supertype
+     *    but also NOT an enclosing outer class of 'currentClass'.
+     */
+    public static boolean isExplicitThisReference(Types types, Type.ClassType currentClass, JCTree tree) {
+        switch (tree.getTag()) {
+            case PARENS:
+                return isExplicitThisReference(types, currentClass, skipParens(tree));
+            case IDENT: {
+                JCIdent ident = (JCIdent)tree;
+                Names names = ident.name.table.names;
+                return ident.name == names._this || ident.name == names._super;
+            }
+            case SELECT: {
+                JCFieldAccess select = (JCFieldAccess)tree;
+                Type selectedType = types.erasure(select.selected.type);
+                if (!selectedType.hasTag(TypeTag.CLASS))
+                    return false;
+                Symbol.ClassSymbol currentClassSym = (Symbol.ClassSymbol)((Type.ClassType)types.erasure(currentClass)).tsym;
+                Symbol.ClassSymbol selectedClassSym = (Symbol.ClassSymbol)((Type.ClassType)selectedType).tsym;
+                Names names = select.name.table.names;
+                return currentClassSym.isSubClass(selectedClassSym, types) &&
+                        (select.name == names._super ||
+                                (select.name == names._this &&
+                                        (currentClassSym == selectedClassSym ||
+                                                !currentClassSym.isEnclosedBy(selectedClassSym))));
+            }
+            default:
+                return false;
+        }
+    }
+
     /** Is this a call to super?
      */
     public static boolean isSuperCall(JCTree tree) {
         Name name = calledMethodName(tree);
         if (name != null) {
diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java
@@ -105,17 +105,19 @@
     public final Name value;
     public final Name valueOf;
     public final Name values;
     public final Name readResolve;
     public final Name readObject;
+    public final Name isValueObject;
 
     // class names
     public final Name java_io_Serializable;
     public final Name java_lang_Class;
     public final Name java_lang_Cloneable;
     public final Name java_lang_Enum;
     public final Name java_lang_Object;
+    public final Name java_lang_System;
 
     // names of builtin classes
     public final Name Array;
     public final Name Bound;
     public final Name Method;
@@ -149,10 +151,11 @@
     public final Name MethodParameters;
     public final Name Module;
     public final Name ModuleResolution;
     public final Name NestHost;
     public final Name NestMembers;
+    public final Name Preload;
     public final Name Record;
     public final Name RuntimeInvisibleAnnotations;
     public final Name RuntimeInvisibleParameterAnnotations;
     public final Name RuntimeInvisibleTypeAnnotations;
     public final Name RuntimeVisibleAnnotations;
@@ -203,10 +206,14 @@
 
     // string concat
     public final Name makeConcat;
     public final Name makeConcatWithConstants;
 
+    // values
+    public final Name dollarValue;
+
+
     // record related
     // members of java.lang.runtime.ObjectMethods
     public final Name bootstrap;
 
     public final Name record;
@@ -300,18 +307,20 @@
         value = fromString("value");
         valueOf = fromString("valueOf");
         values = fromString("values");
         readResolve = fromString("readResolve");
         readObject = fromString("readObject");
+        isValueObject = fromString("isValueObject");
         dollarThis = fromString("$this");
 
         // class names
         java_io_Serializable = fromString("java.io.Serializable");
         java_lang_Class = fromString("java.lang.Class");
         java_lang_Cloneable = fromString("java.lang.Cloneable");
         java_lang_Enum = fromString("java.lang.Enum");
         java_lang_Object = fromString("java.lang.Object");
+        java_lang_System = fromString("java.lang.System");
 
         // names of builtin classes
         Array = fromString("Array");
         Bound = fromString("Bound");
         Method = fromString("Method");
@@ -345,10 +354,11 @@
         MethodParameters = fromString("MethodParameters");
         Module = fromString("Module");
         ModuleResolution = fromString("ModuleResolution");
         NestHost = fromString("NestHost");
         NestMembers = fromString("NestMembers");
+        Preload = fromString("Preload");
         Record = fromString("Record");
         RuntimeInvisibleAnnotations = fromString("RuntimeInvisibleAnnotations");
         RuntimeInvisibleParameterAnnotations = fromString("RuntimeInvisibleParameterAnnotations");
         RuntimeInvisibleTypeAnnotations = fromString("RuntimeInvisibleTypeAnnotations");
         RuntimeVisibleAnnotations = fromString("RuntimeVisibleAnnotations");
@@ -398,10 +408,12 @@
 
         // string concat
         makeConcat = fromString("makeConcat");
         makeConcatWithConstants = fromString("makeConcatWithConstants");
 
+        dollarValue = fromString("$value");
+
         bootstrap = fromString("bootstrap");
         record = fromString("record");
         non = fromString("non");
 
         serialPersistentFields = fromString("serialPersistentFields");
@@ -448,10 +460,14 @@
 
     public UnsharedNameTable newUnsharedNameTable() {
         return UnsharedNameTable.create(this);
     }
 
+    public boolean isInit(Name name) {
+        return name == init;
+    }
+
     public void dispose() {
         table.dispose();
     }
 
     public Name fromChars(char[] cs, int start, int len) {
diff a/src/jdk.compiler/share/classes/module-info.java b/src/jdk.compiler/share/classes/module-info.java
--- a/src/jdk.compiler/share/classes/module-info.java
+++ b/src/jdk.compiler/share/classes/module-info.java
@@ -233,19 +233,21 @@
         jdk.javadoc,
         jdk.jshell;
     exports com.sun.tools.javac.resources to
         jdk.jshell;
     exports com.sun.tools.javac.code to
+        jdk.jdeps,
         jdk.javadoc,
         jdk.jshell;
     exports com.sun.tools.javac.comp to
         jdk.javadoc,
         jdk.jshell;
     exports com.sun.tools.javac.file to
         jdk.jdeps,
         jdk.javadoc;
     exports com.sun.tools.javac.jvm to
+        jdk.jdeps,
         jdk.javadoc;
     exports com.sun.tools.javac.main to
         jdk.javadoc,
         jdk.jshell;
     exports com.sun.tools.javac.model to
diff a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/interpreter/Bytecodes.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/interpreter/Bytecodes.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/interpreter/Bytecodes.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/interpreter/Bytecodes.java
@@ -239,54 +239,58 @@
   public static final int _ifnull               = 198; // 0xc6
   public static final int _ifnonnull            = 199; // 0xc7
   public static final int _goto_w               = 200; // 0xc8
   public static final int _jsr_w                = 201; // 0xc9
   public static final int _breakpoint           = 202; // 0xca
+  public static final int _aconst_init          = 203; // 0xcb
+  public static final int _withfield            = 204; // 0xcc
 
-  public static final int number_of_java_codes  = 203;
+  public static final int number_of_java_codes  = 205;
 
   // JVM bytecodes
   public static final int _fast_agetfield       = number_of_java_codes;
-  public static final int _fast_bgetfield       = 204;
-  public static final int _fast_cgetfield       = 205;
-  public static final int _fast_dgetfield       = 206;
-  public static final int _fast_fgetfield       = 207;
-  public static final int _fast_igetfield       = 208;
-  public static final int _fast_lgetfield       = 209;
-  public static final int _fast_sgetfield       = 210;
-  public static final int _fast_aputfield       = 211;
-  public static final int _fast_bputfield       = 212;
-  public static final int _fast_zputfield       = 213;
-  public static final int _fast_cputfield       = 214;
-  public static final int _fast_dputfield       = 215;
-  public static final int _fast_fputfield       = 216;
-  public static final int _fast_iputfield       = 217;
-  public static final int _fast_lputfield       = 218;
-  public static final int _fast_sputfield       = 219;
-  public static final int _fast_aload_0         = 220;
-  public static final int _fast_iaccess_0       = 221;
-  public static final int _fast_aaccess_0       = 222;
-  public static final int _fast_faccess_0       = 223;
-  public static final int _fast_iload           = 224;
-  public static final int _fast_iload2          = 225;
-  public static final int _fast_icaload         = 226;
-  public static final int _fast_invokevfinal    = 227;
-  public static final int _fast_linearswitch    = 228;
-  public static final int _fast_binaryswitch    = 229;
-  public static final int _fast_aldc            = 230;
-  public static final int _fast_aldc_w          = 231;
-  public static final int _return_register_finalizer = 232;
-  public static final int _invokehandle         = 233;
+    public static final int _fast_qgetfield       = 206;
+  public static final int _fast_bgetfield       = 207;
+  public static final int _fast_cgetfield       = 208;
+  public static final int _fast_dgetfield       = 209;
+  public static final int _fast_fgetfield       = 210;
+  public static final int _fast_igetfield       = 211;
+  public static final int _fast_lgetfield       = 212;
+  public static final int _fast_sgetfield       = 213;
+  public static final int _fast_aputfield       = 214;
+  public static final int _fast_qputfield       = 215;
+  public static final int _fast_bputfield       = 216;
+  public static final int _fast_zputfield       = 217;
+  public static final int _fast_cputfield       = 218;
+  public static final int _fast_dputfield       = 219;
+  public static final int _fast_fputfield       = 220;
+  public static final int _fast_iputfield       = 221;
+  public static final int _fast_lputfield       = 222;
+  public static final int _fast_sputfield       = 223;
+  public static final int _fast_aload_0         = 224;
+  public static final int _fast_iaccess_0       = 225;
+  public static final int _fast_aaccess_0       = 226;
+  public static final int _fast_faccess_0       = 227;
+  public static final int _fast_iload           = 228;
+  public static final int _fast_iload2          = 229;
+  public static final int _fast_icaload         = 230;
+  public static final int _fast_invokevfinal    = 231;
+  public static final int _fast_linearswitch    = 232;
+  public static final int _fast_binaryswitch    = 233;
+  public static final int _fast_aldc            = 234;
+  public static final int _fast_aldc_w          = 235;
+  public static final int _return_register_finalizer = 236;
+  public static final int _invokehandle         = 237;
 
   // Bytecodes rewritten at CDS dump time
-  public static final int _nofast_getfield      = 234;
-  public static final int _nofast_putfield      = 235;
-  public static final int _nofast_aload_0       = 236;
-  public static final int _nofast_iload         = 237;
-  public static final int _shouldnotreachhere   = 238; // For debugging
+  public static final int _nofast_getfield      = 238;
+  public static final int _nofast_putfield      = 239;
+  public static final int _nofast_aload_0       = 240;
+  public static final int _nofast_iload         = 241;
+  public static final int _shouldnotreachhere   = 242; // For debugging
 
-  public static final int number_of_codes       = 239;
+  public static final int number_of_codes       = 243;
 
   // Flag bits derived from format strings, can_trap, can_rewrite, etc.:
   // semantic flags:
   static final int  _bc_can_trap      = 1<<0;     // bytecode execution can trap or block
   static final int  _bc_can_rewrite   = 1<<1;     // bytecode execution has an alternate form
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
@@ -113,21 +113,23 @@
 
     populateMetadataTypeArray(db);
   }
 
   private static void populateMetadataTypeArray(TypeDataBase db) {
-    metadataTypeArray = new Type[9];
+    metadataTypeArray = new Type[11];
 
     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("FlatArrayKlass");
+    metadataTypeArray[10] = db.lookupType("InlineKlass");
   }
 
   public FileMapHeader getHeader() {
     return headerObj;
   }
diff a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/FlatArrayKlass.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/FlatArrayKlass.java
--- /dev/null
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/FlatArrayKlass.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2021, 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.oops;
+
+import java.io.*;
+import java.util.*;
+import sun.jvm.hotspot.debugger.*;
+import sun.jvm.hotspot.runtime.*;
+import sun.jvm.hotspot.types.*;
+import sun.jvm.hotspot.utilities.*;
+import sun.jvm.hotspot.utilities.Observable;
+import sun.jvm.hotspot.utilities.Observer;
+
+// FlatArrayKlass is a proxy for FlatArrayKlass in the JVM
+
+public class FlatArrayKlass extends ArrayKlass {
+  static {
+    VM.registerVMInitializedObserver(new Observer() {
+        public void update(Observable o, Object data) {
+          initialize(VM.getVM().getTypeDataBase());
+        }
+      });
+  }
+
+  private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
+    Type t = db.lookupType("FlatArrayKlass");
+    // TODO: implement similar features as in ObjArrayKlass
+  }
+
+  public FlatArrayKlass(Address addr) {
+    super(addr);
+  }
+
+  public void printValueOn(PrintStream tty) {
+    tty.print("FlatArrayKlass");
+  }
+}
diff a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InlineKlass.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InlineKlass.java
--- /dev/null
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InlineKlass.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2021, 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.oops;
+
+import java.io.*;
+import java.util.*;
+import sun.jvm.hotspot.debugger.*;
+import sun.jvm.hotspot.memory.*;
+import sun.jvm.hotspot.runtime.*;
+import sun.jvm.hotspot.types.*;
+import sun.jvm.hotspot.utilities.*;
+import sun.jvm.hotspot.utilities.Observable;
+import sun.jvm.hotspot.utilities.Observer;
+
+// An InstanceKlass is the VM level representation of a Java class.
+
+public class InlineKlass extends InstanceKlass {
+  static {
+    VM.registerVMInitializedObserver(new Observer() {
+        public void update(Observable o, Object data) {
+          initialize(VM.getVM().getTypeDataBase());
+        }
+      });
+  }
+
+  private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
+    // Just make sure it's there for now
+    Type type = db.lookupType("InlineKlass");
+  }
+
+  public InlineKlass(Address addr) {
+    super(addr);
+  }
+}
diff a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Metadata.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Metadata.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Metadata.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Metadata.java
@@ -62,12 +62,14 @@
     metadataConstructor.addMapping("InstanceKlass", InstanceKlass.class);
     metadataConstructor.addMapping("InstanceMirrorKlass", InstanceMirrorKlass.class);
     metadataConstructor.addMapping("InstanceRefKlass", InstanceRefKlass.class);
     metadataConstructor.addMapping("InstanceClassLoaderKlass", InstanceClassLoaderKlass.class);
     metadataConstructor.addMapping("InstanceStackChunkKlass", InstanceStackChunkKlass.class);
+    metadataConstructor.addMapping("InlineKlass", InlineKlass.class);
     metadataConstructor.addMapping("TypeArrayKlass", TypeArrayKlass.class);
     metadataConstructor.addMapping("ObjArrayKlass", ObjArrayKlass.class);
+    metadataConstructor.addMapping("FlatArrayKlass", FlatArrayKlass.class);
     metadataConstructor.addMapping("Method", Method.class);
     metadataConstructor.addMapping("MethodData", MethodData.class);
     metadataConstructor.addMapping("ConstMethod", ConstMethod.class);
     metadataConstructor.addMapping("ConstantPool", ConstantPool.class);
     metadataConstructor.addMapping("ConstantPoolCache", ConstantPoolCache.class);
diff a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Method.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Method.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Method.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Method.java
@@ -66,10 +66,11 @@
     fromCompiledCodeEntryPoint = type.getAddressField("_from_compiled_code_entry_point");
     interpreterEntry           = type.getAddressField("_from_interpreted_entry");
     */
 
     objectInitializerName = null;
+    valueFactoryName = null;
     classInitializerName = null;
   }
 
   public Method(Address addr) {
     super(addr);
@@ -95,17 +96,24 @@
 
 
   // constant method names - <init>, <clinit>
   // Initialized lazily to avoid initialization ordering dependencies between ArrayKlass and String
   private static String objectInitializerName;
+  private static String valueFactoryName;
   private static String classInitializerName;
   private static String objectInitializerName() {
     if (objectInitializerName == null) {
       objectInitializerName = "<init>";
     }
     return objectInitializerName;
   }
+  private static String valueFactoryName() {
+    if (valueFactoryName == null) {
+      valueFactoryName = "<vnew>";
+    }
+    return classInitializerName;
+  }
   private static String classInitializerName() {
     if (classInitializerName == null) {
       classInitializerName = "<clinit>";
     }
     return classInitializerName;
@@ -251,11 +259,11 @@
   public boolean isAbstract()       { return getAccessFlagsObj().isAbstract();                         }
   public boolean isStrict()         { return getAccessFlagsObj().isStrict();                           }
   public boolean isSynthetic()      { return getAccessFlagsObj().isSynthetic();                        }
 
   public boolean isConstructor() {
-     return (!isStatic()) && getName().equals(objectInitializerName());
+     return (!isStatic()) && (getName().equals(objectInitializerName()) || getName().equals(valueFactoryName()));
   }
 
   public boolean isStaticInitializer() {
      return isStatic() && getName().equals(classInitializerName());
   }
diff a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java
@@ -369,15 +369,16 @@
         }
     }
     protected void writeInterfaces() throws IOException {
         KlassArray interfaces = klass.getLocalInterfaces();
         final int len = interfaces.length();
+        int nb_interfaces = len;
 
-        if (DEBUG) debugMessage("number of interfaces = " + len);
+        if (DEBUG) debugMessage("number of interfaces = " + nb_interfaces);
 
         // write interfaces count
-        dos.writeShort((short) len);
+        dos.writeShort((short) nb_interfaces);
         for (int i = 0; i < len; i++) {
            Klass k = interfaces.getAt(i);
            Short index = classToIndex.get(k.getName().asString());
            dos.writeShort(index.shortValue());
            if (DEBUG) debugMessage("\t" + index);
diff a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ObjectReader.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ObjectReader.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ObjectReader.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ObjectReader.java
@@ -640,11 +640,11 @@
    }
 
    public Object readMethodOrConstructor(sun.jvm.hotspot.oops.Method m)
                      throws NoSuchMethodException, ClassNotFoundException {
       String name = m.getName().asString();
-      if (name.equals("<init>")) {
+      if (name.equals("<init>") || name.equals("<vnew>")) {
          return readConstructor(m);
       } else {
          return readMethod(m);
       }
    }
diff a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotMethodData.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotMethodData.java
--- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotMethodData.java
+++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotMethodData.java
@@ -103,10 +103,13 @@
             new ArgInfoData(this, config.dataLayoutArgInfoDataTag),
             new UnknownProfileData(this, config.dataLayoutCallTypeDataTag),
             new VirtualCallTypeData(this, config.dataLayoutVirtualCallTypeDataTag),
             new UnknownProfileData(this, config.dataLayoutParametersTypeDataTag),
             new UnknownProfileData(this, config.dataLayoutSpeculativeTrapDataTag),
+            new UnknownProfileData(this, config.dataLayoutArrayStoreDataTag),
+            new UnknownProfileData(this, config.dataLayoutArrayLoadDataTag),
+            new UnknownProfileData(this, config.dataLayoutACmpDataTag),
         };
         // @formatter:on
 
         private boolean checkAccessorTags() {
             int expectedTag = 0;
diff a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotMethodDataAccessor.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotMethodDataAccessor.java
--- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotMethodDataAccessor.java
+++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotMethodDataAccessor.java
@@ -55,11 +55,11 @@
         return tag;
     }
 
     static int readTag(HotSpotVMConfig config, HotSpotMethodData data, int position) {
         final int tag = data.readUnsignedByte(position, config.dataLayoutTagOffset);
-        assert tag >= config.dataLayoutNoTag && tag <= config.dataLayoutSpeculativeTrapDataTag : "profile data tag out of bounds: " + tag;
+        assert tag >= config.dataLayoutNoTag && tag <= config.dataLayoutACmpDataTag : "profile data tag out of bounds: " + tag;
         return tag;
     }
 
     /**
      * Returns the BCI stored in the LayoutData header.
diff a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotModifiers.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotModifiers.java
--- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotModifiers.java
+++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotModifiers.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 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.
@@ -20,10 +20,12 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
 package jdk.vm.ci.hotspot;
 
+import jdk.internal.misc.PreviewFeatures;
+
 import static java.lang.reflect.Modifier.ABSTRACT;
 import static java.lang.reflect.Modifier.FINAL;
 import static java.lang.reflect.Modifier.INTERFACE;
 import static java.lang.reflect.Modifier.NATIVE;
 import static java.lang.reflect.Modifier.PRIVATE;
@@ -51,11 +53,12 @@
     public static final int BRIDGE     = config().jvmAccBridge;
     public static final int SYNTHETIC  = config().jvmAccSynthetic;
     // @formatter:on
 
     public static int jvmClassModifiers() {
-        return PUBLIC | FINAL | INTERFACE | ABSTRACT | ANNOTATION | ENUM | SYNTHETIC;
+        return PUBLIC | FINAL | INTERFACE | ABSTRACT | ANNOTATION | ENUM | SYNTHETIC |
+                (PreviewFeatures.isEnabled() ? 0x0020 : 0); // ACC_IDENTITY temp constant to avoid preview dependency
     }
 
     public static int jvmMethodModifiers() {
         return PUBLIC | PRIVATE | PROTECTED | STATIC | FINAL | SYNCHRONIZED | BRIDGE | VARARGS | NATIVE | ABSTRACT | STRICT | SYNTHETIC;
     }
diff a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfig.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfig.java
--- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfig.java
+++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfig.java
@@ -95,11 +95,11 @@
 
     final int instanceKlassInitStateOffset = getFieldOffset("InstanceKlass::_init_state", Integer.class, "InstanceKlass::ClassState");
     final int instanceKlassConstantsOffset = getFieldOffset("InstanceKlass::_constants", Integer.class, "ConstantPool*");
     final int instanceKlassFieldInfoStreamOffset = getFieldOffset("InstanceKlass::_fieldinfo_stream", Integer.class, "Array<u1>*");
     final int instanceKlassAnnotationsOffset = getFieldOffset("InstanceKlass::_annotations", Integer.class, "Annotations*");
-    final int instanceKlassMiscFlagsOffset = getFieldOffset("InstanceKlass::_misc_flags._flags", Integer.class, "u2");
+    final int instanceKlassMiscFlagsOffset = getFieldOffset("InstanceKlass::_misc_flags._flags", Integer.class, "u4");
     final int klassVtableStartOffset = getFieldValue("CompilerToVM::Data::Klass_vtable_start_offset", Integer.class, "int");
     final int klassVtableLengthOffset = getFieldValue("CompilerToVM::Data::Klass_vtable_length_offset", Integer.class, "int");
 
     final int instanceKlassStateLinked = getConstant("InstanceKlass::linked", Integer.class);
     final int instanceKlassStateFullyInitialized = getConstant("InstanceKlass::fully_initialized", Integer.class);
@@ -299,10 +299,13 @@
     final int dataLayoutArgInfoDataTag = getConstant("DataLayout::arg_info_data_tag", Integer.class);
     final int dataLayoutCallTypeDataTag = getConstant("DataLayout::call_type_data_tag", Integer.class);
     final int dataLayoutVirtualCallTypeDataTag = getConstant("DataLayout::virtual_call_type_data_tag", Integer.class);
     final int dataLayoutParametersTypeDataTag = getConstant("DataLayout::parameters_type_data_tag", Integer.class);
     final int dataLayoutSpeculativeTrapDataTag = getConstant("DataLayout::speculative_trap_data_tag", Integer.class);
+    final int dataLayoutArrayLoadDataTag = getConstant("DataLayout::array_load_data_tag", Integer.class);
+    final int dataLayoutArrayStoreDataTag = getConstant("DataLayout::array_store_data_tag", Integer.class);
+    final int dataLayoutACmpDataTag = getConstant("DataLayout::acmp_data_tag", Integer.class);
 
     final int bciProfileWidth = getFlag("BciProfileWidth", Integer.class);
     final int typeProfileWidth = getFlag("TypeProfileWidth", Integer.class);
     final int methodProfileWidth = getFlag("MethodProfileWidth", Integer.class, 0);
 
diff a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaMethod.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaMethod.java
--- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaMethod.java
+++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaMethod.java
@@ -458,11 +458,11 @@
 
     /**
      * Determines if this method is {@link java.lang.Object#Object()}.
      */
     default boolean isJavaLangObjectInit() {
-        return getDeclaringClass().isJavaLangObject() && getName().equals("<init>");
+        return getDeclaringClass().isJavaLangObject() && (getName().equals("<init>") || getName().equals("<vnew>"));
     }
 
     /**
      * Gets a speculation log that can be used when compiling this method to make new speculations
      * and query previously failed speculations. The implementation may return a new
diff a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/AccessFlags.java b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/AccessFlags.java
--- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/AccessFlags.java
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/AccessFlags.java
@@ -41,20 +41,22 @@
     public static final int ACC_PUBLIC        = 0x0001; // class, inner, field, method
     public static final int ACC_PRIVATE       = 0x0002; //        inner, field, method
     public static final int ACC_PROTECTED     = 0x0004; //        inner, field, method
     public static final int ACC_STATIC        = 0x0008; //        inner, field, method
     public static final int ACC_FINAL         = 0x0010; // class, inner, field, method
-    public static final int ACC_SUPER         = 0x0020; // class
+    public static final int ACC_IDENTITY      = 0x0020; // class, inner
     public static final int ACC_SYNCHRONIZED  = 0x0020; //                      method
+    public static final int ACC_VALUE         = 0x0040; // class, inner,
     public static final int ACC_VOLATILE      = 0x0040; //               field
     public static final int ACC_BRIDGE        = 0x0040; //                      method
     public static final int ACC_TRANSIENT     = 0x0080; //               field
     public static final int ACC_VARARGS       = 0x0080; //                      method
     public static final int ACC_NATIVE        = 0x0100; //                      method
     public static final int ACC_INTERFACE     = 0x0200; // class, inner
     public static final int ACC_ABSTRACT      = 0x0400; // class, inner,        method
-    public static final int ACC_STRICT        = 0x0800; //                      method
+    public static final int ACC_PRIMITIVE     = 0x0800; //                      class
+    public static final int ACC_STRICT        = 0x0800; //                      method, field
     public static final int ACC_SYNTHETIC     = 0x1000; // class, inner, field, method
     public static final int ACC_ANNOTATION    = 0x2000; // class, inner
     public static final int ACC_ENUM          = 0x4000; // class, inner, field
     public static final int ACC_MANDATED      = 0x8000; //                          method parameter
     public static final int ACC_MODULE        = 0x8000; // class
@@ -80,16 +82,16 @@
     public int byteLength() {
         return 2;
     }
 
     private static final int[] classModifiers = {
-        ACC_PUBLIC, ACC_FINAL, ACC_ABSTRACT
+        ACC_PUBLIC, ACC_FINAL, ACC_IDENTITY, ACC_ABSTRACT, ACC_PRIMITIVE, ACC_VALUE
     };
 
     private static final int[] classFlags = {
-        ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_INTERFACE, ACC_ABSTRACT,
-        ACC_SYNTHETIC, ACC_ANNOTATION, ACC_ENUM, ACC_MODULE
+        ACC_PUBLIC, ACC_FINAL, ACC_IDENTITY, ACC_INTERFACE, ACC_ABSTRACT,
+        ACC_SYNTHETIC, ACC_ANNOTATION, ACC_ENUM, ACC_MODULE, ACC_PRIMITIVE, ACC_VALUE
     };
 
     public Set<String> getClassModifiers() {
         int f = ((flags & ACC_INTERFACE) != 0 ? flags & ~ACC_ABSTRACT : flags);
         return getModifiers(f, classModifiers, Kind.Class);
@@ -98,17 +100,17 @@
     public Set<String> getClassFlags() {
         return getFlags(classFlags, Kind.Class);
     }
 
     private static final int[] innerClassModifiers = {
-        ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL,
-        ACC_ABSTRACT
+        ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_IDENTITY,
+        ACC_ABSTRACT, ACC_PRIMITIVE, ACC_VALUE
     };
 
     private static final int[] innerClassFlags = {
-        ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SUPER,
-        ACC_INTERFACE, ACC_ABSTRACT, ACC_SYNTHETIC, ACC_ANNOTATION, ACC_ENUM
+        ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_IDENTITY,
+        ACC_INTERFACE, ACC_ABSTRACT, ACC_SYNTHETIC, ACC_ANNOTATION, ACC_ENUM, ACC_PRIMITIVE, ACC_VALUE
     };
 
     public Set<String> getInnerClassModifiers() {
         int f = ((flags & ACC_INTERFACE) != 0 ? flags & ~ACC_ABSTRACT : flags);
         return getModifiers(f, innerClassModifiers, Kind.InnerClass);
@@ -123,11 +125,11 @@
         ACC_VOLATILE, ACC_TRANSIENT
     };
 
     private static final int[] fieldFlags = {
         ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL,
-        ACC_VOLATILE, ACC_TRANSIENT, ACC_SYNTHETIC, ACC_ENUM
+        ACC_VOLATILE, ACC_TRANSIENT, ACC_SYNTHETIC, ACC_ENUM, ACC_STRICT
     };
 
     public Set<String> getFieldModifiers() {
         return getModifiers(fieldModifiers, Kind.Field);
     }
@@ -195,22 +197,22 @@
                 return "protected";
             case ACC_STATIC:
                 return "static";
             case ACC_FINAL:
                 return "final";
-            case ACC_SYNCHRONIZED:
-                return "synchronized";
+            case 0x20:
+                return (t == Kind.Class || t == Kind.InnerClass) ? "identity" : "synchronized";
             case 0x80:
                 return (t == Kind.Field ? "transient" : null);
             case ACC_VOLATILE:
                 return "volatile";
             case ACC_NATIVE:
                 return "native";
             case ACC_ABSTRACT:
                 return "abstract";
-            case ACC_STRICT:
-                return "strictfp";
+            case 0x800:
+                return (t == Kind.Field) ? "strict" : "strictfp";
             case ACC_MANDATED:
                 return "mandated";
             default:
                 return null;
         }
@@ -227,13 +229,13 @@
         case ACC_STATIC:
             return "ACC_STATIC";
         case ACC_FINAL:
             return "ACC_FINAL";
         case 0x20:
-            return (t == Kind.Class ? "ACC_SUPER" : "ACC_SYNCHRONIZED");
+            return ((t == Kind.Class || t == Kind.InnerClass) ? "ACC_IDENTITY" : "ACC_SYNCHRONIZED");
         case 0x40:
-            return (t == Kind.Field ? "ACC_VOLATILE" : "ACC_BRIDGE");
+            return (t == Kind.Field ? "ACC_VOLATILE" : t == Kind.Method ? "ACC_BRIDGE" : "ACC_VALUE");
         case 0x80:
             return (t == Kind.Field ? "ACC_TRANSIENT" : "ACC_VARARGS");
         case ACC_NATIVE:
             return "ACC_NATIVE";
         case ACC_INTERFACE:
diff a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Attribute.java b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Attribute.java
--- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Attribute.java
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Attribute.java
@@ -58,10 +58,11 @@
     public static final String ModulePackages           = "ModulePackages";
     public static final String ModuleResolution         = "ModuleResolution";
     public static final String ModuleTarget             = "ModuleTarget";
     public static final String NestHost                 = "NestHost";
     public static final String NestMembers              = "NestMembers";
+    public static final String Preload                  = "Preload";
     public static final String Record                   = "Record";
     public static final String RuntimeVisibleAnnotations = "RuntimeVisibleAnnotations";
     public static final String RuntimeInvisibleAnnotations = "RuntimeInvisibleAnnotations";
     public static final String RuntimeVisibleParameterAnnotations = "RuntimeVisibleParameterAnnotations";
     public static final String RuntimeInvisibleParameterAnnotations = "RuntimeInvisibleParameterAnnotations";
@@ -134,10 +135,11 @@
             standardAttributes.put(ModulePackages,    ModulePackages_attribute.class);
             standardAttributes.put(ModuleResolution,  ModuleResolution_attribute.class);
             standardAttributes.put(ModuleTarget,      ModuleTarget_attribute.class);
             standardAttributes.put(NestHost, NestHost_attribute.class);
             standardAttributes.put(NestMembers, NestMembers_attribute.class);
+            standardAttributes.put(Preload, Preload_attribute.class);
             standardAttributes.put(Record, Record_attribute.class);
             standardAttributes.put(RuntimeInvisibleAnnotations, RuntimeInvisibleAnnotations_attribute.class);
             standardAttributes.put(RuntimeInvisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations_attribute.class);
             standardAttributes.put(RuntimeVisibleAnnotations, RuntimeVisibleAnnotations_attribute.class);
             standardAttributes.put(RuntimeVisibleParameterAnnotations, RuntimeVisibleParameterAnnotations_attribute.class);
@@ -201,10 +203,11 @@
         R visitModulePackages(ModulePackages_attribute attr, P p);
         R visitModuleResolution(ModuleResolution_attribute attr, P p);
         R visitModuleTarget(ModuleTarget_attribute attr, P p);
         R visitNestHost(NestHost_attribute attr, P p);
         R visitNestMembers(NestMembers_attribute attr, P p);
+        R visitPreload(Preload_attribute attr, P p);
         R visitRecord(Record_attribute attr, P p);
         R visitRuntimeVisibleAnnotations(RuntimeVisibleAnnotations_attribute attr, P p);
         R visitRuntimeInvisibleAnnotations(RuntimeInvisibleAnnotations_attribute attr, P p);
         R visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute attr, P p);
         R visitRuntimeInvisibleParameterAnnotations(RuntimeInvisibleParameterAnnotations_attribute attr, P p);
diff a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java
--- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java
@@ -752,10 +752,18 @@
         @Override
         public Void visitSynthetic(Synthetic_attribute attr, ClassOutputStream out) {
             return null;
         }
 
+        @Override
+        public Void visitPreload(Preload_attribute attr, ClassOutputStream out) {
+            out.writeShort(attr.value_class_info_index.length);
+            for (int index: attr.value_class_info_index)
+                out.writeShort(index);
+            return null;
+        }
+
         protected void writeAccessFlags(AccessFlags flags, ClassOutputStream out) {
             out.writeShort(flags.flags);
         }
 
         protected StackMapTableWriter stackMapWriter;
diff a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Preload_attribute.java b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Preload_attribute.java
--- /dev/null
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Preload_attribute.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2022, 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.
+ */
+
+package com.sun.tools.classfile;
+
+import java.io.IOException;
+
+/**
+ *  <p><b>This is NOT part of any supported API.
+ *  If you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ */
+public class Preload_attribute extends Attribute {
+    Preload_attribute(ClassReader cr, int name_index, int length) throws IOException {
+        super(name_index, length);
+        number_of_classes = cr.readUnsignedShort();
+        value_class_info_index = new int[number_of_classes];
+        for (int i = 0; i < number_of_classes; i++)
+            value_class_info_index[i] = cr.readUnsignedShort();
+    }
+
+    public <R, D> R accept(Visitor<R, D> visitor, D data) {
+        return visitor.visitPreload(this, data);
+    }
+
+    public final int number_of_classes;
+    public final int value_class_info_index[];
+}
diff a/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java
--- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java
@@ -34,10 +34,13 @@
 import java.lang.classfile.constantpool.*;
 import java.lang.classfile.attribute.*;
 import static java.lang.classfile.ClassFile.*;
 import static java.lang.classfile.attribute.StackMapFrameInfo.*;
 
+import com.sun.tools.javac.util.Assert;
+import com.sun.tools.javac.util.StringUtils;
+
 /*
  *  A writer for writing Attributes as text.
  *
  *  <p><b>This is NOT part of any supported API.
  *  If you write code that depends on this, you do so at your own risk.
@@ -522,10 +525,18 @@
                 for (var sc : attr.permittedSubclasses()) {
                     println(constantWriter.stringValue(sc));
                 }
                 indent(-1);
             }
+            case PreloadAttribute attr -> {
+                println("Preload:");
+                indent(+1);
+                for (var sc : attr.preloads()) {
+                    println(constantWriter.stringValue(sc));
+                }
+                indent(-1);
+            }
             case SignatureAttribute attr -> {
                 print("Signature: #" + attr.signature().index());
                 tab();
                 println("// " + attr.signature().stringValue());
             }
diff a/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java
--- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java
@@ -34,10 +34,12 @@
 
 import java.lang.constant.ClassDesc;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.LinkedHashSet;
+
+import com.sun.tools.javac.code.Source;
 import java.lang.classfile.AccessFlags;
 import java.lang.classfile.Attributes;
 import java.lang.classfile.ClassModel;
 import java.lang.classfile.ClassSignature;
 import java.lang.classfile.ClassFile;
@@ -146,11 +148,11 @@
 
         if (options.sysInfo || options.verbose) {
             indent(-1);
         }
 
-        writeModifiers(getClassModifiers(cm.flags().flagsMask()));
+        writeModifiers(getClassModifiers(cm.flags().flagsMask(), classModel.majorVersion(), classModel.minorVersion()));
 
         if ((classModel.flags().flagsMask() & ACC_MODULE) != 0) {
             var attr = classModel.findAttribute(Attributes.MODULE);
             if (attr.isPresent()) {
                 var modAttr = attr.get();
@@ -772,10 +774,20 @@
     private static Set<String> getClassModifiers(int mask) {
         return getModifiers(AccessFlags.ofClass((mask & ACC_INTERFACE) != 0
                 ? mask & ~ACC_ABSTRACT : mask).flags());
     }
 
+    private static Set<String> getClassModifiers(int mask, int majorVersion, int minorVersion) {
+        boolean previewClassFile = minorVersion == ClassFile.PREVIEW_MINOR_VERSION;
+        Set<String> result = getModifiers(AccessFlags.ofClass((mask & ACC_INTERFACE) != 0
+                ? mask & ~ACC_ABSTRACT : mask).flags());
+        if ((mask & ACC_INTERFACE) == 0 && Source.isSupported(Source.Feature.VALUE_CLASSES, majorVersion) && previewClassFile) {
+            result.add("value");
+        }
+        return result;
+    }
+
     private static Set<String> getMethodModifiers(int mask) {
         return getModifiers(AccessFlags.ofMethod(mask).flags());
     }
 
     private static Set<String> getFieldModifiers(int mask) {
diff a/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/BreakpointSpec.java b/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/BreakpointSpec.java
--- a/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/BreakpointSpec.java
+++ b/src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/BreakpointSpec.java
@@ -213,10 +213,11 @@
     }
 
     private boolean isValidMethodName(String s) {
         return isJavaIdentifier(s) ||
                s.equals("<init>") ||
+               s.equals("<vnew>") ||
                s.equals("<clinit>");
     }
 
     /*
      * Compare a method's argument types with a Vector of type names.
diff a/src/jdk.jdi/share/classes/com/sun/tools/jdi/MethodImpl.java b/src/jdk.jdi/share/classes/com/sun/tools/jdi/MethodImpl.java
--- a/src/jdk.jdi/share/classes/com/sun/tools/jdi/MethodImpl.java
+++ b/src/jdk.jdi/share/classes/com/sun/tools/jdi/MethodImpl.java
@@ -214,11 +214,11 @@
     public boolean isBridge() {
         return isModifierSet(VMModifiers.BRIDGE);
     }
 
     public boolean isConstructor() {
-        return name().equals("<init>");
+        return name().equals("<init>") || name().equals("<vnew>");
     }
 
     public boolean isStaticInitializer() {
         return name().equals("<clinit>");
     }
diff a/src/jdk.jdi/share/classes/com/sun/tools/jdi/ObjectReferenceImpl.java b/src/jdk.jdi/share/classes/com/sun/tools/jdi/ObjectReferenceImpl.java
--- a/src/jdk.jdi/share/classes/com/sun/tools/jdi/ObjectReferenceImpl.java
+++ b/src/jdk.jdi/share/classes/com/sun/tools/jdi/ObjectReferenceImpl.java
@@ -146,11 +146,11 @@
     }
 
     public boolean equals(Object obj) {
         if (obj instanceof ObjectReferenceImpl other) {
             return (ref() == other.ref()) &&
-                   super.equals(obj);
+                    super.equals(obj);
         } else {
             return false;
         }
     }
 
diff a/src/jdk.jdwp.agent/share/native/libjdwp/util.c b/src/jdk.jdwp.agent/share/native/libjdwp/util.c
--- a/src/jdk.jdwp.agent/share/native/libjdwp/util.c
+++ b/src/jdk.jdwp.agent/share/native/libjdwp/util.c
@@ -360,11 +360,10 @@
     }
 
     /*
      * For primitive types, the type key is bounced back as is.
      */
-
     (void)outStream_writeByte(out, typeKey);
 
     switch (typeKey) {
         case JDWP_TAG(BYTE):
             (void)outStream_writeByte(out,
diff a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventClassBuilder.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventClassBuilder.java
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventClassBuilder.java
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventClassBuilder.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 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.  Oracle designates this
@@ -37,10 +37,13 @@
 import java.lang.classfile.AnnotationValue;
 import java.lang.classfile.ClassBuilder;
 import java.lang.classfile.ClassFile;
 import java.lang.classfile.Label;
 import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
+
+import jdk.internal.misc.PreviewFeatures;
+
 import jdk.jfr.AnnotationElement;
 import jdk.jfr.Event;
 import jdk.jfr.ValueDescriptor;
 import jdk.jfr.internal.util.Bytecode;
 import jdk.jfr.internal.util.Bytecode.MethodDesc;
@@ -108,11 +111,11 @@
         }));
     }
 
     private void buildClassInfo(ClassBuilder builder) {
         builder.withSuperclass(Bytecode.classDesc(Event.class));
-        builder.withFlags(AccessFlag.FINAL, AccessFlag.PUBLIC, AccessFlag.SUPER);
+        builder.withFlags(AccessFlag.FINAL, AccessFlag.PUBLIC, (PreviewFeatures.isEnabled() ? AccessFlag.IDENTITY : AccessFlag.SUPER));
         List<java.lang.classfile.Annotation> annotations = new ArrayList<>();
         for (jdk.jfr.AnnotationElement a : annotationElements) {
             List<java.lang.classfile.AnnotationElement> list = new ArrayList<>();
             for (ValueDescriptor v : a.getValueDescriptors()) {
                 // ValueDescriptor can only hold primitive
diff a/src/jdk.unsupported/share/classes/sun/misc/Unsafe.java b/src/jdk.unsupported/share/classes/sun/misc/Unsafe.java
--- a/src/jdk.unsupported/share/classes/sun/misc/Unsafe.java
+++ b/src/jdk.unsupported/share/classes/sun/misc/Unsafe.java
@@ -28,11 +28,10 @@
 import jdk.internal.vm.annotation.ForceInline;
 import jdk.internal.misc.VM;
 import jdk.internal.reflect.CallerSensitive;
 import jdk.internal.reflect.Reflection;
 
-import java.lang.invoke.MethodHandles;
 import java.lang.reflect.Field;
 import java.util.Set;
 
 
 /**
@@ -641,10 +640,11 @@
      *
      * @see #getInt(Object, long)
      */
     @Deprecated(since="18")
     @ForceInline
+    @SuppressWarnings("preview")
     public long objectFieldOffset(Field f) {
         if (f == null) {
             throw new NullPointerException();
         }
         Class<?> declaringClass = f.getDeclaringClass();
@@ -652,10 +652,13 @@
             throw new UnsupportedOperationException("can't get field offset on a hidden class: " + f);
         }
         if (declaringClass.isRecord()) {
             throw new UnsupportedOperationException("can't get field offset on a record class: " + f);
         }
+        if (declaringClass.isValue()) {
+            throw new UnsupportedOperationException("can't get field offset on a value class: " + f);
+        }
         return theInternalUnsafe.objectFieldOffset(f);
     }
 
     /**
      * Reports the location of a given static field, in conjunction with {@link
@@ -680,10 +683,11 @@
      *
      * @see #getInt(Object, long)
      */
     @Deprecated(since="18")
     @ForceInline
+    @SuppressWarnings("preview")
     public long staticFieldOffset(Field f) {
         if (f == null) {
             throw new NullPointerException();
         }
         Class<?> declaringClass = f.getDeclaringClass();
@@ -691,10 +695,13 @@
             throw new UnsupportedOperationException("can't get field offset on a hidden class: " + f);
         }
         if (declaringClass.isRecord()) {
             throw new UnsupportedOperationException("can't get field offset on a record class: " + f);
         }
+        if (declaringClass.isValue()) {
+            throw new UnsupportedOperationException("can't get field offset on a value class: " + f);
+        }
         return theInternalUnsafe.staticFieldOffset(f);
     }
 
     /**
      * Reports the location of a given static field, in conjunction with {@link
@@ -711,10 +718,11 @@
      * offset and object reference to a heap memory accessor will be removed
      * in a future release. Use {@link java.lang.invoke.VarHandle} instead.
      */
     @Deprecated(since="18")
     @ForceInline
+    @SuppressWarnings("preview")
     public Object staticFieldBase(Field f) {
         if (f == null) {
             throw new NullPointerException();
         }
         Class<?> declaringClass = f.getDeclaringClass();
@@ -722,10 +730,13 @@
             throw new UnsupportedOperationException("can't get base address on a hidden class: " + f);
         }
         if (declaringClass.isRecord()) {
             throw new UnsupportedOperationException("can't get base address on a record class: " + f);
         }
+        if (declaringClass.isValue()) {
+            throw new UnsupportedOperationException("can't get field offset on a value class: " + f);
+        }
         return theInternalUnsafe.staticFieldBase(f);
     }
 
     /**
      * Reports the offset of the first element in the storage allocation of a
diff a/test/hotspot/gtest/classfile/test_placeholders.cpp b/test/hotspot/gtest/classfile/test_placeholders.cpp
--- a/test/hotspot/gtest/classfile/test_placeholders.cpp
+++ b/test/hotspot/gtest/classfile/test_placeholders.cpp
@@ -47,11 +47,11 @@
   ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
 
   {
     MutexLocker ml(THREAD, SystemDictionary_lock);
 
-    PlaceholderTable::classloadAction super_action = PlaceholderTable::LOAD_SUPER;
+    PlaceholderTable::classloadAction super_action = PlaceholderTable::DETECT_CIRCULARITY;
     PlaceholderTable::classloadAction define_action = PlaceholderTable::DEFINE_CLASS;
 
     // DefineClass A and D
     PlaceholderTable::find_and_add(A, loader_data, define_action, nullptr, THREAD);
     PlaceholderTable::find_and_add(D, loader_data, define_action, nullptr, T2);
@@ -69,11 +69,11 @@
     PlaceholderTable::find_and_add(A, loader_data, super_action, super, THREAD);
     PlaceholderTable::find_and_add(D, loader_data, super_action, super, T2);
 
     // Another thread comes in and finds A loading Super
     PlaceholderEntry* placeholder = PlaceholderTable::get_entry(A, loader_data);
-    SymbolHandle supername = placeholder->supername();
+    SymbolHandle supername = placeholder->next_klass_name();
 
     // Other thread is done before handle_parallel_super_load
     PlaceholderTable::find_and_remove(A, loader_data, super_action, THREAD);
 
     // if THREAD drops reference to supername (loading failed or class unloaded), we're left with
@@ -84,11 +84,11 @@
     PlaceholderTable::find_and_add(A, loader_data, super_action, supername, T2);
 
     // Refcount should be 3: one in table for class A, one in table for class D
     // and one locally with SymbolHandle keeping it alive
     placeholder = PlaceholderTable::get_entry(A, loader_data);
-    supername = placeholder->supername();
+    supername = placeholder->next_klass_name();
     EXPECT_EQ(super->refcount(), 3) << "super class name refcount should be 3";
 
     // Second thread's done too
     PlaceholderTable::find_and_remove(D, loader_data, super_action, T2);
 
diff a/test/hotspot/gtest/code/test_vtableStub.cpp b/test/hotspot/gtest/code/test_vtableStub.cpp
--- a/test/hotspot/gtest/code/test_vtableStub.cpp
+++ b/test/hotspot/gtest/code/test_vtableStub.cpp
@@ -31,26 +31,26 @@
 
 TEST_VM(code, vtableStubs) {
   // Should be in VM to use locks
   ThreadInVMfromNative ThreadInVMfromNative(JavaThread::current());
 
-  VtableStubs::find_vtable_stub(0); // min vtable index
+  VtableStubs::find_vtable_stub(0, false); // min vtable index
   for (int i = 0; i < 15; i++) {
-    VtableStubs::find_vtable_stub((1 << i) - 1);
-    VtableStubs::find_vtable_stub((1 << i));
+    VtableStubs::find_vtable_stub((1 << i) - 1, false);
+    VtableStubs::find_vtable_stub((1 << i), false);
   }
-  VtableStubs::find_vtable_stub((1 << 15) - 1); // max vtable index
+  VtableStubs::find_vtable_stub((1 << 15) - 1, false); // max vtable index
 }
 
 TEST_VM(code, itableStubs) {
   // Should be in VM to use locks
   ThreadInVMfromNative ThreadInVMfromNative(JavaThread::current());
 
-  VtableStubs::find_itable_stub(0); // min itable index
+  VtableStubs::find_itable_stub(0, false); // min itable index
   for (int i = 0; i < 15; i++) {
-    VtableStubs::find_itable_stub((1 << i) - 1);
-    VtableStubs::find_itable_stub((1 << i));
+    VtableStubs::find_itable_stub((1 << i) - 1, false);
+    VtableStubs::find_itable_stub((1 << i), false);
   }
-  VtableStubs::find_itable_stub((1 << 15) - 1); // max itable index
+  VtableStubs::find_itable_stub((1 << 15) - 1, false); // max itable index
 }
 
 #endif
diff a/test/hotspot/gtest/gc/shared/test_preservedMarks.cpp b/test/hotspot/gtest/gc/shared/test_preservedMarks.cpp
--- a/test/hotspot/gtest/gc/shared/test_preservedMarks.cpp
+++ b/test/hotspot/gtest/gc/shared/test_preservedMarks.cpp
@@ -41,11 +41,11 @@
     markWord m = markWord::encode_pointer_as_mark(obj);
     _oop.set_mark(m);
   }
 
   static markWord originalMark() { return markWord(markWord::lock_mask_in_place); }
-  static markWord changedMark()  { return markWord(0x4711); }
+  static markWord changedMark()  { return markWord((uintptr_t)0x4711 << markWord::hash_shift); }
 };
 
 #define ASSERT_MARK_WORD_EQ(a, b) ASSERT_EQ((a).value(), (b).value())
 
 TEST_VM(PreservedMarks, iterate_and_restore) {
diff a/test/hotspot/gtest/oops/test_markWord.cpp b/test/hotspot/gtest/oops/test_markWord.cpp
--- a/test/hotspot/gtest/oops/test_markWord.cpp
+++ b/test/hotspot/gtest/oops/test_markWord.cpp
@@ -45,10 +45,20 @@
   stringStream st;
   object->print_on(&st);
   ASSERT_THAT(st.base(), testing::HasSubstr(pattern));
 }
 
+static void assert_mark_word_print_pattern(Handle object, const char* pattern) {
+  if (LockingMode == LM_MONITOR) {
+    // With heavy monitors, we do not use the mark word. Printing the oop only shows "monitor" regardless of the
+    // locking state.
+    assert_test_pattern(object, "monitor");
+  } else {
+    assert_test_pattern(object, pattern);
+  }
+}
+
 class LockerThread : public JavaTestThread {
   oop _obj;
   public:
   LockerThread(Semaphore* post, oop obj) : JavaTestThread(post), _obj(obj) {}
   virtual ~LockerThread() {}
@@ -84,17 +94,17 @@
   Handle h_obj(THREAD, obj);
 
   // Thread tries to lock it.
   {
     ObjectLocker ol(h_obj, THREAD);
-    assert_test_pattern(h_obj, "locked");
+    assert_mark_word_print_pattern(h_obj, "locked");
   }
-  assert_test_pattern(h_obj, "is_neutral no_hash");
+  assert_mark_word_print_pattern(h_obj, "is_neutral no_hash");
 
   // Hash the object then print it.
   intx hash = h_obj->identity_hash();
-  assert_test_pattern(h_obj, "is_neutral hash=0x");
+  assert_mark_word_print_pattern(h_obj, "is_neutral hash=0x");
 
   // Wait gets the lock inflated.
   {
     ObjectLocker ol(h_obj, THREAD);
 
@@ -106,6 +116,127 @@
     ol.wait(THREAD);
     assert_test_pattern(h_obj, "monitor");
     done.wait_with_safepoint_check(THREAD);  // wait till the thread is done.
   }
 }
+
+static void assert_unlocked_state(markWord mark) {
+  EXPECT_FALSE(mark.has_displaced_mark_helper());
+  if (LockingMode == LM_LEGACY) {
+    EXPECT_FALSE(mark.has_locker());
+  } else if (LockingMode == LM_LIGHTWEIGHT) {
+    EXPECT_FALSE(mark.is_fast_locked());
+  }
+  EXPECT_FALSE(mark.has_monitor());
+  EXPECT_FALSE(mark.is_being_inflated());
+  EXPECT_FALSE(mark.is_locked());
+  EXPECT_TRUE(mark.is_unlocked());
+}
+
+static void assert_copy_set_hash(markWord mark) {
+  const intptr_t hash = 4711;
+  EXPECT_TRUE(mark.has_no_hash());
+  markWord copy = mark.copy_set_hash(hash);
+  EXPECT_EQ(hash, copy.hash());
+  EXPECT_FALSE(copy.has_no_hash());
+}
+
+static void assert_type(markWord mark) {
+  EXPECT_FALSE(mark.is_flat_array());
+  EXPECT_FALSE(mark.is_inline_type());
+  EXPECT_FALSE(mark.is_larval_state());
+  EXPECT_FALSE(mark.is_null_free_array());
+}
+
+TEST_VM(markWord, prototype) {
+  markWord mark = markWord::prototype();
+  assert_unlocked_state(mark);
+  EXPECT_TRUE(mark.is_neutral());
+
+  assert_type(mark);
+
+  EXPECT_TRUE(mark.has_no_hash());
+  EXPECT_FALSE(mark.is_marked());
+  EXPECT_TRUE(mark.decode_pointer() == NULL);
+
+  assert_copy_set_hash(mark);
+  assert_type(mark);
+}
+
+static void assert_inline_type(markWord mark) {
+  EXPECT_FALSE(mark.is_flat_array());
+  EXPECT_TRUE(mark.is_inline_type());
+  EXPECT_FALSE(mark.is_null_free_array());
+}
+
+TEST_VM(markWord, inline_type_prototype) {
+  markWord mark = markWord::inline_type_prototype();
+  assert_unlocked_state(mark);
+  EXPECT_FALSE(mark.is_neutral());
+
+  assert_inline_type(mark);
+  EXPECT_FALSE(mark.is_larval_state());
+
+  EXPECT_TRUE(mark.has_no_hash());
+  EXPECT_FALSE(mark.is_marked());
+  EXPECT_TRUE(mark.decode_pointer() == NULL);
+
+  markWord larval = mark.enter_larval_state();
+  EXPECT_TRUE(larval.is_larval_state());
+  assert_inline_type(larval);
+  mark = larval.exit_larval_state();
+  EXPECT_FALSE(mark.is_larval_state());
+  assert_inline_type(mark);
+
+  EXPECT_TRUE(mark.has_no_hash());
+  EXPECT_FALSE(mark.is_marked());
+  EXPECT_TRUE(mark.decode_pointer() == NULL);
+}
+
+#if _LP64
+
+static void assert_flat_array_type(markWord mark) {
+  EXPECT_TRUE(mark.is_flat_array());
+  EXPECT_FALSE(mark.is_inline_type());
+  EXPECT_FALSE(mark.is_larval_state());
+  EXPECT_TRUE(mark.is_null_free_array());
+}
+
+TEST_VM(markWord, flat_array_prototype) {
+  markWord mark = markWord::flat_array_prototype();
+  assert_unlocked_state(mark);
+  EXPECT_TRUE(mark.is_neutral());
+
+  assert_flat_array_type(mark);
+
+  EXPECT_TRUE(mark.has_no_hash());
+  EXPECT_FALSE(mark.is_marked());
+  EXPECT_TRUE(mark.decode_pointer() == NULL);
+
+  assert_copy_set_hash(mark);
+  assert_flat_array_type(mark);
+}
+
+static void assert_null_free_array_type(markWord mark) {
+  EXPECT_FALSE(mark.is_flat_array());
+  EXPECT_FALSE(mark.is_inline_type());
+  EXPECT_FALSE(mark.is_larval_state());
+  EXPECT_TRUE(mark.is_null_free_array());
+}
+
+TEST_VM(markWord, null_free_array_prototype) {
+  markWord mark = markWord::null_free_array_prototype();
+  assert_unlocked_state(mark);
+  EXPECT_TRUE(mark.is_neutral());
+
+  assert_null_free_array_type(mark);
+
+  EXPECT_TRUE(mark.has_no_hash());
+  EXPECT_FALSE(mark.is_marked());
+  EXPECT_TRUE(mark.decode_pointer() == NULL);
+
+  assert_copy_set_hash(mark);
+  assert_null_free_array_type(mark);
+}
+#endif // _LP64
+
 #endif // PRODUCT
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
@@ -93,10 +93,11 @@
 
 #############################################################################
 
 # :hotspot_runtime
 
+runtime/cds/appcds/redefineClass/RedefineRunningMethods_Shared.java  8304168 generic-all
 runtime/jni/terminatedThread/TestTerminatedThread.java 8317789 aix-ppc64
 runtime/handshake/HandshakeSuspendExitTest.java 8294313 generic-all
 runtime/os/TestTracePageSizes.java#no-options 8267460 linux-aarch64
 runtime/os/TestTracePageSizes.java#explicit-large-page-size 8267460 linux-aarch64
 runtime/os/TestTracePageSizes.java#compiler-options 8267460 linux-aarch64
@@ -113,10 +114,16 @@
 applications/jcstress/copy.java 8229852 linux-all
 
 containers/docker/TestJcmd.java 8278102 linux-all
 containers/docker/TestMemoryAwareness.java 8303470 linux-all
 
+
+# Valhalla
+runtime/AccModule/ConstModule.java 8294051 generic-all
+runtime/valhalla/inlinetypes/InlineOops.java#ZGen 8313607 linux-aarch64,macosx-aarch64
+runtime/cds/CDSMapTest.java 8314993 generic-all
+
 #############################################################################
 
 # :hotspot_serviceability
 
 serviceability/sa/sadebugd/DebugdConnectTest.java 8239062,8270326 macosx-x64,macosx-aarch64
@@ -136,10 +143,40 @@
 
 serviceability/attach/ConcAttachTest.java 8290043 linux-all
 
 serviceability/jvmti/stress/StackTrace/NotSuspended/GetStackTraceNotSuspendedStressTest.java 8315980 linux-all,windows-x64
 
+# Valhalla TODO:
+serviceability/jvmti/Valhalla/HeapDump/HeapDump.java 8317416 generic-all
+
+serviceability/sa/ClhsdbCDSCore.java 8190936 generic-all
+serviceability/sa/ClhsdbCDSJstackPrintAll.java 8190936 generic-all
+serviceability/sa/ClhsdbFindPC.java 8190936 generic-all
+serviceability/sa/ClhsdbInspect.java 8190936 generic-all
+serviceability/sa/ClhsdbLongConstant.java 8190936 generic-all
+serviceability/sa/ClhsdbJdis.java 8190936 generic-all
+serviceability/sa/ClhsdbJstack.java 8190936 generic-all
+serviceability/sa/ClhsdbPrintAll.java 8190936 generic-all
+serviceability/sa/ClhsdbPrintAs.java 8190936 generic-all
+serviceability/sa/ClhsdbPrintStatics.java 8190936 generic-all
+serviceability/sa/ClhsdbSource.java 8190936 generic-all
+serviceability/sa/ClhsdbSymbol.java 8190936 generic-all
+serviceability/sa/ClhsdbWhere.java 8190936 generic-all
+serviceability/sa/JhsdbThreadInfoTest.java 8190936 generic-all
+serviceability/sa/TestClassDump.java 8190936 generic-all
+serviceability/sa/TestClhsdbJstackLock.java 8190936 generic-all
+serviceability/sa/TestCpoolForInvokeDynamic.java 8190936 generic-all
+serviceability/sa/TestHeapDumpForInvokeDynamic.java 8190936 generic-all
+serviceability/sa/TestHeapDumpForLargeArray.java 8190936 generic-all
+serviceability/sa/TestIntConstant.java 8190936 generic-all
+serviceability/sa/TestJhsdbJstackLock.java 8190936 generic-all
+serviceability/sa/TestJmapCore.java 8190936 generic-all
+serviceability/sa/TestJmapCoreMetaspace.java 8190936 generic-all
+serviceability/sa/TestPrintMdo.java 8190936 generic-all
+serviceability/sa/jmap-hprof/JMapHProfLargeHeapTest.java 8190936 generic-all
+serviceability/sa/ClhsdbDumpclass.java 8190936 generic-all
+
 #############################################################################
 
 # :hotspot_misc
 
 #############################################################################
@@ -173,6 +210,8 @@
 
 vmTestbase/nsk/jdwp/ThreadReference/ForceEarlyReturn/forceEarlyReturn001/forceEarlyReturn001.java 7199837 generic-all
 
 vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi005/TestDescription.java 8076494 windows-x64
 
+vmTestbase/vm/mlvm/hiddenloader/stress/byteMutation/Test.java 8317172 generic-all
+
 vmTestbase/nsk/monitoring/ThreadMXBean/findMonitorDeadlockedThreads/find006/TestDescription.java 8310144 macosx-aarch64
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
@@ -42,11 +42,11 @@
 hotspot_gc = \
   gc
 
 # By design this group should include ALL tests under runtime sub-directory
 hotspot_runtime = \
-  runtime
+  runtime \
 
 hotspot_runtime_no_cds = \
   runtime \
   -runtime/cds
 
@@ -54,10 +54,18 @@
   runtime/handshake
 
 hotspot_serviceability = \
   serviceability
 
+hotspot_valhalla = \
+  runtime/valhalla \
+  compiler/valhalla \
+  serviceability/jvmti/Valhalla
+
+hotspot_valhalla_runtime = \
+  runtime/valhalla
+
 hotspot_resourcehogs = \
   resourcehogs
 
 hotspot_misc = \
   / \
@@ -149,12 +157,12 @@
   :hotspot_compiler \
   -:tier1_compiler \
   -:hotspot_slow_compiler
 
 hotspot_slow_compiler = \
-  compiler/codegen/aes \
   compiler/codecache/stress \
+  compiler/codegen/aes \
   compiler/gcbarriers/PreserveFPRegistersTest.java \
   compiler/memoryinitialization/ZeroTLABTest.java \
   compiler/classUnloading/methodUnloading/TestOverloadCompileQueues.java \
   resourcehogs/compiler \
   :hotspot_compiler_arraycopy
@@ -205,10 +213,11 @@
   compiler/runtime/ \
   compiler/startup/ \
   compiler/types/ \
   compiler/uncommontrap/ \
   compiler/unsafe/ \
+  compiler/valhalla/ \
   compiler/vectorization/ \
   -compiler/intrinsics/bmi \
   -compiler/intrinsics/mathexact \
   -compiler/intrinsics/sha \
   -compiler/intrinsics/bigInteger/TestMultiplyToLen.java \
@@ -262,10 +271,17 @@
   -:tier2_compiler
 
 tier1_compiler_not_xcomp = \
   compiler/profiling
 
+tier1_compiler_no_valhalla = \
+  :tier1_compiler_1 \
+  :tier1_compiler_2 \
+  :tier1_compiler_3 \
+  :tier1_compiler_not_xcomp \
+  -compiler/valhalla
+
 ctw_1 = \
   applications/ctw/modules/ \
   -:ctw_2 \
   -:ctw_3
 
@@ -407,10 +423,14 @@
  -runtime/Unsafe/RangeCheck.java \
   sanity/ \
  -:tier1_runtime_appcds_exclude \
  -runtime/signal
 
+tier1_runtime_no_valhalla = \
+  :tier1_runtime \
+  -runtime/valhalla
+
 hotspot_cds = \
   runtime/cds/ \
   runtime/CompressedOops/
 
 hotspot_cds_only = \
diff a/test/hotspot/jtreg/compiler/c1/CanonicalizeGetModifiers.java b/test/hotspot/jtreg/compiler/c1/CanonicalizeGetModifiers.java
--- a/test/hotspot/jtreg/compiler/c1/CanonicalizeGetModifiers.java
+++ b/test/hotspot/jtreg/compiler/c1/CanonicalizeGetModifiers.java
@@ -25,10 +25,11 @@
 /*
  * @test
  * @author Yi Yang
  * @summary Canonicalizes Foo.class.getModifiers() with interpreter mode
  * @library /test/lib
+ * @enablePreview
  * @run main/othervm -Xint
  *                   -XX:CompileCommand=compileonly,*CanonicalizeGetModifiers.test
  *                   compiler.c1.CanonicalizeGetModifiers
  */
 
@@ -36,10 +37,11 @@
  * @test
  * @author Yi Yang
  * @summary Canonicalizes Foo.class.getModifiers() with C1 mode
  * @requires vm.compiler1.enabled
  * @library /test/lib
+ * @enablePreview
  * @run main/othervm -XX:TieredStopAtLevel=1 -XX:+TieredCompilation
  *                   -XX:CompileCommand=compileonly,*CanonicalizeGetModifiers.test
  *                   compiler.c1.CanonicalizeGetModifiers
  */
 
@@ -47,18 +49,20 @@
  * @test
  * @author Yi Yang
  * @summary Canonicalizes Foo.class.getModifiers() with C2 mode
  * @requires vm.compiler2.enabled
  * @library /test/lib
+ * @enablePreview
  * @run main/othervm -XX:-TieredCompilation
  *                   -XX:CompileCommand=compileonly,*CanonicalizeGetModifiers.test
  *                   compiler.c1.CanonicalizeGetModifiers
  */
 
 package compiler.c1;
 
 import java.lang.reflect.Modifier;
+import java.lang.reflect.AccessFlag;
 
 import jdk.test.lib.Asserts;
 
 public class CanonicalizeGetModifiers {
     public static class T1 {
@@ -78,16 +82,16 @@
 
     interface T6 {
     }
 
     static void test(Class poison) {
-        Asserts.assertEQ(CanonicalizeGetModifiers.class.getModifiers(), Modifier.PUBLIC);
-        Asserts.assertEQ(T1.class.getModifiers(), Modifier.PUBLIC | Modifier.STATIC);
-        Asserts.assertEQ(T2.class.getModifiers(), Modifier.PUBLIC | Modifier.FINAL | Modifier.STATIC);
-        Asserts.assertEQ(T3.class.getModifiers(), Modifier.PRIVATE | Modifier.STATIC);
-        Asserts.assertEQ(T4.class.getModifiers(), Modifier.PROTECTED | Modifier.STATIC);
-        Asserts.assertEQ(new CanonicalizeGetModifiers().new T5().getClass().getModifiers(), 0/* NONE */);
+        Asserts.assertEQ(CanonicalizeGetModifiers.class.getModifiers(), Modifier.PUBLIC | Modifier.IDENTITY);
+        Asserts.assertEQ(T1.class.getModifiers(), Modifier.PUBLIC | Modifier.STATIC | Modifier.IDENTITY);
+        Asserts.assertEQ(T2.class.getModifiers(), Modifier.PUBLIC | Modifier.FINAL | Modifier.STATIC | Modifier.IDENTITY);
+        Asserts.assertEQ(T3.class.getModifiers(), Modifier.PRIVATE | Modifier.STATIC | Modifier.IDENTITY);
+        Asserts.assertEQ(T4.class.getModifiers(), Modifier.PROTECTED | Modifier.STATIC | Modifier.IDENTITY);
+        Asserts.assertEQ(new CanonicalizeGetModifiers().new T5().getClass().getModifiers(), 0/* NONE */ | Modifier.IDENTITY);
         Asserts.assertEQ(T6.class.getModifiers(), Modifier.ABSTRACT | Modifier.STATIC | Modifier.INTERFACE);
 
         Asserts.assertEQ(int.class.getModifiers(), Modifier.PUBLIC | Modifier.ABSTRACT | Modifier.FINAL);
         Asserts.assertEQ(long.class.getModifiers(), Modifier.PUBLIC | Modifier.ABSTRACT | Modifier.FINAL);
         Asserts.assertEQ(double.class.getModifiers(), Modifier.PUBLIC | Modifier.ABSTRACT | Modifier.FINAL);
@@ -104,15 +108,15 @@
         Asserts.assertEQ(byte[].class.getModifiers(), Modifier.PUBLIC | Modifier.ABSTRACT | Modifier.FINAL);
         Asserts.assertEQ(short[].class.getModifiers(), Modifier.PUBLIC | Modifier.ABSTRACT | Modifier.FINAL);
         Asserts.assertEQ(Object[].class.getModifiers(), Modifier.PUBLIC | Modifier.ABSTRACT | Modifier.FINAL);
         Asserts.assertEQ(CanonicalizeGetModifiers[].class.getModifiers(), Modifier.PUBLIC | Modifier.ABSTRACT | Modifier.FINAL);
 
-        Asserts.assertEQ(new CanonicalizeGetModifiers().getClass().getModifiers(), Modifier.PUBLIC);
-        Asserts.assertEQ(new T1().getClass().getModifiers(), Modifier.PUBLIC | Modifier.STATIC);
-        Asserts.assertEQ(new T2().getClass().getModifiers(), Modifier.PUBLIC | Modifier.FINAL | Modifier.STATIC);
-        Asserts.assertEQ(new T3().getClass().getModifiers(), Modifier.PRIVATE | Modifier.STATIC);
-        Asserts.assertEQ(new T4().getClass().getModifiers(), Modifier.PROTECTED | Modifier.STATIC);
+        Asserts.assertEQ(new CanonicalizeGetModifiers().getClass().getModifiers(), Modifier.PUBLIC | Modifier.IDENTITY);
+        Asserts.assertEQ(new T1().getClass().getModifiers(), Modifier.PUBLIC | Modifier.STATIC | Modifier.IDENTITY);
+        Asserts.assertEQ(new T2().getClass().getModifiers(), Modifier.PUBLIC | Modifier.FINAL | Modifier.STATIC | Modifier.IDENTITY);
+        Asserts.assertEQ(new T3().getClass().getModifiers(), Modifier.PRIVATE | Modifier.STATIC | Modifier.IDENTITY);
+        Asserts.assertEQ(new T4().getClass().getModifiers(), Modifier.PROTECTED | Modifier.STATIC | Modifier.IDENTITY);
         try {
             // null_check
             poison.getModifiers();
         } catch(NullPointerException npe) {
             // got it!
diff a/test/hotspot/jtreg/compiler/c2/irTests/TestOptimizeUnstableIf.java b/test/hotspot/jtreg/compiler/c2/irTests/TestOptimizeUnstableIf.java
--- a/test/hotspot/jtreg/compiler/c2/irTests/TestOptimizeUnstableIf.java
+++ b/test/hotspot/jtreg/compiler/c2/irTests/TestOptimizeUnstableIf.java
@@ -40,11 +40,12 @@
         TestFramework.run();
     }
 
     @Test
     @Arguments({Argument.MAX}) // the argument needs to be big enough to fall out of cache.
-    @IR(failOn = {IRNode.ALLOC_OF, "Integer"})
+    // TODO 8328675 Re-enable
+    // @IR(failOn = {IRNode.ALLOC_OF, "Integer"})
     public static int boxing_object(int value) {
         Integer ii = Integer.valueOf(value);
         int sum = 0;
 
         if (value > 999_999) {
diff a/test/hotspot/jtreg/compiler/c2/unloaded/TestInlineUnloaded.java b/test/hotspot/jtreg/compiler/c2/unloaded/TestInlineUnloaded.java
--- a/test/hotspot/jtreg/compiler/c2/unloaded/TestInlineUnloaded.java
+++ b/test/hotspot/jtreg/compiler/c2/unloaded/TestInlineUnloaded.java
@@ -184,10 +184,11 @@
 
         pb.command(JDKToolFinder.getJDKTool("java"),
             "-cp", "launcher.jar",
             "-XX:+IgnoreUnrecognizedVMOptions", "-showversion",
             "-XX:-TieredCompilation", "-Xbatch",
+            "-XX:-InlineTypeReturnedAsFields", // TODO Remove this once 8284443 fixed handling of unloaded return types
             "-XX:+PrintCompilation", "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintInlining",
             "-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly,*TestNull::run",
             Launcher.class.getName(), testCaseName);
 
         System.out.println("Command line: [" + pb.command() + "]");
diff a/test/hotspot/jtreg/compiler/ciReplay/TestInliningProtectionDomain.java b/test/hotspot/jtreg/compiler/ciReplay/TestInliningProtectionDomain.java
--- a/test/hotspot/jtreg/compiler/ciReplay/TestInliningProtectionDomain.java
+++ b/test/hotspot/jtreg/compiler/ciReplay/TestInliningProtectionDomain.java
@@ -40,11 +40,12 @@
 public class TestInliningProtectionDomain extends InliningBase {
 
     public static void main(String[] args) {
         new TestInliningProtectionDomain(ProtectionDomainTestCompiledBefore.class, true);
         new TestInliningProtectionDomain(ProtectionDomainTestNoOtherCompilationPublic.class, false);
-        new TestInliningProtectionDomain(ProtectionDomainTestNoOtherCompilationPrivate.class, false);
+        // TODO Re-enable this once 8284443 fixed handling of unloaded return types
+        // new TestInliningProtectionDomain(ProtectionDomainTestNoOtherCompilationPrivate.class, false);
         new TestInliningProtectionDomain(ProtectionDomainTestNoOtherCompilationPrivateString.class, false);
     }
 
     public TestInliningProtectionDomain(Class<?> testClass, boolean compileBar) {
         super(testClass);
diff a/test/hotspot/jtreg/compiler/gcbarriers/TestZGCBarrierElision.java b/test/hotspot/jtreg/compiler/gcbarriers/TestZGCBarrierElision.java
--- a/test/hotspot/jtreg/compiler/gcbarriers/TestZGCBarrierElision.java
+++ b/test/hotspot/jtreg/compiler/gcbarriers/TestZGCBarrierElision.java
@@ -27,21 +27,21 @@
 import java.lang.invoke.VarHandle;
 import java.lang.invoke.MethodHandles;
 import java.util.concurrent.ThreadLocalRandom;
 
 /**
- * @test
+ * @test id=Z
  * @summary Test that the ZGC barrier elision optimization does not elide
  *          necessary barriers. The tests use volatile memory accesses and
  *          blackholes to prevent C2 from simply optimizing them away.
  * @library /test/lib /
  * @requires vm.gc.ZGenerational
  * @run driver compiler.gcbarriers.TestZGCBarrierElision test-correctness
  */
 
 /**
- * @test
+ * @test id=ZGen
  * @summary Test that the ZGC barrier elision optimization elides unnecessary
  *          barriers following simple allocation and domination rules.
  * @library /test/lib /
  * @requires vm.gc.ZGenerational & (vm.simpleArch == "x64" | vm.simpleArch == "aarch64")
  * @run driver compiler.gcbarriers.TestZGCBarrierElision test-effectiveness
@@ -193,11 +193,12 @@
         Common.blackhole(o);
         Common.field1VarHandle.getAndSet​(o, i);
     }
 
     @Test
-    @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
+// TODO 8329234
+//    @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
     @IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE)
     static void testLoadThenAtomic(Outer o, Inner i) {
         Common.blackhole(o.field1);
         Common.field1VarHandle.getAndSet​(o, i);
     }
diff a/test/hotspot/jtreg/compiler/intrinsics/klass/TestGetModifiers.java b/test/hotspot/jtreg/compiler/intrinsics/klass/TestGetModifiers.java
--- a/test/hotspot/jtreg/compiler/intrinsics/klass/TestGetModifiers.java
+++ b/test/hotspot/jtreg/compiler/intrinsics/klass/TestGetModifiers.java
@@ -23,28 +23,31 @@
  */
 
 /*
  * @test
  * @library /test/lib
+ * @enablePreview
  * @run main/othervm -Xint
  *                   -XX:CompileCommand=dontinline,*TestGetModifiers.test
  *                   compiler.intrinsics.klass.TestGetModifiers
  */
 
 /*
  * @test
  * @requires vm.compiler1.enabled
  * @library /test/lib
+ * @enablePreview
  * @run main/othervm -XX:TieredStopAtLevel=1 -XX:+TieredCompilation
  *                   -XX:CompileCommand=dontinline,*TestGetModifiers.test
  *                   compiler.intrinsics.klass.TestGetModifiers
  */
 
 /*
  * @test
  * @requires vm.compiler2.enabled
  * @library /test/lib
+ * @enablePreview
  * @run main/othervm -XX:-TieredCompilation
  *                   -XX:CompileCommand=dontinline,*TestGetModifiers.test
  *                   compiler.intrinsics.klass.TestGetModifiers
  */
 
@@ -82,15 +85,15 @@
             }
         }
     }
 
     public static void main(String... args) {
-        test(T1.class,                                      PUBLIC | STATIC);
-        test(T2.class,                                      PUBLIC | FINAL | STATIC);
-        test(T3.class,                                      PRIVATE | STATIC);
-        test(T4.class,                                      PROTECTED | STATIC);
-        test(new TestGetModifiers().new T5().getClass(),    0);
+        test(T1.class,                                      PUBLIC | STATIC | IDENTITY);
+        test(T2.class,                                      PUBLIC | FINAL | STATIC | IDENTITY);
+        test(T3.class,                                      PRIVATE | STATIC | IDENTITY);
+        test(T4.class,                                      PROTECTED | STATIC | IDENTITY);
+        test(new TestGetModifiers().new T5().getClass(),    IDENTITY);
         test(T6.class,                                      ABSTRACT | STATIC | INTERFACE);
 
         test(int.class,                                     PUBLIC | ABSTRACT | FINAL);
         test(long.class,                                    PUBLIC | ABSTRACT | FINAL);
         test(double.class,                                  PUBLIC | ABSTRACT | FINAL);
@@ -107,12 +110,12 @@
         test(byte[].class,                                  PUBLIC | ABSTRACT | FINAL);
         test(short[].class,                                 PUBLIC | ABSTRACT | FINAL);
         test(Object[].class,                                PUBLIC | ABSTRACT | FINAL);
         test(TestGetModifiers[].class,                      PUBLIC | ABSTRACT | FINAL);
 
-        test(new TestGetModifiers().getClass(),             PUBLIC);
-        test(new T1().getClass(),                           PUBLIC | STATIC);
-        test(new T2().getClass(),                           PUBLIC | FINAL | STATIC);
-        test(new T3().getClass(),                           PRIVATE | STATIC);
-        test(new T4().getClass(),                           PROTECTED | STATIC);
+        test(new TestGetModifiers().getClass(),             PUBLIC | IDENTITY);
+        test(new T1().getClass(),                           PUBLIC | STATIC | IDENTITY);
+        test(new T2().getClass(),                           PUBLIC | FINAL | STATIC | IDENTITY);
+        test(new T3().getClass(),                           PRIVATE | STATIC | IDENTITY);
+        test(new T4().getClass(),                           PROTECTED | STATIC | IDENTITY);
     }
 }
diff a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java
--- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java
+++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java
@@ -207,11 +207,12 @@
         String javaName = lambda.getClass().getName();
         assertEquals(typeName, toInternalName(javaName));
         assertEquals(javaName, internalNameToJava(typeName, true, true));
     }
 
-    @Test
+    // TODO 8291719
+    // @Test
     public void getModifiersTest() {
         for (Class<?> c : classes) {
             ResolvedJavaType type = metaAccess.lookupJavaType(c);
             int mask = Modifier.classModifiers() & ~Modifier.STATIC;
             int expected = c.getModifiers() & mask;
@@ -285,11 +286,13 @@
     public void getInterfacesTest() {
         for (Class<?> c : classes) {
             ResolvedJavaType type = metaAccess.lookupJavaType(c);
             Class<?>[] expected = c.getInterfaces();
             ResolvedJavaType[] actual = type.getInterfaces();
-            assertEquals(expected.length, actual.length);
+            // With injection of the IdentityObject interface by the JVM, the number of
+            // interfaces visible through reflection and through JVMCI could differ by one
+            assertTrue(expected.length == actual.length || (actual.length - expected.length) == 1);
             for (int i = 0; i < expected.length; i++) {
                 assertTrue(actual[i].equals(metaAccess.lookupJavaType(expected[i])));
             }
         }
     }
diff a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java
--- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java
+++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java
@@ -274,29 +274,29 @@
         superWordNodes(ADD_REDUCTION_VL, "AddReductionVL");
     }
 
     public static final String ALLOC = PREFIX + "ALLOC" + POSTFIX;
     static {
-        String optoRegex = "(.*precise .*\\R((.*(?i:mov|mv|xorl|nop|spill).*|\\s*)\\R)*.*(?i:call,static).*wrapper for: _new_instance_Java" + END;
+        String optoRegex = "(.*precise .*\\R((.*(?i:mov|mv|xorl|nop|spill|pushq|popq).*|\\s*)\\R)*.*(?i:call,static).*wrapper for: _new_instance_Java" + END;
         allocNodes(ALLOC, "Allocate", optoRegex);
     }
 
     public static final String ALLOC_OF = COMPOSITE_PREFIX + "ALLOC_OF" + POSTFIX;
     static {
-        String regex = "(.*precise .*" + IS_REPLACED + ":.*\\R((.*(?i:mov|mv|xorl|nop|spill).*|\\s*)\\R)*.*(?i:call,static).*wrapper for: _new_instance_Java" + END;
+        String regex = "(.*precise .*" + IS_REPLACED + ":.*\\R((.*(?i:mov|mv|xorl|nop|spill|pushq|popq).*|\\s*)\\R)*.*(?i:call,static).*wrapper for: _new_instance_Java" + END;
         optoOnly(ALLOC_OF, regex);
     }
 
     public static final String ALLOC_ARRAY = PREFIX + "ALLOC_ARRAY" + POSTFIX;
     static {
-        String optoRegex = "(.*precise \\[.*\\R((.*(?i:mov|mv|xor|nop|spill).*|\\s*|.*(LGHI|LI).*)\\R)*.*(?i:call,static).*wrapper for: _new_array_Java" + END;
+        String optoRegex = "(.*precise \\[.*\\R((.*(?i:mov|mv|xor|nop|spill|pushq|popq).*|\\s*|.*(LGHI|LI).*)\\R)*.*(?i:call,static).*wrapper for: _new_array_Java" + END;
         allocNodes(ALLOC_ARRAY, "AllocateArray", optoRegex);
     }
 
     public static final String ALLOC_ARRAY_OF = COMPOSITE_PREFIX + "ALLOC_ARRAY_OF" + POSTFIX;
     static {
-        String regex = "(.*precise \\[.*" + IS_REPLACED + ":.*\\R((.*(?i:mov|mv|xorl|nop|spill).*|\\s*|.*(LGHI|LI).*)\\R)*.*(?i:call,static).*wrapper for: _new_array_Java" + END;
+        String regex = "(.*precise \\[.*" + IS_REPLACED + ":.*\\R((.*(?i:mov|mv|xorl|nop|spill|pushq|popq).*|\\s*|.*(LGHI|LI).*)\\R)*.*(?i:call,static).*wrapper for: _new_array_Java" + END;
         optoOnly(ALLOC_ARRAY_OF, regex);
     }
 
     public static final String OR = PREFIX + "OR" + POSTFIX;
     static {
@@ -549,10 +549,15 @@
     public static final String IF = PREFIX + "IF" + POSTFIX;
     static {
         beforeMatchingNameRegex(IF, "If\\b");
     }
 
+    public static final String INLINE_TYPE = PREFIX + "INLINE_TYPE" + POSTFIX;
+    static {
+        beforeMatchingNameRegex(INLINE_TYPE, "InlineType");
+    }
+
     // Does not work for VM builds without JVMCI like x86_32 (a rule containing this regex will be skipped without having JVMCI built).
     public static final String INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP = PREFIX + "INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP" + POSTFIX;
     static {
         trapNodes(INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP, "intrinsic_or_type_checked_inlining");
     }
@@ -2148,11 +2153,11 @@
 
     /**
      * Apply {@code regex} on all machine independent ideal graph phases up to and including
      * {@link CompilePhase#BEFORE_MATCHING}.
      */
-    private static void beforeMatching(String irNodePlaceholder, String regex) {
+    public static void beforeMatching(String irNodePlaceholder, String regex) {
         IR_NODE_MAPPINGS.put(irNodePlaceholder, new RegexTypeEntry(RegexType.IDEAL_INDEPENDENT, regex));
     }
 
     /**
      * Apply {@code irNodeRegex} as regex for the IR node name on all machine independent ideal graph phases up to and
@@ -2194,11 +2199,11 @@
 
     /**
      * Apply {@code regex} on all machine dependant ideal graph phases (i.e. on the mach graph) starting from
      * {@link CompilePhase#MATCHING}.
      */
-    private static void optoOnly(String irNodePlaceholder, String regex) {
+    public static void optoOnly(String irNodePlaceholder, String regex) {
         IR_NODE_MAPPINGS.put(irNodePlaceholder, new RegexTypeEntry(RegexType.OPTO_ASSEMBLY, regex));
     }
 
     private static void machOnly(String irNodePlaceholder, String regex) {
         IR_NODE_MAPPINGS.put(irNodePlaceholder, new RegexTypeEntry(RegexType.MACH, regex));
diff a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java
--- a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java
+++ b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java
@@ -547,10 +547,22 @@
      */
     public static void assertDeoptimizedByC2(Method m) {
         TestVM.assertDeoptimizedByC2(m);
     }
 
+    /**
+     * Checks if deopt of {@code m} is stable at the specified {@code compLevel}.
+     *
+     * @param m the method to be checked.
+     * @param compLevel the compilation level.
+     * @return {@code true} if deopt of {@code m} is stable at {@code compLevel};
+     *         {@code false} otherwise.
+     */
+    public static boolean isStableDeopt(Method m, CompLevel compLevel) {
+        return TestVM.isStableDeopt(m, compLevel);
+    }
+
     /**
      * Returns a different boolean each time this method is invoked (switching between {@code false} and {@code true}).
      * The very first invocation returns {@code false}. Note that this method could be used by different tests and
      * thus the first invocation for a test could be {@code true} or {@code false} depending on how many times
      * other tests have already invoked this method.
diff a/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java
--- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java
+++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java
@@ -99,10 +99,11 @@
     private static final boolean SHUFFLE_TESTS = Boolean.parseBoolean(System.getProperty("ShuffleTests", "true"));
     // Use separate flag as VERIFY_IR could have been set by user but due to other flags it was disabled by flag VM.
     private static final boolean PRINT_VALID_IR_RULES = Boolean.getBoolean("ShouldDoIRVerification");
     protected static final long PER_METHOD_TRAP_LIMIT = (Long)WHITE_BOX.getVMFlag("PerMethodTrapLimit");
     protected static final boolean PROFILE_INTERPRETER = (Boolean)WHITE_BOX.getVMFlag("ProfileInterpreter");
+    protected static final boolean DEOPT_BARRIERS_ALOT = (Boolean)WHITE_BOX.getVMFlag("DeoptimizeNMethodBarriersALot");
     private static final boolean FLIP_C1_C2 = Boolean.getBoolean("FlipC1C2");
     private static final boolean IGNORE_COMPILER_CONTROLS = Boolean.getBoolean("IgnoreCompilerControls");
 
     private final HashMap<Method, DeclaredTest> declaredTests = new HashMap<>();
     private final List<AbstractTest> allTests = new ArrayList<>();
@@ -248,12 +249,15 @@
         checkForcedCompilationsCompleted();
         runTests();
     }
 
     private void setupTests() {
-        for (Class<?> clazz : testClass.getDeclaredClasses()) {
-            checkAnnotationsInClass(clazz, "inner");
+        // TODO remove this once JDK-8273591 is fixed
+        if (!IGNORE_COMPILER_CONTROLS) {
+            for (Class<?> clazz : testClass.getDeclaredClasses()) {
+                checkAnnotationsInClass(clazz, "inner");
+            }
         }
         if (DUMP_REPLAY) {
             addReplay();
         }
         // Make sure to first setup test methods and make them non-inlineable and only then process compile commands.
@@ -893,28 +897,27 @@
     public static boolean isCompiledAtLevel(Method m, CompLevel compLevel) {
         return compiledAtLevel(m, compLevel) == TriState.Yes;
     }
 
     public static void assertDeoptimizedByC1(Method m) {
-        if (notUnstableDeoptAssertion(m, CompLevel.C1_SIMPLE)) {
-            TestRun.check(compiledByC1(m) != TriState.Yes || PER_METHOD_TRAP_LIMIT == 0 || !PROFILE_INTERPRETER,
-                          m + " should have been deoptimized by C1");
+        if (isStableDeopt(m, CompLevel.C1_SIMPLE)) {
+            TestRun.check(compiledByC1(m) != TriState.Yes, m + " should have been deoptimized by C1");
         }
     }
 
     public static void assertDeoptimizedByC2(Method m) {
-        if (notUnstableDeoptAssertion(m, CompLevel.C2)) {
-            TestRun.check(compiledByC2(m) != TriState.Yes || PER_METHOD_TRAP_LIMIT == 0 || !PROFILE_INTERPRETER,
-                          m + " should have been deoptimized by C2");
+        if (isStableDeopt(m, CompLevel.C2)) {
+            TestRun.check(compiledByC2(m) != TriState.Yes, m + " should have been deoptimized by C2");
         }
     }
 
     /**
      * Some VM flags could make the deopt assertions unstable.
      */
-    private static boolean notUnstableDeoptAssertion(Method m, CompLevel level) {
+    public static boolean isStableDeopt(Method m, CompLevel level) {
         return (USE_COMPILER && !XCOMP && !IGNORE_COMPILER_CONTROLS && !TEST_C1 &&
+                PER_METHOD_TRAP_LIMIT != 0 && PROFILE_INTERPRETER && !DEOPT_BARRIERS_ALOT &&
                 (!EXCLUDE_RANDOM || WHITE_BOX.isMethodCompilable(m, level.getValue(), false)));
     }
 
     public static void assertCompiledByC1(Method m) {
         TestRun.check(compiledByC1(m) != TriState.No, m + " should have been C1 compiled");
@@ -966,11 +969,11 @@
                     return TriState.Yes;
                 }
                 default -> throw new TestRunException("compiledAtLevel() should not be called with " + level);
             }
         }
-        if (!USE_COMPILER || XCOMP || TEST_C1 || IGNORE_COMPILER_CONTROLS || FLIP_C1_C2 ||
+        if (!USE_COMPILER || XCOMP || TEST_C1 || IGNORE_COMPILER_CONTROLS || FLIP_C1_C2 || DEOPT_BARRIERS_ALOT ||
             (EXCLUDE_RANDOM && !WHITE_BOX.isMethodCompilable(m, level.getValue(), false))) {
             return TriState.Maybe;
         }
         return TriState.No;
     }
diff a/test/hotspot/jtreg/compiler/tiered/ConstantGettersTransitionsTest.java b/test/hotspot/jtreg/compiler/tiered/ConstantGettersTransitionsTest.java
--- a/test/hotspot/jtreg/compiler/tiered/ConstantGettersTransitionsTest.java
+++ b/test/hotspot/jtreg/compiler/tiered/ConstantGettersTransitionsTest.java
@@ -22,10 +22,11 @@
  */
 
 /**
  * @test ConstantGettersTransitionsTest
  * @summary Test the correctness of compilation level transitions for constant getters methods
+ * @requires vm.opt.final.TieredCompilation
  * @library /test/lib /
  * @modules java.base/jdk.internal.misc
  *          java.management
  *
  * @build jdk.test.whitebox.WhiteBox
diff a/test/hotspot/jtreg/compiler/types/TestMeetIncompatibleInterfaceArrays.java b/test/hotspot/jtreg/compiler/types/TestMeetIncompatibleInterfaceArrays.java
--- a/test/hotspot/jtreg/compiler/types/TestMeetIncompatibleInterfaceArrays.java
+++ b/test/hotspot/jtreg/compiler/types/TestMeetIncompatibleInterfaceArrays.java
@@ -23,10 +23,11 @@
 
 /*
  * @test
  * @bug 8141551
  * @summary C2 can not handle returns with inccompatible interface arrays
+ * @requires vm.opt.final.TieredCompilation
  * @requires vm.compMode == "Xmixed" & vm.flavor == "server"
  * @modules java.base/jdk.internal.org.objectweb.asm
  *          java.base/jdk.internal.misc
  * @library /test/lib /
  *
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/BlackholeTest.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/BlackholeTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/BlackholeTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2021, 2024, Red Hat, 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
+ * 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 compiler.valhalla.inlinetypes;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+/*
+ * @test BlackholeTest
+ * @summary Check that blackholes work with inline types
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm
+ *      -Xbatch -XX:+UnlockExperimentalVMOptions
+ *      -XX:CompileCommand=blackhole,compiler/valhalla/inlinetypes/BlackholeTest.blackhole
+ *      compiler.valhalla.inlinetypes.BlackholeTest
+ */
+
+public class BlackholeTest {
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue {
+        int x = 0;
+    }
+
+    @NullRestricted
+    static MyValue v;
+    @NullRestricted
+    static volatile MyValue vv;
+
+    public static void main(String[] args) {
+        for (int c = 0; c < 5; c++) {
+            testNew();
+            testField();
+            testVolatileField();
+        }
+    }
+
+    private static void testNew() {
+        for (int c = 0; c < 100_000; c++) {
+            blackhole(new MyValue());
+        }
+    }
+
+    private static void testField() {
+        for (int c = 0; c < 100_000; c++) {
+            blackhole(v);
+        }
+    }
+
+    private static void testVolatileField() {
+        for (int c = 0; c < 100_000; c++) {
+            blackhole(vv);
+        }
+    }
+
+    public static void blackhole(MyValue v) {
+        // Should be empty
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/GetfieldChains.jcod b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/GetfieldChains.jcod
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/GetfieldChains.jcod
@@ -0,0 +1,702 @@
+/*
+ * Copyright (c) 2020, 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.
+ */
+
+// Generated from the following class files with PointN.x removed and RectangleP.p1 made private
+// java org.openjdk.asmtools.Main jdec NamedRectangleN.class NamedRectangleP.class PointN.class RectangleN.class RectangleP.class
+
+/*
+package compiler.valhalla.inlinetypes;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class PointN {
+    int x; // Removed
+    int y;
+
+    PointN(int i, int j) {
+        x = i;
+        y = j;
+    }
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class RectangleN {
+    @NullRestricted
+    PointN p1 = new PointN(4, 7);
+}
+
+class NamedRectangleN {
+    @NullRestricted
+    RectangleN rect = new RectangleN();
+    String name = "";
+
+    static int getP1X(NamedRectangleN nr) {
+        return nr.rect.p1.x;
+    }
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class RectangleP {
+    @NullRestricted
+    PointN p1 = new PointN(4, 7); // Made private
+}
+
+class NamedRectangleP {
+    @NullRestricted
+    RectangleP rect = new RectangleP();
+    String name = "";
+
+    static int getP1Y(NamedRectangleP nr) {
+        return nr.rect.p1.y;
+    }
+}
+*/
+
+class compiler/valhalla/inlinetypes/NamedRectangleN {
+  0xCAFEBABE;
+  65535; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    class #8; // #7
+    Utf8 "compiler/valhalla/inlinetypes/RectangleN"; // #8
+    Method #7 #3; // #9
+    Field #11 #12; // #10
+    class #13; // #11
+    NameAndType #14 #15; // #12
+    Utf8 "compiler/valhalla/inlinetypes/NamedRectangleN"; // #13
+    Utf8 "rect"; // #14
+    Utf8 "Lcompiler/valhalla/inlinetypes/RectangleN;"; // #15
+    String #17; // #16
+    Utf8 ""; // #17
+    Field #11 #19; // #18
+    NameAndType #20 #21; // #19
+    Utf8 "name"; // #20
+    Utf8 "Ljava/lang/String;"; // #21
+    Field #7 #23; // #22
+    NameAndType #24 #25; // #23
+    Utf8 "p1"; // #24
+    Utf8 "Lcompiler/valhalla/inlinetypes/PointN;"; // #25
+    Field #27 #28; // #26
+    class #29; // #27
+    NameAndType #30 #31; // #28
+    Utf8 "compiler/valhalla/inlinetypes/PointN"; // #29
+    Utf8 "x"; // #30
+    Utf8 "I"; // #31
+    Utf8 "RuntimeVisibleAnnotations"; // #32
+    Utf8 "Ljdk/internal/vm/annotation/NullRestricted;"; // #33
+    Utf8 "Code"; // #34
+    Utf8 "LineNumberTable"; // #35
+    Utf8 "getP1X"; // #36
+    Utf8 "(Lcompiler/valhalla/inlinetypes/NamedRectangleN;)I"; // #37
+    Utf8 "SourceFile"; // #38
+    Utf8 "Point.java"; // #39
+    Utf8 "Preload"; // #40
+  } // Constant Pool
+
+  0x0020; // access
+  #11;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0000; // access
+      #14; // name_index
+      #15; // descriptor_index
+      [] { // Attributes
+        Attr(#32) { // RuntimeVisibleAnnotations
+          [] { // annotations
+            {  //  annotation
+              #33;
+              [] { // element_value_pairs
+              }  //  element_value_pairs
+            }  //  annotation
+          }
+        } // end RuntimeVisibleAnnotations
+      } // Attributes
+    }
+    ;
+    {  // field
+      0x0000; // access
+      #20; // name_index
+      #21; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#34) { // Code
+          3; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB700012ABB0007;
+            0x59B70009B5000A2A;
+            0x1210B50012B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#35) { // LineNumberTable
+              [] { // line_number_table
+                0  26;
+                4  27;
+                15  29;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0008; // access
+      #36; // name_index
+      #37; // descriptor_index
+      [] { // Attributes
+        Attr(#34) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB4000AB40016B4;
+            0x001AAC;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#35) { // LineNumberTable
+              [] { // line_number_table
+                0  32;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#38) { // SourceFile
+      #39;
+    } // end SourceFile
+    ;
+    Attr(#40) { // Preload
+      0x00010007;
+    } // end Preload
+  } // Attributes
+} // end class compiler/valhalla/inlinetypes/NamedRectangleN
+class compiler/valhalla/inlinetypes/NamedRectangleP {
+  0xCAFEBABE;
+  65535; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    class #8; // #7
+    Utf8 "compiler/valhalla/inlinetypes/RectangleP"; // #8
+    Method #7 #3; // #9
+    Field #11 #12; // #10
+    class #13; // #11
+    NameAndType #14 #15; // #12
+    Utf8 "compiler/valhalla/inlinetypes/NamedRectangleP"; // #13
+    Utf8 "rect"; // #14
+    Utf8 "Lcompiler/valhalla/inlinetypes/RectangleP;"; // #15
+    String #17; // #16
+    Utf8 ""; // #17
+    Field #11 #19; // #18
+    NameAndType #20 #21; // #19
+    Utf8 "name"; // #20
+    Utf8 "Ljava/lang/String;"; // #21
+    Field #7 #23; // #22
+    NameAndType #24 #25; // #23
+    Utf8 "p1"; // #24
+    Utf8 "Lcompiler/valhalla/inlinetypes/PointN;"; // #25
+    Field #27 #28; // #26
+    class #29; // #27
+    NameAndType #30 #31; // #28
+    Utf8 "compiler/valhalla/inlinetypes/PointN"; // #29
+    Utf8 "y"; // #30
+    Utf8 "I"; // #31
+    Utf8 "RuntimeVisibleAnnotations"; // #32
+    Utf8 "Ljdk/internal/vm/annotation/NullRestricted;"; // #33
+    Utf8 "Code"; // #34
+    Utf8 "LineNumberTable"; // #35
+    Utf8 "getP1Y"; // #36
+    Utf8 "(Lcompiler/valhalla/inlinetypes/NamedRectangleP;)I"; // #37
+    Utf8 "SourceFile"; // #38
+    Utf8 "Point.java"; // #39
+    Utf8 "Preload"; // #40
+  } // Constant Pool
+
+  0x0020; // access
+  #11;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0000; // access
+      #14; // name_index
+      #15; // descriptor_index
+      [] { // Attributes
+        Attr(#32) { // RuntimeVisibleAnnotations
+          [] { // annotations
+            {  //  annotation
+              #33;
+              [] { // element_value_pairs
+              }  //  element_value_pairs
+            }  //  annotation
+          }
+        } // end RuntimeVisibleAnnotations
+      } // Attributes
+    }
+    ;
+    {  // field
+      0x0000; // access
+      #20; // name_index
+      #21; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#34) { // Code
+          3; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB700012ABB0007;
+            0x59B70009B5000A2A;
+            0x1210B50012B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#35) { // LineNumberTable
+              [] { // line_number_table
+                0  43;
+                4  44;
+                15  46;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0008; // access
+      #36; // name_index
+      #37; // descriptor_index
+      [] { // Attributes
+        Attr(#34) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB4000AB40016B4;
+            0x001AAC;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#35) { // LineNumberTable
+              [] { // line_number_table
+                0  49;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#38) { // SourceFile
+      #39;
+    } // end SourceFile
+    ;
+    Attr(#40) { // Preload
+      0x00010007;
+    } // end Preload
+  } // Attributes
+} // end class compiler/valhalla/inlinetypes/NamedRectangleP
+class compiler/valhalla/inlinetypes/PointN {
+  0xCAFEBABE;
+  65535; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Field #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "compiler/valhalla/inlinetypes/PointN"; // #4
+    Utf8 "y"; // #5
+    Utf8 "I"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #12; // #9
+    Utf8 "java/lang/Object"; // #10
+    Utf8 "<init>"; // #11
+    Utf8 "()V"; // #12
+    Utf8 "(II)V"; // #13
+    Utf8 "Code"; // #14
+    Utf8 "LineNumberTable"; // #15
+    Utf8 "SourceFile"; // #16
+    Utf8 "PointN.java"; // #17
+    Utf8 "RuntimeVisibleAnnotations"; // #18
+    Utf8 "Ljdk/internal/vm/annotation/ImplicitlyConstructible;"; // #19
+    Utf8 "Ljdk/internal/vm/annotation/LooselyConsistentValue;"; // #20
+  } // Constant Pool
+
+  0x0010; // access
+  #2;// this_cpx
+  #8;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0810; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #11; // name_index
+      #13; // descriptor_index
+      [] { // Attributes
+        Attr(#14) { // Code
+          2; // max_stack
+          3; // max_locals
+          Bytes[]{
+            0x2A1CB500012AB700;
+            0x07B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#15) { // LineNumberTable
+              [] { // line_number_table
+                0  13;
+                5  12;
+                9  14;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#16) { // SourceFile
+      #17;
+    } // end SourceFile
+    ;
+    Attr(#18) { // RuntimeVisibleAnnotations
+      [] { // annotations
+        {  //  annotation
+          #19;
+          [] { // element_value_pairs
+          }  //  element_value_pairs
+        }  //  annotation
+        ;
+        {  //  annotation
+          #20;
+          [] { // element_value_pairs
+          }  //  element_value_pairs
+        }  //  annotation
+      }
+    } // end RuntimeVisibleAnnotations
+  } // Attributes
+} // end class compiler/valhalla/inlinetypes/PointN
+class compiler/valhalla/inlinetypes/RectangleN {
+  0xCAFEBABE;
+  65535; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1
+    Utf8 "compiler/valhalla/inlinetypes/PointN"; // #2
+    Method #1 #4; // #3
+    NameAndType #5 #6; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "(II)V"; // #6
+    Field #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #12; // #9
+    Utf8 "compiler/valhalla/inlinetypes/RectangleN"; // #10
+    Utf8 "p1"; // #11
+    Utf8 "Lcompiler/valhalla/inlinetypes/PointN;"; // #12
+    Method #14 #15; // #13
+    class #16; // #14
+    NameAndType #5 #17; // #15
+    Utf8 "java/lang/Object"; // #16
+    Utf8 "()V"; // #17
+    Utf8 "RuntimeVisibleAnnotations"; // #18
+    Utf8 "Ljdk/internal/vm/annotation/NullRestricted;"; // #19
+    Utf8 "Code"; // #20
+    Utf8 "LineNumberTable"; // #21
+    Utf8 "SourceFile"; // #22
+    Utf8 "Point.java"; // #23
+    Utf8 "Ljdk/internal/vm/annotation/ImplicitlyConstructible;"; // #24
+    Utf8 "Ljdk/internal/vm/annotation/LooselyConsistentValue;"; // #25
+    Utf8 "Preload"; // #26
+  } // Constant Pool
+
+  0x0010; // access
+  #8;// this_cpx
+  #14;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0810; // access
+      #11; // name_index
+      #12; // descriptor_index
+      [] { // Attributes
+        Attr(#18) { // RuntimeVisibleAnnotations
+          [] { // annotations
+            {  //  annotation
+              #19;
+              [] { // element_value_pairs
+              }  //  element_value_pairs
+            }  //  annotation
+          }
+        } // end RuntimeVisibleAnnotations
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #17; // descriptor_index
+      [] { // Attributes
+        Attr(#20) { // Code
+          5; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2ABB000159071007;
+            0xB70003B500072AB7;
+            0x000DB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#21) { // LineNumberTable
+              [] { // line_number_table
+                0  22;
+                14  21;
+                18  22;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#22) { // SourceFile
+      #23;
+    } // end SourceFile
+    ;
+    Attr(#18) { // RuntimeVisibleAnnotations
+      [] { // annotations
+        {  //  annotation
+          #24;
+          [] { // element_value_pairs
+          }  //  element_value_pairs
+        }  //  annotation
+        ;
+        {  //  annotation
+          #25;
+          [] { // element_value_pairs
+          }  //  element_value_pairs
+        }  //  annotation
+      }
+    } // end RuntimeVisibleAnnotations
+    ;
+    Attr(#26) { // Preload
+      0x00010001;
+    } // end Preload
+  } // Attributes
+} // end class compiler/valhalla/inlinetypes/RectangleN
+class compiler/valhalla/inlinetypes/RectangleP {
+  0xCAFEBABE;
+  65535; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1
+    Utf8 "compiler/valhalla/inlinetypes/PointN"; // #2
+    Method #1 #4; // #3
+    NameAndType #5 #6; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "(II)V"; // #6
+    Field #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #12; // #9
+    Utf8 "compiler/valhalla/inlinetypes/RectangleP"; // #10
+    Utf8 "p1"; // #11
+    Utf8 "Lcompiler/valhalla/inlinetypes/PointN;"; // #12
+    Method #14 #15; // #13
+    class #16; // #14
+    NameAndType #5 #17; // #15
+    Utf8 "java/lang/Object"; // #16
+    Utf8 "()V"; // #17
+    Utf8 "RuntimeVisibleAnnotations"; // #18
+    Utf8 "Ljdk/internal/vm/annotation/NullRestricted;"; // #19
+    Utf8 "Code"; // #20
+    Utf8 "LineNumberTable"; // #21
+    Utf8 "SourceFile"; // #22
+    Utf8 "Point.java"; // #23
+    Utf8 "Ljdk/internal/vm/annotation/ImplicitlyConstructible;"; // #24
+    Utf8 "Ljdk/internal/vm/annotation/LooselyConsistentValue;"; // #25
+    Utf8 "Preload"; // #26
+  } // Constant Pool
+
+  0x0010; // access
+  #8;// this_cpx
+  #14;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0812; // access
+      #11; // name_index
+      #12; // descriptor_index
+      [] { // Attributes
+        Attr(#18) { // RuntimeVisibleAnnotations
+          [] { // annotations
+            {  //  annotation
+              #19;
+              [] { // element_value_pairs
+              }  //  element_value_pairs
+            }  //  annotation
+          }
+        } // end RuntimeVisibleAnnotations
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #17; // descriptor_index
+      [] { // Attributes
+        Attr(#20) { // Code
+          5; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2ABB000159071007;
+            0xB70003B500072AB7;
+            0x000DB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#21) { // LineNumberTable
+              [] { // line_number_table
+                0  39;
+                14  38;
+                18  39;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#22) { // SourceFile
+      #23;
+    } // end SourceFile
+    ;
+    Attr(#18) { // RuntimeVisibleAnnotations
+      [] { // annotations
+        {  //  annotation
+          #24;
+          [] { // element_value_pairs
+          }  //  element_value_pairs
+        }  //  annotation
+        ;
+        {  //  annotation
+          #25;
+          [] { // element_value_pairs
+          }  //  element_value_pairs
+        }  //  annotation
+      }
+    } // end RuntimeVisibleAnnotations
+    ;
+    Attr(#26) { // Preload
+      0x00010001;
+    } // end Preload
+  } // Attributes
+} // end class compiler/valhalla/inlinetypes/RectangleP
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeIRNode.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeIRNode.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeIRNode.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2022, 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.IRNode;
+
+public class InlineTypeIRNode {
+    private static final String PREFIX = "_#";
+    private static final String POSTFIX = "#I_";
+    public static final String ALLOC_G = PREFIX + "ALLOC_G" + POSTFIX;
+    static {
+        IRNode.optoOnly(ALLOC_G, InlineTypeRegexes.ALLOC_G);
+    }
+
+    public static final String ALLOCA_G = PREFIX + "ALLOCA_G" + POSTFIX;
+    static {
+        IRNode.optoOnly(ALLOCA_G, InlineTypeRegexes.ALLOCA_G);
+    }
+
+    public static final String MYVALUE_ARRAY_KLASS = PREFIX + "MYVALUE_ARRAY_KLASS" + POSTFIX;    static {
+        IRNode.optoOnly(MYVALUE_ARRAY_KLASS, InlineTypeRegexes.MYVALUE_ARRAY_KLASS);
+    }
+
+    public static final String ALLOC = PREFIX + "ALLOC" + POSTFIX;
+    static {
+        IRNode.optoOnly(ALLOC, InlineTypeRegexes.ALLOC);
+    }
+
+    public static final String ALLOCA = PREFIX + "ALLOCA" + POSTFIX;
+    static {
+        IRNode.optoOnly(ALLOCA, InlineTypeRegexes.ALLOCA);
+    }
+
+    public static final String LOAD = PREFIX + "LOAD" + POSTFIX;
+    static {
+        IRNode.beforeMatching(LOAD, InlineTypeRegexes.LOAD);
+    }
+
+    public static final String LOADK = PREFIX + "LOADK" + POSTFIX;
+    static {
+        IRNode.beforeMatching(LOADK, InlineTypeRegexes.LOADK);
+    }
+
+    public static final String STORE = PREFIX + "STORE" + POSTFIX;
+    static {
+        IRNode.beforeMatching(STORE, InlineTypeRegexes.STORE);
+    }
+
+    public static final String LOOP = PREFIX + "LOOP" + POSTFIX;
+    static {
+        IRNode.beforeMatching(LOOP, InlineTypeRegexes.LOOP);
+    }
+
+    public static final String COUNTEDLOOP = PREFIX + "COUNTEDLOOP" + POSTFIX;
+    static {
+        IRNode.beforeMatching(COUNTEDLOOP, InlineTypeRegexes.COUNTEDLOOP);
+    }
+
+    public static final String COUNTEDLOOP_MAIN = PREFIX + "COUNTEDLOOP_MAIN" + POSTFIX;
+    static {
+        IRNode.beforeMatching(COUNTEDLOOP_MAIN, InlineTypeRegexes.COUNTEDLOOP_MAIN);
+    }
+
+    public static final String TRAP = PREFIX + "TRAP" + POSTFIX;
+    static {
+        IRNode.beforeMatching(TRAP, InlineTypeRegexes.TRAP);
+    }
+
+    public static final String LINKTOSTATIC = PREFIX + "LINKTOSTATIC" + POSTFIX;
+    static {
+        IRNode.beforeMatching(LINKTOSTATIC, InlineTypeRegexes.LINKTOSTATIC);
+    }
+
+    public static final String NPE = PREFIX + "NPE" + POSTFIX;
+    static {
+        IRNode.beforeMatching(NPE, InlineTypeRegexes.NPE);
+    }
+
+    public static final String CALL = PREFIX + "CALL" + POSTFIX;
+    static {
+        IRNode.beforeMatching(CALL, InlineTypeRegexes.CALL);
+    }
+
+    public static final String CALL_LEAF = PREFIX + "CALL_LEAF" + POSTFIX;
+    static {
+        IRNode.optoOnly(CALL_LEAF, InlineTypeRegexes.CALL_LEAF);
+    }
+
+    public static final String CALL_LEAF_NOFP = PREFIX + "CALL_LEAF_NOFP" + POSTFIX;
+    static {
+        IRNode.optoOnly(CALL_LEAF_NOFP, InlineTypeRegexes.CALL_LEAF_NOFP);
+    }
+
+    public static final String CALL_UNSAFE = PREFIX + "CALL_UNSAFE" + POSTFIX;
+    static {
+        IRNode.beforeMatching(CALL_UNSAFE, InlineTypeRegexes.CALL_UNSAFE);
+    }
+
+    public static final String STORE_INLINE_FIELDS = PREFIX + "STORE_INLINE_FIELDS" + POSTFIX;
+    static {
+        IRNode.beforeMatching(STORE_INLINE_FIELDS, InlineTypeRegexes.STORE_INLINE_FIELDS);
+    }
+
+    public static final String SCOBJ = PREFIX + "SCOBJ" + POSTFIX;
+    static {
+        IRNode.optoOnly(SCOBJ, InlineTypeRegexes.SCOBJ);
+    }
+
+    public static final String LOAD_UNKNOWN_INLINE = PREFIX + "LOAD_UNKNOWN_INLINE" + POSTFIX;
+    static {
+        IRNode.beforeMatching(LOAD_UNKNOWN_INLINE, InlineTypeRegexes.LOAD_UNKNOWN_INLINE);
+    }
+
+    public static final String STORE_UNKNOWN_INLINE = PREFIX + "STORE_UNKNOWN_INLINE" + POSTFIX;
+    static {
+        IRNode.optoOnly(STORE_UNKNOWN_INLINE, InlineTypeRegexes.STORE_UNKNOWN_INLINE);
+    }
+
+    public static final String INLINE_ARRAY_NULL_GUARD = PREFIX + "INLINE_ARRAY_NULL_GUARD" + POSTFIX;
+    static {
+        IRNode.optoOnly(INLINE_ARRAY_NULL_GUARD, InlineTypeRegexes.INLINE_ARRAY_NULL_GUARD);
+    }
+
+    public static final String INTRINSIC_SLOW_PATH = PREFIX + "INTRINSIC_SLOW_PATH" + POSTFIX;
+    static {
+        IRNode.optoOnly(INTRINSIC_SLOW_PATH, InlineTypeRegexes.INTRINSIC_SLOW_PATH);
+    }
+
+    public static final String CLONE_INTRINSIC_SLOW_PATH = PREFIX + "CLONE_INTRINSIC_SLOW_PATH" + POSTFIX;
+    static {
+        IRNode.optoOnly(CLONE_INTRINSIC_SLOW_PATH, InlineTypeRegexes.CLONE_INTRINSIC_SLOW_PATH);
+    }
+
+    public static final String CLASS_CHECK_TRAP = PREFIX + "CLASS_CHECK_TRAP" + POSTFIX;
+    static {
+        IRNode.beforeMatching(CLASS_CHECK_TRAP, InlineTypeRegexes.CLASS_CHECK_TRAP);
+    }
+
+    public static final String NULL_CHECK_TRAP = PREFIX + "NULL_CHECK_TRAP" + POSTFIX;
+    static {
+        IRNode.beforeMatching(NULL_CHECK_TRAP, InlineTypeRegexes.NULL_CHECK_TRAP);
+    }
+
+    public static final String NULL_ASSERT_TRAP = PREFIX + "NULL_ASSERT_TRAP" + POSTFIX;
+    static {
+        IRNode.beforeMatching(NULL_ASSERT_TRAP, InlineTypeRegexes.NULL_ASSERT_TRAP);
+    }
+
+    public static final String RANGE_CHECK_TRAP = PREFIX + "RANGE_CHECK_TRAP" + POSTFIX;
+    static {
+        IRNode.beforeMatching(RANGE_CHECK_TRAP, InlineTypeRegexes.RANGE_CHECK_TRAP);
+    }
+
+    public static final String UNHANDLED_TRAP = PREFIX + "UNHANDLED_TRAP" + POSTFIX;
+    static {
+        IRNode.beforeMatching(UNHANDLED_TRAP, InlineTypeRegexes.UNHANDLED_TRAP);
+    }
+
+    public static final String PREDICATE_TRAP = PREFIX + "PREDICATE_TRAP" + POSTFIX;
+    static {
+        IRNode.beforeMatching(PREDICATE_TRAP, InlineTypeRegexes.PREDICATE_TRAP);
+    }
+
+    public static final String MEMBAR = PREFIX + "MEMBAR" + POSTFIX;
+    static {
+        IRNode.beforeMatching(MEMBAR, InlineTypeRegexes.MEMBAR);
+    }
+
+    public static final String CHECKCAST_ARRAY = PREFIX + "CHECKCAST_ARRAY" + POSTFIX;
+    static {
+        IRNode.optoOnly(CHECKCAST_ARRAY, InlineTypeRegexes.CHECKCAST_ARRAY);
+    }
+
+    public static final String CHECKCAST_ARRAYCOPY = PREFIX + "CHECKCAST_ARRAYCOPY" + POSTFIX;
+    static {
+        IRNode.optoOnly(CHECKCAST_ARRAYCOPY, InlineTypeRegexes.CHECKCAST_ARRAYCOPY);
+    }
+
+    public static final String JLONG_ARRAYCOPY = PREFIX + "JLONG_ARRAYCOPY" + POSTFIX;
+    static {
+        IRNode.optoOnly(JLONG_ARRAYCOPY, InlineTypeRegexes.JLONG_ARRAYCOPY);
+    }
+
+    public static final String FIELD_ACCESS = PREFIX + "FIELD_ACCESS" + POSTFIX;
+    static {
+        IRNode.optoOnly(FIELD_ACCESS, InlineTypeRegexes.FIELD_ACCESS);
+    }
+
+    public static final String SUBSTITUTABILITY_TEST = PREFIX + "SUBSTITUTABILITY_TEST" + POSTFIX;
+    static {
+        IRNode.beforeMatching(SUBSTITUTABILITY_TEST, InlineTypeRegexes.SUBSTITUTABILITY_TEST);
+    }
+
+    public static final String CMPP = PREFIX + "CMPP" + POSTFIX;
+    static {
+        IRNode.beforeMatching(CMPP, InlineTypeRegexes.CMPP);
+    }
+
+    // Dummy method to call to force the static initializer blocks to be run before starting the IR framework.
+    public static void forceStaticInitialization() {}
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeRegexes.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeRegexes.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeRegexes.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2022, 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 compiler.valhalla.inlinetypes;
+
+public class InlineTypeRegexes {
+    // Regular expressions used to match nodes in the PrintIdeal output
+    private static final String START = "(\\d+ (.*";
+    private static final String MID = ".*)+ ===.*";
+    private static final String END = ")";
+    // Generic allocation
+    public static final String ALLOC_G = "(.*call,static.*wrapper for: _new_instance_Java" + END;
+    public static final String ALLOCA_G = "(.*call,static.*wrapper for: _new_array_Java" + END;
+    // Inline type allocation
+    public static final String MYVALUE_ARRAY_KLASS = "\\[(precise )?compiler/valhalla/inlinetypes/MyValue";
+    public static final String ALLOC = "(.*precise compiler/valhalla/inlinetypes/MyValue.*\\R(.*(?i:mov|xorl|nop|spill).*\\R)*.*_new_instance_Java" + END;
+    public static final String ALLOCA = "(.*" + MYVALUE_ARRAY_KLASS + ".*\\R(.*(?i:mov|xorl|nop|spill).*\\R)*.*_new_array_Java" + END;
+    public static final String LOAD = START + "Load(B|C|S|I|L|F|D|P|N)" + MID + "@compiler/valhalla/inlinetypes/.*" + END;
+    public static final String LOADK = START + "LoadK" + MID + END;
+    public static final String STORE = START + "Store(B|C|S|I|L|F|D|P|N)" + MID + "@compiler/valhalla/inlinetypes/.*" + END;
+    public static final String LOOP = START + "Loop" + MID + "" + END;
+    public static final String COUNTEDLOOP = START + "CountedLoop\\b" + MID + "" + END;
+    public static final String COUNTEDLOOP_MAIN = START + "CountedLoop\\b" + MID + "main" + END;
+    public static final String TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*(unstable_if|predicate)" + END;
+    public static final String LINKTOSTATIC = START + "CallStaticJava" + MID + "linkToStatic" + END;
+    public static final String NPE = START + "CallStaticJava" + MID + "null_check" + END;
+    public static final String CALL = START + "CallStaticJava" + MID + END;
+    public static final String CALL_LEAF = "(CALL, runtime leaf|call_leaf,runtime)";
+    public static final String CALL_LEAF_NOFP = "(CALL, runtime leaf nofp|call_leaf_nofp,runtime)";
+    protected static final String CALL_UNSAFE = START + "CallStaticJava" + MID + "# Static  jdk.internal.misc.Unsafe::" + END;
+    public static final String STORE_INLINE_FIELDS = START + "CallStaticJava" + MID + "store_inline_type_fields" + END;
+    public static final String SCOBJ = "(.*# ScObj.*" + END;
+    public static final String LOAD_UNKNOWN_INLINE = START + "CallStaticJava" + MID + "_load_unknown_inline" + END;
+    public static final String STORE_UNKNOWN_INLINE = "(.*" + CALL_LEAF + ".*store_unknown_inline.*" + END;
+    public static final String INLINE_ARRAY_NULL_GUARD = "(.*call,static.*wrapper for: uncommon_trap.*reason='null_check' action='none'.*" + END;
+    public static final String INTRINSIC_SLOW_PATH = "(.*call,static.*wrapper for: uncommon_trap.*reason='intrinsic_or_type_checked_inlining'.*" + END;
+    public static final String CLONE_INTRINSIC_SLOW_PATH = "(.*call,static.*java.lang.Object::clone.*" + END;
+    public static final String CLASS_CHECK_TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*class_check" + END;
+    public static final String NULL_CHECK_TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*null_check" + END;
+    public static final String NULL_ASSERT_TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*null_assert" + END;
+    public static final String RANGE_CHECK_TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*range_check" + END;
+    public static final String UNHANDLED_TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*unhandled" + END;
+    public static final String PREDICATE_TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*predicate" + END;
+    public static final String MEMBAR = START + "MemBar" + MID + END;
+    public static final String CHECKCAST_ARRAY = "(((?i:cmp|CLFI|CLR).*" + MYVALUE_ARRAY_KLASS + ".*:|.*(?i:mov|or).*" + MYVALUE_ARRAY_KLASS + ".*:.*\\R.*(cmp|CMP|CLR))" + END;
+    public static final String CHECKCAST_ARRAYCOPY = "(.*" + CALL_LEAF_NOFP + ".*checkcast_arraycopy.*" + END;
+    public static final String JLONG_ARRAYCOPY = "(.*" + CALL_LEAF_NOFP + ".*jlong_disjoint_arraycopy.*" + END;
+    public static final String FIELD_ACCESS = "(.*Field: *" + END;
+    public static final String SUBSTITUTABILITY_TEST = START + "CallStaticJava" + MID + "java.lang.runtime.ValueObjectMethods::isSubstitutable" + END;
+    public static final String CMPP = START + "(CmpP|CmpN)" + MID + "" + END;
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypes.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypes.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypes.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2017, 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 compiler.valhalla.inlinetypes;
+
+import jdk.test.lib.Utils;
+import compiler.lib.ir_framework.Scenario;
+import compiler.lib.ir_framework.TestFramework;
+
+public class InlineTypes {
+    public static final int  rI = Utils.getRandomInstance().nextInt() % 1000;
+    public static final long rL = Utils.getRandomInstance().nextLong() % 1000;
+    public static final double rD = Utils.getRandomInstance().nextDouble() % 1000;
+
+    public static final Scenario[] DEFAULT_SCENARIOS = {
+            new Scenario(0,
+                         "--enable-preview",
+                         "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED",
+                         "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED",
+                         "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED",
+                         "-XX:+IgnoreUnrecognizedVMOptions",
+                         "-XX:-UseACmpProfile",
+                         "-XX:+AlwaysIncrementalInline",
+                         "-XX:FlatArrayElementMaxOops=5",
+                         "-XX:FlatArrayElementMaxSize=-1",
+                         "-XX:-UseArrayLoadStoreProfile",
+                         "-XX:InlineFieldMaxFlatSize=-1",
+                         "-XX:+InlineTypePassFieldsAsArgs",
+                         "-XX:+InlineTypeReturnedAsFields"
+            ),
+            new Scenario(1,
+                         "--enable-preview",
+                         "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED",
+                         "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED",
+                         "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED",
+                         "-XX:+IgnoreUnrecognizedVMOptions",
+                         "-XX:-UseACmpProfile",
+                         "-XX:-UseCompressedOops",
+                         "-XX:FlatArrayElementMaxOops=5",
+                         "-XX:FlatArrayElementMaxSize=-1",
+                         "-XX:-UseArrayLoadStoreProfile",
+                         "-XX:InlineFieldMaxFlatSize=-1",
+                         "-XX:-InlineTypePassFieldsAsArgs",
+                         "-XX:-InlineTypeReturnedAsFields"
+            ),
+            new Scenario(2,
+                         "--enable-preview",
+                         "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED",
+                         "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED",
+                         "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED",
+                         "-XX:+IgnoreUnrecognizedVMOptions",
+                         "-XX:-UseACmpProfile",
+                         "-XX:-UseCompressedOops",
+                         "-XX:FlatArrayElementMaxOops=0",
+                         "-XX:FlatArrayElementMaxSize=0",
+                         "-XX:-UseArrayLoadStoreProfile",
+                         "-XX:InlineFieldMaxFlatSize=-1",
+                         "-XX:+InlineTypePassFieldsAsArgs",
+                         "-XX:+InlineTypeReturnedAsFields",
+                         "-XX:+StressInlineTypeReturnedAsFields"
+            ),
+            new Scenario(3,
+                         "--enable-preview",
+                         "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED",
+                         "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED",
+                         "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED",
+                         "-XX:+IgnoreUnrecognizedVMOptions",
+                         "-DVerifyIR=false",
+                         "-XX:+AlwaysIncrementalInline",
+                         "-XX:FlatArrayElementMaxOops=0",
+                         "-XX:FlatArrayElementMaxSize=0",
+                         "-XX:InlineFieldMaxFlatSize=0",
+                         "-XX:+InlineTypePassFieldsAsArgs",
+                         "-XX:+InlineTypeReturnedAsFields"
+            ),
+            new Scenario(4,
+                         "--enable-preview",
+                         "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED",
+                         "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED",
+                         "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED",
+                         "-XX:+IgnoreUnrecognizedVMOptions",
+                         "-DVerifyIR=false",
+                         "-XX:FlatArrayElementMaxOops=-1",
+                         "-XX:FlatArrayElementMaxSize=-1",
+                         "-XX:InlineFieldMaxFlatSize=0",
+                         "-XX:+InlineTypePassFieldsAsArgs",
+                         "-XX:-InlineTypeReturnedAsFields",
+                         "-XX:-ReduceInitialCardMarks"
+            ),
+            new Scenario(5,
+                         "--enable-preview",
+                         "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED",
+                         "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED",
+                         "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED",
+                         "-XX:+IgnoreUnrecognizedVMOptions",
+                         "-XX:-UseACmpProfile",
+                         "-XX:+AlwaysIncrementalInline",
+                         "-XX:FlatArrayElementMaxOops=5",
+                         "-XX:FlatArrayElementMaxSize=-1",
+                         "-XX:-UseArrayLoadStoreProfile",
+                         "-XX:InlineFieldMaxFlatSize=-1",
+                         "-XX:-InlineTypePassFieldsAsArgs",
+                         "-XX:-InlineTypeReturnedAsFields"
+            )
+    };
+
+    public static TestFramework getFramework() {
+        InlineTypeIRNode.forceStaticInitialization();
+        StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
+        return new TestFramework(walker.getCallerClass()).setDefaultWarmup(251);
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyAbstract.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyAbstract.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyAbstract.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2020, 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 compiler.valhalla.inlinetypes;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+public abstract value class MyAbstract implements MyInterface {
+
+}
+
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyInterface.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyInterface.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyInterface.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018, 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 compiler.valhalla.inlinetypes;
+
+public interface MyInterface {
+    public long hash();
+}
+
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValue1.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValue1.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValue1.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2017, 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.DontCompile;
+import compiler.lib.ir_framework.DontInline;
+import compiler.lib.ir_framework.ForceCompileClassInitializer;
+import compiler.lib.ir_framework.ForceInline;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+@ForceCompileClassInitializer
+public value class MyValue1 extends MyAbstract {
+    static int s;
+    static final long sf = InlineTypes.rL;
+    int x;
+    long y;
+    short z;
+    Integer o;
+    int[] oa;
+    @NullRestricted
+    MyValue2 v1;
+    @NullRestricted
+    MyValue2 v2;
+    @NullRestricted
+    static final MyValue2 v3 = MyValue2.createWithFieldsInline(InlineTypes.rI, InlineTypes.rD);
+    MyValue2 v4;
+    @NullRestricted
+    MyValue2 v5;
+    int c;
+
+    @ForceInline
+    public MyValue1(int x, long y, short z, Integer o, int[] oa, MyValue2 v1, MyValue2 v2, MyValue2 v4, MyValue2 v5, int c) {
+        s = 0;
+        this.x = x;
+        this.y = y;
+        this.z = z;
+        this.o = o;
+        this.oa = oa;
+        this.v1 = v1;
+        this.v2 = v2;
+        this.v4 = v4;
+        this.v5 = v5;
+        this.c = c;
+    }
+
+    @DontInline
+    static MyValue1 createDefaultDontInline() {
+        return createDefaultInline();
+    }
+
+    @ForceInline
+    static MyValue1 createDefaultInline() {
+        return new MyValue1(0, 0, (short)0, null, null, MyValue2.createDefaultInline(), MyValue2.createDefaultInline(), null, MyValue2.createDefaultInline(), 0);
+    }
+
+    @DontInline
+    static MyValue1 createWithFieldsDontInline(int x, long y) {
+        return createWithFieldsInline(x, y);
+    }
+
+    @ForceInline
+    static MyValue1 createWithFieldsInline(int x, long y) {
+        MyValue1 v = createDefaultInline();
+        v = setX(v, x);
+        v = setY(v, y);
+        v = setZ(v, (short)x);
+        // Don't use Integer.valueOf here to avoid control flow added by Integer cache check
+        v = setO(v, new Integer(x));
+        int[] oa = {x};
+        v = setOA(v, oa);
+        v = setV1(v, MyValue2.createWithFieldsInline(x, y, InlineTypes.rD));
+        v = setV2(v, MyValue2.createWithFieldsInline(x + 1, y + 1, InlineTypes.rD + 1));
+        v = setV4(v, MyValue2.createWithFieldsInline(x + 2, y + 2, InlineTypes.rD + 2));
+        v = setV5(v, MyValue2.createWithFieldsInline(x + 3, y + 3, InlineTypes.rD + 3));
+        v = setC(v, (int)(x+y));
+        return v;
+    }
+
+    // Hash only primitive and inline type fields to avoid NullPointerException
+    @ForceInline
+    public long hashPrimitive() {
+        return s + sf + x + y + z + c + v1.hash() + v2.hash() + v3.hash() + v5.hash();
+    }
+
+    @ForceInline
+    public long hash() {
+        long res = hashPrimitive();
+        try {
+            res += o;
+        } catch (NullPointerException npe) {}
+        try {
+            res += oa[0];
+        } catch (NullPointerException npe) {}
+        try {
+            res += v4.hash();
+        } catch (NullPointerException npe) {}
+        return res;
+    }
+
+    @DontCompile
+    public long hashInterpreted() {
+        return s + sf + x + y + z + o + oa[0] + c + v1.hashInterpreted() + v2.hashInterpreted() + v3.hashInterpreted() + v4.hashInterpreted() + v5.hashInterpreted();
+    }
+
+    @ForceInline
+    public void print() {
+        System.out.print("s=" + s + ", sf=" + sf + ", x=" + x + ", y=" + y + ", z=" + z + ", o=" + (o != null ? (Integer)o : "NULL") + ", oa=" + (oa != null ? oa[0] : "NULL") + ", v1[");
+        v1.print();
+        System.out.print("], v2[");
+        v2.print();
+        System.out.print("], v3[");
+        v3.print();
+        System.out.print("], v4[");
+        v4.print();
+        System.out.print("], v5[");
+        v5.print();
+        System.out.print("], c=" + c);
+    }
+
+    @ForceInline
+    static MyValue1 setX(MyValue1 v, int x) {
+        return new MyValue1(x, v.y, v.z, v.o, v.oa, v.v1, v.v2, v.v4, v.v5, v.c);
+    }
+
+    @ForceInline
+    static MyValue1 setY(MyValue1 v, long y) {
+        return new MyValue1(v.x, y, v.z, v.o, v.oa, v.v1, v.v2, v.v4, v.v5, v.c);
+    }
+
+    @ForceInline
+    static MyValue1 setZ(MyValue1 v, short z) {
+        return new MyValue1(v.x, v.y, z, v.o, v.oa, v.v1, v.v2, v.v4, v.v5, v.c);
+    }
+
+    @ForceInline
+    static MyValue1 setO(MyValue1 v, Integer o) {
+        return new MyValue1(v.x, v.y, v.z, o, v.oa, v.v1, v.v2, v.v4, v.v5, v.c);
+    }
+
+    @ForceInline
+    static MyValue1 setOA(MyValue1 v, int[] oa) {
+        return new MyValue1(v.x, v.y, v.z, v.o, oa, v.v1, v.v2, v.v4, v.v5, v.c);
+    }
+
+    @ForceInline
+    static MyValue1 setC(MyValue1 v, int c) {
+        return new MyValue1(v.x, v.y, v.z, v.o, v.oa, v.v1, v.v2, v.v4, v.v5, c);
+    }
+
+    @ForceInline
+    static MyValue1 setV1(MyValue1 v, MyValue2 v1) {
+        return new MyValue1(v.x, v.y, v.z, v.o, v.oa, v1, v.v2, v.v4, v.v5, v.c);
+    }
+
+    @ForceInline
+    static MyValue1 setV2(MyValue1 v, MyValue2 v2) {
+        return new MyValue1(v.x, v.y, v.z, v.o, v.oa, v.v1, v2, v.v4, v.v5, v.c);
+    }
+
+    @ForceInline
+    static MyValue1 setV4(MyValue1 v, MyValue2 v4) {
+        return new MyValue1(v.x, v.y, v.z, v.o, v.oa, v.v1, v.v2, v4, v.v5, v.c);
+    }
+
+    @ForceInline
+    static MyValue1 setV5(MyValue1 v, MyValue2 v5) {
+        return new MyValue1(v.x, v.y, v.z, v.o, v.oa, v.v1, v.v2, v.v4, v5, v.c);
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValue2.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValue2.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValue2.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2017, 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.DontInline;
+import compiler.lib.ir_framework.ForceInline;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue2Inline {
+    double d;
+    long l;
+
+    @ForceInline
+    public MyValue2Inline(double d, long l) {
+        this.d = d;
+        this.l = l;
+    }
+
+    @ForceInline
+    static MyValue2Inline setD(MyValue2Inline v, double d) {
+        return new MyValue2Inline(d, v.l);
+    }
+
+    @ForceInline
+    static MyValue2Inline setL(MyValue2Inline v, long l) {
+        return new MyValue2Inline(v.d, l);
+    }
+
+    @ForceInline
+    public static MyValue2Inline createDefault() {
+        return new MyValue2Inline(0, 0);
+    }
+
+    @ForceInline
+    public static MyValue2Inline createWithFieldsInline(double d, long l) {
+        MyValue2Inline v = MyValue2Inline.createDefault();
+        v = MyValue2Inline.setD(v, d);
+        v = MyValue2Inline.setL(v, l);
+        return v;
+    }
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+public value class MyValue2 extends MyAbstract {
+    int x;
+    byte y;
+    @NullRestricted
+    MyValue2Inline v;
+
+    @ForceInline
+    public MyValue2(int x, byte y, MyValue2Inline v) {
+        this.x = x;
+        this.y = y;
+        this.v = v;
+    }
+
+    @ForceInline
+    public static MyValue2 createDefaultInline() {
+        return new MyValue2(0, (byte)0, MyValue2Inline.createDefault());
+    }
+
+    @ForceInline
+    public static MyValue2 createWithFieldsInline(int x, long y, double d) {
+        MyValue2 v = createDefaultInline();
+        v = setX(v, x);
+        v = setY(v, (byte)x);
+        v = setV(v, MyValue2Inline.createWithFieldsInline(d, y));
+        return v;
+    }
+
+    @ForceInline
+    public static MyValue2 createWithFieldsInline(int x, double d) {
+        MyValue2 v = createDefaultInline();
+        v = setX(v, x);
+        v = setY(v, (byte)x);
+        v = setV(v, MyValue2Inline.createWithFieldsInline(d, InlineTypes.rL));
+        return v;
+    }
+
+    @DontInline
+    public static MyValue2 createWithFieldsDontInline(int x, double d) {
+        MyValue2 v = createDefaultInline();
+        v = setX(v, x);
+        v = setY(v, (byte)x);
+        v = setV(v, MyValue2Inline.createWithFieldsInline(d, InlineTypes.rL));
+        return v;
+    }
+
+    @ForceInline
+    public long hash() {
+        return x + y + (long)v.d + v.l;
+    }
+
+    @DontInline
+    public long hashInterpreted() {
+        return x + y + (long)v.d + v.l;
+    }
+
+    @ForceInline
+    public void print() {
+        System.out.print("x=" + x + ", y=" + y + ", d=" + v.d + ", l=" + v.l);
+    }
+
+    @ForceInline
+    static MyValue2 setX(MyValue2 v, int x) {
+        return new MyValue2(x, v.y, v.v);
+    }
+
+    @ForceInline
+    static MyValue2 setY(MyValue2 v, byte y) {
+        return new MyValue2(v.x, y, v.v);
+    }
+
+    @ForceInline
+    static MyValue2 setV(MyValue2 v, MyValue2Inline vi) {
+        return new MyValue2(v.x, v.y, vi);
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValue3.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValue3.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValue3.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2017, 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 compiler.valhalla.inlinetypes;
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.Utils;
+import compiler.lib.ir_framework.DontInline;
+import compiler.lib.ir_framework.ForceInline;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue3Inline {
+    float f7;
+    double f8;
+
+    @ForceInline
+    public MyValue3Inline(float f7, double f8) {
+        this.f7 = f7;
+        this.f8 = f8;
+    }
+
+    @ForceInline
+    static MyValue3Inline setF7(MyValue3Inline v, float f7) {
+        return new MyValue3Inline(f7, v.f8);
+    }
+
+    @ForceInline
+    static MyValue3Inline setF8(MyValue3Inline v, double f8) {
+        return new MyValue3Inline(v.f7, f8);
+    }
+
+    @ForceInline
+    public static MyValue3Inline createDefault() {
+        return new MyValue3Inline(0, 0);
+    }
+
+    @ForceInline
+    public static MyValue3Inline createWithFieldsInline(float f7, double f8) {
+        MyValue3Inline v = createDefault();
+        v = setF7(v, f7);
+        v = setF8(v, f8);
+        return v;
+    }
+}
+
+// Inline type definition to stress test return of an inline type in registers
+// (uses all registers of calling convention on x86_64)
+@ImplicitlyConstructible
+@LooselyConsistentValue
+public value class MyValue3 extends MyAbstract {
+    char c;
+    byte bb;
+    short s;
+    int i;
+    long l;
+    Object o;
+    float f1;
+    double f2;
+    float f3;
+    double f4;
+    float f5;
+    double f6;
+    @NullRestricted
+    MyValue3Inline v1;
+
+    @ForceInline
+    public MyValue3(char c, byte bb, short s, int i, long l, Object o,
+                    float f1, double f2, float f3, double f4, float f5, double f6,
+                    MyValue3Inline v1) {
+        this.c = c;
+        this.bb = bb;
+        this.s = s;
+        this.i = i;
+        this.l = l;
+        this.o = o;
+        this.f1 = f1;
+        this.f2 = f2;
+        this.f3 = f3;
+        this.f4 = f4;
+        this.f5 = f5;
+        this.f6 = f6;
+        this.v1 = v1;
+    }
+
+    @ForceInline
+    static MyValue3 setC(MyValue3 v, char c) {
+        return new MyValue3(c, v.bb, v.s, v.i, v.l, v.o, v.f1, v.f2, v.f3, v.f4, v.f5, v.f6, v.v1);
+    }
+
+    @ForceInline
+    static MyValue3 setBB(MyValue3 v, byte bb) {
+        return new MyValue3(v.c, bb, v.s, v.i, v.l, v.o, v.f1, v.f2, v.f3, v.f4, v.f5, v.f6, v.v1);
+    }
+
+    @ForceInline
+    static MyValue3 setS(MyValue3 v, short s) {
+        return new MyValue3(v.c, v.bb, s, v.i, v.l, v.o, v.f1, v.f2, v.f3, v.f4, v.f5, v.f6, v.v1);
+    }
+
+    @ForceInline
+    static MyValue3 setI(MyValue3 v, int i) {
+        return new MyValue3(v.c, v.bb, v.s, i, v.l, v.o, v.f1, v.f2, v.f3, v.f4, v.f5, v.f6, v.v1);
+    }
+
+    @ForceInline
+    static MyValue3 setL(MyValue3 v, long l) {
+        return new MyValue3(v.c, v.bb, v.s, v.i, l, v.o, v.f1, v.f2, v.f3, v.f4, v.f5, v.f6, v.v1);
+    }
+
+    @ForceInline
+    static MyValue3 setO(MyValue3 v, Object o) {
+        return new MyValue3(v.c, v.bb, v.s, v.i, v.l, o, v.f1, v.f2, v.f3, v.f4, v.f5, v.f6, v.v1);
+    }
+
+    @ForceInline
+    static MyValue3 setF1(MyValue3 v, float f1) {
+        return new MyValue3(v.c, v.bb, v.s, v.i, v.l, v.o, f1, v.f2, v.f3, v.f4, v.f5, v.f6, v.v1);
+    }
+
+    @ForceInline
+    static MyValue3 setF2(MyValue3 v, double f2) {
+        return new MyValue3(v.c, v.bb, v.s, v.i, v.l, v.o, v.f1, f2, v.f3, v.f4, v.f5, v.f6, v.v1);
+    }
+
+    @ForceInline
+    static MyValue3 setF3(MyValue3 v, float f3) {
+        return new MyValue3(v.c, v.bb, v.s, v.i, v.l, v.o, v.f1, v.f2, f3, v.f4, v.f5, v.f6, v.v1);
+    }
+
+    @ForceInline
+    static MyValue3 setF4(MyValue3 v, double f4) {
+        return new MyValue3(v.c, v.bb, v.s, v.i, v.l, v.o, v.f1, v.f2, v.f3, f4, v.f5, v.f6, v.v1);
+    }
+
+    @ForceInline
+    static MyValue3 setF5(MyValue3 v, float f5) {
+        return new MyValue3(v.c, v.bb, v.s, v.i, v.l, v.o, v.f1, v.f2, v.f3, v.f4, f5, v.f6, v.v1);
+    }
+
+    @ForceInline
+    static MyValue3 setF6(MyValue3 v, double f6) {
+        return new MyValue3(v.c, v.bb, v.s, v.i, v.l, v.o, v.f1, v.f2, v.f3, v.f4, v.f5, f6, v.v1);
+    }
+
+    @ForceInline
+    static MyValue3 setV1(MyValue3 v, MyValue3Inline v1) {
+        return new MyValue3(v.c, v.bb, v.s, v.i, v.l, v.o, v.f1, v.f2, v.f3, v.f4, v.f5, v.f6, v1);
+    }
+
+    @ForceInline
+    public static MyValue3 createDefault() {
+        return new MyValue3((char)0, (byte)0, (short)0, 0, 0, null, 0, 0, 0, 0, 0, 0, MyValue3Inline.createDefault());
+    }
+
+    @ForceInline
+    public static MyValue3 create() {
+        java.util.Random r = Utils.getRandomInstance();
+        MyValue3 v = createDefault();
+        v = setC(v, (char)r.nextInt());
+        v = setBB(v, (byte)r.nextInt());
+        v = setS(v, (short)r.nextInt());
+        v = setI(v, r.nextInt());
+        v = setL(v, r.nextLong());
+        v = setO(v, new Object());
+        v = setF1(v, r.nextFloat());
+        v = setF2(v, r.nextDouble());
+        v = setF3(v, r.nextFloat());
+        v = setF4(v, r.nextDouble());
+        v = setF5(v, r.nextFloat());
+        v = setF6(v, r.nextDouble());
+        v = setV1(v, MyValue3Inline.createWithFieldsInline(r.nextFloat(), r.nextDouble()));
+        return v;
+    }
+
+    @DontInline
+    public static MyValue3 createDontInline() {
+        return create();
+    }
+
+    @ForceInline
+    public static MyValue3 copy(MyValue3 other) {
+        MyValue3 v = createDefault();
+        v = setC(v, other.c);
+        v = setBB(v, other.bb);
+        v = setS(v, other.s);
+        v = setI(v, other.i);
+        v = setL(v, other.l);
+        v = setO(v, other.o);
+        v = setF1(v, other.f1);
+        v = setF2(v, other.f2);
+        v = setF3(v, other.f3);
+        v = setF4(v, other.f4);
+        v = setF5(v, other.f5);
+        v = setF6(v, other.f6);
+        v = setV1(v, other.v1);
+        return v;
+    }
+
+    @DontInline
+    public void verify(MyValue3 other) {
+        Asserts.assertEQ(c, other.c);
+        Asserts.assertEQ(bb, other.bb);
+        Asserts.assertEQ(s, other.s);
+        Asserts.assertEQ(i, other.i);
+        Asserts.assertEQ(l, other.l);
+        Asserts.assertEQ(o, other.o);
+        Asserts.assertEQ(f1, other.f1);
+        Asserts.assertEQ(f2, other.f2);
+        Asserts.assertEQ(f3, other.f3);
+        Asserts.assertEQ(f4, other.f4);
+        Asserts.assertEQ(f5, other.f5);
+        Asserts.assertEQ(f6, other.f6);
+        Asserts.assertEQ(v1.f7, other.v1.f7);
+        Asserts.assertEQ(v1.f8, other.v1.f8);
+    }
+
+    @ForceInline
+    public long hash() {
+        return c +
+            bb +
+            s +
+            i +
+            l +
+            o.hashCode() +
+            Float.hashCode(f1) +
+            Double.hashCode(f2) +
+            Float.hashCode(f3) +
+            Double.hashCode(f4) +
+            Float.hashCode(f5) +
+            Double.hashCode(f6) +
+            Float.hashCode(v1.f7) +
+            Double.hashCode(v1.f8);
+    }
+}
+
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValue4.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValue4.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValue4.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2017, 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.ForceInline;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+// Inline type definition with too many fields to return in registers
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue4 extends MyAbstract {
+    @NullRestricted
+    MyValue3 v1;
+    @NullRestricted
+    MyValue3 v2;
+
+    @ForceInline
+    public MyValue4(MyValue3 v1, MyValue3 v2) {
+        this.v1 = v1;
+        this.v2 = v2;
+    }
+
+    @ForceInline
+    static MyValue4 setV1(MyValue4 v, MyValue3 v1) {
+        return new MyValue4(v1, v.v2);
+    }
+
+    @ForceInline
+    static MyValue4 setV2(MyValue4 v, MyValue3 v2) {
+        return new MyValue4(v.v1, v2);
+    }
+
+    @ForceInline
+    public static MyValue4 createDefault() {
+        return new MyValue4(MyValue3.createDefault(), MyValue3.createDefault());
+    }
+
+    @ForceInline
+    public static MyValue4 create() {
+        MyValue4 v = createDefault();
+        MyValue3 v1 = MyValue3.create();
+        v = setV1(v, v1);
+        MyValue3 v2 = MyValue3.create();
+        v = setV2(v, v2);
+        return v;
+    }
+
+    public void verify(MyValue4 other) {
+        v1.verify(other.v1);
+        v2.verify(other.v2);
+    }
+
+    @ForceInline
+    public long hash() {
+        return v1.hash() + v2.hash();
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValueClass1.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValueClass1.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValueClass1.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2022, 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.DontCompile;
+import compiler.lib.ir_framework.DontInline;
+import compiler.lib.ir_framework.ForceCompileClassInitializer;
+import compiler.lib.ir_framework.ForceInline;
+
+@ForceCompileClassInitializer
+public value class MyValueClass1 extends MyAbstract {
+    static int s;
+    static long sf = InlineTypes.rL;
+    int x;
+    long y;
+    short z;
+    Integer o;
+    int[] oa;
+    MyValueClass2 v1;
+    MyValueClass2 v2;
+    static MyValueClass2 v3 = MyValueClass2.createWithFieldsInline(InlineTypes.rI, InlineTypes.rD);
+    MyValueClass2 v4;
+    int c;
+
+    @ForceInline
+    public MyValueClass1(int x, long y, short z, Integer o, int[] oa, MyValueClass2 v1, MyValueClass2 v2, MyValueClass2 v4, int c) {
+        s = 0;
+        this.x = x;
+        this.y = y;
+        this.z = z;
+        this.o = o;
+        this.oa = oa;
+        this.v1 = v1;
+        this.v2 = v2;
+        this.v4 = v4;
+        this.c = c;
+    }
+
+    @DontInline
+    static MyValueClass1 createDefaultDontInline() {
+        return createDefaultInline();
+    }
+
+    @ForceInline
+    static MyValueClass1 createDefaultInline() {
+        return new MyValueClass1(0, 0, (short)0, null, null, null, null, null, 0);
+    }
+
+    @DontInline
+    static MyValueClass1 createWithFieldsDontInline(int x, long y) {
+        return createWithFieldsInline(x, y);
+    }
+
+    @ForceInline
+    static MyValueClass1 createWithFieldsInline(int x, long y) {
+        MyValueClass1 v = createDefaultInline();
+        v = setX(v, x);
+        v = setY(v, y);
+        v = setZ(v, (short)x);
+        // Don't use Integer.valueOf here to avoid control flow added by Integer cache check
+        v = setO(v, new Integer(x));
+        int[] oa = {x};
+        v = setOA(v, oa);
+        v = setV1(v, MyValueClass2.createWithFieldsInline(x, y, InlineTypes.rD));
+        v = setV2(v, MyValueClass2.createWithFieldsInline(x + 1, y + 1, InlineTypes.rD + 1));
+        v = setV4(v, MyValueClass2.createWithFieldsInline(x + 2, y + 2, InlineTypes.rD + 2));
+        v = setC(v, (int)(x+y));
+        return v;
+    }
+
+    // Hash only primitive and inline type fields to avoid NullPointerException
+    @ForceInline
+    public long hashPrimitive() {
+        return s + sf + x + y + z + c + v1.hash() + v2.hash() + v3.hash();
+    }
+
+    @ForceInline
+    public long hash() {
+        long res = hashPrimitive();
+        try {
+            res += o;
+        } catch (NullPointerException npe) {}
+        try {
+            res += oa[0];
+        } catch (NullPointerException npe) {}
+        try {
+            res += v4.hash();
+        } catch (NullPointerException npe) {}
+        return res;
+    }
+
+    @DontCompile
+    public long hashInterpreted() {
+        return s + sf + x + y + z + o + oa[0] + c + v1.hashInterpreted() + v2.hashInterpreted() + v3.hashInterpreted() + v4.hashInterpreted();
+    }
+
+    @ForceInline
+    public void print() {
+        System.out.print("s=" + s + ", sf=" + sf + ", x=" + x + ", y=" + y + ", z=" + z + ", o=" + (o != null ? (Integer)o : "NULL") + ", oa=" + (oa != null ? oa[0] : "NULL") + ", v1[");
+        v1.print();
+        System.out.print("], v2[");
+        v2.print();
+        System.out.print("], v3[");
+        v3.print();
+        System.out.print("], v4[");
+        v4.print();
+        System.out.print("], c=" + c);
+    }
+
+    @ForceInline
+    static MyValueClass1 setX(MyValueClass1 v, int x) {
+        return new MyValueClass1(x, v.y, v.z, v.o, v.oa, v.v1, v.v2, v.v4, v.c);
+    }
+
+    @ForceInline
+    static MyValueClass1 setY(MyValueClass1 v, long y) {
+        return new MyValueClass1(v.x, y, v.z, v.o, v.oa, v.v1, v.v2, v.v4, v.c);
+    }
+
+    @ForceInline
+    static MyValueClass1 setZ(MyValueClass1 v, short z) {
+        return new MyValueClass1(v.x, v.y, z, v.o, v.oa, v.v1, v.v2, v.v4, v.c);
+    }
+
+    @ForceInline
+    static MyValueClass1 setO(MyValueClass1 v, Integer o) {
+        return new MyValueClass1(v.x, v.y, v.z, o, v.oa, v.v1, v.v2, v.v4, v.c);
+    }
+
+    @ForceInline
+    static MyValueClass1 setOA(MyValueClass1 v, int[] oa) {
+        return new MyValueClass1(v.x, v.y, v.z, v.o, oa, v.v1, v.v2, v.v4, v.c);
+    }
+
+    @ForceInline
+    static MyValueClass1 setC(MyValueClass1 v, int c) {
+        return new MyValueClass1(v.x, v.y, v.z, v.o, v.oa, v.v1, v.v2, v.v4, c);
+    }
+
+    @ForceInline
+    static MyValueClass1 setV1(MyValueClass1 v, MyValueClass2 v1) {
+        return new MyValueClass1(v.x, v.y, v.z, v.o, v.oa, v1, v.v2, v.v4, v.c);
+    }
+
+    @ForceInline
+    static MyValueClass1 setV2(MyValueClass1 v, MyValueClass2 v2) {
+        return new MyValueClass1(v.x, v.y, v.z, v.o, v.oa, v.v1, v2, v.v4, v.c);
+    }
+
+    @ForceInline
+    static MyValueClass1 setV4(MyValueClass1 v, MyValueClass2 v4) {
+        return new MyValueClass1(v.x, v.y, v.z, v.o, v.oa, v.v1, v.v2, v4, v.c);
+    }
+
+    @DontInline
+    void dontInline(MyValueClass1 arg) {
+
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValueClass2.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValueClass2.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValueClass2.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2022, 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.DontInline;
+import compiler.lib.ir_framework.ForceInline;
+
+value class MyValueClass2Inline {
+    double d;
+    long l;
+
+    @ForceInline
+    public MyValueClass2Inline(double d, long l) {
+        this.d = d;
+        this.l = l;
+    }
+
+    @ForceInline
+    static MyValueClass2Inline setD(MyValueClass2Inline v, double d) {
+        return new MyValueClass2Inline(d, v.l);
+    }
+
+    @ForceInline
+    static MyValueClass2Inline setL(MyValueClass2Inline v, long l) {
+        return new MyValueClass2Inline(v.d, l);
+    }
+
+    @ForceInline
+    public static MyValueClass2Inline createDefault() {
+        return new MyValueClass2Inline(0, 0);
+    }
+
+    @ForceInline
+    public static MyValueClass2Inline createWithFieldsInline(double d, long l) {
+        MyValueClass2Inline v = MyValueClass2Inline.createDefault();
+        v = MyValueClass2Inline.setD(v, d);
+        v = MyValueClass2Inline.setL(v, l);
+        return v;
+    }
+}
+
+public value class MyValueClass2 extends MyAbstract {
+    int x;
+    byte y;
+    MyValueClass2Inline v;
+
+    @ForceInline
+    public MyValueClass2(int x, byte y, MyValueClass2Inline v) {
+        this.x = x;
+        this.y = y;
+        this.v = v;
+    }
+
+    @ForceInline
+    public static MyValueClass2 createDefaultInline() {
+        return new MyValueClass2(0, (byte)0, null);
+    }
+
+    @ForceInline
+    public static MyValueClass2 createWithFieldsInline(int x, long y, double d) {
+        MyValueClass2 v = createDefaultInline();
+        v = setX(v, x);
+        v = setY(v, (byte)x);
+        v = setV(v, MyValueClass2Inline.createWithFieldsInline(d, y));
+        return v;
+    }
+
+    @ForceInline
+    public static MyValueClass2 createWithFieldsInline(int x, double d) {
+        MyValueClass2 v = createDefaultInline();
+        v = setX(v, x);
+        v = setY(v, (byte)x);
+        v = setV(v, MyValueClass2Inline.createWithFieldsInline(d, InlineTypes.rL));
+        return v;
+    }
+
+    @DontInline
+    public static MyValueClass2 createWithFieldsDontInline(int x, double d) {
+        MyValueClass2 v = createDefaultInline();
+        v = setX(v, x);
+        v = setY(v, (byte)x);
+        v = setV(v, MyValueClass2Inline.createWithFieldsInline(d, InlineTypes.rL));
+        return v;
+    }
+
+    @ForceInline
+    public long hash() {
+        return x + y + (long)v.d + v.l;
+    }
+
+    @DontInline
+    public long hashInterpreted() {
+        return x + y + (long)v.d + v.l;
+    }
+
+    @ForceInline
+    public void print() {
+        System.out.print("x=" + x + ", y=" + y + ", d=" + v.d + ", l=" + v.l);
+    }
+
+    @ForceInline
+    static MyValueClass2 setX(MyValueClass2 v, int x) {
+        return new MyValueClass2(x, v.y, v.v);
+    }
+
+    @ForceInline
+    static MyValueClass2 setY(MyValueClass2 v, byte y) {
+        return new MyValueClass2(v.x, y, v.v);
+    }
+
+    @ForceInline
+    static MyValueClass2 setV(MyValueClass2 v, MyValueClass2Inline vi) {
+        return new MyValueClass2(v.x, v.y, vi);
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValueEmpty.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValueEmpty.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/MyValueEmpty.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020, 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 compiler.valhalla.inlinetypes;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+public value class MyValueEmpty extends MyAbstract {
+    public long hash() { return 0; }
+
+    public MyValueEmpty copy(MyValueEmpty other) { return other; }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/NonValueClass.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/NonValueClass.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/NonValueClass.java
@@ -0,0 +1,35 @@
+/*
+ * 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.ForceInline;
+
+public class NonValueClass {
+    public final int x;
+
+    @ForceInline
+    public NonValueClass(int x) {
+        this.x = x;
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/SimpleInlineType.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/SimpleInlineType.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/SimpleInlineType.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+value class SimpleInlineType {
+    int x;
+
+    private SimpleInlineType() {
+        x = 0;
+    }
+
+    static SimpleInlineType create() {
+        return new SimpleInlineType();
+    }
+}
+
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestAcmpWithUnstableIf.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestAcmpWithUnstableIf.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestAcmpWithUnstableIf.java
@@ -0,0 +1,146 @@
+/*
+ * 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
+ * @key randomness
+ * @summary Test that deoptimization at unstable ifs in acmp works as expected.
+ * @library /test/lib
+ * @enablePreview
+ * @run main/othervm TestAcmpWithUnstableIf
+ * @run main/othervm -XX:CompileCommand=compileonly,TestAcmpWithUnstableIf::test* -Xbatch TestAcmpWithUnstableIf
+ */
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.Utils;
+
+public class TestAcmpWithUnstableIf {
+
+    public static final int     EQUAL = Utils.getRandomInstance().nextInt();
+    public static final int NOT_EQUAL = Utils.getRandomInstance().nextInt();
+
+    static value class MyValue {
+        int x;
+
+        public MyValue(int x) {
+            this.x = x;
+        }
+    }
+
+    public static int test1(MyValue val1, MyValue val2, int resEqual, int resNotEqual) {
+        if (val1 == val2) {
+            return resEqual;
+        }
+        return resNotEqual;
+    }
+
+    public static int test2(MyValue val1, MyValue val2, int resEqual, int resNotEqual) {
+        if (val1 == val2) {
+            return resEqual;
+        }
+        return resNotEqual;
+    }
+
+    public static int test3(MyValue val1, MyValue val2, int resEqual, int resNotEqual) {
+        if (val1 == val2) {
+            return resEqual;
+        }
+        return resNotEqual;
+    }
+
+    public static int test4(MyValue val1, MyValue val2, int resEqual, int resNotEqual) {
+        if (val1 == val2) {
+            return resEqual;
+        }
+        return resNotEqual;
+    }
+
+    public static int test5(MyValue val1, MyValue val2, int resEqual, int resNotEqual) {
+        if (val1 != val2) {
+            return resNotEqual;
+        }
+        return resEqual;
+    }
+
+    public static int test6(MyValue val1, MyValue val2, int resEqual, int resNotEqual) {
+        if (val1 != val2) {
+            return resNotEqual;
+        }
+        return resEqual;
+    }
+
+    public static int test7(MyValue val1, MyValue val2, int resEqual, int resNotEqual) {
+        if (val1 != val2) {
+            return resNotEqual;
+        }
+        return resEqual;
+    }
+
+    public static int test8(MyValue val1, MyValue val2, int resEqual, int resNotEqual) {
+        if (val1 != val2) {
+            return resNotEqual;
+        }
+        return resEqual;
+    }
+
+    public static void main(String[] args) {
+        MyValue val = new MyValue(EQUAL);
+        MyValue val_copy = new MyValue(EQUAL);
+        MyValue val_diff = new MyValue(EQUAL + 1);
+
+        // Warmup
+        for (int i = 0; i < 50_000; ++i) {
+            // Equal arguments, same oop
+            Asserts.assertEquals(test1(val, val, EQUAL, NOT_EQUAL), EQUAL);
+            Asserts.assertEquals(test5(val, val, EQUAL, NOT_EQUAL), EQUAL);
+
+            // Equal arguments, different oop
+            Asserts.assertEquals(test2(val, val_copy, EQUAL, NOT_EQUAL), EQUAL);
+            Asserts.assertEquals(test6(val, val_copy, EQUAL, NOT_EQUAL), EQUAL);
+
+            // Different arguments
+            Asserts.assertEquals(test3(val, val_diff, EQUAL, NOT_EQUAL), NOT_EQUAL);
+            Asserts.assertEquals(test4(val, val_diff, EQUAL, NOT_EQUAL), NOT_EQUAL);
+
+            Asserts.assertEquals(test7(val, val_diff, EQUAL, NOT_EQUAL), NOT_EQUAL);
+            Asserts.assertEquals(test8(val, val_diff, EQUAL, NOT_EQUAL), NOT_EQUAL);
+        }
+
+        // Now trigger deoptimization
+
+        // Different arguments
+        Asserts.assertEquals(test1(val, val_diff, EQUAL, NOT_EQUAL), NOT_EQUAL);
+        Asserts.assertEquals(test2(val, val_diff, EQUAL, NOT_EQUAL), NOT_EQUAL);
+
+        Asserts.assertEquals(test5(val, val_diff, EQUAL, NOT_EQUAL), NOT_EQUAL);
+        Asserts.assertEquals(test6(val, val_diff, EQUAL, NOT_EQUAL), NOT_EQUAL);
+
+        // Equal arguments, same oop
+        Asserts.assertEquals(test3(val, val, EQUAL, NOT_EQUAL), EQUAL);
+        Asserts.assertEquals(test7(val, val, EQUAL, NOT_EQUAL), EQUAL);
+
+        // Equal arguments, different oop
+        Asserts.assertEquals(test4(val, val_copy, EQUAL, NOT_EQUAL), EQUAL);
+        Asserts.assertEquals(test8(val, val_copy, EQUAL, NOT_EQUAL), EQUAL);
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrayAccessDeopt.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrayAccessDeopt.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrayAccessDeopt.java
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+// TODO 8325106 Investigate why this suddenly started to throw java.lang.OutOfMemoryError without -Xmx200m
+// and -XX:-UseCompressedOops -XX:+UseG1GC -XX:InitiatingHeapOccupancyPercent=0 -Xmx20m -Xmn1m -XX:G1HeapRegionSize=1m -XX:-ReduceInitialCardMarks
+
+/**
+ * @test
+ * @summary Verify that certain array accesses do not trigger deoptimization.
+ * @library /test/lib
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm -Xmx200m TestArrayAccessDeopt
+ */
+
+import java.io.File;
+import jdk.test.lib.Asserts;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue1 {
+    public int x = 0;
+}
+
+public class TestArrayAccessDeopt {
+
+    public static void test1(Object[] va, Object vt) {
+        va[0] = vt;
+    }
+
+    public static void test2(Object[] va, MyValue1 vt) {
+        va[0] = vt;
+    }
+
+    public static void test3(MyValue1[] va, Object vt) {
+        va[0] = (MyValue1)vt;
+    }
+
+    public static void test4(MyValue1[] va, MyValue1 vt) {
+        va[0] = vt;
+    }
+
+    public static void test5(Object[] va, MyValue1 vt) {
+        va[0] = vt;
+    }
+
+    public static void test6(MyValue1[] va, Object vt) {
+        va[0] = (MyValue1)vt;
+    }
+
+    public static void test7(MyValue1[] va, MyValue1 vt) {
+        va[0] = vt;
+    }
+
+    public static void test8(MyValue1[] va, MyValue1 vt) {
+        va[0] = vt;
+    }
+
+    public static void test9(MyValue1[] va, MyValue1 vt) {
+        va[0] = (MyValue1)vt;
+    }
+
+    public static void test10(Object[] va) {
+        va[0] = null;
+    }
+
+    public static void test11(MyValue1[] va) {
+        va[0] = null;
+    }
+
+    static public void main(String[] args) throws Exception {
+        if (args.length == 0) {
+            // Run test in new VM instance
+            String[] arg = {"--enable-preview", "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED", "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED",
+                            "-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly,TestArrayAccessDeopt::test*", "-XX:-UseArrayLoadStoreProfile",
+                            "-XX:+TraceDeoptimization", "-Xbatch", "-XX:-MonomorphicArrayCheck", "-Xmixed", "-XX:+ProfileInterpreter", "TestArrayAccessDeopt", "run"};
+            OutputAnalyzer oa = ProcessTools.executeTestJava(arg);
+            String output = oa.getOutput();
+            oa.shouldNotContain("Uncommon trap occurred");
+        } else {
+            MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+            MyValue1[] vaB = new MyValue1[1];
+            MyValue1 vt = new MyValue1();
+            for (int i = 0; i < 10_000; ++i) {
+                test1(va, vt);
+                test1(vaB, vt);
+                test1(vaB, null);
+                test2(va, vt);
+                test2(vaB, vt);
+                test2(vaB, null);
+                test3(va, vt);
+                test3(vaB, vt);
+                test3(vaB, null);
+                test4(va, vt);
+                test4(vaB, vt);
+                test4(vaB, null);
+                test5(va, vt);
+                test5(vaB, vt);
+                test6(va, vt);
+                try {
+                    test6(va, null);
+                    throw new RuntimeException("NullPointerException expected");
+                } catch (NullPointerException npe) {
+                    // Expected
+                }
+                test7(va, vt);
+                test8(va, vt);
+                test8(vaB, vt);
+                test9(va, vt);
+                try {
+                    test9(va, null);
+                    throw new RuntimeException("NullPointerException expected");
+                } catch (NullPointerException npe) {
+                    // Expected
+                }
+                test10(vaB);
+                test11(vaB);
+            }
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrayCopyWithOops.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrayCopyWithOops.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrayCopyWithOops.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2020, 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.
+ */
+
+// TODO 8325106 Fix and enable _arraycopy intrinsic for this test (fails with -XX:-TieredCompilation). We need more test coverage because other tests don't seem to trigger the failure.
+
+/**
+ * @test
+ * @bug 8252506
+ * @summary Verify that arraycopy intrinsics properly handle flat value class arrays with oop fields.
+ * @library /test/lib
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_arraycopy
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.inlinetypes.TestArrayCopyWithOops::test*
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.inlinetypes.TestArrayCopyWithOops::create*
+ *                   -Xbatch
+ *                   compiler.valhalla.inlinetypes.TestArrayCopyWithOops
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_arraycopy
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.inlinetypes.TestArrayCopyWithOops::test*
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.inlinetypes.TestArrayCopyWithOops::create*
+ *                   -Xbatch -XX:FlatArrayElementMaxSize=0
+ *                   compiler.valhalla.inlinetypes.TestArrayCopyWithOops
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_arraycopy
+ *                   compiler.valhalla.inlinetypes.TestArrayCopyWithOops
+ */
+
+package compiler.valhalla.inlinetypes;
+
+import java.util.Arrays;
+
+import jdk.test.lib.Asserts;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+public class TestArrayCopyWithOops {
+    static final int LEN = 200;
+
+    static class MyObject {
+        long val = Integer.MAX_VALUE;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class ManyOops {
+        MyObject o1 = new MyObject();
+        MyObject o2 = new MyObject();
+        MyObject o3 = new MyObject();
+        MyObject o4 = new MyObject();
+
+        long hash() {
+            return o1.val + o2.val + o3.val + o4.val;
+        }
+    }
+
+    static ManyOops[] createValueClassArray() {
+        ManyOops[] array = (ManyOops[])ValueClass.newNullRestrictedArray(ManyOops.class, LEN);
+        for (int i = 0; i < LEN; ++i) {
+            array[i] = new ManyOops();
+        }
+        return array;
+    }
+
+    static Object[] createObjectArray() {
+        return createValueClassArray();
+    }
+
+    static Object createObject() {
+        return createValueClassArray();
+    }
+
+    // System.arraycopy tests
+
+    static void test1(ManyOops[] dst) {
+        System.arraycopy(createValueClassArray(), 0, dst, 0, LEN);
+    }
+
+    static void test2(Object[] dst) {
+        System.arraycopy(createObjectArray(), 0, dst, 0, LEN);
+    }
+
+    static void test3(ManyOops[] dst) {
+        System.arraycopy(createObjectArray(), 0, dst, 0, LEN);
+    }
+
+    static void test4(Object[] dst) {
+        System.arraycopy(createValueClassArray(), 0, dst, 0, LEN);
+    }
+
+    // System.arraycopy tests (tightly coupled with allocation of dst array)
+
+    static Object[] test5() {
+        ManyOops[] dst = (ManyOops[])ValueClass.newNullRestrictedArray(ManyOops.class, LEN);
+        System.arraycopy(createValueClassArray(), 0, dst, 0, LEN);
+        return dst;
+    }
+
+    static Object[] test6() {
+        Object[] dst = new Object[LEN];
+        System.arraycopy(createObjectArray(), 0, dst, 0, LEN);
+        return dst;
+    }
+
+    static Object[] test7() {
+        ManyOops[] dst = (ManyOops[])ValueClass.newNullRestrictedArray(ManyOops.class, LEN);
+        System.arraycopy(createObjectArray(), 0, dst, 0, LEN);
+        return dst;
+    }
+
+    static Object[] test8() {
+        Object[] dst = new Object[LEN];
+        System.arraycopy(createValueClassArray(), 0, dst, 0, LEN);
+        return dst;
+    }
+
+    // Arrays.copyOf tests
+
+    // TODO 8325106 test9/test11 and test10/test12 are equivalent
+    // Using ManyOops[].class in both test9/11 and running with -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation -XX:-DoEscapeAnalysis -XX:+AlwaysIncrementalInline triggers an exception
+    static Object[] test9() {
+        ManyOops[] src = createValueClassArray();
+        return Arrays.copyOf(src, LEN, src.getClass());
+    }
+
+    static Object[] test10() {
+        return Arrays.copyOf(createObjectArray(), LEN, Object[].class);
+    }
+
+    static Object[] test11() {
+        ManyOops[] src = createValueClassArray();
+        return Arrays.copyOf(src, LEN, src.getClass());
+    }
+
+    static Object[] test12() {
+        return Arrays.copyOf(createObjectArray(), LEN, Object[].class);
+    }
+
+    // System.arraycopy test using generic_copy stub
+
+    static void test13(Object dst) {
+        System.arraycopy(createObject(), 0, dst, 0, LEN);
+    }
+
+    static void produceGarbage() {
+        for (int i = 0; i < 100; ++i) {
+            Object[] arrays = new Object[1024];
+            for (int j = 0; j < arrays.length; j++) {
+                arrays[i] = new int[1024];
+            }
+        }
+        System.gc();
+    }
+
+    public static void main(String[] args) {
+        ManyOops[] dst1 = createValueClassArray();
+        ManyOops[] dst2 = createValueClassArray();
+        ManyOops[] dst3 = createValueClassArray();
+        ManyOops[] dst4 = createValueClassArray();
+        ManyOops[] dst13 = createValueClassArray();
+
+        // Warmup runs to trigger compilation
+        for (int i = 0; i < 50_000; ++i) {
+            test1(dst1);
+            test2(dst2);
+            test3(dst3);
+            test4(dst4);
+            test5();
+            test6();
+            test7();
+            test8();
+            test9();
+            test10();
+            test11();
+            test12();
+            test13(dst13);
+        }
+
+        // Trigger GC to make sure dst arrays are moved to old gen
+        produceGarbage();
+
+        // Move data from flat src to flat dest
+        test1(dst1);
+        test2(dst2);
+        test3(dst3);
+        test4(dst4);
+        Object[] dst5 = test5();
+        Object[] dst6 = test6();
+        Object[] dst7 = test7();
+        Object[] dst8 = test8();
+        Object[] dst9 = test9();
+        Object[] dst10 = test10();
+        Object[] dst11 = test11();
+        Object[] dst12 = test12();
+        test13(dst13);
+
+        // Trigger GC again to make sure that the now dead src arrays are collected.
+        // MyObjects should be kept alive via oop references from the dst array.
+        produceGarbage();
+
+        // Verify content
+        long expected = 4L*Integer.MAX_VALUE;
+        for (int i = 0; i < LEN; ++i) {
+            Asserts.assertEquals(dst1[i].hash(), expected);
+            Asserts.assertEquals(dst2[i].hash(), expected);
+            Asserts.assertEquals(dst3[i].hash(), expected);
+            Asserts.assertEquals(dst4[i].hash(), expected);
+            Asserts.assertEquals(((ManyOops)dst5[i]).hash(), expected);
+            Asserts.assertEquals(((ManyOops)dst7[i]).hash(), expected);
+            Asserts.assertEquals(((ManyOops)dst8[i]).hash(), expected);
+            Asserts.assertEquals(((ManyOops)dst8[i]).hash(), expected);
+            Asserts.assertEquals(((ManyOops)dst9[i]).hash(), expected);
+            Asserts.assertEquals(((ManyOops)dst10[i]).hash(), expected);
+            Asserts.assertEquals(((ManyOops)dst11[i]).hash(), expected);
+            Asserts.assertEquals(((ManyOops)dst12[i]).hash(), expected);
+            Asserts.assertEquals(dst13[i].hash(), expected);
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java
@@ -0,0 +1,3597 @@
+/*
+ * Copyright (c) 2017, 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.*;
+import jdk.test.lib.Asserts;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+import static compiler.valhalla.inlinetypes.InlineTypeIRNode.*;
+import static compiler.valhalla.inlinetypes.InlineTypes.*;
+
+/*
+ * @test
+ * @key randomness
+ * @summary Test value class arrays.
+ * @library /test/lib /
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
+ * @enablePreview
+ * @run main/othervm/timeout=300 compiler.valhalla.inlinetypes.TestArrays
+ */
+
+@ForceCompileClassInitializer
+public class TestArrays {
+
+    public static void main(String[] args) {
+        Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS;
+        scenarios[2].addFlags("--enable-preview", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast", "-XX:+StressArrayCopyMacroNode");
+        scenarios[3].addFlags("--enable-preview", "-XX:-MonomorphicArrayCheck", "-XX:FlatArrayElementMaxSize=-1", "-XX:-UncommonNullCast");
+        scenarios[4].addFlags("--enable-preview", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast");
+        scenarios[5].addFlags("--enable-preview", "-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast", "-XX:+StressArrayCopyMacroNode");
+
+        InlineTypes.getFramework()
+                   .addScenarios(scenarios)
+                   .addHelperClasses(MyValue1.class, MyValue2.class, MyValue2Inline.class)
+                   .start();
+    }
+
+    static {
+        // Make sure RuntimeException is loaded to prevent uncommon traps in IR verified tests
+        RuntimeException tmp = new RuntimeException("42");
+    }
+
+    // Helper methods and classes
+
+    protected long hash() {
+        return hash(rI, rL);
+    }
+
+    protected long hash(int x, long y) {
+        return MyValue1.createWithFieldsInline(x, y).hash();
+    }
+
+    static void verify(Object[] src, Object[] dst) {
+        if (src instanceof MyInterface[] && dst instanceof MyInterface[]) {
+            for (int i = 0; i < src.length; ++i) {
+                Asserts.assertEQ(((MyInterface)src[i]).hash(), ((MyInterface)dst[i]).hash());
+            }
+        } else {
+            for (int i = 0; i < src.length; ++i) {
+                Asserts.assertEQ(src[i], dst[i]);
+            }
+        }
+    }
+
+    static void verify(MyValue1[] src, MyValue1[] dst) {
+        for (int i = 0; i < src.length; ++i) {
+            Asserts.assertEQ(src[i].hash(), dst[i].hash());
+        }
+    }
+
+    static void verify(MyValue1[] src, Object[] dst) {
+        for (int i = 0; i < src.length; ++i) {
+            Asserts.assertEQ(src[i].hash(), ((MyInterface)dst[i]).hash());
+        }
+    }
+
+    static void verify(MyValue2[] src, MyValue2[] dst) {
+        for (int i = 0; i < src.length; ++i) {
+            Asserts.assertEQ(src[i].hash(), dst[i].hash());
+        }
+    }
+
+    static void verify(MyValue2[] src, Object[] dst) {
+        for (int i = 0; i < src.length; ++i) {
+            Asserts.assertEQ(src[i].hash(), ((MyInterface)dst[i]).hash());
+        }
+    }
+
+    static boolean compile_and_run_again_if_deoptimized(RunInfo info) {
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            if (TestFramework.isCompiled(m)) {
+                TestFramework.compile(m, CompLevel.C2);
+            }
+        }
+        return false;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class NotFlattenable {
+        private final Object o1 = null;
+        private final Object o2 = null;
+        private final Object o3 = null;
+        private final Object o4 = null;
+        private final Object o5 = null;
+        private final Object o6 = null;
+    }
+
+    // Test value class array creation and initialization
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        counts = {ALLOCA, "= 1"})
+    @IR(applyIf = {"FlatArrayElementMaxSize", "!= -1"},
+        counts = {ALLOCA, "= 1"},
+        failOn = LOAD)
+    public MyValue1[] test1(int len) {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        for (int i = 0; i < len; ++i) {
+            va[i] = MyValue1.createWithFieldsDontInline(rI, rL);
+        }
+        return va;
+    }
+
+    @Run(test = "test1")
+    public void test1_verifier() {
+        int len = Math.abs(rI % 10);
+        MyValue1[] va = test1(len);
+        for (int i = 0; i < len; ++i) {
+            Asserts.assertEQ(va[i].hash(), hash());
+        }
+    }
+
+    // Test creation of a value class array and element access
+    @Test
+    @IR(failOn = {ALLOC, ALLOCA, LOOP, LOAD, STORE, TRAP})
+    public long test2() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        va[0] = MyValue1.createWithFieldsInline(rI, rL);
+        return va[0].hash();
+    }
+
+    @Run(test = "test2")
+    public void test2_verifier() {
+        long result = test2();
+        Asserts.assertEQ(result, hash());
+    }
+
+    // Test receiving a value class array from the interpreter,
+    // updating its elements in a loop and computing a hash.
+    @Test
+    @IR(failOn = ALLOCA)
+    public long test3(MyValue1[] va) {
+        long result = 0;
+        for (int i = 0; i < 10; ++i) {
+            result += va[i].hash();
+            va[i] = MyValue1.createWithFieldsInline(rI + 1, rL + 1);
+        }
+        return result;
+    }
+
+    @Run(test = "test3")
+    public void test3_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 10);
+        long expected = 0;
+        for (int i = 0; i < 10; ++i) {
+            va[i] = MyValue1.createWithFieldsDontInline(rI + i, rL + i);
+            expected += va[i].hash();
+        }
+        long result = test3(va);
+        Asserts.assertEQ(expected, result);
+        for (int i = 0; i < 10; ++i) {
+            if (va[i].hash() != hash(rI + 1, rL + 1)) {
+                Asserts.assertEQ(va[i].hash(), hash(rI + 1, rL + 1));
+            }
+        }
+    }
+
+    // Test returning a value class array received from the interpreter
+    @Test
+    @IR(failOn = {ALLOC, ALLOCA, LOAD, STORE, LOOP, TRAP})
+    public MyValue1[] test4(MyValue1[] va) {
+        return va;
+    }
+
+    @Run(test = "test4")
+    public void test4_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 10);
+        for (int i = 0; i < 10; ++i) {
+            va[i] = MyValue1.createWithFieldsDontInline(rI + i, rL + i);
+        }
+        va = test4(va);
+        for (int i = 0; i < 10; ++i) {
+            Asserts.assertEQ(va[i].hash(), hash(rI + i, rL + i));
+        }
+    }
+
+    // Merge value class arrays created from two branches
+    @Test
+    public MyValue1[] test5(boolean b) {
+        MyValue1[] va;
+        if (b) {
+            va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 5);
+            for (int i = 0; i < 5; ++i) {
+                va[i] = MyValue1.createWithFieldsInline(rI, rL);
+            }
+        } else {
+            va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 10);
+            for (int i = 0; i < 10; ++i) {
+                va[i] = MyValue1.createWithFieldsInline(rI + i, rL + i);
+            }
+        }
+        long sum = va[0].hashInterpreted();
+        if (b) {
+            va[0] = MyValue1.createWithFieldsDontInline(rI, sum);
+        } else {
+            va[0] = MyValue1.createWithFieldsDontInline(rI + 1, sum + 1);
+        }
+        return va;
+    }
+
+    @Run(test = "test5")
+    public void test5_verifier() {
+        MyValue1[] va = test5(true);
+        Asserts.assertEQ(va.length, 5);
+        Asserts.assertEQ(va[0].hash(), hash(rI, hash()));
+        for (int i = 1; i < 5; ++i) {
+            Asserts.assertEQ(va[i].hash(), hash());
+        }
+        va = test5(false);
+        Asserts.assertEQ(va.length, 10);
+        Asserts.assertEQ(va[0].hash(), hash(rI + 1, hash(rI, rL) + 1));
+        for (int i = 1; i < 10; ++i) {
+            Asserts.assertEQ(va[i].hash(), hash(rI + i, rL + i));
+        }
+    }
+
+    // Test creation of value class array with single element
+    @Test
+    @IR(failOn = {ALLOC, ALLOCA, LOOP, LOAD, STORE, TRAP})
+    public MyValue1 test6() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        return va[0];
+    }
+
+    @Run(test = "test6")
+    public void test6_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        MyValue1 v = test6();
+        Asserts.assertEQ(v.hashPrimitive(), va[0].hashPrimitive());
+    }
+
+    // Test default initialization of value class arrays
+    @Test
+    @IR(failOn = LOAD)
+    public MyValue1[] test7(int len) {
+        return (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+    }
+
+    @Run(test = "test7")
+    public void test7_verifier() {
+        int len = Math.abs(rI % 10);
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        MyValue1[] var = test7(len);
+        for (int i = 0; i < len; ++i) {
+            Asserts.assertEQ(va[i].hashPrimitive(), var[i].hashPrimitive());
+        }
+    }
+
+    // Test creation of value class array with zero length
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE, LOOP, TRAP})
+    public MyValue1[] test8() {
+        return (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 0);
+    }
+
+    @Run(test = "test8")
+    public void test8_verifier() {
+        MyValue1[] va = test8();
+        Asserts.assertEQ(va.length, 0);
+    }
+
+    static MyValue1[] test9_va;
+
+    // Test that value class array loaded from field has correct type
+    @Test
+    @IR(failOn = LOOP)
+    public long test9() {
+        return test9_va[0].hash();
+    }
+
+    @Run(test = "test9")
+    public void test9_verifier() {
+        test9_va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        test9_va[0] = MyValue1.createWithFieldsInline(rI, rL);
+        long result = test9();
+        Asserts.assertEQ(result, hash());
+    }
+
+    // Multi-dimensional arrays
+    @Test
+    public MyValue1[][][] test10(int len1, int len2, int len3) {
+        MyValue1[][][] arr = new MyValue1[len1][len2][len3];
+        for (int i = 0; i < len1; i++) {
+            for (int j = 0; j < len2; j++) {
+                for (int k = 0; k < len3; k++) {
+                    arr[i][j][k] = MyValue1.createWithFieldsDontInline(rI + i , rL + j + k);
+                }
+            }
+        }
+        return arr;
+    }
+
+    @Run(test = "test10")
+    public void test10_verifier() {
+        MyValue1[][][] arr = test10(2, 3, 4);
+        for (int i = 0; i < 2; i++) {
+            for (int j = 0; j < 3; j++) {
+                for (int k = 0; k < 4; k++) {
+                    Asserts.assertEQ(arr[i][j][k].hash(), MyValue1.createWithFieldsDontInline(rI + i , rL + j + k).hash());
+                }
+            }
+        }
+    }
+
+    @Test
+    public void test11(MyValue1[][][] arr, long[] res) {
+        int l = 0;
+        for (int i = 0; i < arr.length; i++) {
+            for (int j = 0; j < arr[i].length; j++) {
+                for (int k = 0; k < arr[i][j].length; k++) {
+                    res[l] = arr[i][j][k].hash();
+                    l++;
+                }
+            }
+        }
+    }
+
+    @Run(test = "test11")
+    public void test11_verifier() {
+        MyValue1[][][] arr = new MyValue1[2][3][4];
+        long[] res = new long[2*3*4];
+        long[] verif = new long[2*3*4];
+        int l = 0;
+        for (int i = 0; i < 2; i++) {
+            for (int j = 0; j < 3; j++) {
+                for (int k = 0; k < 4; k++) {
+                    arr[i][j][k] = MyValue1.createWithFieldsDontInline(rI + i, rL + j + k);
+                    verif[l] = arr[i][j][k].hash();
+                    l++;
+                }
+            }
+        }
+        test11(arr, res);
+        for (int i = 0; i < verif.length; i++) {
+            Asserts.assertEQ(res[i], verif[i]);
+        }
+    }
+
+    // Array load out of bounds (upper bound) at compile time
+    @Test
+    public int test12() {
+        int arraySize = Math.abs(rI) % 10;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, arraySize);
+
+        for (int i = 0; i < arraySize; i++) {
+            va[i] = MyValue1.createWithFieldsDontInline(rI + 1, rL);
+        }
+
+        try {
+            return va[arraySize + 1].x;
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return rI;
+        }
+    }
+
+    @Run(test = "test12")
+    public void test12_verifier() {
+        Asserts.assertEQ(test12(), rI);
+    }
+
+    // Array load  out of bounds (lower bound) at compile time
+    @Test
+    public int test13() {
+        int arraySize = Math.abs(rI) % 10;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, arraySize);
+
+        for (int i = 0; i < arraySize; i++) {
+            va[i] = MyValue1.createWithFieldsDontInline(rI + i, rL);
+        }
+
+        try {
+            return va[-arraySize].x;
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return rI;
+        }
+    }
+
+    @Run(test = "test13")
+    public void test13_verifier() {
+        Asserts.assertEQ(test13(), rI);
+    }
+
+    // Array load out of bound not known to compiler (both lower and upper bound)
+    @Test
+    public int test14(MyValue1[] va, int index)  {
+        return va[index].x;
+    }
+
+    @Run(test = "test14")
+    public void test14_verifier() {
+        int arraySize = Math.abs(rI) % 10;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, arraySize);
+
+        for (int i = 0; i < arraySize; i++) {
+            va[i] = MyValue1.createWithFieldsDontInline(rI, rL);
+        }
+
+        int result;
+        for (int i = -20; i < 20; i++) {
+            try {
+                result = test14(va, i);
+            } catch (ArrayIndexOutOfBoundsException e) {
+                result = rI;
+            }
+            Asserts.assertEQ(result, rI);
+        }
+    }
+
+    // Array store out of bounds (upper bound) at compile time
+    @Test
+    public int test15() {
+        int arraySize = Math.abs(rI) % 10;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, arraySize);
+
+        try {
+            for (int i = 0; i <= arraySize; i++) {
+                va[i] = MyValue1.createWithFieldsDontInline(rI + 1, rL);
+            }
+            return rI - 1;
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return rI;
+        }
+    }
+
+    @Run(test = "test15")
+    public void test15_verifier() {
+        Asserts.assertEQ(test15(), rI);
+    }
+
+    // Array store out of bounds (lower bound) at compile time
+    @Test
+    public int test16() {
+        int arraySize = Math.abs(rI) % 10;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, arraySize);
+
+        try {
+            for (int i = -1; i <= arraySize; i++) {
+                va[i] = MyValue1.createWithFieldsDontInline(rI + 1, rL);
+            }
+            return rI - 1;
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return rI;
+        }
+    }
+
+    @Run(test = "test16")
+    public void test16_verifier() {
+        Asserts.assertEQ(test16(), rI);
+    }
+
+    // Array store out of bound not known to compiler (both lower and upper bound)
+    @Test
+    public int test17(MyValue1[] va, int index, MyValue1 vt)  {
+        va[index] = vt;
+        return va[index].x;
+    }
+
+    @Run(test = "test17")
+    public void test17_verifier() {
+        int arraySize = Math.abs(rI) % 10;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, arraySize);
+
+        for (int i = 0; i < arraySize; i++) {
+            va[i] = MyValue1.createWithFieldsDontInline(rI, rL);
+        }
+
+        MyValue1 vt = MyValue1.createWithFieldsDontInline(rI + 1, rL);
+        int result;
+        for (int i = -20; i < 20; i++) {
+            try {
+                result = test17(va, i, vt);
+            } catch (ArrayIndexOutOfBoundsException e) {
+                result = rI + 1;
+            }
+            Asserts.assertEQ(result, rI + 1);
+        }
+
+        for (int i = 0; i < arraySize; i++) {
+            Asserts.assertEQ(va[i].x, rI + 1);
+        }
+    }
+
+    // clone() as stub call
+    @Test
+    public MyValue1[] test18(MyValue1[] va) {
+        return va.clone();
+    }
+
+    @Run(test = "test18")
+    public void test18_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        for (int i = 0; i < len; ++i) {
+            va[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        MyValue1[] result = test18(va);
+        for (int i = 0; i < len; ++i) {
+            Asserts.assertEQ(result[i].hash(), va[i].hash());
+        }
+    }
+
+    // clone() as series of loads/stores
+    static MyValue1[] test19_orig = null;
+
+    @Test
+    public MyValue1[] test19() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+        for (int i = 0; i < va.length; ++i) {
+            va[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        test19_orig = va;
+
+        return va.clone();
+    }
+
+    @Run(test = "test19")
+    public void test19_verifier() {
+        MyValue1[] result = test19();
+        for (int i = 0; i < test19_orig.length; ++i) {
+            Asserts.assertEQ(result[i].hash(), test19_orig[i].hash());
+        }
+    }
+
+    // arraycopy() of value class array with oop fields
+    @Test
+    public void test20(MyValue1[] src, MyValue1[] dst) {
+        System.arraycopy(src, 0, dst, 0, src.length);
+    }
+
+    @Run(test = "test20")
+    public void test20_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] src = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        MyValue1[] dst = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        for (int i = 0; i < len; ++i) {
+            src[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        test20(src, dst);
+        for (int i = 0; i < len; ++i) {
+            Asserts.assertEQ(src[i].hash(), dst[i].hash());
+        }
+    }
+
+    // arraycopy() of value class array with no oop field
+    @Test
+    public void test21(MyValue2[] src, MyValue2[] dst) {
+        System.arraycopy(src, 0, dst, 0, src.length);
+    }
+
+    @Run(test = "test21")
+    public void test21_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue2[] src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, len);
+        MyValue2[] dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, len);
+        for (int i = 0; i < len; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test21(src, dst);
+        for (int i = 0; i < len; ++i) {
+            Asserts.assertEQ(src[i].hash(), dst[i].hash());
+        }
+    }
+
+    // arraycopy() of value class array with oop field and tightly
+    // coupled allocation as dest
+    @Test
+    public MyValue1[] test22(MyValue1[] src) {
+        MyValue1[] dst = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, src.length);
+        System.arraycopy(src, 0, dst, 0, src.length);
+        return dst;
+    }
+
+    @Run(test = "test22")
+    public void test22_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] src = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        for (int i = 0; i < len; ++i) {
+            src[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        MyValue1[] dst = test22(src);
+        for (int i = 0; i < len; ++i) {
+            Asserts.assertEQ(src[i].hash(), dst[i].hash());
+        }
+    }
+
+    // arraycopy() of value class array with oop fields and tightly
+    // coupled allocation as dest
+    @Test
+    public MyValue1[] test23(MyValue1[] src) {
+        MyValue1[] dst = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, src.length + 10);
+        System.arraycopy(src, 0, dst, 5, src.length);
+        return dst;
+    }
+
+    @Run(test = "test23")
+    public void test23_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] src = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        for (int i = 0; i < len; ++i) {
+            src[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        MyValue1[] dst = test23(src);
+        for (int i = 5; i < len; ++i) {
+            Asserts.assertEQ(src[i].hash(), dst[i].hash());
+        }
+    }
+
+    // arraycopy() of value class array passed as Object
+    @Test
+    public void test24(MyValue1[] src, Object dst) {
+        System.arraycopy(src, 0, dst, 0, src.length);
+    }
+
+    @Run(test = "test24")
+    public void test24_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] src = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        MyValue1[] dst1 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        Object[] dst2 = new Object[len];
+        for (int i = 0; i < len; ++i) {
+            src[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        test24(src, dst1);
+        for (int i = 0; i < len; ++i) {
+            Asserts.assertEQ(src[i].hash(), dst1[i].hash());
+        }
+        test24(src, dst2);
+        for (int i = 0; i < len; ++i) {
+            Asserts.assertEQ(src[i].hash(), ((MyValue1)dst2[i]).hash());
+        }
+    }
+
+    // short arraycopy() with no oop field
+    @Test
+    public void test25(MyValue2[] src, MyValue2[] dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test25")
+    public void test25_verifier() {
+        MyValue2[] src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 8);
+        MyValue2[] dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 8);
+        for (int i = 0; i < 8; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test25(src, dst);
+        for (int i = 0; i < 8; ++i) {
+            Asserts.assertEQ(src[i].hash(), dst[i].hash());
+        }
+    }
+
+    // short arraycopy() with oop fields
+    @Test
+    public void test26(MyValue1[] src, MyValue1[] dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test26")
+    public void test26_verifier() {
+        MyValue1[] src = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+        MyValue1[] dst = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+        for (int i = 0; i < 8; ++i) {
+            src[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        test26(src, dst);
+        for (int i = 0; i < 8; ++i) {
+            Asserts.assertEQ(src[i].hash(), dst[i].hash());
+        }
+    }
+
+    // short arraycopy() with oop fields and offsets
+    @Test
+    public void test27(MyValue1[] src, MyValue1[] dst) {
+        System.arraycopy(src, 1, dst, 2, 6);
+    }
+
+    @Run(test = "test27")
+    public void test27_verifier() {
+        MyValue1[] src = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+        MyValue1[] dst = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+        for (int i = 0; i < 8; ++i) {
+            src[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        test27(src, dst);
+        for (int i = 2; i < 8; ++i) {
+            Asserts.assertEQ(src[i-1].hash(), dst[i].hash());
+        }
+    }
+
+    // non escaping allocations
+    // TODO 8252027: Make sure this is optimized with ZGC
+    @Test
+    @IR(applyIf = {"UseZGC", "false"},
+        failOn = {ALLOCA, LOOP, LOAD, TRAP})
+    public MyValue2 test28() {
+        MyValue2[] src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 10);
+        src[0] = MyValue2.createWithFieldsInline(rI, rD);
+        MyValue2[] dst = (MyValue2[])src.clone();
+        return dst[0];
+    }
+
+    @Run(test = "test28")
+    public void test28_verifier() {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, rD);
+        MyValue2 result = test28();
+        Asserts.assertEQ(result.hash(), v.hash());
+    }
+
+    // non escaping allocations
+    // TODO 8227588: shouldn't this have the same IR matching rules as test6?
+    // @Test(failOn = ALLOC + ALLOCA + LOOP + LOAD + STORE + TRAP)
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        failOn = {ALLOCA, LOOP, LOAD, TRAP})
+    @IR(applyIf = {"FlatArrayElementMaxSize", "!= -1"},
+        failOn = {ALLOCA, LOOP, TRAP})
+    public MyValue2 test29(MyValue2[] src) {
+        MyValue2[] dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 10);
+        System.arraycopy(src, 0, dst, 0, 10);
+        return dst[0];
+    }
+
+    @Run(test = "test29")
+    public void test29_verifier() {
+        MyValue2[] src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 10);
+        for (int i = 0; i < 10; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        MyValue2 v = test29(src);
+        Asserts.assertEQ(src[0].hash(), v.hash());
+    }
+
+    // non escaping allocation with uncommon trap that needs
+    // eliminated value class array element as debug info
+    @Test
+    public MyValue2 test30(MyValue2[] src, boolean flag) {
+        MyValue2[] dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 10);
+        System.arraycopy(src, 0, dst, 0, 10);
+        if (flag) { }
+        return dst[0];
+    }
+
+    @Run(test = "test30")
+    @Warmup(10000)
+    public void test30_verifier(RunInfo info) {
+        MyValue2[] src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 10);
+        for (int i = 0; i < 10; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        MyValue2 v = test30(src, !info.isWarmUp());
+        Asserts.assertEQ(src[0].hash(), v.hash());
+    }
+
+
+    // non escaping allocation with memory phi
+    @Test
+    @IR(failOn = {ALLOC, ALLOCA, LOOP, LOAD, STORE, TRAP})
+    public long test31(boolean b, boolean deopt, Method m) {
+        MyValue2[] src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 1);
+        if (b) {
+            src[0] = MyValue2.createWithFieldsInline(rI, rD);
+        } else {
+            src[0] = MyValue2.createWithFieldsInline(rI+1, rD+1);
+        }
+        if (deopt) {
+            // uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return src[0].hash();
+    }
+
+    @Run(test = "test31")
+    public void test31_verifier(RunInfo info) {
+        MyValue2 v1 = MyValue2.createWithFieldsInline(rI, rD);
+        long result1 = test31(true, !info.isWarmUp(), info.getTest());
+        Asserts.assertEQ(result1, v1.hash());
+        MyValue2 v2 = MyValue2.createWithFieldsInline(rI + 1, rD + 1);
+        long result2 = test31(false, !info.isWarmUp(), info.getTest());
+        Asserts.assertEQ(result2, v2.hash());
+    }
+
+    // Tests with Object arrays and clone/arraycopy
+    // clone() as stub call
+    @Test
+    public Object[] test32(Object[] va) {
+        return va.clone();
+    }
+
+    @Run(test = "test32")
+    public void test32_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        for (int i = 0; i < len; ++i) {
+            va[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        MyValue1[] result = (MyValue1[])test32(va);
+        for (int i = 0; i < len; ++i) {
+            Asserts.assertEQ(((MyValue1)result[i]).hash(), ((MyValue1)va[i]).hash());
+        }
+    }
+
+    @Test
+    public Object[] test33(Object[] va) {
+        return va.clone();
+    }
+
+    @Run(test = "test33")
+    public void test33_verifier() {
+        int len = Math.abs(rI) % 10;
+        Object[] va = new Object[len];
+        for (int i = 0; i < len; ++i) {
+            va[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        Object[] result = test33(va);
+        for (int i = 0; i < len; ++i) {
+            Asserts.assertEQ(((MyValue1)result[i]).hash(), ((MyValue1)va[i]).hash());
+            // Check that array has correct properties (null-ok)
+            result[i] = null;
+        }
+    }
+
+    // clone() as series of loads/stores
+    static Object[] test34_orig = null;
+
+    @ForceInline
+    public Object[] test34_helper(boolean flag) {
+        Object[] va = null;
+        if (flag) {
+            va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+            for (int i = 0; i < va.length; ++i) {
+                va[i] = MyValue1.createWithFieldsDontInline(rI, rL);
+            }
+        } else {
+            va = new Object[8];
+        }
+        return va;
+    }
+
+    @Test
+    public Object[] test34(boolean flag) {
+        Object[] va = test34_helper(flag);
+        test34_orig = va;
+        return va.clone();
+    }
+
+    @Run(test = "test34")
+    public void test34_verifier(RunInfo info) {
+        test34(false);
+        for (int i = 0; i < 10; i++) { // make sure we do deopt
+            Object[] result = test34(true);
+            verify(test34_orig, result);
+            // Check that array has correct properties (null-free)
+            try {
+                result[0] = null;
+                throw new RuntimeException("Should throw NullPointerException");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+        if (compile_and_run_again_if_deoptimized(info)) {
+            Object[] result = test34(true);
+            verify(test34_orig, result);
+            // Check that array has correct properties (null-free)
+            try {
+                result[0] = null;
+                throw new RuntimeException("Should throw NullPointerException");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    // arraycopy() of value class array of unknown size
+    @Test
+    public void test35(Object src, Object dst, int len) {
+        System.arraycopy(src, 0, dst, 0, len);
+    }
+
+    @Run(test = "test35")
+    public void test35_verifier(RunInfo info) {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] src = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        MyValue1[] dst1 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        Object[] dst2 = new Object[len];
+        for (int i = 0; i < len; ++i) {
+            src[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        test35(src, dst1, src.length);
+        verify(src, dst1);
+        test35(src, dst2, src.length);
+        verify(src, dst2);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test35(src, dst1, src.length);
+            verify(src, dst1);
+        }
+    }
+
+    @Test
+    public void test36(Object src, MyValue2[] dst) {
+        System.arraycopy(src, 0, dst, 0, dst.length);
+    }
+
+    @Run(test = "test36")
+    public void test36_verifier(RunInfo info) {
+        int len = Math.abs(rI) % 10;
+        MyValue2[] src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, len);
+        MyValue2[] dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, len);
+        for (int i = 0; i < len; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test36(src, dst);
+        verify(src, dst);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test36(src, dst);
+            verify(src, dst);
+        }
+    }
+
+    @Test
+    public void test37(MyValue2[] src, Object dst) {
+        System.arraycopy(src, 0, dst, 0, src.length);
+    }
+
+    @Run(test = "test37")
+    public void test37_verifier(RunInfo info) {
+        int len = Math.abs(rI) % 10;
+        MyValue2[] src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, len);
+        MyValue2[] dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, len);
+        for (int i = 0; i < len; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test37(src, dst);
+        verify(src, dst);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test37(src, dst);
+            verify(src, dst);
+        }
+    }
+
+    @Test
+    public void test38(Object src, MyValue2[] dst) {
+        System.arraycopy(src, 0, dst, 0, dst.length);
+    }
+
+    @Run(test = "test38")
+    @Warmup(1) // Avoid early compilation
+    public void test38_verifier(RunInfo info) {
+        int len = Math.abs(rI) % 10;
+        Object[] src = new Object[len];
+        MyValue2[] dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, len);
+        for (int i = 0; i < len; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test38(src, dst);
+        verify(dst, src);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertDeoptimizedByC2(m);
+            TestFramework.compile(m, CompLevel.C2);
+            test38(src, dst);
+            verify(dst, src);
+            TestFramework.assertCompiledByC2(m);
+        }
+    }
+
+    @Test
+    public void test39(MyValue2[] src, Object dst) {
+        System.arraycopy(src, 0, dst, 0, src.length);
+    }
+
+    @Run(test = "test39")
+    public void test39_verifier(RunInfo info) {
+        int len = Math.abs(rI) % 10;
+        MyValue2[] src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, len);
+        Object[] dst = new Object[len];
+        for (int i = 0; i < len; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test39(src, dst);
+        verify(src, dst);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test39(src, dst);
+            verify(src, dst);
+        }
+    }
+
+    @Test
+    public void test40(Object[] src, Object dst) {
+        System.arraycopy(src, 0, dst, 0, src.length);
+    }
+
+    @Run(test = "test40")
+    @Warmup(1) // Avoid early compilation
+    public void test40_verifier(RunInfo info) {
+        int len = Math.abs(rI) % 10;
+        Object[] src = new Object[len];
+        MyValue2[] dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, len);
+        for (int i = 0; i < len; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test40(src, dst);
+        verify(dst, src);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertDeoptimizedByC2(m);
+            TestFramework.compile(m, CompLevel.C2);
+            test40(src, dst);
+            verify(dst, src);
+            TestFramework.assertCompiledByC2(m);
+        }
+    }
+
+    @Test
+    public void test41(Object src, Object[] dst) {
+        System.arraycopy(src, 0, dst, 0, dst.length);
+    }
+
+    @Run(test = "test41")
+    public void test41_verifier(RunInfo info) {
+        int len = Math.abs(rI) % 10;
+        MyValue2[] src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, len);
+        Object[] dst = new Object[len];
+        for (int i = 0; i < len; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test41(src, dst);
+        verify(src, dst);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test41(src, dst);
+            verify(src, dst);
+        }
+    }
+
+    @Test
+    public void test42(Object[] src, Object[] dst) {
+        System.arraycopy(src, 0, dst, 0, src.length);
+    }
+
+    @Run(test = "test42")
+    public void test42_verifier(RunInfo info) {
+        int len = Math.abs(rI) % 10;
+        Object[] src = new Object[len];
+        Object[] dst = new Object[len];
+        for (int i = 0; i < len; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test42(src, dst);
+        verify(src, dst);
+        if (!info.isWarmUp()) {
+            TestFramework.assertCompiledByC2(info.getTest());
+        }
+    }
+
+    // short arraycopy()'s
+    @Test
+    public void test43(Object src, Object dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test43")
+    public void test43_verifier(RunInfo info) {
+        MyValue1[] src = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+        MyValue1[] dst = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+        for (int i = 0; i < 8; ++i) {
+            src[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        test43(src, dst);
+        verify(src, dst);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test43(src, dst);
+            verify(src, dst);
+        }
+    }
+
+    @Test
+    public void test44(Object src, MyValue2[] dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test44")
+    public void test44_verifier(RunInfo info) {
+        MyValue2[] src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 8);
+        MyValue2[] dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 8);
+        for (int i = 0; i < 8; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test44(src, dst);
+        verify(src, dst);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test44(src, dst);
+            verify(src, dst);
+        }
+    }
+
+    @Test
+    public void test45(MyValue2[] src, Object dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test45")
+    public void test45_verifier(RunInfo info) {
+        MyValue2[] src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 8);
+        MyValue2[] dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 8);
+        for (int i = 0; i < 8; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test45(src, dst);
+        verify(src, dst);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test45(src, dst);
+            verify(src, dst);
+        }
+    }
+
+    @Test
+    public void test46(Object[] src, MyValue2[] dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test46")
+    @Warmup(1) // Avoid early compilation
+    public void test46_verifier(RunInfo info) {
+        Object[] src = new Object[8];
+        MyValue2[] dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 8);
+        for (int i = 0; i < 8; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test46(src, dst);
+        verify(dst, src);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertDeoptimizedByC2(m);
+            TestFramework.compile(m, CompLevel.C2);
+            test46(src, dst);
+            verify(dst, src);
+            TestFramework.assertCompiledByC2(m);
+        }
+    }
+
+    @Test
+    public void test47(MyValue2[] src, Object[] dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test47")
+    public void test47_verifier(RunInfo info) {
+        MyValue2[] src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 8);
+        Object[] dst = new Object[8];
+        for (int i = 0; i < 8; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test47(src, dst);
+        verify(src, dst);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test47(src, dst);
+            verify(src, dst);
+        }
+    }
+
+    @Test
+    public void test48(Object[] src, Object dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test48")
+    @Warmup(1) // Avoid early compilation
+    public void test48_verifier(RunInfo info) {
+        Object[] src = new Object[8];
+        MyValue2[] dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 8);
+        for (int i = 0; i < 8; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test48(src, dst);
+        verify(dst, src);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertDeoptimizedByC2(m);
+            TestFramework.compile(m, CompLevel.C2);
+            test48(src, dst);
+            verify(dst, src);
+            TestFramework.assertCompiledByC2(m);
+        }
+    }
+
+    @Test
+    public void test49(Object src, Object[] dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test49")
+    public void test49_verifier(RunInfo info) {
+        MyValue2[] src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 8);
+        Object[] dst = new Object[8];
+        for (int i = 0; i < 8; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test49(src, dst);
+        verify(src, dst);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test49(src, dst);
+            verify(src, dst);
+        }
+    }
+
+    @Test
+    public void test50(Object[] src, Object[] dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test50")
+    public void test50_verifier(RunInfo info) {
+        Object[] src = new Object[8];
+        Object[] dst = new Object[8];
+        for (int i = 0; i < 8; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test50(src, dst);
+        verify(src, dst);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertCompiledByC2(m);
+        }
+    }
+
+    @Test
+    public MyValue1[] test51(MyValue1[] va) {
+        Object[] res = Arrays.copyOf(va, va.length, MyValue1[].class);
+        return (MyValue1[]) res;
+    }
+
+    @Run(test = "test51")
+    public void test51_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        for (int i = 0; i < len; ++i) {
+            va[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        MyValue1[] result = test51(va);
+        verify(va, result);
+    }
+
+    static final MyValue1[] test52_va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+
+    @Test
+    public MyValue1[] test52() {
+        Object[] res = Arrays.copyOf(test52_va, 8, MyValue1[].class);
+        return (MyValue1[]) res;
+    }
+
+    @Run(test = "test52")
+    public void test52_verifier() {
+        for (int i = 0; i < 8; ++i) {
+            test52_va[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        MyValue1[] result = test52();
+        verify(test52_va, result);
+    }
+
+    @Test
+    public MyValue1[] test53(Object[] va) {
+        Object[] res = Arrays.copyOf(va, va.length, MyValue1[].class);
+        return (MyValue1[]) res;
+    }
+
+    @Run(test = "test53")
+    public void test53_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        for (int i = 0; i < len; ++i) {
+            va[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        MyValue1[] result = test53(va);
+        verify(result, va);
+    }
+
+    @Test
+    public Object[] test54(MyValue1[] va) {
+        return Arrays.copyOf(va, va.length, Object[].class);
+    }
+
+    @Run(test = "test54")
+    public void test54_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        for (int i = 0; i < len; ++i) {
+            va[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        Object[] result = test54(va);
+        verify(va, result);
+    }
+
+    @Test
+    public Object[] test55(Object[] va) {
+        return Arrays.copyOf(va, va.length, Object[].class);
+    }
+
+    @Run(test = "test55")
+    public void test55_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        for (int i = 0; i < len; ++i) {
+            va[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        Object[] result = test55(va);
+        verify(va, result);
+    }
+
+    @Test
+    public MyValue1[] test56(Object[] va) {
+        Object[] res = Arrays.copyOf(va, va.length, MyValue1[].class);
+        return (MyValue1[]) res;
+    }
+
+    @Run(test = "test56")
+    public void test56_verifier() {
+        int len = Math.abs(rI) % 10;
+        Object[] va = new Object[len];
+        for (int i = 0; i < len; ++i) {
+            va[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        MyValue1[] result = test56(va);
+        verify(result, va);
+    }
+
+    @Test
+    public Object[] test57(Object[] va, Class klass) {
+        return Arrays.copyOf(va, va.length, klass);
+    }
+
+    @Run(test = "test57")
+    public void test57_verifier() {
+        int len = Math.abs(rI) % 10;
+        Object[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        for (int i = 0; i < len; ++i) {
+            va[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        Object[] result = test57(va, MyValue1[].class);
+        verify(va, result);
+    }
+
+    @Test
+    public Object[] test58(MyValue1[] va, Class klass) {
+        return Arrays.copyOf(va, va.length, klass);
+    }
+
+    @Run(test = "test58")
+    public void test58_verifier(RunInfo info) {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        for (int i = 0; i < len; ++i) {
+            va[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        for (int i = 0; i < 10; i++) {
+            Object[] result = test58(va, MyValue1[].class);
+            verify(va, result);
+        }
+        if (compile_and_run_again_if_deoptimized(info)) {
+            Object[] result = test58(va, MyValue1[].class);
+            verify(va, result);
+        }
+    }
+
+    // Ignore: JDK-8329224
+    // @Test
+    public Object[] test59(MyValue1[] va) {
+        return Arrays.copyOf(va, va.length+1, va.getClass());
+    }
+
+    // @Run(test = "test59")
+    public void test59_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        MyValue1[] verif = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len + 1);
+        for (int i = 0; i < len; ++i) {
+            va[i] = MyValue1.createWithFieldsInline(rI, rL);
+            verif[i] = va[i];
+        }
+        Object[] result = test59(va);
+        // Result is a null-restricted array
+        Asserts.assertEQ(result[len], ValueClass.zeroInstance(MyValue1.class));
+        result[len] = MyValue1.createDefaultInline();
+        verify(verif, result);
+    }
+
+    // Ignore: JDK-8329224
+    // @Test
+    public Object[] test60(Object[] va, Class klass) {
+        return Arrays.copyOf(va, va.length+1, klass);
+    }
+
+    // @Run(test = "test60")
+    public void test60_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        MyValue1[] verif = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len + 1);
+        for (int i = 0; i < len; ++i) {
+            va[i] = MyValue1.createWithFieldsInline(rI, rL);
+            verif[i] = (MyValue1)va[i];
+        }
+        Object[] result = test60(va, va.getClass());
+        // Result is a null-restricted array
+        Asserts.assertEQ(result[len], ValueClass.zeroInstance(MyValue1.class));
+        result[len] = MyValue1.createDefaultInline();
+        verify(verif, result);
+    }
+
+    @Test
+    public Object[] test61(Object[] va, Class klass) {
+        return Arrays.copyOf(va, va.length+1, klass);
+    }
+
+    @Run(test = "test61")
+    public void test61_verifier() {
+        int len = Math.abs(rI) % 10;
+        Object[] va = new NonValueClass[len];
+        for (int i = 0; i < len; ++i) {
+            va[i] = new NonValueClass(rI);
+        }
+        Object[] result = test61(va, NonValueClass[].class);
+        for (int i = 0; i < va.length; ++i) {
+            Asserts.assertEQ(va[i], result[i]);
+        }
+    }
+
+    @ForceInline
+    public Object[] test62_helper(int i, MyValue1[] va, NonValueClass[] oa) {
+        Object[] arr = null;
+        if (i == 10) {
+            arr = oa;
+        } else {
+            arr = va;
+        }
+        return arr;
+    }
+
+    @Test
+    public Object[] test62(MyValue1[] va, NonValueClass[] oa) {
+        int i = 0;
+        for (; i < 10; i++);
+
+        Object[] arr = test62_helper(i, va, oa);
+
+        return Arrays.copyOf(arr, arr.length+1, arr.getClass());
+    }
+
+    @Run(test = "test62")
+    public void test62_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        NonValueClass[] oa = new NonValueClass[len];
+        for (int i = 0; i < len; ++i) {
+            oa[i] = new NonValueClass(rI);
+        }
+        test62_helper(42, va, oa);
+        Object[] result = test62(va, oa);
+        for (int i = 0; i < va.length; ++i) {
+            Asserts.assertEQ(oa[i], result[i]);
+        }
+    }
+
+    @ForceInline
+    public Object[] test63_helper(int i, MyValue1[] va, NonValueClass[] oa) {
+        Object[] arr = null;
+        if (i == 10) {
+            arr = va;
+        } else {
+            arr = oa;
+        }
+        return arr;
+    }
+
+    // Ignore: JDK-8329224
+    // @Test
+    public Object[] test63(MyValue1[] va, NonValueClass[] oa) {
+        int i = 0;
+        for (; i < 10; i++);
+
+        Object[] arr = test63_helper(i, va, oa);
+
+        return Arrays.copyOf(arr, arr.length+1, arr.getClass());
+    }
+
+    // @Run(test = "test63")
+    public void test63_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        MyValue1[] verif = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len + 1);
+        for (int i = 0; i < len; ++i) {
+            va[i] = MyValue1.createWithFieldsInline(rI, rL);
+            verif[i] = va[i];
+        }
+        NonValueClass[] oa = new NonValueClass[len];
+        test63_helper(42, va, oa);
+        Object[] result = test63(va, oa);
+        // Result is not a null-restricted array
+        Asserts.assertEQ(result[len], null);
+        result[len] = MyValue1.createDefaultInline();
+        verify(verif, result);
+    }
+
+    // Test default initialization of value class arrays: small array
+    @Test
+    public MyValue1[] test64() {
+        return (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+    }
+
+    @Run(test = "test64")
+    public void test64_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+        MyValue1[] var = test64();
+        for (int i = 0; i < 8; ++i) {
+            Asserts.assertEQ(va[i].hashPrimitive(), var[i].hashPrimitive());
+        }
+    }
+
+    // Test default initialization of value class arrays: large array
+    @Test
+    public MyValue1[] test65() {
+        return (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 32);
+    }
+
+    @Run(test = "test65")
+    public void test65_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 32);
+        MyValue1[] var = test65();
+        for (int i = 0; i < 32; ++i) {
+            Asserts.assertEQ(va[i].hashPrimitive(), var[i].hashPrimitive());
+        }
+    }
+
+    // Check init store elimination
+    @Test
+    @IR(counts = {ALLOCA, "= 1"})
+    public MyValue1[] test66(MyValue1 vt) {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        va[0] = vt;
+        return va;
+    }
+
+    @Run(test = "test66")
+    public void test66_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsDontInline(rI, rL);
+        MyValue1[] va = test66(vt);
+        Asserts.assertEQ(va[0].hashPrimitive(), vt.hashPrimitive());
+    }
+
+    // Zeroing elimination and arraycopy
+    @Test
+    public MyValue1[] test67(MyValue1[] src) {
+        MyValue1[] dst = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 16);
+        System.arraycopy(src, 0, dst, 0, 13);
+        return dst;
+    }
+
+    @Run(test = "test67")
+    public void test67_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 16);
+        MyValue1[] var = test67(va);
+        for (int i = 0; i < 16; ++i) {
+            Asserts.assertEQ(va[i].hashPrimitive(), var[i].hashPrimitive());
+        }
+    }
+
+    // A store with a default value can be eliminated
+    @Test
+    public MyValue1[] test68() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        va[0] = va[1];
+        return va;
+    }
+
+    @Run(test = "test68")
+    public void test68_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        MyValue1[] var = test68();
+        for (int i = 0; i < 2; ++i) {
+            Asserts.assertEQ(va[i].hashPrimitive(), var[i].hashPrimitive());
+        }
+    }
+
+    // Requires individual stores to init array
+    @Test
+    public MyValue1[] test69(MyValue1 vt) {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 4);
+        va[0] = vt;
+        va[3] = vt;
+        return va;
+    }
+
+    @Run(test = "test69")
+    public void test69_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsDontInline(rI, rL);
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 4);
+        va[0] = vt;
+        va[3] = vt;
+        MyValue1[] var = test69(vt);
+        for (int i = 0; i < va.length; ++i) {
+            Asserts.assertEQ(va[i].hashPrimitive(), var[i].hashPrimitive());
+        }
+    }
+
+    // A store with a default value can be eliminated: same as test68
+    // but store is further away from allocation
+    @Test
+    public MyValue1[] test70(MyValue1[] other) {
+        other[1] = other[0];
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        other[0] = va[1];
+        va[0] = va[1];
+        return va;
+    }
+
+    @Run(test = "test70")
+    public void test70_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        MyValue1[] var = test70(va);
+        for (int i = 0; i < 2; ++i) {
+            Asserts.assertEQ(va[i].hashPrimitive(), var[i].hashPrimitive());
+        }
+    }
+
+    // EA needs to consider oop fields in flattened arrays
+    @Test
+    public void test71() {
+        int len = 10;
+        MyValue2[] src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, len);
+        MyValue2[] dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, len);
+        for (int i = 0; i < len; ++i) {
+            src[i] = MyValue2.createWithFieldsDontInline(rI+i, rD+i);
+        }
+        System.arraycopy(src, 0, dst, 0, src.length);
+        for (int i = 0; i < len; ++i) {
+            Asserts.assertEQ(src[i].hash(), dst[i].hash());
+        }
+    }
+
+    @Run(test = "test71")
+    public void test71_verifier() {
+        test71();
+    }
+
+    // Test EA with leaf call to 'store_unknown_inline'
+    @Test
+    public void test72(Object[] o, boolean b, Object element) {
+        Object[] arr1 = new Object[10];
+        Object[] arr2 = new Object[10];
+        if (b) {
+            arr1 = o;
+        }
+        arr1[0] = element;
+        arr2[0] = element;
+    }
+
+    @Run(test = "test72")
+    public void test72_verifier() {
+        Object[] arr = new Object[1];
+        Object elem = new Object();
+        test72(arr, true, elem);
+        test72(arr, false, elem);
+    }
+
+    @Test
+    public void test73(Object[] oa, MyValue1 v, Object o) {
+        // TestLWorld.test38 use a C1 Phi node for the array. This test
+        // adds the case where the stored value is a C1 Phi node.
+        Object o2 = (o == null) ? v : o;
+        oa[0] = v;  // The stored value is known to be flattenable
+        oa[1] = o;  // The stored value may be flattenable
+        oa[2] = o2; // The stored value may be flattenable (a C1 Phi node)
+        oa[0] = oa; // The stored value is known to be not flattenable (an Object[])
+    }
+
+    @Run(test = "test73")
+    public void test73_verifier() {
+        MyValue1 v0 = MyValue1.createWithFieldsDontInline(rI, rL);
+        MyValue1 v1 = MyValue1.createWithFieldsDontInline(rI+1, rL+1);
+        MyValue1[] arr = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 3);
+        try {
+            test73(arr, v0, v1);
+            throw new RuntimeException("ArrayStoreException expected");
+        } catch (ArrayStoreException e) {
+            // expected
+        }
+        Asserts.assertEQ(arr[0].hash(), v0.hash());
+        Asserts.assertEQ(arr[1].hash(), v1.hash());
+        Asserts.assertEQ(arr[2].hash(), v1.hash());
+    }
+
+    public static void test74Callee(MyValue1[] va) { }
+
+    // Tests invoking unloaded method with value class array in signature
+    @Test
+    public void test74(MethodHandle m, MyValue1[] va) throws Throwable {
+        m.invoke(va);
+    }
+
+    @Run(test = "test74")
+    @Warmup(0)
+    public void test74_verifier() throws Throwable {
+        MethodHandle m = MethodHandles.lookup().findStatic(TestArrays.class, "test74Callee", MethodType.methodType(void.class, MyValue1[].class));
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 0);
+        test74(m, va);
+    }
+
+    // Some more array clone tests
+    @ForceInline
+    public Object[] test75_helper(int i, MyValue1[] va, NonValueClass[] oa) {
+        Object[] arr = null;
+        if (i == 10) {
+            arr = oa;
+        } else {
+            arr = va;
+        }
+        return arr;
+    }
+
+    @Test
+    public Object[] test75(MyValue1[] va, NonValueClass[] oa) {
+        int i = 0;
+        for (; i < 10; i++);
+
+        Object[] arr = test75_helper(i, va, oa);
+        return arr.clone();
+    }
+
+    @Run(test = "test75")
+    public void test75_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        NonValueClass[] oa = new NonValueClass[len];
+        for (int i = 0; i < len; ++i) {
+            oa[i] = new NonValueClass(rI);
+        }
+        test75_helper(42, va, oa);
+        Object[] result = test75(va, oa);
+
+        for (int i = 0; i < va.length; ++i) {
+            Asserts.assertEQ(oa[i], result[i]);
+            // Check that array has correct properties (null-ok)
+            result[i] = null;
+        }
+    }
+
+    @ForceInline
+    public Object[] test76_helper(int i, MyValue1[] va, NonValueClass[] oa) {
+        Object[] arr = null;
+        if (i == 10) {
+            arr = va;
+        } else {
+            arr = oa;
+        }
+        return arr;
+    }
+
+    @Test
+    public Object[] test76(MyValue1[] va, NonValueClass[] oa) {
+        int i = 0;
+        for (; i < 10; i++);
+
+        Object[] arr = test76_helper(i, va, oa);
+        return arr.clone();
+    }
+
+    @Run(test = "test76")
+    public void test76_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        MyValue1[] verif = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        for (int i = 0; i < len; ++i) {
+            va[i] = MyValue1.createWithFieldsInline(rI, rL);
+            verif[i] = va[i];
+        }
+        NonValueClass[] oa = new NonValueClass[len];
+        test76_helper(42, va, oa);
+        Object[] result = test76(va, oa);
+        verify(verif, result);
+        // Check that array has correct properties (null-free)
+        if (len > 0) {
+            try {
+                result[0] = null;
+                throw new RuntimeException("Should throw NullPointerException");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    @Test
+    public void test77() {
+        MyValue1 v0 = MyValue1.createWithFieldsDontInline(rI, rL);
+        MyValue1 v1 = MyValue1.createWithFieldsDontInline(rI+1, rL+1);
+        MyValue1[] arr = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+
+        Object[] oa = arr;
+        Object o1 = v1;
+        Object o = (o1 == null) ? v0 : o1;
+
+        oa[0] = o; // For C1, due to IfOp optimization, the declared_type of o becomes NULL.
+
+        Asserts.assertEQ(arr[0].hash(), v1.hash());
+    }
+
+
+    @Run(test = "test77")
+    public void test77_verifier() {
+        test77();
+    }
+
+    @Test
+    public long test78(MyValue1 v, int n) {
+        long x = 0;
+        for (int i = 0; i < n; i++) {
+        }
+
+        MyValue1[] a = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, n);
+        a[0] = v;
+        for (int i = 0; i<n; i++) {
+            x += a[i].hash(); // C1 PhiSimplifier changes "a" from a Phi node to a NewObjectArray node
+        }
+
+        return x;
+    }
+
+    @Run(test = "test78")
+    public void test78_verifier() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        Asserts.assertEQ(test78(v, 1), v.hash());
+    }
+
+    // Verify that casting an array element to a non-flattenable type marks the array as not-flat
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        counts = {LOAD_UNKNOWN_INLINE, "= 1"})
+    @IR(applyIf = {"FlatArrayElementMaxSize", "!= -1"},
+        failOn = {ALLOC_G, ALLOCA_G, LOAD_UNKNOWN_INLINE})
+    public Object test79(Object[] array, int i) {
+        NonValueClass i1 = (NonValueClass)array[0];
+        Object o = array[1];
+        return array[i];
+    }
+
+    @Run(test = "test79")
+    public void test79_verifier() {
+        NonValueClass obj = new NonValueClass(rI);
+        NonValueClass[] array = new NonValueClass[2];
+        array[1] = obj;
+        Object result = test79(array, 1);
+        Asserts.assertEquals(result, obj);
+    }
+
+    // Same as test79 but with not-flattenable value class
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        counts = {LOAD_UNKNOWN_INLINE, "= 1"})
+    @IR(applyIf = {"FlatArrayElementMaxSize", "!= -1"},
+        failOn = {ALLOC_G, ALLOCA_G, LOAD_UNKNOWN_INLINE})
+    public Object test80(Object[] array, int i) {
+        NotFlattenable vt = (NotFlattenable)array[0];
+        Object o = array[1];
+        return array[i];
+    }
+
+    @Run(test = "test80")
+    public void test80_verifier() {
+        NotFlattenable vt = new NotFlattenable();
+        NotFlattenable[] array = (NotFlattenable[])ValueClass.newNullRestrictedArray(NotFlattenable.class, 2);
+        array[1] = vt;
+        Object result = test80(array, 1);
+        Asserts.assertEquals(result, vt);
+    }
+
+    // Verify that writing an object of a non-inline, non-null type to an array marks the array as not-null-free and not-flat
+    @Test
+    @IR(failOn = {ALLOC_G, ALLOCA_G, LOAD_UNKNOWN_INLINE, STORE_UNKNOWN_INLINE, INLINE_ARRAY_NULL_GUARD})
+    public Object test81(Object[] array, NonValueClass v, Object o, int i) {
+        if (v == null) {
+          return null;
+        }
+        array[0] = v;
+        array[1] = array[0];
+        array[2] = o;
+        return array[i];
+    }
+
+    @Run(test = "test81")
+    public void test81_verifier() {
+        NonValueClass obj = new NonValueClass(rI);
+        NonValueClass[] array1 = new NonValueClass[3];
+        Object[] array2 = new Object[3];
+        Object result = test81(array1, obj, obj, 0);
+        Asserts.assertEquals(array1[0], obj);
+        Asserts.assertEquals(array1[1], obj);
+        Asserts.assertEquals(array1[2], obj);
+        Asserts.assertEquals(result, obj);
+        result = test81(array2, obj, obj, 1);
+        Asserts.assertEquals(array2[0], obj);
+        Asserts.assertEquals(array2[1], obj);
+        Asserts.assertEquals(array2[2], obj);
+        Asserts.assertEquals(result, obj);
+    }
+
+    // Verify that writing an object of a non-flattenable value class to an array marks the array as not-flat
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
+        failOn = {ALLOCA_G, LOAD_UNKNOWN_INLINE, STORE_UNKNOWN_INLINE})
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+        failOn = {ALLOC_G, ALLOCA_G, LOAD_UNKNOWN_INLINE, STORE_UNKNOWN_INLINE})
+    public Object test82(Object[] array, NotFlattenable vt, Object o, int i) {
+        array[0] = vt;
+        array[1] = array[0];
+        array[2] = o;
+        return array[i];
+    }
+
+    @Run(test = "test82")
+    public void test82_verifier() {
+        NotFlattenable vt = new NotFlattenable();
+        NotFlattenable[] array1 = (NotFlattenable[])ValueClass.newNullRestrictedArray(NotFlattenable.class, 3);
+        Object[] array2 = new Object[3];
+        Object result = test82(array1, vt, vt, 0);
+        Asserts.assertEquals(array1[0], vt);
+        Asserts.assertEquals(array1[1], vt);
+        Asserts.assertEquals(array1[2], vt);
+        Asserts.assertEquals(result, vt);
+        result = test82(array2, vt, vt, 1);
+        Asserts.assertEquals(array2[0], vt);
+        Asserts.assertEquals(array2[1], vt);
+        Asserts.assertEquals(array2[2], vt);
+        Asserts.assertEquals(result, vt);
+    }
+
+    // Verify that casting an array element to a non-value class type type marks the array as not-null-free and not-flat
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        counts = {LOAD_UNKNOWN_INLINE, "= 1"},
+        failOn = {ALLOCA_G, STORE_UNKNOWN_INLINE, INLINE_ARRAY_NULL_GUARD})
+    @IR(applyIf = {"FlatArrayElementMaxSize", "!= -1"},
+            failOn = {ALLOC_G, ALLOCA_G, LOAD_UNKNOWN_INLINE, STORE_UNKNOWN_INLINE, INLINE_ARRAY_NULL_GUARD})
+    public void test83(Object[] array, Object o) {
+        NonValueClass i = (NonValueClass)array[0];
+        array[1] = o;
+    }
+
+    @Run(test = "test83")
+    public void test83_verifier() {
+        NonValueClass obj = new NonValueClass(rI);
+        NonValueClass[] array1 = new NonValueClass[2];
+        Object[] array2 = new Object[2];
+        test83(array1, obj);
+        Asserts.assertEquals(array1[1], obj);
+        test83(array2, null);
+        Asserts.assertEquals(array2[1], null);
+    }
+
+    // Verify that writing constant null into an array marks the array as not-null-free and not-flat
+    @Test
+    @IR(failOn = {ALLOC_G, ALLOCA_G, LOAD_UNKNOWN_INLINE, STORE_UNKNOWN_INLINE},
+        counts = {INLINE_ARRAY_NULL_GUARD, "= 1"})
+    public Object test84(Object[] array, int i) {
+        array[0] = null;
+        array[1] = null;
+        return array[i];
+    }
+
+    @Run(test = "test84")
+    public void test84_verifier(RunInfo info) {
+        NotFlattenable[] array1 = new NotFlattenable[2];
+        Object[] array2 = new Object[2];
+        Object result = test84(array1, 0);
+        Asserts.assertEquals(array1[0], null);
+        Asserts.assertEquals(result, null);
+        result = test84(array2, 1);
+        Asserts.assertEquals(array2[0], null);
+        Asserts.assertEquals(result, null);
+        if (!info.isWarmUp()) {
+            NotFlattenable[] array3 = (NotFlattenable[])ValueClass.newNullRestrictedArray(NotFlattenable.class, 2);
+            try {
+                test84(array3, 1);
+                throw new RuntimeException("Should throw NullPointerException");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    // Same as test84 but with branches
+    @Test
+    @IR(failOn = {ALLOC_G, ALLOCA_G, LOAD_UNKNOWN_INLINE, STORE_UNKNOWN_INLINE},
+        counts = {INLINE_ARRAY_NULL_GUARD, "= 2"})
+    public void test85(Object[] array, Object o, boolean b) {
+        if (b) {
+            array[0] = null;
+        } else {
+            array[1] = null;
+        }
+        array[1] = o;
+    }
+
+    @Run(test = "test85")
+    public void test85_verifier(RunInfo info) {
+        NonValueClass obj = new NonValueClass(rI);
+        NonValueClass[] array1 = new NonValueClass[2];
+        Object[] array2 = new Object[2];
+        test85(array1, obj, true);
+        Asserts.assertEquals(array1[1], obj);
+        test85(array1, null, false);
+        Asserts.assertEquals(array1[1], null);
+        test85(array2, obj, true);
+        Asserts.assertEquals(array2[1], obj);
+        test85(array2, null, false);
+        Asserts.assertEquals(array2[1], null);
+        if (!info.isWarmUp()) {
+            NotFlattenable[] array3 = (NotFlattenable[])ValueClass.newNullRestrictedArray(NotFlattenable.class, 2);
+            try {
+                test85(array3, null, true);
+                throw new RuntimeException("Should throw NullPointerException");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    // Same as test85 but with not-flattenable value class array
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
+        failOn = {ALLOCA_G, LOAD_UNKNOWN_INLINE, STORE_UNKNOWN_INLINE},
+        counts = {INLINE_ARRAY_NULL_GUARD, "= 2", ALLOC_G, "= 1"})
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+        failOn = {ALLOC_G, ALLOCA_G, LOAD_UNKNOWN_INLINE, STORE_UNKNOWN_INLINE},
+        counts = {INLINE_ARRAY_NULL_GUARD, "= 2"})
+    public void test86(NotFlattenable[] array, NotFlattenable o, boolean b) {
+        if (b) {
+            array[0] = null;
+        } else {
+            array[1] = null;
+        }
+        array[1] = o;
+    }
+
+    @Run(test = "test86")
+    public void test86_verifier(RunInfo info) {
+        NotFlattenable vt = new NotFlattenable();
+        NotFlattenable[] array1 = new NotFlattenable[2];
+        test86(array1, vt, true);
+        Asserts.assertEquals(array1[1], vt);
+        test86(array1, null, false);
+        Asserts.assertEquals(array1[1], null);
+        if (!info.isWarmUp()) {
+            NotFlattenable[] array2 = (NotFlattenable[])ValueClass.newNullRestrictedArray(NotFlattenable.class, 2);
+            try {
+                test86(array2, null, true);
+                throw new RuntimeException("Should throw NullPointerException");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    // Same as test85 but with value class array
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
+        failOn = {ALLOCA_G, LOAD_UNKNOWN_INLINE, STORE_UNKNOWN_INLINE},
+        counts = {INLINE_ARRAY_NULL_GUARD, "= 2", ALLOC_G, "= 1"})
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+        failOn = {ALLOC_G, ALLOCA_G, LOAD_UNKNOWN_INLINE, STORE_UNKNOWN_INLINE},
+        counts = {INLINE_ARRAY_NULL_GUARD, "= 2"})
+    public void test87(MyValue1[] array, MyValue1 o, boolean b) {
+        if (b) {
+            array[0] = null;
+        } else {
+            array[1] = null;
+        }
+        array[1] = o;
+    }
+
+    @Run(test = "test87")
+    public void test87_verifier(RunInfo info) {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1[] array1 = new MyValue1[2];
+        test87(array1, vt, true);
+        Asserts.assertEquals(array1[1], vt);
+        test87(array1, null, false);
+        Asserts.assertEquals(array1[1], null);
+        if (!info.isWarmUp()) {
+            MyValue1[] array2 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+            try {
+                test87(array2, null, true);
+                throw new RuntimeException("Should throw NullPointerException");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    // Additional correctness tests to make sure we have the required null checks
+    @Test
+    public void test88(Object[] array, NonValueClass v) {
+        array[0] = v;
+    }
+
+    @Run(test = "test88")
+    public void test88_verifier(RunInfo info) {
+        NonValueClass[] array1 = new NonValueClass[1];
+        Object[] array2 = new Object[1];
+        test88(array1, null);
+        Asserts.assertEquals(array1[0], null);
+        test88(array2, null);
+        Asserts.assertEquals(array2[0], null);
+        if (!info.isWarmUp()) {
+            MyValue1[] array3 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+            try {
+                test88(array3, null);
+                throw new RuntimeException("Should throw NullPointerException");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    @Test
+    public void test89(MyValue1[] array, NonValueClass v) {
+        Object o = v;
+        array[0] = (MyValue1)o;
+    }
+
+    @Run(test = "test89")
+    public void test89_verifier(RunInfo info) {
+        MyValue1[] array1 = new MyValue1[1];
+        test89(array1, null);
+        Asserts.assertEquals(array1[0], null);
+        if (!info.isWarmUp()) {
+            MyValue1[] array2 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+            try {
+                test89(array2, null);
+                throw new RuntimeException("Should throw NullPointerException");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    @Test
+    public boolean test90() {
+        boolean b = true;
+
+        MyValue1[] qArray = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 0);
+        MyValue1[] lArray = new MyValue1[0];
+
+        b = b && (qArray instanceof MyValue1[]);
+        b = b && (lArray instanceof MyValue1[]);
+
+        MyValue1[][] qArray2 = { (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 0) };
+        MyValue1[][] lArray2 = new MyValue1[0][0];
+
+        b = b && (qArray2 instanceof MyValue1[][]);
+        b = b && (lArray2 instanceof MyValue1[][]);
+
+        return b;
+    }
+
+    @Run(test = "test90")
+    public void test90_verifier() {
+        Asserts.assertEQ(test90(), true);
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test91Value {
+        public int f0;
+        public int f1;
+        public int f2;
+        public int f3;
+        public int f4;
+        public int f5;
+
+        public Test91Value(int i) {
+            this.f0 = i;
+            this.f1 = i;
+            this.f2 = i;
+            this.f3 = i;
+            this.f4 = i;
+            this.f5 = i;
+        }
+
+        public void verify() {
+            if ((f0 != f1) || (f1 != f2) || (f2 != f3) || (f3 != f4) || (f4 != f5)) {
+                throw new RuntimeException("test91 failed");
+            }
+        }
+    }
+
+    // Test anti-dependencies between loads and stores from flattened array
+    @Test
+    public int test91(Test91Value[] array, int lo, int val) {
+        int i = 3;
+        while (lo < i) {
+            Test91Value tmp = array[lo];
+            array[lo++] = array[i];
+            array[i--] = tmp;
+        }
+        return val;
+    }
+
+    @Run(test = "test91")
+    @Warmup(0)
+    public void test91_verifier() {
+        Test91Value[] array = (Test91Value[])ValueClass.newNullRestrictedArray(Test91Value.class, 5);
+        for (int i = 0; i < 5; ++i) {
+            array[i] = new Test91Value(i);
+            array[i].verify();
+        }
+        Asserts.assertEQ(test91(array, 0, 5), 5);
+        for (int i = 0; i < 5; ++i) {
+            array[i].verify();
+        }
+    }
+
+    @Test
+    public void test92(Object[] src, Object[] dst) {
+        System.arraycopy(src, 0, dst, 0, src.length);
+    }
+
+    @Run(test = "test92")
+    public void test92_verifier() {
+        MyValue1[] a = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        MyValue1[] b = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        try {
+            test92(a, null);
+            throw new RuntimeException("Should throw NullPointerException");
+        } catch (NullPointerException expected) {}
+
+        try {
+            test92(null, b);
+            throw new RuntimeException("Should throw NullPointerException");
+        } catch (NullPointerException expected) {}
+
+        a[0] = MyValue1.createWithFieldsInline(rI, rL);
+        test92(a, b);
+        verify(a, b);
+    }
+
+    // Same as test30 but accessing all elements of the non-escaping array
+    @Test
+    public long test93(MyValue2[] src, boolean flag) {
+        MyValue2[] dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 10);
+        System.arraycopy(src, 0, dst, 0, 10);
+        if (flag) {  }
+        return dst[0].hash() + dst[1].hash() + dst[2].hash() + dst[3].hash() + dst[4].hash() +
+               dst[5].hash() + dst[6].hash() + dst[7].hash() + dst[8].hash() + dst[9].hash();
+    }
+
+    @Run(test = "test93")
+    @Warmup(10000)
+    public void test93_verifier(RunInfo info) {
+        MyValue2[] src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 10);
+        for (int i = 0; i < 10; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        long res = test93(src, !info.isWarmUp());
+        long expected = 0;
+        for (int i = 0; i < 10; ++i) {
+            expected += src[i].hash();
+        }
+        Asserts.assertEQ(res, expected);
+    }
+
+    // Same as test93 but with variable source array offset
+    @Test
+    public long test94(MyValue2[] src, int i, boolean flag) {
+        MyValue2[] dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 10);
+        System.arraycopy(src, i, dst, 0, 1);
+        if (flag) {  }
+        return dst[0].hash() + dst[1].hash() + dst[2].hash() + dst[3].hash() + dst[4].hash() +
+               dst[5].hash() + dst[6].hash() + dst[7].hash() + dst[8].hash() + dst[9].hash();
+    }
+
+    @Run(test = "test94")
+    @Warmup(10000)
+    public void test94_verifier(RunInfo info) {
+        MyValue2[] src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 10);
+        for (int i = 0; i < 10; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        for (int i = 0; i < 10; ++i) {
+            long res = test94(src, i, !info.isWarmUp());
+            long expected = src[i].hash() + 9*MyValue2.createDefaultInline().hash();
+            Asserts.assertEQ(res, expected);
+        }
+    }
+
+    // Test propagation of not null-free/flat information
+    @Test
+    @IR(failOn = CHECKCAST_ARRAY)
+    public MyValue1[] test95(Object[] array) {
+        array[0] = null;
+        // Always throws a ClassCastException because we just successfully
+        // stored null and therefore the array can't be an value class array.
+        return (MyValue1[])array;
+    }
+
+    @Run(test = "test95")
+    public void test95_verifier() {
+        MyValue1[] array1 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        NonValueClass[] array2 = new NonValueClass[1];
+        try {
+            test95(array1);
+            throw new RuntimeException("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        try {
+            test95(array2);
+            throw new RuntimeException("Should throw ClassCastException");
+        } catch (ClassCastException e) {
+            // Expected
+        }
+    }
+
+    // Same as test95 but with cmp user of cast result
+    @Test
+    @IR(failOn = CHECKCAST_ARRAY)
+    public boolean test96(Object[] array) {
+        array[0] = null;
+        // Always throws a ClassCastException because we just successfully
+        // stored null and therefore the array can't be an value class array.
+        MyValue1[] casted = (MyValue1[])array;
+        return casted != null;
+    }
+
+    @Run(test = "test96")
+    public void test96_verifier() {
+        MyValue1[] array1 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        NonValueClass[] array2 = new NonValueClass[1];
+        try {
+            test96(array1);
+            throw new RuntimeException("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        try {
+            test96(array2);
+            throw new RuntimeException("Should throw ClassCastException");
+        } catch (ClassCastException e) {
+            // Expected
+        }
+    }
+
+    // Same as test95 but with instanceof instead of cast
+    @Test
+    @IR(failOn = CHECKCAST_ARRAY)
+    public boolean test97(Object[] array) {
+        array[0] = new NonValueClass(42);
+        // Always throws a ClassCastException because we just successfully stored
+        // a non-value type value and therefore the array can't be a value class array.
+        return array instanceof MyValue1[];
+    }
+
+    @Run(test = "test97")
+    public void test97_verifier() {
+        MyValue1[] array1 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        NonValueClass[] array2 = new NonValueClass[1];
+        try {
+            test97(array1);
+            throw new RuntimeException("Should throw ArrayStoreException");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+        boolean res = test97(array2);
+        Asserts.assertFalse(res);
+    }
+
+    // Same as test95 but with non-flattenable store
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        failOn = CHECKCAST_ARRAY)
+    public MyValue1[] test98(Object[] array) {
+        array[0] = new NotFlattenable();
+        // Always throws a ClassCastException because we just successfully stored a
+        // non-flattenable value and therefore the array can't be a flat array.
+        return (MyValue1[])array;
+    }
+
+    @Run(test = "test98")
+    public void test98_verifier() {
+        MyValue1[] array1 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        NotFlattenable[] array2 = (NotFlattenable[])ValueClass.newNullRestrictedArray(NotFlattenable.class, 1);
+        try {
+            test98(array1);
+            throw new RuntimeException("Should throw ArrayStoreException");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+        try {
+            test98(array2);
+            throw new RuntimeException("Should throw ClassCastException");
+        } catch (ClassCastException e) {
+            // Expected
+        }
+    }
+
+    // Same as test98 but with cmp user of cast result
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        failOn = CHECKCAST_ARRAY)
+    public boolean test99(Object[] array) {
+        array[0] = new NotFlattenable();
+        // Always throws a ClassCastException because we just successfully stored a
+        // non-flattenable value and therefore the array can't be a flat array.
+        MyValue1[] casted = (MyValue1[])array;
+        return casted != null;
+    }
+
+    @Run(test = "test99")
+    public void test99_verifier() {
+        MyValue1[] array1 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        NotFlattenable[] array2 = (NotFlattenable[])ValueClass.newNullRestrictedArray(NotFlattenable.class, 1);
+        try {
+            test99(array1);
+            throw new RuntimeException("Should throw ArrayStoreException");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+        try {
+            test99(array2);
+            throw new RuntimeException("Should throw ClassCastException");
+        } catch (ClassCastException e) {
+            // Expected
+        }
+    }
+
+    // Same as test98 but with instanceof instead of cast
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        failOn = CHECKCAST_ARRAY)
+    public boolean test100(Object[] array) {
+        array[0] = new NotFlattenable();
+        // Always throws a ClassCastException because we just successfully stored a
+        // non-flattenable value and therefore the array can't be a flat array.
+        return array instanceof MyValue1[];
+    }
+
+    @Run(test = "test100")
+    public void test100_verifier() {
+        MyValue1[] array1 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        NotFlattenable[] array2 = (NotFlattenable[])ValueClass.newNullRestrictedArray(NotFlattenable.class, 1);
+        try {
+            test100(array1);
+            throw new RuntimeException("Should throw ArrayStoreException");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+        boolean res = test100(array2);
+        Asserts.assertFalse(res);
+    }
+
+    // Test that CHECKCAST_ARRAY matching works as expected
+    @Test
+    // TODO 8325106 This fails to detect the "movq    R10, precise [compiler/valhalla/inlinetypes/MyValue1" shape, also affects mainline
+    // @IR(counts = { IRNode.CHECKCAST_ARRAY, "= 1" })
+    public boolean test101(Object[] array) {
+        return array instanceof MyValue1[];
+    }
+
+    @Run(test = "test101")
+    public void test101_verifier() {
+        MyValue1[] array1 = new MyValue1[1];
+        NotFlattenable[] array2 = (NotFlattenable[])ValueClass.newNullRestrictedArray(NotFlattenable.class, 1);
+        Asserts.assertTrue(test101(array1));
+        Asserts.assertFalse(test101(array2));
+    }
+
+    static final MyValue2[] val_src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 8);
+    static final MyValue2[] val_dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 8);
+    static final Object[]   obj_src = new Object[8];
+    static final Object[]   obj_null_src = new Object[8];
+    static final Object[]   obj_dst = new Object[8];
+
+    @ForceInline
+    static Object get_val_src() { return val_src; }
+    @ForceInline
+    static Object get_val_dst() { return val_dst; }
+    @ForceInline
+    static Class get_val_class() { return val_src.getClass(); }
+    @ForceInline
+    static Class get_non_val_class() { return NonValueClass[].class; }
+    @ForceInline
+    static Object get_obj_src() { return obj_src; }
+    @ForceInline
+    static Object get_obj_null_src() { return obj_null_src; }
+    @ForceInline
+    static Object get_obj_dst() { return obj_dst; }
+    @ForceInline
+    static Class get_obj_class() { return Object[].class; }
+
+    static {
+        for (int i = 0; i < 8; ++i) {
+            val_src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+            obj_src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+            obj_null_src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        obj_null_src[0] = null;
+    }
+
+    // Arraycopy with constant source and destination arrays
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        counts = {INTRINSIC_SLOW_PATH, "= 1"})
+    @IR(applyIf = {"FlatArrayElementMaxSize", "!= -1"},
+        failOn = INTRINSIC_SLOW_PATH)
+    public void test102() {
+        System.arraycopy(val_src, 0, obj_dst, 0, 8);
+    }
+
+    @Run(test = "test102")
+    public void test102_verifier() {
+        test102();
+        verify(val_src, obj_dst);
+    }
+
+    // Same as test102 but with MyValue2[] dst
+    @Test
+    @IR(failOn = INTRINSIC_SLOW_PATH)
+    public void test103() {
+        System.arraycopy(val_src, 0, val_dst, 0, 8);
+    }
+
+    @Run(test = "test103")
+    public void test103_verifier() {
+        test103();
+        verify(val_src, val_dst);
+    }
+
+    // Same as test102 but with Object[] src
+    @Test
+    @IR(failOn = INTRINSIC_SLOW_PATH)
+    public void test104() {
+        System.arraycopy(obj_src, 0, obj_dst, 0, 8);
+    }
+
+    @Run(test = "test104")
+    public void test104_verifier() {
+        test104();
+        verify(obj_src, obj_dst);
+    }
+
+    // Same as test103 but with Object[] src
+    @Test
+    @IR(counts = {INTRINSIC_SLOW_PATH, "= 1"})
+    public void test105() {
+        System.arraycopy(obj_src, 0, val_dst, 0, 8);
+    }
+
+    @Run(test = "test105")
+    public void test105_verifier() {
+        test105();
+        verify(obj_src, val_dst);
+    }
+
+    // Same as test103 but with Object[] src containing null
+    @Test
+    @IR(counts = {INTRINSIC_SLOW_PATH, "= 1"})
+    public void test105_null() {
+        System.arraycopy(obj_null_src, 0, val_dst, 0, 8);
+    }
+
+    @Run(test = "test105_null")
+    public void test105_null_verifier() {
+        try {
+            test105_null();
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    // Below tests are equal to test102-test105 but hide the src/dst types until
+    // after the arraycopy intrinsic is emitted (with incremental inlining).
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        counts = {INTRINSIC_SLOW_PATH, "= 1"})
+    @IR(applyIf = {"FlatArrayElementMaxSize", "!= -1"},
+        failOn = INTRINSIC_SLOW_PATH)
+    public void test106() {
+        System.arraycopy(get_val_src(), 0, get_obj_dst(), 0, 8);
+    }
+
+    @Run(test = "test106")
+    public void test106_verifier() {
+        test106();
+        verify(val_src, obj_dst);
+    }
+
+    // TODO 8251971: Should be optimized but we are bailing out because
+    // at parse time it looks as if src could be flat and dst could be not flat.
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "!= -1"},
+        failOn = INTRINSIC_SLOW_PATH)
+    public void test107() {
+        System.arraycopy(get_val_src(), 0, get_val_dst(), 0, 8);
+    }
+
+    @Run(test = "test107")
+    public void test107_verifier() {
+        test107();
+        verify(val_src, val_dst);
+    }
+
+    @Test
+    @IR(failOn = INTRINSIC_SLOW_PATH)
+    public void test108() {
+        System.arraycopy(get_obj_src(), 0, get_obj_dst(), 0, 8);
+    }
+
+    @Run(test = "test108")
+    public void test108_verifier() {
+        test108();
+        verify(obj_src, obj_dst);
+    }
+
+    @Test
+    @IR(counts = {INTRINSIC_SLOW_PATH, "= 1"})
+    public void test109() {
+        System.arraycopy(get_obj_src(), 0, get_val_dst(), 0, 8);
+    }
+
+    @Run(test = "test109")
+    public void test109_verifier() {
+        test109();
+        verify(obj_src, val_dst);
+    }
+
+    @Test
+    @IR(counts = {INTRINSIC_SLOW_PATH, "= 1"})
+    public void test109_null() {
+        System.arraycopy(get_obj_null_src(), 0, get_val_dst(), 0, 8);
+    }
+
+    @Run(test = "test109_null")
+    public void test109_null_verifier() {
+        try {
+            test109_null();
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    // Arrays.copyOf with constant source and destination arrays
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        counts = {INTRINSIC_SLOW_PATH, "= 1"})
+    @IR(applyIf = {"FlatArrayElementMaxSize", "!= -1"},
+        failOn = {INTRINSIC_SLOW_PATH, CLASS_CHECK_TRAP})
+    public Object[] test110() {
+        return Arrays.copyOf(val_src, 8, Object[].class);
+    }
+
+    @Run(test = "test110")
+    public void test110_verifier() {
+        Object[] res = test110();
+        verify(val_src, res);
+    }
+
+    // Same as test110 but with MyValue2[] dst
+    @Test
+    @IR(failOn = {INTRINSIC_SLOW_PATH, CLASS_CHECK_TRAP})
+    public Object[] test111() {
+        return Arrays.copyOf(val_src, 8, val_src.getClass());
+    }
+
+    @Run(test = "test111")
+    public void test111_verifier() {
+        Object[] res = test111();
+        verify(val_src, res);
+    }
+
+    // Same as test110 but with Object[] src
+    @Test
+    @IR(failOn = {INTRINSIC_SLOW_PATH, CLASS_CHECK_TRAP})
+    public Object[] test112() {
+        return Arrays.copyOf(obj_src, 8, Object[].class);
+    }
+
+    @Run(test = "test112")
+    public void test112_verifier() {
+        Object[] res = test112();
+        verify(obj_src, res);
+    }
+
+    // Same as test111 but with Object[] src
+    @Test
+    @IR(counts = {CLASS_CHECK_TRAP, " = 1"})
+    public Object[] test113() {
+        return Arrays.copyOf(obj_src, 8, MyValue2[].class);
+    }
+
+    @Run(test = "test113")
+    public void test113_verifier() {
+        Object[] res = test113();
+        verify(obj_src, res);
+    }
+
+    // Same as test111 but with Object[] src containing null
+    @Test
+    @IR(counts = {CLASS_CHECK_TRAP, " = 1"})
+    public Object[] test113_null() {
+        return Arrays.copyOf(obj_null_src, 8, val_src.getClass());
+    }
+
+    @Run(test = "test113_null")
+    public void test113_null_verifier() {
+        Object[] res = test113_null();
+        verify(obj_null_src, res);
+    }
+
+    // Below tests are equal to test110-test113 but hide the src/dst types until
+    // after the arraycopy intrinsic is emitted (with incremental inlining).
+
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        counts = {INTRINSIC_SLOW_PATH, "= 1"})
+    @IR(applyIf = {"FlatArrayElementMaxSize", "!= -1"},
+        failOn = {INTRINSIC_SLOW_PATH, CLASS_CHECK_TRAP})
+    public Object[] test114() {
+        return Arrays.copyOf((Object[])get_val_src(), 8, get_obj_class());
+    }
+
+    @Run(test = "test114")
+    public void test114_verifier() {
+        Object[] res = test114();
+        verify(val_src, res);
+    }
+
+    // TODO 8251971: Should be optimized but we are bailing out because
+    // at parse time it looks as if src could be flat and dst could be not flat
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "!= -1"},
+        failOn = {INTRINSIC_SLOW_PATH, CLASS_CHECK_TRAP})
+    public Object[] test115() {
+        return Arrays.copyOf((Object[])get_val_src(), 8, get_val_class());
+    }
+
+    @Run(test = "test115")
+    public void test115_verifier() {
+        Object[] res = test115();
+        verify(val_src, res);
+    }
+
+    @Test
+    @IR(failOn = {INTRINSIC_SLOW_PATH, CLASS_CHECK_TRAP})
+    public Object[] test116() {
+        return Arrays.copyOf((Object[])get_obj_src(), 8, get_obj_class());
+    }
+
+    @Run(test = "test116")
+    public void test116_verifier() {
+        Object[] res = test116();
+        verify(obj_src, res);
+    }
+
+    @Test
+    @IR(counts = {CLASS_CHECK_TRAP, " = 1"})
+    public Object[] test117() {
+        return Arrays.copyOf((Object[])get_obj_src(), 8, get_val_class());
+    }
+
+    @Run(test = "test117")
+    public void test117_verifier() {
+        Object[] res = test117();
+        verify(obj_src, res);
+    }
+
+    @Test
+    @IR(counts = {CLASS_CHECK_TRAP, " = 1"})
+    public Object[] test117_null() {
+        return Arrays.copyOf((Object[])get_obj_null_src(), 8, get_val_class());
+    }
+
+    @Run(test = "test117_null")
+    public void test117_null_verifier() {
+        Object[] res = test117_null();
+        verify((Object[])get_obj_null_src(), res);
+    }
+
+    // Some more Arrays.copyOf tests with only constant class
+
+    @Test
+    @IR(counts = {CLASS_CHECK_TRAP, "= 1"},
+        failOn = INTRINSIC_SLOW_PATH)
+    public Object[] test118(Object[] src) {
+        return Arrays.copyOf(src, 8, val_src.getClass());
+    }
+
+    @Run(test = "test118")
+    public void test118_verifier() {
+        Object[] res = test118(obj_src);
+        verify(obj_src, res);
+        res = test118(val_src);
+        verify(val_src, res);
+        res = test118(obj_null_src);
+        verify(obj_null_src, res);
+    }
+
+    @Test
+    public Object[] test119(Object[] src) {
+        return Arrays.copyOf(src, 8, Object[].class);
+    }
+
+    @Run(test = "test119")
+    public void test119_verifier() {
+        Object[] res = test119(obj_src);
+        verify(obj_src, res);
+        res = test119(val_src);
+        verify(val_src, res);
+    }
+
+    @Test
+    @IR(counts = {CLASS_CHECK_TRAP, "= 1"},
+        failOn = INTRINSIC_SLOW_PATH)
+    public Object[] test120(Object[] src) {
+        return Arrays.copyOf(src, 8, NonValueClass[].class);
+    }
+
+    @Run(test = "test120")
+    public void test120_verifier() {
+        NonValueClass[] arr = new NonValueClass[8];
+        for (int i = 0; i < 8; ++i) {
+            arr[i] = new NonValueClass(rI + i);
+        }
+        Object[] res = test120(arr);
+        verify(arr, res);
+        try {
+            test120(val_src);
+            throw new RuntimeException("ArrayStoreException expected");
+        } catch (ArrayStoreException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public Object[] test121(Object[] src) {
+        return Arrays.copyOf(src, 8, val_src.getClass());
+    }
+
+    @Run(test = "test121")
+    @Warmup(10000) // Make sure we hit too_many_traps for the src <: dst check
+    public void test121_verifier() {
+        Object[] res = test121(obj_src);
+        verify(obj_src, res);
+        res = test121(val_src);
+        verify(val_src, res);
+        res = test121(obj_null_src);
+        verify(obj_null_src, res);
+    }
+    @Test
+    public Object[] test122(Object[] src) {
+        return Arrays.copyOf(src, 8, get_val_class());
+    }
+
+    @Run(test = "test122")
+    @Warmup(10000) // Make sure we hit too_many_traps for the src <: dst check
+    public void test122_verifier() {
+        Object[] res = test122(obj_src);
+        verify(obj_src, res);
+        res = test122(val_src);
+        verify(val_src, res);
+        res = test122(obj_null_src);
+        verify(obj_null_src, res);
+    }
+
+    @Test
+    public Object[] test123(Object[] src) {
+        return Arrays.copyOf(src, 8, NonValueClass[].class);
+    }
+
+    @Run(test = "test123")
+    @Warmup(10000) // Make sure we hit too_many_traps for the src <: dst check
+    public void test123_verifier() {
+        NonValueClass[] arr = new NonValueClass[8];
+        for (int i = 0; i < 8; ++i) {
+            arr[i] = new NonValueClass(rI + i);
+        }
+        Object[] res = test123(arr);
+        verify(arr, res);
+        try {
+            test123(val_src);
+            throw new RuntimeException("ArrayStoreException expected");
+        } catch (ArrayStoreException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public Object[] test124(Object[] src) {
+        return Arrays.copyOf(src, 8, get_non_val_class());
+    }
+
+    @Run(test = "test124")
+    @Warmup(10000) // Make sure we hit too_many_traps for the src <: dst check
+    public void test124_verifier() {
+        NonValueClass[] arr = new NonValueClass[8];
+        for (int i = 0; i < 8; ++i) {
+            arr[i] = new NonValueClass(rI + i);
+        }
+        Object[] res = test124(arr);
+        verify(arr, res);
+        try {
+            test124(val_src);
+            throw new RuntimeException("ArrayStoreException expected");
+        } catch (ArrayStoreException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public Object[] test125(Object[] src, Class klass) {
+        return Arrays.copyOf(src, 8, klass);
+    }
+
+    @Run(test = "test125")
+    @Warmup(10000) // Make sure we hit too_many_traps for the src <: dst check
+    public void test125_verifier() {
+        NonValueClass[] arr = new NonValueClass[8];
+        for (int i = 0; i < 8; ++i) {
+            arr[i] = new NonValueClass(rI + i);
+        }
+        Object[] res = test125(arr, NonValueClass[].class);
+        verify((Object[])arr, res);
+        res = test125(val_src, val_src.getClass());
+        verify(val_src, res);
+        res = test125(obj_src, val_src.getClass());
+        verify(val_src, res);
+        try {
+            test125(obj_null_src, val_src.getClass());
+// TODO 8325106 Remove
+//            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // expected
+        }
+        try {
+            test125(arr, val_src.getClass());
+            throw new RuntimeException("ArrayStoreException expected");
+        } catch (ArrayStoreException e) {
+            // expected
+        }
+        try {
+            test125(val_src, MyValue1[].class);
+            throw new RuntimeException("ArrayStoreException expected");
+        } catch (ArrayStoreException e) {
+            // expected
+        }
+    }
+
+    // Verify that clone from (flat) value class array not containing oops is always optimized.
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        counts = {JLONG_ARRAYCOPY, "= 1"},
+        failOn = {CHECKCAST_ARRAYCOPY, CLONE_INTRINSIC_SLOW_PATH})
+    @IR(applyIf = {"FlatArrayElementMaxSize", "!= -1"},
+        failOn = CLONE_INTRINSIC_SLOW_PATH)
+    public Object[] test126() {
+        return val_src.clone();
+    }
+
+    @Run(test = "test126")
+    public void test126_verifier() {
+        Object[] res = test126();
+        verify(val_src, res);
+    }
+
+    // Verify correctness of generic_copy stub
+    @Test
+    public void test127(Object src, Object dst, int len) {
+        System.arraycopy(src, 0, dst, 0, len);
+    }
+
+    @Run(test = "test127")
+    public void test127_verifier() {
+        test127(val_src, obj_dst, 8);
+        verify(val_src, obj_dst);
+        test127(val_src, val_dst, 8);
+        verify(val_src, val_dst);
+        test127(obj_src, val_dst, 8);
+        verify(obj_src, val_dst);
+        try {
+            test127(obj_null_src, val_dst, 8);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    // Verify that copyOf with known source and unknown destination class is optimized
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        counts = {JLONG_ARRAYCOPY, "= 1"},
+        failOn = CHECKCAST_ARRAYCOPY)
+    public Object[] test128(Class klass) {
+        return Arrays.copyOf(val_src, 8, klass);
+    }
+
+    @Run(test = "test128")
+    public void test128_verifier() {
+        Object[] res = test128(MyValue2[].class);
+        verify(val_src, res);
+        res = test128(Object[].class);
+        verify(val_src, res);
+        try {
+            test128(MyValue1[].class);
+            throw new RuntimeException("ArrayStoreException expected");
+        } catch (ArrayStoreException e) {
+            // expected
+        }
+    }
+
+    // Arraycopy with non-array src/dst
+    @Test
+    public void test129(Object src, Object dst, int len) {
+        System.arraycopy(src, 0, dst, 0, len);
+    }
+
+    @Run(test = "test129")
+    public void test129_verifier() {
+        try {
+            test129(new Object(), new Object[0], 0);
+            throw new RuntimeException("ArrayStoreException expected");
+        } catch (ArrayStoreException e) {
+            // expected
+        }
+        try {
+            test129(new Object[0], new Object(), 0);
+            throw new RuntimeException("ArrayStoreException expected");
+        } catch (ArrayStoreException e) {
+            // expected
+        }
+    }
+
+    @NullRestricted
+    static final MyValueEmpty empty = new MyValueEmpty();
+
+    // Empty value class array access
+    @Test
+    // TODO 8325106 Shouldn't profiling determine that the array is null restricted?
+    //@IR(failOn = {ALLOC, ALLOCA, LOAD, STORE})
+    public MyValueEmpty test130(MyValueEmpty[] array) {
+        array[0] = new MyValueEmpty();
+        return array[1];
+    }
+
+    @Run(test = "test130")
+    public void test130_verifier() {
+        MyValueEmpty[] array = (MyValueEmpty[])ValueClass.newNullRestrictedArray(MyValueEmpty.class, 2);
+        MyValueEmpty res = test130(array);
+        Asserts.assertEquals(array[0], empty);
+        Asserts.assertEquals(res, empty);
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class EmptyContainer {
+        @NullRestricted
+        MyValueEmpty empty = new MyValueEmpty();
+    }
+
+    // Empty value class container array access
+    @Test
+    // TODO 8325106 Shouldn't profiling determine that the array is null restricted?
+    //@IR(failOn = {ALLOC, ALLOCA, LOAD, STORE})
+    public MyValueEmpty test131(EmptyContainer[] array) {
+        array[0] = new EmptyContainer();
+        return array[1].empty;
+    }
+
+    @Run(test = "test131")
+    public void test131_verifier() {
+        EmptyContainer[] array = (EmptyContainer[])ValueClass.newNullRestrictedArray(EmptyContainer.class, 2);
+        MyValueEmpty res = test131(array);
+        Asserts.assertEquals(array[0], new EmptyContainer());
+        Asserts.assertEquals(res, new MyValueEmpty());
+    }
+
+    // Empty value class array access with unknown array type
+    @Test
+    public Object test132(Object[] array) {
+        array[0] = new MyValueEmpty();
+        return array[1];
+    }
+
+    @Run(test = "test132")
+    public void test132_verifier() {
+        Object[] array = (MyValueEmpty[])ValueClass.newNullRestrictedArray(MyValueEmpty.class, 2);
+        Object res = test132(array);
+        Asserts.assertEquals(array[0], empty);
+        Asserts.assertEquals(res, empty);
+        array = new Object[2];
+        res = test132(array);
+        Asserts.assertEquals(array[0], empty);
+        Asserts.assertEquals(res, null);
+    }
+
+    // Empty value class container array access with unknown array type
+    @Test
+    public Object test133(Object[] array) {
+        array[0] = new EmptyContainer();
+        return array[1];
+    }
+
+    @Run(test = "test133")
+    public void test133_verifier() {
+        EmptyContainer empty = new EmptyContainer();
+        Object[] array = (EmptyContainer[])ValueClass.newNullRestrictedArray(EmptyContainer.class, 2);
+        Object res = test133(array);
+        Asserts.assertEquals(array[0], empty);
+        Asserts.assertEquals(res, empty);
+        array = new Object[2];
+        res = test133(array);
+        Asserts.assertEquals(array[0], empty);
+        Asserts.assertEquals(res, null);
+    }
+
+    // Non-escaping empty value class array access
+    @Test
+    @IR(failOn = {ALLOC, ALLOCA, LOAD, STORE})
+    public static MyValueEmpty test134() {
+        MyValueEmpty[] array = new MyValueEmpty[1];
+        array[0] = empty;
+        return array[0];
+    }
+
+    @Run(test = "test134")
+    public void test134_verifier() {
+        MyValueEmpty res = test134();
+        Asserts.assertEquals(res, empty);
+    }
+
+    // Test accessing a locked (value class) array
+    @Test
+    public Object test135(Object[] array, Object val) {
+        array[0] = val;
+        return array[1];
+    }
+
+    @Run(test = "test135")
+    public void test135_verifier() {
+        MyValue1[] array1 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        array1[1] = MyValue1.createWithFieldsInline(rI, rL);
+        synchronized (array1) {
+            Object res = test135(array1, array1[1]);
+            Asserts.assertEquals(((MyValue1)res).hash(), array1[1].hash());
+            Asserts.assertEquals(array1[0].hash(), array1[1].hash());
+        }
+        NonValueClass[] array2 = new NonValueClass[2];
+        array2[1] = new NonValueClass(rI);
+        synchronized (array2) {
+            Object res = test135(array2, array2[1]);
+            Asserts.assertEquals(res, array2[1]);
+            Asserts.assertEquals(array2[0], array2[1]);
+        }
+    }
+
+    // Same as test135 but with locking in compiled method
+    @Test
+    public Object test136(Object[] array, Object val) {
+        Object res = null;
+        synchronized (array) {
+            array[0] = val;
+            res = array[1];
+        }
+        return res;
+    }
+
+    @Run(test = "test136")
+    public void test136_verifier() {
+        MyValue1[] array1 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        array1[1] = MyValue1.createWithFieldsInline(rI, rL);
+        Object res = test136(array1, array1[1]);
+        Asserts.assertEquals(((MyValue1)res).hash(), array1[1].hash());
+        Asserts.assertEquals(array1[0].hash(), array1[1].hash());
+        NonValueClass[] array2 = new NonValueClass[2];
+        array2[1] = new NonValueClass(rI);
+        res = test136(array2, array2[1]);
+        Asserts.assertEquals(res, array2[1]);
+        Asserts.assertEquals(array2[0], array2[1]);
+    }
+
+    Object oFld1, oFld2;
+
+    // Test loop unwswitching with locked (value class) array accesses
+    @Test
+    public void test137(Object[] array1, Object[] array2) {
+        for (int i = 0; i < array1.length; i++) {
+            oFld1 = array1[i];
+            oFld2 = array2[i];
+        }
+    }
+
+    @Run(test = "test137")
+    public void test137_verifier() {
+        MyValue1[] array1 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 100);
+        Arrays.fill(array1, MyValue1.createWithFieldsInline(rI, rL));
+        NonValueClass[] array2 = new NonValueClass[100];
+        Arrays.fill(array2, new NonValueClass(rI));
+        synchronized (array1) {
+            test137(array1, array1);
+            Asserts.assertEquals(oFld1, array1[0]);
+            Asserts.assertEquals(oFld2, array1[0]);
+            test137(array1, array2);
+            Asserts.assertEquals(oFld1, array1[0]);
+            Asserts.assertEquals(oFld2, array2[0]);
+            test137(array2, array1);
+            Asserts.assertEquals(oFld1, array2[0]);
+            Asserts.assertEquals(oFld2, array1[0]);
+        }
+        synchronized (array2) {
+            test137(array2, array2);
+            Asserts.assertEquals(oFld1, array2[0]);
+            Asserts.assertEquals(oFld2, array2[0]);
+            test137(array1, array2);
+            Asserts.assertEquals(oFld1, array1[0]);
+            Asserts.assertEquals(oFld2, array2[0]);
+            test137(array2, array1);
+            Asserts.assertEquals(oFld1, array2[0]);
+            Asserts.assertEquals(oFld2, array1[0]);
+        }
+    }
+
+    // Same as test137 but with locking in loop
+    @Test
+    public void test138(Object[] array1, Object[] array2) {
+        for (int i = 0; i < array1.length; i++) {
+            synchronized (array1) {
+                oFld1 = array1[i];
+            }
+            synchronized (array2) {
+                oFld2 = array2[i];
+            }
+        }
+    }
+
+    @Run(test = "test138")
+    public void test138_verifier() {
+        MyValue1[] array1 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 100);
+        Arrays.fill(array1, MyValue1.createWithFieldsInline(rI, rL));
+        NonValueClass[] array2 = new NonValueClass[100];
+        Arrays.fill(array2, new NonValueClass(rI));
+        test138(array1, array1);
+        Asserts.assertEquals(oFld1, array1[0]);
+        Asserts.assertEquals(oFld2, array1[0]);
+        test138(array1, array2);
+        Asserts.assertEquals(oFld1, array1[0]);
+        Asserts.assertEquals(oFld2, array2[0]);
+        test138(array2, array1);
+        Asserts.assertEquals(oFld1, array2[0]);
+        Asserts.assertEquals(oFld2, array1[0]);
+        test138(array2, array2);
+        Asserts.assertEquals(oFld1, array2[0]);
+        Asserts.assertEquals(oFld2, array2[0]);
+        Asserts.assertEquals(oFld2, array2[0]);
+    }
+
+    // Test load from array that is only known to be not a value class array after parsing
+    @Test
+    @IR(failOn = {ALLOC_G, ALLOCA_G, LOOP, LOAD, STORE, TRAP, LOAD_UNKNOWN_INLINE,
+                  STORE_UNKNOWN_INLINE, INLINE_ARRAY_NULL_GUARD})
+    public Object test139() {
+        Object[]  array = null;
+        Object[] oarray = new NonValueClass[1];
+        Object[] varray = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        for (int i = 0; i < 10; i++) {
+            array = varray;
+            varray = oarray;
+        }
+        return array[0];
+    }
+
+    @Run(test = "test139")
+    public void test139_verifier() {
+        Object res = test139();
+        Asserts.assertEquals(res, null);
+    }
+
+    // Test store to array that is only known to be not a value class array after parsing
+    @Test
+    @IR(failOn = {ALLOCA, ALLOC_G, LOOP, LOAD, TRAP,
+                  LOAD_UNKNOWN_INLINE, STORE_UNKNOWN_INLINE, INLINE_ARRAY_NULL_GUARD})
+    public Object[] test140(Object val) {
+        Object[]  array = null;
+        Object[] oarray = new NonValueClass[1];
+        Object[] varray = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        for (int i = 0; i < 10; i++) {
+            array = varray;
+            varray = oarray;
+        }
+        array[0] = val;
+        return array;
+    }
+
+    @Run(test = "test140")
+    public void test140_verifier() {
+        NonValueClass obj = new NonValueClass(rI);
+        Object[] res = test140(obj);
+        Asserts.assertEquals(res[0], obj);
+        res = test140(null);
+        Asserts.assertEquals(res[0], null);
+    }
+
+    // Test load from array that is only known to be not a value class array after parsing
+    // TODO 8255938
+    @Test
+    // @IR(failOn = {ALLOC_G, ALLOCA_G, LOOP, LOAD, STORE, TRAP, LOAD_UNKNOWN_INLINE, STORE_UNKNOWN_INLINE, INLINE_ARRAY_NULL_GUARD})
+    public Object test141() {
+        Object[]  array = null;
+        Object[] oarray = new NonValueClass[1];
+        Object[] varray = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        for (int i = 0; i < 10; i++) {
+            array = oarray;
+            oarray = varray;
+        }
+        return array[0];
+    }
+
+    @Run(test = "test141")
+    public void test141_verifier() {
+        Object res = test141();
+        Asserts.assertEquals(res, MyValue1.createDefaultInline());
+    }
+
+    // Test store to array that is only known to be not a value class array after parsing
+    // TODO 8255938
+    @Test
+    // @IR(failOn = {ALLOCA, ALLOC_G, LOOP, LOAD, STORE, TRAP, LOAD_UNKNOWN_INLINE, STORE_UNKNOWN_INLINE, INLINE_ARRAY_NULL_GUARD})
+    public Object[] test142(Object val) {
+        Object[]  array = null;
+        Object[] oarray = new NonValueClass[1];
+        Object[] varray = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        for (int i = 0; i < 10; i++) {
+            array = oarray;
+            oarray = varray;
+        }
+        array[0] = val;
+        return array;
+    }
+
+    @Run(test = "test142")
+    public void test142_verifier(RunInfo info) {
+        Object[] res = test142(MyValue1.createDefaultInline());
+        Asserts.assertEquals(res[0], MyValue1.createDefaultInline());
+        if (!info.isWarmUp()) {
+            try {
+                test142(null);
+                throw new RuntimeException("Should throw NullPointerException");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    static interface MyInterface143 {
+        public int hash();
+    }
+
+    static class MyObject143 implements MyInterface143 {
+        public int hash() { return 42; }
+    }
+
+    volatile MyInterface143[] array143 = new MyObject143[1];
+    int len143 = 0;
+
+    volatile int vf = 0;
+
+    // Test that triggers an anti dependency failure when array mark word is loaded from immutable memory
+    @Test
+    public void test143() {
+        MyInterface143[] arr = array143;
+        int tmp = arr.length;
+        for (int i = 0; i < len143; i++) {
+            if (arr[i].hash() > 0) {
+                return;
+            }
+        }
+    }
+
+    @Run(test = "test143")
+    @Warmup(0)
+    public void test143_verifier() {
+        test143();
+    }
+
+    // Same as test143 but with two flat array checks that are unswitched
+    @Test
+    public void test144() {
+        MyInterface143[] arr1 = array143;
+        MyInterface143[] arr2 = array143;
+        int tmp1 = arr1.length;
+        int tmp2 = arr2.length;
+        for (int i = 0; i < len143; i++) {
+            if (arr1[i].hash() > 0 && arr2[i].hash() > 0) {
+                return;
+            }
+        }
+    }
+
+    @Run(test = "test144")
+    @Warmup(0)
+    public void test144_verifier() {
+        test144();
+    }
+
+    // Test that array load slow path correctly initializes non-flattened field of empty value class
+    @Test
+    public Object test145(Object[] array) {
+        return array[0];
+    }
+
+    @Run(test = "test145")
+    public void test145_verifier() {
+        Object[] array = (EmptyContainer[])ValueClass.newNullRestrictedArray(EmptyContainer.class, 1);
+        EmptyContainer empty = (EmptyContainer)test145(array);
+        Asserts.assertEquals(empty, new EmptyContainer());
+    }
+
+    // Test that non-flattened array does not block scalarization
+    @Test
+    @IR(failOn = {ALLOC, ALLOCA, LOOP, LOAD, STORE})
+    public void test146(boolean b) {
+        MyValue2 vt = MyValue2.createWithFieldsInline(rI, rD);
+        MyValue2[] array = { vt };
+        if (b) {
+            for (int i = 0; i < 10; ++i) {
+                if (array != array) {
+                    array = null;
+                }
+            }
+        }
+    }
+
+    @Run(test = "test146")
+    @Warmup(50000)
+    public void test146_verifier() {
+        test146(true);
+    }
+
+    // Test that non-flattened array does not block scalarization
+    @Test
+    @IR(failOn = {ALLOC, ALLOCA, LOOP, LOAD, STORE})
+    public int test147(boolean deopt) {
+        // Both vt and array should be scalarized
+        MyValue2 vt = MyValue2.createWithFieldsInline(rI, rD);
+        MyValue2[] array = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 1);
+
+        // Delay scalarization to after loop opts
+        boolean store = false;
+        for (int i = 0; i < 5; ++i) {
+            if (i == 1) {
+                store = true;
+            }
+        }
+        if (store) {
+            array[0] = vt;
+        }
+
+        if (deopt) {
+            // Uncommon trap referencing array
+            return array[0].x + 42;
+        }
+        return array[0].x;
+    }
+
+    @Run(test = "test147")
+    @Warmup(50000)
+    public void test147_verifier(RunInfo info) {
+        int res = test147(!info.isWarmUp());
+        Asserts.assertEquals(res, MyValue2.createWithFieldsInline(rI, rD).x + (info.isWarmUp() ? 0 : 42));
+    }
+
+    // Test that correct basic types are used when folding field
+    // loads from a scalar replaced array through an arraycopy.
+    @Test
+    public void test148(MyValue1 vt) {
+        MyValue1[] src = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        MyValue1[] dst = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        src[0] = vt;
+        System.arraycopy(src, 0, dst, 0, 1);
+        if (src[0].hash() != dst[0].hash()) {
+          throw new RuntimeException("Unexpected hash");
+        }
+    }
+
+    @Run(test = "test148")
+    public void test148_verifier() {
+        test148(MyValue1.createWithFieldsInline(rI, rL));
+    }
+
+    // Abstract class without any value class implementers
+    static abstract class MyAbstract149 {
+        public abstract int get();
+    }
+
+    static class TestClass149 extends MyAbstract149 {
+        final int x;
+
+        public int get() { return x; };
+
+        public TestClass149(int x) {
+            this.x = x;
+        }
+    }
+
+    // Test OSR compilation with array known to be not null-free/flat
+    @Test
+    public int test149(MyAbstract149[] array) {
+        int res = 0;
+        // Trigger OSR compilation
+        for (int i = 0; i < 10_000; ++i) {
+            res += array[i % 10].get();
+        }
+        return res;
+    }
+
+    @Run(test = "test149")
+    public void test149_verifier() {
+        TestClass149[] array = new TestClass149[10];
+        for (int i = 0; i < 10; ++i) {
+            array[i] = new TestClass149(i);
+        }
+        Asserts.assertEquals(test149(array), 45000);
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test150Value {
+        Object s = "test";
+    }
+
+    // Test that optimizing a checkcast of a load from a flat array works as expected
+    @Test
+    static String test150() {
+        Test150Value[] array = (Test150Value[])ValueClass.newNullRestrictedArray(Test150Value.class, 1);
+        array[0] = new Test150Value();
+        return (String)array[0].s;
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBasicFunctionality.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBasicFunctionality.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBasicFunctionality.java
@@ -0,0 +1,1065 @@
+/*
+ * 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.
+ *
+ * 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.*;
+import jdk.test.lib.Asserts;
+
+import java.lang.reflect.Method;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import static compiler.valhalla.inlinetypes.InlineTypeIRNode.*;
+import static compiler.valhalla.inlinetypes.InlineTypes.*;
+
+/*
+ * @test
+ * @key randomness
+ * @bug 8327695
+ * @summary Test the basic value class implementation in C2.
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
+ * @library /test/lib /
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm/timeout=300 compiler.valhalla.inlinetypes.TestBasicFunctionality
+ */
+
+@ForceCompileClassInitializer
+public class TestBasicFunctionality {
+
+    public static void main(String[] args) {
+        InlineTypes.getFramework()
+                   .addScenarios(InlineTypes.DEFAULT_SCENARIOS)
+                   .addHelperClasses(MyValue1.class,
+                                     MyValue2.class,
+                                     MyValue2Inline.class,
+                                     MyValue3.class,
+                                     MyValue3Inline.class)
+                   .start();
+    }
+
+    protected long hash() {
+        return hash(rI, rL);
+    }
+
+    protected long hash(int x, long y) {
+        return MyValue1.createWithFieldsInline(x, y).hash();
+    }
+
+    // Receive value class through call to interpreter
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test1() {
+        MyValue1 v = MyValue1.createWithFieldsDontInline(rI, rL);
+        return v.hash();
+    }
+
+    @Run(test = "test1")
+    public void test1_verifier() {
+        long result = test1();
+        Asserts.assertEQ(result, hash());
+    }
+
+    // Receive value object from interpreter via parameter
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test2(MyValue1 v) {
+        return v.hash();
+    }
+
+    @Run(test = "test2")
+    public void test2_verifier() {
+        MyValue1 v = MyValue1.createWithFieldsDontInline(rI, rL);
+        long result = test2(v);
+        Asserts.assertEQ(result, hash());
+    }
+
+    // Return incoming value object without accessing fields
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
+        counts = {ALLOC, "= 1", STORE, "= 19"},
+        failOn = {LOAD, TRAP})
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+        failOn = {ALLOC, LOAD, STORE, TRAP})
+    public MyValue1 test3(MyValue1 v) {
+        return v;
+    }
+
+    @Run(test = "test3")
+    public void test3_verifier() {
+        MyValue1 v1 = MyValue1.createWithFieldsDontInline(rI, rL);
+        MyValue1 v2 = test3(v1);
+        Asserts.assertEQ(v1.x, v2.x);
+        Asserts.assertEQ(v1.y, v2.y);
+    }
+
+    // Create a value object in compiled code and only use fields.
+    // Allocation should go away because value object does not escape.
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE, TRAP})
+    public long test4() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        return v.hash();
+    }
+
+    @Run(test = "test4")
+    public void test4_verifier() {
+        long result = test4();
+        Asserts.assertEQ(result, hash());
+    }
+
+    // Create a value object in compiled code and pass it to
+    // an inlined compiled method via a call.
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE, TRAP})
+    public long test5() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        return test5Inline(v);
+    }
+
+    @ForceInline
+    public long test5Inline(MyValue1 v) {
+        return v.hash();
+    }
+
+    @Run(test = "test5")
+    public void test5_verifier() {
+        long result = test5();
+        Asserts.assertEQ(result, hash());
+    }
+
+    // Create a value object in compiled code and pass it to
+    // the interpreter via a call.
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
+        counts = {ALLOC, "= 1"},
+        failOn = {LOAD, TRAP})
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+        counts = {ALLOC, "= 2"},
+        failOn = {LOAD, TRAP})
+    public long test6() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        // Pass to interpreter
+        return v.hashInterpreted();
+    }
+
+    @Run(test = "test6")
+    public void test6_verifier() {
+        long result = test6();
+        Asserts.assertEQ(result, hash());
+    }
+
+    // Create a value object in compiled code and pass it to
+    // the interpreter by returning.
+    @Test
+    // TODO 8325106 We are hitting 8314999 here and sometimes fail to detect two allocations although there are two.
+    @IR(counts = {ALLOC, "<= 2"},
+        failOn = {LOAD, TRAP})
+    public MyValue1 test7(int x, long y) {
+        return MyValue1.createWithFieldsInline(x, y);
+    }
+
+    @Run(test = "test7")
+    public void test7_verifier() {
+        MyValue1 v = test7(rI, rL);
+        Asserts.assertEQ(v.hash(), hash());
+    }
+
+    // Merge value objects created from two branches
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test8(boolean b) {
+        MyValue1 v;
+        if (b) {
+            v = MyValue1.createWithFieldsInline(rI, rL);
+        } else {
+            v = MyValue1.createWithFieldsDontInline(rI + 1, rL + 1);
+        }
+        return v.hash();
+    }
+
+    @Run(test = "test8")
+    public void test8_verifier() {
+        Asserts.assertEQ(test8(true), hash());
+        Asserts.assertEQ(test8(false), hash(rI + 1, rL + 1));
+    }
+
+    // Merge value objects created from two branches
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
+        counts = {ALLOC, "= 1", LOAD, "= 19",
+                  STORE, "= 3"}, // InitializeNode::coalesce_subword_stores merges stores
+        failOn = {TRAP})
+    // TODO 8325106
+    // @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+    //     counts = {ALLOC, "= 2", STORE, "= 19"},
+    //     failOn = {LOAD, TRAP})
+    public MyValue1 test9(boolean b, int localrI, long localrL) {
+        MyValue1 v;
+        if (b) {
+            // Value object is not allocated
+            // Do not use rI/rL directly here as null values may cause
+            // some redundant null initializations to be optimized out
+            // and matching to fail.
+            v = MyValue1.createWithFieldsInline(localrI, localrL);
+        } else {
+            // Value object is allocated by the callee
+            v = MyValue1.createWithFieldsDontInline(rI + 1, rL + 1);
+        }
+        // Need to allocate value object if 'b' is true
+        long sum = v.hashInterpreted();
+        if (b) {
+            v = MyValue1.createWithFieldsDontInline(rI, sum);
+        } else {
+            v = MyValue1.createWithFieldsDontInline(rI, sum + 1);
+        }
+        // Don't need to allocate value object because both branches allocate
+        return v;
+    }
+
+    @Run(test = "test9")
+    public void test9_verifier() {
+        MyValue1 v = test9(true, rI, rL);
+        Asserts.assertEQ(v.x, rI);
+        Asserts.assertEQ(v.y, hash());
+        v = test9(false, rI, rL);
+        Asserts.assertEQ(v.x, rI);
+        Asserts.assertEQ(v.y, hash(rI + 1, rL + 1) + 1);
+    }
+
+    // Merge value objects created in a loop (not inlined)
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test10(int x, long y) {
+        MyValue1 v = MyValue1.createWithFieldsDontInline(x, y);
+        for (int i = 0; i < 10; ++i) {
+            v = MyValue1.createWithFieldsDontInline(v.x + 1, v.y + 1);
+        }
+        return v.hash();
+    }
+
+    @Run(test = "test10")
+    public void test10_verifier() {
+        long result = test10(rI, rL);
+        Asserts.assertEQ(result, hash(rI + 10, rL + 10));
+    }
+
+    // Merge value objects created in a loop (inlined)
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE, TRAP})
+    public long test11(int x, long y) {
+        MyValue1 v = MyValue1.createWithFieldsInline(x, y);
+        for (int i = 0; i < 10; ++i) {
+            v = MyValue1.createWithFieldsInline(v.x + 1, v.y + 1);
+        }
+        return v.hash();
+    }
+
+    @Run(test = "test11")
+    public void test11_verifier() {
+        long result = test11(rI, rL);
+        Asserts.assertEQ(result, hash(rI + 10, rL + 10));
+    }
+
+    // Test loop with uncommon trap referencing a value object
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        counts = {SCOBJ, ">= 1", LOAD, "<= 12"}) // TODO 8227588 (loads should be removed)
+    public long test12(boolean b) {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, Math.abs(rI) % 10);
+        for (int i = 0; i < va.length; ++i) {
+            va[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        long result = rL;
+        for (int i = 0; i < 1000; ++i) {
+            if (b) {
+                result += v.x;
+            } else {
+                // Uncommon trap referencing v. We delegate allocation to the
+                // interpreter by adding a SafePointScalarObjectNode.
+                result = v.hashInterpreted();
+                for (int j = 0; j < va.length; ++j) {
+                    result += va[j].hash();
+                }
+            }
+        }
+        return result;
+    }
+
+    @Run(test = "test12")
+    public void test12_verifier(RunInfo info) {
+        long result = test12(info.isWarmUp());
+        Asserts.assertEQ(result, info.isWarmUp() ? rL + (1000 * rI) : ((Math.abs(rI) % 10) + 1) * hash());
+    }
+
+    // Test loop with uncommon trap referencing a value object
+    @Test
+    public long test13(boolean b) {
+        MyValue1 v = MyValue1.createWithFieldsDontInline(rI, rL);
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, Math.abs(rI) % 10);
+        for (int i = 0; i < va.length; ++i) {
+            va[i] = MyValue1.createWithFieldsDontInline(rI, rL);
+        }
+        long result = rL;
+        for (int i = 0; i < 1000; ++i) {
+            if (b) {
+                result += v.x;
+            } else {
+                // Uncommon trap referencing v. Should not allocate
+                // but just pass the existing oop to the uncommon trap.
+                result = v.hashInterpreted();
+                for (int j = 0; j < va.length; ++j) {
+                    result += va[j].hashInterpreted();
+                }
+            }
+        }
+        return result;
+    }
+
+    @Run(test = "test13")
+    public void test13_verifier(RunInfo info) {
+        long result = test13(info.isWarmUp());
+        Asserts.assertEQ(result, info.isWarmUp() ? rL + (1000 * rI) : ((Math.abs(rI) % 10) + 1) * hash());
+    }
+
+    // Create a value object in a non-inlined method and then call a
+    // non-inlined method on that value object.
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
+        failOn = {ALLOC, STORE, TRAP},
+        counts = {LOAD, "= 19"})
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+        failOn = {ALLOC, LOAD, STORE, TRAP})
+    public long test14() {
+        MyValue1 v = MyValue1.createWithFieldsDontInline(rI, rL);
+        return v.hashInterpreted();
+    }
+
+    @Run(test = "test14")
+    public void test14_verifier() {
+        long result = test14();
+        Asserts.assertEQ(result, hash());
+    }
+
+    // Create a value object in an inlined method and then call a
+    // non-inlined method on that value object.
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
+        failOn = {LOAD, TRAP},
+        counts = {ALLOC, "= 1"})
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+        failOn = {LOAD, TRAP},
+        counts = {ALLOC, "= 2"})
+    public long test15() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        return v.hashInterpreted();
+    }
+
+    @Run(test = "test15")
+    public void test15_verifier() {
+        long result = test15();
+        Asserts.assertEQ(result, hash());
+    }
+
+    // Create a value object in a non-inlined method and then call an
+    // inlined method on that value object.
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test16() {
+        MyValue1 v = MyValue1.createWithFieldsDontInline(rI, rL);
+        return v.hash();
+    }
+
+    @Run(test = "test16")
+    public void test16_verifier() {
+        long result = test16();
+        Asserts.assertEQ(result, hash());
+    }
+
+    // Create a value object in an inlined method and then call an
+    // inlined method on that value object.
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE, TRAP})
+    public long test17() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        return v.hash();
+    }
+
+    @Run(test = "test17")
+    public void test17_verifier() {
+        long result = test17();
+        Asserts.assertEQ(result, hash());
+    }
+
+    // Create a value object in compiled code and pass it to the
+    // interpreter via a call. The value object is live at the first call so
+    // debug info should include a reference to all its fields.
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
+        counts = {ALLOC, "= 1"},
+        failOn = {LOAD, TRAP})
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+        counts = {ALLOC, "= 2"},
+        failOn = {LOAD, TRAP})
+    public long test18() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        v.hashInterpreted();
+        return v.hashInterpreted();
+    }
+
+    @Run(test = "test18")
+    public void test18_verifier() {
+        long result = test18();
+        Asserts.assertEQ(result, hash());
+    }
+
+    // Create a value object in compiled code and pass it to the
+    // interpreter via a call. The value object is passed twice but
+    // should only be allocated once.
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
+        counts = {ALLOC, "= 1"},
+        failOn = {LOAD, TRAP})
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+        counts = {ALLOC, "= 2"},
+        failOn = {LOAD, TRAP})
+    public long test19() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        return sumValue(v, v);
+    }
+
+    @DontCompile
+    public long sumValue(MyValue1 v, MyValue1 dummy) {
+        return v.hash();
+    }
+
+    @Run(test = "test19")
+    public void test19_verifier() {
+        long result = test19();
+        Asserts.assertEQ(result, hash());
+    }
+
+    // Create a value type (array) in compiled code and pass it to the
+    // interpreter via a call. The value object is live at the uncommon
+    // trap: verify that deoptimization causes the value object to be
+    // correctly allocated.
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
+        counts = {ALLOC, "= 1"},
+        failOn = {LOAD})
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+        counts = {ALLOC, "= 2"},
+        failOn = LOAD)
+    public long test20(boolean deopt, Method m) {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue2[] va = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 3);
+        if (deopt) {
+            // uncommon trap
+            TestFramework.deoptimize(m);
+        }
+
+        return v.hashInterpreted() + va[0].hashInterpreted() +
+               va[1].hashInterpreted() + va[2].hashInterpreted();
+    }
+
+    @Run(test = "test20")
+    public void test20_verifier(RunInfo info) {
+        MyValue2[] va = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 42);
+        long result = test20(!info.isWarmUp(), info.getTest());
+        Asserts.assertEQ(result, hash() + va[0].hash() + va[1].hash() + va[2].hash());
+    }
+
+    // Value class fields in regular object
+    @NullRestricted
+    MyValue1 val1;
+    @NullRestricted
+    MyValue2 val2;
+    @NullRestricted
+    final MyValue1 val3 = MyValue1.createWithFieldsInline(rI, rL);
+    @NullRestricted
+    static MyValue1 val4;
+    @NullRestricted
+    static final MyValue1 val5 = MyValue1.createWithFieldsInline(rI, rL);
+
+    // Test value class fields in objects
+    @Test
+    // TODO 8325106 We already buffer the larval and we had to disable InlineTypeNode::remove_redundant_allocations for larvals
+//    @IR(counts = {ALLOC, "= 2"},
+//        failOn = TRAP)
+    public long test21(int x, long y) {
+        // Compute hash of value class fields
+        long result = val1.hash() + val2.hash() + val3.hash() + val4.hash() + val5.hash();
+        // Update fields
+        val1 = MyValue1.createWithFieldsInline(x, y);
+        val2 = MyValue2.createWithFieldsInline(x, rD);
+        val4 = MyValue1.createWithFieldsInline(x, y);
+        return result;
+    }
+
+    @Run(test = "test21")
+    public void test21_verifier() {
+        // Check if hash computed by test18 is correct
+        val1 = MyValue1.createWithFieldsInline(rI, rL);
+        val2 = val1.v2;
+        // val3 is initialized in the constructor
+        val4 = val1;
+        // val5 is initialized in the static initializer
+        long hash = val1.hash() + val2.hash() + val3.hash() + val4.hash() + val5.hash();
+        long result = test21(rI + 1, rL + 1);
+        Asserts.assertEQ(result, hash);
+        // Check if value class fields were updated
+        Asserts.assertEQ(val1.hash(), hash(rI + 1, rL + 1));
+        Asserts.assertEQ(val2.hash(), MyValue2.createWithFieldsInline(rI + 1, rD).hash());
+        Asserts.assertEQ(val4.hash(), hash(rI + 1, rL + 1));
+    }
+
+    // Test folding of constant value class fields
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE, LOOP, TRAP})
+    public long test22() {
+        // This should be constant folded
+        return val5.hash() + val5.v3.hash();
+    }
+
+    @Run(test = "test22")
+    public void test22_verifier() {
+        long result = test22();
+        Asserts.assertEQ(result, val5.hash() + val5.v3.hash());
+    }
+
+    // Test aconst_init
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE, LOOP, TRAP})
+    public long test23() {
+        MyValue2 v = MyValue2.createDefaultInline();
+        return v.hash();
+    }
+
+    @Run(test = "test23")
+    public void test23_verifier() {
+        long result = test23();
+        Asserts.assertEQ(result, MyValue2.createDefaultInline().hash());
+    }
+
+    // Test aconst_init
+    @Test
+    @IR(failOn = {ALLOC, STORE, LOOP, TRAP})
+    public long test24() {
+        MyValue1 v1 = MyValue1.createDefaultInline();
+        MyValue1 v2 = MyValue1.createDefaultDontInline();
+        return v1.hashPrimitive() + v2.hashPrimitive();
+    }
+
+    @Run(test = "test24")
+    public void test24_verifier() {
+        long result = test24();
+        Asserts.assertEQ(result, 2 * MyValue1.createDefaultInline().hashPrimitive());
+    }
+
+    // Test field initialization
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE, LOOP, TRAP})
+    public long test25() {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, rD);
+        return v.hash();
+    }
+
+    @Run(test = "test25")
+    public void test25_verifier() {
+        long result = test25();
+        Asserts.assertEQ(result, MyValue2.createWithFieldsInline(rI, rD).hash());
+    }
+
+    // Test field initialization
+    @Test
+    @IR(failOn = {ALLOC, STORE, LOOP, TRAP})
+    public long test26() {
+        MyValue1 v1 = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1 v2 = MyValue1.createWithFieldsDontInline(rI, rL);
+        return v1.hash() + v2.hash();
+    }
+
+    @Run(test = "test26")
+    public void test26_verifier() {
+        long result = test26();
+        Asserts.assertEQ(result, 2 * hash());
+    }
+
+    class TestClass27 {
+        @NullRestricted
+        public MyValue1 v;
+    }
+
+    // Test allocation elimination of unused object with initialized value class field
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE, LOOP})
+    public void test27(boolean deopt, Method m) {
+        TestClass27 unused = new TestClass27();
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        unused.v = v;
+        if (deopt) {
+            // uncommon trap
+            TestFramework.deoptimize(m);
+        }
+    }
+
+    @Run(test = "test27")
+    public void test27_verifier(RunInfo info) {
+        test27(!info.isWarmUp(), info.getTest());
+    }
+
+    @NullRestricted
+    static MyValue3 staticVal3;
+    @NullRestricted
+    static MyValue3 staticVal3_copy;
+
+    // Check elimination of redundant value class allocations
+    @Test
+    // TODO 8325106 With incremental inlining, we already buffer the larval and we had to disable InlineTypeNode::remove_redundant_allocations for larvals
+    @IR(applyIf = {"AlwaysIncrementalInline", "false"},
+        counts = {ALLOC, "= 1"})
+    public MyValue3 test28(MyValue3[] va) {
+        // Create value object and force allocation
+        MyValue3 vt = MyValue3.create();
+        va[0] = vt;
+        staticVal3 = vt;
+        vt.verify(staticVal3);
+
+        // Value object is now allocated, make a copy and force allocation.
+        // Because copy is equal to vt, C2 should remove this redundant allocation.
+        MyValue3 copy = MyValue3.setC(vt, vt.c);
+        va[0] = copy;
+        staticVal3_copy = copy;
+        copy.verify(staticVal3_copy);
+        return copy;
+    }
+
+    @Run(test = "test28")
+    public void test28_verifier() {
+        MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedArray(MyValue3.class, 1);
+        MyValue3 vt = test28(va);
+        staticVal3.verify(vt);
+        staticVal3.verify(va[0]);
+        staticVal3_copy.verify(vt);
+        staticVal3_copy.verify(va[0]);
+    }
+
+    // Verify that only dominating allocations are re-used
+    @Test
+    public MyValue3 test29(boolean warmup) {
+        MyValue3 vt = MyValue3.create();
+        if (warmup) {
+            staticVal3 = vt; // Force allocation
+        }
+        // Force allocation to verify that above
+        // non-dominating allocation is not re-used
+        MyValue3 copy = MyValue3.setC(vt, vt.c);
+        staticVal3_copy = copy;
+        copy.verify(vt);
+        return copy;
+    }
+
+    @Run(test = "test29")
+    public void test29_verifier(RunInfo info) {
+        MyValue3 vt = test29(info.isWarmUp());
+        if (info.isWarmUp()) {
+            staticVal3.verify(vt);
+        }
+    }
+
+    // Verify that C2 recognizes value class loads and re-uses the oop to avoid allocations
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        failOn = {ALLOC, ALLOCA, STORE})
+    public MyValue3 test30() {
+        // C2 can re-use the oop of staticVal3 because staticVal3 is equal to copy
+        MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedArray(MyValue3.class, 1);
+        MyValue3 copy = MyValue3.copy(staticVal3);
+        va[0] = copy;
+        copy.verify(va[0]);
+        staticVal3 = copy;
+        copy.verify(staticVal3);
+        return copy;
+    }
+
+    @Run(test = "test30")
+    public void test30_verifier() {
+        staticVal3 = MyValue3.create();
+        MyValue3 vt = test30();
+        staticVal3.verify(vt);
+    }
+
+    // Verify that C2 recognizes value class loads and re-uses the oop to avoid allocations
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+        failOn = {ALLOC, ALLOCA, STORE})
+    public MyValue3 test31() {
+        // C2 can re-use the oop returned by createDontInline()
+        // because the corresponding value object is equal to 'copy'.
+        MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedArray(MyValue3.class, 1);
+        MyValue3 copy = MyValue3.copy(MyValue3.createDontInline());
+        va[0] = copy;
+        copy.verify(va[0]);
+        staticVal3 = copy;
+        copy.verify(staticVal3);
+        return copy;
+    }
+
+    @Run(test = "test31")
+    public void test31_verifier() {
+        MyValue3 vt = test31();
+        staticVal3.verify(vt);
+    }
+
+    // Verify that C2 recognizes value class loads and re-uses the oop to avoid allocations
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+        failOn = {ALLOC, ALLOCA, STORE})
+    public MyValue3 test32(MyValue3 vt) {
+        // C2 can re-use the oop of vt because vt is equal to 'copy'.
+        MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedArray(MyValue3.class, 1);
+        MyValue3 copy = MyValue3.copy(vt);
+        va[0] = copy;
+        copy.verify(vt);
+        staticVal3 = copy;
+        copy.verify(staticVal3);
+        return copy;
+    }
+
+    @Run(test = "test32")
+    public void test32_verifier() {
+        MyValue3 vt = MyValue3.create();
+        MyValue3 result = test32(vt);
+        staticVal3.verify(vt);
+        result.verify(vt);
+    }
+
+    // Test correct identification of value object copies
+    @Test
+    public MyValue3 test33() {
+        MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedArray(MyValue3.class, 1);
+        MyValue3 vt = MyValue3.copy(staticVal3);
+        vt = MyValue3.setI(vt, vt.c);
+        // vt is not equal to staticVal3, so C2 should not re-use the oop
+        va[0] = vt;
+        Asserts.assertEQ(va[0].i, (int)vt.c);
+        staticVal3 = vt;
+        vt.verify(staticVal3);
+        return vt;
+    }
+
+    @Run(test = "test33")
+    public void test33_verifier() {
+        staticVal3 = MyValue3.create();
+        MyValue3 vt = test33();
+        Asserts.assertEQ(staticVal3.i, (int)staticVal3.c);
+        Asserts.assertEQ(vt.i, (int)staticVal3.c);
+    }
+
+    static final MyValue3[] test34Array = (MyValue3[])ValueClass.newNullRestrictedArray(MyValue3.class, 2);
+
+    // Verify that the default value class is never allocated.
+    // C2 code should load and use the default oop from the java mirror.
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        failOn = {ALLOC, ALLOCA, LOAD, STORE, LOOP, TRAP})
+    public MyValue3 test34() {
+        // Explicitly create default value
+        MyValue3 vt = MyValue3.createDefault();
+        test34Array[0] = vt;
+        staticVal3 = vt;
+        vt.verify(vt);
+
+        // Load default value from uninitialized value class array
+        MyValue3[] dva = (MyValue3[])ValueClass.newNullRestrictedArray(MyValue3.class, 1);
+        staticVal3_copy = dva[0];
+        test34Array[1] = dva[0];
+        dva[0].verify(dva[0]);
+        return vt;
+    }
+
+    @Run(test = "test34")
+    public void test34_verifier() {
+        MyValue3 vt = MyValue3.createDefault();
+        test34Array[0] = MyValue3.create();
+        test34Array[1] = MyValue3.create();
+        MyValue3 res = test34();
+        res.verify(vt);
+        staticVal3.verify(vt);
+        staticVal3_copy.verify(vt);
+        test34Array[0].verify(vt);
+        test34Array[1].verify(vt);
+    }
+
+    static final MyValue3[] test35Array = (MyValue3[])ValueClass.newNullRestrictedArray(MyValue3.class, 1);
+
+    // Same as above but manually initialize value class fields to default.
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        failOn = {ALLOC, ALLOCA, LOAD, STORE, LOOP, TRAP})
+    public MyValue3 test35(MyValue3 vt) {
+        vt = MyValue3.setC(vt, (char)0);
+        vt = MyValue3.setBB(vt, (byte)0);
+        vt = MyValue3.setS(vt, (short)0);
+        vt = MyValue3.setI(vt, 0);
+        vt = MyValue3.setL(vt, 0);
+        vt = MyValue3.setO(vt, null);
+        vt = MyValue3.setF1(vt, 0);
+        vt = MyValue3.setF2(vt, 0);
+        vt = MyValue3.setF3(vt, 0);
+        vt = MyValue3.setF4(vt, 0);
+        vt = MyValue3.setF5(vt, 0);
+        vt = MyValue3.setF6(vt, 0);
+        vt = MyValue3.setV1(vt, MyValue3Inline.createDefault());
+        test35Array[0] = vt;
+        staticVal3 = vt;
+        vt.verify(vt);
+        return vt;
+    }
+
+    @Run(test = "test35")
+    public void test35_verifier() {
+        MyValue3 vt = MyValue3.createDefault();
+        test35Array[0] = MyValue3.create();
+        MyValue3 res = test35(test35Array[0]);
+        res.verify(vt);
+        staticVal3.verify(vt);
+        test35Array[0].verify(vt);
+    }
+
+    // Merge value objects created from two branches
+
+    private Object test36_helper(Object v) {
+        return v;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test36(boolean b) {
+        Object o;
+        if (b) {
+            o = test36_helper(MyValue1.createWithFieldsInline(rI, rL));
+        } else {
+            o = test36_helper(MyValue1.createWithFieldsDontInline(rI + 1, rL + 1));
+        }
+        MyValue1 v = (MyValue1)o;
+        return v.hash();
+    }
+
+    @Run(test = "test36")
+    public void test36_verifier() {
+        Asserts.assertEQ(test36(true), hash());
+        Asserts.assertEQ(test36(false), hash(rI + 1, rL + 1));
+    }
+
+    // Test correct loading of flattened fields
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    value class Test37Value2 {
+        int x = 0;
+        int y = 0;
+    }
+
+// TODO 8325106: Re-enable once JDK-8327695 is fixed
+/*
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    value class Test37Value1 {
+        double d = 0;
+        float f = 0;
+        @NullRestricted
+        Test37Value2 v = new Test37Value2();
+    }
+
+    @Test
+    public Test37Value1 test37(Test37Value1 vt) {
+        return vt;
+    }
+
+    @Run(test = "test37")
+    public void test37_verifier() {
+        Test37Value1 vt = new Test37Value1();
+        Asserts.assertEQ(test37(vt), vt);
+    }
+*/
+
+    // Test elimination of value class allocations without a unique CheckCastPP
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    value class Test38Value {
+        public int i;
+        public Test38Value(int i) { this.i = i; }
+    }
+
+    @NullRestricted
+    static Test38Value test38Field;
+
+    @Test
+    public void test38() {
+        for (int i = 3; i < 100; ++i) {
+            int j = 1;
+            while (++j < 11) {
+                try {
+                    test38Field = new Test38Value(i);
+                } catch (ArithmeticException ae) { }
+            }
+        }
+    }
+
+    @Run(test = "test38")
+    public void test38_verifier() {
+        test38Field = new Test38Value(0);
+        test38();
+        Asserts.assertEQ(test38Field, new Test38Value(99));
+    }
+
+    // Tests split if with value class Phi users
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test39Value {
+        public int iFld1;
+        public int iFld2;
+
+        public Test39Value(int i1, int i2) { iFld1 = i1; iFld2 = i2; }
+    }
+
+    static int test39A1[][] = new int[400][400];
+    static double test39A2[] = new double[400];
+    @NullRestricted
+    static Test39Value test39Val = new Test39Value(0, 0);
+
+    @DontInline
+    public int[] getArray() {
+        return new int[400];
+    }
+
+    @Test
+    public int test39() {
+        int result = 0;
+        for (int i = 0; i < 100; ++i) {
+            switch ((i >>> 1) % 3) {
+                case 0:
+                    test39A1[i][i] = i;
+                    break;
+                case 1:
+                    for (int j = 0; j < 100; ++j) {
+                        test39A1[i] = getArray();
+                        test39Val = new Test39Value(j, test39Val.iFld2);
+                    }
+                    break;
+                case 2:
+                    for (float f = 142; f > i; f--) {
+                        test39A2[i + 1] += 3;
+                    }
+                    result += test39Val.iFld1;
+                    break;
+            }
+            double d1 = 1;
+            while (++d1 < 142) {
+                test39A1[(i >>> 1) % 400][i + 1] = result;
+                test39Val = new Test39Value(i, test39Val.iFld2);
+            }
+        }
+        return result;
+    }
+
+    @Run(test = "test39")
+    @Warmup(10)
+    public void test39_verifier() {
+        int result = test39();
+        Asserts.assertEQ(result, 1552);
+    }
+
+    // Test scalar replacement of value class array containing value class with oop fields
+    @Test
+    public long test40(boolean b) {
+        MyValue1[] va = {MyValue1.createWithFieldsInline(rI, rL)};
+        long result = 0;
+        for (int i = 0; i < 1000; ++i) {
+            if (!b) {
+                result = va[0].hash();
+            }
+        }
+        return result;
+    }
+
+    @Run(test = "test40")
+    public void test40_verifier(RunInfo info) {
+        long result = test40(info.isWarmUp());
+        Asserts.assertEQ(result, info.isWarmUp() ? 0 : hash());
+    }
+
+    static value class MyValue41 {
+        int x;
+
+        public MyValue41(int x) {
+            this.x = x;
+        }
+
+        static MyValue41 make() {
+            return new MyValue41(0);
+        }
+    }
+
+    static MyValue41 field41;
+
+    // Test detection of value object copies and removal of the MemBarRelease following the value buffer initialization
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+        failOn = {ALLOC, ALLOCA, STORE})
+    public void test41(MyValue41 val) {
+        field41 = new MyValue41(val.x);
+    }
+
+    @Run(test = "test41")
+    public void test41_verifier() {
+        MyValue41 val = new MyValue41(rI);
+        test41(val);
+        Asserts.assertEQ(field41, val);
+    }
+
+    @DontInline
+    public void test42_helper(MyValue41 val) {
+        Asserts.assertEQ(val, new MyValue41(rI));
+    }
+
+    // Same as test41 but with call argument requiring buffering
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+        failOn = {ALLOC, ALLOCA, STORE})
+    public void test42(MyValue41 val) {
+        test42_helper(new MyValue41(val.x));
+    }
+
+    @Run(test = "test42")
+    public void test42_verifier() {
+        MyValue41 val = new MyValue41(rI);
+        test42(val);
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBimorphicInlining.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBimorphicInlining.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBimorphicInlining.java
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ *
+ * 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 compiler.valhalla.inlinetypes;
+
+import java.util.Random;
+import jdk.test.lib.Asserts;
+
+/**
+ * @test
+ * @key randomness
+ * @bug 8209009
+ * @summary Test bimorphic inlining with value object receivers.
+ * @library /testlibrary /test/lib
+ * @enablePreview
+ * @run main/othervm -Xbatch -XX:TypeProfileLevel=222
+ *                   -XX:CompileCommand=compileonly,compiler.valhalla.inlinetypes.TestBimorphicInlining::test*
+ *                   -XX:CompileCommand=quiet -XX:CompileCommand=print,compiler.valhalla.inlinetypes.TestBimorphicInlining::test*
+ *                   compiler.valhalla.inlinetypes.TestBimorphicInlining
+ * @run main/othervm -Xbatch -XX:TypeProfileLevel=222
+ *                   -XX:+UnlockExperimentalVMOptions -XX:PerMethodTrapLimit=0 -XX:PerMethodSpecTrapLimit=0
+ *                   -XX:CompileCommand=compileonly,compiler.valhalla.inlinetypes.TestBimorphicInlining::test*
+ *                   -XX:CompileCommand=quiet -XX:CompileCommand=print,compiler.valhalla.inlinetypes.TestBimorphicInlining::test*
+ *                   compiler.valhalla.inlinetypes.TestBimorphicInlining
+ */
+
+interface MyInterface {
+    public MyInterface hash(MyInterface arg);
+}
+
+value class TestValue1 implements MyInterface {
+    int x;
+
+    public TestValue1(int x) {
+        this.x = x;
+    }
+
+    public TestValue1 hash(MyInterface arg) {
+        return new TestValue1(x + ((TestValue1)arg).x);
+    }
+}
+
+value class TestValue2 implements MyInterface {
+    int x;
+
+    public TestValue2(int x) {
+        this.x = x;
+    }
+
+    public TestValue2 hash(MyInterface arg) {
+        return new TestValue2(x + ((TestValue2)arg).x);
+    }
+}
+
+class TestClass implements MyInterface {
+    int x;
+
+    public TestClass(int x) {
+        this.x = x;
+    }
+
+    public MyInterface hash(MyInterface arg) {
+        return new TestClass(x + ((TestClass)arg).x);
+    }
+}
+
+public class TestBimorphicInlining {
+
+    public static MyInterface test1(MyInterface i1, MyInterface i2) {
+        MyInterface result = i1.hash(i2);
+        i1.hash(i2);
+        return result;
+    }
+
+    public static MyInterface test2(MyInterface i1, MyInterface i2) {
+        MyInterface result = i1.hash(i2);
+        i1.hash(i2);
+        return result;
+    }
+
+    public static MyInterface test3(MyInterface i1, MyInterface i2) {
+        MyInterface result = i1.hash(i2);
+        i1.hash(i2);
+        return result;
+    }
+
+    public static MyInterface test4(MyInterface i1, MyInterface i2) {
+        MyInterface result = i1.hash(i2);
+        i1.hash(i2);
+        return result;
+    }
+
+    static public void main(String[] args) {
+        Random rand = new Random();
+        TestClass  testObject = new TestClass(rand.nextInt());
+        TestValue1 testValue1 = new TestValue1(rand.nextInt());
+        TestValue2 testValue2 = new TestValue2(rand.nextInt());
+
+        for (int i = 0; i < 10_000; ++i) {
+            // Trigger bimorphic inlining by calling test methods with different arguments
+            MyInterface arg, res;
+            boolean rare = (i % 10 == 0);
+
+            arg = rare ? testValue1 : testObject;
+            res = test1(arg, arg);
+            Asserts.assertEQ(rare ? ((TestValue1)res).x : ((TestClass)res).x, 2 * (rare ? testValue1.x : testObject.x), "test1 failed");
+
+            arg = rare ? testObject : testValue1;
+            res = test2(arg, arg);
+            Asserts.assertEQ(rare ? ((TestClass)res).x : ((TestValue1)res).x, 2 * (rare ? testObject.x : testValue1.x), "test2 failed");
+
+            arg = rare ? testValue1 : testValue2;
+            res = test3(arg, arg);
+            Asserts.assertEQ(rare ? ((TestValue1)res).x : ((TestValue2)res).x, 2 * (rare ? testValue1.x : testValue2.x), "test3 failed");
+
+            arg = rare ? testValue2 : testValue1;
+            res = test4(arg, arg);
+            Asserts.assertEQ(rare ? ((TestValue2)res).x : ((TestValue1)res).x, 2 * (rare ? testValue2.x : testValue1.x), "test4 failed");
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBufferTearing.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBufferTearing.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBufferTearing.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2020, 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 compiler.valhalla.inlinetypes;
+
+import java.lang.invoke.*;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import jdk.test.lib.Asserts;
+import jdk.internal.misc.Unsafe;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+/**
+ * @test TestBufferTearing
+ * @key randomness
+ * @summary Detect tearing on value class buffer writes due to missing barriers.
+ * @library /testlibrary /test/lib /compiler/whitebox /
+ * @enablePreview
+ * @modules java.base/jdk.internal.misc
+ *          java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm -XX:InlineFieldMaxFlatSize=0 -XX:FlatArrayElementMaxSize=0
+ *                   -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM -XX:+StressLCM
+ *                   compiler.valhalla.inlinetypes.TestBufferTearing
+ * @run main/othervm -XX:InlineFieldMaxFlatSize=0 -XX:FlatArrayElementMaxSize=0
+ *                   -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM -XX:+StressLCM
+ *                   -XX:+IgnoreUnrecognizedVMOptions -XX:+AlwaysIncrementalInline
+ *                   compiler.valhalla.inlinetypes.TestBufferTearing
+ * @run main/othervm -XX:InlineFieldMaxFlatSize=0 -XX:FlatArrayElementMaxSize=0
+ *                   -XX:CompileCommand=dontinline,*::incrementAndCheck*
+ *                   -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM -XX:+StressLCM
+ *                   compiler.valhalla.inlinetypes.TestBufferTearing
+ * @run main/othervm -XX:InlineFieldMaxFlatSize=0 -XX:FlatArrayElementMaxSize=0
+ *                   -XX:CompileCommand=dontinline,*::incrementAndCheck*
+ *                   -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM -XX:+StressLCM
+ *                   -XX:+IgnoreUnrecognizedVMOptions -XX:+AlwaysIncrementalInline
+ *                   compiler.valhalla.inlinetypes.TestBufferTearing
+ */
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue {
+    int x;
+    int y;
+
+    private static final Unsafe U = Unsafe.getUnsafe();
+    private static final long X_OFFSET;
+    private static final long Y_OFFSET;
+    static {
+        try {
+            Field xField = MyValue.class.getDeclaredField("x");
+            X_OFFSET = U.objectFieldOffset(xField);
+            Field yField = MyValue.class.getDeclaredField("y");
+            Y_OFFSET = U.objectFieldOffset(yField);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    MyValue(int x, int y) {
+        this.x = x;
+        this.y = y;
+    }
+
+    MyValue incrementAndCheck() {
+        Asserts.assertEQ(x, y, "Inconsistent field values");
+        return new MyValue(x + 1, y + 1);
+    }
+
+    MyValue incrementAndCheckUnsafe() {
+        Asserts.assertEQ(x, y, "Inconsistent field values");
+        MyValue vt = U.makePrivateBuffer(this);
+        U.putInt(vt, X_OFFSET, x + 1);
+        U.putInt(vt, Y_OFFSET, y + 1);
+        return U.finishPrivateBuffer(vt);
+    }
+}
+
+public class TestBufferTearing {
+    @NullRestricted
+    static MyValue vtField1;
+    @NullRestricted
+    MyValue vtField2;
+    MyValue[] vtField3 = (MyValue[])ValueClass.newNullRestrictedArray(MyValue.class, 1);
+
+    static final MethodHandle incrementAndCheck_mh;
+
+    static {
+        try {
+            Class<?> clazz = MyValue.class;
+            MethodHandles.Lookup lookup = MethodHandles.lookup();
+
+            MethodType mt = MethodType.methodType(MyValue.class);
+            incrementAndCheck_mh = lookup.findVirtual(clazz, "incrementAndCheck", mt);
+        } catch (NoSuchMethodException | IllegalAccessException e) {
+            e.printStackTrace();
+            throw new RuntimeException("Method handle lookup failed");
+        }
+    }
+
+    static class Runner extends Thread {
+        TestBufferTearing test;
+
+        public Runner(TestBufferTearing test) {
+            this.test = test;
+        }
+
+        public void run() {
+            for (int i = 0; i < 1_000_000; ++i) {
+                test.vtField1 = test.vtField1.incrementAndCheck();
+                test.vtField2 = test.vtField2.incrementAndCheck();
+                test.vtField3[0] = test.vtField3[0].incrementAndCheck();
+
+                test.vtField1 = test.vtField1.incrementAndCheckUnsafe();
+                test.vtField2 = test.vtField2.incrementAndCheckUnsafe();
+                test.vtField3[0] = test.vtField3[0].incrementAndCheckUnsafe();
+
+                try {
+                    test.vtField1 = (MyValue)incrementAndCheck_mh.invokeExact(test.vtField1);
+                    test.vtField2 = (MyValue)incrementAndCheck_mh.invokeExact(test.vtField2);
+                    test.vtField3[0] = (MyValue)incrementAndCheck_mh.invokeExact(test.vtField3[0]);
+                } catch (Throwable t) {
+                    throw new RuntimeException("Invoke failed", t);
+                }
+            }
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        // Create threads that concurrently update some value class (array) fields
+        // and check the fields of the value classes for consistency to detect tearing.
+        TestBufferTearing test = new TestBufferTearing();
+        Thread runner = null;
+        for (int i = 0; i < 10; ++i) {
+            runner = new Runner(test);
+            runner.start();
+        }
+        runner.join();
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBufferTearingC1.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBufferTearingC1.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBufferTearingC1.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, Arm Limited. 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 compiler.valhalla.inlinetypes;
+
+/**
+ * @test TestBufferTearingC1
+ * @key randomness
+ * @summary Tests for C1 missing barriers when buffering value classes.
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm -XX:InlineFieldMaxFlatSize=-1 -XX:FlatArrayElementMaxSize=-1
+ *                   compiler.valhalla.inlinetypes.TestBufferTearingC1
+ * @run main/othervm -XX:InlineFieldMaxFlatSize=-1 -XX:FlatArrayElementMaxSize=-1
+ *                   -XX:TieredStopAtLevel=1
+ *                   compiler.valhalla.inlinetypes.TestBufferTearingC1
+ */
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class Point {
+    public int x, y;
+
+    public Point(int x, int y) {
+        this.x = x;
+        this.y = y;
+    }
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class Rect {
+    public Point a, b;
+
+    public Rect(Point a, Point b) {
+        this.a = a;
+        this.b = b;
+    }
+}
+
+public class TestBufferTearingC1 {
+
+    public static Point[] points = (Point[])ValueClass.newNullRestrictedArray(Point.class, 1);
+    public static Rect rect = new Rect(new Point(1, 1), new Point(2, 2));
+    public static Rect[] rects = (Rect[])ValueClass.newNullRestrictedArray(Rect.class, 1);
+
+    static {
+        points[0] = new Point(1, 1);
+        rects[0] = rect;
+    }
+
+    public static Object ref1 = points[0];
+    public static Object ref2 = rect.a;
+    public static Object ref3 = rects[0].a;
+
+    static volatile boolean running = true;
+
+    public static void writeRefs(int iter) {
+        ref1 = points[0];    // Indexed load of flattened array
+        ref2 = rect.a;       // Load from flattened field
+        ref3 = rects[0].a;   // Indexed load (delayed) followed by flattened field access
+
+        points[0] = new Point(iter, iter);
+        rect = new Rect(new Point(iter, iter), new Point(iter + 1, iter + 1));
+        rects[0] = rect;
+    }
+
+    private static void checkMissingBarrier() {
+        while (running) {
+            // Each refN holds a "buffered" reference created when reading a
+            // flattened field or array element.  It should not be possible to
+            // read through this reference and see the intermediate
+            // zero-initialised state of the object (i.e. there should be a
+            // store-store barrier after copying the flattened field contents
+            // before the store that publishes it).
+
+            if (((Point)ref1).x == 0 || ((Point)ref1).y == 0) {
+                throw new IllegalStateException();
+            }
+
+            if (((Point)ref2).x == 0 || ((Point)ref2).y == 0) {
+                throw new IllegalStateException();
+            }
+
+            if (((Point)ref3).x == 0 || ((Point)ref3).y == 0) {
+                throw new IllegalStateException();
+            }
+        }
+    }
+
+    public static void main(String[] args) throws InterruptedException {
+        Thread[] threads = new Thread[10];
+        for (int i = 0; i < 10; i++) {
+            threads[i] = new Thread(TestBufferTearingC1::checkMissingBarrier);
+            threads[i].start();
+        }
+
+        for (int i = 1; i < 1_000_000; i++) {
+            writeRefs(i);
+        }
+
+        running = false;
+
+        for (int i = 0; i < 10; i++) {
+            threads[i].join();
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestC1.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestC1.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestC1.java
@@ -0,0 +1,380 @@
+/*
+ * 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.
+ *
+ * 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.CompLevel;
+import compiler.lib.ir_framework.Run;
+import compiler.lib.ir_framework.Scenario;
+import compiler.lib.ir_framework.Test;
+import jdk.test.lib.Asserts;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import static compiler.valhalla.inlinetypes.InlineTypes.rI;
+import static compiler.valhalla.inlinetypes.InlineTypes.rL;
+
+/*
+ * @test
+ * @key randomness
+ * @summary Various tests that are specific to C1.
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm/timeout=300 compiler.valhalla.inlinetypes.TestC1
+ */
+
+public class TestC1 {
+    public static void main(String[] args) {
+        final Scenario[] scenarios = {
+                // C1 only
+                new Scenario(0, "-XX:TieredStopAtLevel=1", "-XX:+TieredCompilation"),
+                // C2 only. (Make sure the tests are correctly written)
+                new Scenario(1, "-XX:TieredStopAtLevel=4", "-XX:-TieredCompilation"),
+                // interpreter only
+                new Scenario(2, "-Xint"),
+                // Xcomp Only C1.
+                new Scenario(3, "-XX:TieredStopAtLevel=1", "-XX:+TieredCompilation", "-Xcomp"),
+                // Xcomp Only C2.
+                new Scenario(4, "-XX:TieredStopAtLevel=4", "-XX:-TieredCompilation", "-Xcomp")
+        };
+
+        InlineTypes.getFramework()
+                   .addScenarios(scenarios)
+                   .addFlags("--enable-preview",
+                             "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED",
+                             "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED")
+                   .addHelperClasses(MyValue1.class,
+                                     MyValue2.class,
+                                     MyValue2Inline.class,
+                                     MyValue3.class,
+                                     MyValue3Inline.class)
+                   .start();
+    }
+
+    // JDK-8229799
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public long test1(Object a, Object b, long n) {
+        long r;
+        n += (a == b) ? 0x5678123456781234L : 0x1234567812345678L;
+        n -= 1;
+        return n;
+    }
+
+    @Run(test = "test1")
+    public void test1_verifier() {
+        MyValue1 v1 = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1 v2 = MyValue1.createWithFieldsInline(rI, rL+1);
+        long r1 = test1(v1, v1, 1);
+        long r2 = test1(v1, v2, 1);
+        Asserts.assertEQ(r1, 0x5678123456781234L);
+        Asserts.assertEQ(r2, 0x1234567812345678L);
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class SimpleValue2 {
+        int value;
+
+        SimpleValue2(int value) {
+            this.value = value;
+        }
+    }
+
+    // JDK-8231961
+    // Test that the value numbering optimization does not remove
+    // the second load from the buffered array element.
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public int test2(SimpleValue2[] array) {
+        return array[0].value + array[0].value;
+    }
+
+    @Run(test = "test2")
+    public void test2_verifier() {
+        SimpleValue2[] array = (SimpleValue2[])ValueClass.newNullRestrictedArray(SimpleValue2.class, 1);
+        array[0] = new SimpleValue2(rI);
+        int result = test2(array);
+        Asserts.assertEQ(result, 2*rI);
+    }
+
+
+    // Tests below (3 to 8) check the behavior of the C1 optimization to access
+    // sub-elements of a flattened array without copying the element first
+
+    // Test access to a null array
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public int test3(MyValue2[] array, int index) {
+        return array[index].x;
+    }
+
+    @Run(test = "test3")
+    public void test3_verifier() {
+        NullPointerException npe = null;
+        try {
+            test3(null, 0);
+        } catch(NullPointerException e) {
+            npe = e;
+        }
+        Asserts.assertNE(npe, null);
+    }
+
+    // Test out of bound accesses
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public int test4(MyValue2[] array, int index) {
+        return array[index].x;
+    }
+
+    @Run(test = "test4")
+    public void test4_verifier() {
+        MyValue2[] array = new MyValue2[2];
+        ArrayIndexOutOfBoundsException aioob = null;
+        try {
+            test3(array, -1);
+        } catch(ArrayIndexOutOfBoundsException e) {
+            aioob = e;
+        }
+        Asserts.assertNE(aioob, null);
+        aioob = null;
+        try {
+            test3(array, 2);
+        } catch(ArrayIndexOutOfBoundsException e) {
+            aioob = e;
+        }
+        Asserts.assertNE(aioob, null);
+    }
+
+    // Test 1st level sub-element access to primitive field
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public int test5(MyValue2[] array, int index) {
+        return array[index].x;
+    }
+
+    @Run(test = "test5")
+    public void test5_verifier() {
+        MyValue2[] array = new MyValue2[2];
+        MyValue2 v = new MyValue2(1,(byte)2, new MyValue2Inline(5.0d, 345L));
+        array[1] = v;
+        int x = test5(array, 1);
+        Asserts.assertEQ(x, 1);
+    }
+
+    // Test 1st level sub-element access to flattened field
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public MyValue2Inline test6(MyValue2[] array, int index) {
+        return array[index].v;
+    }
+
+    @Run(test = "test6")
+    public void test6_verifier() {
+        MyValue2[] array = new MyValue2[2];
+        MyValue2Inline vi = new MyValue2Inline(3.5d, 678L);
+        MyValue2 v = new MyValue2(1,(byte)2, vi);
+        array[0] = v;
+        MyValue2Inline vi2 = test6(array, 0);
+        Asserts.assertEQ(vi, vi2);
+    }
+
+    // Test 1st level sub-element access to non-flattened field
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Big {
+        long l0,l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11,l12,l13,l14,l15,l16,l17,l18,l19 ;
+
+        Big(long n) {
+            l0 = n++; l1 = n++; l2 = n++; l3 = n++; l4 = n++; l5 = n++; l6 = n++; l7 = n++; l8 = n++;
+            l9 = n++; l10 = n++; l11 = n++; l12 = n++; l13 = n++; l14 = n++; l15 = n++; l16= n++;
+            l17 = n++; l18 = n++; l19 = n++;
+        }
+
+        void check(long n, int i) {
+            Asserts.assertEQ(l0, n); n += i;
+            Asserts.assertEQ(l1, n); n += i;
+            Asserts.assertEQ(l2, n); n += i;
+            Asserts.assertEQ(l3, n); n += i;
+            Asserts.assertEQ(l4, n); n += i;
+            Asserts.assertEQ(l5, n); n += i;
+            Asserts.assertEQ(l6, n); n += i;
+            Asserts.assertEQ(l7, n); n += i;
+            Asserts.assertEQ(l8, n); n += i;
+            Asserts.assertEQ(l9, n); n += i;
+            Asserts.assertEQ(l10, n); n += i;
+            Asserts.assertEQ(l11, n); n += i;
+            Asserts.assertEQ(l12, n); n += i;
+            Asserts.assertEQ(l13, n); n += i;
+            Asserts.assertEQ(l14, n); n += i;
+            Asserts.assertEQ(l15, n); n += i;
+            Asserts.assertEQ(l16, n); n += i;
+            Asserts.assertEQ(l17, n); n += i;
+            Asserts.assertEQ(l18, n); n += i;
+            Asserts.assertEQ(l19, n);
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class TestValue {
+        int i;
+        @NullRestricted
+        Big big;
+
+        TestValue(int n) {
+            i = n;
+            big = new Big(n);
+        }
+    }
+
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public Big test7(TestValue[] array, int index) {
+        return array[index].big;
+    }
+
+    @Run(test = "test7")
+    public void test7_verifier() {
+        TestValue[] array = (TestValue[])ValueClass.newNullRestrictedArray(TestValue.class, 7);
+        Big b0 = test7(array, 3);
+        b0.check(0, 0);
+        TestValue tv = new TestValue(9);
+        array[5] = tv;
+        Big b1 = test7(array, 5);
+        b1.check(9, 1);
+    }
+
+    // Test 2nd level sub-element access to primitive field
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public byte test8(MyValue1[] array, int index) {
+        return array[index].v2.y;
+    }
+
+    @Run(test = "test8")
+    public void test8_verifier() {
+        MyValue1[] array = new MyValue1[23];
+        MyValue2 mv2a = MyValue2.createWithFieldsInline(7, 63L, 8.9d);
+        MyValue2 mv2b = MyValue2.createWithFieldsInline(11, 69L, 17.3d);
+        MyValue1 mv1 = new MyValue1(1, 2L, (short)3, 4, null, mv2a, mv2b, mv2a, mv2b, 'z');
+        array[19] = mv1;
+        byte b = test8(array, 19);
+        Asserts.assertEQ(b, (byte)11);
+    }
+
+
+    // Test optimizations for arrays of empty types
+    // (read/write are not performed, pre-allocated instance is used for reads)
+    // Most tests check that error conditions are still correctly handled
+    // (OOB, null pointer)
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class EmptyType {}
+
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public EmptyType test9() {
+        EmptyType[] array = (EmptyType[])ValueClass.newNullRestrictedArray(EmptyType.class, 10);
+        return array[4];
+    }
+
+    @Run(test = "test9")
+    public void test9_verifier() {
+        EmptyType et = test9();
+        Asserts.assertEQ(et, new EmptyType());
+    }
+
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public EmptyType test10(EmptyType[] array) {
+        return array[0];
+    }
+
+    @Run(test = "test10")
+    public void test10_verifier() {
+        EmptyType[] array = (EmptyType[])ValueClass.newNullRestrictedArray(EmptyType.class, 16);
+        EmptyType et = test10(array);
+        Asserts.assertEQ(et, new EmptyType());
+    }
+
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public EmptyType test11(EmptyType[] array, int index) {
+        return array[index];
+    }
+
+    @Run(test = "test11")
+    public void test11_verifier() {
+        Exception e = null;
+        EmptyType[] array = (EmptyType[])ValueClass.newNullRestrictedArray(EmptyType.class, 10);
+        try {
+            EmptyType et = test11(array, 11);
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            e = ex;
+        }
+        Asserts.assertNotNull(e);
+        e = null;
+        try {
+            EmptyType et = test11(array, -1);
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            e = ex;
+        }
+        Asserts.assertNotNull(e);
+        e = null;
+        try {
+            EmptyType et = test11(null, 1);
+        } catch (NullPointerException ex) {
+            e = ex;
+        }
+        Asserts.assertNotNull(e);
+    }
+
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public void test12(EmptyType[] array, int index, EmptyType value) {
+        array[index] = value;
+    }
+
+    @Run(test = "test12")
+    public void test12_verifier() {
+        EmptyType empty = new EmptyType();
+        EmptyType[] array = (EmptyType[])ValueClass.newNullRestrictedArray(EmptyType.class, 16);
+        test12(array, 2, empty);
+        Exception e = null;
+        try {
+            test12(null, 2, empty);
+        } catch(NullPointerException ex) {
+            e = ex;
+        }
+        Asserts.assertNotNull(e);
+        e = null;
+        try {
+            test12(array, 17, empty);
+        } catch(ArrayIndexOutOfBoundsException ex) {
+            e = ex;
+        }
+        Asserts.assertNotNull(e);
+        e = null;
+        try {
+            test12(array, -8, empty);
+        } catch(ArrayIndexOutOfBoundsException ex) {
+            e = ex;
+        }
+        Asserts.assertNotNull(e);
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestC1ValueNumbering.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestC1ValueNumbering.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestC1ValueNumbering.java
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ *
+ * 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 compiler.valhalla.inlinetypes;
+
+import jdk.test.lib.Asserts;
+
+/*
+ * @test
+ * @summary Test value numbering behaves correctly with flat fields.
+ * @library /testlibrary /test/lib
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm -Xcomp -XX:TieredStopAtLevel=1 -ea
+ *                   -XX:CompileCommand=compileonly,compiler.valhalla.inlinetypes.TestC1ValueNumbering::*
+ *                   compiler.valhalla.inlinetypes.TestC1ValueNumbering
+ */
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+public class TestC1ValueNumbering {
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Point {
+        int x;
+        int y;
+
+        public Point() {
+            x = 0;
+            y = 0;
+        }
+
+        public Point(int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+    }
+
+    @NullRestricted
+    Point p;
+
+    // Notes on test 1:
+    // 1 - asserts are important create several basic blocks (asserts create branches)
+    // 2 - local variables x, y must be read in the same block as the putfield
+    static void test1() {
+        Point p = new Point(4,5);
+        TestC1ValueNumbering test = new TestC1ValueNumbering();
+        assert test.p.x == 0;
+        assert test.p.y == 0;
+        test.p = p;
+        int x = test.p.x;
+        int y = test.p.y;
+        Asserts.assertEQ(x, 4, "Bad field value");
+        Asserts.assertEQ(y, 5, "Bad field value");
+    }
+
+    public static void main(String[] args) {
+        for (int i = 0; i < 10; i++) {
+            test1();
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestC2CCalls.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestC2CCalls.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestC2CCalls.java
@@ -0,0 +1,631 @@
+/*
+ * 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.
+ *
+ * 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
+ * @key randomness
+ * @summary Test value class calling convention with compiled to compiled calls.
+ * @library /test/lib /compiler/whitebox /
+ * @enablePreview
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   TestC2CCalls
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:-UseBimorphicInlining -Xbatch
+ *                   -XX:CompileCommand=compileonly,TestC2CCalls*::test*
+ *                   -XX:CompileCommand=dontinline,TestC2CCalls*::test*
+ *                   TestC2CCalls
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:-UseBimorphicInlining -Xbatch -XX:-ProfileInterpreter
+ *                   -XX:CompileCommand=compileonly,TestC2CCalls*::test*
+ *                   -XX:CompileCommand=dontinline,TestC2CCalls*::test*
+ *                   TestC2CCalls
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:-UseBimorphicInlining -Xbatch
+ *                   -XX:CompileCommand=compileonly,TestC2CCalls::test*
+ *                   -XX:CompileCommand=dontinline,TestC2CCalls*::test*
+ *                   TestC2CCalls
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:-UseBimorphicInlining -Xbatch -XX:-ProfileInterpreter
+ *                   -XX:CompileCommand=compileonly,TestC2CCalls::test*
+ *                   -XX:CompileCommand=dontinline,TestC2CCalls*::test*
+ *                   TestC2CCalls
+ */
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.Utils;
+
+import jdk.test.whitebox.WhiteBox;
+
+public class TestC2CCalls {
+    public static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+    public static final int COMP_LEVEL_FULL_OPTIMIZATION = 4; // C2 or JVMCI
+    public static final int rI = Utils.getRandomInstance().nextInt() % 1000;
+
+    static value class OtherVal {
+        public int x;
+
+        private OtherVal(int x) {
+            this.x = x;
+        }
+    }
+
+    static interface MyInterface1 {
+        public MyInterface1 test1(OtherVal other, int y);
+        public MyInterface1 test2(OtherVal other1, OtherVal other2, int y);
+        public MyInterface1 test3(OtherVal other1, OtherVal other2, int y, boolean deopt);
+        public MyInterface1 test4(OtherVal other1, OtherVal other2, int y);
+        public MyInterface1 test5(OtherVal other1, OtherVal other2, int y);
+        public MyInterface1 test6();
+        public MyInterface1 test7(int i1, int i2, int i3, int i4, int i5, int i6);
+        public MyInterface1 test8(int i1, int i2, int i3, int i4, int i5, int i6, int i7);
+        public MyInterface1 test9(MyValue3 other, int i1, int i2, int i3, int i4, int i5, int i6);
+        public MyInterface1 test10(MyValue4 other, int i1, int i2, int i3, int i4, int i5, int i6);
+
+        public int getValue();
+    }
+
+    static value class MyValue1 implements MyInterface1 {
+        public int x;
+
+        private MyValue1(int x) {
+            this.x = x;
+        }
+
+        @Override
+        public int getValue() {
+            return x;
+        }
+
+        @Override
+        public MyValue1 test1(OtherVal other, int y) {
+            return new MyValue1(x + other.x + y);
+        }
+
+        @Override
+        public MyValue1 test2(OtherVal other1, OtherVal other2, int y) {
+            return new MyValue1(x + other1.x + other2.x + y);
+        }
+
+        @Override
+        public MyValue1 test3(OtherVal other1, OtherVal other2, int y, boolean deopt) {
+            if (!deopt) {
+              return new MyValue1(x + other1.x + other2.x + y);
+            } else {
+              // Uncommon trap
+              return test1(other1, y);
+            }
+        }
+
+        @Override
+        public MyValue1 test4(OtherVal other1, OtherVal other2, int y) {
+            return new MyValue1(x + other1.x + other2.x + y);
+        }
+
+        @Override
+        public MyValue1 test5(OtherVal other1, OtherVal other2, int y) {
+            return new MyValue1(x + other1.x + other2.x + y);
+        }
+
+        @Override
+        public MyValue1 test6() {
+            return this;
+        }
+
+        @Override
+        public MyValue1 test7(int i1, int i2, int i3, int i4, int i5, int i6) {
+            return new MyValue1(x + i1 + i2 + i3 + i4 + i5 + i6);
+        }
+
+        @Override
+        public MyValue1 test8(int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
+            return new MyValue1(x + i1 + i2 + i3 + i4 + i5 + i6 + i7);
+        }
+
+        public MyValue1 test9(MyValue3 other, int i1, int i2, int i3, int i4, int i5, int i6) {
+            return new MyValue1(x + (int)(other.d1 + other.d2 + other.d3 + other.d4) + i1 + i2 + i3 + i4 + i5 + i6);
+        }
+
+        public MyValue1 test10(MyValue4 other, int i1, int i2, int i3, int i4, int i5, int i6) {
+            return new MyValue1(x + other.x1 + other.x2 + other.x3 + other.x4 + i1 + i2 + i3 + i4 + i5 + i6);
+        }
+    }
+
+    static value class MyValue2 implements MyInterface1 {
+        public int x;
+
+        private MyValue2(int x) {
+            this.x = x;
+        }
+
+        @Override
+        public int getValue() {
+            return x;
+        }
+
+        @Override
+        public MyValue2 test1(OtherVal other, int y) {
+            return new MyValue2(x + other.x + y);
+        }
+
+        @Override
+        public MyValue2 test2(OtherVal other1, OtherVal other2, int y) {
+            return new MyValue2(x + other1.x + other2.x + y);
+        }
+
+        @Override
+        public MyValue2 test3(OtherVal other1, OtherVal other2, int y, boolean deopt) {
+            if (!deopt) {
+              return new MyValue2(x + other1.x + other2.x + y);
+            } else {
+              // Uncommon trap
+              return test1(other1, y);
+            }
+        }
+
+        @Override
+        public MyValue2 test4(OtherVal other1, OtherVal other2, int y) {
+            return new MyValue2(x + other1.x + other2.x + y);
+        }
+
+        @Override
+        public MyValue2 test5(OtherVal other1, OtherVal other2, int y) {
+            return new MyValue2(x + other1.x + other2.x + y);
+        }
+
+        @Override
+        public MyValue2 test6() {
+            return this;
+        }
+
+        @Override
+        public MyValue2 test7(int i1, int i2, int i3, int i4, int i5, int i6) {
+            return new MyValue2(x + i1 + i2 + i3 + i4 + i5 + i6);
+        }
+
+        @Override
+        public MyValue2 test8(int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
+            return new MyValue2(x + i1 + i2 + i3 + i4 + i5 + i6 + i7);
+        }
+
+        public MyValue2 test9(MyValue3 other, int i1, int i2, int i3, int i4, int i5, int i6) {
+            return new MyValue2(x + (int)(other.d1 + other.d2 + other.d3 + other.d4) + i1 + i2 + i3 + i4 + i5 + i6);
+        }
+
+        public MyValue2 test10(MyValue4 other, int i1, int i2, int i3, int i4, int i5, int i6) {
+            return new MyValue2(x + other.x1 + other.x2 + other.x3 + other.x4 + i1 + i2 + i3 + i4 + i5 + i6);
+        }
+    }
+
+    static value class MyValue3 implements MyInterface1 {
+        public double d1;
+        public double d2;
+        public double d3;
+        public double d4;
+
+        private MyValue3(double d) {
+            this.d1 = d;
+            this.d2 = d;
+            this.d3 = d;
+            this.d4 = d;
+        }
+
+        @Override
+        public int getValue() {
+            return (int)d4;
+        }
+
+        @Override
+        public MyValue3 test1(OtherVal other, int y) { return new MyValue3(0); }
+        @Override
+        public MyValue3 test2(OtherVal other1, OtherVal other2, int y)  { return new MyValue3(0); }
+        @Override
+        public MyValue3 test3(OtherVal other1, OtherVal other2, int y, boolean deopt)  { return new MyValue3(0); }
+        @Override
+        public MyValue3 test4(OtherVal other1, OtherVal other2, int y)  { return new MyValue3(0); }
+        @Override
+        public MyValue3 test5(OtherVal other1, OtherVal other2, int y)  { return new MyValue3(0); }
+        @Override
+        public MyValue3 test6()  { return new MyValue3(0); }
+
+        @Override
+        public MyValue3 test7(int i1, int i2, int i3, int i4, int i5, int i6)  {
+            return new MyValue3(d1 + d2 + d3 + d4 + i1 + i2 + i3 + i4 + i5 + i6);
+        }
+
+        @Override
+        public MyValue3 test8(int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
+            return new MyValue3(d1 + d2 + d3 + d4 + i1 + i2 + i3 + i4 + i5 + i6 + i7);
+        }
+
+        public MyValue3 test9(MyValue3 other, int i1, int i2, int i3, int i4, int i5, int i6) {
+            return new MyValue3(d1 + d2 + d3 + d4 + other.d1 + other.d2 + other.d3 + other.d4 + i1 + i2 + i3 + i4 + i5 + i6);
+        }
+
+        public MyValue3 test10(MyValue4 other, int i1, int i2, int i3, int i4, int i5, int i6) {
+            return new MyValue3(d1 + d2 + d3 + d4 + other.x1 + other.x2 + other.x3 + other.x4 + i1 + i2 + i3 + i4 + i5 + i6);
+        }
+    }
+
+    static value class MyValue4 implements MyInterface1 {
+        public int x1;
+        public int x2;
+        public int x3;
+        public int x4;
+
+        private MyValue4(int i) {
+            this.x1 = i;
+            this.x2 = i;
+            this.x3 = i;
+            this.x4 = i;
+        }
+
+        @Override
+        public int getValue() {
+            return x4;
+        }
+
+        @Override
+        public MyValue4 test1(OtherVal other, int y) { return new MyValue4(0); }
+        @Override
+        public MyValue4 test2(OtherVal other1, OtherVal other2, int y)  { return new MyValue4(0); }
+        @Override
+        public MyValue4 test3(OtherVal other1, OtherVal other2, int y, boolean deopt)  { return new MyValue4(0); }
+        @Override
+        public MyValue4 test4(OtherVal other1, OtherVal other2, int y)  { return new MyValue4(0); }
+        @Override
+        public MyValue4 test5(OtherVal other1, OtherVal other2, int y)  { return new MyValue4(0); }
+        @Override
+        public MyValue4 test6()  { return new MyValue4(0); }
+
+        @Override
+        public MyValue4 test7(int i1, int i2, int i3, int i4, int i5, int i6)  {
+            return new MyValue4(x1 + x2 + x3 + x4 + i1 + i2 + i3 + i4 + i5 + i6);
+        }
+
+        @Override
+        public MyValue4 test8(int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
+            return new MyValue4(x1 + x2 + x3 + x4 + i1 + i2 + i3 + i4 + i5 + i6 + i7);
+        }
+
+        public MyValue4 test9(MyValue3 other, int i1, int i2, int i3, int i4, int i5, int i6) {
+            return new MyValue4(x1 + x2 + x3 + x4 + (int)(other.d1 + other.d2 + other.d3 + other.d4) + i1 + i2 + i3 + i4 + i5 + i6);
+        }
+
+        public MyValue4 test10(MyValue4 other, int i1, int i2, int i3, int i4, int i5, int i6) {
+            return new MyValue4(x1 + x2 + x3 + x4 + other.x1 + other.x2 + other.x3 + other.x4 + i1 + i2 + i3 + i4 + i5 + i6);
+        }
+    }
+
+    static class MyObject implements MyInterface1 {
+        private final int x;
+
+        private MyObject(int x) {
+            this.x = x;
+        }
+
+        @Override
+        public int getValue() {
+            return x;
+        }
+
+        @Override
+        public MyObject test1(OtherVal other, int y) {
+            return new MyObject(x + other.x + y);
+        }
+
+        @Override
+        public MyObject test2(OtherVal other1, OtherVal other2, int y) {
+            return new MyObject(x + other1.x + other2.x + y);
+        }
+
+        @Override
+        public MyObject test3(OtherVal other1, OtherVal other2, int y, boolean deopt) {
+            if (!deopt) {
+              return new MyObject(x + other1.x + other2.x + y);
+            } else {
+              // Uncommon trap
+              return test1(other1, y);
+            }
+        }
+
+        @Override
+        public MyObject test4(OtherVal other1, OtherVal other2, int y) {
+            return new MyObject(x + other1.x + other2.x + y);
+        }
+
+        @Override
+        public MyObject test5(OtherVal other1, OtherVal other2, int y) {
+            return new MyObject(x + other1.x + other2.x + y);
+        }
+
+        @Override
+        public MyObject test6() {
+            return this;
+        }
+
+        @Override
+        public MyObject test7(int i1, int i2, int i3, int i4, int i5, int i6) {
+            return new MyObject(x + i1 + i2 + i3 + i4 + i5 + i6);
+        }
+
+        @Override
+        public MyObject test8(int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
+            return new MyObject(x + i1 + i2 + i3 + i4 + i5 + i6 + i7);
+        }
+
+        public MyObject test9(MyValue3 other, int i1, int i2, int i3, int i4, int i5, int i6) {
+            return new MyObject(x + (int)(other.d1 + other.d2 + other.d3 + other.d4) + i1 + i2 + i3 + i4 + i5 + i6);
+        }
+
+        public MyObject test10(MyValue4 other, int i1, int i2, int i3, int i4, int i5, int i6) {
+            return new MyObject(x + other.x1 + other.x2 + other.x3 + other.x4 + i1 + i2 + i3 + i4 + i5 + i6);
+        }
+    }
+
+    // Test calling methods with value class arguments through an interface
+    public static int test1(MyInterface1 intf, OtherVal other, int y) {
+        return intf.test1(other, y).getValue();
+    }
+
+    public static int test2(MyInterface1 intf, OtherVal other, int y) {
+        return intf.test2(other, other, y).getValue();
+    }
+
+    // Test mixing null-tolerant and null-free value class arguments
+    public static int test3(MyValue1 vt, OtherVal other, int y) {
+        return vt.test2(other, other, y).getValue();
+    }
+
+    public static int test4(MyObject obj, OtherVal other, int y) {
+        return obj.test2(other, other, y).getValue();
+    }
+
+    // Optimized interface call with value class receiver
+    public static int test5(MyInterface1 intf, OtherVal other, int y) {
+        return intf.test1(other, y).getValue();
+    }
+
+    public static int test6(MyInterface1 intf, OtherVal other, int y) {
+        return intf.test2(other, other, y).getValue();
+    }
+
+    // Optimized interface call with object receiver
+    public static int test7(MyInterface1 intf, OtherVal other, int y) {
+        return intf.test1(other, y).getValue();
+    }
+
+    public static int test8(MyInterface1 intf, OtherVal other, int y) {
+        return intf.test2(other, other, y).getValue();
+    }
+
+    // Interface calls with deoptimized callee
+    public static int test9(MyInterface1 intf, OtherVal other, int y, boolean deopt) {
+        return intf.test3(other, other, y, deopt).getValue();
+    }
+
+    public static int test10(MyInterface1 intf, OtherVal other, int y, boolean deopt) {
+        return intf.test3(other, other, y, deopt).getValue();
+    }
+
+    // Optimized interface calls with deoptimized callee
+    public static int test11(MyInterface1 intf, OtherVal other, int y, boolean deopt) {
+        return intf.test3(other, other, y, deopt).getValue();
+    }
+
+    public static int test12(MyInterface1 intf, OtherVal other, int y, boolean deopt) {
+        return intf.test3(other, other, y, deopt).getValue();
+    }
+
+    public static int test13(MyInterface1 intf, OtherVal other, int y, boolean deopt) {
+        return intf.test3(other, other, y, deopt).getValue();
+    }
+
+    public static int test14(MyInterface1 intf, OtherVal other, int y, boolean deopt) {
+        return intf.test3(other, other, y, deopt).getValue();
+    }
+
+    // Interface calls without warmed up / compiled callees
+    public static int test15(MyInterface1 intf, OtherVal other, int y) {
+        return intf.test4(other, other, y).getValue();
+    }
+
+    public static int test16(MyInterface1 intf, OtherVal other, int y) {
+        return intf.test5(other, other, y).getValue();
+    }
+
+    // Interface call with no arguments
+    public static int test17(MyInterface1 intf) {
+        return intf.test6().getValue();
+    }
+
+    // Calls that require stack extension
+    public static int test18(MyInterface1 intf, int y) {
+        return intf.test7(y, y, y, y, y, y).getValue();
+    }
+
+    public static int test19(MyInterface1 intf, int y) {
+        return intf.test8(y, y, y, y, y, y, y).getValue();
+    }
+
+    public static int test20(MyInterface1 intf, MyValue3 v, int y) {
+        return intf.test9(v, y, y, y, y, y, y).getValue();
+    }
+
+    public static int test21(MyInterface1 intf, MyValue4 v, int y) {
+        return intf.test10(v, y, y, y, y, y, y).getValue();
+    }
+
+    public static void main(String[] args) {
+        // Sometimes, exclude some methods from compilation with C2 to stress test the calling convention
+        if (Utils.getRandomInstance().nextBoolean()) {
+            ArrayList<Method> methods = new ArrayList<Method>();
+            Collections.addAll(methods, MyValue1.class.getDeclaredMethods());
+            Collections.addAll(methods, MyValue2.class.getDeclaredMethods());
+            Collections.addAll(methods, MyValue3.class.getDeclaredMethods());
+            Collections.addAll(methods, MyValue4.class.getDeclaredMethods());
+            Collections.addAll(methods, MyObject.class.getDeclaredMethods());
+            Collections.addAll(methods, TestC2CCalls.class.getDeclaredMethods());
+            System.out.println("Excluding methods from C2 compilation:");
+            for (Method m : methods) {
+                if (Utils.getRandomInstance().nextBoolean()) {
+                    System.out.println(m);
+                    WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_FULL_OPTIMIZATION, false);
+                }
+            }
+        }
+
+        MyValue1 val1 = new MyValue1(rI);
+        MyValue2 val2 = new MyValue2(rI+1);
+        MyValue3 val3 = new MyValue3(rI+2);
+        MyValue4 val4 = new MyValue4(rI+3);
+        OtherVal other = new OtherVal(rI+4);
+        MyObject obj = new MyObject(rI+5);
+
+        // Make sure callee methods are compiled
+        for (int i = 0; i < 10_000; ++i) {
+            Asserts.assertEQ(val1.test1(other, rI).getValue(), val1.x + other.x + rI);
+            Asserts.assertEQ(val2.test1(other, rI).getValue(), val2.x + other.x + rI);
+            Asserts.assertEQ(obj.test1(other, rI).getValue(), obj.x + other.x + rI);
+            Asserts.assertEQ(val1.test2(other, other, rI).getValue(), val1.x + 2*other.x + rI);
+            Asserts.assertEQ(val2.test2(other, other, rI).getValue(), val2.x + 2*other.x + rI);
+            Asserts.assertEQ(obj.test2(other, other, rI).getValue(), obj.x + 2*other.x + rI);
+            Asserts.assertEQ(val1.test3(other, other, rI, false).getValue(), val1.x + 2*other.x + rI);
+            Asserts.assertEQ(val2.test3(other, other, rI, false).getValue(), val2.x + 2*other.x + rI);
+            Asserts.assertEQ(obj.test3(other, other, rI, false).getValue(), obj.x + 2*other.x + rI);
+            Asserts.assertEQ(val1.test7(rI, rI, rI, rI, rI, rI).getValue(), val1.x + 6*rI);
+            Asserts.assertEQ(val2.test7(rI, rI, rI, rI, rI, rI).getValue(), val2.x + 6*rI);
+            Asserts.assertEQ(val3.test7(rI, rI, rI, rI, rI, rI).getValue(), (int)(4*val3.d1 + 6*rI));
+            Asserts.assertEQ(val4.test7(rI, rI, rI, rI, rI, rI).getValue(), (int)(4*val4.x1 + 6*rI));
+            Asserts.assertEQ(obj.test7(rI, rI, rI, rI, rI, rI).getValue(), obj.x + 6*rI);
+            Asserts.assertEQ(val1.test8(rI, rI, rI, rI, rI, rI, rI).getValue(), val1.x + 7*rI);
+            Asserts.assertEQ(val2.test8(rI, rI, rI, rI, rI, rI, rI).getValue(), val2.x + 7*rI);
+            Asserts.assertEQ(val3.test8(rI, rI, rI, rI, rI, rI, rI).getValue(), (int)(4*val3.d1 + 7*rI));
+            Asserts.assertEQ(val4.test8(rI, rI, rI, rI, rI, rI, rI).getValue(), (int)(4*val4.x1 + 7*rI));
+            Asserts.assertEQ(obj.test8(rI, rI, rI, rI, rI, rI, rI).getValue(), obj.x + 7*rI);
+            Asserts.assertEQ(val1.test9(val3, rI, rI, rI, rI, rI, rI).getValue(), (int)(val1.x + 4*val3.d1 + 6*rI));
+            Asserts.assertEQ(val2.test9(val3, rI, rI, rI, rI, rI, rI).getValue(), (int)(val2.x + 4*val3.d1 + 6*rI));
+            Asserts.assertEQ(val3.test9(val3, rI, rI, rI, rI, rI, rI).getValue(), (int)(4*val3.d1 + 4*val3.d1 + 6*rI));
+            Asserts.assertEQ(val4.test9(val3, rI, rI, rI, rI, rI, rI).getValue(), (int)(4*val4.x1 + 4*val3.d1 + 6*rI));
+            Asserts.assertEQ(obj.test9(val3, rI, rI, rI, rI, rI, rI).getValue(), (int)(obj.x + 4*val3.d1 + 6*rI));
+            Asserts.assertEQ(val1.test10(val4, rI, rI, rI, rI, rI, rI).getValue(), (int)(val1.x + 4*val4.x1 + 6*rI));
+            Asserts.assertEQ(val2.test10(val4, rI, rI, rI, rI, rI, rI).getValue(), (int)(val2.x + 4*val4.x1 + 6*rI));
+            Asserts.assertEQ(val3.test10(val4, rI, rI, rI, rI, rI, rI).getValue(), (int)(4*val3.d1 + 4*val4.x1 + 6*rI));
+            Asserts.assertEQ(val4.test10(val4, rI, rI, rI, rI, rI, rI).getValue(), (int)(4*val4.x1 + 4*val4.x1 + 6*rI));
+            Asserts.assertEQ(obj.test10(val4, rI, rI, rI, rI, rI, rI).getValue(), (int)(obj.x + 4*val4.x1 + 6*rI));
+        }
+
+        // Polute call profile
+        for (int i = 0; i < 100; ++i) {
+            Asserts.assertEQ(test15(val1, other, rI), val1.x + 2*other.x + rI);
+            Asserts.assertEQ(test16(obj, other, rI), obj.x + 2*other.x + rI);
+            Asserts.assertEQ(test17(obj), obj.x);
+        }
+
+        // Trigger compilation of caller methods
+        for (int i = 0; i < 100_000; ++i) {
+            val1 = new MyValue1(rI+i);
+            val2 = new MyValue2(rI+i+1);
+            val3 = new MyValue3(rI+i+2);
+            val4 = new MyValue4(rI+i+3);
+            other = new OtherVal(rI+i+4);
+            obj = new MyObject(rI+i+5);
+
+            Asserts.assertEQ(test1(val1, other, rI), val1.x + other.x + rI);
+            Asserts.assertEQ(test1(obj, other, rI), obj.x + other.x + rI);
+            Asserts.assertEQ(test2(obj, other, rI), obj.x + 2*other.x + rI);
+            Asserts.assertEQ(test2(val1, other, rI), val1.x + 2*other.x + rI);
+            Asserts.assertEQ(test3(val1, other, rI), val1.x + 2*other.x + rI);
+            Asserts.assertEQ(test4(obj, other, rI), obj.x + 2*other.x + rI);
+            Asserts.assertEQ(test5(val1, other, rI), val1.x + other.x + rI);
+            Asserts.assertEQ(test6(val1, other, rI), val1.x + 2*other.x + rI);
+            Asserts.assertEQ(test7(obj, other, rI), obj.x + other.x + rI);
+            Asserts.assertEQ(test8(obj, other, rI), obj.x + 2*other.x + rI);
+            Asserts.assertEQ(test9(val1, other, rI, false), val1.x + 2*other.x + rI);
+            Asserts.assertEQ(test9(obj, other, rI, false), obj.x + 2*other.x + rI);
+            Asserts.assertEQ(test10(val1, other, rI, false), val1.x + 2*other.x + rI);
+            Asserts.assertEQ(test10(obj, other, rI, false), obj.x + 2*other.x + rI);
+            Asserts.assertEQ(test11(val1, other, rI, false), val1.x + 2*other.x + rI);
+            Asserts.assertEQ(test12(val1, other, rI, false), val1.x + 2*other.x + rI);
+            Asserts.assertEQ(test13(obj, other, rI, false), obj.x + 2*other.x + rI);
+            Asserts.assertEQ(test14(obj, other, rI, false), obj.x + 2*other.x + rI);
+            Asserts.assertEQ(test15(obj, other, rI), obj.x + 2*other.x + rI);
+            Asserts.assertEQ(test16(val1, other, rI), val1.x + 2*other.x + rI);
+            Asserts.assertEQ(test17(val1), val1.x);
+            Asserts.assertEQ(test18(val1, rI), val1.x + 6*rI);
+            Asserts.assertEQ(test18(val2, rI), val2.x + 6*rI);
+            Asserts.assertEQ(test18(val3, rI), (int)(4*val3.d1 + 6*rI));
+            Asserts.assertEQ(test18(val4, rI), 4*val4.x1 + 6*rI);
+            Asserts.assertEQ(test18(obj, rI), obj.x + 6*rI);
+            Asserts.assertEQ(test19(val1, rI), val1.x + 7*rI);
+            Asserts.assertEQ(test19(val2, rI), val2.x + 7*rI);
+            Asserts.assertEQ(test19(val3, rI), (int)(4*val3.d1 + 7*rI));
+            Asserts.assertEQ(test19(val4, rI), 4*val4.x1 + 7*rI);
+            Asserts.assertEQ(test19(obj, rI), obj.x + 7*rI);
+            Asserts.assertEQ(test20(val1, val3, rI), (int)(val1.x + 4*val3.d1 + 6*rI));
+            Asserts.assertEQ(test20(val2, val3, rI), (int)(val2.x + 4*val3.d1 + 6*rI));
+            Asserts.assertEQ(test20(val3, val3, rI), (int)(4*val3.d1 + 4*val3.d1 + 6*rI));
+            Asserts.assertEQ(test20(val4, val3, rI), (int)(4*val4.x1 + 4*val3.d1 + 6*rI));
+            Asserts.assertEQ(test20(obj, val3, rI), (int)(obj.x + 4*val3.d1 + 6*rI));
+            Asserts.assertEQ(test21(val1, val4, rI), val1.x + 4*val4.x1 + 6*rI);
+            Asserts.assertEQ(test21(val2, val4, rI), val2.x + 4*val4.x1 + 6*rI);
+            Asserts.assertEQ(test21(val3, val4, rI), (int)(4*val3.d1 + 4*val4.x1 + 6*rI));
+            Asserts.assertEQ(test21(val4, val4, rI), 4*val4.x1 + 4*val4.x1 + 6*rI);
+            Asserts.assertEQ(test21(obj, val4, rI), obj.x + 4*val4.x1 + 6*rI);
+        }
+
+        // Trigger deoptimization
+        Asserts.assertEQ(val1.test3(other, other, rI, true).getValue(), val1.x + other.x + rI);
+        Asserts.assertEQ(obj.test3(other, other, rI, true).getValue(), obj.x + other.x + rI);
+
+        // Check results of methods still calling the deoptimized methods
+        Asserts.assertEQ(test9(val1, other, rI, false), val1.x + 2*other.x + rI);
+        Asserts.assertEQ(test9(obj, other, rI, false), obj.x + 2*other.x + rI);
+        Asserts.assertEQ(test10(obj, other, rI, false), obj.x + 2*other.x + rI);
+        Asserts.assertEQ(test10(val1, other, rI, false), val1.x + 2*other.x + rI);
+        Asserts.assertEQ(test11(val1, other, rI, false), val1.x + 2*other.x + rI);
+        Asserts.assertEQ(test11(obj, other, rI, false), obj.x + 2*other.x + rI);
+        Asserts.assertEQ(test12(obj, other, rI, false), obj.x + 2*other.x + rI);
+        Asserts.assertEQ(test12(val1, other, rI, false), val1.x + 2*other.x + rI);
+        Asserts.assertEQ(test13(val1, other, rI, false), val1.x + 2*other.x + rI);
+        Asserts.assertEQ(test13(obj, other, rI, false), obj.x + 2*other.x + rI);
+        Asserts.assertEQ(test14(obj, other, rI, false), obj.x + 2*other.x + rI);
+        Asserts.assertEQ(test14(val1, other, rI, false), val1.x + 2*other.x + rI);
+
+        // Check with unexpected arguments
+        Asserts.assertEQ(test1(val2, other, rI), val2.x + other.x + rI);
+        Asserts.assertEQ(test2(val2, other, rI), val2.x + 2*other.x + rI);
+        Asserts.assertEQ(test5(val2, other, rI), val2.x + other.x + rI);
+        Asserts.assertEQ(test6(val2, other, rI), val2.x + 2*other.x + rI);
+        Asserts.assertEQ(test7(val1, other, rI), val1.x + other.x + rI);
+        Asserts.assertEQ(test8(val1, other, rI), val1.x + 2*other.x + rI);
+        Asserts.assertEQ(test15(val1, other, rI), val1.x + 2*other.x + rI);
+        Asserts.assertEQ(test16(obj, other, rI), obj.x + 2*other.x + rI);
+        Asserts.assertEQ(test17(obj), obj.x);
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCallingConvention.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCallingConvention.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCallingConvention.java
@@ -0,0 +1,1332 @@
+/*
+ * Copyright (c) 2017, 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.*;
+import jdk.test.lib.Asserts;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+
+import static compiler.valhalla.inlinetypes.InlineTypeIRNode.*;
+import static compiler.valhalla.inlinetypes.InlineTypes.*;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+/*
+ * @test
+ * @key randomness
+ * @summary Test value class calling convention optimizations.
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm/timeout=450 compiler.valhalla.inlinetypes.TestCallingConvention
+ */
+
+@ForceCompileClassInitializer
+public class TestCallingConvention {
+
+    static {
+        try {
+            Class<?> clazz = TestCallingConvention.class;
+            MethodHandles.Lookup lookup = MethodHandles.lookup();
+
+            MethodType mt = MethodType.methodType(MyValue2.class, boolean.class);
+            test32_mh = lookup.findVirtual(clazz, "test32_interp", mt);
+
+            mt = MethodType.methodType(Object.class, boolean.class);
+            test33_mh = lookup.findVirtual(clazz, "test33_interp", mt);
+
+            mt = MethodType.methodType(int.class);
+            test37_mh = lookup.findVirtual(Test37Value.class, "test", mt);
+
+            mt = MethodType.methodType(MyValue2.class);
+            test54_mh = lookup.findVirtual(clazz, "test54_callee", mt);
+
+            mt = MethodType.methodType(MyValue2.class, boolean.class);
+            test56_mh = lookup.findVirtual(clazz, "test56_callee", mt);
+        } catch (NoSuchMethodException | IllegalAccessException e) {
+            e.printStackTrace();
+            throw new RuntimeException("Method handle lookup failed");
+        }
+    }
+
+    public static void main(String[] args) {
+
+        Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS;
+        // Don't generate bytecodes but call through runtime for reflective calls
+        scenarios[0].addFlags("-Dsun.reflect.inflationThreshold=10000");
+        scenarios[1].addFlags("-Dsun.reflect.inflationThreshold=10000");
+        scenarios[3].addFlags("-XX:FlatArrayElementMaxSize=0");
+        scenarios[4].addFlags("-XX:-UseTLAB");
+
+        InlineTypes.getFramework()
+                   .addScenarios(scenarios)
+                   .addHelperClasses(MyValue1.class,
+                                     MyValue2.class,
+                                     MyValue2Inline.class,
+                                     MyValue3.class,
+                                     MyValue3Inline.class,
+                                     MyValue4.class)
+                   .start();
+    }
+
+    // Helper methods and classes
+
+    private void deoptimize(String name, Class<?>... params) {
+        try {
+            TestFramework.deoptimize(getClass().getDeclaredMethod(name, params));
+        } catch (NoSuchMethodException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    // Test interpreter to compiled code with various signatures
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test1(MyValue2 v) {
+        return v.hash();
+    }
+
+    @Run(test = "test1")
+    public void test1_verifier() {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, rD);
+        long result = test1(v);
+        Asserts.assertEQ(result, v.hashInterpreted());
+    }
+
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test2(int i1, MyValue2 v, int i2) {
+        return v.hash() + i1 - i2;
+    }
+
+    @Run(test = "test2")
+    public void test2_verifier() {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, rD);
+        long result = test2(rI, v, 2*rI);
+        Asserts.assertEQ(result, v.hashInterpreted() - rI);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test3(long l1, MyValue2 v, long l2) {
+        return v.hash() + l1 - l2;
+    }
+
+    @Run(test = "test3")
+    public void test3_verifier() {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, rD);
+        long result = test3(rL, v, 2*rL);
+        Asserts.assertEQ(result, v.hashInterpreted() - rL);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test4(int i, MyValue2 v, long l) {
+        return v.hash() + i + l;
+    }
+
+    @Run(test = "test4")
+    public void test4_verifier() {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, rD);
+        long result = test4(rI, v, rL);
+        Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test5(long l, MyValue2 v, int i) {
+        return v.hash() + i + l;
+    }
+
+    @Run(test = "test5")
+    public void test5_verifier() {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, rD);
+        long result = test5(rL, v, rI);
+        Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test6(long l, MyValue1 v1, int i, MyValue2 v2) {
+        return v1.hash() + i + l + v2.hash();
+    }
+
+    @Run(test = "test6")
+    public void test6_verifier() {
+        MyValue1 v1 = MyValue1.createWithFieldsDontInline(rI, rL);
+        MyValue2 v2 = MyValue2.createWithFieldsInline(rI, rD);
+        long result = test6(rL, v1, rI, v2);
+        Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted());
+    }
+
+    // Test compiled code to interpreter with various signatures
+    @DontCompile
+    public long test7_interp(MyValue2 v) {
+        return v.hash();
+    }
+
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test7(MyValue2 v) {
+        return test7_interp(v);
+    }
+
+    @Run(test = "test7")
+    public void test7_verifier() {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, rD);
+        long result = test7(v);
+        Asserts.assertEQ(result, v.hashInterpreted());
+    }
+
+    @DontCompile
+    public long test8_interp(int i1, MyValue2 v, int i2) {
+        return v.hash() + i1 - i2;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test8(int i1, MyValue2 v, int i2) {
+        return test8_interp(i1, v, i2);
+    }
+
+    @Run(test = "test8")
+    public void test8_verifier() {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, rD);
+        long result = test8(rI, v, 2*rI);
+        Asserts.assertEQ(result, v.hashInterpreted() - rI);
+    }
+
+    @DontCompile
+    public long test9_interp(long l1, MyValue2 v, long l2) {
+        return v.hash() + l1 - l2;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test9(long l1, MyValue2 v, long l2) {
+        return test9_interp(l1, v, l2);
+    }
+
+    @Run(test = "test9")
+    public void test9_verifier() {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, rD);
+        long result = test9(rL, v, 2*rL);
+        Asserts.assertEQ(result, v.hashInterpreted() - rL);
+    }
+
+    @DontCompile
+    public long test10_interp(int i, MyValue2 v, long l) {
+        return v.hash() + i + l;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test10(int i, MyValue2 v, long l) {
+        return test10_interp(i, v, l);
+    }
+
+    @Run(test = "test10")
+    public void test10_verifier() {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, rD);
+        long result = test10(rI, v, rL);
+        Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
+    }
+
+    @DontCompile
+    public long test11_interp(long l, MyValue2 v, int i) {
+        return v.hash() + i + l;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test11(long l, MyValue2 v, int i) {
+        return test11_interp(l, v, i);
+    }
+
+    @Run(test = "test11")
+    public void test11_verifier() {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, rD);
+        long result = test11(rL, v, rI);
+        Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
+    }
+
+    @DontCompile
+    public long test12_interp(long l, MyValue1 v1, int i, MyValue2 v2) {
+        return v1.hash() + i + l + v2.hash();
+    }
+
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test12(long l, MyValue1 v1, int i, MyValue2 v2) {
+        return test12_interp(l, v1, i, v2);
+    }
+
+    @Run(test = "test12")
+    public void test12_verifier() {
+        MyValue1 v1 = MyValue1.createWithFieldsDontInline(rI, rL);
+        MyValue2 v2 = MyValue2.createWithFieldsInline(rI, rD);
+        long result = test12(rL, v1, rI, v2);
+        Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted());
+    }
+
+    // Test that debug info at a call is correct
+    @DontCompile
+    public long test13_interp(MyValue2 v, MyValue1[] va, boolean deopt) {
+        if (deopt) {
+            // uncommon trap
+            deoptimize("test13", MyValue2.class, MyValue1[].class, boolean.class, long.class);
+        }
+        return v.hash() + va[0].hash() + va[1].hash();
+    }
+
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test13(MyValue2 v, MyValue1[] va, boolean flag, long l) {
+        return test13_interp(v, va, flag) + l;
+    }
+
+    @Run(test = "test13")
+    public void test13_verifier(RunInfo info) {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, rD);
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        va[0] = MyValue1.createWithFieldsDontInline(rI, rL);
+        va[1] = MyValue1.createWithFieldsDontInline(rI, rL);
+        long result = test13(v, va, !info.isWarmUp(), rL);
+        Asserts.assertEQ(result, v.hashInterpreted() + va[0].hash() + va[1].hash() + rL);
+    }
+
+    // Test deoptimization at call return with value object returned in registers
+    @DontCompile
+    public MyValue2 test14_interp(boolean deopt) {
+        if (deopt) {
+            // uncommon trap
+            deoptimize("test14", boolean.class);
+        }
+        return MyValue2.createWithFieldsInline(rI, rD);
+    }
+
+    @Test
+    public MyValue2 test14(boolean flag) {
+        return test14_interp(flag);
+    }
+
+    @Run(test = "test14")
+    public void test14_verifier(RunInfo info) {
+        MyValue2 result = test14(!info.isWarmUp());
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, rD);
+        Asserts.assertEQ(result.hash(), v.hash());
+    }
+
+    // Return value objects in registers from interpreter -> compiled
+    @NullRestricted
+    final MyValue3 test15_vt = MyValue3.create();
+
+    @DontCompile
+    public MyValue3 test15_interp() {
+        return test15_vt;
+    }
+
+    @NullRestricted
+    MyValue3 test15_vt2;
+
+    @Test
+    @IR(applyIf = {"InlineTypeReturnedAsFields", "true"},
+        failOn = {ALLOC, TRAP})
+    public void test15() {
+        test15_vt2 = test15_interp();
+    }
+
+    @Run(test = "test15")
+    public void test15_verifier() {
+        test15();
+        test15_vt.verify(test15_vt2);
+    }
+
+    // Return value objects in registers from compiled -> interpreter
+    @NullRestricted
+    final MyValue3 test16_vt = MyValue3.create();
+
+    @Test
+    @IR(applyIf = {"InlineTypeReturnedAsFields", "true"},
+        failOn = {ALLOC, STORE, TRAP})
+    public MyValue3 test16() {
+        return test16_vt;
+    }
+
+    @Run(test = "test16")
+    public void test16_verifier() {
+        MyValue3 vt = test16();
+        test16_vt.verify(vt);
+    }
+
+    // Return value objects in registers from compiled -> compiled
+    @NullRestricted
+    final MyValue3 test17_vt = MyValue3.create();
+
+    @DontInline
+    public MyValue3 test17_comp() {
+        return test17_vt;
+    }
+
+    @NullRestricted
+    MyValue3 test17_vt2;
+
+    @Test
+    @IR(applyIf = {"InlineTypeReturnedAsFields", "true"},
+        failOn = {ALLOC, TRAP})
+    public void test17() {
+        test17_vt2 = test17_comp();
+    }
+
+    @Run(test = "test17")
+    public void test17_verifier(RunInfo info) throws Exception {
+        Method helper_m = getClass().getDeclaredMethod("test17_comp");
+        if (!info.isWarmUp() && TestFramework.isCompiled(helper_m)) {
+            TestFramework.compile(helper_m, CompLevel.C2);
+            TestFramework.assertCompiledByC2(helper_m);
+        }
+
+        test17();
+        test17_vt.verify(test17_vt2);
+    }
+
+    // Same tests as above but with a value class that cannot be returned in registers
+
+    // Return value objects in registers from interpreter -> compiled
+    @NullRestricted
+    final MyValue4 test18_vt = MyValue4.create();
+
+    @DontCompile
+    public MyValue4 test18_interp() {
+        return test18_vt;
+    }
+
+    @NullRestricted
+    MyValue4 test18_vt2;
+
+    @Test
+    public void test18() {
+        test18_vt2 = test18_interp();
+    }
+
+    @Run(test = "test18")
+    public void test18_verifier() {
+        test18();
+        test18_vt.verify(test18_vt2);
+    }
+
+    // Return value objects in registers from compiled -> interpreter
+    @NullRestricted
+    final MyValue4 test19_vt = MyValue4.create();
+
+    @Test
+    public MyValue4 test19() {
+        return test19_vt;
+    }
+
+    @Run(test = "test19")
+    public void test19_verifier() {
+        MyValue4 vt = test19();
+        test19_vt.verify(vt);
+    }
+
+    // Return value objects in registers from compiled -> compiled
+    @NullRestricted
+    final MyValue4 test20_vt = MyValue4.create();
+
+    @DontInline
+    public MyValue4 test20_comp() {
+        return test20_vt;
+    }
+
+    @NullRestricted
+    MyValue4 test20_vt2;
+
+    @Test
+    public void test20() {
+        test20_vt2 = test20_comp();
+    }
+
+    @Run(test = "test20")
+    public void test20_verifier(RunInfo info) throws Exception {
+        Method helper_m = getClass().getDeclaredMethod("test20_comp");
+        if (!info.isWarmUp() && TestFramework.isCompiled(helper_m)) {
+            TestFramework.compile(helper_m, CompLevel.C2);
+            TestFramework.assertCompiledByC2(helper_m);
+        }
+        test20();
+        test20_vt.verify(test20_vt2);
+    }
+
+    // Test no result from inlined method for incremental inlining
+    @NullRestricted
+    final MyValue3 test21_vt = MyValue3.create();
+
+    public MyValue3 test21_inlined() {
+        throw new RuntimeException();
+    }
+
+    @Test
+    public MyValue3 test21() {
+        try {
+            return test21_inlined();
+        } catch (RuntimeException ex) {
+            return test21_vt;
+        }
+    }
+
+    @Run(test = "test21")
+    public void test21_verifier() {
+        MyValue3 vt = test21();
+        test21_vt.verify(vt);
+    }
+
+    // Test returning a non-flattened value object as fields
+    MyValue3 test22_vt = MyValue3.create();
+
+    @Test
+    public MyValue3 test22() {
+        return (MyValue3) test22_vt;
+    }
+
+    @Run(test = "test22")
+    public void test22_verifier() {
+        MyValue3 vt = test22();
+        test22_vt.verify(vt);
+    }
+
+    // Test calling a method that has circular register/stack dependencies when unpacking value class arguments
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    value class TestValue23 {
+        double f1;
+
+        TestValue23(double val) {
+            f1 = val;
+        }
+    }
+
+    static double test23Callee(int i1, int i2, int i3, int i4, int i5, int i6,
+                               TestValue23 v1, TestValue23 v2, TestValue23 v3, TestValue23 v4, TestValue23 v5, TestValue23 v6, TestValue23 v7, TestValue23 v8,
+                               double d1, double d2, double d3, double d4, double d5, double d6, double d7, double d8) {
+        return i1 + i2 + i3 + i4 + i5 + i6 + v1.f1 + v2.f1 + v3.f1 + v4.f1 + v5.f1 + v6.f1 + v7.f1 + v8.f1 + d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8;
+    }
+
+    @Test
+    public double test23(int i1, int i2, int i3, int i4, int i5, int i6,
+                         TestValue23 v1, TestValue23 v2, TestValue23 v3, TestValue23 v4, TestValue23 v5, TestValue23 v6, TestValue23 v7, TestValue23 v8,
+                         double d1, double d2, double d3, double d4, double d5, double d6, double d7, double d8) {
+        return test23Callee(i1, i2, i3, i4, i5, i6,
+                            v1, v2, v3, v4, v5, v6, v7, v8,
+                            d1, d2, d3, d4, d5, d6, d7, d8);
+    }
+
+    @Run(test = "test23")
+    public void test23_verifier() {
+        TestValue23 vt = new TestValue23(rI);
+        double res1 = test23(rI, rI, rI, rI, rI, rI,
+                            vt, vt, vt, vt, vt, vt, vt, vt,
+                            rI, rI, rI, rI, rI, rI, rI, rI);
+        double res2 = test23Callee(rI, rI, rI, rI, rI, rI,
+                                   vt, vt, vt, vt, vt, vt, vt, vt,
+                                   rI, rI, rI, rI, rI, rI, rI, rI);
+        double res3 = 6*rI + 8*rI + 8*rI;
+        Asserts.assertEQ(res1, res2);
+        Asserts.assertEQ(res2, res3);
+    }
+
+    // Should not return a nullable value object as fields
+    @Test
+    public MyValue2 test24() {
+        return null;
+    }
+
+    @Run(test = "test24")
+    public void test24_verifier() {
+        MyValue2 vt = test24();
+        Asserts.assertEQ(vt, null);
+    }
+
+    // Same as test24 but with control flow and inlining
+    @ForceInline
+    public MyValue2 test26_callee(boolean b) {
+        if (b) {
+            return null;
+        } else {
+            return MyValue2.createWithFieldsInline(rI, rD);
+        }
+    }
+
+    @Test
+    public MyValue2 test26(boolean b) {
+        return test26_callee(b);
+    }
+
+    @Run(test = "test26")
+    public void test26_verifier() {
+        MyValue2 vt = test26(true);
+        Asserts.assertEQ(vt, null);
+        vt = test26(false);
+        Asserts.assertEQ(vt.hash(), MyValue2.createWithFieldsInline(rI, rD).hash());
+    }
+
+    // Test calling convention with deep hierarchy of flattened fields
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    value class Test27Value1 {
+        Test27Value2 valueField;
+
+        private Test27Value1(Test27Value2 val2) {
+            valueField = val2;
+        }
+
+        @DontInline
+        public int test(Test27Value1 val1) {
+            return valueField.test(valueField) + val1.valueField.test(valueField);
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    value class Test27Value2 {
+        Test27Value3 valueField;
+
+        private Test27Value2(Test27Value3 val3) {
+            valueField = val3;
+        }
+
+        @DontInline
+        public int test(Test27Value2 val2) {
+            return valueField.test(valueField) + val2.valueField.test(valueField);
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    value class Test27Value3 {
+        int x;
+
+        private Test27Value3(int x) {
+            this.x = x;
+        }
+
+        @DontInline
+        public int test(Test27Value3 val3) {
+            return x + val3.x;
+        }
+    }
+
+    @Test
+    public int test27(Test27Value1 val) {
+        return val.test(val);
+    }
+
+    @Run(test = "test27")
+    public void test27_verifier() {
+        Test27Value3 val3 = new Test27Value3(rI);
+        Test27Value2 val2 = new Test27Value2(val3);
+        Test27Value1 val1 = new Test27Value1(val2);
+        int result = test27(val1);
+        Asserts.assertEQ(result, 8*rI);
+    }
+
+    static final MyValue1 test28Val = MyValue1.createWithFieldsDontInline(rI, rL);
+
+    @Test
+    public String test28() {
+        return test28Val.toString();
+    }
+
+    @Run(test = "test28")
+    @Warmup(0)
+    public void test28_verifier() {
+        String result = test28();
+    }
+
+    // Test calling a method returning a value object as fields via reflection
+    @NullRestricted
+    MyValue3 test29_vt = MyValue3.create();
+
+    @Test
+    public MyValue3 test29() {
+        return test29_vt;
+    }
+
+    @Run(test = "test29")
+    public void test29_verifier() throws Exception {
+        MyValue3 vt = (MyValue3)TestCallingConvention.class.getDeclaredMethod("test29").invoke(this);
+        test29_vt.verify(vt);
+    }
+
+    @Test
+    public MyValue3 test30(MyValue3[] array) {
+        MyValue3 result = MyValue3.create();
+        array[0] = result;
+        return result;
+    }
+
+    @Run(test = "test30")
+    public void test30_verifier() throws Exception {
+        MyValue3[] array = (MyValue3[])ValueClass.newNullRestrictedArray(MyValue3.class, 1);
+        MyValue3 vt = (MyValue3)TestCallingConvention.class.getDeclaredMethod("test30", MyValue3[].class).invoke(this, (Object)array);
+        array[0].verify(vt);
+    }
+
+    @NullRestricted
+    MyValue3 test31_vt;
+
+    @Test
+    public MyValue3 test31() {
+        MyValue3 result = MyValue3.create();
+        test31_vt = result;
+        return result;
+    }
+
+    @Run(test = "test31")
+    public void test31_verifier() throws Exception {
+        MyValue3 vt = (MyValue3)TestCallingConvention.class.getDeclaredMethod("test31").invoke(this);
+        test31_vt.verify(vt);
+    }
+
+    // Test deoptimization at call return with value object returned in registers.
+    // Same as test14, except the interpreted method is called via a MethodHandle.
+    static MethodHandle test32_mh;
+
+    @DontCompile
+    public MyValue2 test32_interp(boolean deopt) {
+        if (deopt) {
+            // uncommon trap
+            deoptimize("test32", boolean.class);
+        }
+        return MyValue2.createWithFieldsInline(rI+32, rD);
+    }
+
+    @Test
+    public MyValue2 test32(boolean flag) throws Throwable {
+        return (MyValue2)test32_mh.invokeExact(this, flag);
+    }
+
+    @Run(test = "test32")
+    public void test32_verifier(RunInfo info) throws Throwable {
+        MyValue2 result = test32(!info.isWarmUp());
+        MyValue2 v = MyValue2.createWithFieldsInline(rI+32, rD);
+        Asserts.assertEQ(result.hash(), v.hash());
+    }
+
+    // Same as test32, except the return type is not flattenable.
+    static MethodHandle test33_mh;
+
+    @DontCompile
+    public Object test33_interp(boolean deopt) {
+        if (deopt) {
+            // uncommon trap
+            deoptimize("test33", boolean.class);
+        }
+        return MyValue2.createWithFieldsInline(rI+33, rD);
+    }
+
+    @Test
+    public MyValue2 test33(boolean flag) throws Throwable {
+        Object o = test33_mh.invokeExact(this, flag);
+        return (MyValue2)o;
+    }
+
+    @Run(test = "test33")
+    public void test33_verifier(RunInfo info) throws Throwable {
+        MyValue2 result = test33(!info.isWarmUp());
+        MyValue2 v = MyValue2.createWithFieldsInline(rI+33, rD);
+        Asserts.assertEQ(result.hash(), v.hash());
+    }
+
+    // Test selection of correct entry point in SharedRuntime::handle_wrong_method
+    static boolean test34_deopt = false;
+
+    @DontInline
+    public static long test34_callee(MyValue2 vt, int i1, int i2, int i3, int i4) {
+        Asserts.assertEQ(i1, rI);
+        Asserts.assertEQ(i2, rI);
+        Asserts.assertEQ(i3, rI);
+        Asserts.assertEQ(i4, rI);
+
+        if (test34_deopt) {
+            // uncommon trap
+            int result = 0;
+            for (int i = 0; i < 10; ++i) {
+                result += rL;
+            }
+            return vt.hash() + i1 + i2 + i3 + i4 + result;
+        }
+        return vt.hash() + i1 + i2 + i3 + i4;
+    }
+
+    @Test
+    public static long test34(MyValue2 vt, int i1, int i2, int i3, int i4) {
+        return test34_callee(vt, i1, i2, i3, i4);
+    }
+
+    @Run(test = "test34")
+    @Warmup(10000) // Make sure test34_callee is compiled
+    public void test34_verifier(RunInfo info) {
+        MyValue2 vt = MyValue2.createWithFieldsInline(rI, rD);
+        long result = test34(vt, rI, rI, rI, rI);
+        Asserts.assertEQ(result, vt.hash()+4*rI);
+        if (!info.isWarmUp()) {
+            test34_deopt = true;
+            for (int i = 0; i < 100; ++i) {
+                result = test34(vt, rI, rI, rI, rI);
+                Asserts.assertEQ(result, vt.hash()+4*rI+10*rL);
+            }
+        }
+    }
+
+    // Test OSR compilation of method with scalarized argument
+    @Test
+    public static long test35(MyValue2 vt, int i1, int i2, int i3, int i4) {
+        int result = 0;
+        // Trigger OSR compilation
+        for (int i = 0; i < 10_000; ++i) {
+            result += i1;
+        }
+        return vt.hash() + i1 + i2 + i3 + i4 + result;
+    }
+
+    @Run(test = "test35")
+    public void test35_verifier() {
+        MyValue2 vt = MyValue2.createWithFieldsInline(rI, rD);
+        long result = test35(vt, rI, rI, rI, rI);
+        Asserts.assertEQ(result, vt.hash()+10004*rI);
+    }
+
+    // Same as test31 but with GC in callee to verify that the
+    // pre-allocated buffer for the returned value object remains valid.
+    @NullRestricted
+    MyValue3 test36_vt;
+
+    @Test
+    public MyValue3 test36() {
+        MyValue3 result = MyValue3.create();
+        test36_vt = result;
+        System.gc();
+        return result;
+    }
+
+    @Run(test = "test36")
+    public void test36_verifier() throws Exception {
+        MyValue3 vt = (MyValue3)TestCallingConvention.class.getDeclaredMethod("test36").invoke(this);
+        test36_vt.verify(vt);
+    }
+
+    // Test method resolution with scalarized value object receiver at invokespecial
+    static final MethodHandle test37_mh;
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    value class Test37Value {
+        int x = rI;
+
+        @DontInline
+        public int test() {
+            return x;
+        }
+    }
+
+    @Test
+    public int test37(Test37Value vt) throws Throwable {
+        // Generates invokespecial call of Test37Value::test
+        return (int)test37_mh.invokeExact(vt);
+    }
+
+    @Run(test = "test37")
+    public void test37_verifier() throws Throwable {
+        Test37Value vt = new Test37Value();
+        int res = test37(vt);
+        Asserts.assertEQ(res, rI);
+    }
+
+    // Test passing/returning an empty value object
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE, TRAP})
+    public MyValueEmpty test38(MyValueEmpty vt) {
+        return vt.copy(vt);
+    }
+
+    @Run(test = "test38")
+    public void test38_verifier() {
+        MyValueEmpty vt = new MyValueEmpty();
+        MyValueEmpty res = test38(vt);
+        Asserts.assertEQ(res, vt);
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class LargeValueWithOops {
+        // Use all 6 int registers + 50/2 on stack = 29
+        Object o1 = null;
+        Object o2 = null;
+        Object o3 = null;
+        Object o4 = null;
+        Object o5 = null;
+        Object o6 = null;
+        Object o7 = null;
+        Object o8 = null;
+        Object o9 = null;
+        Object o10 = null;
+        Object o11 = null;
+        Object o12 = null;
+        Object o13 = null;
+        Object o14 = null;
+        Object o15 = null;
+        Object o16 = null;
+        Object o17 = null;
+        Object o18 = null;
+        Object o19 = null;
+        Object o20 = null;
+        Object o21 = null;
+        Object o22 = null;
+        Object o23 = null;
+        Object o24 = null;
+        Object o25 = null;
+        Object o26 = null;
+        Object o27 = null;
+        Object o28 = null;
+        Object o29 = null;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class LargeValueWithoutOops {
+        // Use all 6 int registers + 50/2 on stack = 29
+        int i1 = 0;
+        int i2 = 0;
+        int i3 = 0;
+        int i4 = 0;
+        int i5 = 0;
+        int i6 = 0;
+        int i7 = 0;
+        int i8 = 0;
+        int i9 = 0;
+        int i10 = 0;
+        int i11 = 0;
+        int i12 = 0;
+        int i13 = 0;
+        int i14 = 0;
+        int i15 = 0;
+        int i16 = 0;
+        int i17 = 0;
+        int i18 = 0;
+        int i19 = 0;
+        int i20 = 0;
+        int i21 = 0;
+        int i22 = 0;
+        int i23 = 0;
+        int i24 = 0;
+        int i25 = 0;
+        int i26 = 0;
+        int i27 = 0;
+        int i28 = 0;
+        int i29 = 0;
+        // Use all 7 float registers
+        double d1 = 0;
+        double d2 = 0;
+        double d3 = 0;
+        double d4 = 0;
+        double d5 = 0;
+        double d6 = 0;
+        double d7 = 0;
+        double d8 = 0;
+    }
+
+    // Test passing/returning a large value object with oop fields
+    @Test
+    public static LargeValueWithOops test39(LargeValueWithOops vt) {
+        return vt;
+    }
+
+    @Run(test = "test39")
+    public void test39_verifier() {
+        LargeValueWithOops vt = new LargeValueWithOops();
+        LargeValueWithOops res = test39(vt);
+        Asserts.assertEQ(res, vt);
+    }
+
+    // Test passing/returning a large value object with only int/float fields
+    @Test
+    public static LargeValueWithoutOops test40(LargeValueWithoutOops vt) {
+        return vt;
+    }
+
+    @Run(test = "test40")
+    public void test40_verifier() {
+        LargeValueWithoutOops vt = new LargeValueWithoutOops();
+        LargeValueWithoutOops res = test40(vt);
+        Asserts.assertEQ(res, vt);
+    }
+
+    // Test passing/returning an empty value object together with non-empty
+    // value objects such that only some value class arguments are scalarized.
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE, TRAP})
+    public MyValueEmpty test41(MyValue1 vt1, MyValueEmpty vt2, MyValue1 vt3) {
+        return vt2.copy(vt2);
+    }
+
+    @Run(test = "test41")
+    public void test41_verifier() {
+        MyValueEmpty res = test41(MyValue1.createDefaultInline(), new MyValueEmpty(), MyValue1.createDefaultInline());
+        Asserts.assertEQ(res, new MyValueEmpty());
+    }
+
+    // More empty value class tests with containers
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class EmptyContainer {
+        @NullRestricted
+        private MyValueEmpty empty;
+
+        @ForceInline
+        EmptyContainer(MyValueEmpty empty) {
+            this.empty = empty;
+        }
+
+        @ForceInline
+        MyValueEmpty getInline() { return empty; }
+
+        @DontInline
+        MyValueEmpty getNoInline() { return empty; }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MixedContainer {
+        public int val;
+        @NullRestricted
+        private EmptyContainer empty;
+
+        @ForceInline
+        MixedContainer(int val, EmptyContainer empty) {
+            this.val = val;
+            this.empty = empty;
+        }
+
+        @ForceInline
+        EmptyContainer getInline() { return empty; }
+
+        @DontInline
+        EmptyContainer getNoInline() { return empty; }
+    }
+
+    // Empty value object return
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE, TRAP})
+    public MyValueEmpty test42() {
+        EmptyContainer c = new EmptyContainer(new MyValueEmpty());
+        return c.getInline();
+    }
+
+    @Run(test = "test42")
+    public void test42_verifier() {
+        MyValueEmpty empty = test42();
+        Asserts.assertEquals(empty, new MyValueEmpty());
+    }
+
+    // Empty value class container return
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE, TRAP})
+    public EmptyContainer test43(EmptyContainer c) {
+        return c;
+    }
+
+    @Run(test = "test43")
+    public void test43_verifier() {
+        EmptyContainer empty = new EmptyContainer(new MyValueEmpty());
+        EmptyContainer c = test43(empty);
+        Asserts.assertEquals(c, empty);
+    }
+
+    // Empty value class container (mixed) return
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE, TRAP})
+    public MixedContainer test44() {
+        MixedContainer c = new MixedContainer(rI, new EmptyContainer(new MyValueEmpty()));
+        c = new MixedContainer(rI, c.getInline());
+        return c;
+    }
+
+    @Run(test = "test44")
+    public void test44_verifier() {
+        MixedContainer c = test44();
+        Asserts.assertEquals(c, new MixedContainer(rI, new EmptyContainer(new MyValueEmpty())));
+    }
+
+    // Empty value class container argument
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE, TRAP})
+    public EmptyContainer test45(EmptyContainer c) {
+        return new EmptyContainer(c.getInline());
+    }
+
+    @Run(test = "test45")
+    public void test45_verifier() {
+        EmptyContainer empty = new EmptyContainer(new MyValueEmpty());
+        EmptyContainer c = test45(empty);
+        Asserts.assertEquals(c, empty);
+    }
+
+    // Empty value class container and mixed container arguments
+    @Test
+// TODO 8325106
+//    @IR(failOn = {ALLOC, STORE, TRAP})
+    public MyValueEmpty test46(EmptyContainer c1, MixedContainer c2, MyValueEmpty empty) {
+        c2 = new MixedContainer(c2.val, c1);
+        return c2.getNoInline().getNoInline();
+    }
+
+    @Run(test = "test46")
+    public void test46_verifier() {
+        MyValueEmpty empty = test46(new EmptyContainer(new MyValueEmpty()), new MixedContainer(0, new EmptyContainer(new MyValueEmpty())), new MyValueEmpty());
+        Asserts.assertEquals(empty, new MyValueEmpty());
+    }
+
+    // No receiver and only empty argument
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE, TRAP})
+    public static MyValueEmpty test47(MyValueEmpty empty) {
+        return empty;
+    }
+
+    @Run(test = "test47")
+    public void test47_verifier() {
+        MyValueEmpty empty = test47(new MyValueEmpty());
+        Asserts.assertEquals(empty,new MyValueEmpty());
+    }
+
+    // No receiver and only empty container argument
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE, TRAP})
+    public static MyValueEmpty test48(EmptyContainer empty) {
+        return empty.getNoInline();
+    }
+
+    @Run(test = "test48")
+    public void test48_verifier() {
+        MyValueEmpty empty = test48(new EmptyContainer(new MyValueEmpty()));
+        Asserts.assertEquals(empty, new MyValueEmpty());
+    }
+
+    // Test conditional value class return with incremental inlining
+    public MyValue3 test49_inlined1(boolean b) {
+        if (b) {
+            return MyValue3.create();
+        } else {
+            return MyValue3.create();
+        }
+    }
+
+    public MyValue3 test49_inlined2(boolean b) {
+        return test49_inlined1(b);
+    }
+
+    @Test
+    public void test49(boolean b) {
+        test49_inlined2(b);
+    }
+
+    @Run(test = "test49")
+    public void test49_verifier() {
+        test49(true);
+        test49(false);
+    }
+
+    // Variant of test49 with result verification (triggered different failure mode)
+    @NullRestricted
+    final MyValue3 test50_vt = MyValue3.create();
+    @NullRestricted
+    final MyValue3 test50_vt2 = test50_vt;
+
+    public MyValue3 test50_inlined1(boolean b) {
+        if (b) {
+            return test50_vt;
+        } else {
+            return test50_vt2;
+        }
+    }
+
+    public MyValue3 test50_inlined2(boolean b) {
+        return test50_inlined1(b);
+    }
+
+    @Test
+    public void test50(boolean b) {
+        MyValue3 vt = test50_inlined2(b);
+        test50_vt.verify(vt);
+    }
+
+    @Run(test = "test50")
+    public void test50_verifier() {
+        test50(true);
+        test50(false);
+    }
+
+    // Test stack repair with stack slots reserved for monitors
+    private static final Object lock1 = new Object();
+    private static final Object lock2 = new Object();
+    private static final Object lock3 = new Object();
+
+    @DontInline
+    static void test51_callee() { }
+
+    @Test
+    public void test51(MyValue1 val) {
+        synchronized (lock1) {
+            test51_callee();
+        }
+    }
+
+    @Run(test = "test51")
+    public void test51_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        test51(vt);
+    }
+
+    @DontInline
+    static void test52_callee() { }
+
+    @Test
+    public void test52(MyValue1 val) {
+        synchronized (lock1) {
+            synchronized (lock2) {
+                test52_callee();
+            }
+        }
+    }
+
+    @Run(test = "test52")
+    public void test52_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        test52(vt);
+    }
+
+    @DontInline
+    static void test53_callee() { }
+
+    @Test
+    public void test53(MyValue1 val) {
+        synchronized (lock1) {
+            synchronized (lock2) {
+                synchronized (lock3) {
+                    test53_callee();
+                }
+            }
+        }
+    }
+
+    @Run(test = "test53")
+    public void test53_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        test53(vt);
+    }
+
+    static MethodHandle test54_mh;
+
+    @DontInline
+    public MyValue2 test54_callee() {
+        return MyValue2.createWithFieldsInline(rI, rD);
+    }
+
+    // Test that method handle invocation does not block scalarization of return value
+    @Test
+    @IR(failOn = {ALLOC_G, STORE})
+    public long test54(Method m, boolean b1, boolean b2) throws Throwable {
+        MyInterface obj = MyValue2.createWithFieldsInline(rI, rD);
+        if (b1) {
+            obj = (MyValue2)test54_mh.invokeExact(this);
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+            return obj.hash();
+        }
+        return -1;
+    }
+
+    @Run(test = "test54")
+    @Warmup(10000)
+    public void test54_verifier(RunInfo info) throws Throwable {
+        Asserts.assertEQ(test54(info.getTest(), true, false), -1L);
+        Asserts.assertEQ(test54(info.getTest(), false, false), -1L);
+        if (!info.isWarmUp()) {
+            MyValue2 v = MyValue2.createWithFieldsInline(rI, rD);
+            Asserts.assertEQ(test54(info.getTest(), true, true), v.hash());
+        }
+    }
+
+    @DontInline
+    public MyValue2 test55_callee() {
+        return MyValue2.createWithFieldsInline(rI, rD);
+    }
+
+    // Test scalarization of nullable return value that is unused
+    @Test
+    public void test55() {
+        test55_callee();
+    }
+
+    @Run(test = "test55")
+    public void test55_verifier() {
+        test55();
+    }
+
+    static MethodHandle test56_mh;
+
+    @DontInline
+    public MyValue2 test56_callee(boolean b) {
+        return b ? MyValue2.createWithFieldsInline(rI, rD) : null;
+    }
+
+    // Test that scalarization of nullable return works properly for method handle calls
+    @Test
+    public MyValue2 test56(boolean b) throws Throwable {
+        return (MyValue2)test56_mh.invokeExact(this, b);
+    }
+
+    @Run(test = "test56")
+    @Warmup(10000)
+    public void test56_verifier(RunInfo info) throws Throwable {
+        MyValue2 vt = MyValue2.createWithFieldsInline(rI, rD);
+        Asserts.assertEQ(test56(true).hash(), vt.hash());
+        if (!info.isWarmUp()) {
+            Asserts.assertEQ(test56(false), null);
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCallingConventionC1.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCallingConventionC1.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCallingConventionC1.java
@@ -0,0 +1,2419 @@
+/*
+ * 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.
+ *
+ * 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.*;
+import jdk.test.lib.Asserts;
+import jdk.test.whitebox.WhiteBox;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import static compiler.valhalla.inlinetypes.InlineTypes.rI;
+import static compiler.valhalla.inlinetypes.InlineTypes.rL;
+
+/*
+ * @test
+ * @key randomness
+ * @summary Test calls from {C1} to {C2, Interpreter}, and vice versa.
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm/timeout=300 compiler.valhalla.inlinetypes.TestCallingConventionC1
+ */
+
+@ForceCompileClassInitializer
+public class TestCallingConventionC1 {
+
+    public static void main(String[] args) {
+        final Scenario[] scenarios = {
+                // Default: both C1 and C2 are enabled, tiered compilation enabled
+                new Scenario(0,
+                             "--enable-preview",
+                             "-XX:CICompilerCount=2",
+                             "-XX:TieredStopAtLevel=4",
+                             "-XX:+TieredCompilation"),
+                // Default: both C1 and C2 are enabled, tiered compilation enabled
+                new Scenario(1,
+                             "--enable-preview",
+                             "-XX:CICompilerCount=2",
+                             "-XX:TieredStopAtLevel=4",
+                             "-XX:+TieredCompilation",
+                             "-XX:+IgnoreUnrecognizedVMOptions",
+                             "-XX:+StressInlineTypeReturnedAsFields"),
+                // Same as above, but flip all the compLevel=CompLevel.C1_SIMPLE and compLevel=CompLevel.C2, so we test
+                // the compliment of the above scenario.
+                new Scenario(2,
+                             "--enable-preview",
+                             "-XX:CICompilerCount=2",
+                             "-XX:TieredStopAtLevel=4",
+                             "-XX:+TieredCompilation",
+                             "-DFlipC1C2=true"),
+                // Only C1. Tiered compilation disabled.
+                new Scenario(3,
+                             "--enable-preview",
+                             "-XX:TieredStopAtLevel=1",
+                             "-XX:+TieredCompilation",
+                             "-XX:+IgnoreUnrecognizedVMOptions",
+                             "-XX:-PatchALot"),
+                // Only C2.
+                new Scenario(4,
+                             "--enable-preview",
+                             "-XX:TieredStopAtLevel=4",
+                             "-XX:-TieredCompilation")
+        };
+
+        System.gc(); // Resolve this call, to avoid C1 code patching in the test cases.
+
+        InlineTypes.getFramework()
+                   .addScenarios(scenarios)
+                   .start();
+    }
+
+    // Helper methods and classes
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Point {
+        int x;
+        int y;
+        public Point(int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+
+        @DontCompile
+        public int func() {
+            return x + y;
+        }
+
+        @ForceCompile(CompLevel.C1_SIMPLE)
+        @DontInline
+        public int func_c1(Point p) {
+            return x + y + p.x + p.y;
+        }
+    }
+
+    static interface FunctorInterface {
+        public int apply_interp(Point p);
+    }
+
+    static class Functor implements FunctorInterface {
+        @DontCompile
+        public int apply_interp(Point p) {
+            return p.func() + 0;
+        }
+    }
+
+    static class Functor1 extends Functor {
+        @DontCompile
+        public int apply_interp(Point p) {
+            return p.func() + 10000;
+        }
+    }
+
+    static class Functor2 extends Functor {
+        @DontCompile
+        public int apply_interp(Point p) {
+            return p.func() + 20000;
+        }
+    }
+
+    static class Functor3 extends Functor {
+        @DontCompile
+        public int apply_interp(Point p) {
+            return p.func() + 30000;
+        }
+    }
+
+    static class Functor4 extends Functor {
+        @DontCompile
+        public int apply_interp(Point p) {
+            return p.func() + 40000;
+        }
+    }
+
+    static Functor functors[] = {
+        new Functor(),
+        new Functor1(),
+        new Functor2(),
+        new Functor3(),
+        new Functor4()
+    };
+    static int functorCounter = 0;
+    static Functor getFunctor() {
+        int n = (++ functorCounter) % functors.length;
+        return functors[n];
+    }
+
+    @NullRestricted
+    static Point pointField  = new Point(123, 456);
+    @NullRestricted
+    static Point pointField1 = new Point(1123, 1456);
+    @NullRestricted
+    static Point pointField2 = new Point(2123, 2456);
+
+    static interface Intf {
+        public int func1(int a, int b);
+        public int func2(int a, int b, Point p);
+    }
+
+    static class MyImplPojo0 implements Intf {
+        int field = 0;
+        @DontCompile
+        public int func1(int a, int b)             { return field + a + b + 1; }
+        @DontCompile
+        public int func2(int a, int b, Point p)     { return field + a + b + p.x + p.y + 1; }
+    }
+
+    static class MyImplPojo1 implements Intf {
+        int field = 1000;
+
+        @DontInline
+        @ForceCompile(CompLevel.C1_SIMPLE)
+        public int func1(int a, int b)             { return field + a + b + 20; }
+
+        @DontInline
+        @ForceCompile(CompLevel.C1_SIMPLE)
+        public int func2(int a, int b, Point p)    { return field + a + b + p.x + p.y + 20; }
+    }
+
+    static class MyImplPojo2 implements Intf {
+        int field = 2000;
+
+        @DontInline
+        @ForceCompile(CompLevel.C2)
+        public int func1(int a, int b)             { return field + a + b + 20; }
+
+        @DontInline
+        @ForceCompile(CompLevel.C2)
+        public int func2(int a, int b, Point p)    { return field + a + b + p.x + p.y + 20; }
+    }
+
+    static class MyImplPojo3 implements Intf {
+        int field = 0;
+        @DontInline // will be compiled with counters
+        public int func1(int a, int b)             { return field + a + b + 1; }
+        @DontInline // will be compiled with counters
+        public int func2(int a, int b, Point p)     { return field + a + b + p.x + p.y + 1; }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyImplVal1 implements Intf {
+        int field;
+        MyImplVal1() {
+            field = 11000;
+        }
+
+        @DontInline
+        @ForceCompile(CompLevel.C1_SIMPLE)
+        public int func1(int a, int b) { return field + a + b + 300; }
+
+        @DontInline
+        @ForceCompile(CompLevel.C1_SIMPLE)
+        public int func2(int a, int b, Point p)    { return field + a + b + p.x + p.y + 300; }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyImplVal2 implements Intf {
+        int field;
+        MyImplVal2() {
+            field = 12000;
+        }
+
+        @DontInline
+        @ForceCompile(CompLevel.C2)
+        public int func1(int a, int b)             { return field + a + b + 300; }
+
+        @DontInline
+        @ForceCompile(CompLevel.C2)
+        public int func2(int a, int b, Point p)    { return field + a + b + p.x + p.y + 300; }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyImplVal1X implements Intf {
+        int field;
+        MyImplVal1X() {
+            field = 11000;
+        }
+
+        @DontCompile
+        public int func1(int a, int b)             { return field + a + b + 300; }
+
+        @DontCompile
+        public int func2(int a, int b, Point p)    { return field + a + b + p.x + p.y + 300; }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyImplVal2X implements Intf {
+        int field;
+        MyImplVal2X() {
+            field = 12000;
+        }
+
+        @DontInline // will be compiled with counters
+        public int func1(int a, int b)             { return field + a + b + 300; }
+
+        @DontInline // will be compiled with counters
+        public int func2(int a, int b, Point p)    { return field + a + b + p.x + p.y + 300; }
+    }
+
+    static Intf intfs[] = {
+        new MyImplPojo0(), // methods not compiled
+        new MyImplPojo1(), // methods compiled by C1
+        new MyImplPojo2(), // methods compiled by C2
+        new MyImplVal1(),  // methods compiled by C1
+        new MyImplVal2()   // methods compiled by C2
+    };
+    static Intf getIntf(int i) {
+        int n = i % intfs.length;
+        return intfs[n];
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class FixedPoints {
+        boolean Z0 = false;
+        boolean Z1 = true;
+        byte    B  = (byte)2;
+        char    C  = (char)34;
+        short   S  = (short)456;
+        int     I  = 5678;
+        long    J  = 0x1234567800abcdefL;
+    }
+    @NullRestricted
+    static FixedPoints fixedPointsField = new FixedPoints();
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class FloatPoint {
+        float x;
+        float y;
+        public FloatPoint(float x, float y) {
+            this.x = x;
+            this.y = y;
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class DoublePoint {
+        double x;
+        double y;
+        public DoublePoint(double x, double y) {
+            this.x = x;
+            this.y = y;
+        }
+    }
+    @NullRestricted
+    static FloatPoint floatPointField = new FloatPoint(123.456f, 789.012f);
+    @NullRestricted
+    static DoublePoint doublePointField = new DoublePoint(123.456, 789.012);
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class EightFloats {
+        float f1, f2, f3, f4, f5, f6, f7, f8;
+        public EightFloats() {
+            f1 = 1.1f;
+            f2 = 2.2f;
+            f3 = 3.3f;
+            f4 = 4.4f;
+            f5 = 5.5f;
+            f6 = 6.6f;
+            f7 = 7.7f;
+            f8 = 8.8f;
+        }
+    }
+
+    static EightFloats eightFloatsField = new EightFloats();
+
+    static class Number {
+        int n;
+        Number(int v) {
+            n = v;
+        }
+        void set(int v) {
+            n = v;
+        }
+    }
+
+    static interface RefPoint_Access {
+        public int func1(RefPoint rp2);
+        public int func2(RefPoint rp1, RefPoint rp2, Number n1, RefPoint rp3, RefPoint rp4, Number n2);
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class RefPoint implements RefPoint_Access {
+        Number x;
+        Number y;
+        public RefPoint(int x, int y) {
+            this.x = new Number(x);
+            this.y = new Number(y);
+        }
+        public RefPoint(Number x, Number y) {
+            this.x = x;
+            this.y = y;
+        }
+
+        @DontInline
+        @ForceCompile(CompLevel.C1_SIMPLE)
+        public final int final_func(RefPoint rp2) { // opt_virtual_call
+            return this.x.n + this.y.n + rp2.x.n + rp2.y.n;
+        }
+
+        @DontInline
+        @ForceCompile(CompLevel.C1_SIMPLE)
+        public int func1(RefPoint rp2) {
+            return this.x.n + this.y.n + rp2.x.n + rp2.y.n;
+        }
+
+        @DontInline
+        @ForceCompile(CompLevel.C1_SIMPLE)
+        public int func2(RefPoint rp1, RefPoint rp2, Number n1, RefPoint rp3, RefPoint rp4, Number n2) {
+            return x.n + y.n +
+                   rp1.x.n + rp1.y.n +
+                   rp2.x.n + rp2.y.n +
+                   n1.n +
+                   rp3.x.n + rp3.y.n +
+                   rp4.x.n + rp4.y.n +
+                   n2.n;
+        }
+    }
+
+    static class RefPoint_Access_Impl1 implements RefPoint_Access {
+        @DontCompile
+        public int func1(RefPoint rp2) {
+            return rp2.x.n + rp2.y.n + 1111111;
+        }
+        @DontInline
+        @ForceCompile(CompLevel.C1_SIMPLE)
+        public int func2(RefPoint rp1, RefPoint rp2, Number n1, RefPoint rp3, RefPoint rp4, Number n2) {
+            return 111111 +
+                   rp1.x.n + rp1.y.n +
+                   rp2.x.n + rp2.y.n +
+                   n1.n +
+                   rp3.x.n + rp3.y.n +
+                   rp4.x.n + rp4.y.n +
+                   n2.n;
+        }
+    }
+
+    static class RefPoint_Access_Impl2 implements RefPoint_Access {
+        @DontCompile
+        public int func1(RefPoint rp2) {
+            return rp2.x.n + rp2.y.n + 2222222;
+        }
+        @DontInline
+        @ForceCompile(CompLevel.C1_SIMPLE)
+        public int func2(RefPoint rp1, RefPoint rp2, Number n1, RefPoint rp3, RefPoint rp4, Number n2) {
+            return 222222 +
+                   rp1.x.n + rp1.y.n +
+                   rp2.x.n + rp2.y.n +
+                   n1.n +
+                   rp3.x.n + rp3.y.n +
+                   rp4.x.n + rp4.y.n +
+                   n2.n;
+        }
+    }
+
+    static RefPoint_Access refPoint_Access_impls[] = {
+        new RefPoint_Access_Impl1(),
+        new RefPoint_Access_Impl2(),
+        new RefPoint(0x12345, 0x6789a)
+    };
+
+    static int next_RefPoint_Access = 0;
+    static RefPoint_Access get_RefPoint_Access() {
+        int i = next_RefPoint_Access ++;
+        return refPoint_Access_impls[i % refPoint_Access_impls.length];
+    }
+
+    @NullRestricted
+    static RefPoint refPointField1 = new RefPoint(12, 34);
+    @NullRestricted
+    static RefPoint refPointField2 = new RefPoint(56789, 0x12345678);
+
+    // This value class has too many fields to fit in registers on x64 for
+    // InlineTypeReturnedAsFields.
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class TooBigToReturnAsFields {
+        int a0 = 0;
+        int a1 = 1;
+        int a2 = 2;
+        int a3 = 3;
+        int a4 = 4;
+        int a5 = 5;
+        int a6 = 6;
+        int a7 = 7;
+        int a8 = 8;
+        int a9 = 9;
+    }
+
+    @NullRestricted
+    static TooBigToReturnAsFields tooBig = new TooBigToReturnAsFields();
+
+    //**********************************************************************
+    // PART 1 - C1 calls interpreted code
+    //**********************************************************************
+
+    //** C1 passes value object to interpreter (static)
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public int test1() {
+        return test1_helper(pointField);
+    }
+
+    @DontCompile
+    private static int test1_helper(Point p) {
+        return p.func();
+    }
+
+    @Run(test = "test1")
+    public void test1_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 10;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            int result = test1() + i;
+            Asserts.assertEQ(result, pointField.func() + i);
+        }
+    }
+
+    //** C1 passes value object to interpreter (monomorphic)
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public int test2() {
+        return test2_helper(pointField);
+    }
+
+    @DontCompile
+    private int test2_helper(Point p) {
+        return p.func();
+    }
+
+    @Run(test = "test2")
+    public void test2_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 10;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            int result = test2() + i;
+            Asserts.assertEQ(result, pointField.func() + i);
+        }
+    }
+
+    // C1 passes value object to interpreter (megamorphic: vtable)
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public int test3(Functor functor) {
+        return functor.apply_interp(pointField);
+    }
+
+    @Run(test = "test3")
+    public void test3_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 100;
+        for (int i = 0; i < count; i++) {  // need a loop to test inline cache and vtable indexing
+            Functor functor = info.isWarmUp() ? functors[0] : getFunctor();
+            int result = test3(functor) + i;
+            Asserts.assertEQ(result, functor.apply_interp(pointField) + i);
+        }
+    }
+
+    // Same as test3, but compiled with C2. Test the hastable of VtableStubs
+    @Test(compLevel = CompLevel.C2)
+    public int test3b(Functor functor) {
+        return functor.apply_interp(pointField);
+    }
+
+    @Run(test = "test3b")
+    public void test3b_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 100;
+        for (int i = 0; i < count; i++) {  // need a loop to test inline cache and vtable indexing
+            Functor functor = info.isWarmUp() ? functors[0] : getFunctor();
+            int result = test3b(functor) + i;
+            Asserts.assertEQ(result, functor.apply_interp(pointField) + i);
+        }
+    }
+
+    // C1 passes value object to interpreter (megamorphic: itable)
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public int test4(FunctorInterface fi) {
+        return fi.apply_interp(pointField);
+    }
+
+    @Run(test = "test4")
+    public void test4_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 100;
+        for (int i = 0; i < count; i++) {  // need a loop to test inline cache and itable indexing
+            Functor functor = info.isWarmUp() ? functors[0] : getFunctor();
+            int result = test4(functor) + i;
+            Asserts.assertEQ(result, functor.apply_interp(pointField) + i);
+        }
+    }
+
+    //**********************************************************************
+    // PART 2 - interpreter calls C1
+    //**********************************************************************
+
+    // Interpreter passes value object to C1 (static)
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    static public int test20(Point p1, long l, Point p2) {
+        return p1.x + p2.y;
+    }
+
+    @Run(test = "test20")
+    public void test20_verifier() {
+        int result = test20(pointField1, 0, pointField2);
+        int n = pointField1.x + pointField2.y;
+        Asserts.assertEQ(result, n);
+    }
+
+    // Interpreter passes value object to C1 (instance method in value class)
+    @Test
+    public int test21(Point p) {
+        return test21_helper(p);
+    }
+
+    @DontCompile
+    int test21_helper(Point p) {
+        return p.func_c1(p);
+    }
+
+    @Run(test = "test21")
+    public void test21_verifier() {
+        int result = test21(pointField);
+        int n = 2 * (pointField.x + pointField.y);
+        Asserts.assertEQ(result, n);
+    }
+
+
+    //**********************************************************************
+    // PART 3 - C2 calls C1
+    //**********************************************************************
+
+    // C2->C1 invokestatic, single inline arg
+    @Test(compLevel = CompLevel.C2)
+    public int test30() {
+        return test30_helper(pointField);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static int test30_helper(Point p) {
+        return p.x + p.y;
+    }
+
+    @Run(test = "test30")
+    public void test30_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 2;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            int result = test30();
+            int n = pointField.x + pointField.y;
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, two single inline args
+    @Test(compLevel = CompLevel.C2)
+    public int test31() {
+      return test31_helper(pointField1, pointField2);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static int test31_helper(Point p1, Point p2) {
+        return p1.x + p2.y;
+    }
+
+    @Run(test = "test31")
+    public void test31_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 2;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            int result = test31();
+            int n = pointField1.x + pointField2.y;
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, two single inline args and interleaving ints (all passed in registers on x64)
+    @Test(compLevel = CompLevel.C2)
+    public int test32() {
+      return test32_helper(0, pointField1, 1, pointField2);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static int test32_helper(int x, Point p1, int y, Point p2) {
+        return p1.x + p2.y + x + y;
+    }
+
+    @Run(test = "test32")
+    public void test32_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 2;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            int result = test32();
+            int n = pointField1.x + pointField2.y + 0 + 1;
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokeinterface -- no verified_ro_entry (no inline args except for receiver)
+    @Test(compLevel = CompLevel.C2)
+    public int test33(Intf intf, int a, int b) {
+        return intf.func1(a, b);
+    }
+
+    @Run(test = "test33")
+    public void test33_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 20;
+        for (int i = 0; i < count; i++) {
+            Intf intf = info.isWarmUp() ? intfs[0] : getIntf(i+1);
+            int result = test33(intf, 123, 456) + i;
+            Asserts.assertEQ(result, intf.func1(123, 456) + i);
+        }
+    }
+
+    // C2->C1 invokeinterface -- use verified_ro_entry (has inline args other than receiver)
+    @Test(compLevel = CompLevel.C2)
+    public int test34(Intf intf, int a, int b) {
+        return intf.func2(a, b, pointField);
+    }
+
+    @Run(test = "test34")
+    public void test34_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 20;
+        for (int i = 0; i < count; i++) {
+            Intf intf = info.isWarmUp() ? intfs[0] : getIntf(i+1);
+            int result = test34(intf, 123, 456) + i;
+            Asserts.assertEQ(result, intf.func2(123, 456, pointField) + i);
+        }
+    }
+
+    // C2->C1 invokestatic, Point.y is on stack (x64)
+    @Test(compLevel = CompLevel.C2)
+    public int test35() {
+        return test35_helper(1, 2, 3, 4, 5, pointField);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static int test35_helper(int a1, int a2, int a3, int a4, int a5, Point p) {
+        return a1 + a2 + a3 + a4 + a5 + p.x + p.y;
+    }
+
+    @Run(test = "test35")
+    public void test35_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 2;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            int result = test35();
+            int n = 1 + 2 + 3  + 4 + 5 + pointField.x + pointField.y;
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, shuffling arguments that are passed on stack
+    @Test(compLevel = CompLevel.C2)
+    public int test36() {
+        return test36_helper(pointField, 1, 2, 3, 4, 5, 6, 7, 8);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static int test36_helper(Point p, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+        return a6 + a8;
+    }
+
+    @Run(test = "test36")
+    public void test36_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 2;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            int result = test36();
+            int n = 6 + 8;
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, shuffling long arguments
+    @Test(compLevel = CompLevel.C2)
+    public int test37() {
+        return test37_helper(pointField, 1, 2, 3, 4, 5, 6, 7, 8);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static int test37_helper(Point p, long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) {
+        return (int)(a6 + a8);
+    }
+
+    @Run(test = "test37")
+    public void test37_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 2;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            int result = test37();
+            int n = 6 + 8;
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, shuffling boolean, byte, char, short, int, long arguments
+    @Test(compLevel = CompLevel.C2)
+    public int test38() {
+        return test38_helper(pointField, true, (byte)1, (char)2, (short)3, 4, 5, (byte)6, (short)7, 8);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static int test38_helper(Point p, boolean a0, byte a1, char a2, short a3, int a4, long a5, byte a6, short a7, int a8) {
+        if (a0) {
+            return (int)(a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8);
+        } else {
+            return -1;
+        }
+    }
+
+    @Run(test = "test38")
+    public void test38_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 2;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            int result = test38();
+            int n = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8;
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, packing a value object with all types of fixed point primitive fields.
+    @Test(compLevel = CompLevel.C2)
+    public long test39() {
+        return test39_helper(1, fixedPointsField, 2, fixedPointsField);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static long test39_helper(int a1, FixedPoints f1, int a2, FixedPoints f2) {
+        if (f1.Z0 == false && f1.Z1 == true && f2.Z0 == false && f2.Z1 == true) {
+            return f1.B + f2.C + f1.S + f2.I + f1.J;
+        } else {
+            return -1;
+        }
+    }
+
+    @Run(test = "test39")
+    public void test39_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 2;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            long result = test39();
+            long n = test39_helper(1, fixedPointsField, 2, fixedPointsField);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, shuffling floating point args
+    @Test(compLevel = CompLevel.C2)
+    public double test40() {
+        return test40_helper(1.1f, 1.2, floatPointField, doublePointField, 1.3f, 1.4, 1.5f, 1.7, 1.7, 1.8, 1.9, 1.10, 1.11, 1.12);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static double test40_helper(float a1, double a2, FloatPoint fp, DoublePoint dp, float a3, double a4, float a5, double a6, double a7, double a8, double a9, double a10, double a11, double a12) {
+        return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + fp.x + fp.y - dp.x - dp.y;
+    }
+
+    @Run(test = "test40")
+    public void test40_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 2;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            double result = test40();
+            double n = test40_helper(1.1f, 1.2, floatPointField, doublePointField, 1.3f, 1.4, 1.5f, 1.7, 1.7, 1.8, 1.9, 1.10, 1.11, 1.12);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, mixing floats and ints
+    @Test(compLevel = CompLevel.C2)
+    public double test41() {
+        return test41_helper(1, 1.2, pointField, floatPointField, doublePointField, 1.3f, 4, 1.5f, 1.7, 1.7, 1.8, 9, 1.10, 1.11, 1.12);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static double test41_helper(int a1, double a2, Point p, FloatPoint fp, DoublePoint dp, float a3, int a4, float a5, double a6, double a7, double a8, long a9, double a10, double a11, double a12) {
+      return a1 + a2  + fp.x + fp.y - dp.x - dp.y + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12;
+    }
+
+    @Run(test = "test41")
+    public void test41_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 2;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            double result = test41();
+            double n = test41_helper(1, 1.2, pointField, floatPointField, doublePointField, 1.3f, 4, 1.5f, 1.7, 1.7, 1.8, 9, 1.10, 1.11, 1.12);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, circular dependency (between rdi and first stack slot on x64)
+    @Test(compLevel = CompLevel.C2)
+    public float test42() {
+        return test42_helper(eightFloatsField, pointField, 3, 4, 5, floatPointField, 7);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static float test42_helper(EightFloats ep1, // (xmm0 ... xmm7) -> rsi
+                                       Point p2,        // (rsi, rdx) -> rdx
+                                       int i3,          // rcx -> rcx
+                                       int i4,          // r8 -> r8
+                                       int i5,          // r9 -> r9
+                                       FloatPoint fp6,  // (stk[0], stk[1]) -> rdi   ** circ depend
+                                       int i7)          // rdi -> stk[0]             ** circ depend
+    {
+        return ep1.f1 + ep1.f2 + ep1.f3 + ep1.f4 + ep1.f5 + ep1.f6 + ep1.f7 + ep1.f8 +
+            p2.x + p2.y + i3 + i4 + i5 + fp6.x + fp6.y + i7;
+    }
+
+    @Run(test = "test42")
+    public void test42_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 2;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            float result = test42();
+            float n = test42_helper(eightFloatsField, pointField, 3, 4, 5, floatPointField, 7);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, packing causes stack growth (1 extra stack word)
+    @Test(compLevel = CompLevel.C2)
+    public float test43() {
+        return test43_helper(floatPointField, 1, 2, 3, 4, 5, 6);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static float test43_helper(FloatPoint fp, int a1, int a2, int a3, int a4, int a5, int a6) {
+        // On x64:
+        //    Scalarized entry -- all parameters are passed in registers
+        //    Non-scalarized entry -- a6 is passed on stack[0]
+        return fp.x + fp.y + a1 + a2 + a3 + a4 + a5 + a6;
+    }
+
+    @Run(test = "test43")
+    public void test43_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 2;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            float result = test43();
+            float n = test43_helper(floatPointField, 1, 2, 3, 4, 5, 6);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, packing causes stack growth (2 extra stack words)
+    @Test(compLevel = CompLevel.C2)
+    public float test44() {
+      return test44_helper(floatPointField, floatPointField, 1, 2, 3, 4, 5, 6);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static float test44_helper(FloatPoint fp1, FloatPoint fp2, int a1, int a2, int a3, int a4, int a5, int a6) {
+        // On x64:
+        //    Scalarized entry -- all parameters are passed in registers
+        //    Non-scalarized entry -- a5 is passed on stack[0]
+        //    Non-scalarized entry -- a6 is passed on stack[1]
+        return fp1.x + fp1.y +
+               fp2.x + fp2.y +
+               a1 + a2 + a3 + a4 + a5 + a6;
+    }
+
+    @Run(test = "test44")
+    public void test44_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 2;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            float result = test44();
+            float n = test44_helper(floatPointField, floatPointField, 1, 2, 3, 4, 5, 6);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, packing causes stack growth (5 extra stack words)
+    @Test(compLevel = CompLevel.C2)
+    public float test45() {
+      return test45_helper(floatPointField, floatPointField, floatPointField, floatPointField, floatPointField, 1, 2, 3, 4, 5, 6, 7);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static float test45_helper(FloatPoint fp1, FloatPoint fp2, FloatPoint fp3, FloatPoint fp4, FloatPoint fp5, int a1, int a2, int a3, int a4, int a5, int a6, int a7) {
+        return fp1.x + fp1.y +
+               fp2.x + fp2.y +
+               fp3.x + fp3.y +
+               fp4.x + fp4.y +
+               fp5.x + fp5.y +
+               a1 + a2 + a3 + a4 + a5 + a6 + a7;
+    }
+
+    @Run(test = "test45")
+    public void test45_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 2;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            float result = test45();
+            float n = test45_helper(floatPointField, floatPointField, floatPointField, floatPointField, floatPointField, 1, 2, 3, 4, 5, 6, 7);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, packing causes stack growth (1 extra stack word -- mixing Point and FloatPoint)
+    @Test(compLevel = CompLevel.C2)
+    public float test46() {
+      return test46_helper(floatPointField, floatPointField, pointField, floatPointField, floatPointField, pointField, floatPointField, 1, 2, 3, 4, 5, 6, 7);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static float test46_helper(FloatPoint fp1, FloatPoint fp2, Point p1, FloatPoint fp3, FloatPoint fp4, Point p2, FloatPoint fp5, int a1, int a2, int a3, int a4, int a5, int a6, int a7) {
+        return p1.x + p1.y +
+               p2.x + p2.y +
+               fp1.x + fp1.y +
+               fp2.x + fp2.y +
+               fp3.x + fp3.y +
+               fp4.x + fp4.y +
+               fp5.x + fp5.y +
+               a1 + a2 + a3 + a4 + a5 + a6 + a7;
+    }
+
+    @Run(test = "test46")
+    public void test46_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 2;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            float result = test46();
+            float n = test46_helper(floatPointField, floatPointField, pointField, floatPointField, floatPointField, pointField, floatPointField, 1, 2, 3, 4, 5, 6, 7);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    static class MyRuntimeException extends RuntimeException {
+        MyRuntimeException(String s) {
+            super(s);
+        }
+    }
+
+    static void checkStackTrace(Throwable t, String... methodNames) {
+        StackTraceElement[] trace = t.getStackTrace();
+        for (int i = 0; i < methodNames.length; i++) {
+            if (!methodNames[i].equals(trace[i].getMethodName())) {
+                String error = "Unexpected stack trace: level " + i + " should be " + methodNames[i];
+                System.out.println(error);
+                t.printStackTrace(System.out);
+                throw new RuntimeException(error, t);
+            }
+        }
+    }
+    //*
+
+    // C2->C1 invokestatic, make sure stack walking works (with static variable)
+    @Test(compLevel = CompLevel.C2)
+    public void test47(int n) {
+        try {
+            test47_helper(floatPointField, 1, 2, 3, 4, 5);
+            test47_value = 666;
+        } catch (MyRuntimeException e) {
+            // expected;
+        }
+        test47_value = n;
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static float test47_helper(FloatPoint fp, int a1, int a2, int a3, int a4, int a5) {
+        test47_thrower();
+        return 0.0f;
+    }
+
+    @DontCompile
+    private static void test47_thrower() {
+        MyRuntimeException e = new MyRuntimeException("This exception should have been caught!");
+        checkStackTrace(e, "test47_thrower", "test47_helper", "test47", "test47_verifier");
+        throw e;
+    }
+
+    static int test47_value = 999;
+
+    @Run(test = "test47")
+    public void test47_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 5;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            test47_value = 777 + i;
+            test47(i);
+            Asserts.assertEQ(test47_value, i);
+        }
+    }
+
+    // C2->C1 invokestatic, make sure stack walking works (with returned value object)
+    @Test(compLevel = CompLevel.C2)
+    public int test48(int n) {
+        try {
+            test48_helper(floatPointField, 1, 2, 3, 4, 5);
+            return 666;
+        } catch (MyRuntimeException e) {
+            // expected;
+        }
+        return n;
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static float test48_helper(FloatPoint fp, int a1, int a2, int a3, int a4, int a5) {
+        test48_thrower();
+        return 0.0f;
+    }
+
+    @DontCompile
+    private static void test48_thrower() {
+        MyRuntimeException e = new MyRuntimeException("This exception should have been caught!");
+        checkStackTrace(e, "test48_thrower", "test48_helper", "test48", "test48_verifier");
+        throw e;
+    }
+
+    @Run(test = "test48")
+    public void test48_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 5;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            int n = test48(i);
+            Asserts.assertEQ(n, i);
+        }
+    }
+
+    // C2->interpreter invokestatic, make sure stack walking works (same as test 48, but with stack extension/repair)
+    // (this is the baseline for test50 --
+    // the only difference is: test49_helper is interpreted but test50_helper is compiled by C1).
+    @Test(compLevel = CompLevel.C2)
+    public int test49(int n) {
+        try {
+            test49_helper(floatPointField, 1, 2, 3, 4, 5, 6);
+            return 666;
+        } catch (MyRuntimeException e) {
+            // expected;
+        }
+        return n;
+    }
+
+    @DontCompile
+    private static float test49_helper(FloatPoint fp, int a1, int a2, int a3, int a4, int a5, int a6) {
+        test49_thrower();
+        return 0.0f;
+    }
+
+    @DontCompile
+    private static void test49_thrower() {
+        MyRuntimeException e = new MyRuntimeException("This exception should have been caught!");
+        checkStackTrace(e, "test49_thrower", "test49_helper", "test49", "test49_verifier");
+        throw e;
+    }
+
+    @Run(test = "test49")
+    public void test49_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 5;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            int n = test49(i);
+            Asserts.assertEQ(n, i);
+        }
+    }
+
+    // C2->C1 invokestatic, make sure stack walking works (same as test 48, but with stack extension/repair)
+    @Test(compLevel = CompLevel.C2)
+    public int test50(int n) {
+        try {
+            test50_helper(floatPointField, 1, 2, 3, 4, 5, 6);
+            return 666;
+        } catch (MyRuntimeException e) {
+            // expected;
+        }
+        return n;
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static float test50_helper(FloatPoint fp, int a1, int a2, int a3, int a4, int a5, int a6) {
+        test50_thrower();
+        return 0.0f;
+    }
+
+    @DontCompile
+    private static void test50_thrower() {
+        MyRuntimeException e = new MyRuntimeException("This exception should have been caught!");
+        checkStackTrace(e, "test50_thrower", "test50_helper", "test50", "test50_verifier");
+        throw e;
+    }
+
+    @Run(test = "test50")
+    public void test50_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 5;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            int n = test50(i);
+            Asserts.assertEQ(n, i);
+        }
+    }
+
+
+    // C2->C1 invokestatic, value class with ref fields (RefPoint)
+    @Test(compLevel = CompLevel.C2)
+    public int test51() {
+        return test51_helper(refPointField1);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static int test51_helper(RefPoint rp1) {
+        return rp1.x.n + rp1.y.n;
+    }
+
+    @Run(test = "test51")
+    public void test51_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 5;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            int result = test51();
+            int n = test51_helper(refPointField1);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, value class with ref fields (Point, RefPoint)
+    @Test(compLevel = CompLevel.C2)
+    public int test52() {
+        return test52_helper(pointField, refPointField1);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static int test52_helper(Point p1, RefPoint rp1) {
+        return p1.x + p1.y + rp1.x.n + rp1.y.n;
+    }
+
+    @Run(test = "test52")
+    public void test52_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 5;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            int result = test52();
+            int n = test52_helper(pointField, refPointField1);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, value class with ref fields (RefPoint, RefPoint, RefPoint, RefPoint)
+    @Test(compLevel = CompLevel.C2)
+    public int test53() {
+        return test53_helper(refPointField1, refPointField2, refPointField1, refPointField2);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static int test53_helper(RefPoint rp1, RefPoint rp2, RefPoint rp3, RefPoint rp4) {
+        return rp1.x.n + rp1.y.n +
+               rp2.x.n + rp2.y.n +
+               rp3.x.n + rp3.y.n +
+               rp4.x.n + rp4.y.n;
+    }
+
+    @Run(test = "test53")
+    public void test53_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 5;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            int result = test53();
+            int n = test53_helper(refPointField1, refPointField2, refPointField1, refPointField2);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, value class with ref fields (RefPoint, RefPoint, float, int, RefPoint, RefPoint)
+    @Test(compLevel = CompLevel.C2)
+    public int test54() {
+        return test54_helper(refPointField1, refPointField2, 1.0f, 2, refPointField1, refPointField2);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static int test54_helper(RefPoint rp1, RefPoint rp2, float f, int i, RefPoint rp3, RefPoint rp4) {
+        return rp1.x.n + rp1.y.n +
+               rp2.x.n + rp2.y.n +
+               (int)(f) + i +
+               rp3.x.n + rp3.y.n +
+               rp4.x.n + rp4.y.n;
+    }
+
+    @Run(test = "test54")
+    public void test54_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 5;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            int result = test54();
+            int n = test54_helper(refPointField1, refPointField2, 1.0f, 2, refPointField1, refPointField2);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    /**
+     * Each allocation with a "try" block like this will cause a GC
+     *
+     *       try (ForceGCMarker m = ForceGCMarker.mark(warmup)) {
+     *           result = test55(p1);
+     *       }
+     */
+
+    static final String ScavengeALot = "ScavengeALot";
+
+    static class ForceGCMarker implements java.io.Closeable {
+        ForceGCMarker() {
+            WhiteBox.getWhiteBox().setBooleanVMFlag(ScavengeALot, true);
+        }
+        public void close() {
+            WhiteBox.getWhiteBox().setBooleanVMFlag(ScavengeALot, false);
+        }
+
+        static ForceGCMarker mark(boolean warmup) {
+            return warmup ? null : new ForceGCMarker();
+        }
+    }
+
+    // C2->C1 invokestatic, force GC for every allocation when entering a C1 VEP (Point)
+    @Test(compLevel = CompLevel.C2)
+    public int test55(Point p1) {
+        return test55_helper(p1);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static int test55_helper(Point p1) {
+        return p1.x + p1.y;
+    }
+
+    @Run(test = "test55")
+    public void test55_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 5;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            Point p1 = new Point(1, 2);
+            int result;
+            try (ForceGCMarker m = ForceGCMarker.mark(info.isWarmUp())) {
+                result = test55(p1);
+            }
+            int n = test55_helper(p1);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, force GC for every allocation when entering a C1 VEP (RefPoint)
+    @Test(compLevel = CompLevel.C2)
+    public int test56(RefPoint rp1) {
+        return test56_helper(rp1);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static int test56_helper(RefPoint rp1) {
+        return rp1.x.n + rp1.y.n;
+    }
+
+    @Run(test = "test56")
+    public void test56_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 5;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            RefPoint rp1 = new RefPoint(1, 2);
+            int result;
+            try (ForceGCMarker m = ForceGCMarker.mark(info.isWarmUp())) {
+                result = test56(rp1);
+            }
+            int n = test56_helper(rp1);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->Interpreter (same as test56, but test C2i entry instead of C1)
+    @Test(compLevel = CompLevel.C2)
+    public int test57(RefPoint rp1) {
+        return test57_helper(rp1);
+    }
+
+    @DontCompile
+    private static int test57_helper(RefPoint rp1) {
+        return rp1.x.n + rp1.y.n;
+    }
+
+    @Run(test = "test57")
+    public void test57_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 5;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            RefPoint rp1 = new RefPoint(1, 2);
+            int result;
+            try (ForceGCMarker m = ForceGCMarker.mark(info.isWarmUp())) {
+                result = test57(rp1);
+            }
+            int n = test57_helper(rp1);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, force GC for every allocation when entering a C1 VEP (a bunch of RefPoints and Numbers);
+    @Test(compLevel = CompLevel.C2)
+    public int test58(RefPoint rp1, RefPoint rp2, Number n1, RefPoint rp3, RefPoint rp4, Number n2) {
+        return test58_helper(rp1, rp2, n1, rp3, rp4, n2);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static int test58_helper(RefPoint rp1, RefPoint rp2, Number n1, RefPoint rp3, RefPoint rp4, Number n2) {
+        return rp1.x.n + rp1.y.n +
+               rp2.x.n + rp2.y.n +
+               n1.n +
+               rp3.x.n + rp3.y.n +
+               rp4.x.n + rp4.y.n +
+               n2.n;
+    }
+
+    @Run(test = "test58")
+    public void test58_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 5;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            RefPoint rp1 = new RefPoint(1, 2);
+            RefPoint rp2 = refPointField1;
+            RefPoint rp3 = new RefPoint(222, 777);
+            RefPoint rp4 = refPointField2;
+            Number n1 = new Number(5878);
+            Number n2 = new Number(1234);
+            int result;
+            try (ForceGCMarker m = ForceGCMarker.mark(info.isWarmUp())) {
+                result = test58(rp1, rp2, n1, rp3, rp4, n2);
+            }
+            int n = test58_helper(rp1, rp2, n1, rp3, rp4, n2);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, GC inside main body of C1-compiled method (caller's args should not be GC'ed).
+    @Test(compLevel = CompLevel.C2)
+    public int test59(RefPoint rp1, boolean doGC) {
+      return test59_helper(rp1, 11, 222, 3333, 4444, doGC);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static int test59_helper(RefPoint rp1, int a1, int a2, int a3, int a4, boolean doGC) {
+        if (doGC) {
+            System.gc();
+        }
+        return rp1.x.n + rp1.y.n + a1 + a2 + a3 + a4;
+    }
+
+    @Run(test = "test59")
+    public void test59_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 5;
+        boolean doGC = !info.isWarmUp();
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            RefPoint rp1 = new RefPoint(1, 2);
+            int result = test59(rp1, doGC);
+            int n = test59_helper(rp1, 11, 222, 3333, 4444, doGC);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic, GC inside main body of C1-compiled method (caller's args should not be GC'ed).
+    // same as test59, but the incoming (scalarized) oops are passed in both registers and stack.
+    @Test(compLevel = CompLevel.C2)
+    public int test60(RefPoint rp1, RefPoint rp2, boolean doGC) {
+        return test60_helper(555, 6666, 77777, rp1, rp2, 11, 222, 3333, 4444, doGC);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static int test60_helper(int x0, int x1, int x2, RefPoint rp1, RefPoint rp2,int a1, int a2, int a3, int a4, boolean doGC) {
+        // On x64, C2 passes:   reg0=x1, reg1=x1, reg2=x2, reg3=rp1.x, reg4=rp1.y, reg5=rp2.x stack0=rp2.y ....
+        //         C1 expects:  reg0=x1, reg1=x1, reg2=x2, reg3=rp1,   reg4=rp2,   reg5=a1    stack0=a2 ...
+        // When GC happens, make sure it does not treat reg5 and stack0 as oops!
+        if (doGC) {
+            System.gc();
+        }
+        return x0 + x1 + x2 + rp1.x.n + rp1.y.n + rp2.x.n + rp2.y.n + a1 + a2 + a3 + a4;
+    }
+
+    @Run(test = "test60")
+    public void test60_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 5;
+        boolean doGC = !info.isWarmUp();
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            RefPoint rp1 = new RefPoint(1, 2);
+            RefPoint rp2 = new RefPoint(33, 44);
+            int result = test60(rp1, rp2, doGC);
+            int n = test60_helper(555, 6666, 77777, rp1, rp2, 11, 222, 3333, 4444, doGC);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokeinterface via VVEP(RO)
+    @Test(compLevel = CompLevel.C2)
+    public int test61(RefPoint_Access rpa, RefPoint rp2) {
+        return rpa.func1(rp2);
+    }
+
+    @Run(test = "test61")
+    public void test61_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 20;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            RefPoint_Access rpa = get_RefPoint_Access();
+            RefPoint rp2 = refPointField2;
+            int result = test61(rpa, rp2);
+            int n = rpa.func1(rp2);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokeinterface via VVEP(RO) -- force GC for every allocation when entering a C1 VVEP(RO) (RefPoint)
+    @Test(compLevel = CompLevel.C2)
+    public int test62(RefPoint_Access rpa, RefPoint rp2) {
+        return rpa.func1(rp2);
+    }
+
+    @Run(test = "test62")
+    public void test62_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 20;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            RefPoint_Access rpa = get_RefPoint_Access();
+            RefPoint rp2 = new RefPoint(111, 2222);
+            int result;
+            try (ForceGCMarker m = ForceGCMarker.mark(info.isWarmUp())) {
+                result = test62(rpa, rp2);
+            }
+            int n = rpa.func1(rp2);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokeinterface via VVEP(RO) -- force GC for every allocation when entering a C1 VVEP(RO) (a bunch of RefPoints and Numbers)
+    @Test(compLevel = CompLevel.C2)
+    public int test63(RefPoint_Access rpa, RefPoint rp1, RefPoint rp2, Number n1, RefPoint rp3, RefPoint rp4, Number n2) {
+        return rpa.func2(rp1, rp2, n1, rp3, rp4, n2);
+    }
+
+    @Run(test = "test63")
+    public void test63_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 20;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            RefPoint_Access rpa = get_RefPoint_Access();
+            RefPoint rp1 = new RefPoint(1, 2);
+            RefPoint rp2 = refPointField1;
+            RefPoint rp3 = new RefPoint(222, 777);
+            RefPoint rp4 = refPointField2;
+            Number n1 = new Number(5878);
+            Number n2 = new Number(1234);
+            int result;
+            try (ForceGCMarker m = ForceGCMarker.mark(info.isWarmUp())) {
+                result = test63(rpa, rp1, rp2, n1, rp3, rp4, n2);
+            }
+            int n = rpa.func2(rp1, rp2, n1, rp3, rp4, n2);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokestatic (same as test63, but use invokestatic instead)
+    @Test(compLevel = CompLevel.C2)
+    public int test64(RefPoint_Access rpa, RefPoint rp1, RefPoint rp2, Number n1, RefPoint rp3, RefPoint rp4, Number n2) {
+        return test64_helper(rpa, rp1, rp2, n1, rp3, rp4, n2);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    public static int test64_helper(RefPoint_Access rpa, RefPoint rp1, RefPoint rp2, Number n1, RefPoint rp3, RefPoint rp4, Number n2) {
+        return rp3.y.n;
+    }
+
+    @Run(test = "test64")
+    public void test64_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 20;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            RefPoint_Access rpa = get_RefPoint_Access();
+            RefPoint rp1 = new RefPoint(1, 2);
+            RefPoint rp2 = refPointField1;
+            RefPoint rp3 = new RefPoint(222, 777);
+            RefPoint rp4 = refPointField2;
+            Number n1 = new Number(5878);
+            Number n2 = new Number(1234);
+            int result;
+            try (ForceGCMarker m = ForceGCMarker.mark(info.isWarmUp())) {
+                result = test64(rpa, rp1, rp2, n1, rp3, rp4, n2);
+            }
+            int n = test64_helper(rpa, rp1, rp2, n1, rp3, rp4, n2);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokevirtual via VVEP(RO) (opt_virtual_call)
+    @Test(compLevel = CompLevel.C2)
+    public int test76(RefPoint rp1, RefPoint rp2) {
+        return rp1.final_func(rp2);
+    }
+
+    @Run(test = "test76")
+    public void test76_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 5;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            RefPoint rp1 = refPointField1;
+            RefPoint rp2 = refPointField2;
+            int result = test76(rp1, rp2);
+            int n = rp1.final_func(rp2);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C2->C1 invokevirtual, force GC for every allocation when entering a C1 VEP (RefPoint)
+    // Same as test56, except we call the VVEP(RO) instead of VEP.
+    @Test(compLevel = CompLevel.C2)
+    public int test77(RefPoint rp1, RefPoint rp2) {
+        return rp1.final_func(rp2);
+    }
+
+    @Run(test = "test77")
+    public void test77_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 5;
+        for (int i = 0; i < count; i++) { // need a loop to test inline cache
+            RefPoint rp1 = new RefPoint(1, 2);
+            RefPoint rp2 = new RefPoint(22, 33);
+            int result;
+            try (ForceGCMarker m = ForceGCMarker.mark(info.isWarmUp())) {
+                result = test77(rp1, rp2);
+            }
+            int n = rp1.final_func(rp2);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    //-------------------------------------------------------------------------------
+    // Tests for how C1 handles InlineTypeReturnedAsFields in both calls and returns
+    //-------------------------------------------------------------------------------
+    // C2->C1 invokestatic with InlineTypeReturnedAsFields (Point)
+    @Test(compLevel = CompLevel.C2)
+    public int test78(Point p) {
+        Point np = test78_helper(p);
+        return np.x + np.y;
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static Point test78_helper(Point p) {
+        return p;
+    }
+
+    @Run(test = "test78")
+    public void test78_verifier() {
+        int result = test78(pointField1);
+        int n = pointField1.x + pointField1.y;
+        Asserts.assertEQ(result, n);
+    }
+
+    // C2->C1 invokestatic with InlineTypeReturnedAsFields (RefPoint)
+    @Test(compLevel = CompLevel.C2)
+    public int test79(RefPoint p) {
+        RefPoint np = test79_helper(p);
+        return np.x.n + np.y.n;
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static RefPoint test79_helper(RefPoint p) {
+        return p;
+    }
+
+    @Run(test = "test79")
+    public void test79_verifier() {
+        int result = test79(refPointField1);
+        int n = refPointField1.x.n + refPointField1.y.n;
+        Asserts.assertEQ(result, n);
+    }
+
+    // C1->C2 invokestatic with InlineTypeReturnedAsFields (RefPoint)
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public int test80(RefPoint p) {
+        RefPoint np = test80_helper(p);
+        return np.x.n + np.y.n;
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C2)
+    private static RefPoint test80_helper(RefPoint p) {
+        return p;
+    }
+
+    @Run(test = "test80")
+    public void test80_verifier() {
+        int result = test80(refPointField1);
+        int n = refPointField1.x.n + refPointField1.y.n;
+        Asserts.assertEQ(result, n);
+    }
+
+    // Interpreter->C1 invokestatic with InlineTypeReturnedAsFields (Point)
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public Point test81(Point p) {
+        return p;
+    }
+
+    @Run(test = "test81")
+    public void test81_verifier() {
+        Point p = test81(pointField1);
+        Asserts.assertEQ(p.x, pointField1.x);
+        Asserts.assertEQ(p.y, pointField1.y);
+        p = test81(pointField2);
+        Asserts.assertEQ(p.x, pointField2.x);
+        Asserts.assertEQ(p.y, pointField2.y);
+    }
+
+    // C1->Interpreter invokestatic with InlineTypeReturnedAsFields (RefPoint)
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public int test82(RefPoint p) {
+        RefPoint np = test82_helper(p);
+        return np.x.n + np.y.n;
+    }
+
+    @DontCompile
+    private static RefPoint test82_helper(RefPoint p) {
+        return p;
+    }
+
+    @Run(test = "test82")
+    public void test82_verifier() {
+        int result = test82(refPointField1);
+        int n = refPointField1.x.n + refPointField1.y.n;
+        Asserts.assertEQ(result, n);
+    }
+
+    //-------------------------------------------------------------------------------
+    // Tests for InlineTypeReturnedAsFields vs the value class TooBigToReturnAsFields
+    //-------------------------------------------------------------------------------
+
+    // C2->C1 invokestatic with InlineTypeReturnedAsFields (TooBigToReturnAsFields)
+    @Test(compLevel = CompLevel.C2)
+    public int test83(TooBigToReturnAsFields p) {
+        TooBigToReturnAsFields np = test83_helper(p);
+        return p.a0 + p.a5;
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static TooBigToReturnAsFields test83_helper(TooBigToReturnAsFields p) {
+        return p;
+    }
+
+    @Run(test = "test83")
+    public void test83_verifier() {
+        int result = test83(tooBig);
+        int n = tooBig.a0 + tooBig.a5;
+        Asserts.assertEQ(result, n);
+    }
+
+    // C1->C2 invokestatic with InlineTypeReturnedAsFields (TooBigToReturnAsFields)
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public int test84(TooBigToReturnAsFields p) {
+        TooBigToReturnAsFields np = test84_helper(p);
+        return p.a0 + p.a5;
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C2)
+    private static TooBigToReturnAsFields test84_helper(TooBigToReturnAsFields p) {
+        return p;
+    }
+
+    @Run(test = "test84")
+    public void test84_verifier() {
+        int result = test84(tooBig);
+        int n = tooBig.a0 + tooBig.a5;
+        Asserts.assertEQ(result, n);
+    }
+
+    // Interpreter->C1 invokestatic with InlineTypeReturnedAsFields (TooBigToReturnAsFields)
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public TooBigToReturnAsFields test85(TooBigToReturnAsFields p) {
+        return p;
+    }
+
+    @Run(test = "test85")
+    public void test85_verifier() {
+        TooBigToReturnAsFields p = test85(tooBig);
+        Asserts.assertEQ(p.a0, tooBig.a0);
+        Asserts.assertEQ(p.a2, tooBig.a2);
+    }
+
+    // C1->Interpreter invokestatic with InlineTypeReturnedAsFields (TooBigToReturnAsFields)
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public int test86(TooBigToReturnAsFields p) {
+        TooBigToReturnAsFields np = test86_helper(p);
+        return p.a0 + p.a5;
+    }
+
+    @DontCompile
+    private static TooBigToReturnAsFields test86_helper(TooBigToReturnAsFields p) {
+        return p;
+    }
+
+    @Run(test = "test86")
+    public void test86_verifier() {
+        int result = test86(tooBig);
+        int n = tooBig.a0 + tooBig.a5;
+        Asserts.assertEQ(result, n);
+    }
+
+    //-------------------------------------------------------------------------------
+    // Tests for how C1 handles InlineTypeReturnedAsFields in both calls and returns
+    //-------------------------------------------------------------------------------
+
+    // C2->C1 invokestatic with InlineTypeReturnedAsFields (RefPoint)
+    @Test(compLevel = CompLevel.C2)
+    public RefPoint test87(RefPoint p) {
+        return test87_helper(p);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static RefPoint test87_helper(RefPoint p) {
+        return p;
+    }
+
+    @Run(test = "test87")
+    public void test87_verifier() {
+        Asserts.assertEQ(test87(null), null);
+        Asserts.assertEQ(test87(refPointField1), refPointField1);
+    }
+
+    // C2->C1 invokestatic with InlineTypeReturnedAsFields (RefPoint with constant null)
+    @Test(compLevel = CompLevel.C2)
+    public RefPoint test88() {
+        return test88_helper();
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static RefPoint test88_helper() {
+        return null;
+    }
+
+    @Run(test = "test88")
+    public void test88_verifier() {
+        Asserts.assertEQ(test88(), null);
+    }
+
+    // C1->C2 invokestatic with InlineTypeReturnedAsFields (RefPoint)
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public RefPoint test89(RefPoint p) {
+        return test89_helper(p);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C2)
+    private static RefPoint test89_helper(RefPoint p) {
+        return p;
+    }
+
+    @Run(test = "test89")
+    public void test89_verifier() {
+        Asserts.assertEQ(test89(null), null);
+        Asserts.assertEQ(test89(refPointField1), refPointField1);
+    }
+
+    //----------------------------------------------------------------------------------
+    // Tests for unverified entries: there are 6 cases:
+    // C1 -> Unverified Value Entry compiled by C1
+    // C1 -> Unverified Value Entry compiled by C2
+    // C2 -> Unverified Entry compiled by C1 (target is NOT a value class)
+    // C2 -> Unverified Entry compiled by C2 (target is NOT a value class)
+    // C2 -> Unverified Entry compiled by C1 (target IS a value class, i.e., has VVEP_RO)
+    // C2 -> Unverified Entry compiled by C2 (target IS a value class, i.e., has VVEP_RO)
+    //----------------------------------------------------------------------------------
+
+    // C1->C1 invokeinterface -- call Unverified Value Entry of MyImplPojo1.func2 (compiled by C1)
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public int test90(Intf intf, int a, int b) {
+        return intf.func2(a, b, pointField);
+    }
+
+    static Intf test90_intfs[] = {
+        new MyImplPojo1(),
+        new MyImplPojo2(),
+    };
+
+    @Run(test = "test90")
+    public void test90_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 20;
+        for (int i = 0; i < count; i++) {
+            Intf intf = test90_intfs[i % test90_intfs.length];
+            int result = test90(intf, 123, 456) + i;
+            Asserts.assertEQ(result, intf.func2(123, 456, pointField) + i);
+        }
+    }
+
+    // C1->C2 invokeinterface -- call Unverified Value Entry of MyImplPojo2.func2 (compiled by C2)
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public int test91(Intf intf, int a, int b) {
+        return intf.func2(a, b, pointField);
+    }
+
+    static Intf test91_intfs[] = {
+        new MyImplPojo2(),
+        new MyImplPojo1(),
+    };
+
+    @Run(test = "test91")
+    public void test91_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 20;
+        for (int i = 0; i < count; i++) {
+            Intf intf = test91_intfs[i % test91_intfs.length];
+            int result = test91(intf, 123, 456) + i;
+            Asserts.assertEQ(result, intf.func2(123, 456, pointField) + i);
+        }
+    }
+
+    // C2->C1 invokeinterface -- call Unverified Entry of MyImplPojo1.func2 (compiled by C1)
+    @Test(compLevel = CompLevel.C2)
+    public int test92(Intf intf, int a, int b) {
+        return intf.func2(a, b, pointField);
+    }
+
+    static Intf test92_intfs[] = {
+        new MyImplPojo1(),
+        new MyImplPojo2(),
+    };
+
+    @Run(test = "test92")
+    public void test92_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 20;
+        for (int i = 0; i < count; i++) {
+            Intf intf = test92_intfs[i % test92_intfs.length];
+            int result = test92(intf, 123, 456) + i;
+            Asserts.assertEQ(result, intf.func2(123, 456, pointField) + i);
+        }
+    }
+
+    // C2->C2 invokeinterface -- call Unverified Entry of MyImplPojo2.func2 (compiled by C2)
+    @Test(compLevel = CompLevel.C2)
+    public int test93(Intf intf, int a, int b) {
+        return intf.func2(a, b, pointField);
+    }
+
+    static Intf test93_intfs[] = {
+        new MyImplPojo2(),
+        new MyImplPojo1(),
+    };
+
+    @Run(test = "test93")
+    public void test93_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 20;
+        for (int i = 0; i < count; i++) {
+            Intf intf = test93_intfs[i % test93_intfs.length];
+            int result = test93(intf, 123, 456) + i;
+            Asserts.assertEQ(result, intf.func2(123, 456, pointField) + i);
+        }
+    }
+
+    // C2->C1 invokeinterface -- call Unverified Entry of MyImplVal1.func2 (compiled by C1 - has VVEP_RO)
+    @Test(compLevel = CompLevel.C2)
+    public int test94(Intf intf, int a, int b) {
+        return intf.func2(a, b, pointField);
+    }
+
+    static Intf test94_intfs[] = {
+        new MyImplVal1(),
+        new MyImplVal2(),
+    };
+
+    @Run(test = "test94")
+    public void test94_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 20;
+        for (int i = 0; i < count; i++) {
+            Intf intf = test94_intfs[i % test94_intfs.length];
+            int result = test94(intf, 123, 456) + i;
+            Asserts.assertEQ(result, intf.func2(123, 456, pointField) + i);
+        }
+    }
+
+    // C2->C2 invokeinterface -- call Unverified Entry of MyImplVal2.func2 (compiled by C2 - has VVEP_RO)
+    @Test(compLevel = CompLevel.C2)
+    public int test95(Intf intf, int a, int b) {
+        return intf.func2(a, b, pointField);
+    }
+
+    static Intf test95_intfs[] = {
+        new MyImplVal2(),
+        new MyImplVal1(),
+    };
+
+    @Run(test = "test95")
+    public void test95_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 20;
+        for (int i = 0; i < count; i++) {
+            Intf intf = test95_intfs[i % test95_intfs.length];
+            int result = test95(intf, 123, 456) + i;
+            Asserts.assertEQ(result, intf.func2(123, 456, pointField) + i);
+        }
+    }
+
+    // C1->C2 GC handling in StubRoutines::store_inline_type_fields_to_buf()
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public RefPoint test96(RefPoint rp, boolean b) {
+        RefPoint p = test96_helper(rp);
+        if (b) {
+            return rp;
+        }
+        return p;
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C2)
+    public RefPoint test96_helper(RefPoint rp) {
+        return rp;
+    }
+
+    @Run(test = "test96")
+    public void test96_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 20000; // Do enough iteration to cause GC inside StubRoutines::store_inline_type_fields_to_buf
+        Number x = new Number(10); // old object
+        for (int i = 0; i < count; i++) {
+            Number y = new Number(i); // new object for each iteraton
+            RefPoint rp1 = new RefPoint(x, y);
+            RefPoint rp2 = test96(rp1, info.isWarmUp());
+
+            Asserts.assertEQ(rp1.x, x);
+            Asserts.assertEQ(rp1.y, y);
+            Asserts.assertEQ(rp1.y.n, i);
+        }
+    }
+
+    // C1->C1  - caller is compiled first. It invokes callee(test97) a few times while the
+    //           callee is executed by the interpreter. Then, callee is compiled
+    //           and SharedRuntime::fixup_callers_callsite is called to fix up the
+    //           callsite from test97_verifier->test97.
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public int test97(Point p1, Point p2) {
+        return test97_helper(p1, p2);
+    }
+
+    @DontCompile
+    public int test97_helper(Point p1, Point p2) {
+        return p1.x + p1.y + p2.x + p2.y;
+    }
+
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    public void test97_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 20;
+        for (int i = 0; i < count; i++) {
+            int result = test97(pointField1, pointField2);
+            int n = test97_helper(pointField1, pointField2);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    @Run(test = "test97")
+    public void run_test97_verifier(RunInfo info) {
+        test97_verifier(info);
+    }
+
+    // C1->C2  - same as test97, except the callee is compiled by C2.
+    @Test(compLevel = CompLevel.C2)
+    public int test98(Point p1, Point p2) {
+        return test98_helper(p1, p2);
+    }
+
+    @DontCompile
+    public int test98_helper(Point p1, Point p2) {
+        return p1.x + p1.y + p2.x + p2.y;
+    }
+
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    public void test98_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 20;
+        for (int i = 0; i < count; i++) {
+            int result = test98(pointField1, pointField2);
+            int n = test98_helper(pointField1, pointField2);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    @Run(test = "test98")
+    public void run_test98_verifier(RunInfo info) {
+        test98_verifier(info);
+    }
+
+    // C1->C2  - same as test97, except the callee is a static method.
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public static int test99(Point p1, Point p2) {
+        return test99_helper(p1, p2);
+    }
+
+    @DontCompile
+    public static int test99_helper(Point p1, Point p2) {
+        return p1.x + p1.y + p2.x + p2.y;
+    }
+
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    public void test99_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 20;
+        for (int i = 0; i < count; i++) {
+            int result = test99(pointField1, pointField2);
+            int n = test99_helper(pointField1, pointField2);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    @Run(test = "test99")
+    public void run_test99_verifier(RunInfo info) {
+        test99_verifier(info);
+    }
+
+    // C2->C1 invokestatic, packing causes stack growth (1 extra stack word).
+    // Make sure stack frame is set up properly for GC.
+    @Test(compLevel = CompLevel.C2)
+    public float test100(FloatPoint fp1, FloatPoint fp2, RefPoint rp, int a1, int a2, int a3, int a4) {
+        return test100_helper(fp1, fp2, rp, a1, a2, a3, a4);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C1_SIMPLE)
+    private static float test100_helper(FloatPoint fp1, FloatPoint fp2, RefPoint rp, int a1, int a2, int a3, int a4) {
+        // On x64:
+        //    Scalarized entry -- all parameters are passed in registers
+        //          xmm0 = fp1.x
+        //          xmm1 = fp1.y
+        //          xmm2 = fp2.x
+        //          xmm3 = fp2.y
+        //          rsi  = rp.x  (oop)
+        //          rdx  = rp.y  (oop)
+        //          cx   = a1
+        //          r8   = a2
+        //          r9   = a3
+        //          di   = a4
+        //    Non-scalarized entry -- a6 is passed on stack[0]
+        //          rsi  = fp1
+        //          rdx  = fp2
+        //          rcx  = rp
+        //          r8   = a1
+        //          r9   = a2
+        //          di   = a3
+        //    [sp + ??]  = a4
+        return fp1.x + fp1.y + fp2.x + fp2.y + rp.x.n + rp.y.n + a1 + a2 + a3 + a4;
+    }
+
+    @Run(test = "test100")
+    public void test100_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 4;
+        for (int i = 0; i < count; i++) {
+            FloatPoint fp1 = new FloatPoint(i+0,  i+11);
+            FloatPoint fp2 = new FloatPoint(i+222, i+3333);
+            RefPoint rp = new RefPoint(i+44444, i+555555);
+            float result;
+            try (ForceGCMarker m = ForceGCMarker.mark(info.isWarmUp())) {
+                result = test100(fp1, fp2, rp, 1, 2, 3, 4);
+            }
+            float n = test100_helper(fp1, fp2, rp, 1, 2, 3, 4);
+            Asserts.assertEQ(result, n);
+        }
+    }
+
+    // C1->C2 force GC for every allocation when storing the returned
+    // fields back into a buffered object.
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public RefPoint test101(RefPoint rp) {
+        return test101_helper(rp);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C2)
+    public RefPoint test101_helper(RefPoint rp) {
+        return rp;
+    }
+
+    @Run(test = "test101")
+    public void test101_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 5;
+        for (int i = 0; i < count; i++) {
+            RefPoint rp = new RefPoint(1, 2);
+            Object x = rp.x;
+            Object y = rp.y;
+            RefPoint result = new RefPoint(3, 4);
+            try (ForceGCMarker m = ForceGCMarker.mark(info.isWarmUp())) {
+                result = test101(rp);
+            }
+            Asserts.assertEQ(rp.x, result.x);
+            Asserts.assertEQ(rp.y, result.y);
+            Asserts.assertEQ(x, result.x);
+            Asserts.assertEQ(y, result.y);
+        }
+    }
+
+    // Same as test101, except we have Interpreter->C2 instead.
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public RefPoint test102(RefPoint rp) {
+        return test102_interp(rp);
+    }
+
+    @DontInline
+    public RefPoint test102_interp(RefPoint rp) {
+        return test102_helper(rp);
+    }
+
+    @DontInline
+    @ForceCompile(CompLevel.C2)
+    public RefPoint test102_helper(RefPoint rp) {
+        return rp;
+    }
+
+    @Run(test = "test102")
+    public void test102_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 5;
+        for (int i = 0; i < count; i++) {
+            RefPoint rp = new RefPoint(11, 22);
+            Object x = rp.x;
+            Object y = rp.y;
+            RefPoint result = new RefPoint(333, 444);
+            try (ForceGCMarker m = ForceGCMarker.mark(info.isWarmUp())) {
+                result = test102(rp);
+            }
+            Asserts.assertEQ(rp.x, result.x);
+            Asserts.assertEQ(rp.y, result.y);
+            Asserts.assertEQ(x, result.x);
+            Asserts.assertEQ(y, result.y);
+        }
+    }
+
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public void test103() {
+        // when this method is compiled by C1, the Test103Value class is not yet loaded.
+        test103_v = new Test103Value(); // invokestatic "Test103Value.<init>()QTest103Value;"
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test103Value {
+        int x = rI;
+    }
+
+    static Object test103_v;
+
+    @Run(test = "test103")
+    public void test103_verifier(RunInfo info) {
+        if (info.isWarmUp()) {
+            // Make sure test103() is compiled before Test103Value is loaded
+            return;
+        }
+        test103();
+        Test103Value v = (Test103Value)test103_v;
+        Asserts.assertEQ(v.x, rI);
+    }
+
+
+    // Same as test103, but with a value class that's too big to return as fields.
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public void test104() {
+        // when this method is compiled by C1, the Test104Value class is not yet loaded.
+        test104_v = new Test104Value(); // invokestatic "Test104Value.<init>()QTest104Value;"
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test104Value {
+        long x0 = rL;
+        long x1 = rL;
+        long x2 = rL;
+        long x3 = rL;
+        long x4 = rL;
+        long x5 = rL;
+        long x6 = rL;
+        long x7 = rL;
+        long x8 = rL;
+        long x9 = rL;
+        long xa = rL;
+        long xb = rL;
+        long xc = rL;
+        long xd = rL;
+        long xe = rL;
+        long xf = rL;
+    }
+
+    static Object test104_v;
+
+    @Run(test = "test104")
+    public void test104_verifier(RunInfo info) {
+        if (info.isWarmUp()) {
+            // Make sure test104() is compiled before Test104Value is loaded
+            return;
+        }
+        test104();
+        Test104Value v = (Test104Value)test104_v;
+        Asserts.assertEQ(v.x0, rL);
+    }
+
+    // C2->C1 invokeinterface -- call Unverified Entry of MyImplVal1.func1 (compiled by C1 - has VVEP_RO)
+    /// (same as test94, except we are calling func1, which shares VVEP and VVEP_RO
+    @Test(compLevel = CompLevel.C2)
+    public int test105(Intf intf, int a, int b) {
+        return intf.func1(a, b);
+    }
+
+    static Intf test105_intfs[] = {
+        new MyImplVal1(),
+        new MyImplVal2(),
+    };
+
+    @Run(test = "test105")
+    public void test105_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 20;
+        for (int i = 0; i < count; i++) {
+            Intf intf = test105_intfs[i % test105_intfs.length];
+            int result = test105(intf, 123, 456) + i;
+            Asserts.assertEQ(result, intf.func1(123, 456) + i);
+        }
+    }
+
+    // C2->C2 invokeinterface -- call Unverified Entry of MyImplVal2.func1 (compiled by C2 - has VVEP_RO)
+    /// (same as test95, except we are calling func1, which shares VVEP and VVEP_RO
+    @Test(compLevel = CompLevel.C2)
+    public int test106(Intf intf, int a, int b) {
+        return intf.func1(a, b);
+    }
+
+    static Intf test106_intfs[] = {
+        new MyImplVal2(),
+        new MyImplVal1(),
+    };
+
+    @Run(test = "test106")
+    public void test106_verifier(RunInfo info) {
+        int count = info.isWarmUp() ? 1 : 20;
+        for (int i = 0; i < count; i++) {
+            Intf intf = test106_intfs[i % test106_intfs.length];
+            int result = test106(intf, 123, 456) + i;
+            Asserts.assertEQ(result, intf.func1(123, 456) + i);
+        }
+    }
+
+    // C2->C1 invokeinterface -- C2 calls call Unverified Entry of MyImplVal2X.func1 (compiled by
+    //                           C1, with VVEP_RO==VVEP)
+    // This test is developed to validate JDK-8230325.
+    @Test(compLevel = CompLevel.WAIT_FOR_COMPILATION)
+    public int test107(Intf intf, int a, int b) {
+        return intf.func1(a, b);
+    }
+
+    @ForceCompile
+    public void test107_verifier() {
+        Intf intf1 = new MyImplVal1X();
+        Intf intf2 = new MyImplVal2X();
+
+        for (int i = 0; i < 1000; i++) {
+            test107(intf1, 123, 456);
+        }
+        for (int i = 0; i < 500_000; i++) {
+            // Run enough loops so that test107 will be compiled by C2.
+            if (i % 30 == 0) {
+                // This will indirectly call MyImplVal2X.func1, but the call frequency is low, so
+                // test107 will be compiled by C2, but MyImplVal2X.func1 will compiled by C1 only.
+                int result = test107(intf2, 123, 456) + i;
+                Asserts.assertEQ(result, intf2.func1(123, 456) + i);
+            } else {
+                // Call test107 with a mix of intf1 and intf2, so C2 will use a virtual call (not an optimized call)
+                // for the invokeinterface bytecode in test107.
+                test107(intf1, 123, 456);
+            }
+        }
+    }
+
+    @Run(test = "test107")
+    @Warmup(0)
+    public void run_test107_verifier() {
+        test107_verifier();
+    }
+
+    // Same as test107, except we call MyImplVal2X.func2 (compiled by C1, VVEP_RO != VVEP)
+    @Test(compLevel = CompLevel.WAIT_FOR_COMPILATION)
+    public int test108(Intf intf, int a, int b) {
+        return intf.func2(a, b, pointField);
+    }
+
+    @ForceCompile
+    public void test108_verifier() {
+        Intf intf1 = new MyImplVal1X();
+        Intf intf2 = new MyImplVal2X();
+
+        for (int i = 0; i < 1000; i++) {
+            test108(intf1, 123, 456);
+        }
+        for (int i = 0; i < 500_000; i++) {
+            // Run enough loops so that test108 will be compiled by C2.
+            if (i % 30 == 0) {
+                // This will indirectly call MyImplVal2X.func2, but the call frequency is low, so
+                // test108 will be compiled by C2, but MyImplVal2X.func2 will compiled by C1 only.
+                int result = test108(intf2, 123, 456) + i;
+                Asserts.assertEQ(result, intf2.func2(123, 456, pointField) + i);
+            } else {
+                // Call test108 with a mix of intf1 and intf2, so C2 will use a virtual call (not an optimized call)
+                // for the invokeinterface bytecode in test108.
+                test108(intf1, 123, 456);
+            }
+        }
+    }
+
+    @Run(test = "test108")
+    @Warmup(0)
+    public void run_test108_verifier() {
+        test108_verifier();
+    }
+
+    // Same as test107, except we call MyImplPojo3.func2 (compiled by C1, VVEP_RO == VEP)
+    @Test(compLevel = CompLevel.WAIT_FOR_COMPILATION)
+    public int test109(Intf intf, int a, int b) {
+        return intf.func2(a, b, pointField);
+    }
+
+    @ForceCompile
+    public void test109_verifier() {
+        Intf intf1 = new MyImplPojo0();
+        Intf intf2 = new MyImplPojo3();
+
+        for (int i = 0; i < 1000; i++) {
+            test109(intf1, 123, 456);
+        }
+        for (int i = 0; i < 500_000; i++) {
+            // Run enough loops so that test109 will be compiled by C2.
+            if (i % 30 == 0) {
+                // This will indirectly call MyImplPojo3.func2, but the call frequency is low, so
+                // test109 will be compiled by C2, but MyImplPojo3.func2 will compiled by C1 only.
+                int result = test109(intf2, 123, 456) + i;
+                Asserts.assertEQ(result, intf2.func2(123, 456, pointField) + i);
+            } else {
+                // Call test109 with a mix of intf1 and intf2, so C2 will use a virtual call (not an optimized call)
+                // for the invokeinterface bytecode in test109.
+                test109(intf1, 123, 456);
+            }
+        }
+    }
+
+    @Run(test = "test109")
+    @Warmup(0)
+    public void run_test109_verifier() {
+        test109_verifier();
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCastMismatch.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCastMismatch.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCastMismatch.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2023, 2024, Arm Limited. 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 8309650
+ * @summary Fix mismatch inline type issue during method calls
+ * @library /test/lib
+ * @enablePreview
+ * @run main/othervm -XX:-TieredCompilation -Xcomp
+ *                   compiler.valhalla.inlinetypes.TestCastMismatch
+ */
+
+package compiler.valhalla.inlinetypes;
+
+import java.util.Random;
+import jdk.test.lib.Utils;
+
+public class TestCastMismatch {
+    private static int LOOP_COUNT = 50000;
+
+    private static final Random RD = Utils.getRandomInstance();
+
+    public static MultiValues add(MultiValues v1, MultiValues v2) {
+        return v1.factory(v1.value1() + v2.value1(), v1.value2() + v2.value2());
+    }
+
+    public static void main(String[] args) {
+        Point p1 = new Point(RD.nextInt(), RD.nextInt());
+        Point p2 = new Point(RD.nextInt(), RD.nextInt());
+        for (int i = 0; i < LOOP_COUNT; i++) {
+            p1 = (Point) add(p1, p2);
+        }
+
+        System.out.println("PASS");
+    }
+
+    static abstract value class MultiValues {
+        public abstract int value1();
+        public abstract int value2();
+        public abstract MultiValues factory(int value1, int value2);
+    }
+
+    static value class Point extends MultiValues {
+        private int x;
+        private int y;
+
+        private Point(int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+
+        @Override
+        public int value1() {
+            return x;
+        }
+
+        @Override
+        public int value2() {
+            return y;
+        }
+
+        @Override
+        public Point factory(int value1, int value2) {
+            return new Point(value1, value2);
+        }
+    }
+}
+
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestDeadAllocationRemoval.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestDeadAllocationRemoval.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestDeadAllocationRemoval.java
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ *
+ * 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 8230397
+ * @summary Test removal of an already dead AllocateNode with not-yet removed proj outputs.
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm -Xbatch TestDeadAllocationRemoval
+ */
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+public class TestDeadAllocationRemoval {
+
+    public static void main(String[] args) {
+        Test test = new Test();
+        for (int i = 0; i < 10; ++i) {
+            test.test();
+        }
+    }
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue {
+    public static long instanceCount = 0;
+    public float fFld = 0;
+    public boolean bFld = true;
+    public float fFld1 = 0;
+}
+
+class Test {
+    public static final int N = 400;
+
+    public static long instanceCount=2149450457L;
+    public static double dFld=63.1805;
+    public static boolean bFld1=false;
+    public static int iFld=-4;
+    public static double dArrFld[]=new double[N];
+    public static int iArrFld[]=new int[N];
+    @NullRestricted
+    public static MyValue OFld=new MyValue();
+
+    public static long vMeth_check_sum = 0;
+    public static long lMeth_check_sum = 0;
+
+    public void vMeth(int i) {
+        for (double d = 8; d < 307; d++) {
+            for (int i3 = 1; i3 < 6; i3 += 2) {
+                i <<= -23877;
+            }
+        }
+    }
+
+    public void test() {
+        int i21=-35918, i22=11, i23=31413, i24=-7, i25=0, i26=70;
+        double d3=0.122541;
+
+        vMeth(Test.iFld);
+        for (i21 = 20; i21 < 396; ++i21) {
+            d3 = 1;
+            while (++d3 < 67) {
+                byte by=38;
+                Test.dArrFld[(int)(d3)] = -7;
+                switch ((i21 % 9) + 1) {
+                case 1:
+                    for (i23 = 1; i23 < 1; i23 += 3) {
+                        Test.instanceCount = i22;
+                        Test.iFld -= (int)Test.OFld.fFld1;
+                        Test.instanceCount >>= MyValue.instanceCount;
+                        i22 = (int)Test.OFld.fFld1;
+                        Test.bFld1 = false;
+                        Test.iArrFld[(int)(d3 - 1)] &= i23;
+                        i22 += (i23 + i24);
+                        i22 -= (int)d3;
+                        Test.iFld |= (int)MyValue.instanceCount;
+                    }
+                    Test.iFld -= (int)Test.instanceCount;
+                    break;
+                case 2:
+                    for (i25 = 1; i25 < 1; i25++) {
+                        i26 += i22;
+                        i26 += i25;
+                        Test.iArrFld[i25 + 1] += (int)MyValue.instanceCount;
+                        i22 += (i25 - Test.instanceCount);
+                        i26 += (i25 + i21);
+                    }
+                    Test.instanceCount -= 2;
+                    Test.dFld = i22;
+                    Test.iFld += (int)(((d3 * by) + by) - i24);
+                    break;
+                case 3:
+                    i24 = (int)1.84829;
+                    Test.OFld = new MyValue();
+                    break;
+                case 4:
+                    Test.OFld = new MyValue();
+                    MyValue.instanceCount += (long)d3;
+                    break;
+                case 5:
+                    MyValue.instanceCount += (long)(d3 * d3);
+                    break;
+                case 6:
+                    Test.dFld -= i25;
+                case 7:
+                    try {
+                        i24 = (78 / Test.iFld);
+                        Test.iFld = (-5836 / Test.iArrFld[(int)(d3 + 1)]);
+                        i24 = (i23 / -205);
+                    } catch (ArithmeticException a_e) {}
+                    break;
+                case 8:
+                    if (Test.bFld1) continue;
+                case 9:
+                default:
+                    try {
+                        i26 = (i24 / -929688879);
+                        i24 = (Test.iArrFld[(int)(d3)] % -1067487586);
+                        Test.iArrFld[(int)(d3)] = (-208 % i24);
+                    } catch (ArithmeticException a_e) {}
+                }
+            }
+        }
+
+        System.out.println("i21 i22 d3 = " + i21 + "," + i22 + "," + Double.doubleToLongBits(d3));
+        System.out.println("i23 i24 Test.OFld.fFld1 = " + i23 + "," + i24 + "," + Float.floatToIntBits(Test.OFld.fFld1));
+        System.out.println("MyValue = " + MyValue.instanceCount);
+        System.out.println("Test.instanceCount Test.dFld Test.bFld1 = " + Test.instanceCount + "," + Double.doubleToLongBits(Test.dFld) + "," + (Test.bFld1 ? 1 : 0));
+        System.out.println("MyValue = " + MyValue.instanceCount);
+        System.out.println("lMeth_check_sum: " + lMeth_check_sum);
+        System.out.println("vMeth_check_sum: " + vMeth_check_sum);
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestDeoptimizationWhenBuffering.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestDeoptimizationWhenBuffering.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestDeoptimizationWhenBuffering.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ *
+ * 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 compiler.valhalla.inlinetypes;
+
+import java.lang.invoke.*;
+import java.lang.reflect.Method;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import jdk.test.lib.Asserts;
+
+import jdk.test.whitebox.WhiteBox;
+
+/**
+ * @test TestDeoptimizationWhenBuffering
+ * @summary Test correct execution after deoptimizing from inline type specific runtime calls.
+ * @library /testlibrary /test/lib /compiler/whitebox /
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @build org.openjdk.asmtools.* org.openjdk.asmtools.jasm.*
+ * @build jdk.test.whitebox.WhiteBox
+ * @enablePreview
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:+DeoptimizeALot -XX:CompileCommand=dontinline,compiler.valhalla.inlinetypes.*::test*
+ *                   compiler.valhalla.inlinetypes.TestDeoptimizationWhenBuffering C1
+ * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:+DeoptimizeALot -XX:-UseTLAB -Xbatch
+ *                   compiler.valhalla.inlinetypes.TestDeoptimizationWhenBuffering
+ * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:+DeoptimizeALot -XX:-UseTLAB -Xbatch -XX:-MonomorphicArrayCheck -XX:-AlwaysIncrementalInline
+ *                   -XX:-InlineTypePassFieldsAsArgs -XX:-InlineTypeReturnedAsFields -XX:FlatArrayElementMaxSize=1
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.inlinetypes.*::test*
+ *                   compiler.valhalla.inlinetypes.TestDeoptimizationWhenBuffering
+ * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:+DeoptimizeALot -XX:-UseTLAB -Xbatch -XX:-MonomorphicArrayCheck -XX:+AlwaysIncrementalInline
+ *                   -XX:-InlineTypePassFieldsAsArgs -XX:-InlineTypeReturnedAsFields -XX:FlatArrayElementMaxSize=1
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.inlinetypes.*::test*
+ *                   compiler.valhalla.inlinetypes.TestDeoptimizationWhenBuffering
+ * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:+DeoptimizeALot -XX:-UseTLAB -Xbatch -XX:-MonomorphicArrayCheck -XX:-AlwaysIncrementalInline
+ *                   -XX:+InlineTypePassFieldsAsArgs -XX:+InlineTypeReturnedAsFields -XX:FlatArrayElementMaxSize=-1
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.inlinetypes.*::test*
+ *                   compiler.valhalla.inlinetypes.TestDeoptimizationWhenBuffering
+ * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:+DeoptimizeALot -XX:-UseTLAB -Xbatch -XX:-MonomorphicArrayCheck -XX:+AlwaysIncrementalInline
+ *                   -XX:+InlineTypePassFieldsAsArgs -XX:+InlineTypeReturnedAsFields -XX:FlatArrayElementMaxSize=-1
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.inlinetypes.*::test*
+ *                   compiler.valhalla.inlinetypes.TestDeoptimizationWhenBuffering
+ * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:+DeoptimizeALot -XX:-UseTLAB -Xbatch -XX:-MonomorphicArrayCheck -XX:-AlwaysIncrementalInline
+ *                   -XX:+InlineTypePassFieldsAsArgs -XX:+InlineTypeReturnedAsFields -XX:FlatArrayElementMaxSize=-1 -XX:InlineFieldMaxFlatSize=0
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.inlinetypes.*::test*
+ *                   compiler.valhalla.inlinetypes.TestDeoptimizationWhenBuffering
+ * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:+DeoptimizeALot -XX:-UseTLAB -Xbatch -XX:-MonomorphicArrayCheck -XX:+AlwaysIncrementalInline
+ *                   -XX:+InlineTypePassFieldsAsArgs -XX:+InlineTypeReturnedAsFields -XX:FlatArrayElementMaxSize=-1 -XX:InlineFieldMaxFlatSize=0
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.inlinetypes.*::test*
+ *                   compiler.valhalla.inlinetypes.TestDeoptimizationWhenBuffering
+ */
+
+public class TestDeoptimizationWhenBuffering {
+    static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+    static final int COMP_LEVEL_FULL_OPTIMIZATION = 4; // C2 or JVMCI
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue1 {
+        static int cnt = 0;
+        int x;
+        @NullRestricted
+        MyValue2 vtField1;
+        MyValue2 vtField2;
+
+        public MyValue1() {
+            cnt++;
+            x = cnt;
+            vtField1 = new MyValue2();
+            vtField2 = new MyValue2();
+        }
+
+        public MyValue1(int x, MyValue2 vtField1, MyValue2 vtField2) {
+            this.x = x;
+            this.vtField1 = vtField1;
+            this.vtField2 = vtField2;
+        }
+
+        public int hash() {
+            return x + vtField1.x + vtField2.x;
+        }
+
+        public MyValue1 testWithField(int x) {
+            return new MyValue1(x, vtField1, vtField2);
+        }
+
+        public static MyValue1 makeDefault() {
+            return new MyValue1(0, MyValue2.makeDefault(), null);
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue2 {
+        static int cnt = 0;
+        int x;
+
+        public MyValue2() {
+            cnt++;
+            x = cnt;
+        }
+
+        public MyValue2(int x) {
+            this.x = x;
+        }
+
+        public static MyValue2 makeDefault() {
+            return new MyValue2(0);
+        }
+    }
+
+    static {
+        try {
+            Class<?> clazz = TestDeoptimizationWhenBuffering.class;
+            MethodHandles.Lookup lookup = MethodHandles.lookup();
+
+            MethodType mt = MethodType.methodType(MyValue1.class);
+            test9_mh = lookup.findStatic(clazz, "test9Callee", mt);
+            test10_mh = lookup.findStatic(clazz, "test10Callee", mt);
+        } catch (NoSuchMethodException | IllegalAccessException e) {
+            e.printStackTrace();
+            throw new RuntimeException("Method handle lookup failed");
+        }
+    }
+
+    MyValue1 test1() {
+        return new MyValue1();
+    }
+
+    @NullRestricted
+    static MyValue1 vtField1;
+
+    MyValue1 test2() {
+        vtField1 = new MyValue1();
+        return vtField1;
+    }
+
+    public int test3Callee(MyValue1 vt) {
+        return vt.hash();
+    }
+
+    int test3() {
+        MyValue1 vt = new MyValue1();
+        return test3Callee(vt);
+    }
+
+    static MyValue1[] vtArray = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+
+    MyValue1 test4() {
+        vtArray[0] = new MyValue1();
+        return vtArray[0];
+    }
+
+    Object test5(Object[] array) {
+        array[0] = new MyValue1();
+        return array[0];
+    }
+
+    boolean test6(Object obj) {
+        MyValue1 vt = new MyValue1();
+        return vt == obj;
+    }
+
+    Object test7(Object[] obj) {
+        return obj[0];
+    }
+
+    MyValue1 test8(MyValue1[] obj) {
+        return obj[0];
+    }
+
+    static final MethodHandle test9_mh;
+
+    public static MyValue1 test9Callee() {
+        return new MyValue1();
+    }
+
+    MyValue1 test9() throws Throwable {
+        return (MyValue1)test9_mh.invokeExact();
+    }
+
+    static final MethodHandle test10_mh;
+    @NullRestricted
+    static final MyValue1 test10Field = new MyValue1();
+    static int test10Counter = 0;
+
+    public static MyValue1 test10Callee() {
+        test10Counter++;
+        return test10Field;
+    }
+
+    Object test10() throws Throwable {
+        return test10_mh.invoke();
+    }
+
+    MyValue1 test11(MyValue1 vt) {
+        return vt.testWithField(42);
+    }
+
+    MyValue1 vtField2;
+
+    MyValue1 test12() {
+        vtField2 = new MyValue1();
+        return vtField2;
+    }
+
+    public static void main(String[] args) throws Throwable {
+        if (args.length > 0) {
+            // Compile callees with C1 only, to exercise deoptimization while buffering at method entry
+            Asserts.assertEQ(args[0], "C1", "unsupported mode");
+            Method m = MyValue1.class.getMethod("testWithField", int.class);
+            WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_FULL_OPTIMIZATION, false);
+            m = TestDeoptimizationWhenBuffering.class.getMethod("test3Callee", MyValue1.class);
+            WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_FULL_OPTIMIZATION, false);
+            m = TestDeoptimizationWhenBuffering.class.getMethod("test9Callee");
+            WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_FULL_OPTIMIZATION, false);
+            m = TestDeoptimizationWhenBuffering.class.getMethod("test10Callee");
+            WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_FULL_OPTIMIZATION, false);
+        }
+
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 3);
+        va[0] = new MyValue1();
+        Object[] oa = new Object[3];
+        oa[0] = va[0];
+        TestDeoptimizationWhenBuffering t = new TestDeoptimizationWhenBuffering();
+        for (int i = 0; i < 100_000; ++i) {
+            // Check counters to make sure that we don't accidentially reexecute calls when deoptimizing
+            int expected = MyValue1.cnt + MyValue2.cnt + MyValue2.cnt;
+            Asserts.assertEQ(t.test1().hash(), expected + 4);
+            vtField1 = MyValue1.makeDefault();
+            Asserts.assertEQ(t.test2().hash(), expected + 9);
+            Asserts.assertEQ(vtField1.hash(), expected + 9);
+            Asserts.assertEQ(t.test3(), expected + 14);
+            Asserts.assertEQ(t.test4().hash(), expected + 19);
+            Asserts.assertEQ(((MyValue1)t.test5(vtArray)).hash(), expected + 24);
+            Asserts.assertEQ(t.test6(vtField1), false);
+            Asserts.assertEQ(t.test7(((i % 2) == 0) ? va : oa), va[0]);
+            Asserts.assertEQ(t.test8(va), va[0]);
+            Asserts.assertEQ(t.test8(va), va[0]);
+            Asserts.assertEQ(t.test9().hash(), expected + 34);
+            int count = test10Counter;
+            Asserts.assertEQ(((MyValue1)t.test10()).hash(), test10Field.hash());
+            Asserts.assertEQ(t.test10Counter, count + 1);
+            Asserts.assertEQ(t.test11(va[0]).hash(), va[0].testWithField(42).hash());
+            t.vtField2 = MyValue1.makeDefault();
+            Asserts.assertEQ(t.test12().hash(), expected + 39);
+            Asserts.assertEQ(t.vtField2.hash(), expected + 39);
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestFlatArrayAliasesCardMark.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestFlatArrayAliasesCardMark.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestFlatArrayAliasesCardMark.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2021, Red Hat, Inc. 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.
+ *
+ * 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 8260363
+ * @summary C2 compilation fails with assert(n->Opcode() != Op_Phi) failed: cannot match
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm -XX:-BackgroundCompilation TestFlatArrayAliasesCardMark
+ */
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class Test0 {
+    int x = 42;
+    short[] array = new short[7];
+}
+
+public class TestFlatArrayAliasesCardMark {
+    int f = 1;
+
+    public void method1(Test0[] array) {
+        for (int i = 0; i < 100; ++i) {
+            array[0] = array[0];
+            for (int j = 0; j < 10; ++j) {
+                for (int k = 0; k < 10; ++k) {
+                    f = 42;
+                }
+            }
+        }
+    }
+
+    public static void main(String[] args) {
+        TestFlatArrayAliasesCardMark t = new TestFlatArrayAliasesCardMark();
+        Test0[] array = (Test0[])ValueClass.newNullRestrictedArray(Test0.class, 1);
+        array[0] = new Test0();
+
+        for (int l1 = 0; l1 < 10_000; ++l1) {
+            t.method1(array);
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestFlatArrayThreshold.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestFlatArrayThreshold.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestFlatArrayThreshold.java
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ *
+ * 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 accessing value class arrays that exceed the flattening threshold.
+ * @library /test/lib
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm -Xbatch
+ *                   TestFlatArrayThreshold
+ * @run main/othervm -XX:FlatArrayElementMaxOops=1 -Xbatch
+ *                   TestFlatArrayThreshold
+ * @run main/othervm -XX:FlatArrayElementMaxSize=1 -Xbatch
+ *                   TestFlatArrayThreshold
+
+ */
+
+import jdk.test.lib.Asserts;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue1 {
+    Object o1;
+    Object o2;
+
+    public MyValue1() {
+        o1 = new Integer(42);
+        o2 = new Integer(43);
+    }
+}
+
+public class TestFlatArrayThreshold {
+
+    public static MyValue1 test1(MyValue1[] va, MyValue1 vt) {
+        va[0] = vt;
+        return va[1];
+    }
+
+    public static MyValue1 test2(MyValue1[] va, MyValue1 vt) {
+        va[0] = vt;
+        return va[1];
+    }
+
+    public static Object test3(Object[] va, MyValue1 vt) {
+        va[0] = vt;
+        return va[1];
+    }
+
+    public static Object test4(Object[] va, MyValue1 vt) {
+        va[0] = vt;
+        return va[1];
+    }
+
+    public static MyValue1 test5(MyValue1[] va, Object vt) {
+        va[0] = (MyValue1)vt;
+        return va[1];
+    }
+
+    public static MyValue1 test6(MyValue1[] va, Object vt) {
+        va[0] = (MyValue1)vt;
+        return va[1];
+    }
+
+    public static Object test7(Object[] va, Object vt) {
+        va[0] = vt;
+        return va[1];
+    }
+
+    static public void main(String[] args) {
+        MyValue1 vt = new MyValue1();
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        MyValue1[] vaB = new MyValue1[2];
+        va[1] = vt;
+        for (int i = 0; i < 10_000; ++i) {
+            MyValue1 result1 = test1(va, vt);
+            Asserts.assertEQ(result1.o1, 42);
+            Asserts.assertEQ(result1.o2, 43);
+
+            MyValue1 result2 = test2(va, vt);
+            Asserts.assertEQ(result2.o1, 42);
+            Asserts.assertEQ(result2.o2, 43);
+            result2 = test2(vaB, null);
+            Asserts.assertEQ(result2, null);
+
+            MyValue1 result3 = (MyValue1)test3(va, vt);
+            Asserts.assertEQ(result3.o1, 42);
+            Asserts.assertEQ(result3.o2, 43);
+            result3 = (MyValue1)test3(vaB, vt);
+            Asserts.assertEQ(result3, null);
+
+            MyValue1 result4 = (MyValue1)test4(va, vt);
+            Asserts.assertEQ(result4.o1, 42);
+            Asserts.assertEQ(result4.o2, 43);
+            result4 = (MyValue1)test4(vaB, null);
+            Asserts.assertEQ(result4, null);
+
+            MyValue1 result5 = test5(va, vt);
+            Asserts.assertEQ(result5.o1, 42);
+            Asserts.assertEQ(result5.o2, 43);
+
+            MyValue1 result6 = test6(va, vt);
+            Asserts.assertEQ(result6.o1, 42);
+            Asserts.assertEQ(result6.o2, 43);
+            result6 = test6(vaB, null);
+            Asserts.assertEQ(result6, null);
+
+            MyValue1 result7 = (MyValue1)test7(va, vt);
+            Asserts.assertEQ(result7.o1, 42);
+            Asserts.assertEQ(result7.o2, 43);
+            result7 = (MyValue1)test7(vaB, null);
+            Asserts.assertEQ(result7, null);
+        }
+        try {
+            test2(va, null);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException npe) {
+            // Expected
+        }
+        try {
+            test4(va, null);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException npe) {
+            // Expected
+        }
+        try {
+            test5(va, null);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException npe) {
+            // Expected
+        }
+        try {
+            test6(va, null);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException npe) {
+            // Expected
+        }
+        try {
+            test7(va, null);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException npe) {
+            // Expected
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestFlatInArraysFolding.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestFlatInArraysFolding.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestFlatInArraysFolding.java
@@ -0,0 +1,239 @@
+/*
+ * 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=serialgc
+ * @bug 8321734
+ * @requires vm.gc.Serial
+ * @summary Test that CmpPNode::sub and SubTypeCheckNode::sub correctly identify unrelated classes based on the flat
+ *          in array property of the types. Additionally check that the type system properly handles the case of a
+ *          super class being flat in array while the sub klass could be flat in array.
+ * @library /test/lib /
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main compiler.valhalla.inlinetypes.TestFlatInArraysFolding serial
+ */
+
+/*
+ * @test
+ * @bug 8321734
+ * @summary Test that CmpPNode::sub and SubTypeCheckNode::sub correctly identify unrelated classes based on the flat
+ *          in array property of the types. Additionally check that the type system properly handles the case of a
+ *          super class being flat in array while the sub klass could be flat in array.
+ * @library /test/lib /
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main compiler.valhalla.inlinetypes.TestFlatInArraysFolding
+ */
+
+package compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.*;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+
+public class TestFlatInArraysFolding {
+    static Object[] oArrArr = new Object[100][100];
+    static Object[] oArr = new Object[100];
+    static Object o = new Object();
+
+    // Make sure these are loaded such that A has a flat in array and a not flat in array sub class.
+    static FlatInArray flat = new FlatInArray(34);
+    static NotFlatInArray notFlat = new NotFlatInArray(34);
+
+    // Make sure PUnique is the unique concrete sub class loaded from AUnique.
+    static PUnique pUnique = new PUnique(34);
+
+    static int iFld;
+
+    public static void main(String[] args) {
+        Scenario flatArrayElementMaxSize1Scenario = new Scenario(1, "-XX:FlatArrayElementMaxSize=1");
+        Scenario flatArrayElementMaxSize4Scenario = new Scenario(2, "-XX:FlatArrayElementMaxSize=4");
+        Scenario noFlagsScenario = new Scenario(3);
+        TestFramework testFramework = new TestFramework();
+        testFramework.setDefaultWarmup(0)
+                .addFlags("--enable-preview",
+                          "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED",
+                          "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED")
+                .addScenarios(flatArrayElementMaxSize1Scenario,
+                              flatArrayElementMaxSize4Scenario, noFlagsScenario);
+
+        if (args.length > 0) {
+            // Disable Loop Unrolling for IR matching in testCmpP().
+            // Use IgnoreUnrecognizedVMOptions since LoopMaxUnroll is a C2 flag.
+            // testSubTypeCheck() only triggers with SerialGC.
+            Scenario serialGCScenario = new Scenario(4, "-XX:+UseSerialGC", "-XX:+IgnoreUnrecognizedVMOptions",
+                                                     "-XX:LoopMaxUnroll=0");
+            testFramework.addScenarios(serialGCScenario);
+        }
+        testFramework.start();
+    }
+
+    // SubTypeCheck is not folded while CheckCastPPNode is replaced by top which results in a bad graph (data dies while
+    // control does not).
+    @Test
+    static void testSubTypeCheck() {
+        for (int i = 0; i < 100; i++) {
+            Object arrayElement = oArrArr[i];
+            oArr = (Object[])arrayElement;
+        }
+    }
+
+    @Test
+    @IR(counts = {IRNode.COUNTED_LOOP, "2", // Loop Unswitching done?
+                  IRNode.STORE_I, "1"}, // CmpP folded in unswitched loop version with flat in array?
+        applyIf = {"LoopMaxUnroll", "0"})
+    static void testCmpP() {
+        for (int i = 0; i < 100; i++) {
+            Object arrayElement = oArrArr[i];
+            if (arrayElement == oArr) {
+                iFld = 34;
+            }
+        }
+    }
+
+    // Type system does not correctly handle the case that a super klass is flat in array while the sub klass is
+    // maybe flat in array. This leads to a bad graph.
+    @Test
+    static void testUnswitchingAbstractClass() {
+        Object[] arr = oArr;
+        for (int i = 0; i < 100; i++) {
+            Object arrayElement = arr[i];
+            if (arrayElement instanceof A) {
+                A a = (A)arrayElement;
+                if (a == o) {
+                    a.foo();
+                }
+            }
+        }
+    }
+
+    // Same as testUnswitchingAbstractClass() but with interfaces. This worked before because I has type Object(I)
+    // from which Object is a sub type of.
+    @Test
+    static void testUnswitchingInterface() {
+        Object[] arr = oArr;
+        for (int i = 0; i < 100; i++) {
+            Object arrayElement = arr[i];
+            if (arrayElement instanceof I) {
+                I iVar = (I)arrayElement;
+                if (iVar == o) {
+                    iVar.bar();
+                }
+            }
+        }
+    }
+
+    // PUnique is the unique concrete sub class of AUnique and is not flat in array (with FlatArrayElementMaxSize=4).
+    // The CheckCastPP output of the sub type check uses PUnique while the sub type check itself uses AUnique. This leads
+    // to a bad graph because the type system determines that the flat in array super klass cannot be met with the
+    // not flat in array sub klass. But the sub type check does not fold away because AUnique *could* be flat in array.
+    // Fixed with in JDK-8328480 in mainline but not yet merged in. Applied manually to make this work.
+    @Test
+    static void testSubTypeCheckNotFoldedParsingAbstractClass() {
+        Object[] arr = oArr;
+        for (int i = 0; i < 100; i++) {
+            Object arrayElement = arr[i];
+            if (arrayElement instanceof AUnique) {
+                AUnique aUnique = (AUnique)arrayElement;
+                if (aUnique == o) {
+                    aUnique.foo();
+                }
+            }
+        }
+    }
+
+    // Same as testSubTypeCheckNotFoldedParsingAbstractClass() but with interfaces. This worked before because IUnique
+    // has type Object(IUnique) from which Object is a sub type of.
+    @Test
+    static void testSubTypeCheckNotFoldedParsingInterface() {
+        Object[] arr = oArr;
+        for (int i = 0; i < 100; i++) {
+            Object arrayElement = arr[i];
+            if (arrayElement instanceof IUnique) {
+                IUnique iUnique = (IUnique)arrayElement;
+                if (iUnique == o) {
+                    iUnique.bar();
+                }
+            }
+        }
+    }
+
+    interface IUnique {
+        abstract void bar();
+    }
+
+    static abstract value class AUnique implements IUnique {
+        abstract void foo();
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class PUnique extends AUnique {
+        int x;
+        int y;
+        PUnique(int x) {
+            this.x = x;
+            this.y = 34;
+        }
+
+        public void foo() {}
+        public void bar() {}
+    }
+
+    interface I {
+        void bar();
+    }
+
+    static abstract value class A implements I {
+        abstract void foo();
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class FlatInArray extends A implements I {
+        int x;
+        FlatInArray(int x) {
+            this.x = x;
+        }
+
+        public void foo() {}
+        public void bar() {}
+    }
+
+    // Not flat in array with -XX:FlatArrayElementMaxSize=4
+    static value class NotFlatInArray extends A implements I {
+        int x;
+        int y;
+        NotFlatInArray(int x) {
+            this.x = x;
+            this.y = 34;
+        }
+
+        public void foo() {}
+        public void bar() {}
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestGenerated.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestGenerated.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestGenerated.java
@@ -0,0 +1,363 @@
+/*
+ * 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.
+ *
+ * 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 8260034 8260225 8260283 8261037 8261874 8262128 8262831 8306986
+ * @summary A selection of generated tests that triggered bugs not covered by other tests.
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm -Xbatch
+ *                   compiler.valhalla.inlinetypes.TestGenerated
+ * @run main/othervm -Xbatch -XX:FlatArrayElementMaxSize=0
+ *                   compiler.valhalla.inlinetypes.TestGenerated
+ */
+
+package compiler.valhalla.inlinetypes;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class EmptyPrimitive {
+
+}
+
+value class EmptyValue {
+
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue1 {
+    int x = 42;
+    int[] array = new int[1];
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue2 {
+    int[] a = new int[1];
+    int[] b = new int[6];
+    int[] c = new int[5];
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue3 {
+    int[] intArray = new int[1];
+    float[] floatArray = new float[1];
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue4 {
+    short b = 2;
+    int c = 8;
+}
+
+class MyValue4Wrapper {
+    public MyValue4 val;
+
+    public MyValue4Wrapper(MyValue4 val) {
+        this.val = val;
+    }
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue5 {
+    int b = 2;
+}
+
+value class MyValue6 {
+    int x = 42;
+}
+
+public class TestGenerated {
+    @NullRestricted
+    EmptyPrimitive f1 = new EmptyPrimitive();
+    @NullRestricted
+    EmptyPrimitive f2 = new EmptyPrimitive();
+
+    void test1(EmptyPrimitive[] array) {
+        for (int i = 0; i < 10; ++i) {
+            f1 = array[0];
+            f2 = array[0];
+        }
+    }
+
+    MyValue1 test2(MyValue1[] array) {
+        MyValue1 res = new MyValue1();
+        for (int i = 0; i < array.length; ++i) {
+            res = array[i];
+        }
+        for (int i = 0; i < 1000; ++i) {
+
+        }
+        return res;
+    }
+
+    void test3(MyValue1[] array) {
+        for (int i = 0; i < array.length; ++i) {
+            array[i] = new MyValue1();
+        }
+        for (int i = 0; i < 1000; ++i) {
+
+        }
+    }
+
+    void test4(MyValue1[] array) {
+        array[0].array[0] = 0;
+    }
+
+    int test5(MyValue1[] array) {
+        return array[0].array[0];
+    }
+
+    long f3;
+    @NullRestricted
+    MyValue1 f4 = new MyValue1();
+
+    void test6() {
+        f3 = 123L;
+        int res = f4.x;
+        if (res != 42) {
+            throw new RuntimeException("test6 failed");
+        }
+    }
+
+    @NullRestricted
+    MyValue2 f5;
+
+    void test7(boolean b) {
+        MyValue2[] array1 = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 6);
+        array1[0] = new MyValue2();
+        array1[1] = new MyValue2();
+        array1[2] = new MyValue2();
+        array1[3] = new MyValue2();
+        array1[4] = new MyValue2();
+        array1[5] = new MyValue2();
+
+        MyValue2 h = new MyValue2();
+        MyValue2 n = new MyValue2();
+        int[] array2 = new int[1];
+
+        for (int i = 0; i < 10; ++i) {
+          for (int j = 0; j < 10; ++j) {
+            array1[0] = array1[0];
+            if (i == 1) {
+              h = h;
+              array2[0] *= 42;
+            }
+          }
+        }
+        if (b) {
+          f5 = n;
+        }
+    }
+
+    boolean test8(MyValue1[] array) {
+        return array[0].array == array[0].array;
+    }
+
+    void test9(boolean b) {
+        MyValue1[] array = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        if (b) {
+            for (int i = 0; i < 10; ++i) {
+                if (array != array) {
+                    array = null;
+                }
+            }
+        }
+    }
+
+    int[] f6 = new int[1];
+
+    void test10(MyValue3[] array) {
+        float[] floatArray = array[0].floatArray;
+        if (f6 == f6) {
+            f6 = array[0].intArray;
+        }
+    }
+
+    void test11(MyValue3[] array) {
+        float[] floatArray = array[0].floatArray;
+        if (array[0].intArray[0] != 42) {
+            throw new RuntimeException("test11 failed");
+        }
+    }
+
+    MyValue4[] d = (MyValue4[])ValueClass.newNullRestrictedArray(MyValue4.class, 1);
+    @NullRestricted
+    MyValue4 e;
+    byte f;
+
+    byte test12() {
+        MyValue4 i = new MyValue4();
+        for (int j = 0; j < 6; ++j) {
+            MyValue4[] k = (MyValue4[])ValueClass.newNullRestrictedArray(MyValue4.class, 0);
+            if (i.b < 101) {
+                i = e;
+            }
+            for (int l = 0; l < 9; ++l) {
+                MyValue4 m = new MyValue4();
+                i = m;
+            }
+        }
+        if (d[0].c > 1) {
+            for (int n = 0; n < 7; ++n) {
+            }
+        }
+        return f;
+    }
+
+    int test13_iField;
+    @NullRestricted
+    MyValue5 test13_c;
+    @NullRestricted
+    MyValue5 test13_t;
+
+    void test13(MyValue5[] array) {
+        for (int i = 0; i < 10; ++i) {
+            for (int j = 0; j < 10; ++j) {
+                test13_iField = 6;
+            }
+            for (int j = 0; j < 2; ++j) {
+                test13_iField += array[0].b;
+            }
+            MyValue5[] array2 = (MyValue5[])ValueClass.newNullRestrictedArray(MyValue5.class, 1);
+            test13_c = array[0];
+            array2[0] = test13_t;
+        }
+    }
+
+    void test14(boolean b, MyValue4 val) {
+        for (int i = 0; i < 10; ++i) {
+            if (b) {
+                val = new MyValue4();
+            }
+            MyValue4[] array = (MyValue4[])ValueClass.newNullRestrictedArray(MyValue4.class, 1);
+            array[0] = val;
+
+            for (int j = 0; j < 5; ++j) {
+                for (int k = 0; k < 5; ++k) {
+                }
+            }
+        }
+    }
+
+    void test15() {
+        MyValue4 val = new MyValue4();
+        for (int i = 0; i < 10; ++i) {
+            for (int j = 0; j < 10; ++j) {
+                MyValue4[] array = (MyValue4[])ValueClass.newNullRestrictedArray(MyValue4.class, 1);
+                for (int k = 0; k < 10; ++k) {
+                    array[0] = val;
+                    val = array[0];
+                }
+            }
+        }
+    }
+
+    void test16() {
+        MyValue4 val = new MyValue4();
+        for (int i = 0; i < 10; ++i) {
+            for (int j = 0; j < 10; ++j) {
+                val = (new MyValue4Wrapper(val)).val;
+                for (int k = 0; k < 10; ++k) {
+                }
+            }
+        }
+    }
+
+    static MyValue6 test17Field = new MyValue6();
+
+    void test17() {
+        for (int i = 0; i < 10; ++i) {
+            MyValue6 val = new MyValue6();
+            for (int j = 0; j < 10; ++j) {
+                test17Field = val;
+            }
+        }
+    }
+
+    EmptyValue test18Field;
+
+    EmptyValue test18() {
+        EmptyValue val = new EmptyValue();
+        test18Field = val;
+        return test18Field;
+    }
+
+    @NullRestricted
+    MyValue1 test19Field = new MyValue1();
+
+    public void test19() {
+        for (int i = 0; i < 10; ++i) {
+            MyValue1 val = new MyValue1();
+            for (int j = 0; j < 10; ++j)
+                test19Field = val;
+        }
+    }
+
+    public static void main(String[] args) {
+        TestGenerated t = new TestGenerated();
+        EmptyPrimitive[] array1 = (EmptyPrimitive[])ValueClass.newNullRestrictedArray(EmptyPrimitive.class, 1);
+        MyValue1[] array2 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 10);
+        MyValue1[] array3 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        array3[0] = new MyValue1();
+        MyValue3[] array4 = (MyValue3[])ValueClass.newNullRestrictedArray(MyValue3.class, 1);
+        array4[0] = new MyValue3();
+        MyValue5[] array5 = (MyValue5[])ValueClass.newNullRestrictedArray(MyValue5.class, 1);
+        array5[0] = new MyValue5();
+        array4[0].intArray[0] = 42;
+
+        for (int i = 0; i < 50_000; ++i) {
+            t.test1(array1);
+            t.test2(array2);
+            t.test3(array2);
+            t.test4(array3);
+            t.test5(array3);
+            t.test6();
+            t.test7(false);
+            t.test8(array3);
+            t.test9(true);
+            t.test10(array4);
+            t.test11(array4);
+            t.test12();
+            t.test13(array5);
+            t.test14(false, new MyValue4());
+            t.test15();
+            // TODO 8325106 Triggers "nothing between inner and outer loop" assert
+            // t.test16();
+            t.test17();
+            t.test18();
+            t.test19();
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestGetfieldChains.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestGetfieldChains.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestGetfieldChains.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2020, 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.CompLevel;
+import compiler.lib.ir_framework.Run;
+import compiler.lib.ir_framework.Scenario;
+import compiler.lib.ir_framework.Test;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import jdk.test.lib.Asserts;
+
+/*
+ * @test
+ * @key randomness
+ * @summary Verify that chains of getfields on flat fields are correctly optimized.
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @compile GetfieldChains.jcod
+ * @run main/othervm/timeout=300 compiler.valhalla.inlinetypes.TestGetfieldChains
+ */
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class Point {
+    int x = 4;
+    int y = 7;
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class Rectangle {
+    @NullRestricted
+    Point p0 = new Point();
+    @NullRestricted
+    Point p1 = new Point();
+}
+
+class NamedRectangle {
+    @NullRestricted
+    Rectangle rect = new Rectangle();
+    String name = "";
+
+    static int getP1X(NamedRectangle nr) {
+        return nr.rect
+            .p1
+            .x;
+    }
+
+    static Point getP1(NamedRectangle nr) {
+        return nr.rect
+            .p1;
+    }
+}
+
+public class TestGetfieldChains {
+
+    public static void main(String[] args) {
+
+        final Scenario[] scenarios = {
+                new Scenario(0,
+                        // C1 only
+                        "-XX:TieredStopAtLevel=1",
+                        "-XX:+TieredCompilation"),
+                new Scenario(1,
+                        // C2 only. (Make sure the tests are correctly written)
+                        "-XX:TieredStopAtLevel=4",
+                        "-XX:-TieredCompilation",
+                        "-XX:-OmitStackTraceInFastThrow"),
+                new Scenario(2,
+                        // interpreter only
+                        "-Xint"),
+                new Scenario(3,
+                        // Xcomp Only C1
+                        "-XX:TieredStopAtLevel=1",
+                        "-XX:+TieredCompilation",
+                        "-Xcomp"),
+                new Scenario(4,
+                        // Xcomp Only C2
+                        "-XX:TieredStopAtLevel=4",
+                        "-XX:-TieredCompilation",
+                        "-XX:-OmitStackTraceInFastThrow",
+                        "-Xcomp")
+        };
+
+        InlineTypes.getFramework()
+                   .addScenarios(scenarios)
+                   .addFlags("--enable-preview",
+                             "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED",
+                             "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED")
+                   .start();
+    }
+
+
+    // Simple chain of getfields ending with value type field
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public int test1() {
+        return NamedRectangle.getP1X(new NamedRectangle());
+    }
+
+    @Run(test = "test1")
+    public void test1_verifier() {
+        int res = test1();
+        Asserts.assertEQ(res, 4);
+    }
+
+    // Simple chain of getfields ending with a flat field
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public Point test2() {
+        return NamedRectangle.getP1(new NamedRectangle());
+    }
+
+    @Run(test = "test2")
+    public void test2_verifier() {
+        Point p = test2();
+        Asserts.assertEQ(p.x, 4);
+        Asserts.assertEQ(p.y, 7);
+    }
+
+    // Chain of getfields but the initial receiver is null
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public NullPointerException test3() {
+        NullPointerException npe = null;
+        try {
+            NamedRectangle.getP1X(null);
+            throw new RuntimeException("No NullPointerException thrown");
+        } catch (NullPointerException e) {
+            npe = e;
+        }
+        return npe;
+    }
+
+    @Run(test = "test3")
+    public void test3_verifier() {
+        NullPointerException npe = test3();
+        Asserts.assertNE(npe, null);
+        StackTraceElement st = npe.getStackTrace()[0];
+        Asserts.assertEQ(st.getMethodName(), "getP1X");
+    }
+
+    // Chain of getfields but one getfield in the middle of the chain triggers an illegal access
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public IllegalAccessError test4() {
+        IllegalAccessError iae = null;
+        try {
+            int i = NamedRectangleP.getP1Y(new NamedRectangleP());
+            throw new RuntimeException("No IllegalAccessError thrown");
+        } catch (IllegalAccessError e) {
+            iae = e;
+        }
+        return iae;
+    }
+
+    @Run(test = "test4")
+    public void test4_verifier() {
+        IllegalAccessError iae = test4();
+        Asserts.assertNE(iae, null);
+        StackTraceElement st = iae.getStackTrace()[0];
+        Asserts.assertEQ(st.getMethodName(), "getP1Y");
+        Asserts.assertTrue(iae.getMessage().contains("class compiler.valhalla.inlinetypes.NamedRectangleP tried to access private field compiler.valhalla.inlinetypes.RectangleP.p1"));
+    }
+
+    // Chain of getfields but the last getfield triggers a NoSuchFieldError
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public NoSuchFieldError test5() {
+        NoSuchFieldError nsfe = null;
+        try {
+            int i = NamedRectangleN.getP1X(new NamedRectangleN());
+            throw new RuntimeException("No NoSuchFieldError thrown");
+        } catch (NoSuchFieldError e) {
+            nsfe = e;
+        }
+        return nsfe;
+    }
+
+    @Run(test = "test5")
+    public void test5_verifier() {
+        NoSuchFieldError nsfe = test5();
+        Asserts.assertNE(nsfe, null);
+        StackTraceElement st = nsfe.getStackTrace()[0];
+        Asserts.assertEQ(st.getMethodName(), "getP1X");
+        Asserts.assertEQ(nsfe.getMessage(), "Class compiler.valhalla.inlinetypes.PointN does not have member field 'int x'");
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class EmptyType1 { }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class EmptyContainer1 {
+        int i = 0;
+        @NullRestricted
+        EmptyType1 et = new EmptyType1();
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Container1 {
+        @NullRestricted
+        EmptyContainer1 container0 = new EmptyContainer1();
+        @NullRestricted
+        EmptyContainer1 container1 = new EmptyContainer1();
+    }
+
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public EmptyType1 test6() {
+        Container1 c = new Container1();
+        return c.container1.et;
+    }
+
+    @Run(test = "test6")
+    public void test6_verifier() {
+        EmptyType1 et = test6();
+        Asserts.assertEQ(et, new EmptyType1());
+    }
+
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public EmptyType1 test7() {
+        Container1[] ca = (Container1[])ValueClass.newNullRestrictedArray(Container1.class, 10);
+        return ca[3].container0.et;
+    }
+
+    @Run(test = "test7")
+    public void test7_verifier() {
+        EmptyType1 et = test7();
+        Asserts.assertEQ(et, new EmptyType1());
+    }
+
+    // Same as test6/test7 but not null-free and EmptyContainer2 with only one field
+
+    static value class EmptyType2 { }
+
+    static value class EmptyContainer2 {
+        EmptyType2 et = null;
+    }
+
+    static value class Container2 {
+        EmptyContainer2 container0 = new EmptyContainer2();
+        EmptyContainer2 container1 = new EmptyContainer2();
+    }
+
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public EmptyType2 test8() {
+        Container2 c = new Container2();
+        return c.container1.et;
+    }
+
+    @Run(test = "test8")
+    public void test8_verifier() {
+        EmptyType2 et = test8();
+        Asserts.assertEQ(et, null);
+    }
+
+    @Test(compLevel = CompLevel.C1_SIMPLE)
+    public EmptyType2 test9() {
+        Container2[] ca = new Container2[10];
+        ca[3] = new Container2();
+        return ca[3].container0.et;
+    }
+
+    @Run(test = "test9")
+    public void test9_verifier() {
+        EmptyType2 et = test9();
+        Asserts.assertEQ(et, null);
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestInlineFieldNonFlattened.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestInlineFieldNonFlattened.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestInlineFieldNonFlattened.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2023, Arm Limited. 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.
+ *
+ * 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.*;
+import java.util.Random;
+
+import jdk.test.lib.Utils;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+/*
+ * @test
+ * @bug 8311219
+ * @summary VM option "InlineFieldMaxFlatSize" does not work well.
+ * @library /test/lib /
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm -XX:-TieredCompilation
+ *                   -XX:InlineFieldMaxFlatSize=0
+ *                   compiler.valhalla.inlinetypes.TestInlineFieldNonFlattened
+ */
+
+public class TestInlineFieldNonFlattened {
+    static class MyClass {
+        @NullRestricted
+        public final MyValue v1 = new MyValue(5);
+
+        public MyValue v2;
+
+        public MyClass(MyValue v) {
+            v2 = v;
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue {
+        public int field;
+
+        public MyValue(int f) {
+            field = f;
+        }
+    }
+
+    private static final Random RD = Utils.getRandomInstance();
+
+    static MyClass c;
+
+    static {
+        c = new MyClass(new MyValue(RD.nextInt(100)));
+    }
+
+    static int f;
+
+    @Test
+    @IR(counts = {IRNode.LOAD_N, "2"})
+    public static void testNonFlattenedField() {
+        f = c.v2.field;
+    }
+
+    @Test
+    @IR(counts = {IRNode.LOAD_N, "2"})
+    public static void testNonFlattenedFinalField() {
+        f = c.v1.field;
+    }
+
+    public static void main(String[] args) {
+        TestFramework testFramework = new TestFramework();
+        testFramework.setDefaultWarmup(10000)
+                     .addFlags("--enable-preview",
+                               "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED",
+                               "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED",
+                               "-XX:-TieredCompilation",
+                               "-XX:InlineFieldMaxFlatSize=0")
+                     .start();
+    }
+}
+
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIntrinsics.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIntrinsics.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIntrinsics.java
@@ -0,0 +1,1675 @@
+/*
+ * Copyright (c) 2017, 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 compiler.valhalla.inlinetypes;
+
+import jdk.internal.misc.Unsafe;
+import jdk.test.lib.Asserts;
+import compiler.lib.ir_framework.*;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import static compiler.valhalla.inlinetypes.InlineTypeIRNode.*;
+import static compiler.valhalla.inlinetypes.InlineTypes.rI;
+import static compiler.valhalla.inlinetypes.InlineTypes.rL;
+
+/*
+ * @test
+ * @key randomness
+ * @summary Test intrinsic support for value classes.
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.misc
+ *          java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm/timeout=300 compiler.valhalla.inlinetypes.TestIntrinsics
+ */
+
+@ForceCompileClassInitializer
+public class TestIntrinsics {
+
+    public static void main(String[] args) {
+
+        Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS;
+        scenarios[3].addFlags("-XX:-MonomorphicArrayCheck", "-XX:FlatArrayElementMaxSize=-1");
+        scenarios[4].addFlags("-XX:-MonomorphicArrayCheck", "-XX:+UnlockExperimentalVMOptions", "-XX:PerMethodSpecTrapLimit=0", "-XX:PerMethodTrapLimit=0");
+
+        InlineTypes.getFramework()
+                   .addScenarios(scenarios)
+                   .addFlags("--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED",
+                             "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED",
+                             // Don't run with DeoptimizeALot until JDK-8239003 is fixed
+                             "-XX:-DeoptimizeALot")
+                   .addHelperClasses(MyValue1.class,
+                                     MyValue2.class,
+                                     MyValue2Inline.class)
+                   .start();
+    }
+
+    static {
+        // Make sure RuntimeException is loaded to prevent uncommon traps in IR verified tests
+        RuntimeException tmp = new RuntimeException("42");
+    }
+
+    // Test correctness of the Class::isAssignableFrom intrinsic
+    @Test
+    public boolean test1(Class<?> supercls, Class<?> subcls) {
+        return supercls.isAssignableFrom(subcls);
+    }
+
+    @Run(test = "test1")
+    public void test1_verifier() {
+        Asserts.assertTrue(test1(java.util.AbstractList.class, java.util.ArrayList.class), "test1_1 failed");
+        Asserts.assertTrue(test1(MyValue1.class, MyValue1.class), "test1_2 failed");
+        Asserts.assertTrue(test1(Object.class, java.util.ArrayList.class), "test1_3 failed");
+        Asserts.assertTrue(test1(Object.class, MyValue1.class), "test1_4 failed");
+        Asserts.assertTrue(!test1(MyValue1.class, Object.class), "test1_5 failed");
+    }
+
+    // Verify that Class::isAssignableFrom checks with statically known classes are folded
+    @Test
+    @IR(failOn = {LOADK})
+    public boolean test2() {
+        boolean check1 = java.util.AbstractList.class.isAssignableFrom(java.util.ArrayList.class);
+        boolean check2 = MyValue1.class.isAssignableFrom(MyValue1.class);
+        boolean check3 = Object.class.isAssignableFrom(java.util.ArrayList.class);
+        boolean check4 = Object.class.isAssignableFrom(MyValue1.class);
+        boolean check5 = !MyValue1.class.isAssignableFrom(Object.class);
+        return check1 && check2 && check3 && check4 && check5;
+    }
+
+    @Run(test = "test2")
+    public void test2_verifier() {
+        Asserts.assertTrue(test2(), "test2 failed");
+    }
+
+    // Test correctness of the Class::getSuperclass intrinsic
+    @Test
+    public Class<?> test3(Class<?> cls) {
+        return cls.getSuperclass();
+    }
+
+    @Run(test = "test3")
+    public void test3_verifier() {
+        Asserts.assertTrue(test3(Object.class) == null, "test3_1 failed");
+        Asserts.assertTrue(test3(MyValue1.class) == MyAbstract.class, "test3_2 failed");
+        Asserts.assertTrue(test3(MyValue1.class) == MyAbstract.class, "test3_3 failed");
+        Asserts.assertTrue(test3(Class.class) == Object.class, "test3_4 failed");
+    }
+
+    // Verify that Class::getSuperclass checks with statically known classes are folded
+    @Test
+    @IR(failOn = {LOADK})
+    public boolean test4() {
+        boolean check1 = Object.class.getSuperclass() == null;
+        boolean check2 = MyValue1.class.getSuperclass() == MyAbstract.class;
+        boolean check3 = MyValue1.class.getSuperclass() == MyAbstract.class;
+        boolean check4 = Class.class.getSuperclass() == Object.class;
+        return check1 && check2 && check3 && check4;
+    }
+
+    @Run(test = "test4")
+    public void test4_verifier() {
+        Asserts.assertTrue(test4(), "test4 failed");
+    }
+
+    // Test toString() method
+    @Test
+    public String test5(MyValue1 v) {
+        return v.toString();
+    }
+
+    @Run(test = "test5")
+    public void test5_verifier() {
+        MyValue1 v = MyValue1.createDefaultInline();
+        test5(v);
+    }
+
+    // Test hashCode() method
+    @Test
+    public int test6(MyValue1 v) {
+        return v.hashCode();
+    }
+
+    @Run(test = "test6")
+    public void test6_verifier() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        int res = test6(v);
+        Asserts.assertEQ(res, v.hashCode());
+    }
+
+    // Test default value class array creation via reflection
+    @Test
+    public Object[] test7(Class<?> componentType, int len) {
+        Object[] va = ValueClass.newNullRestrictedArray(componentType, len);
+        return va;
+    }
+
+    @Run(test = "test7")
+    public void test7_verifier() {
+        int len = Math.abs(rI) % 42;
+        long hash = MyValue1.createDefaultDontInline().hashPrimitive();
+        Object[] va = test7(MyValue1.class, len);
+        for (int i = 0; i < len; ++i) {
+            Asserts.assertEQ(((MyValue1)va[i]).hashPrimitive(), hash);
+        }
+    }
+
+    // Class.isInstance
+    @Test
+    public boolean test8(Class c, MyValue1 vt) {
+        return c.isInstance(vt);
+    }
+
+    @Run(test = "test8")
+    public void test8_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        boolean result = test8(MyValue1.class, vt);
+        Asserts.assertTrue(result);
+        result = test8(MyValue1.class, vt);
+        Asserts.assertTrue(result);
+    }
+
+    @Test
+    public boolean test9(Class c, MyValue1 vt) {
+        return c.isInstance(vt);
+    }
+
+    @Run(test = "test9")
+    public void test9_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        boolean result = test9(MyValue2.class, vt);
+        Asserts.assertFalse(result);
+        result = test9(MyValue2.class, vt);
+        Asserts.assertFalse(result);
+    }
+
+    // Class.cast
+    @Test
+    public Object test10(Class c, MyValue1 vt) {
+        return c.cast(vt);
+    }
+
+    @Run(test = "test10")
+    public void test10_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        Object result = test10(MyValue1.class, vt);
+        Asserts.assertEQ(((MyValue1)result).hash(), vt.hash());
+    }
+
+    @Test
+    public Object test11(Class c, MyValue1 vt) {
+        return c.cast(vt);
+    }
+
+    @Run(test = "test11")
+    public void test11_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        try {
+            test11(MyValue2.class, vt);
+            throw new RuntimeException("should have thrown");
+        } catch (ClassCastException cce) {
+        }
+    }
+
+    @Test
+    public Object test12(MyValue1 vt) {
+        return MyValue1.class.cast(vt);
+    }
+
+    @Run(test = "test12")
+    public void test12_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        Object result = test12(vt);
+        Asserts.assertEQ(((MyValue1)result).hash(), vt.hash());
+    }
+
+    @Test
+    public Object test13(MyValue1 vt) {
+        return MyValue2.class.cast(vt);
+    }
+
+    @Run(test = "test13")
+    public void test13_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        try {
+            test13(vt);
+            throw new RuntimeException("should have thrown");
+        } catch (ClassCastException cce) {
+        }
+    }
+
+    // Value class array creation via reflection
+    @Test
+    public void test14(int len, long hash) {
+        Object[] va = ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        for (int i = 0; i < len; ++i) {
+            Asserts.assertEQ(((MyValue1)va[i]).hashPrimitive(), hash);
+        }
+    }
+
+    @Run(test = "test14")
+    public void test14_verifier() {
+        int len = Math.abs(rI) % 42;
+        long hash = MyValue1.createDefaultDontInline().hashPrimitive();
+        test14(len, hash);
+    }
+
+    // Test hashCode() method
+    @Test
+    public int test15(Object v) {
+        return v.hashCode();
+    }
+
+    @Run(test = "test15")
+    public void test15_verifier() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        int res = test15(v);
+        Asserts.assertEQ(res, v.hashCode());
+    }
+
+    @Test
+    public int test16(Object v) {
+        return System.identityHashCode(v);
+    }
+
+    @Run(test = "test16")
+    public void test16_verifier() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        int res = test16(v);
+        Asserts.assertEQ(res, System.identityHashCode((Object)v));
+    }
+
+    @Test
+    public int test17(Object v) {
+        return System.identityHashCode(v);
+    }
+
+    @Run(test = "test17")
+    public void test17_verifier() {
+        Integer v = Integer.valueOf(rI);
+        int res = test17(v);
+        Asserts.assertEQ(res, System.identityHashCode(v));
+    }
+
+    @Test
+    public int test18(Object v) {
+        return System.identityHashCode(v);
+    }
+
+    @Run(test = "test18")
+    public void test18_verifier() {
+        Object v = null;
+        int res = test18(v);
+        Asserts.assertEQ(res, System.identityHashCode(v));
+    }
+
+    // hashCode() and toString() with different value objects
+    @Test
+    public int test19(MyValue1 vt1, MyValue1 vt2, boolean b) {
+        MyValue1 res = b ? vt1 : vt2;
+        return res.hashCode();
+    }
+
+    @Run(test = "test19")
+    public void test19_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        int res = test19(vt, vt, true);
+        Asserts.assertEQ(res, vt.hashCode());
+        res = test19(vt, vt, false);
+        Asserts.assertEQ(res, vt.hashCode());
+    }
+
+    @Test
+    public String test20(MyValue1 vt1, MyValue1 vt2, boolean b) {
+        MyValue1 res = b ? vt1 : vt2;
+        return res.toString();
+    }
+
+    @Run(test = "test20")
+    public void test20_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        String res = test20(vt, vt, true);
+        Asserts.assertEQ(res, vt.toString());
+        res = test20(vt, vt, false);
+        Asserts.assertEQ(res, vt.toString());
+    }
+
+    private static final Unsafe U = Unsafe.getUnsafe();
+    private static final long X_OFFSET;
+    private static final long Y_OFFSET;
+    private static final long V1_OFFSET;
+    private static final boolean V1_FLATTENED;
+
+    static {
+        try {
+            Field xField = MyValue1.class.getDeclaredField("x");
+            X_OFFSET = U.objectFieldOffset(xField);
+            Field yField = MyValue1.class.getDeclaredField("y");
+            Y_OFFSET = U.objectFieldOffset(yField);
+            Field v1Field = MyValue1.class.getDeclaredField("v1");
+            V1_OFFSET = U.objectFieldOffset(v1Field);
+            V1_FLATTENED = U.isFlatField(v1Field);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public int test21(MyValue1 v) {
+       return U.getInt(v, X_OFFSET);
+    }
+
+    @Run(test = "test21")
+    public void test21_verifier() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        int res = test21(v);
+        Asserts.assertEQ(res, v.x);
+    }
+
+    @NullRestricted
+    MyValue1 test22_vt;
+
+    @Test
+    @IR(failOn = {CALL_UNSAFE, ALLOC})
+    public void test22(MyValue1 v) {
+        v = U.makePrivateBuffer(v);
+        U.putInt(v, X_OFFSET, rI);
+        v = U.finishPrivateBuffer(v);
+        test22_vt = v;
+    }
+
+    @Run(test = "test22")
+    public void test22_verifier() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        test22(v.setX(v, 0));
+        Asserts.assertEQ(test22_vt.hash(), v.hash());
+    }
+
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public int test23(MyValue1 v, long offset) {
+        return U.getInt(v, offset);
+    }
+
+    @Run(test = "test23")
+    public void test23_verifier() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        int res = test23(v, X_OFFSET);
+        Asserts.assertEQ(res, v.x);
+    }
+
+    @NullRestricted
+    MyValue1 test24_vt = MyValue1.createWithFieldsInline(rI, rL);
+
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public int test24(long offset) {
+        return U.getInt(test24_vt, offset);
+    }
+
+    @Run(test = "test24")
+    public void test24_verifier() {
+        int res = test24(X_OFFSET);
+        Asserts.assertEQ(res, test24_vt.x);
+    }
+
+    // Test copyOf intrinsic with allocated value object in it's debug information
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    value class Test25Value {
+        int x;
+
+        public Test25Value() {
+            this.x = 42;
+        }
+    }
+
+    final Test25Value[] test25Array = (Test25Value[])ValueClass.newNullRestrictedArray(Test25Value.class, 10);
+
+    @Test
+    public Test25Value[] test25(Test25Value element) {
+        Object[] newArray = Arrays.copyOf(test25Array, test25Array.length + 1);
+        newArray[test25Array.length] = element;
+        return (Test25Value[]) newArray;
+    }
+
+    @Run(test = "test25")
+    public void test25_verifier() {
+        Test25Value vt = new Test25Value();
+        test25(vt);
+    }
+
+    @Test
+    @IR(failOn = IRNode.LOAD_I) // Load of the default value should be folded
+    public Object test26() {
+        Class<?>[] ca = new Class<?>[1];
+        for (int i = 0; i < 1; ++i) {
+          // Folds during loop opts
+          ca[i] = MyValue1.class;
+        }
+        return ValueClass.newNullRestrictedArray(ca[0], 1);
+    }
+
+    @Run(test = "test26")
+    public void test26_verifier() {
+        Object[] res = (Object[])test26();
+        Asserts.assertEQ(((MyValue1)res[0]).hashPrimitive(), MyValue1.createDefaultInline().hashPrimitive());
+    }
+
+    // Load non-flattenable value class field with unsafe
+    MyValue1 test27_vt;
+    private static final long TEST27_OFFSET;
+    static {
+        try {
+            Field field = TestIntrinsics.class.getDeclaredField("test27_vt");
+            TEST27_OFFSET = U.objectFieldOffset(field);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public MyValue1 test27() {
+        return (MyValue1)U.getReference(this, TEST27_OFFSET);
+    }
+
+    @Run(test = "test27")
+    public void test27_verifier() {
+        test27_vt = null;
+        MyValue1 res = test27();
+        Asserts.assertEQ(res, null);
+        test27_vt = MyValue1.createWithFieldsInline(rI, rL);
+        res = test27();
+        Asserts.assertEQ(res.hash(), test24_vt.hash());
+    }
+
+    // Mismatched type
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public int test28(MyValue1 v) {
+        return U.getByte(v, X_OFFSET);
+    }
+
+    @Run(test = "test28")
+    public void test28_verifier() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        int res = test28(v);
+        if (java.nio.ByteOrder.nativeOrder() == java.nio.ByteOrder.LITTLE_ENDIAN) {
+            Asserts.assertEQ(res, (int)((byte)v.x));
+        } else {
+            Asserts.assertEQ(res, (int)((byte)Integer.reverseBytes(v.x)));
+        }
+    }
+
+    // Wrong alignment
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public long test29(MyValue1 v) {
+        // Read the field that's guaranteed to not be last in the
+        // value class so we don't read out of bounds.
+        if (X_OFFSET < Y_OFFSET) {
+            return U.getInt(v, X_OFFSET+1);
+        }
+        return U.getLong(v, Y_OFFSET+1);
+    }
+
+    @Run(test = "test29")
+    public void test29_verifier() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        long res = test29(v);
+        if (java.nio.ByteOrder.nativeOrder() == java.nio.ByteOrder.LITTLE_ENDIAN) {
+            if (X_OFFSET < Y_OFFSET) {
+                Asserts.assertEQ(((int)res) << 8, (v.x >> 8) << 8);
+            } else {
+                Asserts.assertEQ(res << 8, (v.y >> 8) << 8);
+            }
+        } else {
+            if (X_OFFSET < Y_OFFSET) {
+                Asserts.assertEQ(((int)res), v.x >>> 8);
+            } else {
+                Asserts.assertEQ(res, v.y >>> 8);
+            }
+        }
+    }
+
+    // getValue to retrieve flattened field from value object
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public MyValue2 test30(MyValue1 v) {
+        if (V1_FLATTENED) {
+            return U.getValue(v, V1_OFFSET, MyValue2.class);
+        }
+        return (MyValue2)U.getReference(v, V1_OFFSET);
+    }
+
+    @Run(test = "test30")
+    public void test30_verifier(RunInfo info) {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue2 res = test30(v);
+        Asserts.assertEQ(res.hash(), v.v1.hash());
+    }
+
+    @NullRestricted
+    MyValue1 test31_vt;
+    private static final long TEST31_VT_OFFSET;
+    private static final boolean TEST31_VT_FLATTENED;
+    static {
+        try {
+            Field test31_vt_Field = TestIntrinsics.class.getDeclaredField("test31_vt");
+            TEST31_VT_OFFSET = U.objectFieldOffset(test31_vt_Field);
+            TEST31_VT_FLATTENED = U.isFlatField(test31_vt_Field);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    // getValue to retrieve flattened field from object
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public MyValue1 test31() {
+        if (TEST31_VT_FLATTENED) {
+            return U.getValue(this, TEST31_VT_OFFSET, MyValue1.class);
+        }
+        return (MyValue1)U.getReference(this, TEST31_VT_OFFSET);
+    }
+
+    @Run(test = "test31")
+    public void test31_verifier() {
+        test31_vt = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1 res = test31();
+        Asserts.assertEQ(res.hash(), test31_vt.hash());
+    }
+
+    // putValue to set flattened field in object
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public void test32(MyValue1 vt) {
+        if (TEST31_VT_FLATTENED) {
+            U.putValue(this, TEST31_VT_OFFSET, MyValue1.class, vt);
+        } else {
+            U.putReference(this, TEST31_VT_OFFSET, vt);
+        }
+    }
+
+    @Run(test = "test32")
+    public void test32_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        test31_vt = MyValue1.createDefaultInline();
+        test32(vt);
+        Asserts.assertEQ(vt.hash(), test31_vt.hash());
+    }
+
+    private static final int TEST33_BASE_OFFSET;
+    private static final int TEST33_INDEX_SCALE;
+    private static final MyValue1[] TEST33_ARRAY;
+    private static final boolean TEST33_FLATTENED_ARRAY;
+    static {
+        try {
+            TEST33_ARRAY = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+            TEST33_BASE_OFFSET = U.arrayBaseOffset(TEST33_ARRAY.getClass());
+            TEST33_INDEX_SCALE = U.arrayIndexScale(TEST33_ARRAY.getClass());
+            TEST33_FLATTENED_ARRAY = U.isFlatArray(TEST33_ARRAY.getClass());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+    // getValue to retrieve flattened field from array
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public MyValue1 test33() {
+        if (TEST33_FLATTENED_ARRAY) {
+            return U.getValue(TEST33_ARRAY, TEST33_BASE_OFFSET + TEST33_INDEX_SCALE, MyValue1.class);
+        }
+        return (MyValue1)U.getReference(TEST33_ARRAY, TEST33_BASE_OFFSET + TEST33_INDEX_SCALE);
+    }
+
+    @Run(test = "test33")
+    public void test33_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        TEST33_ARRAY[1] = vt;
+        MyValue1 res = test33();
+        Asserts.assertEQ(res.hash(), vt.hash());
+    }
+
+    // putValue to set flattened field in array
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public void test34(MyValue1 vt) {
+        if (TEST33_FLATTENED_ARRAY) {
+            U.putValue(TEST33_ARRAY, TEST33_BASE_OFFSET + TEST33_INDEX_SCALE, MyValue1.class, vt);
+        } else {
+            U.putReference(TEST33_ARRAY, TEST33_BASE_OFFSET + TEST33_INDEX_SCALE, vt);
+        }
+    }
+
+    @Run(test = "test34")
+    public void test34_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        test34(vt);
+        Asserts.assertEQ(TEST33_ARRAY[1].hash(), vt.hash());
+    }
+
+    // getValue to retrieve flattened field from object with unknown
+    // container type
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public MyValue1 test35(Object o) {
+        if (TEST31_VT_FLATTENED) {
+            return U.getValue(o, TEST31_VT_OFFSET, MyValue1.class);
+        }
+        return (MyValue1)U.getReference(o, TEST31_VT_OFFSET);
+    }
+
+    @Run(test = "test35")
+    public void test35_verifier() {
+        test31_vt = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1 res = test35(this);
+        Asserts.assertEQ(res.hash(), test31_vt.hash());
+    }
+
+    // getValue to retrieve flattened field from object at unknown
+    // offset
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public MyValue1 test36(long offset) {
+        if (TEST31_VT_FLATTENED) {
+            return U.getValue(this, offset, MyValue1.class);
+        }
+        return (MyValue1)U.getReference(this, offset);
+    }
+
+    @Run(test = "test36")
+    public void test36_verifier() {
+        test31_vt = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1 res = test36(TEST31_VT_OFFSET);
+        Asserts.assertEQ(res.hash(), test31_vt.hash());
+    }
+
+    // putValue to set flattened field in object with unknown
+    // container
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public void test37(Object o, MyValue1 vt) {
+        if (TEST31_VT_FLATTENED) {
+            U.putValue(o, TEST31_VT_OFFSET, MyValue1.class, vt);
+        } else {
+            U.putReference(o, TEST31_VT_OFFSET, vt);
+        }
+    }
+
+    @Run(test = "test37")
+    public void test37_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        test31_vt = MyValue1.createDefaultInline();
+        test37(this, vt);
+        Asserts.assertEQ(vt.hash(), test31_vt.hash());
+    }
+
+    // putValue to set flattened field in object, non inline argument
+    // to store
+    @Test
+    @IR(counts = {CALL_UNSAFE, "= 1"})
+    public void test38(Object o) {
+        if (TEST31_VT_FLATTENED) {
+            U.putValue(this, TEST31_VT_OFFSET, MyValue1.class, o);
+        } else {
+            U.putReference(this, TEST31_VT_OFFSET, o);
+        }
+    }
+
+    @Run(test = "test38")
+    public void test38_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        test31_vt = MyValue1.createDefaultInline();
+        test38(vt);
+        Asserts.assertEQ(vt.hash(), test31_vt.hash());
+    }
+
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public MyValue1 test39(MyValue1 v) {
+        v = U.makePrivateBuffer(v);
+        U.putInt(v, X_OFFSET, rI);
+        v = U.finishPrivateBuffer(v);
+        return v;
+    }
+
+    @Run(test = "test39")
+    public void test39_verifier() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1 res = test39(v.setX(v, 0));
+        Asserts.assertEQ(res.hash(), v.hash());
+    }
+
+    // Test default value class array creation via reflection
+    @Test
+    public Object[] test40(Class<?> componentType, int len) {
+        Object[] va = (Object[])Array.newInstance(componentType, len);
+        return va;
+    }
+
+    @Run(test = "test40")
+    public void test40_verifier() {
+        int len = Math.abs(rI) % 42;
+        Object[] va = test40(MyValue1.class, len);
+        for (int i = 0; i < len; ++i) {
+            Asserts.assertEQ(va[i], null);
+        }
+    }
+
+    // Class.isInstance
+    @Test
+    public boolean test41(Class c, MyValue1 vt) {
+        return c.isInstance(vt);
+    }
+
+    @Run(test = "test41")
+    public void test41_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        boolean result = test41(MyValue1.class, vt);
+        Asserts.assertTrue(result);
+        result = test41(MyValue1.class, null);
+        Asserts.assertFalse(result);
+        result = test41(MyValue1.class, vt);
+        Asserts.assertTrue(result);
+        result = test41(MyValue1.class, null);
+        Asserts.assertFalse(result);
+    }
+
+    @Test
+    public boolean test42(Class c, MyValue1 vt) {
+        return c.isInstance(vt);
+    }
+
+    @Run(test = "test42")
+    public void test42_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        boolean result = test42(MyValue2.class, vt);
+        Asserts.assertFalse(result);
+        result = test42(MyValue2.class, null);
+        Asserts.assertFalse(result);
+        result = test42(MyValue2.class, vt);
+        Asserts.assertFalse(result);
+        result = test42(MyValue2.class, null);
+        Asserts.assertFalse(result);
+    }
+
+    // Class.cast
+    @Test
+    public Object test43(Class c, MyValue1 vt) {
+        return c.cast(vt);
+    }
+
+    @Run(test = "test43")
+    public void test43_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        Object result = test43(MyValue1.class, vt);
+        Asserts.assertEQ(result, vt);
+        result = test43(MyValue1.class, null);
+        Asserts.assertEQ(result, null);
+        result = test43(MyValue1.class, vt);
+        Asserts.assertEQ(result, vt);
+        result = test43(NonValueClass.class, null);
+        Asserts.assertEQ(result, null);
+    }
+
+    @Test
+    public Object test44(Class c, MyValue1 vt) {
+        return c.cast(vt);
+    }
+
+    @Run(test = "test44")
+    public void test44_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        try {
+            test44(MyValue2.class, vt);
+            throw new RuntimeException("should have thrown");
+        } catch (ClassCastException cce) {
+        }
+        Object res = test44(MyValue2.class, null);
+        Asserts.assertEQ(res, null);
+        try {
+            test44(MyValue2.class, vt);
+            throw new RuntimeException("should have thrown");
+        } catch (ClassCastException cce) {
+        }
+    }
+
+    @Test
+    public Object test45(MyValue1 vt) {
+        return MyValue1.class.cast(vt);
+    }
+
+    @Run(test = "test45")
+    public void test45_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        Object result = test45(vt);
+        Asserts.assertEQ(((MyValue1)result).hash(), vt.hash());
+        result = test45(null);
+        Asserts.assertEQ(result, null);
+    }
+
+    @Test
+    public Object test46(MyValue1 vt) {
+        return MyValue2.class.cast(vt);
+    }
+
+    @Run(test = "test46")
+    public void test46_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        Object result = test46(null);
+        Asserts.assertEQ(result, null);
+        try {
+            test46(vt);
+            throw new RuntimeException("should have thrown");
+        } catch (ClassCastException cce) {
+        }
+    }
+
+    @Test
+    public Object test47(MyValue1 vt) {
+        return MyValue1.class.cast(vt);
+    }
+
+    @Run(test = "test47")
+    public void test47_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        Object result = test47(vt);
+        Asserts.assertEQ(((MyValue1)result).hash(), vt.hash());
+        result = test47(null);
+        Asserts.assertEQ(result, null);
+    }
+
+    @Test
+    public Object test48(Class c, MyValue1 vt) {
+        return c.cast(vt);
+    }
+
+    @Run(test = "test48")
+    public void test48_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        Object result = test48(MyValue1.class, vt);
+        Asserts.assertEQ(((MyValue1)result).hash(), vt.hash());
+        result = test48(MyValue1.class, null);
+        Asserts.assertEQ(result, null);
+    }
+
+    @Test
+    public Object test49(MyValue1 vt) {
+        return MyValue1.class.cast(vt);
+    }
+
+    @Run(test = "test49")
+    public void test49_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        Object result = test49(vt);
+        Asserts.assertEQ(((MyValue1)result).hash(), vt.hash());
+    }
+
+    @Test
+    public Object test50(Class c, Object obj) {
+        return c.cast(obj);
+    }
+
+    @Run(test = "test50")
+    public void test50_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1[] va  = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 42);
+        MyValue1[] vba = new MyValue1[42];
+        Object result = test50(MyValue1.class, vt);
+        Asserts.assertEQ(((MyValue1)result).hash(), vt.hash());
+        result = test50(MyValue1.class, vt);
+        Asserts.assertEQ(((MyValue1)result).hash(), vt.hash());
+        result = test50(MyValue1[].class, va);
+        Asserts.assertEQ(result, va);
+        result = test50(MyValue1[].class, vba);
+        Asserts.assertEQ(result, vba);
+        result = test50(MyValue1[].class, va);
+        Asserts.assertEQ(result, va);
+        result = test50(MyValue1.class, null);
+        Asserts.assertEQ(result, null);
+        try {
+            test50(va.getClass(), vba);
+            throw new RuntimeException("should have thrown");
+        } catch (ClassCastException cce) {
+        }
+    }
+
+    // Value class array creation via reflection
+    @Test
+    public void test51(int len) {
+        Object[] va = (Object[])Array.newInstance(MyValue1.class, len);
+        for (int i = 0; i < len; ++i) {
+            Asserts.assertEQ(va[i], null);
+        }
+    }
+
+    @Run(test = "test51")
+    public void test51_verifier() {
+        int len = Math.abs(rI) % 42;
+        test51(len);
+    }
+
+    // multidimensional value class array creation via reflection
+    @Test
+    public Object[][] test52(int len, int val) {
+        MyValue1[][] va1 = (MyValue1[][])Array.newInstance(MyValue1[].class, len);
+        MyValue1[][] va2 = (MyValue1[][])Array.newInstance(MyValue1[].class, len);
+        Object[][] result;
+        if (val == 1) {
+            va1[0] = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+            result = va1;
+        } else {
+            va2[0] = new MyValue1[1];
+            result = va2;
+        }
+        if (val == 1) {
+            Asserts.assertEQ(va1[0][0].hash(), ((MyValue1)result[0][0]).hash());
+        } else {
+            Asserts.assertEQ(result[0][0], null);
+            result[0][0] = null;
+        }
+        return result;
+    }
+
+    @Run(test = "test52")
+    public void test52_verifier() {
+        test52(1, 1);
+        test52(1, 2);
+    }
+
+    @Test
+    public Object[][] test53(Class<?> c1, Class<?> c2, int len, int val) {
+        MyValue1[][] va1 = (MyValue1[][])Array.newInstance(MyValue1[].class, len);
+        MyValue1[][] va2 = (MyValue1[][])Array.newInstance(MyValue1[].class, len);
+        Object[][] va3 = (Object[][])Array.newInstance(c1, len);
+        Object[][] va4 = (Object[][])Array.newInstance(c2, len);
+        for (int i = 0; i < len; ++i) {
+            Asserts.assertEQ(va1[i], null);
+            Asserts.assertEQ(va2[i], null);
+            Asserts.assertEQ(va3[i], null);
+            Asserts.assertEQ(va4[i], null);
+            va1[i] = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+            va2[i] = new MyValue1[1];
+            va3[i] = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+            va4[i] = new MyValue1[1];
+            Asserts.assertEQ(va1[i][0].hash(), ((MyValue1)va3[i][0]).hash());
+            Asserts.assertEQ(va2[i][0], null);
+            Asserts.assertEQ(va4[i][0], null);
+        }
+        Object[][] result;
+        if (val == 1) {
+            result = va1;
+        } else if (val == 2) {
+            result = va2;
+        } else if (val == 3) {
+            result = va3;
+        } else {
+            result = va4;
+        }
+        if ((val == 1 || val == 3) && len > 0) {
+            Asserts.assertEQ(va1[0][0].hash(), ((MyValue1)result[0][0]).hash());
+        } else if (len > 0) {
+            Asserts.assertEQ(result[0][0], null);
+            result[0][0] = null;
+        }
+        return result;
+    }
+
+    @Run(test = "test53")
+    public void test53_verifier() {
+        int len = Math.abs(rI) % 42;
+        test53(MyValue1[].class, MyValue1[].class, len, 1);
+        test53(MyValue1[].class, MyValue1[].class, len, 2);
+        test53(MyValue1[].class, MyValue1[].class, len, 3);
+        test53(MyValue1[].class, MyValue1[].class, len, 4);
+    }
+
+    // TODO 8239003 Re-enable
+    /*
+    // Same as test39 but Unsafe.putInt to buffer is not intrinsified/compiled
+    @DontCompile
+    public void test54_callee(Object v) { // Use Object here to make sure the argument is not scalarized (otherwise larval information is lost)
+        U.putInt(v, X_OFFSET, rI);
+    }
+
+    @Test
+    public MyValue1 test54(MyValue1 v) {
+        v = U.makePrivateBuffer(v);
+        test54_callee(v);
+        v = U.finishPrivateBuffer(v);
+        return v;
+    }
+
+    @Run(test = "test54")
+    @Warmup(10000) // Fill up the TLAB to trigger slow path allocation
+    public void test54_verifier() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1 res = test54(v.setX(v, 0));
+        Asserts.assertEQ(res.hash(), v.hash());
+    }
+    */
+
+    @NullRestricted
+    static final MyValue1 test55_vt = MyValue1.createWithFieldsInline(rI, rL);
+
+    // Same as test30 but with constant field holder
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public MyValue2 test55() {
+        if (V1_FLATTENED) {
+            return U.getValue(test55_vt, V1_OFFSET, MyValue2.class);
+        }
+        return (MyValue2)U.getReference(test55_vt, V1_OFFSET);
+    }
+
+    @Run(test = "test55")
+    public void test55_verifier() {
+        MyValue2 res = test55();
+        Asserts.assertEQ(res.hash(), test55_vt.v1.hash());
+    }
+
+    // Test OptimizePtrCompare part of Escape Analysis
+    @Test
+    public void test56(int idx) {
+        Object[] va = ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        if (va[idx] == null) {
+            throw new RuntimeException("Unexpected null");
+        }
+    }
+
+    @Run(test = "test56")
+    public void test56_verifier() {
+        test56(0);
+    }
+
+    // Same as test56 but with load from known array index
+    @Test
+    public void test57() {
+        Object[] va = ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        if (va[0] == null) {
+            throw new RuntimeException("Unexpected null");
+        }
+    }
+
+    @Run(test = "test57")
+    public void test57_verifier() {
+        test57();
+    }
+
+    // Test unsafe allocation
+    @Test
+    public boolean test58(Class<?> c1, Class<?> c2) throws Exception {
+        Object obj1 = U.allocateInstance(c1);
+        Object obj2 = U.allocateInstance(c2);
+        return obj1 == obj2;
+    }
+
+    @Run(test = "test58")
+    public void test58_verifier() throws Exception {
+        boolean res = test58(MyValue1.class, MyValue1.class);
+        Asserts.assertTrue(res);
+        res = test58(Object.class, MyValue1.class);
+        Asserts.assertFalse(res);
+        res = test58(MyValue1.class, Object.class);
+        Asserts.assertFalse(res);
+    }
+
+    // Test synchronization on unsafe value object allocation
+    @Test
+    public void test59(Class<?> c) throws Exception {
+        Object obj = U.allocateInstance(c);
+        synchronized (obj) {
+
+        }
+    }
+
+    @Run(test = "test59")
+    public void test59_verifier() throws Exception {
+        test59(Object.class);
+        try {
+            test59(MyValue1.class);
+            throw new RuntimeException("test59 failed: synchronization on value object should not succeed");
+        } catch (IllegalMonitorStateException e) {
+
+        }
+    }
+
+    // Test mark word load optimization on unsafe value object allocation
+    @Test
+    public boolean test60(Class<?> c1, Class<?> c2, boolean b1, boolean b2) throws Exception {
+        Object obj1 = b1 ? new Object() : U.allocateInstance(c1);
+        Object obj2 = b2 ? new Object() : U.allocateInstance(c2);
+        return obj1 == obj2;
+    }
+
+    @Run(test = "test60")
+    public void test60_verifier() throws Exception {
+        Asserts.assertTrue(test60(MyValue1.class, MyValue1.class, false, false));
+        Asserts.assertFalse(test60(MyValue1.class, MyValue2.class, false, false));
+        Asserts.assertFalse(test60(MyValue1.class, MyValue1.class, false, true));
+        Asserts.assertFalse(test60(MyValue1.class, MyValue1.class, true, false));
+        Asserts.assertFalse(test60(MyValue1.class, MyValue1.class, true, true));
+    }
+
+    // compareAndSet to flattened field in object
+    @Test
+    public boolean test63(MyValue1 oldVal, MyValue1 newVal) {
+        if (TEST31_VT_FLATTENED) {
+            return U.compareAndSetValue(this, TEST31_VT_OFFSET, MyValue1.class, oldVal, newVal);
+        } else {
+            return U.compareAndSetReference(this, TEST31_VT_OFFSET, oldVal, newVal);
+        }
+    }
+
+    @Run(test = "test63")
+    public void test63_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        test31_vt = MyValue1.createDefaultInline();
+
+        boolean res = test63(test31_vt, vt);
+        // Checks are disabled for non-flattened field because reference comparison
+        // fails if C2 scalarizes and re-allocates the value class arguments.
+        if (TEST31_VT_FLATTENED) {
+            Asserts.assertTrue(res);
+            Asserts.assertEQ(test31_vt, vt);
+        }
+
+        res = test63(MyValue1.createDefaultInline(), MyValue1.createDefaultInline());
+        if (TEST31_VT_FLATTENED) {
+            Asserts.assertFalse(res);
+            Asserts.assertEQ(test31_vt, vt);
+        }
+    }
+
+    // compareAndSet to flattened field in array
+    @Test
+    public boolean test64(MyValue1[] arr, MyValue1 oldVal, Object newVal) {
+        if (TEST33_FLATTENED_ARRAY) {
+            return U.compareAndSetValue(arr, TEST33_BASE_OFFSET + TEST33_INDEX_SCALE, MyValue1.class, oldVal, newVal);
+        } else {
+            return U.compareAndSetReference(arr, TEST33_BASE_OFFSET + TEST33_INDEX_SCALE, oldVal, newVal);
+        }
+    }
+
+    @Run(test = "test64")
+    public void test64_verifier() {
+        MyValue1[] arr = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+
+        boolean res = test64(arr, arr[1], vt);
+        // Checks are disabled for non-flattened array because reference comparison
+        // fails if C2 scalarizes and re-allocates the value class arguments.
+        if (TEST33_FLATTENED_ARRAY) {
+            Asserts.assertTrue(res);
+            Asserts.assertEQ(arr[1], vt);
+        }
+
+        res = test64(arr, MyValue1.createDefaultInline(), MyValue1.createDefaultInline());
+        if (TEST33_FLATTENED_ARRAY) {
+            Asserts.assertFalse(res);
+            Asserts.assertEQ(arr[1], vt);
+        }
+    }
+
+    // compareAndSet to flattened field in object with unknown container
+    @Test
+    public boolean test65(Object o, Object oldVal, MyValue1 newVal) {
+        if (TEST31_VT_FLATTENED) {
+            return U.compareAndSetValue(o, TEST31_VT_OFFSET, MyValue1.class, oldVal, newVal);
+        } else {
+            return U.compareAndSetReference(o, TEST31_VT_OFFSET, oldVal, newVal);
+        }
+    }
+
+    @Run(test = "test65")
+    public void test65_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        test31_vt = MyValue1.createDefaultInline();
+
+        boolean res = test65(this, test31_vt, vt);
+        Asserts.assertTrue(res);
+        Asserts.assertEQ(test31_vt, vt);
+
+        res = test65(this, MyValue1.createDefaultInline(), MyValue1.createDefaultInline());
+        Asserts.assertFalse(res);
+        Asserts.assertEQ(test31_vt, vt);
+    }
+
+    // compareAndSet to flattened field in object, non-inline arguments to compare and set
+    @Test
+    public boolean test66(Object oldVal, Object newVal) {
+        if (TEST31_VT_FLATTENED) {
+            return U.compareAndSetValue(this, TEST31_VT_OFFSET, MyValue1.class, oldVal, newVal);
+        } else {
+            return U.compareAndSetReference(this, TEST31_VT_OFFSET, oldVal, newVal);
+        }
+    }
+
+    @Run(test = "test66")
+    public void test66_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        test31_vt = MyValue1.createDefaultInline();
+
+        boolean res = test66(test31_vt, vt);
+        Asserts.assertTrue(res);
+        Asserts.assertEQ(test31_vt, vt);
+
+        res = test66(MyValue1.createDefaultInline(), MyValue1.createDefaultInline());
+        Asserts.assertFalse(res);
+        Asserts.assertEQ(test31_vt, vt);
+    }
+
+    // compareAndExchange to flattened field in object
+    @Test
+    public Object test67(MyValue1 oldVal, MyValue1 newVal) {
+        if (TEST31_VT_FLATTENED) {
+            return U.compareAndExchangeValue(this, TEST31_VT_OFFSET, MyValue1.class, oldVal, newVal);
+        } else {
+            return U.compareAndExchangeReference(this, TEST31_VT_OFFSET, oldVal, newVal);
+        }
+    }
+
+    @Run(test = "test67")
+    public void test67_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1 oldVal = MyValue1.createDefaultInline();
+        test31_vt = oldVal;
+
+        Object res = test67(test31_vt, vt);
+        // Checks are disabled for non-flattened field because reference comparison
+        // fails if C2 scalarizes and re-allocates the value class arguments.
+        if (TEST31_VT_FLATTENED) {
+            Asserts.assertEQ(res, oldVal);
+            Asserts.assertEQ(test31_vt, vt);
+        }
+
+        res = test67(MyValue1.createDefaultInline(), MyValue1.createDefaultInline());
+        if (TEST31_VT_FLATTENED) {
+            Asserts.assertEQ(res, vt);
+            Asserts.assertEQ(test31_vt, vt);
+        }
+    }
+
+    // compareAndExchange to flattened field in array
+    @Test
+    public Object test68(MyValue1[] arr, MyValue1 oldVal, Object newVal) {
+        if (TEST33_FLATTENED_ARRAY) {
+            return U.compareAndExchangeValue(arr, TEST33_BASE_OFFSET + TEST33_INDEX_SCALE, MyValue1.class, oldVal, newVal);
+        } else {
+            return U.compareAndExchangeReference(arr, TEST33_BASE_OFFSET + TEST33_INDEX_SCALE, oldVal, newVal);
+        }
+    }
+
+    @Run(test = "test68")
+    public void test68_verifier() {
+        MyValue1[] arr = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+
+        Object res = test68(arr, arr[1], vt);
+        // Checks are disabled for non-flattened array because reference comparison
+        // fails if C2 scalarizes and re-allocates the value class arguments.
+        if (TEST33_FLATTENED_ARRAY) {
+            Asserts.assertEQ(res, MyValue1.createDefaultInline());
+            Asserts.assertEQ(arr[1], vt);
+        }
+
+        res = test68(arr, MyValue1.createDefaultInline(), MyValue1.createDefaultInline());
+        if (TEST33_FLATTENED_ARRAY) {
+            Asserts.assertEQ(res, vt);
+            Asserts.assertEQ(arr[1], vt);
+        }
+    }
+
+    // compareAndExchange to flattened field in object with unknown container
+    @Test
+    public Object test69(Object o, Object oldVal, MyValue1 newVal) {
+        if (TEST31_VT_FLATTENED) {
+            return U.compareAndExchangeValue(o, TEST31_VT_OFFSET, MyValue1.class, oldVal, newVal);
+        } else {
+            return U.compareAndExchangeReference(o, TEST31_VT_OFFSET, oldVal, newVal);
+        }
+    }
+
+    @Run(test = "test69")
+    public void test69_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1 oldVal = MyValue1.createDefaultInline();
+        test31_vt = oldVal;
+
+        Object res = test69(this, test31_vt, vt);
+        Asserts.assertEQ(res, oldVal);
+        Asserts.assertEQ(test31_vt, vt);
+
+        res = test69(this, MyValue1.createDefaultInline(), MyValue1.createDefaultInline());
+        Asserts.assertEQ(res, vt);
+        Asserts.assertEQ(test31_vt, vt);
+    }
+
+    // compareAndExchange to flattened field in object, non-inline arguments to compare and set
+    @Test
+    public Object test70(Object oldVal, Object newVal) {
+        if (TEST31_VT_FLATTENED) {
+            return U.compareAndExchangeValue(this, TEST31_VT_OFFSET, MyValue1.class, oldVal, newVal);
+        } else {
+            return U.compareAndExchangeReference(this, TEST31_VT_OFFSET, oldVal, newVal);
+        }
+    }
+
+    @Run(test = "test70")
+    public void test70_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1 oldVal = MyValue1.createDefaultInline();
+        test31_vt = oldVal;
+
+        Object res = test70(test31_vt, vt);
+        Asserts.assertEQ(res, oldVal);
+        Asserts.assertEQ(test31_vt, vt);
+
+        res = test70(MyValue1.createDefaultInline(), MyValue1.createDefaultInline());
+        Asserts.assertEQ(res, vt);
+        Asserts.assertEQ(test31_vt, vt);
+    }
+
+    // getValue to retrieve flattened field from (nullable) value class
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public MyValue2 test71(boolean b, MyValue1 v1, MyValue1 v2) {
+        if (b) {
+            if (V1_FLATTENED) {
+                return U.getValue(v1, V1_OFFSET, MyValue2.class);
+            }
+            return (MyValue2)U.getReference(v1, V1_OFFSET);
+        } else {
+            if (V1_FLATTENED) {
+                return U.getValue(v2, V1_OFFSET, MyValue2.class);
+            }
+            return (MyValue2)U.getReference(v2, V1_OFFSET);
+        }
+    }
+
+    @Run(test = "test71")
+    public void test71_verifier() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        Asserts.assertEQ(test71(true, v, v), v.v1);
+        Asserts.assertEQ(test71(false, v, v), v.v1);
+    }
+
+    // Same as test71 but with non-constant offset
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public MyValue2 test72(boolean b, MyValue1 v1, MyValue1 v2, long offset) {
+        if (b) {
+            if (V1_FLATTENED) {
+                return U.getValue(v1, offset, MyValue2.class);
+            }
+            return (MyValue2)U.getReference(v1, offset);
+        } else {
+            if (V1_FLATTENED) {
+                return U.getValue(v2, offset, MyValue2.class);
+            }
+            return (MyValue2)U.getReference(v2, offset);
+        }
+    }
+
+    @Run(test = "test72")
+    public void test72_verifier() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        Asserts.assertEQ(test72(true, v, v, V1_OFFSET), v.v1);
+        Asserts.assertEQ(test72(false, v, v, V1_OFFSET), v.v1);
+    }
+
+    @NullRestricted
+    static final MyValue1 test73_value1 = MyValue1.createWithFieldsInline(rI, rL);
+    static final MyValue1 test73_value2 = MyValue1.createWithFieldsInline(rI+1, rL+1);
+
+    // Same as test72 but with constant base
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public MyValue2 test73(boolean b, long offset) {
+        if (b) {
+            if (V1_FLATTENED) {
+                return U.getValue(test73_value1, offset, MyValue2.class);
+            }
+            return (MyValue2)U.getReference(test73_value1, offset);
+        } else {
+            if (V1_FLATTENED) {
+                return U.getValue(test73_value2, offset, MyValue2.class);
+            }
+            return (MyValue2)U.getReference(test73_value2, offset);
+        }
+    }
+
+    @Run(test = "test73")
+    public void test73_verifier() {
+        Asserts.assertEQ(test73(true, V1_OFFSET), test73_value1.v1);
+        Asserts.assertEQ(test73(false, V1_OFFSET), test73_value2.v1);
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class EmptyInline {
+
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class ByteInline {
+        byte x = 0;
+    }
+
+    @Test
+    public void test74(EmptyInline[] emptyArray) {
+        System.arraycopy(emptyArray, 0, emptyArray, 10, 10);
+        System.arraycopy(emptyArray, 0, emptyArray, 20, 10);
+    }
+
+    @Run(test = "test74")
+    public void test74_verifier() {
+        EmptyInline[] emptyArray = (EmptyInline[])ValueClass.newNullRestrictedArray(EmptyInline.class, 100);
+        test74(emptyArray);
+        for (EmptyInline empty : emptyArray) {
+            Asserts.assertEQ(empty, new EmptyInline());
+        }
+    }
+
+    @Test
+    public void test75(EmptyInline[] emptyArray) {
+        System.arraycopy(emptyArray, 0, emptyArray, 10, 10);
+    }
+
+    @Run(test = "test75")
+    public void test75_verifier() {
+        EmptyInline[] emptyArray = (EmptyInline[])ValueClass.newNullRestrictedArray(EmptyInline.class, 100);
+        test75(emptyArray);
+        for (EmptyInline empty : emptyArray) {
+            Asserts.assertEQ(empty, new EmptyInline());
+        }
+    }
+
+    @Test
+    public void test76(ByteInline[] byteArray) {
+        System.arraycopy(byteArray, 0, byteArray, 10, 10);
+        System.arraycopy(byteArray, 0, byteArray, 20, 10);
+    }
+
+    @Run(test = "test76")
+    public void test76_verifier() {
+        ByteInline[] byteArray = (ByteInline[])ValueClass.newNullRestrictedArray(ByteInline.class, 100);
+        test76(byteArray);
+        for (ByteInline b : byteArray) {
+            Asserts.assertEQ(b, new ByteInline());
+        }
+    }
+
+    @Test
+    public void test77(ByteInline[] byteArray) {
+        System.arraycopy(byteArray, 0, byteArray, 10, 10);
+    }
+
+    @Run(test = "test77")
+    public void test77_verifier() {
+        ByteInline[] byteArray = (ByteInline[])ValueClass.newNullRestrictedArray(ByteInline.class, 100);
+        test77(byteArray);
+        for (ByteInline b : byteArray) {
+            Asserts.assertEQ(b, new ByteInline());
+        }
+    }
+
+    @Test
+    public Object test78(MyValue1 vt) {
+        return NonValueClass.class.cast(vt);
+    }
+
+    @Run(test = "test78")
+    public void test78_verifier() {
+        Object result = test78(null);
+        Asserts.assertEQ(result, null);
+        try {
+            test78(MyValue1.createWithFieldsInline(rI, rL));
+            throw new RuntimeException("should have thrown");
+        } catch (ClassCastException cce) {
+        }
+    }
+
+    // TODO 8284443 Fix this in GraphKit::gen_checkcast
+    /*
+    @Test
+    public Object test79(MyValue1 vt) {
+        Object tmp = vt;
+        return (NonValueClass)tmp;
+    }
+
+    @Run(test = "test79")
+    public void test79_verifier() {
+        Object result = test79(null);
+        Asserts.assertEQ(result, null);
+        try {
+            test79(MyValue1.createWithFieldsInline(rI, rL));
+            throw new RuntimeException("should have thrown");
+        } catch (ClassCastException cce) {
+        }
+    }
+    */
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    public static value class Test80Value1 {
+        @NullRestricted
+        Test80Value2 v = new Test80Value2();
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    public static value class Test80Value2 {
+        long l = rL;
+        NonValueClass obj = new NonValueClass(rI);
+    }
+
+    // Test that unsafe access is not incorrectly classified as mismatched
+    @Test
+    @IR(failOn = {CALL_UNSAFE})
+    public Test80Value2 test80(Test80Value1 v, boolean flat, long offset) {
+        if (flat) {
+            return U.getValue(v, offset, Test80Value2.class);
+        } else {
+            return (Test80Value2)U.getReference(v, offset);
+        }
+    }
+
+    @Run(test = "test80")
+    public void test80_verifier() throws Exception {
+        Test80Value1 v = new Test80Value1();
+        Field field = Test80Value1.class.getDeclaredField("v");
+        Asserts.assertEQ(test80(v, U.isFlatField(field), U.objectFieldOffset(field)), v.v);
+    }
+
+    // Test correctness of the Unsafe::isFlatArray intrinsic
+    @Test
+    public boolean test81(Class<?> cls) {
+        return U.isFlatArray(cls);
+    }
+
+    @Run(test = "test81")
+    public void test81_verifier() {
+        Asserts.assertEQ(test81(TEST33_ARRAY.getClass()), TEST33_FLATTENED_ARRAY, "test81_1 failed");
+        Asserts.assertFalse(test81(String[].class), "test81_2 failed");
+        Asserts.assertFalse(test81(String.class), "test81_3 failed");
+        Asserts.assertFalse(test81(int[].class), "test81_4 failed");
+    }
+
+    // Verify that Unsafe::isFlatArray checks with statically known classes
+    // are folded
+    @Test
+    @IR(failOn = {LOADK})
+    public boolean test82() {
+        boolean check1 = U.isFlatArray(TEST33_ARRAY.getClass());
+        if (!TEST33_FLATTENED_ARRAY) {
+            check1 = !check1;
+        }
+        boolean check2 = !U.isFlatArray(String[].class);
+        boolean check3 = !U.isFlatArray(String.class);
+        boolean check4 = !U.isFlatArray(int[].class);
+        return check1 && check2 && check3 && check4;
+    }
+
+    @Run(test = "test82")
+    public void test82_verifier() {
+        Asserts.assertTrue(test82(), "test82 failed");
+    }
+
+    // Test that LibraryCallKit::arraycopy_move_allocation_here works as expected
+    @Test
+    public MyValue1 test83(Object[] src) {
+        MyValue1[] dst = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 10);
+        System.arraycopy(src, 0, dst, 0, 10);
+        return dst[0];
+    }
+
+    @Run(test = "test83")
+    public void test83_verifier(RunInfo info) {
+        if (info.isWarmUp()) {
+            MyValue1[] src = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 10);
+            Asserts.assertEQ(test83(src), src[0]);
+        } else {
+            // Trigger deoptimization to verify that re-execution works
+            try {
+                test83(new NonValueClass[10]);
+                throw new RuntimeException("No NullPointerException thrown");
+            } catch (NullPointerException npe) {
+                // Expected
+            }
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIsSubstitutableReresolution.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIsSubstitutableReresolution.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIsSubstitutableReresolution.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ *
+ * 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 compiler.valhalla.inlinetypes;
+
+import jdk.test.lib.Asserts;
+
+/**
+ * @test
+ * @bug 8234108
+ * @library /testlibrary /test/lib
+ * @summary Verify that call reresolution works for C2 compiled calls to java.lang.runtime.ValueObjectMethods::isSubstitutable0.
+ * @enablePreview
+ * @run main/othervm -XX:CompileCommand=dontinline,compiler.valhalla.inlinetypes.TestIsSubstitutableReresolution::test
+ *                   compiler.valhalla.inlinetypes.TestIsSubstitutableReresolution
+ */
+
+value class MyValue {
+    int x;
+
+    public MyValue(int x) {
+        this.x = x;
+    }
+}
+
+public class TestIsSubstitutableReresolution {
+
+    static boolean test(Object obj) {
+        MyValue vt = new MyValue(42);
+        return vt == obj;
+    }
+
+    public static void main(String[] args) throws Exception {
+        MyValue vt1 = new MyValue(42);
+        MyValue vt2 = new MyValue(43);
+        for (int i = 0; i < 1_000_000; ++i) {
+            Asserts.assertEQ(test(vt1), true);
+            Asserts.assertEQ(test(vt2), false);
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestJNICalls.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestJNICalls.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestJNICalls.java
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ *
+ * 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 compiler.valhalla.inlinetypes;
+
+import jdk.test.lib.Asserts;
+import compiler.lib.ir_framework.*;
+
+import static compiler.valhalla.inlinetypes.InlineTypes.rI;
+import static compiler.valhalla.inlinetypes.InlineTypes.rL;
+
+/*
+ * @test
+ * @key randomness
+ * @summary Test calling native methods with value class arguments from compiled code.
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm/timeout=300 compiler.valhalla.inlinetypes.TestJNICalls
+ */
+
+@ForceCompileClassInitializer
+public class TestJNICalls {
+
+    public static void main(String[] args) {
+        Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS;
+
+        InlineTypes.getFramework()
+                   .addScenarios(scenarios)
+                   .addHelperClasses(MyValue1.class)
+                   .start();
+    }
+
+    static {
+        System.loadLibrary("TestJNICalls");
+    }
+
+    public native Object testMethod1(MyValue1 o);
+    public native long testMethod2(MyValue1 o);
+
+    // Pass a value object to a native method that calls back into Java code and returns a value object
+    @Test
+    public MyValue1 test1(MyValue1 vt, boolean callback) {
+        if (!callback) {
+          return (MyValue1)testMethod1(vt);
+        } else {
+          return vt;
+        }
+    }
+
+    @Run(test = "test1")
+    @Warmup(10000) // Make sure native method is compiled
+    public void test1_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1 result = test1(vt, false);
+        Asserts.assertEQ(result.hash(), vt.hash());
+        result = test1(vt, true);
+        Asserts.assertEQ(result.hash(), vt.hash());
+    }
+
+    // Pass a value object to a native method that calls the hash method and returns the result
+    @Test
+    public long test2(MyValue1 vt) {
+        return testMethod2(vt);
+    }
+
+    @Run(test = "test2")
+    @Warmup(10000) // Make sure native method is compiled
+    public void test2_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        long result = test2(vt);
+        Asserts.assertEQ(result, vt.hash());
+    }
+
+    static value class MyValueWithNative {
+        public int x;
+
+        private MyValueWithNative(int x) {
+            this.x = x;
+        }
+
+        public native int testMethod3();
+    }
+
+    // Call a native method with a value class receiver
+    @Test
+    public int test3(MyValueWithNative vt) {
+        return vt.testMethod3();
+    }
+
+    @Run(test = "test3")
+    @Warmup(10000) // Make sure native method is compiled
+    public void test3_verifier() {
+        MyValueWithNative vt = new MyValueWithNative(rI);
+        int result = test3(vt);
+        Asserts.assertEQ(result, rI);
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java
@@ -0,0 +1,4375 @@
+/*
+ * 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.
+ *
+ * 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.*;
+import jdk.test.lib.Asserts;
+import test.java.lang.invoke.lib.OldInstructionHelper;
+
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import jdk.experimental.bytecode.TypeTag;
+import java.util.Arrays;
+import java.util.Objects;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import static compiler.valhalla.inlinetypes.InlineTypeIRNode.*;
+import static compiler.valhalla.inlinetypes.InlineTypes.*;
+
+/*
+ * @test
+ * @key randomness
+ * @summary Test inline types in LWorld.
+ * @library /test/lib /test/jdk/lib/testlibrary/bytecode /test/jdk/java/lang/invoke/common /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.OldInstructionHelper
+ * @run main/othervm/timeout=450 compiler.valhalla.inlinetypes.TestLWorld
+ */
+
+@ForceCompileClassInitializer
+public class TestLWorld {
+
+    public static void main(String[] args) {
+        // Make sure Test140Value is loaded but not linked
+        Class<?> class1 = Test140Value.class;
+        // Make sure Test141Value is linked but not initialized
+        Class<?> class2 = Test141Value.class;
+        class2.getDeclaredFields();
+
+        Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS;
+        scenarios[3].addFlags("-XX:-MonomorphicArrayCheck", "-XX:FlatArrayElementMaxSize=-1");
+        scenarios[4].addFlags("-XX:-MonomorphicArrayCheck");
+
+        InlineTypes.getFramework()
+                   .addScenarios(scenarios)
+                   .addHelperClasses(MyValue1.class,
+                                     MyValue2.class,
+                                     MyValue2Inline.class,
+                                     MyValue3.class,
+                                     MyValue3Inline.class)
+                   .start();
+    }
+
+    static {
+        // Make sure RuntimeException is loaded to prevent uncommon traps in IR verified tests
+        RuntimeException tmp = new RuntimeException("42");
+    }
+
+    // Helper methods
+
+    @NullRestricted
+    private static final MyValue1 testValue1 = MyValue1.createWithFieldsInline(rI, rL);
+    @NullRestricted
+    private static final MyValue2 testValue2 = MyValue2.createWithFieldsInline(rI, rD);
+
+    protected long hash() {
+        return testValue1.hash();
+    }
+
+    // Test passing an inline type as an Object
+    @DontInline
+    public Object test1_dontinline1(Object o) {
+        return o;
+    }
+
+    @DontInline
+    public MyValue1 test1_dontinline2(Object o) {
+        return (MyValue1)o;
+    }
+
+    @ForceInline
+    public Object test1_inline1(Object o) {
+        return o;
+    }
+
+    @ForceInline
+    public MyValue1 test1_inline2(Object o) {
+        return (MyValue1)o;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public MyValue1 test1() {
+        MyValue1 vt = testValue1;
+        vt = (MyValue1)test1_dontinline1(vt);
+        vt =           test1_dontinline2(vt);
+        vt = (MyValue1)test1_inline1(vt);
+        vt =           test1_inline2(vt);
+        return vt;
+    }
+
+    @Run(test = "test1")
+    public void test1_verifier() {
+        Asserts.assertEQ(test1().hash(), hash());
+    }
+
+    // Test storing/loading inline types to/from Object and inline type fields
+    Object objectField1 = null;
+    Object objectField2 = null;
+    Object objectField3 = null;
+    Object objectField4 = null;
+    Object objectField5 = null;
+    Object objectField6 = null;
+
+    @NullRestricted
+    MyValue1 valueField1 = testValue1;
+    @NullRestricted
+    MyValue1 valueField2 = testValue1;
+    MyValue1 valueField3 = testValue1;
+    @NullRestricted
+    MyValue1 valueField4;
+    MyValue1 valueField5;
+
+    static MyValue1 staticValueField1 = testValue1;
+    @NullRestricted
+    static MyValue1 staticValueField2 = testValue1;
+    @NullRestricted
+    static MyValue1 staticValueField3;
+    static MyValue1 staticValueField4;
+
+    @DontInline
+    public Object readValueField5() {
+        return (Object)valueField5;
+    }
+
+    @DontInline
+    public Object readStaticValueField4() {
+        return (Object)staticValueField4;
+    }
+
+    @Test
+    public long test2(MyValue1 vt1, Object vt2) {
+        objectField1 = vt1;
+        objectField2 = (MyValue1)vt2;
+        objectField3 = testValue1;
+        objectField4 = MyValue1.createWithFieldsDontInline(rI, rL);
+        objectField5 = valueField1;
+        objectField6 = valueField3;
+        valueField1 = (MyValue1)objectField1;
+        valueField2 = (MyValue1)vt2;
+        valueField3 = (MyValue1)vt2;
+        staticValueField1 = (MyValue1)objectField1;
+        staticValueField2 = (MyValue1)vt1;
+        // Don't inline these methods because reading NULL will trigger a deoptimization
+        if (readValueField5() != null || readStaticValueField4() != null) {
+            throw new RuntimeException("Should be null");
+        }
+        return ((MyValue1)objectField1).hash() + ((MyValue1)objectField2).hash() +
+               ((MyValue1)objectField3).hash() + ((MyValue1)objectField4).hash() +
+               ((MyValue1)objectField5).hash() + ((MyValue1)objectField6).hash() +
+                valueField1.hash() + valueField2.hash() + valueField3.hash() + valueField4.hashPrimitive() +
+                staticValueField1.hash() + staticValueField2.hash() + staticValueField3.hashPrimitive();
+    }
+
+    @Run(test = "test2")
+    public void test2_verifier() {
+        MyValue1 vt = testValue1;
+        MyValue1 def = MyValue1.createDefaultDontInline();
+        long result = test2(vt, vt);
+        Asserts.assertEQ(result, 11*vt.hash() + 2*def.hashPrimitive());
+    }
+
+    // Test merging inline types and objects
+    @Test
+    public Object test3(int state) {
+        Object res = null;
+        if (state == 0) {
+            res = new NonValueClass(rI);
+        } else if (state == 1) {
+            res = MyValue1.createWithFieldsInline(rI, rL);
+        } else if (state == 2) {
+            res = MyValue1.createWithFieldsDontInline(rI, rL);
+        } else if (state == 3) {
+            res = (MyValue1)objectField1;
+        } else if (state == 4) {
+            res = valueField1;
+        } else if (state == 5) {
+            res = null;
+        } else if (state == 6) {
+            res = MyValue2.createWithFieldsInline(rI, rD);
+        } else if (state == 7) {
+            res = testValue2;
+        }
+        return res;
+    }
+
+    @Run(test = "test3")
+    public void test3_verifier() {
+        objectField1 = valueField1;
+        Object result = null;
+        result = test3(0);
+        Asserts.assertEQ(((NonValueClass)result).x, rI);
+        result = test3(1);
+        Asserts.assertEQ(((MyValue1)result).hash(), hash());
+        result = test3(2);
+        Asserts.assertEQ(((MyValue1)result).hash(), hash());
+        result = test3(3);
+        Asserts.assertEQ(((MyValue1)result).hash(), hash());
+        result = test3(4);
+        Asserts.assertEQ(((MyValue1)result).hash(), hash());
+        result = test3(5);
+        Asserts.assertEQ(result, null);
+        result = test3(6);
+        Asserts.assertEQ(((MyValue2)result).hash(), testValue2.hash());
+        result = test3(7);
+        Asserts.assertEQ(((MyValue2)result).hash(), testValue2.hash());
+    }
+
+    // Test merging inline types and objects in loops
+    @Test
+    public Object test4(int iters) {
+        Object res = new NonValueClass(rI);
+        for (int i = 0; i < iters; ++i) {
+            if (res instanceof NonValueClass) {
+                res = MyValue1.createWithFieldsInline(rI, rL);
+            } else {
+                res = MyValue1.createWithFieldsInline(((MyValue1)res).x + 1, rL);
+            }
+        }
+        return res;
+    }
+
+    @Run(test = "test4")
+    public void test4_verifier() {
+        NonValueClass result1 = (NonValueClass)test4(0);
+        Asserts.assertEQ(result1.x, rI);
+        int iters = (Math.abs(rI) % 10) + 1;
+        MyValue1 result2 = (MyValue1)test4(iters);
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI + iters - 1, rL);
+        Asserts.assertEQ(result2.hash(), vt.hash());
+    }
+
+    // Test inline types in object variables that are live at safepoint
+    @Test
+    @IR(failOn = {ALLOC, STORE, LOOP})
+    public long test5(MyValue1 arg, boolean deopt, Method m) {
+        Object vt1 = MyValue1.createWithFieldsInline(rI, rL);
+        Object vt2 = MyValue1.createWithFieldsDontInline(rI, rL);
+        Object vt3 = arg;
+        Object vt4 = valueField1;
+        if (deopt) {
+            // uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return ((MyValue1)vt1).hash() + ((MyValue1)vt2).hash() +
+               ((MyValue1)vt3).hash() + ((MyValue1)vt4).hash();
+    }
+
+    @Run(test = "test5")
+    public void test5_verifier(RunInfo info) {
+        long result = test5(valueField1, !info.isWarmUp(), info.getTest());
+        Asserts.assertEQ(result, 4*hash());
+    }
+
+    // Test comparing inline types with objects
+    @Test
+    public boolean test6(Object arg) {
+        Object vt = MyValue1.createWithFieldsInline(rI, rL);
+        if (vt == arg || vt == (Object)valueField1 || vt == objectField1 || vt == null ||
+            arg == vt || (Object)valueField1 == vt || objectField1 == vt || null == vt) {
+            return true;
+        }
+        return false;
+    }
+
+    @Run(test = "test6")
+    public void test6_verifier() {
+        boolean result = test6(null);
+        Asserts.assertFalse(result);
+    }
+
+    // merge of inline type and non-inline type
+    @Test
+    public Object test7(boolean flag) {
+        Object res = null;
+        if (flag) {
+            res = valueField1;
+        } else {
+            res = objectField1;
+        }
+        return res;
+    }
+
+    @Run(test = "test7")
+    public void test7_verifier() {
+        test7(true);
+        test7(false);
+    }
+
+    @Test
+    public Object test8(boolean flag) {
+        Object res = null;
+        if (flag) {
+            res = objectField1;
+        } else {
+            res = valueField1;
+        }
+        return res;
+    }
+
+    @Run(test = "test8")
+    public void test8_verifier() {
+        test8(true);
+        test8(false);
+    }
+
+    // merge of inline types in a loop, stored in an object local
+    @Test
+    public Object test9() {
+        Object o = valueField1;
+        for (int i = 1; i < 100; i *= 2) {
+            MyValue1 v = (MyValue1)o;
+            o = MyValue1.setX(v, v.x + 1);
+        }
+        return o;
+    }
+
+    @Run(test = "test9")
+    public void test9_verifier() {
+        test9();
+    }
+
+    // merge of inline types in an object local
+    @ForceInline
+    public Object test10_helper() {
+        return valueField1;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public void test10(boolean flag) {
+        Object o = null;
+        if (flag) {
+            o = valueField1;
+        } else {
+            o = test10_helper();
+        }
+        valueField1 = (MyValue1)o;
+    }
+
+    @Run(test = "test10")
+    public void test10_verifier() {
+        test10(true);
+        test10(false);
+    }
+
+    // Interface tests
+
+    @DontInline
+    public MyInterface test11_dontinline1(MyInterface o) {
+        return o;
+    }
+
+    @DontInline
+    public MyValue1 test11_dontinline2(MyInterface o) {
+        return (MyValue1)o;
+    }
+
+    @ForceInline
+    public MyInterface test11_inline1(MyInterface o) {
+        return o;
+    }
+
+    @ForceInline
+    public MyValue1 test11_inline2(MyInterface o) {
+        return (MyValue1)o;
+    }
+
+    @Test
+    public MyValue1 test11() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        vt = (MyValue1)test11_dontinline1(vt);
+        vt =           test11_dontinline2(vt);
+        vt = (MyValue1)test11_inline1(vt);
+        vt =           test11_inline2(vt);
+        return vt;
+    }
+
+    @Run(test = "test11")
+    public void test11_verifier() {
+        Asserts.assertEQ(test11().hash(), hash());
+    }
+
+    // Test storing/loading inline types to/from interface and inline type fields
+    MyInterface interfaceField1 = null;
+    MyInterface interfaceField2 = null;
+    MyInterface interfaceField3 = null;
+    MyInterface interfaceField4 = null;
+    MyInterface interfaceField5 = null;
+    MyInterface interfaceField6 = null;
+
+    @DontInline
+    public MyInterface readValueField5AsInterface() {
+        return (MyInterface)valueField5;
+    }
+
+    @DontInline
+    public MyInterface readStaticValueField4AsInterface() {
+        return (MyInterface)staticValueField4;
+    }
+
+    @Test
+    public long test12(MyValue1 vt1, MyInterface vt2) {
+        interfaceField1 = vt1;
+        interfaceField2 = (MyValue1)vt2;
+        interfaceField3 = MyValue1.createWithFieldsInline(rI, rL);
+        interfaceField4 = MyValue1.createWithFieldsDontInline(rI, rL);
+        interfaceField5 = valueField1;
+        interfaceField6 = valueField3;
+        valueField1 = (MyValue1)interfaceField1;
+        valueField2 = (MyValue1)vt2;
+        valueField3 = (MyValue1)vt2;
+        staticValueField1 = (MyValue1)interfaceField1;
+        staticValueField2 = (MyValue1)vt1;
+        // Don't inline these methods because reading NULL will trigger a deoptimization
+        if (readValueField5AsInterface() != null || readStaticValueField4AsInterface() != null) {
+            throw new RuntimeException("Should be null");
+        }
+        return ((MyValue1)interfaceField1).hash() + ((MyValue1)interfaceField2).hash() +
+               ((MyValue1)interfaceField3).hash() + ((MyValue1)interfaceField4).hash() +
+               ((MyValue1)interfaceField5).hash() + ((MyValue1)interfaceField6).hash() +
+                valueField1.hash() + valueField2.hash() + valueField3.hash() + valueField4.hashPrimitive() +
+                staticValueField1.hash() + staticValueField2.hash() + staticValueField3.hashPrimitive();
+    }
+
+    @Run(test = "test12")
+    public void test12_verifier() {
+        MyValue1 vt = testValue1;
+        MyValue1 def = MyValue1.createDefaultDontInline();
+        long result = test12(vt, vt);
+        Asserts.assertEQ(result, 11*vt.hash() + 2*def.hashPrimitive());
+    }
+
+    class MyObject1 implements MyInterface {
+        public int x;
+
+        public MyObject1(int x) {
+            this.x = x;
+        }
+
+        @ForceInline
+        public long hash() {
+            return x;
+        }
+    }
+
+    // Test merging inline types and interfaces
+    @Test
+    public MyInterface test13(int state) {
+        MyInterface res = null;
+        if (state == 0) {
+            res = new MyObject1(rI);
+        } else if (state == 1) {
+            res = MyValue1.createWithFieldsInline(rI, rL);
+        } else if (state == 2) {
+            res = MyValue1.createWithFieldsDontInline(rI, rL);
+        } else if (state == 3) {
+            res = (MyValue1)objectField1;
+        } else if (state == 4) {
+            res = valueField1;
+        } else if (state == 5) {
+            res = null;
+        }
+        return res;
+    }
+
+    @Run(test = "test13")
+    public void test13_verifier() {
+        objectField1 = valueField1;
+        MyInterface result = null;
+        result = test13(0);
+        Asserts.assertEQ(((MyObject1)result).x, rI);
+        result = test13(1);
+        Asserts.assertEQ(((MyValue1)result).hash(), hash());
+        result = test13(2);
+        Asserts.assertEQ(((MyValue1)result).hash(), hash());
+        result = test13(3);
+        Asserts.assertEQ(((MyValue1)result).hash(), hash());
+        result = test13(4);
+        Asserts.assertEQ(((MyValue1)result).hash(), hash());
+        result = test13(5);
+        Asserts.assertEQ(result, null);
+    }
+
+    // Test merging inline types and interfaces in loops
+    @Test
+    public MyInterface test14(int iters) {
+        MyInterface res = new MyObject1(rI);
+        for (int i = 0; i < iters; ++i) {
+            if (res instanceof MyObject1) {
+                res = MyValue1.createWithFieldsInline(rI, rL);
+            } else {
+                res = MyValue1.createWithFieldsInline(((MyValue1)res).x + 1, rL);
+            }
+        }
+        return res;
+    }
+
+    @Run(test = "test14")
+    public void test14_verifier() {
+        MyObject1 result1 = (MyObject1)test14(0);
+        Asserts.assertEQ(result1.x, rI);
+        int iters = (Math.abs(rI) % 10) + 1;
+        MyValue1 result2 = (MyValue1)test14(iters);
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI + iters - 1, rL);
+        Asserts.assertEQ(result2.hash(), vt.hash());
+    }
+
+    // Test inline types in interface variables that are live at safepoint
+    @Test
+    @IR(failOn = {ALLOC, STORE, LOOP})
+    public long test15(MyValue1 arg, boolean deopt, Method m) {
+        MyInterface vt1 = MyValue1.createWithFieldsInline(rI, rL);
+        MyInterface vt2 = MyValue1.createWithFieldsDontInline(rI, rL);
+        MyInterface vt3 = arg;
+        MyInterface vt4 = valueField1;
+        return ((MyValue1)vt1).hash() + ((MyValue1)vt2).hash() +
+               ((MyValue1)vt3).hash() + ((MyValue1)vt4).hash();
+    }
+
+    @Run(test = "test15")
+    public void test15_verifier(RunInfo info) {
+        long result = test15(valueField1, !info.isWarmUp(), info.getTest());
+        Asserts.assertEQ(result, 4*hash());
+    }
+
+    // Test comparing inline types with interfaces
+    @Test
+    public boolean test16(Object arg) {
+        MyInterface vt = MyValue1.createWithFieldsInline(rI, rL);
+        if (vt == arg || vt == (MyInterface)valueField1 || vt == interfaceField1 || vt == null ||
+            arg == vt || (MyInterface)valueField1 == vt || interfaceField1 == vt || null == vt) {
+            return true;
+        }
+        return false;
+    }
+
+    @Run(test = "test16")
+    public void test16_verifier() {
+        boolean result = test16(null);
+        Asserts.assertFalse(result);
+    }
+
+    // Test subtype check when casting to inline type
+    @Test
+    @IR(failOn = {ALLOC})
+    public MyValue1 test17(MyValue1 vt, Object obj) {
+        try {
+            vt = (MyValue1)obj;
+            throw new RuntimeException("ClassCastException expected");
+        } catch (ClassCastException e) {
+            // Expected
+        }
+        return vt;
+    }
+
+    @Run(test = "test17")
+    public void test17_verifier() {
+        MyValue1 vt = testValue1;
+        MyValue1 result = test17(vt, new NonValueClass(rI));
+        Asserts.assertEquals(result.hash(), vt.hash());
+    }
+
+    @Test
+    public MyValue1 test18(MyValue1 vt) {
+        Object obj = vt;
+        vt = (MyValue1)obj;
+        return vt;
+    }
+
+    @Run(test = "test18")
+    public void test18_verifier() {
+        MyValue1 vt = testValue1;
+        MyValue1 result = test18(vt);
+        Asserts.assertEquals(result.hash(), vt.hash());
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public void test19(MyValue1 vt) {
+        if (vt == null) {
+            return;
+        }
+        Object obj = vt;
+        try {
+            MyValue2 vt2 = (MyValue2)obj;
+            throw new RuntimeException("ClassCastException expected");
+        } catch (ClassCastException e) {
+            // Expected
+        }
+    }
+
+    @Run(test = "test19")
+    public void test19_verifier() {
+        test19(valueField1);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public void test20(MyValue1 vt) {
+        if (vt == null) {
+            return;
+        }
+        Object obj = vt;
+        try {
+            NonValueClass i = (NonValueClass)obj;
+            throw new RuntimeException("ClassCastException expected");
+        } catch (ClassCastException e) {
+            // Expected
+        }
+    }
+
+    @Run(test = "test20")
+    public void test20_verifier() {
+        test20(valueField1);
+    }
+
+    // Array tests
+
+    private static final MyValue1[] testValue1Array = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 3);
+    static {
+        for (int i = 0; i < 3; ++i) {
+            testValue1Array[i] = testValue1;
+        }
+    }
+
+    private static final MyValue1[][] testValue1Array2 = new MyValue1[][] {testValue1Array,
+                                                                           testValue1Array,
+                                                                           testValue1Array};
+
+    private static final MyValue2[] testValue2Array = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 3);
+    static {
+        for (int i = 0; i < 3; ++i) {
+            testValue2Array[i] = testValue2;
+        }
+    }
+
+    private static final NonValueClass[] testNonValueArray = new NonValueClass[42];
+
+    // Test load from (flattened) inline type array disguised as object array
+    @Test
+    public Object test21(Object[] oa, int index) {
+        return oa[index];
+    }
+
+    @Run(test = "test21")
+    public void test21_verifier() {
+        MyValue1 result = (MyValue1)test21(testValue1Array, Math.abs(rI) % 3);
+        Asserts.assertEQ(result.hash(), hash());
+    }
+
+    // Test load from (flattened) inline type array disguised as interface array
+    @Test
+    public Object test22Interface(MyInterface[] ia, int index) {
+        return ia[index];
+    }
+
+    @Run(test = "test22Interface")
+    public void test22Interface_verifier() {
+        MyValue1 result = (MyValue1)test22Interface(testValue1Array, Math.abs(rI) % 3);
+        Asserts.assertEQ(result.hash(), hash());
+    }
+
+    // Test load from (flattened) inline type array disguised as abstract array
+    @Test
+    public Object test22Abstract(MyAbstract[] ia, int index) {
+        return ia[index];
+    }
+
+    @Run(test = "test22Abstract")
+    public void test22Abstract_verifier() {
+        MyValue1 result = (MyValue1)test22Abstract(testValue1Array, Math.abs(rI) % 3);
+        Asserts.assertEQ(result.hash(), hash());
+    }
+
+    // Test inline store to (flattened) inline type array disguised as object array
+    @ForceInline
+    public void test23_inline(Object[] oa, Object o, int index) {
+        oa[index] = o;
+    }
+
+    @Test
+    public void test23(Object[] oa, MyValue1 vt, int index) {
+        test23_inline(oa, vt, index);
+    }
+
+    @Run(test = "test23")
+    public void test23_verifier() {
+        int index = Math.abs(rI) % 3;
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI + 1, rL + 1);
+        test23(testValue1Array, vt, index);
+        Asserts.assertEQ(testValue1Array[index].hash(), vt.hash());
+        testValue1Array[index] = testValue1;
+        try {
+            test23(testValue2Array, vt, index);
+            throw new RuntimeException("No ArrayStoreException thrown");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+        Asserts.assertEQ(testValue2Array[index].hash(), testValue2.hash());
+    }
+
+    @ForceInline
+    public void test24_inline(Object[] oa, Object o, int index) {
+        oa[index] = o;
+    }
+
+    @Test
+    public void test24(Object[] oa, MyValue1 vt, int index) {
+        test24_inline(oa, vt, index);
+    }
+
+    @Run(test = "test24")
+    public void test24_verifier() {
+        int index = Math.abs(rI) % 3;
+        try {
+            test24(testNonValueArray, testValue1, index);
+            throw new RuntimeException("No ArrayStoreException thrown");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+    }
+
+    @ForceInline
+    public void test25_inline(Object[] oa, Object o, int index) {
+        oa[index] = o;
+    }
+
+    @Test
+    public void test25(Object[] oa, MyValue1 vt, int index) {
+        test25_inline(oa, vt, index);
+    }
+
+    @Run(test = "test25")
+    public void test25_verifier() {
+        int index = Math.abs(rI) % 3;
+        try {
+            test25(null, testValue1, index);
+            throw new RuntimeException("No NPE thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    // Test inline store to (flattened) inline type array disguised as interface array
+    @ForceInline
+    public void test26Interface_inline(MyInterface[] ia, MyInterface i, int index) {
+        ia[index] = i;
+    }
+
+    @Test
+    public void test26Interface(MyInterface[] ia, MyValue1 vt, int index) {
+      test26Interface_inline(ia, vt, index);
+    }
+
+    @Run(test = "test26Interface")
+    public void test26Interface_verifier() {
+        int index = Math.abs(rI) % 3;
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI + 1, rL + 1);
+        test26Interface(testValue1Array, vt, index);
+        Asserts.assertEQ(testValue1Array[index].hash(), vt.hash());
+        testValue1Array[index] = testValue1;
+        try {
+            test26Interface(testValue2Array, vt, index);
+            throw new RuntimeException("No ArrayStoreException thrown");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+        Asserts.assertEQ(testValue2Array[index].hash(), testValue2.hash());
+    }
+
+    @ForceInline
+    public void test27Interface_inline(MyInterface[] ia, MyInterface i, int index) {
+        ia[index] = i;
+    }
+
+    @Test
+    public void test27Interface(MyInterface[] ia, MyValue1 vt, int index) {
+        test27Interface_inline(ia, vt, index);
+    }
+
+    @Run(test = "test27Interface")
+    public void test27Interface_verifier() {
+        int index = Math.abs(rI) % 3;
+        try {
+            test27Interface(null, testValue1, index);
+            throw new RuntimeException("No NPE thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    // Test inline store to (flattened) inline type array disguised as abstract array
+    @ForceInline
+    public void test26Abstract_inline(MyAbstract[] ia, MyAbstract i, int index) {
+        ia[index] = i;
+    }
+
+    @Test
+    public void test26Abstract(MyAbstract[] ia, MyValue1 vt, int index) {
+      test26Abstract_inline(ia, vt, index);
+    }
+
+    @Run(test = "test26Abstract")
+    public void test26Abstract_verifier() {
+        int index = Math.abs(rI) % 3;
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI + 1, rL + 1);
+        test26Abstract(testValue1Array, vt, index);
+        Asserts.assertEQ(testValue1Array[index].hash(), vt.hash());
+        testValue1Array[index] = testValue1;
+        try {
+            test26Abstract(testValue2Array, vt, index);
+            throw new RuntimeException("No ArrayStoreException thrown");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+        Asserts.assertEQ(testValue2Array[index].hash(), testValue2.hash());
+    }
+
+    @ForceInline
+    public void test27Abstract_inline(MyAbstract[] ia, MyAbstract i, int index) {
+        ia[index] = i;
+    }
+
+    @Test
+    public void test27Abstract(MyAbstract[] ia, MyValue1 vt, int index) {
+        test27Abstract_inline(ia, vt, index);
+    }
+
+    @Run(test = "test27Abstract")
+    public void test27Abstract_verifier() {
+        int index = Math.abs(rI) % 3;
+        try {
+            test27Abstract(null, testValue1, index);
+            throw new RuntimeException("No NPE thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    // Test object store to (flattened) inline type array disguised as object array
+    @ForceInline
+    public void test28_inline(Object[] oa, Object o, int index) {
+        oa[index] = o;
+    }
+
+    @Test
+    public void test28(Object[] oa, Object o, int index) {
+        test28_inline(oa, o, index);
+    }
+
+    @Run(test = "test28")
+    public void test28_verifier() {
+        int index = Math.abs(rI) % 3;
+        MyValue1 vt1 = MyValue1.createWithFieldsInline(rI + 1, rL + 1);
+        test28(testValue1Array, vt1, index);
+        Asserts.assertEQ(testValue1Array[index].hash(), vt1.hash());
+        try {
+            test28(testValue1Array, testValue2, index);
+            throw new RuntimeException("No ArrayStoreException thrown");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+        Asserts.assertEQ(testValue1Array[index].hash(), vt1.hash());
+        testValue1Array[index] = testValue1;
+    }
+
+    @ForceInline
+    public void test29_inline(Object[] oa, Object o, int index) {
+        oa[index] = o;
+    }
+
+    @Test
+    public void test29(Object[] oa, Object o, int index) {
+        test29_inline(oa, o, index);
+    }
+
+    @Run(test = "test29")
+    public void test29_verifier() {
+        int index = Math.abs(rI) % 3;
+        try {
+            test29(testValue2Array, testValue1, index);
+            throw new RuntimeException("No ArrayStoreException thrown");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+        Asserts.assertEQ(testValue2Array[index].hash(), testValue2.hash());
+    }
+
+    @ForceInline
+    public void test30_inline(Object[] oa, Object o, int index) {
+        oa[index] = o;
+    }
+
+    @Test
+    public void test30(Object[] oa, Object o, int index) {
+        test30_inline(oa, o, index);
+    }
+
+    @Run(test = "test30")
+    public void test30_verifier() {
+        int index = Math.abs(rI) % 3;
+        try {
+            test30(testNonValueArray, testValue1, index);
+            throw new RuntimeException("No ArrayStoreException thrown");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+    }
+
+    // Test inline store to (flattened) inline type array disguised as interface array
+    @ForceInline
+    public void test31Interface_inline(MyInterface[] ia, MyInterface i, int index) {
+        ia[index] = i;
+    }
+
+    @Test
+    public void test31Interface(MyInterface[] ia, MyInterface i, int index) {
+        test31Interface_inline(ia, i, index);
+    }
+
+    @Run(test = "test31Interface")
+    public void test31Interface_verifier() {
+        int index = Math.abs(rI) % 3;
+        MyValue1 vt1 = MyValue1.createWithFieldsInline(rI + 1, rL + 1);
+        test31Interface(testValue1Array, vt1, index);
+        Asserts.assertEQ(testValue1Array[index].hash(), vt1.hash());
+        try {
+            test31Interface(testValue1Array, testValue2, index);
+            throw new RuntimeException("No ArrayStoreException thrown");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+        Asserts.assertEQ(testValue1Array[index].hash(), vt1.hash());
+        testValue1Array[index] = testValue1;
+    }
+
+    @ForceInline
+    public void test32Interface_inline(MyInterface[] ia, MyInterface i, int index) {
+        ia[index] = i;
+    }
+
+    @Test
+    public void test32Interface(MyInterface[] ia, MyInterface i, int index) {
+        test32Interface_inline(ia, i, index);
+    }
+
+    @Run(test = "test32Interface")
+    public void test32Interface_verifier() {
+        int index = Math.abs(rI) % 3;
+        try {
+            test32Interface(testValue2Array, testValue1, index);
+            throw new RuntimeException("No ArrayStoreException thrown");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+    }
+
+    // Test inline store to (flattened) inline type array disguised as abstract array
+    @ForceInline
+    public void test31Abstract_inline(MyAbstract[] ia, MyAbstract i, int index) {
+        ia[index] = i;
+    }
+
+    @Test
+    public void test31Abstract(MyAbstract[] ia, MyAbstract i, int index) {
+        test31Abstract_inline(ia, i, index);
+    }
+
+    @Run(test = "test31Abstract")
+    public void test31Abstract_verifier() {
+        int index = Math.abs(rI) % 3;
+        MyValue1 vt1 = MyValue1.createWithFieldsInline(rI + 1, rL + 1);
+        test31Abstract(testValue1Array, vt1, index);
+        Asserts.assertEQ(testValue1Array[index].hash(), vt1.hash());
+        try {
+            test31Abstract(testValue1Array, testValue2, index);
+            throw new RuntimeException("No ArrayStoreException thrown");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+        Asserts.assertEQ(testValue1Array[index].hash(), vt1.hash());
+        testValue1Array[index] = testValue1;
+    }
+
+    @ForceInline
+    public void test32Abstract_inline(MyAbstract[] ia, MyAbstract i, int index) {
+        ia[index] = i;
+    }
+
+    @Test
+    public void test32Abstract(MyAbstract[] ia, MyAbstract i, int index) {
+        test32Abstract_inline(ia, i, index);
+    }
+
+    @Run(test = "test32Abstract")
+    public void test32Abstract_verifier() {
+        int index = Math.abs(rI) % 3;
+        try {
+            test32Abstract(testValue2Array, testValue1, index);
+            throw new RuntimeException("No ArrayStoreException thrown");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+    }
+
+    // Test writing null to a (flattened) inline type array disguised as object array
+    @ForceInline
+    public void test33_inline(Object[] oa, Object o, int index) {
+        oa[index] = o;
+    }
+
+    @Test
+    public void test33(Object[] oa, Object o, int index) {
+        test33_inline(oa, o, index);
+    }
+
+    @Run(test = "test33")
+    public void test33_verifier() {
+        int index = Math.abs(rI) % 3;
+        try {
+            test33(testValue1Array, null, index);
+            throw new RuntimeException("No NPE thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        Asserts.assertEQ(testValue1Array[index].hash(), hash());
+    }
+
+    // Test writing constant null to a (flattened) inline type array disguised as object array
+
+    @ForceInline
+    public void test34_inline(Object[] oa, Object o, int index) {
+        oa[index] = o;
+    }
+
+    @Test
+    public void test34(Object[] oa, int index) {
+        test34_inline(oa, null, index);
+    }
+
+    @Run(test = "test34")
+    public void test34_verifier() {
+        int index = Math.abs(rI) % 3;
+        try {
+            test34(testValue1Array, index);
+            throw new RuntimeException("No NPE thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        Asserts.assertEQ(testValue1Array[index].hash(), hash());
+    }
+
+    // Test writing constant null to a (flattened) inline type array
+
+    private static final MethodHandle setArrayElementNull = OldInstructionHelper.loadCode(MethodHandles.lookup(),
+        "setArrayElementNull",
+        MethodType.methodType(void.class, TestLWorld.class, MyValue1[].class, int.class),
+        CODE -> {
+            CODE.
+            aload_1().
+            iload_2().
+            aconst_null().
+            aastore().
+            return_();
+        });
+
+    @Test
+    public void test35(MyValue1[] va, int index) throws Throwable {
+        setArrayElementNull.invoke(this, va, index);
+    }
+
+    @Run(test = "test35")
+    @Warmup(10000)
+    public void test35_verifier() throws Throwable {
+        int index = Math.abs(rI) % 3;
+        try {
+            test35(testValue1Array, index);
+            throw new RuntimeException("No NPE thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        Asserts.assertEQ(testValue1Array[index].hash(), hash());
+    }
+
+    // Test writing an inline type to a null inline type array
+    @Test
+    public void test36(MyValue1[] va, MyValue1 vt, int index) {
+        va[index] = vt;
+    }
+
+    @Run(test = "test36")
+    public void test36_verifier() {
+        int index = Math.abs(rI) % 3;
+        try {
+            test36(null, testValue1Array[index], index);
+            throw new RuntimeException("No NPE thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    // Test incremental inlining
+    @ForceInline
+    public void test37_inline(Object[] oa, Object o, int index) {
+        oa[index] = o;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public void test37(MyValue1[] va, Object o, int index) {
+        test37_inline(va, o, index);
+    }
+
+    @Run(test = "test37")
+    public void test37_verifier() {
+        int index = Math.abs(rI) % 3;
+        MyValue1 vt1 = MyValue1.createWithFieldsInline(rI + 1, rL + 1);
+        test37(testValue1Array, vt1, index);
+        Asserts.assertEQ(testValue1Array[index].hash(), vt1.hash());
+        try {
+            test37(testValue1Array, testValue2, index);
+            throw new RuntimeException("No ArrayStoreException thrown");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+        Asserts.assertEQ(testValue1Array[index].hash(), vt1.hash());
+        testValue1Array[index] = testValue1;
+    }
+
+    // Test merging of inline type arrays
+
+    @ForceInline
+    public Object[] test38_inline() {
+        return (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 42);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public Object[] test38(Object[] oa, Object o, int i1, int i2, int num) {
+        Object[] result = null;
+        switch (num) {
+        case 0:
+            result = test38_inline();
+            break;
+        case 1:
+            result = oa;
+            break;
+        case 2:
+            result = testValue1Array;
+            break;
+        case 3:
+            result = testValue2Array;
+            break;
+        case 4:
+            result = testNonValueArray;
+            break;
+        case 5:
+            result = null;
+            break;
+        case 6:
+            result = testValue1Array2;
+            break;
+        }
+        result[i1] = result[i2];
+        result[i2] = o;
+        return result;
+    }
+
+    @Run(test = "test38")
+    public void test38_verifier() {
+        int index = Math.abs(rI) % 3;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 42);
+        Object[] result = test38(null, testValue1, index, index, 0);
+        Asserts.assertEQ(((MyValue1)result[index]).hash(), testValue1.hash());
+        result = test38(testValue1Array, testValue1, index, index, 1);
+        Asserts.assertEQ(((MyValue1)result[index]).hash(), testValue1.hash());
+        result = test38(null, testValue1, index, index, 2);
+        Asserts.assertEQ(((MyValue1)result[index]).hash(), testValue1.hash());
+        result = test38(null, testValue2, index, index, 3);
+        Asserts.assertEQ(((MyValue2)result[index]).hash(), testValue2.hash());
+        try {
+            result = test38(null, null, index, index, 3);
+            throw new RuntimeException("No NPE thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        result = test38(null, null, index, index, 4);
+        try {
+            result = test38(null, testValue1, index, index, 4);
+            throw new RuntimeException("No ArrayStoreException thrown");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+        try {
+            result = test38(null, testValue1, index, index, 5);
+            throw new RuntimeException("No NPE thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        result = test38(null, testValue1Array, index, index, 6);
+        Asserts.assertEQ(((MyValue1[][])result)[index][index].hash(), testValue1.hash());
+    }
+
+    @ForceInline
+    public Object test39_inline() {
+        return (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 42);
+    }
+
+    // Same as above but merging into Object instead of Object[]
+    @Test
+    public Object test39(Object oa, Object o, int i1, int i2, int num) {
+        Object result = null;
+        switch (num) {
+        case 0:
+            result = test39_inline();
+            break;
+        case 1:
+            result = oa;
+            break;
+        case 2:
+            result = testValue1Array;
+            break;
+        case 3:
+            result = testValue2Array;
+            break;
+        case 4:
+            result = testNonValueArray;
+            break;
+        case 5:
+            result = null;
+            break;
+        case 6:
+            result = testValue1;
+            break;
+        case 7:
+            result = testValue2;
+            break;
+        case 8:
+            result = MyValue1.createWithFieldsInline(rI, rL);
+            break;
+        case 9:
+            result = new NonValueClass(42);
+            break;
+        case 10:
+            result = testValue1Array2;
+            break;
+        }
+        if (result instanceof Object[]) {
+            ((Object[])result)[i1] = ((Object[])result)[i2];
+            ((Object[])result)[i2] = o;
+        }
+        return result;
+    }
+
+    @Run(test = "test39")
+    public void test39_verifier() {
+        int index = Math.abs(rI) % 3;
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 42);
+        Object result = test39(null, testValue1, index, index, 0);
+        Asserts.assertEQ(((MyValue1[])result)[index].hash(), testValue1.hash());
+        result = test39(testValue1Array, testValue1, index, index, 1);
+        Asserts.assertEQ(((MyValue1[])result)[index].hash(), testValue1.hash());
+        result = test39(null, testValue1, index, index, 2);
+        Asserts.assertEQ(((MyValue1[])result)[index].hash(), testValue1.hash());
+        result = test39(null, testValue2, index, index, 3);
+        Asserts.assertEQ(((MyValue2[])result)[index].hash(), testValue2.hash());
+        try {
+            result = test39(null, null, index, index, 3);
+            throw new RuntimeException("No NPE thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        result = test39(null, null, index, index, 4);
+        try {
+            result = test39(null, testValue1, index, index, 4);
+            throw new RuntimeException("No ArrayStoreException thrown");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+        result = test39(null, testValue1, index, index, 5);
+        Asserts.assertEQ(result, null);
+        result = test39(null, testValue1, index, index, 6);
+        Asserts.assertEQ(((MyValue1)result).hash(), testValue1.hash());
+        result = test39(null, testValue1, index, index, 7);
+        Asserts.assertEQ(((MyValue2)result).hash(), testValue2.hash());
+        result = test39(null, testValue1, index, index, 8);
+        Asserts.assertEQ(((MyValue1)result).hash(), testValue1.hash());
+        result = test39(null, testValue1, index, index, 9);
+        Asserts.assertEQ(((NonValueClass)result).x, 42);
+        result = test39(null, testValue1Array, index, index, 10);
+        Asserts.assertEQ(((MyValue1[][])result)[index][index].hash(), testValue1.hash());
+    }
+
+    // Test instanceof with inline types and arrays
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
+        failOn = {ALLOC_G})
+    public long test40(Object o, int index) {
+        if (o instanceof MyValue1) {
+          return ((MyValue1)o).hashInterpreted();
+        } else if (o instanceof MyValue1[]) {
+          return ((MyValue1[])o)[index].hashInterpreted();
+        } else if (o instanceof MyValue2) {
+          return ((MyValue2)o).hash();
+        } else if (o instanceof MyValue2[]) {
+          return ((MyValue2[])o)[index].hash();
+        } else if (o instanceof MyValue1[][]) {
+          return ((MyValue1[][])o)[index][index].hash();
+        } else if (o instanceof Long) {
+          return (long)o;
+        }
+        return 0;
+    }
+
+    @Run(test = "test40")
+    public void test40_verifier() {
+        int index = Math.abs(rI) % 3;
+        long result = test40(testValue1, 0);
+        Asserts.assertEQ(result, testValue1.hashInterpreted());
+        result = test40(testValue1Array, index);
+        Asserts.assertEQ(result, testValue1.hashInterpreted());
+        result = test40(testValue2, index);
+        Asserts.assertEQ(result, testValue2.hash());
+        result = test40(testValue2Array, index);
+        Asserts.assertEQ(result, testValue2.hash());
+        result = test40(testValue1Array2, index);
+        Asserts.assertEQ(result, testValue1.hash());
+        result = test40(Long.valueOf(42), index);
+        Asserts.assertEQ(result, 42L);
+    }
+
+    // Test for bug in Escape Analysis
+    // TODO 8325106 Re-enable
+    /*
+    @DontInline
+    public void test41_dontinline(Object o) {
+        Asserts.assertEQ(o, rI);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public void test41() {
+        MyValue1[] vals = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        vals[0] = testValue1;
+        test41_dontinline(vals[0].oa[0]);
+        test41_dontinline(vals[0].oa[0]);
+    }
+
+    @Run(test = "test41")
+    public void test41_verifier() {
+        test41();
+    }
+    */
+
+    // Test for bug in Escape Analysis
+    private static final MyValue1 test42VT1 = MyValue1.createWithFieldsInline(rI, rL);
+    private static final MyValue1 test42VT2 = MyValue1.createWithFieldsInline(rI + 1, rL + 1);
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public void test42() {
+        MyValue1[] vals = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        vals[0] = test42VT1;
+        vals[1] = test42VT2;
+        Asserts.assertEQ(vals[0].hash(), test42VT1.hash());
+        Asserts.assertEQ(vals[1].hash(), test42VT2.hash());
+    }
+
+    @Run(test = "test42")
+    public void test42_verifier(RunInfo info) {
+        if (!info.isWarmUp()) test42(); // We need -Xcomp behavior
+    }
+
+    // Test for bug in Escape Analysis
+    @Test
+    @IR(failOn = {ALLOC})
+    public long test43(boolean deopt, Method m) {
+        MyValue1[] vals = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        vals[0] = test42VT1;
+        vals[1] = test42VT2;
+
+        if (deopt) {
+            // uncommon trap
+            TestFramework.deoptimize(m);
+            Asserts.assertEQ(vals[0].hash(), test42VT1.hash());
+            Asserts.assertEQ(vals[1].hash(), test42VT2.hash());
+        }
+
+        return vals[0].hash();
+    }
+
+    @Run(test = "test43")
+    public void test43_verifier(RunInfo info) {
+        test43(!info.isWarmUp(), info.getTest());
+    }
+
+    // Tests writing an array element with a (statically known) incompatible type
+    private static final MethodHandle setArrayElementIncompatible = OldInstructionHelper.loadCode(MethodHandles.lookup(),
+        "setArrayElementIncompatible",
+        MethodType.methodType(void.class, TestLWorld.class, MyValue1[].class, int.class, MyValue2.class),
+        CODE -> {
+            CODE.
+            aload_1().
+            iload_2().
+            aload_3().
+            aastore().
+            return_();
+        });
+
+    @Test
+    public void test44(MyValue1[] va, int index, MyValue2 v) throws Throwable {
+        setArrayElementIncompatible.invoke(this, va, index, v);
+    }
+
+    @Run(test = "test44")
+    @Warmup(10000)
+    public void test44_verifier() throws Throwable {
+        int index = Math.abs(rI) % 3;
+        try {
+            test44(testValue1Array, index, testValue2);
+            throw new RuntimeException("No ArrayStoreException thrown");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+        Asserts.assertEQ(testValue1Array[index].hash(), hash());
+    }
+
+    // Tests writing an array element with a (statically known) incompatible type
+    @ForceInline
+    public void test45_inline(Object[] oa, Object o, int index) {
+        oa[index] = o;
+    }
+
+    @Test
+    public void test45(MyValue1[] va, int index, MyValue2 v) throws Throwable {
+        test45_inline(va, v, index);
+    }
+
+    @Run(test = "test45")
+    public void test45_verifier() throws Throwable {
+        int index = Math.abs(rI) % 3;
+        try {
+            test45(testValue1Array, index, testValue2);
+            throw new RuntimeException("No ArrayStoreException thrown");
+        } catch (ArrayStoreException e) {
+            // Expected
+        }
+        Asserts.assertEQ(testValue1Array[index].hash(), hash());
+    }
+
+    // instanceof tests with inline types
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public boolean test46(MyValue1 vt) {
+        Object obj = vt;
+        return obj instanceof MyValue1;
+    }
+
+    @Run(test = "test46")
+    public void test46_verifier() {
+        MyValue1 vt = testValue1;
+        boolean result = test46(vt);
+        Asserts.assertTrue(result);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public boolean test47(MyValue1 vt) {
+        Object obj = vt;
+        return obj instanceof MyValue2;
+    }
+
+    @Run(test = "test47")
+    public void test47_verifier() {
+        MyValue1 vt = testValue1;
+        boolean result = test47(vt);
+        Asserts.assertFalse(result);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public boolean test48(Object obj) {
+        return obj instanceof MyValue1;
+    }
+
+    @Run(test = "test48")
+    public void test48_verifier() {
+        MyValue1 vt = testValue1;
+        boolean result = test48(vt);
+        Asserts.assertTrue(result);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public boolean test49(Object obj) {
+        return obj instanceof MyValue2;
+    }
+
+    @Run(test = "test49")
+    public void test49_verifier() {
+        MyValue1 vt = testValue1;
+        boolean result = test49(vt);
+        Asserts.assertFalse(result);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public boolean test50(Object obj) {
+        return obj instanceof MyValue1;
+    }
+
+    @Run(test = "test50")
+    public void test50_verifier() {
+        Asserts.assertFalse(test49(new NonValueClass(42)));
+    }
+
+    // Inline type with some non-flattened fields
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    value class Test51Value {
+        Object objectField1;
+        Object objectField2;
+        Object objectField3;
+        Object objectField4;
+        Object objectField5;
+        Object objectField6;
+
+        @NullRestricted
+        MyValue1 valueField1;
+        @NullRestricted
+        MyValue1 valueField2;
+        MyValue1 valueField3;
+        @NullRestricted
+        MyValue1 valueField4;
+        MyValue1 valueField5;
+
+        public Test51Value() {
+            objectField1 = null;
+            objectField2 = null;
+            objectField3 = null;
+            objectField4 = null;
+            objectField5 = null;
+            objectField6 = null;
+            valueField1 = testValue1;
+            valueField2 = testValue1;
+            valueField3 = testValue1;
+            valueField4 = MyValue1.createDefaultDontInline();
+            valueField5 = MyValue1.createDefaultDontInline();
+        }
+
+        public Test51Value(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6,
+                           MyValue1 vt1, MyValue1 vt2, MyValue1 vt3, MyValue1 vt4, MyValue1 vt5) {
+            objectField1 = o1;
+            objectField2 = o2;
+            objectField3 = o3;
+            objectField4 = o4;
+            objectField5 = o5;
+            objectField6 = o6;
+            valueField1 = vt1;
+            valueField2 = vt2;
+            valueField3 = vt3;
+            valueField4 = vt4;
+            valueField5 = vt5;
+        }
+
+        @ForceInline
+        public long test(Test51Value holder, MyValue1 vt1, Object vt2) {
+            holder = new Test51Value(vt1, holder.objectField2, holder.objectField3, holder.objectField4, holder.objectField5, holder.objectField6,
+                                     holder.valueField1, holder.valueField2, holder.valueField3, holder.valueField4, holder.valueField5);
+            holder = new Test51Value(holder.objectField1, (MyValue1)vt2, holder.objectField3, holder.objectField4, holder.objectField5, holder.objectField6,
+                                     holder.valueField1, holder.valueField2, holder.valueField3, holder.valueField4, holder.valueField5);
+            holder = new Test51Value(holder.objectField1, holder.objectField2, testValue1, holder.objectField4, holder.objectField5, holder.objectField6,
+                                     holder.valueField1, holder.valueField2, holder.valueField3, holder.valueField4, holder.valueField5);
+            holder = new Test51Value(holder.objectField1, holder.objectField2, holder.objectField3, MyValue1.createWithFieldsDontInline(rI, rL), holder.objectField5, holder.objectField6,
+                                     holder.valueField1, holder.valueField2, holder.valueField3, holder.valueField4, holder.valueField5);
+            holder = new Test51Value(holder.objectField1, holder.objectField2, holder.objectField3, holder.objectField4, holder.valueField1, holder.objectField6,
+                                     holder.valueField1, holder.valueField2, holder.valueField3, holder.valueField4, holder.valueField5);
+            holder = new Test51Value(holder.objectField1, holder.objectField2, holder.objectField3, holder.objectField4, holder.objectField5, holder.valueField3,
+                                     holder.valueField1, holder.valueField2, holder.valueField3, holder.valueField4, holder.valueField5);
+            holder = new Test51Value(holder.objectField1, holder.objectField2, holder.objectField3, holder.objectField4, holder.objectField5, holder.objectField6,
+                                     (MyValue1)holder.objectField1, holder.valueField2, holder.valueField3, holder.valueField4, holder.valueField5);
+            holder = new Test51Value(holder.objectField1, holder.objectField2, holder.objectField3, holder.objectField4, holder.objectField5, holder.objectField6,
+                                     holder.valueField1, (MyValue1)vt2, holder.valueField3, holder.valueField4, holder.valueField5);
+            holder = new Test51Value(holder.objectField1, holder.objectField2, holder.objectField3, holder.objectField4, holder.objectField5, holder.objectField6,
+                                     holder.valueField1, holder.valueField2, (MyValue1)vt2, holder.valueField4, holder.valueField5);
+
+            return ((MyValue1)holder.objectField1).hash() +
+                   ((MyValue1)holder.objectField2).hash() +
+                   ((MyValue1)holder.objectField3).hash() +
+                   ((MyValue1)holder.objectField4).hash() +
+                   ((MyValue1)holder.objectField5).hash() +
+                   ((MyValue1)holder.objectField6).hash() +
+                   holder.valueField1.hash() +
+                   holder.valueField2.hash() +
+                   holder.valueField3.hash() +
+                   holder.valueField4.hashPrimitive();
+        }
+    }
+
+    // Pass arguments via fields to avoid exzessive spilling leading to compilation bailouts
+    @NullRestricted
+    static Test51Value test51_arg1;
+    @NullRestricted
+    static MyValue1 test51_arg2;
+    static Object test51_arg3;
+
+    // Same as test2 but with field holder being an inline type
+    @Test
+    public long test51() {
+        return test51_arg1.test(test51_arg1, test51_arg2, test51_arg3);
+    }
+
+    @Run(test = "test51")
+    public void test51_verifier() {
+        MyValue1 vt = testValue1;
+        MyValue1 def = MyValue1.createDefaultDontInline();
+        Test51Value holder = new Test51Value();
+        Asserts.assertEQ(testValue1.hash(), vt.hash());
+        Asserts.assertEQ(holder.valueField1.hash(), vt.hash());
+        test51_arg1 = holder;
+        test51_arg2 = vt;
+        test51_arg3 = vt;
+        long result = test51();
+        Asserts.assertEQ(result, 9*vt.hash() + def.hashPrimitive());
+    }
+
+    // Access non-flattened, uninitialized inline type field with inline type holder
+    @Test
+    @IR(failOn = {ALLOC})
+    public void test52(Test51Value holder) {
+        if ((Object)holder.valueField5 != null) {
+            throw new RuntimeException("Should be null");
+        }
+    }
+
+    @Run(test = "test52")
+    public void test52_verifier() {
+        Test51Value vt = new Test51Value(null, null, null, null, null, null,
+                                         MyValue1.createDefaultInline(), MyValue1.createDefaultInline(), null, MyValue1.createDefaultInline(), null);
+        test52(vt);
+    }
+
+    // Merging inline types of different types
+    @Test
+    public Object test53(Object o, boolean b) {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        return b ? vt : o;
+    }
+
+    @Run(test = "test53")
+    public void test53_verifier() {
+        test53(new Object(), false);
+        MyValue1 result = (MyValue1)test53(new Object(), true);
+        Asserts.assertEQ(result.hash(), hash());
+    }
+
+    @Test
+    public Object test54(boolean b) {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        return b ? vt : testValue2;
+    }
+
+    @Run(test = "test54")
+    public void test54_verifier() {
+        MyValue1 result1 = (MyValue1)test54(true);
+        Asserts.assertEQ(result1.hash(), hash());
+        MyValue2 result2 = (MyValue2)test54(false);
+        Asserts.assertEQ(result2.hash(), testValue2.hash());
+    }
+
+    @Test
+    public Object test55(boolean b) {
+        MyValue1 vt1 = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue2 vt2 = MyValue2.createWithFieldsInline(rI, rD);
+        return b ? vt1 : vt2;
+    }
+
+    @Run(test = "test55")
+    public void test55_verifier() {
+        MyValue1 result1 = (MyValue1)test55(true);
+        Asserts.assertEQ(result1.hash(), hash());
+        MyValue2 result2 = (MyValue2)test55(false);
+        Asserts.assertEQ(result2.hash(), testValue2.hash());
+    }
+
+    // Test synchronization on inline types
+    @Test
+    public void test56(Object vt) {
+        synchronized (vt) {
+            throw new RuntimeException("test56 failed: synchronization on inline type should not succeed");
+        }
+    }
+
+    @Run(test = "test56")
+    public void test56_verifier() {
+        try {
+            test56(testValue1);
+            throw new RuntimeException("test56 failed: no exception thrown");
+        } catch (IllegalMonitorStateException ex) {
+            // Expected
+        }
+    }
+
+    @ForceInline
+    public void test57_inline(Object vt) {
+        synchronized (vt) {
+            throw new RuntimeException("test57 failed: synchronization on inline type should not succeed");
+        }
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public void test57(MyValue1 vt) {
+        test57_inline(vt);
+    }
+
+    @Run(test = "test57")
+    public void test57_verifier() {
+        try {
+            test57(testValue1);
+            throw new RuntimeException("test57 failed: no exception thrown");
+        } catch (IllegalMonitorStateException ex) {
+            // Expected
+        }
+    }
+
+    @ForceInline
+    public void test58_inline(Object vt) {
+        synchronized (vt) {
+            throw new RuntimeException("test58 failed: synchronization on inline type should not succeed");
+        }
+    }
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public void test58() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        test58_inline(vt);
+    }
+
+    @Run(test = "test58")
+    public void test58_verifier() {
+        try {
+            test58();
+            throw new RuntimeException("test58 failed: no exception thrown");
+        } catch (IllegalMonitorStateException ex) {
+            // Expected
+        }
+    }
+
+    @Test
+    public void test59(Object o, boolean b) {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        Object sync = b ? vt : o;
+        synchronized (sync) {
+            if (b) {
+                throw new RuntimeException("test59 failed: synchronization on inline type should not succeed");
+            }
+        }
+    }
+
+    @Run(test = "test59")
+    public void test59_verifier() {
+        test59(new Object(), false);
+        try {
+            test59(new Object(), true);
+            throw new RuntimeException("test59 failed: no exception thrown");
+        } catch (IllegalMonitorStateException ex) {
+            // Expected
+        }
+    }
+
+    @Test
+    public void test60(boolean b) {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        Object sync = b ? vt : testValue2;
+        synchronized (sync) {
+            throw new RuntimeException("test60 failed: synchronization on inline type should not succeed");
+        }
+    }
+
+    @Run(test = "test60")
+    public void test60_verifier() {
+        try {
+            test60(false);
+            throw new RuntimeException("test60 failed: no exception thrown");
+        } catch (IllegalMonitorStateException ex) {
+            // Expected
+        }
+        try {
+            test60(true);
+            throw new RuntimeException("test60 failed: no exception thrown");
+        } catch (IllegalMonitorStateException ex) {
+            // Expected
+        }
+    }
+
+    // Test catching the IllegalMonitorStateException in compiled code
+    @Test
+    public void test61(Object vt) {
+        boolean thrown = false;
+        try {
+            synchronized (vt) {
+                throw new RuntimeException("test61 failed: no exception thrown");
+            }
+        } catch (IllegalMonitorStateException ex) {
+            thrown = true;
+        }
+        if (!thrown) {
+            throw new RuntimeException("test61 failed: no exception thrown");
+        }
+    }
+
+    @Run(test = "test61")
+    public void test61_verifier() {
+        test61(testValue1);
+    }
+
+    @Test
+    public void test62(Object o) {
+        try {
+            synchronized (o) { }
+        } catch (IllegalMonitorStateException ex) {
+            // Expected
+            return;
+        }
+        throw new RuntimeException("test62 failed: no exception thrown");
+    }
+
+    @Run(test = "test62")
+    public void test62_verifier() {
+        test62(testValue1);
+    }
+
+    // Test synchronization without any instructions in the synchronized block
+    @Test
+    public void test63(Object o) {
+        synchronized (o) { }
+    }
+
+    @Run(test = "test63")
+    public void test63_verifier() {
+        try {
+            test63(testValue1);
+        } catch (IllegalMonitorStateException ex) {
+            // Expected
+            return;
+        }
+        throw new RuntimeException("test63 failed: no exception thrown");
+    }
+
+    // type system test with interface and inline type
+    @ForceInline
+    public MyInterface test64Interface_helper(MyValue1 vt) {
+        return vt;
+    }
+
+    @Test
+    public MyInterface test64Interface(MyValue1 vt) {
+        return test64Interface_helper(vt);
+    }
+
+    @Run(test = "test64Interface")
+    public void test64Interface_verifier() {
+        test64Interface(testValue1);
+    }
+
+    // type system test with abstract and inline type
+    @ForceInline
+    public MyAbstract test64Abstract_helper(MyValue1 vt) {
+        return vt;
+    }
+
+    @Test
+    public MyAbstract test64Abstract(MyValue1 vt) {
+        return test64Abstract_helper(vt);
+    }
+
+    @Run(test = "test64Abstract")
+    public void test64Abstract_verifier() {
+        test64Abstract(testValue1);
+    }
+
+    // Array store tests
+    @Test
+    public void test65(Object[] array, MyValue1 vt) {
+        array[0] = vt;
+    }
+
+    @Run(test = "test65")
+    public void test65_verifier() {
+        Object[] array = new Object[1];
+        test65(array, testValue1);
+        Asserts.assertEQ(((MyValue1)array[0]).hash(), testValue1.hash());
+    }
+
+    @Test
+    public void test66(Object[] array, MyValue1 vt) {
+        array[0] = vt;
+    }
+
+    @Run(test = "test66")
+    public void test66_verifier() {
+        MyValue1[] array = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        test66(array, testValue1);
+        Asserts.assertEQ(array[0].hash(), testValue1.hash());
+    }
+
+    @Test
+    public void test67(Object[] array, Object vt) {
+        array[0] = vt;
+    }
+
+    @Run(test = "test67")
+    public void test67_verifier() {
+        MyValue1[] array = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        test67(array, testValue1);
+        Asserts.assertEQ(array[0].hash(), testValue1.hash());
+    }
+
+    @Test
+    public void test68(Object[] array, NonValueClass o) {
+        array[0] = o;
+    }
+
+    @Run(test = "test68")
+    public void test68_verifier() {
+        NonValueClass[] array = new NonValueClass[1];
+        NonValueClass obj = new NonValueClass(1);
+        test68(array, obj);
+        Asserts.assertEQ(array[0], obj);
+    }
+
+    // Test convertion between an inline type and java.lang.Object without an allocation
+    @ForceInline
+    public Object test69_sum(Object a, Object b) {
+        int sum = ((MyValue1)a).x + ((MyValue1)b).x;
+        return MyValue1.setX(((MyValue1)a), sum);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, STORE})
+    public int test69(MyValue1[] array) {
+        MyValue1 result = MyValue1.createDefaultInline();
+        for (int i = 0; i < array.length; ++i) {
+            result = (MyValue1)test69_sum(result, array[i]);
+        }
+        return result.x;
+    }
+
+    @Run(test = "test69")
+    public void test69_verifier() {
+        int result = test69(testValue1Array);
+        Asserts.assertEQ(result, rI * testValue1Array.length);
+    }
+
+    // Same as test69 but with an Interface
+    @ForceInline
+    public MyInterface test70Interface_sum(MyInterface a, MyInterface b) {
+        int sum = ((MyValue1)a).x + ((MyValue1)b).x;
+        return MyValue1.setX(((MyValue1)a), sum);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, STORE})
+    public int test70Interface(MyValue1[] array) {
+        MyValue1 result = MyValue1.createDefaultInline();
+        for (int i = 0; i < array.length; ++i) {
+            result = (MyValue1)test70Interface_sum(result, array[i]);
+        }
+        return result.x;
+    }
+
+    @Run(test = "test70Interface")
+    public void test70Interface_verifier() {
+        int result = test70Interface(testValue1Array);
+        Asserts.assertEQ(result, rI * testValue1Array.length);
+    }
+
+    // Same as test69 but with an Abstract
+    @ForceInline
+    public MyAbstract test70Abstract_sum(MyAbstract a, MyAbstract b) {
+        int sum = ((MyValue1)a).x + ((MyValue1)b).x;
+        return MyValue1.setX(((MyValue1)a), sum);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, STORE})
+    public int test70Abstract(MyValue1[] array) {
+        MyValue1 result = MyValue1.createDefaultInline();
+        for (int i = 0; i < array.length; ++i) {
+            result = (MyValue1)test70Abstract_sum(result, array[i]);
+        }
+        return result.x;
+    }
+
+    @Run(test = "test70Abstract")
+    public void test70Abstract_verifier() {
+        int result = test70Abstract(testValue1Array);
+        Asserts.assertEQ(result, rI * testValue1Array.length);
+    }
+
+    // Test that allocated inline type is not used in non-dominated path
+    public MyValue1 test71_inline(Object obj) {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        try {
+            vt = (MyValue1)Objects.requireNonNull(obj);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        return vt;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public MyValue1 test71() {
+        return test71_inline(null);
+    }
+
+    @Run(test = "test71")
+    public void test71_verifier() {
+        MyValue1 vt = test71();
+        Asserts.assertEquals(vt.hash(), hash());
+    }
+
+    // Test calling a method on an uninitialized inline type
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    value class Test72Value {
+        int x = 0;
+
+        public int get() {
+            return x;
+        }
+    }
+
+    // Make sure Test72Value is loaded but not initialized
+    public void unused(Test72Value vt) { }
+
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public int test72() {
+        Test72Value vt = new Test72Value();
+        return vt.get();
+    }
+
+    @Run(test = "test72")
+    @Warmup(0)
+    public void test72_verifier() {
+        int result = test72();
+        Asserts.assertEquals(result, 0);
+    }
+
+    // Tests for loading/storing unkown values
+    @Test
+    public Object test73(Object[] va) {
+        return va[0];
+    }
+
+    @Run(test = "test73")
+    public void test73_verifier() {
+        MyValue1 vt = (MyValue1)test73(testValue1Array);
+        Asserts.assertEquals(testValue1Array[0].hash(), vt.hash());
+    }
+
+    @Test
+    public void test74(Object[] va, Object vt) {
+        va[0] = vt;
+    }
+
+    @Run(test = "test74")
+    public void test74_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        test74(va, testValue1);
+        Asserts.assertEquals(va[0].hash(), testValue1.hash());
+    }
+
+    // Verify that mixing instances and arrays with the clone api
+    // doesn't break anything
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public Object test75(Object o) {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        Object[] next = va;
+        Object[] arr = va;
+        for (int i = 0; i < 10; i++) {
+            arr = next;
+            next = new NonValueClass[1];
+        }
+        return arr[0];
+    }
+
+    @Run(test = "test75")
+    public void test75_verifier() {
+        test75(42);
+    }
+
+    // Casting an NonValueClass to a inline type should throw a ClassCastException
+    @ForceInline
+    public MyValue1 test77_helper(Object o) {
+        return (MyValue1)o;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public MyValue1 test77(NonValueClass obj) throws Throwable {
+        return test77_helper(obj);
+    }
+
+    @Run(test = "test77")
+    public void test77_verifier() throws Throwable {
+        try {
+            test77(new NonValueClass(42));
+            throw new RuntimeException("ClassCastException expected");
+        } catch (ClassCastException e) {
+            // Expected
+        } catch (Exception e) {
+            throw new RuntimeException("test77 failed: unexpected exception", e);
+        }
+    }
+
+    // Casting a null NonValueClass to a nullable inline type should not throw
+    @ForceInline
+    public MyValue1 test78_helper(Object o) {
+        return (MyValue1)o;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public MyValue1 test78(NonValueClass obj) throws Throwable {
+        return test78_helper(obj);
+    }
+
+    @Run(test = "test78")
+    public void test78_verifier() throws Throwable {
+        try {
+            test78(null); // Should not throw
+        } catch (Exception e) {
+            throw new RuntimeException("test78 failed: unexpected exception", e);
+        }
+    }
+
+    // Casting an NonValueClass to a nullable inline type should throw a ClassCastException
+    @ForceInline
+    public MyValue1 test79_helper(Object o) {
+        return (MyValue1)o;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public MyValue1 test79(NonValueClass obj) throws Throwable {
+        return test79_helper(obj);
+    }
+
+    @Run(test = "test79")
+    public void test79_verifier() throws Throwable {
+        try {
+            test79(new NonValueClass(42));
+            throw new RuntimeException("ClassCastException expected");
+        } catch (ClassCastException e) {
+            // Expected
+        } catch (Exception e) {
+            throw new RuntimeException("test79 failed: unexpected exception", e);
+        }
+    }
+
+    // Test flattened field with non-flattenend (but flattenable) inline type field
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Small {
+        int i;
+        @NullRestricted
+        Big big; // Too big to be flattened
+
+        private Small() {
+            i = rI;
+            big = new Big();
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Big {
+        long l0,l1,l2,l3,l4,l5,l6,l7,l8,l9;
+        long l10,l11,l12,l13,l14,l15,l16,l17,l18,l19;
+        long l20,l21,l22,l23,l24,l25,l26,l27,l28,l29;
+
+        private Big() {
+            l0 = l1 = l2 = l3 = l4 = l5 = l6 = l7 = l8 = l9 = rL;
+            l10 = l11 = l12 = l13 = l14 = l15 = l16 = l17 = l18 = l19 = rL+1;
+            l20 = l21 = l22 = l23 = l24 = l25 = l26 = l27 = l28 = l29 = rL+2;
+        }
+    }
+
+    @NullRestricted
+    Small small = new Small();
+    @NullRestricted
+    Small smallDefault;
+    @NullRestricted
+    Big big = new Big();
+    @NullRestricted
+    Big bigDefault;
+
+    @Test
+    public long test80() {
+        return small.i + small.big.l0 + smallDefault.i + smallDefault.big.l29 + big.l0 + bigDefault.l29;
+    }
+
+    @Run(test = "test80")
+    public void test80_verifier() throws Throwable {
+        long result = test80();
+        Asserts.assertEQ(result, rI + 2*rL);
+    }
+
+    // Test scalarization with exceptional control flow
+    public int test81Callee(MyValue1 vt)  {
+        return vt.x;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD, STORE})
+    public int test81()  {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        int result = 0;
+        for (int i = 0; i < 10; i++) {
+            try {
+                result += test81Callee(vt);
+            } catch (NullPointerException npe) {
+                result += rI;
+            }
+        }
+        return result;
+    }
+
+    @Run(test = "test81")
+    public void test81_verifier() {
+        int result = test81();
+        Asserts.assertEQ(result, 10*rI);
+    }
+
+    // Test check for null free array when storing to inline tpye array
+    @Test
+    public void test82(Object[] dst, Object v) {
+        dst[0] = v;
+    }
+
+    @Run(test = "test82")
+    public void test82_verifier(RunInfo info) {
+        MyValue2[] dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 1);
+        test82(dst, testValue2);
+        if (!info.isWarmUp()) {
+            try {
+                test82(dst, null);
+                throw new RuntimeException("No ArrayStoreException thrown");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public void test83(Object[] dst, Object v, boolean flag) {
+        if (dst == null) { // null check
+        }
+        if (flag) {
+            if (dst.getClass() == MyValue1[].class) { // trigger split if
+            }
+        } else {
+            dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 1); // constant null free property
+        }
+        dst[0] = v;
+    }
+
+    @Run(test = "test83")
+    @Warmup(10000)
+    public void test83_verifier(RunInfo info) {
+        MyValue2[] dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 1);
+        test83(dst, testValue2, false);
+        test83(dst, testValue2, true);
+        if (!info.isWarmUp()) {
+            try {
+                test83(dst, null, true);
+                throw new RuntimeException("No ArrayStoreException thrown");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    private void rerun_and_recompile_for(Method m, int num, Runnable test) {
+        for (int i = 1; i < num; i++) {
+            test.run();
+
+            if (!TestFramework.isCompiled(m)) {
+                TestFramework.compile(m, CompLevel.C2);
+            }
+        }
+    }
+
+    // Tests for the Loop Unswitching optimization
+    // Should make 2 copies of the loop, one for non flattened arrays, one for other cases.
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        counts = {COUNTEDLOOP_MAIN, "= 2"})
+    @IR(applyIf = {"FlatArrayElementMaxSize", "!= -1"},
+        counts = {COUNTEDLOOP_MAIN, "= 1"})
+    public void test84(Object[] src, Object[] dst) {
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Run(test = "test84")
+    @Warmup(0)
+    public void test84_verifier(RunInfo info) {
+        MyValue2[] src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 100);
+        Arrays.fill(src, testValue2);
+        MyValue2[] dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 100);
+        rerun_and_recompile_for(info.getTest(), 10,
+                                () ->  { test84(src, dst);
+                                         Asserts.assertTrue(Arrays.equals(src, dst)); });
+    }
+
+    @Test
+    @IR(applyIfAnd = {"UseG1GC", "true", "FlatArrayElementMaxSize", "= -1"},
+        counts = {COUNTEDLOOP, "= 2", LOAD_UNKNOWN_INLINE, "= 1"})
+    @IR(applyIfAnd = {"UseG1GC", "false", "FlatArrayElementMaxSize", "= -1"},
+        counts = {COUNTEDLOOP_MAIN, "= 2", LOAD_UNKNOWN_INLINE, "= 4"})
+    public void test85(Object[] src, Object[] dst) {
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Run(test = "test85")
+    @Warmup(0)
+    public void test85_verifier(RunInfo info) {
+        Object[] src = new Object[100];
+        Arrays.fill(src, new Object());
+        src[0] = null;
+        Object[] dst = new Object[100];
+        rerun_and_recompile_for(info.getTest(), 10,
+                                () -> { test85(src, dst);
+                                        Asserts.assertTrue(Arrays.equals(src, dst)); });
+    }
+
+    @Test
+    @IR(applyIfAnd = {"UseG1GC", "true", "FlatArrayElementMaxSize", "= -1"},
+        counts = {COUNTEDLOOP, "= 2"})
+    @IR(applyIfAnd = {"UseG1GC", "false", "FlatArrayElementMaxSize", "= -1"},
+        counts = {COUNTEDLOOP_MAIN, "= 2"})
+    public void test86(Object[] src, Object[] dst) {
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Run(test = "test86")
+    @Warmup(0)
+    public void test86_verifier(RunInfo info) {
+        MyValue2[] src = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 100);
+        Arrays.fill(src, testValue2);
+        Object[] dst = new Object[100];
+        rerun_and_recompile_for(info.getTest(), 10,
+                                () -> { test86(src, dst);
+                                        Asserts.assertTrue(Arrays.equals(src, dst)); });
+    }
+
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        counts = {COUNTEDLOOP_MAIN, "= 2"})
+    @IR(applyIf = {"FlatArrayElementMaxSize", "!= -1"},
+        counts = {COUNTEDLOOP_MAIN, "= 1"})
+    public void test87(Object[] src, Object[] dst) {
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Run(test = "test87")
+    @Warmup(0)
+    public void test87_verifier(RunInfo info) {
+        Object[] src = new Object[100];
+        Arrays.fill(src, testValue2);
+        MyValue2[] dst = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 100);
+
+        rerun_and_recompile_for(info.getTest(), 10,
+                                () -> { test87(src, dst);
+                                        Asserts.assertTrue(Arrays.equals(src, dst)); });
+    }
+
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        counts = {COUNTEDLOOP_MAIN, "= 2"})
+    @IR(applyIf = {"FlatArrayElementMaxSize", "!= -1"},
+        counts = {COUNTEDLOOP_MAIN, "= 0"})
+    public void test88(Object[] src1, Object[] dst1, Object[] src2, Object[] dst2) {
+        for (int i = 0; i < src1.length; i++) {
+            dst1[i] = src1[i];
+            dst2[i] = src2[i];
+        }
+    }
+
+    @Run(test = "test88")
+    @Warmup(0)
+    public void test88_verifier(RunInfo info) {
+        MyValue2[] src1 = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 100);
+        Arrays.fill(src1, testValue2);
+        MyValue2[] dst1 = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 100);
+        Object[] src2 = new Object[100];
+        Arrays.fill(src2, new Object());
+        Object[] dst2 = new Object[100];
+
+        rerun_and_recompile_for(info.getTest(), 10,
+                                () -> { test88(src1, dst1, src2, dst2);
+                                        Asserts.assertTrue(Arrays.equals(src1, dst1));
+                                        Asserts.assertTrue(Arrays.equals(src2, dst2)); });
+    }
+
+    @Test
+    public boolean test89(Object obj) {
+        return obj.getClass() == NonValueClass.class;
+    }
+
+    @Run(test = "test89")
+    public void test89_verifier() {
+        Asserts.assertTrue(test89(new NonValueClass(42)));
+        Asserts.assertFalse(test89(new Object()));
+    }
+
+    @Test
+    public NonValueClass test90(Object obj) {
+        return (NonValueClass)obj;
+    }
+
+    @Run(test = "test90")
+    public void test90_verifier() {
+        test90(new NonValueClass(42));
+        try {
+            test90(new Object());
+            throw new RuntimeException("ClassCastException expected");
+        } catch (ClassCastException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public boolean test91(Object obj) {
+        return obj.getClass() == MyValue2[].class;
+    }
+
+    @Run(test = "test91")
+    public void test91_verifier() {
+        Asserts.assertFalse(test91((MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 1)));
+        Asserts.assertTrue(test91(new MyValue2[1]));
+        Asserts.assertFalse(test91(new Object()));
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test92Value {
+        int field;
+
+        public Test92Value() {
+            field = 0x42;
+        }
+    }
+
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        counts = {CLASS_CHECK_TRAP, "= 2"},
+        failOn = {LOAD_UNKNOWN_INLINE, ALLOC_G, MEMBAR})
+    public Object test92(Object[] array) {
+        // Dummy loops to ensure we run enough passes of split if
+        for (int i = 0; i < 2; i++) {
+            for (int j = 0; j < 2; j++) {
+              for (int k = 0; k < 2; k++) {
+              }
+            }
+        }
+
+        return (NonValueClass)array[0];
+    }
+
+    @Run(test = "test92")
+    @Warmup(10000)
+    public void test92_verifier() {
+        Object[] array = new Object[1];
+        Object obj = new NonValueClass(rI);
+        array[0] = obj;
+        Object result = test92(array);
+        Asserts.assertEquals(result, obj);
+    }
+
+    // If the class check succeeds, the flattened array check that
+    // precedes will never succeed and the flat array branch should
+    // trigger an uncommon trap.
+    @Test
+    public Object test93(Object[] array) {
+        for (int i = 0; i < 2; i++) {
+            for (int j = 0; j < 2; j++) {
+            }
+        }
+
+        Object v = (NonValueClass)array[0];
+        return v;
+    }
+
+    @Run(test = "test93")
+    @Warmup(10000)
+    public void test93_verifier(RunInfo info) {
+        if (info.isWarmUp()) {
+            Object[] array = new Object[1];
+            array[0] = new NonValueClass(42);
+            Object result = test93(array);
+            Asserts.assertEquals(((NonValueClass)result).x, 42);
+        } else {
+            Object[] array = (Test92Value[])ValueClass.newNullRestrictedArray(Test92Value.class, 1);
+            Method m = info.getTest();
+            int extra = 3;
+            for (int j = 0; j < extra; j++) {
+                for (int i = 0; i < 10; i++) {
+                    try {
+                        test93(array);
+                    } catch (ClassCastException cce) {
+                    }
+                }
+                boolean compiled = TestFramework.isCompiled(m);
+                boolean compilationSkipped = info.isCompilationSkipped();
+                Asserts.assertTrue(compilationSkipped || compiled || (j != extra-1));
+                if (!compilationSkipped && !compiled) {
+                    TestFramework.compile(m, CompLevel.ANY);
+                }
+            }
+        }
+    }
+
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        counts = {CLASS_CHECK_TRAP, "= 2", LOOP, "= 1"},
+        failOn = {LOAD_UNKNOWN_INLINE, ALLOC_G, MEMBAR})
+    public int test94(Object[] array) {
+        int res = 0;
+        for (int i = 1; i < 4; i *= 2) {
+            Object v = array[i];
+            res += ((NonValueClass)v).x;
+        }
+        return res;
+    }
+
+    @Run(test = "test94")
+    @Warmup(10000)
+    public void test94_verifier() {
+        Object[] array = new Object[4];
+        Object obj = new NonValueClass(rI);
+        array[0] = obj;
+        array[1] = obj;
+        array[2] = obj;
+        array[3] = obj;
+        int result = test94(array);
+        Asserts.assertEquals(result, rI * 2);
+    }
+
+    @Test
+    public boolean test95(Object o1, Object o2) {
+        return o1 == o2;
+    }
+
+    @Run(test = "test95")
+    @Warmup(10000)
+    public void test95_verifier() {
+        Object o1 = new Object();
+        Object o2 = new Object();
+        Asserts.assertTrue(test95(o1, o1));
+        Asserts.assertTrue(test95(null, null));
+        Asserts.assertFalse(test95(o1, null));
+        Asserts.assertFalse(test95(o1, o2));
+    }
+
+    @Test
+    public boolean test96(Object o1, Object o2) {
+        return o1 == o2;
+    }
+
+    @Run(test = "test96")
+    @Warmup(10000)
+    public void test96_verifier(RunInfo info) {
+        Object o1 = new Object();
+        Object o2 = new Object();
+        Asserts.assertTrue(test96(o1, o1));
+        Asserts.assertFalse(test96(o1, o2));
+        if (!info.isWarmUp()) {
+            Asserts.assertTrue(test96(null, null));
+            Asserts.assertFalse(test96(o1, null));
+        }
+    }
+
+    // Abstract class tests
+
+    @DontInline
+    public MyAbstract test97_dontinline1(MyAbstract o) {
+        return o;
+    }
+
+    @DontInline
+    public MyValue1 test97_dontinline2(MyAbstract o) {
+        return (MyValue1)o;
+    }
+
+    @ForceInline
+    public MyAbstract test97_inline1(MyAbstract o) {
+        return o;
+    }
+
+    @ForceInline
+    public MyValue1 test97_inline2(MyAbstract o) {
+        return (MyValue1)o;
+    }
+
+    @Test
+    public MyValue1 test97() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        vt = (MyValue1)test97_dontinline1(vt);
+        vt =           test97_dontinline2(vt);
+        vt = (MyValue1)test97_inline1(vt);
+        vt =           test97_inline2(vt);
+        return vt;
+    }
+
+    @Run(test = "test97")
+    public void test97_verifier() {
+        Asserts.assertEQ(test97().hash(), hash());
+    }
+
+    // Test storing/loading inline types to/from abstract and inline type fields
+    MyAbstract abstractField1 = null;
+    MyAbstract abstractField2 = null;
+    MyAbstract abstractField3 = null;
+    MyAbstract abstractField4 = null;
+    MyAbstract abstractField5 = null;
+    MyAbstract abstractField6 = null;
+
+    @DontInline
+    public MyAbstract readValueField5AsAbstract() {
+        return (MyAbstract)valueField5;
+    }
+
+    @DontInline
+    public MyAbstract readStaticValueField4AsAbstract() {
+        return (MyAbstract)staticValueField4;
+    }
+
+    @Test
+    public long test98(MyValue1 vt1, MyAbstract vt2) {
+        abstractField1 = vt1;
+        abstractField2 = (MyValue1)vt2;
+        abstractField3 = MyValue1.createWithFieldsInline(rI, rL);
+        abstractField4 = MyValue1.createWithFieldsDontInline(rI, rL);
+        abstractField5 = valueField1;
+        abstractField6 = valueField3;
+        valueField1 = (MyValue1)abstractField1;
+        valueField2 = (MyValue1)vt2;
+        valueField3 = (MyValue1)vt2;
+        staticValueField1 = (MyValue1)abstractField1;
+        staticValueField2 = (MyValue1)vt1;
+        // Don't inline these methods because reading NULL will trigger a deoptimization
+        if (readValueField5AsAbstract() != null || readStaticValueField4AsAbstract() != null) {
+            throw new RuntimeException("Should be null");
+        }
+        return ((MyValue1)abstractField1).hash() + ((MyValue1)abstractField2).hash() +
+               ((MyValue1)abstractField3).hash() + ((MyValue1)abstractField4).hash() +
+               ((MyValue1)abstractField5).hash() + ((MyValue1)abstractField6).hash() +
+                valueField1.hash() + valueField2.hash() + valueField3.hash() + valueField4.hashPrimitive() +
+                staticValueField1.hash() + staticValueField2.hash() + staticValueField3.hashPrimitive();
+    }
+
+    @Run(test = "test98")
+    public void test98_verifier() {
+        MyValue1 vt = testValue1;
+        MyValue1 def = MyValue1.createDefaultDontInline();
+        long result = test98(vt, vt);
+        Asserts.assertEQ(result, 11*vt.hash() + 2*def.hashPrimitive());
+    }
+
+    value class MyObject2 extends MyAbstract {
+        public int x;
+
+        public MyObject2(int x) {
+            this.x = x;
+        }
+
+        @ForceInline
+        public long hash() {
+            return x;
+        }
+    }
+
+    // Test merging inline types and abstract classes
+    @Test
+    public MyAbstract test99(int state) {
+        MyAbstract res = null;
+        if (state == 0) {
+            res = new MyObject2(rI);
+        } else if (state == 1) {
+            res = MyValue1.createWithFieldsInline(rI, rL);
+        } else if (state == 2) {
+            res = MyValue1.createWithFieldsDontInline(rI, rL);
+        } else if (state == 3) {
+            res = (MyValue1)objectField1;
+        } else if (state == 4) {
+            res = valueField1;
+        } else if (state == 5) {
+            res = null;
+        }
+        return res;
+    }
+
+    @Run(test = "test99")
+    public void test99_verifier() {
+        objectField1 = valueField1;
+        MyAbstract result = null;
+        result = test99(0);
+        Asserts.assertEQ(((MyObject2)result).x, rI);
+        result = test99(1);
+        Asserts.assertEQ(((MyValue1)result).hash(), hash());
+        result = test99(2);
+        Asserts.assertEQ(((MyValue1)result).hash(), hash());
+        result = test99(3);
+        Asserts.assertEQ(((MyValue1)result).hash(), hash());
+        result = test99(4);
+        Asserts.assertEQ(((MyValue1)result).hash(), hash());
+        result = test99(5);
+        Asserts.assertEQ(result, null);
+    }
+
+    // Test merging inline types and abstract classes in loops
+    @Test
+    public MyAbstract test100(int iters) {
+        MyAbstract res = new MyObject2(rI);
+        for (int i = 0; i < iters; ++i) {
+            if (res instanceof MyObject2) {
+                res = MyValue1.createWithFieldsInline(rI, rL);
+            } else {
+                res = MyValue1.createWithFieldsInline(((MyValue1)res).x + 1, rL);
+            }
+        }
+        return res;
+    }
+
+    @Run(test = "test100")
+    public void test100_verifier() {
+        MyObject2 result1 = (MyObject2)test100(0);
+        Asserts.assertEQ(result1.x, rI);
+        int iters = (Math.abs(rI) % 10) + 1;
+        MyValue1 result2 = (MyValue1)test100(iters);
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI + iters - 1, rL);
+        Asserts.assertEQ(result2.hash(), vt.hash());
+    }
+
+    // Test inline types in abstract class variables that are live at safepoint
+    @Test
+    @IR(failOn = {ALLOC, STORE, LOOP})
+    public long test101(MyValue1 arg, boolean deopt, Method m) {
+        MyAbstract vt1 = MyValue1.createWithFieldsInline(rI, rL);
+        MyAbstract vt2 = MyValue1.createWithFieldsDontInline(rI, rL);
+        MyAbstract vt3 = arg;
+        MyAbstract vt4 = valueField1;
+        if (deopt) {
+            // uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return ((MyValue1)vt1).hash() + ((MyValue1)vt2).hash() +
+               ((MyValue1)vt3).hash() + ((MyValue1)vt4).hash();
+    }
+
+    @Run(test = "test101")
+    public void test101_verifier(RunInfo info) {
+        long result = test101(valueField1, !info.isWarmUp(), info.getTest());
+        Asserts.assertEQ(result, 4*hash());
+    }
+
+    // Test comparing inline types with abstract classes
+    @Test
+    public boolean test102(Object arg) {
+        MyAbstract vt = MyValue1.createWithFieldsInline(rI, rL);
+        if (vt == arg || vt == (MyAbstract)valueField1 || vt == abstractField1 || vt == null ||
+            arg == vt || (MyAbstract)valueField1 == vt || abstractField1 == vt || null == vt) {
+            return true;
+        }
+        return false;
+    }
+
+    @Run(test = "test102")
+    public void test102_verifier() {
+        boolean result = test102(null);
+        Asserts.assertFalse(result);
+    }
+
+    // An abstract class with a non-static field can never be implemented by an inline type
+    abstract class NoValueImplementors1 {
+        int field = 42;
+    }
+
+    class MyObject3 extends NoValueImplementors1 {
+
+    }
+
+    class MyObject4 extends NoValueImplementors1 {
+
+    }
+
+    // Loading from an abstract class array does not require a flatness check if the abstract class has a non-static field
+    @Test
+    @IR(failOn = {ALLOC_G, MEMBAR, LOAD_UNKNOWN_INLINE, STORE_UNKNOWN_INLINE, INLINE_ARRAY_NULL_GUARD})
+    public NoValueImplementors1 test103(NoValueImplementors1[] array, int i) {
+        return array[i];
+    }
+
+    @Run(test = "test103")
+    public void test103_verifier() {
+        NoValueImplementors1[] array1 = new NoValueImplementors1[3];
+        MyObject3[] array2 = new MyObject3[3];
+        MyObject4[] array3 = new MyObject4[3];
+        NoValueImplementors1 result = test103(array1, 0);
+        Asserts.assertEquals(result, array1[0]);
+
+        result = test103(array2, 1);
+        Asserts.assertEquals(result, array1[1]);
+
+        result = test103(array3, 2);
+        Asserts.assertEquals(result, array1[2]);
+    }
+
+    // Storing to an abstract class array does not require a flatness/null check if the abstract class has a non-static field
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD_UNKNOWN_INLINE, STORE_UNKNOWN_INLINE, INLINE_ARRAY_NULL_GUARD})
+    public NoValueImplementors1 test104(NoValueImplementors1[] array, NoValueImplementors1 v, MyObject3 o, int i) {
+        array[0] = v;
+        array[1] = array[0];
+        array[2] = o;
+        return array[i];
+    }
+
+    @Run(test = "test104")
+    public void test104_verifier() {
+        MyObject4 v = new MyObject4();
+        MyObject3 o = new MyObject3();
+        NoValueImplementors1[] array1 = new NoValueImplementors1[3];
+        MyObject3[] array2 = new MyObject3[3];
+        MyObject4[] array3 = new MyObject4[3];
+        NoValueImplementors1 result = test104(array1, v, o, 0);
+        Asserts.assertEquals(array1[0], v);
+        Asserts.assertEquals(array1[1], v);
+        Asserts.assertEquals(array1[2], o);
+        Asserts.assertEquals(result, v);
+
+        result = test104(array2, o, o, 1);
+        Asserts.assertEquals(array2[0], o);
+        Asserts.assertEquals(array2[1], o);
+        Asserts.assertEquals(array2[2], o);
+        Asserts.assertEquals(result, o);
+
+        result = test104(array3, v, null, 1);
+        Asserts.assertEquals(array3[0], v);
+        Asserts.assertEquals(array3[1], v);
+        Asserts.assertEquals(array3[2], null);
+        Asserts.assertEquals(result, v);
+    }
+
+    // An abstract class with a single, non-inline implementor
+    abstract class NoValueImplementors2 {
+
+    }
+
+    class MyObject5 extends NoValueImplementors2 {
+
+    }
+
+    // Loading from an abstract class array does not require a flatness check if the abstract class has no inline implementor
+    @Test
+    @IR(failOn = {ALLOC_G, MEMBAR, LOAD_UNKNOWN_INLINE, STORE_UNKNOWN_INLINE, INLINE_ARRAY_NULL_GUARD})
+    public NoValueImplementors2 test105(NoValueImplementors2[] array, int i) {
+        return array[i];
+    }
+
+    @Run(test = "test105")
+    public void test105_verifier() {
+        NoValueImplementors2[] array1 = new NoValueImplementors2[3];
+        MyObject5[] array2 = new MyObject5[3];
+        NoValueImplementors2 result = test105(array1, 0);
+        Asserts.assertEquals(result, array1[0]);
+
+        result = test105(array2, 1);
+        Asserts.assertEquals(result, array1[1]);
+    }
+
+    // Storing to an abstract class array does not require a flatness/null check if the abstract class has no inline implementor
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD_UNKNOWN_INLINE, STORE_UNKNOWN_INLINE, INLINE_ARRAY_NULL_GUARD})
+    public NoValueImplementors2 test106(NoValueImplementors2[] array, NoValueImplementors2 v, MyObject5 o, int i) {
+        array[0] = v;
+        array[1] = array[0];
+        array[2] = o;
+        return array[i];
+    }
+
+    @Run(test = "test106")
+    public void test106_verifier() {
+        MyObject5 v = new MyObject5();
+        NoValueImplementors2[] array1 = new NoValueImplementors2[3];
+        MyObject5[] array2 = new MyObject5[3];
+        NoValueImplementors2 result = test106(array1, v, null, 0);
+        Asserts.assertEquals(array1[0], v);
+        Asserts.assertEquals(array1[1], v);
+        Asserts.assertEquals(array1[2], null);
+        Asserts.assertEquals(result, v);
+
+        result = test106(array2, v, v, 1);
+        Asserts.assertEquals(array2[0], v);
+        Asserts.assertEquals(array2[1], v);
+        Asserts.assertEquals(array2[2], v);
+        Asserts.assertEquals(result, v);
+    }
+
+    // More tests for the Loop Unswitching optimization (similar to test84 and following)
+    Object oFld1, oFld2;
+
+    @Test
+    @IR(applyIfAnd = {"UseG1GC", "true", "FlatArrayElementMaxSize", "= -1"},
+        failOn = {STORE_UNKNOWN_INLINE, INLINE_ARRAY_NULL_GUARD},
+        counts = {COUNTEDLOOP, "= 2", LOAD_UNKNOWN_INLINE, "= 2"})
+    @IR(applyIfAnd = {"UseG1GC", "false", "FlatArrayElementMaxSize", "= -1"},
+        failOn = {STORE_UNKNOWN_INLINE, INLINE_ARRAY_NULL_GUARD},
+        counts = {COUNTEDLOOP, "= 3", LOAD_UNKNOWN_INLINE, "= 2"})
+    public void test107(Object[] src1, Object[] src2) {
+        for (int i = 0; i < src1.length; i++) {
+            oFld1 = src1[i];
+            oFld2 = src2[i];
+        }
+    }
+
+    @Run(test = "test107")
+    @Warmup(0)
+    public void test107_verifier(RunInfo info) {
+        MyValue2[] src1 = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 100);
+        Arrays.fill(src1, testValue2);
+        Object[] src2 = new Object[100];
+        Object obj = new Object();
+        Arrays.fill(src2, obj);
+        rerun_and_recompile_for(info.getTest(), 10,
+                                () -> { test107(src1, src2);
+                                        Asserts.assertEquals(oFld1, testValue2);
+                                        Asserts.assertEquals(oFld2, obj);
+                                        test107(src2, src1);
+                                        Asserts.assertEquals(oFld1, obj);
+                                        Asserts.assertEquals(oFld2, testValue2);  });
+    }
+
+    @Test
+    @IR(applyIfAnd = {"UseG1GC", "true", "FlatArrayElementMaxSize", "= -1"},
+        failOn = {LOAD_UNKNOWN_INLINE, INLINE_ARRAY_NULL_GUARD},
+        counts = {COUNTEDLOOP, "= 4", STORE_UNKNOWN_INLINE, "= 9"})
+    @IR(applyIfAnd = {"UseG1GC", "false", "FlatArrayElementMaxSize", "= -1"},
+        failOn = {LOAD_UNKNOWN_INLINE, INLINE_ARRAY_NULL_GUARD},
+        counts = {COUNTEDLOOP, "= 4", STORE_UNKNOWN_INLINE, "= 12"})
+    public void test108(Object[] dst1, Object[] dst2, Object o1, Object o2) {
+        for (int i = 0; i < dst1.length; i++) {
+            dst1[i] = o1;
+            dst2[i] = o2;
+        }
+    }
+
+    @Run(test = "test108")
+    @Warmup(0)
+    public void test108_verifier(RunInfo info) {
+        MyValue2[] dst1 = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 100);
+        Object[] dst2 = new Object[100];
+        Object o1 = new Object();
+        rerun_and_recompile_for(info.getTest(), 10,
+                                () -> { test108(dst1, dst2, testValue2, o1);
+                                        for (int i = 0; i < dst1.length; i++) {
+                                            Asserts.assertEquals(dst1[i], testValue2);
+                                            Asserts.assertEquals(dst2[i], o1);
+                                        }
+                                        test108(dst2, dst1, o1, testValue2);
+                                        for (int i = 0; i < dst1.length; i++) {
+                                            Asserts.assertEquals(dst1[i], testValue2);
+                                            Asserts.assertEquals(dst2[i], o1);
+                                        } });
+    }
+
+    // Escape analysis tests
+
+    static interface WrapperInterface {
+        long value();
+
+        final static WrapperInterface ZERO = new LongWrapper(0);
+
+        @ForceInline
+        static WrapperInterface wrap(long val) {
+            return (val == 0L) ? ZERO : new LongWrapper(val);
+        }
+    }
+
+    @ForceCompileClassInitializer
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class LongWrapper implements WrapperInterface {
+        @NullRestricted
+        final static LongWrapper ZERO = new LongWrapper(0);
+        private long val;
+
+        @ForceInline
+        LongWrapper(long val) {
+            this.val = val;
+        }
+
+        @ForceInline
+        static LongWrapper wrap(long val) {
+            return (val == 0L) ? ZERO : new LongWrapper(val);
+        }
+
+        @ForceInline
+        public long value() {
+            return val;
+        }
+    }
+
+    static class InterfaceBox {
+        WrapperInterface content;
+
+        @ForceInline
+        InterfaceBox(WrapperInterface content) {
+            this.content = content;
+        }
+
+        @ForceInline
+        static InterfaceBox box_sharp(long val) {
+            return new InterfaceBox(LongWrapper.wrap(val));
+        }
+
+        @ForceInline
+        static InterfaceBox box(long val) {
+            return new InterfaceBox(WrapperInterface.wrap(val));
+        }
+    }
+
+    static class ObjectBox {
+        Object content;
+
+        @ForceInline
+        ObjectBox(Object content) {
+            this.content = content;
+        }
+
+        @ForceInline
+        static ObjectBox box_sharp(long val) {
+            return new ObjectBox(LongWrapper.wrap(val));
+        }
+
+        @ForceInline
+        static ObjectBox box(long val) {
+            return new ObjectBox(WrapperInterface.wrap(val));
+        }
+    }
+
+    static class RefBox {
+        LongWrapper content;
+
+        @ForceInline
+        RefBox(LongWrapper content) {
+            this.content = content;
+        }
+
+        @ForceInline
+        static RefBox box_sharp(long val) {
+            return new RefBox(LongWrapper.wrap(val));
+        }
+
+        @ForceInline
+        static RefBox box(long val) {
+            return new RefBox((LongWrapper)WrapperInterface.wrap(val));
+        }
+    }
+
+    static class InlineBox {
+        @NullRestricted
+        LongWrapper content;
+
+        @ForceInline
+        InlineBox(long val) {
+            this.content = LongWrapper.wrap(val);
+        }
+
+        @ForceInline
+        static InlineBox box(long val) {
+            return new InlineBox(val);
+        }
+    }
+
+    static class GenericBox<T> {
+        T content;
+
+        @ForceInline
+        static GenericBox<LongWrapper> box_sharp(long val) {
+            GenericBox<LongWrapper> res = new GenericBox<>();
+            res.content = LongWrapper.wrap(val);
+            return res;
+        }
+
+        @ForceInline
+        static GenericBox<WrapperInterface> box(long val) {
+            GenericBox<WrapperInterface> res = new GenericBox<>();
+            res.content = WrapperInterface.wrap(val);
+            return res;
+        }
+    }
+
+    long[] lArr = {0L, rL, 0L, rL, 0L, rL, 0L, rL, 0L, rL};
+
+    // Test removal of allocations when inline type instance is wrapped into box object
+    @Test
+    @IR(failOn = {ALLOC_G, MEMBAR},
+        counts = {PREDICATE_TRAP, "= 1"})
+    public long test109() {
+        long res = 0;
+        for (int i = 0; i < lArr.length; i++) {
+            res += InterfaceBox.box(lArr[i]).content.value();
+        }
+        return res;
+    }
+
+    @Run(test = "test109")
+    @Warmup(10000) // Make sure interface calls are inlined
+    public void test109_verifier() {
+        long res = test109();
+        Asserts.assertEquals(res, 5*rL);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, MEMBAR},
+        counts = {PREDICATE_TRAP, "= 1"})
+    @IR(failOn = {ALLOC_G, MEMBAR})
+    public long test109_sharp() {
+        long res = 0;
+        for (int i = 0; i < lArr.length; i++) {
+            res += InterfaceBox.box_sharp(lArr[i]).content.value();
+        }
+        return res;
+    }
+
+    @Run(test = "test109_sharp")
+    @Warmup(10000) // Make sure interface calls are inlined
+    public void test109_sharp_verifier() {
+        long res = test109_sharp();
+        Asserts.assertEquals(res, 5*rL);
+    }
+
+    // Same as test109 but with ObjectBox
+    @Test
+    @IR(failOn = {ALLOC_G, MEMBAR},
+        counts = {PREDICATE_TRAP, "= 1"})
+    public long test110() {
+        long res = 0;
+        for (int i = 0; i < lArr.length; i++) {
+            res += ((WrapperInterface)ObjectBox.box(lArr[i]).content).value();
+        }
+        return res;
+    }
+
+    @Run(test = "test110")
+    @Warmup(10000) // Make sure interface calls are inlined
+    public void test110_verifier() {
+        long res = test110();
+        Asserts.assertEquals(res, 5*rL);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, MEMBAR},
+        counts = {PREDICATE_TRAP, "= 1"})
+    @IR(failOn = {ALLOC_G, MEMBAR})
+    public long test110_sharp() {
+        long res = 0;
+        for (int i = 0; i < lArr.length; i++) {
+            res += ((WrapperInterface)ObjectBox.box_sharp(lArr[i]).content).value();
+        }
+        return res;
+    }
+
+    @Run(test = "test110_sharp")
+    @Warmup(10000) // Make sure interface calls are inlined
+    public void test110_sharp_verifier() {
+        long res = test110_sharp();
+        Asserts.assertEquals(res, 5*rL);
+    }
+
+    // Same as test109 but with RefBox
+    @Test
+    @IR(failOn = {ALLOC_G, MEMBAR},
+        counts = {PREDICATE_TRAP, "= 1"})
+    public long test111() {
+        long res = 0;
+        for (int i = 0; i < lArr.length; i++) {
+            res += RefBox.box(lArr[i]).content.value();
+        }
+        return res;
+    }
+
+    @Run(test = "test111")
+    public void test111_verifier() {
+        long res = test111();
+        Asserts.assertEquals(res, 5*rL);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, MEMBAR},
+        counts = {PREDICATE_TRAP, "= 1"})
+    public long test111_sharp() {
+        long res = 0;
+        for (int i = 0; i < lArr.length; i++) {
+            res += RefBox.box_sharp(lArr[i]).content.value();
+        }
+        return res;
+    }
+
+    @Run(test = "test111_sharp")
+    public void test111_sharp_verifier() {
+        long res = test111_sharp();
+        Asserts.assertEquals(res, 5*rL);
+    }
+
+    // Same as test109 but with InlineBox
+    @Test
+    @IR(failOn = {ALLOC_G, MEMBAR},
+        counts = {PREDICATE_TRAP, "= 1"})
+    public long test112() {
+        long res = 0;
+        for (int i = 0; i < lArr.length; i++) {
+            res += InlineBox.box(lArr[i]).content.value();
+        }
+        return res;
+    }
+
+    @Run(test = "test112")
+    public void test112_verifier() {
+        long res = test112();
+        Asserts.assertEquals(res, 5*rL);
+    }
+
+    // Same as test109 but with GenericBox
+    @Test
+    @IR(failOn = {ALLOC_G, MEMBAR},
+        counts = {PREDICATE_TRAP, "= 1"})
+    public long test113() {
+        long res = 0;
+        for (int i = 0; i < lArr.length; i++) {
+            res += GenericBox.box(lArr[i]).content.value();
+        }
+        return res;
+    }
+
+    @Run(test = "test113")
+    @Warmup(10000) // Make sure interface calls are inlined
+    public void test113_verifier() {
+        long res = test113();
+        Asserts.assertEquals(res, 5*rL);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, MEMBAR},
+        counts = {PREDICATE_TRAP, "= 1"})
+    public long test113_sharp() {
+        long res = 0;
+        for (int i = 0; i < lArr.length; i++) {
+            res += GenericBox.box_sharp(lArr[i]).content.value();
+        }
+        return res;
+    }
+
+    @Run(test = "test113_sharp")
+    @Warmup(10000) // Make sure interface calls are inlined
+    public void test113_sharp_verifier() {
+        long res = test113_sharp();
+        Asserts.assertEquals(res, 5*rL);
+    }
+
+    static interface WrapperInterface2 {
+        public long value();
+
+        static final InlineWrapper ZERO = new InlineWrapper(0);
+
+        @ForceInline
+        public static WrapperInterface2 wrap(long val) {
+            return (val == 0) ? ZERO.content : new LongWrapper2(val);
+        }
+
+        @ForceInline
+        public static WrapperInterface2 wrap_default(long val) {
+            return (val == 0) ? new LongWrapper2(0) : new LongWrapper2(val);
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class LongWrapper2 implements WrapperInterface2 {
+        private long val;
+
+        @ForceInline
+        public LongWrapper2(long val) {
+            this.val = val;
+        }
+
+        @ForceInline
+        public long value() {
+            return val;
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class InlineWrapper {
+        WrapperInterface2 content;
+
+        @ForceInline
+        public InlineWrapper(long val) {
+            content = new LongWrapper2(val);
+        }
+    }
+
+    static class InterfaceBox2 {
+        WrapperInterface2 content;
+
+        @ForceInline
+        public InterfaceBox2(long val, boolean def) {
+            this.content = def ? WrapperInterface2.wrap_default(val) : WrapperInterface2.wrap(val);
+        }
+
+        @ForceInline
+        static InterfaceBox2 box(long val) {
+            return new InterfaceBox2(val, false);
+        }
+
+        @ForceInline
+        static InterfaceBox2 box_default(long val) {
+            return new InterfaceBox2(val, true);
+        }
+    }
+
+    // Same as tests above but with ZERO hidden in field of another inline type
+    @Test
+    @IR(failOn = {ALLOC_G, MEMBAR},
+        counts = {PREDICATE_TRAP, "= 1"})
+    public long test114() {
+        long res = 0;
+        for (int i = 0; i < lArr.length; i++) {
+            res += InterfaceBox2.box(lArr[i]).content.value();
+        }
+        return res;
+    }
+
+    @Run(test = "test114")
+    @Warmup(10000)
+    public void test114_verifier() {
+        long res = test114();
+        Asserts.assertEquals(res, 5*rL);
+    }
+
+    // Same as test114 but with default instead of ZERO field
+    @Test
+    @IR(failOn = {ALLOC_G, MEMBAR},
+        counts = {PREDICATE_TRAP, "= 1"})
+    public long test115() {
+        long res = 0;
+        for (int i = 0; i < lArr.length; i++) {
+            res += InterfaceBox2.box_default(lArr[i]).content.value();
+        }
+        return res;
+    }
+
+    @Run(test = "test115")
+    @Warmup(10000)
+    public void test115_verifier() {
+        long res = test115();
+        Asserts.assertEquals(res, 5*rL);
+    }
+
+    @NullRestricted
+    static MyValueEmpty fEmpty1;
+    static MyValueEmpty fEmpty2 = new MyValueEmpty();
+    @NullRestricted
+           MyValueEmpty fEmpty3;
+           MyValueEmpty fEmpty4 = new MyValueEmpty();
+
+    // Test fields loads/stores with empty inline types
+    @Test
+    @IR(failOn = {ALLOC_G, TRAP})
+    public void test116() {
+        fEmpty1 = fEmpty4;
+        fEmpty2 = fEmpty1;
+        fEmpty3 = fEmpty2;
+        fEmpty4 = fEmpty3;
+    }
+
+    @Run(test = "test116")
+    public void test116_verifier() {
+        test116();
+        Asserts.assertEquals(fEmpty1, fEmpty2);
+        Asserts.assertEquals(fEmpty2, fEmpty3);
+        Asserts.assertEquals(fEmpty3, fEmpty4);
+    }
+
+    // Test array loads/stores with empty inline types
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public MyValueEmpty test117(MyValueEmpty[] arr1, MyValueEmpty[] arr2) {
+        arr1[0] = arr2[0];
+        arr2[0] = new MyValueEmpty();
+        return arr1[0];
+    }
+
+    @Run(test = "test117")
+    public void test117_verifier() {
+        MyValueEmpty[] arr1 = new MyValueEmpty[] { new MyValueEmpty() };
+        MyValueEmpty res = test117(arr1, arr1);
+        Asserts.assertEquals(res, new MyValueEmpty());
+        Asserts.assertEquals(arr1[0], new MyValueEmpty());
+    }
+
+    // Test acmp with empty inline types
+    @Test
+    public boolean test118(MyValueEmpty v1, MyValueEmpty v2, Object o1) {
+        return (v1 == v2) && (v2 == o1);
+    }
+
+    @Run(test = "test118")
+    public void test118_verifier() {
+        boolean res = test118(new MyValueEmpty(), new MyValueEmpty(), new MyValueEmpty());
+        Asserts.assertTrue(res);
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class EmptyContainer {
+        @NullRestricted
+        private MyValueEmpty empty = new MyValueEmpty();
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MixedContainer {
+        public int val = 0;
+        @NullRestricted
+        private EmptyContainer empty = new EmptyContainer();
+    }
+
+    @NullRestricted
+    static final MyValueEmpty empty = new MyValueEmpty();
+
+    @NullRestricted
+    static final EmptyContainer emptyC = new EmptyContainer();
+
+    @NullRestricted
+    static final MixedContainer mixedContainer = new MixedContainer();
+
+    // Test re-allocation of empty inline type array during deoptimization
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public void test119(boolean deopt, Method m) {
+        MyValueEmpty[]   array1 = new MyValueEmpty[] { empty };
+        EmptyContainer[] array2 = (EmptyContainer[])ValueClass.newNullRestrictedArray(EmptyContainer.class, 1);
+        array2[0] = emptyC;
+        MixedContainer[] array3 = (MixedContainer[])ValueClass.newNullRestrictedArray(MixedContainer.class, 1);
+        array3[0] = mixedContainer;
+        if (deopt) {
+            // uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        Asserts.assertEquals(array1[0], empty);
+        Asserts.assertEquals(array2[0], emptyC);
+        Asserts.assertEquals(array3[0], mixedContainer);
+    }
+
+    @Run(test = "test119")
+    public void test119_verifier(RunInfo info) {
+        test119(!info.isWarmUp(), info.getTest());
+    }
+
+    // Test removal of empty inline type field stores
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD, STORE, FIELD_ACCESS, NULL_CHECK_TRAP, TRAP})
+    public void test120() {
+        fEmpty1 = empty;
+        fEmpty3 = empty;
+        // fEmpty2 and fEmpty4 could be null, store can't be removed
+    }
+
+    @Run(test = "test120")
+    public void test120_verifier() {
+        test120();
+        Asserts.assertEquals(fEmpty1, empty);
+        Asserts.assertEquals(fEmpty2, empty);
+    }
+
+    // Test removal of empty inline type field loads
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD, STORE, FIELD_ACCESS, NULL_CHECK_TRAP, TRAP})
+    public boolean test121() {
+        return fEmpty1.equals(fEmpty3);
+        // fEmpty2 and fEmpty4 could be null, load can't be removed
+    }
+
+    @Run(test = "test121")
+    public void test121_verifier() {
+        boolean res = test121();
+        Asserts.assertTrue(res);
+    }
+
+    // Verify that empty inline type field loads check for null holder
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public MyValueEmpty test122(TestLWorld t) {
+        return t.fEmpty3;
+    }
+
+    @Run(test = "test122")
+    public void test122_verifier() {
+        MyValueEmpty res = test122(this);
+        Asserts.assertEquals(res, new MyValueEmpty());
+        try {
+            test122(null);
+            throw new RuntimeException("No NPE thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    // Verify that empty inline type field stores check for null holder
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public void test123(TestLWorld t) {
+        t.fEmpty3 = new MyValueEmpty();
+    }
+
+    @Run(test = "test123")
+    public void test123_verifier() {
+        test123(this);
+        Asserts.assertEquals(fEmpty3, new MyValueEmpty());
+        try {
+            test123(null);
+            throw new RuntimeException("No NPE thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    // acmp doesn't need substitutability test when one input is known
+    // not to be a value type
+    @Test
+    @IR(failOn = SUBSTITUTABILITY_TEST)
+    public boolean test124(NonValueClass o1, Object o2) {
+        return o1 == o2;
+    }
+
+    @Run(test = "test124")
+    public void test124_verifier() {
+        NonValueClass obj = new NonValueClass(rI);
+        test124(obj, obj);
+        test124(obj, testValue1);
+    }
+
+    // acmp doesn't need substitutability test when one input is null
+    @Test
+    @IR(failOn = {SUBSTITUTABILITY_TEST})
+    public boolean test125(Object o1) {
+        Object o2 = null;
+        return o1 == o2;
+    }
+
+    @Run(test = "test125")
+    public void test125_verifier() {
+        test125(testValue1);
+        test125(null);
+    }
+
+    // Test inline type that can only be scalarized after loop opts
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD, STORE})
+    public long test126(boolean trap) {
+        MyValue2 nonNull = MyValue2.createWithFieldsInline(rI, rD);
+        MyValue2 val = null;
+
+        for (int i = 0; i < 4; i++) {
+            if ((i % 2) == 0) {
+                val = nonNull;
+            }
+        }
+        // 'val' is always non-null here but that's only known after loop opts
+        if (trap) {
+            // Uncommon trap with an inline input that can only be scalarized after loop opts
+            return val.hash();
+        }
+        return 0;
+    }
+
+    @Run(test = "test126")
+    @Warmup(10000)
+    public void test126_verifier(RunInfo info) {
+        long res = test126(false);
+        Asserts.assertEquals(res, 0L);
+        if (!info.isWarmUp()) {
+            res = test126(true);
+            Asserts.assertEquals(res, testValue2.hash());
+        }
+    }
+
+    // Same as test126 but with interface type
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD, STORE})
+    public long test127(boolean trap) {
+        MyValue2 nonNull = MyValue2.createWithFieldsInline(rI, rD);
+        MyInterface val = null;
+
+        for (int i = 0; i < 4; i++) {
+            if ((i % 2) == 0) {
+                val = nonNull;
+            }
+        }
+        // 'val' is always non-null here but that's only known after loop opts
+        if (trap) {
+            // Uncommon trap with an inline input that can only be scalarized after loop opts
+            return val.hash();
+        }
+        return 0;
+    }
+
+    @Run(test = "test127")
+    @Warmup(10000)
+    public void test127_verifier(RunInfo info) {
+        long res = test127(false);
+        Asserts.assertEquals(res, 0L);
+        if (!info.isWarmUp()) {
+            res = test127(true);
+            Asserts.assertEquals(res, testValue2.hash());
+        }
+    }
+
+    // Test inline type that can only be scalarized after CCP
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD, STORE})
+    public long test128(boolean trap) {
+        MyValue2 nonNull = MyValue2.createWithFieldsInline(rI, rD);
+        MyValue2 val = null;
+
+        int limit = 2;
+        for (; limit < 4; limit *= 2);
+        for (int i = 2; i < limit; i++) {
+            val = nonNull;
+        }
+        // 'val' is always non-null here but that's only known after CCP
+        if (trap) {
+            // Uncommon trap with an inline input that can only be scalarized after CCP
+            return val.hash();
+        }
+        return 0;
+    }
+
+    @Run(test = "test128")
+    @Warmup(10000)
+    public void test128_verifier(RunInfo info) {
+        long res = test128(false);
+        Asserts.assertEquals(res, 0L);
+        if (!info.isWarmUp()) {
+            res = test128(true);
+            Asserts.assertEquals(res, testValue2.hash());
+        }
+    }
+
+    // Same as test128 but with interface type
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD, STORE})
+    public long test129(boolean trap) {
+        MyValue2 nonNull = MyValue2.createWithFieldsInline(rI, rD);
+        MyInterface val = null;
+
+        int limit = 2;
+        for (; limit < 4; limit *= 2);
+        for (int i = 0; i < limit; i++) {
+            val = nonNull;
+        }
+        // 'val' is always non-null here but that's only known after CCP
+        if (trap) {
+            // Uncommon trap with an inline input that can only be scalarized after CCP
+            return val.hash();
+        }
+        return 0;
+    }
+
+    @Run(test = "test129")
+    @Warmup(10000)
+    public void test129_verifier(RunInfo info) {
+        long res = test129(false);
+        Asserts.assertEquals(res, 0L);
+        if (!info.isWarmUp()) {
+            res = test129(true);
+            Asserts.assertEquals(res, testValue2.hash());
+        }
+    }
+
+    // Lock on inline type (known after inlining)
+    @ForceInline
+    public Object test130_inlinee() {
+        return MyValue1.createWithFieldsInline(rI, rL);
+    }
+
+    @Test
+    // TODO 8325106
+    /*
+    @IR(failOn = {LOAD},
+        // LockNode keeps MyValue1 allocation alive up until macro expansion which in turn keeps MyValue2
+        // alloc alive. Although the MyValue1 allocation is removed (unused), MyValue2 is expanded first
+        // and therefore stays.
+        counts = {ALLOC, "<= 1", STORE, "<= 1"})
+    */
+    public void test130() {
+        Object obj = test130_inlinee();
+        synchronized (obj) {
+            throw new RuntimeException("test130 failed: synchronization on inline type should not succeed");
+        }
+    }
+
+    @Run(test = "test130")
+    public void test130_verifier() {
+        try {
+            test130();
+            throw new RuntimeException("test130 failed: no exception thrown");
+        } catch (IllegalMonitorStateException ex) {
+            // Expected
+        }
+    }
+
+    // Same as test130 but with field load instead of allocation
+    @ForceInline
+    public Object test131_inlinee() {
+        return testValue1;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public void test131() {
+        Object obj = test131_inlinee();
+        synchronized (obj) {
+            throw new RuntimeException("test131 failed: synchronization on inline type should not succeed");
+        }
+    }
+
+    @Run(test = "test131")
+    public void test131_verifier() {
+        try {
+            test131();
+            throw new RuntimeException("test131 failed: no exception thrown");
+        } catch (IllegalMonitorStateException ex) {
+            // Expected
+        }
+    }
+
+    // Test locking on object that is known to be an inline type only after CCP
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE})
+    public void test132() {
+        MyValue2 vt = MyValue2.createWithFieldsInline(rI, rD);
+        Object obj = new NonValueClass(42);
+
+        int limit = 2;
+        for (; limit < 4; limit *= 2);
+        for (int i = 2; i < limit; i++) {
+            obj = vt;
+        }
+        synchronized (obj) {
+            throw new RuntimeException("test132 failed: synchronization on inline type should not succeed");
+        }
+    }
+
+    @Run(test = "test132")
+    @Warmup(10000)
+    public void test132_verifier() {
+        try {
+            test132();
+            throw new RuntimeException("test132 failed: no exception thrown");
+        } catch (IllegalMonitorStateException ex) {
+            // Expected
+        }
+    }
+
+    // Test conditional locking on inline type and non-escaping object
+    @Test
+    public void test133(boolean b) {
+        Object obj = b ? new NonValueClass(rI) : MyValue2.createWithFieldsInline(rI, rD);
+        synchronized (obj) {
+            if (!b) {
+                throw new RuntimeException("test133 failed: synchronization on inline type should not succeed");
+            }
+        }
+    }
+
+    @Run(test = "test133")
+    public void test133_verifier() {
+        test133(true);
+        try {
+            test133(false);
+            throw new RuntimeException("test133 failed: no exception thrown");
+        } catch (IllegalMonitorStateException ex) {
+            // Expected
+        }
+    }
+
+    // Variant with non-scalarized inline type
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public void test134(boolean b) {
+        Object obj = null;
+        if (b) {
+            obj = MyValue2.createWithFieldsInline(rI, rD);
+        }
+        synchronized (obj) {
+
+        }
+    }
+
+    @Run(test = "test134")
+    public void test134_verifier() {
+        try {
+            test134(true);
+            throw new RuntimeException("test134 failed: no exception thrown");
+        } catch (IllegalMonitorStateException ex) {
+            // Expected
+        }
+    }
+
+    // Test that acmp of the same inline object is removed
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD, STORE, NULL_CHECK_TRAP, TRAP})
+    public boolean test135() {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        return val == val;
+    }
+
+    @Run(test = "test135")
+    public void test135_verifier() {
+        Asserts.assertTrue(test135());
+    }
+
+    // Same as test135 but with null
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD, STORE, NULL_CHECK_TRAP, TRAP})
+    public boolean test136(boolean b) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b) {
+            val = null;
+        }
+        return val == val;
+    }
+
+    @Run(test = "test136")
+    public void test136_verifier() {
+        Asserts.assertTrue(test136(false));
+        Asserts.assertTrue(test136(true));
+    }
+
+    // Test that acmp of different inline objects with same content is removed
+    @Test
+    // TODO 8228361
+    // @IR(failOn = {ALLOC_G, LOAD, STORE, NULL_CHECK_TRAP, TRAP})
+    public boolean test137(int i) {
+        MyValue2 val1 = MyValue2.createWithFieldsInline(i, rD);
+        MyValue2 val2 = MyValue2.createWithFieldsInline(i, rD);
+        return val1 == val2;
+    }
+
+    @Run(test = "test137")
+    public void test137_verifier() {
+        Asserts.assertTrue(test137(rI));
+    }
+
+    // Same as test137 but with null
+    @Test
+    // TODO 8228361
+    // @IR(failOn = {ALLOC_G, LOAD, STORE, NULL_CHECK_TRAP, TRAP})
+    public boolean test138(int i, boolean b) {
+        MyValue2 val1 = MyValue2.createWithFieldsInline(i, rD);
+        MyValue2 val2 = MyValue2.createWithFieldsInline(i, rD);
+        if (b) {
+            val1 = null;
+            val2 = null;
+        }
+        return val1 == val2;
+    }
+
+    @Run(test = "test138")
+    public void test138_verifier() {
+        Asserts.assertTrue(test138(rI, false));
+        Asserts.assertTrue(test138(rI, true));
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test139Value {
+        Object obj = null;
+        @NullRestricted
+        MyValueEmpty empty = new MyValueEmpty();
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test139Wrapper {
+        @NullRestricted
+        Test139Value value = new Test139Value();
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD, STORE, TRAP})
+    public MyValueEmpty test139() {
+        Test139Wrapper w = new Test139Wrapper();
+        return w.value.empty;
+    }
+
+    @Run(test = "test139")
+    public void test139_verifier() {
+        MyValueEmpty empty = test139();
+        Asserts.assertEquals(empty, new MyValueEmpty());
+    }
+
+    // Test calling a method on a loaded but not linked inline type
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    value class Test140Value {
+        int x = 0;
+
+        public int get() {
+            return x;
+        }
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public int test140() {
+        Test140Value vt = new Test140Value();
+        return vt.get();
+    }
+
+    @Run(test = "test140")
+    @Warmup(0)
+    public void test140_verifier() {
+        int result = test140();
+        Asserts.assertEquals(result, 0);
+    }
+
+    // Test calling a method on a linked but not initialized inline type
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    value class Test141Value {
+        int x = 0;
+
+        public int get() {
+            return x;
+        }
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public int test141() {
+        Test141Value vt = new Test141Value();
+        return vt.get();
+    }
+
+    @Run(test = "test141")
+    @Warmup(0)
+    public void test141_verifier() {
+        int result = test141();
+        Asserts.assertEquals(result, 0);
+    }
+
+    // Test that virtual calls on inline type receivers are properly inlined
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD, STORE})
+    public long test142() {
+        MyValue2 nonNull = MyValue2.createWithFieldsInline(rI, rD);
+        MyInterface val = null;
+
+        for (int i = 0; i < 4; i++) {
+            if ((i % 2) == 0) {
+                val = nonNull;
+            }
+        }
+        return val.hash();
+    }
+
+    @Run(test = "test142")
+    public void test142_verifier() {
+        long res = test142();
+        Asserts.assertEquals(res, testValue2.hash());
+    }
+
+    // Test merging of buffered default and non-default inline types
+    @Test
+    // TODO 8325106 With incremental inlining, we already buffer the larval which can't use the default oop because it might be overridden.
+    @IR(applyIf = {"AlwaysIncrementalInline", "false"},
+        failOn = {ALLOC_G})
+    public Object test144(int i) {
+        if (i == 0) {
+            return MyValue1.createDefaultInline();
+        } else if (i == 1) {
+            return testValue1;
+        } else {
+            return MyValue1.createDefaultInline();
+        }
+    }
+
+    @Run(test = "test144")
+    public void test144_verifier() {
+        Asserts.assertEquals(test144(0), MyValue1.createDefaultInline());
+        Asserts.assertEquals(test144(1), testValue1);
+        Asserts.assertEquals(test144(2), MyValue1.createDefaultInline());
+    }
+
+    // Tests writing an array element with a (statically known) incompatible type
+    private static final MethodHandle setArrayElementIncompatibleRef = OldInstructionHelper.loadCode(MethodHandles.lookup(),
+        "setArrayElementIncompatibleRef",
+        MethodType.methodType(void.class, TestLWorld.class, MyValue1[].class, int.class, MyValue2.class),
+        CODE -> {
+            CODE.
+            aload_1().
+            iload_2().
+            aload_3().
+            aastore().
+            return_();
+        });
+
+    // Test inline type connected to result node
+    @Test
+    @IR(failOn = {ALLOC_G})
+    public MyValue1 test146(Object obj) {
+        return (MyValue1)obj;
+    }
+
+    @Run(test = "test146")
+    @Warmup(10000)
+    public void test146_verifier() {
+        Asserts.assertEQ(test146(testValue1), testValue1);
+    }
+
+    @ForceInline
+    public Object test148_helper(Object obj) {
+        return (MyValue1)obj;
+    }
+
+    // Same as test146 but with helper method
+    @Test
+    public Object test148(Object obj) {
+        return test148_helper(obj);
+    }
+
+    @Run(test = "test148")
+    @Warmup(10000)
+    public void test148_verifier() {
+        Asserts.assertEQ(test148(testValue1), testValue1);
+    }
+
+    @ForceInline
+    public Object test149_helper(Object obj) {
+        return (MyValue1)obj;
+    }
+
+    // Same as test147 but with helper method
+    @Test
+    public Object test149(Object obj) {
+        return test149_helper(obj);
+    }
+
+    @Run(test = "test149")
+    @Warmup(10000)
+    public void test149_verifier() {
+        Asserts.assertEQ(test149(testValue1), testValue1);
+        Asserts.assertEQ(test149(null), null);
+    }
+
+    // Test post-parse call devirtualization with inline type receiver
+    @Test
+    // TODO 8325106
+    /*
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
+        failOn = {ALLOC})
+    */
+    @IR(failOn = {compiler.lib.ir_framework.IRNode.DYNAMIC_CALL_OF_METHOD, "MyValue2::hash"},
+        counts = {compiler.lib.ir_framework.IRNode.STATIC_CALL_OF_METHOD, "MyValue2::hash", "= 1"})
+    public long test150() {
+        MyValue2 val = MyValue2.createWithFieldsInline(rI, rD);
+        MyInterface receiver = MyValue1.createWithFieldsInline(rI, rL);
+
+        for (int i = 0; i < 4; i++) {
+            if ((i % 2) == 0) {
+                receiver = val;
+            }
+        }
+        // Trigger post parse call devirtualization (strength-reducing
+        // virtual calls to direct calls).
+        return receiver.hash();
+    }
+
+    @Run(test = "test150")
+    public void test150_verifier() {
+        Asserts.assertEquals(test150(), testValue2.hash());
+    }
+
+// TODO 8325106 This triggers #  assert(false) failed: Should have been buffered
+/*
+    // Same as test150 but with val not being allocated in the scope of the method
+    @Test
+    @IR(failOn = {compiler.lib.ir_framework.IRNode.DYNAMIC_CALL_OF_METHOD, "MyValue2::hash"},
+        counts = {compiler.lib.ir_framework.IRNode.STATIC_CALL_OF_METHOD, "MyValue2::hash", "= 1"})
+    public long test151(MyValue2 val) {
+        MyAbstract receiver = MyValue1.createWithFieldsInline(rI, rL);
+
+        for (int i = 0; i < 100; i++) {
+            if ((i % 2) == 0) {
+                receiver = val;
+            }
+        }
+        // Trigger post parse call devirtualization (strength-reducing
+        // virtual calls to direct calls).
+        return receiver.hash();
+    }
+
+    @Run(test = "test151")
+    @Warmup(0) // Make sure there is no receiver type profile
+    public void test151_verifier() {
+        Asserts.assertEquals(test151(testValue2), testValue2.hash());
+    }
+*/
+
+    static interface MyInterface2 {
+        public int val();
+    }
+
+    static abstract value class MyAbstract2 implements MyInterface2 {
+
+    }
+
+    static class MyClass152 extends MyAbstract2 {
+        private int val;
+
+        @ForceInline
+        public MyClass152(int val) {
+            this.val = val;
+        }
+
+        @Override
+        public int val() {
+            return val;
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue152 extends MyAbstract2 {
+        private int unused = 0; // Make sure sub-offset of val is field non-zero
+        private int val;
+
+        @ForceInline
+        public MyValue152(int val) {
+            this.val = val;
+        }
+
+        @Override
+        public int val() {
+            return val;
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyWrapper152 {
+        private int unused = 0; // Make sure sub-offset of val field is non-zero
+        @NullRestricted
+        MyValue152 val;
+
+        @ForceInline
+        public MyWrapper152(MyInterface2 val) {
+            this.val = (MyValue152)val;
+        }
+    }
+
+    // Test that checkcast with speculative type does not break scalarization in return
+    @Test
+    public MyWrapper152 test152(MyInterface2 val) {
+        return new MyWrapper152(val);
+    }
+
+    @Run(test = "test152")
+    @Warmup(10000) // Make sure profile information is available at cast
+    public void test152_verifier() {
+        MyClass152 unused = new MyClass152(rI);
+        MyValue152 val = new MyValue152(rI);
+        Asserts.assertEquals(test152(val).val, val);
+    }
+
+    @DontInline
+    static void test153_helper(MyWrapper152 arg) {
+
+    }
+
+    // Test that checkcast with speculative type does not prevent scalarization in args
+    @Test
+    public void test153(MyInterface2 val) {
+        test153_helper(new MyWrapper152(val));
+    }
+
+    @Run(test = "test153")
+    @Warmup(10000) // Make sure profile information is available at cast
+    public void test153_verifier() {
+        MyClass152 unused = new MyClass152(rI);
+        MyValue152 val = new MyValue152(rI);
+        test153(val);
+    }
+
+    // Test that checkcast with speculative type enables scalarization
+    @Test
+    @IR(failOn = {ALLOC_G, STORE})
+    public int test154(Method m, MyInterface2 val, boolean b1, boolean b2) {
+        MyInterface2 obj = new MyValue152(rI);
+        if (b1) {
+            // Speculative cast to MyValue152 enables scalarization
+            obj = (MyAbstract2)val;
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+            return obj.val();
+        }
+        return -1;
+    }
+
+    @Run(test = "test154")
+    @Warmup(10000) // Make sure profile information is available at cast
+    public void test154_verifier(RunInfo info) {
+        MyClass152 unused = new MyClass152(rI);
+        MyValue152 val = new MyValue152(rI);
+        Asserts.assertEquals(test154(info.getTest(), val, false, false), -1);
+        Asserts.assertEquals(test154(info.getTest(), val, true, false), -1);
+        if (!info.isWarmUp()) {
+            Asserts.assertEquals(test154(info.getTest(), val, false, true), rI);
+        }
+    }
+
+    // Same as test154 but with null val
+    @Test
+    @IR(failOn = {ALLOC_G, STORE})
+    public int test155(Method m, MyInterface2 val, boolean b1, boolean b2) {
+        MyInterface2 obj = new MyValue152(rI);
+        if (b1) {
+            // Speculative cast to MyValue152 enables scalarization
+            obj = (MyAbstract2)val;
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+            return obj.val();
+        }
+        return -1;
+    }
+
+    @Run(test = "test155")
+    @Warmup(10000) // Make sure profile information is available at cast
+    public void test155_verifier(RunInfo info) {
+        MyClass152 unused = new MyClass152(rI);
+        MyValue152 val = new MyValue152(rI);
+        Asserts.assertEquals(test155(info.getTest(), val, false, false), -1);
+        Asserts.assertEquals(test155(info.getTest(), val, true, false), -1);
+        Asserts.assertEquals(test155(info.getTest(), null, true, false), -1);
+        if (!info.isWarmUp()) {
+            Asserts.assertEquals(test155(info.getTest(), val, false, true), rI);
+        }
+    }
+
+    @NullRestricted
+    final static MyValue1 test157Cache = MyValue1.createWithFieldsInline(rI, 0);
+
+    // Test merging buffered inline type from field load with non-buffered inline type
+    @Test
+    public MyValue1 test157(long val) {
+        return (val == 0L) ? test157Cache : MyValue1.createWithFieldsInline(rI, val);
+    }
+
+    @Run(test = "test157")
+    public void test157_verifier() {
+        Asserts.assertEquals(test157(0), test157Cache);
+        Asserts.assertEquals(test157(rL).hash(), testValue1.hash());
+    }
+
+    @NullRestricted
+    static MyValue1 test158Cache = MyValue1.createWithFieldsInline(rI, 0);
+
+    // Same as test157 but with non-final field load
+    @Test
+    public MyValue1 test158(long val) {
+        return (val == 0L) ? test158Cache : MyValue1.createWithFieldsInline(rI, val);
+    }
+
+    @Run(test = "test158")
+    public void test158_verifier() {
+        Asserts.assertEquals(test158(0), test158Cache);
+        Asserts.assertEquals(test158(rL).hash(), testValue1.hash());
+    }
+
+    // Verify that cast that with incompatible types is properly handled
+    @Test
+    public void test160(NonValueClass arg) {
+        Object tmp = arg;
+        MyValue1 res = (MyValue1)tmp;
+    }
+
+    @Run(test = "test160")
+    @Warmup(10000)
+    public void test160_verifier(RunInfo info) {
+        try {
+            test160(new NonValueClass(42));
+            throw new RuntimeException("No CCE thrown");
+        } catch (ClassCastException e) {
+            // Expected
+        }
+        test160(null);
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorldProfiling.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorldProfiling.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorldProfiling.java
@@ -0,0 +1,1174 @@
+/*
+ * 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.
+ *
+ * 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.*;
+import jdk.test.lib.Asserts;
+import jdk.test.whitebox.WhiteBox;
+
+import java.lang.reflect.Method;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import static compiler.valhalla.inlinetypes.InlineTypeIRNode.*;
+import static compiler.valhalla.inlinetypes.InlineTypes.*;
+
+/*
+ * @test
+ * @key randomness
+ * @summary Test value class specific type profiling.
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm/timeout=300 compiler.valhalla.inlinetypes.TestLWorldProfiling
+ */
+
+@ForceCompileClassInitializer
+public class TestLWorldProfiling {
+
+    public static void main(String[] args) {
+        final Scenario[] scenarios = {
+                new Scenario(0,
+                        "-XX:FlatArrayElementMaxSize=-1",
+                        "-XX:-UseArrayLoadStoreProfile",
+                        "-XX:-UseACmpProfile",
+                        "-XX:TypeProfileLevel=0",
+                        "-XX:-MonomorphicArrayCheck"),
+                new Scenario(1,
+                        "-XX:FlatArrayElementMaxSize=-1",
+                        "-XX:+UseArrayLoadStoreProfile",
+                        "-XX:+UseACmpProfile",
+                        "-XX:TypeProfileLevel=0"),
+                new Scenario(2,
+                        "-XX:FlatArrayElementMaxSize=-1",
+                        "-XX:-UseArrayLoadStoreProfile",
+                        "-XX:-UseACmpProfile",
+                        "-XX:TypeProfileLevel=222",
+                        "-XX:-MonomorphicArrayCheck"),
+                new Scenario(3,
+                        "-XX:FlatArrayElementMaxSize=-1",
+                        "-XX:-UseArrayLoadStoreProfile",
+                        "-XX:-UseACmpProfile",
+                        "-XX:TypeProfileLevel=0",
+                        "-XX:-MonomorphicArrayCheck",
+                        "-XX:TieredStopAtLevel=4",
+                        "-XX:-TieredCompilation"),
+                new Scenario(4,
+                        "-XX:FlatArrayElementMaxSize=-1",
+                        "-XX:+UseArrayLoadStoreProfile",
+                        "-XX:+UseACmpProfile",
+                        "-XX:TypeProfileLevel=0",
+                        "-XX:TieredStopAtLevel=4",
+                        "-XX:-TieredCompilation"),
+                new Scenario(5,
+                        "-XX:FlatArrayElementMaxSize=-1",
+                        "-XX:-UseArrayLoadStoreProfile",
+                        "-XX:-UseACmpProfile",
+                        "-XX:TypeProfileLevel=222",
+                        "-XX:-MonomorphicArrayCheck",
+                        "-XX:TieredStopAtLevel=4",
+                        "-XX:-TieredCompilation")
+        };
+
+        InlineTypes.getFramework()
+                   .addScenarios(scenarios)
+                   .addFlags("-XX:+IgnoreUnrecognizedVMOptions", "--enable-preview",
+                             "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED",
+                             "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED")
+                   .addHelperClasses(MyValue1.class,
+                                     MyValue2.class)
+                   .start();
+    }
+
+    @NullRestricted
+    private static final MyValue1 testValue1 = MyValue1.createWithFieldsInline(rI, rL);
+    @NullRestricted
+    private static final MyValue2 testValue2 = MyValue2.createWithFieldsInline(rI, rD);
+    private static final MyValue1[] testValue1Array = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+    static {
+        testValue1Array[0] = testValue1;
+    }
+    private static final MyValue2[] testValue2Array = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 1);
+    static {
+        testValue2Array[0] = testValue2;
+    }
+    private static final Integer[] testIntegerArray = new Integer[] { 42 };
+    private static final Long[] testLongArray = new Long[] { 42L };
+    private static final Double[] testDoubleArray = new Double[] { 42.0D };
+    private static final MyValue1[] testValue1NotFlatArray = new MyValue1[] { testValue1 };
+    private static final MyValue1[][] testValue1ArrayArray = new MyValue1[][] { testValue1Array };
+
+    // Wrap these variables into helper class because
+    // WhiteBox API needs to be initialized by TestFramework first.
+    static class WBFlags {
+        static final boolean UseACmpProfile = (Boolean) WhiteBox.getWhiteBox().getVMFlag("UseACmpProfile");
+        static final boolean TieredCompilation = (Boolean) WhiteBox.getWhiteBox().getVMFlag("TieredCompilation");
+        static final boolean ProfileInterpreter = (Boolean) WhiteBox.getWhiteBox().getVMFlag("ProfileInterpreter");
+        static final boolean UseArrayLoadStoreProfile = (Boolean) WhiteBox.getWhiteBox().getVMFlag("UseArrayLoadStoreProfile");
+        static final long TypeProfileLevel = (Long) WhiteBox.getWhiteBox().getVMFlag("TypeProfileLevel");
+    }
+
+    static abstract class NonValueAbstract {
+
+    }
+
+    static class NonValueClass1 extends NonValueAbstract {
+        int x;
+
+        public NonValueClass1(int x) {
+            this.x = x;
+        }
+    }
+
+    static class NonValueClass2 extends NonValueAbstract {
+        int x;
+
+        public NonValueClass2(int x) {
+            this.x = x;
+        }
+    }
+
+    static final NonValueClass1 obj = new NonValueClass1(rI);
+    static final NonValueClass2 otherObj = new NonValueClass2(rI);
+
+    // aaload
+
+    @Test
+    @IR(applyIfOr = {"UseArrayLoadStoreProfile", "true", "TypeProfileLevel", "= 222"},
+        failOn = {LOAD_UNKNOWN_INLINE})
+    @IR(applyIfAnd={"UseACmpProfile", "false", "TypeProfileLevel", "!= 222"},
+        counts = {LOAD_UNKNOWN_INLINE, "= 1"})
+    public Object test1(Object[] array) {
+        return array[0];
+    }
+
+    @Run(test = "test1")
+    @Warmup(10000)
+    public void test1_verifier(RunInfo info) {
+        if (info.isWarmUp()) {
+            Object o = test1(testValue1Array);
+            Asserts.assertEQ(((MyValue1)o).hash(), testValue1.hash());
+        } else {
+            Object o = test1(testValue2Array);
+            Asserts.assertEQ(((MyValue2)o).hash(), testValue2.hash());
+        }
+    }
+
+    @Test
+    @IR(applyIfOr = {"UseArrayLoadStoreProfile", "true", "TypeProfileLevel", "= 222"},
+        failOn = {LOAD_UNKNOWN_INLINE})
+    @IR(applyIfAnd = {"UseArrayLoadStoreProfile", "false", "TypeProfileLevel", "!= 222"},
+        counts = {LOAD_UNKNOWN_INLINE, "= 1"})
+    public Object test2(Object[] array) {
+        return array[0];
+    }
+
+    @Run(test = "test2")
+    @Warmup(10000)
+    public void test2_verifier(RunInfo info) {
+        if (info.isWarmUp()) {
+            Object o = test2(testIntegerArray);
+            Asserts.assertEQ(o, 42);
+        } else {
+            Object o = test2(testLongArray);
+            Asserts.assertEQ(o, 42L);
+        }
+    }
+
+    @Test
+    @IR(counts = {LOAD_UNKNOWN_INLINE, "= 1"})
+    public Object test3(Object[] array) {
+        return array[0];
+    }
+
+    @Run(test = "test3")
+    @Warmup(10000)
+    public void test3_verifier() {
+        Object o = test3(testValue1Array);
+        Asserts.assertEQ(((MyValue1)o).hash(), testValue1.hash());
+        o = test3(testValue2Array);
+        Asserts.assertEQ(((MyValue2)o).hash(), testValue2.hash());
+    }
+
+    @Test
+    @IR(applyIf = {"UseArrayLoadStoreProfile", "true"},
+        failOn = {LOAD_UNKNOWN_INLINE})
+    @IR(applyIf = {"UseArrayLoadStoreProfile", "false"},
+        counts = {LOAD_UNKNOWN_INLINE, "= 1"})
+    public Object test4(Object[] array) {
+        return array[0];
+    }
+
+    @Run(test = "test4")
+    @Warmup(10000)
+    public void test4_verifier(RunInfo info) {
+        if (info.isWarmUp()) {
+            Object o = test4(testIntegerArray);
+            Asserts.assertEQ(o, 42);
+            o = test4(testLongArray);
+            Asserts.assertEQ(o, 42L);
+        } else {
+            Object o = test4(testValue2Array);
+            Asserts.assertEQ(((MyValue2)o).hash(), testValue2.hash());
+        }
+    }
+
+    @Test
+    @IR(counts = {LOAD_UNKNOWN_INLINE, "= 1"})
+    public Object test5(Object[] array) {
+        return array[0];
+    }
+
+    @Run(test = "test5")
+    @Warmup(10000)
+    public void test5_verifier() {
+        Object o = test5(testValue1Array);
+        Asserts.assertEQ(((MyValue1)o).hash(), testValue1.hash());
+        o = test5(testValue1NotFlatArray);
+        Asserts.assertEQ(((MyValue1)o).hash(), testValue1.hash());
+    }
+
+    // Check that profile data that's useless at the aaload is
+    // leveraged at a later point
+    @DontInline
+    public void test6_no_inline() {
+    }
+
+    @ForceInline
+    public void test6_helper(NonValueAbstract[] arg) {
+        if (arg instanceof NonValueClass1[]) {
+            test6_no_inline();
+        }
+    }
+
+    @Test
+    // TODO 8325106 double-check rule modifications done by 8325660
+    @IR(applyIfOr = {"UseArrayLoadStoreProfile", "true", "TypeProfileLevel", "= 222"},
+        counts = {CALL, "= 4", CLASS_CHECK_TRAP, "= 1", NULL_CHECK_TRAP, "= 1", RANGE_CHECK_TRAP, "= 1"})
+    @IR(applyIfAnd = {"UseArrayLoadStoreProfile", "false", "TypeProfileLevel", "!= 222"},
+        counts = {CALL, "= 3", RANGE_CHECK_TRAP, "= 1", NULL_CHECK_TRAP, "= 1"})
+    public Object test6(NonValueAbstract[] array) {
+        NonValueAbstract v = array[0];
+        test6_helper(array);
+        return v;
+    }
+
+    @Run(test = "test6")
+    @Warmup(10000)
+    public void test6_verifier(RunInfo info) {
+        if (info.isWarmUp()) {
+            // pollute profile
+            test6_helper(new NonValueClass1[1]);
+            test6_helper(new NonValueClass2[1]);
+        }
+        test6(new NonValueClass1[1]);
+    }
+
+    @DontInline
+    public void test7_no_inline() {
+    }
+
+    @ForceInline
+    public void test7_helper(Object arg) {
+        if (arg instanceof NonValueClass1) {
+            test7_no_inline();
+        }
+    }
+
+    @Test
+    // TODO 8325106 double-check rule modifications done by 8325660
+    @IR(applyIfOr = {"UseArrayLoadStoreProfile", "true", "TypeProfileLevel", "= 222"},
+        counts = {CALL, "= 3", CLASS_CHECK_TRAP, "= 0", NULL_CHECK_TRAP, "= 1", RANGE_CHECK_TRAP, "= 1"})
+    @IR(applyIfAnd = {"UseArrayLoadStoreProfile", "false", "TypeProfileLevel", "!= 222"},
+        counts = {CALL, "= 3", RANGE_CHECK_TRAP, "= 1", NULL_CHECK_TRAP, "= 1"})
+    public Object test7(NonValueAbstract[] array) {
+        NonValueAbstract v = array[0];
+        test7_helper(v);
+        return v;
+    }
+
+    @Run(test = "test7")
+    @Warmup(10000)
+    public void test7_verifier(RunInfo info) {
+        if (info.isWarmUp()) {
+            // pollute profile
+            test7_helper(new NonValueClass1(rI));
+            test7_helper(new NonValueClass2(rI));
+        }
+        test7(new NonValueClass1[1]);
+    }
+
+    @DontInline
+    public void test8_no_inline() {
+    }
+
+    public void test8_helper(Object arg) {
+        if (arg instanceof Long) {
+            test8_no_inline();
+        }
+    }
+
+    @Test
+    @IR(applyIf = {"UseArrayLoadStoreProfile", "true"},
+        counts = {CALL, "= 5", CLASS_CHECK_TRAP, "= 1", NULL_CHECK_TRAP, "= 2",
+                  RANGE_CHECK_TRAP, "= 1"})
+    @IR(applyIf = {"UseArrayLoadStoreProfile", "false"},
+        counts = {CALL, "= 5", RANGE_CHECK_TRAP, "= 1", NULL_CHECK_TRAP, "= 2"})
+    public Object test8(Object[] array) {
+        Object v = array[0];
+        test8_helper(v);
+        return v;
+    }
+
+    @Run(test = "test8")
+    @Warmup(10000)
+    public void test8_verifier(RunInfo info) {
+        if (info.isWarmUp()) {
+            // pollute profile
+            test8_helper(42L);
+            test8_helper(42.0D);
+        }
+        test8(testValue1Array);
+        test8(testValue1NotFlatArray);
+    }
+
+    // aastore
+
+    @Test
+    @IR(applyIfOr = {"UseArrayLoadStoreProfile", "true", "TypeProfileLevel", "= 222"},
+        failOn = {STORE_UNKNOWN_INLINE})
+    @IR(applyIfAnd = {"UseArrayLoadStoreProfile", "false", "TypeProfileLevel", "!= 222"},
+        counts = {STORE_UNKNOWN_INLINE, "= 1"})
+    public void test9(Object[] array, Object v) {
+        array[0] = v;
+    }
+
+    @Run(test = "test9")
+    @Warmup(10000)
+    public void test9_verifier() {
+        test9(testValue1Array, testValue1);
+        Asserts.assertEQ(testValue1Array[0].hash(), testValue1.hash());
+    }
+
+    @Test
+    @IR(applyIfOr = {"UseArrayLoadStoreProfile", "true", "TypeProfileLevel", "= 222"},
+        failOn = {STORE_UNKNOWN_INLINE})
+    @IR(applyIfAnd = {"UseArrayLoadStoreProfile", "false", "TypeProfileLevel", "!= 222"},
+        counts = {STORE_UNKNOWN_INLINE, "= 1"})
+    public void test10(Object[] array, Object v) {
+        array[0] = v;
+    }
+
+    @Run(test = "test10")
+    @Warmup(10000)
+    public void test10_verifier() {
+        test10(testIntegerArray, 42);
+    }
+
+    @Test
+    @IR(counts = {STORE_UNKNOWN_INLINE, "= 1"})
+    public void test11(Object[] array, Object v) {
+        array[0] = v;
+    }
+
+    @Run(test = "test11")
+    @Warmup(10000)
+    public void test11_verifier() {
+        test11(testValue1Array, testValue1);
+        test11(testValue2Array, testValue2);
+    }
+
+    @Test
+    @IR(applyIf = {"UseArrayLoadStoreProfile", "true"},
+        failOn = {STORE_UNKNOWN_INLINE})
+    @IR(applyIf = {"UseArrayLoadStoreProfile", "false"},
+        counts = {STORE_UNKNOWN_INLINE, "= 1"})
+    public void test12(Object[] array, Object v) {
+        array[0] = v;
+    }
+
+    @Run(test = "test12")
+    @Warmup(10000)
+    public void test12_verifier() {
+        test12(testIntegerArray, 42);
+        test12(testLongArray, 42L);
+    }
+
+    @Test
+    @IR(counts = {STORE_UNKNOWN_INLINE, "= 1"})
+    public void test13(Object[] array, Object v) {
+        array[0] = v;
+    }
+
+    @Run(test = "test13")
+    @Warmup(10000)
+    public void test13_verifier() {
+        test13(testValue1Array, testValue1);
+        test13(testValue1NotFlatArray, testValue1);
+    }
+
+    // MonomorphicArrayCheck
+    @Test
+    public void test14(Number[] array, Number v) {
+        array[0] = v;
+    }
+
+    @Run(test = "test14")
+    @Warmup(10000)
+    public void test14_verifier(RunInfo info) {
+        if (info.isWarmUp()) {
+            test14(testIntegerArray, 42);
+        } else {
+            Method m = info.getTest();
+            boolean deopt = false;
+            for (int i = 0; i < 100; i++) {
+                test14(testIntegerArray, 42);
+                if (!info.isCompilationSkipped() && !TestFramework.isCompiled(m)) {
+                    deopt = true;
+                }
+            }
+            if (deopt && TestFramework.isStableDeopt(m, CompLevel.C2) && !WBFlags.TieredCompilation && WBFlags.ProfileInterpreter &&
+                (WBFlags.UseArrayLoadStoreProfile || WBFlags.TypeProfileLevel == 222)) {
+                throw new RuntimeException("Monomorphic array check should rely on profiling and be accurate");
+            }
+        }
+    }
+
+    // null free array profiling
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class NotFlattenable {
+        private Object o1 = null;
+        private Object o2 = null;
+        private Object o3 = null;
+        private Object o4 = null;
+        private Object o5 = null;
+        private Object o6 = null;
+    }
+
+    @NullRestricted
+    private static final NotFlattenable notFlattenable = new NotFlattenable();
+    private static final NotFlattenable[] testNotFlattenableArray = (NotFlattenable[])ValueClass.newNullRestrictedArray(NotFlattenable.class, 1);
+
+    @Test
+// TODO 8325106
+//    @IR(applyIfOr = {"UseArrayLoadStoreProfile", "true", "TypeProfileLevel", "= 222"},
+    @IR(applyIf = {"UseArrayLoadStoreProfile", "true"},
+        counts = {NULL_CHECK_TRAP, "= 2"},
+        failOn = {STORE_UNKNOWN_INLINE})
+    @IR(applyIfAnd = {"UseArrayLoadStoreProfile", "false", "TypeProfileLevel", "!= 222"},
+        counts = {NULL_CHECK_TRAP, "= 3", STORE_UNKNOWN_INLINE, "= 1"})
+    public void test15(Object[] array, Object v) {
+        array[0] = v;
+    }
+
+    @Run(test = "test15")
+    @Warmup(10000)
+    public void test15_verifier() {
+        test15(testNotFlattenableArray, notFlattenable);
+        try {
+            test15(testNotFlattenableArray, null);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException npe) {
+            // Expected
+        }
+    }
+
+    @Test
+    @IR(applyIf = {"UseArrayLoadStoreProfile", "true"},
+        counts = {NULL_CHECK_TRAP, "= 2"},
+        failOn = {STORE_UNKNOWN_INLINE})
+    @IR(applyIf = {"UseArrayLoadStoreProfile", "false"},
+        counts = {NULL_CHECK_TRAP, "= 3", STORE_UNKNOWN_INLINE, "= 1"})
+    public void test16(Object[] array, Object v) {
+        array[0] = v;
+    }
+
+    @Run(test = "test16")
+    @Warmup(10000)
+    public void test16_verifier() {
+        test16(testNotFlattenableArray, notFlattenable);
+        try {
+            test16(testNotFlattenableArray, null);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException npe) {
+            // Expected
+        }
+        test16(testIntegerArray, 42);
+    }
+
+    @Test
+    @IR(applyIf = {"UseArrayLoadStoreProfile", "true"},
+        counts = {NULL_CHECK_TRAP, "= 1"},
+        failOn = {STORE_UNKNOWN_INLINE})
+    @IR(applyIf = {"UseArrayLoadStoreProfile", "false"},
+        counts = {NULL_CHECK_TRAP, "= 3", STORE_UNKNOWN_INLINE, "= 1"})
+    public void test17(Object[] array, Object v) {
+        array[0] = v;
+    }
+
+    @Run(test = "test17")
+    @Warmup(10000)
+    public void test17_verifier() {
+        test17(testIntegerArray, 42);
+        test17(testIntegerArray, null);
+        testIntegerArray[0] = 42;
+        test17(testLongArray, 42L);
+    }
+
+    public void test18_helper(Object[] array, Object v) {
+        array[0] = v;
+    }
+
+    @Test
+    @IR(applyIf = {"UseArrayLoadStoreProfile", "true"},
+        counts = {NULL_CHECK_TRAP, "= 1"},
+        failOn = {STORE_UNKNOWN_INLINE})
+    @IR(applyIf = {"UseArrayLoadStoreProfile", "false"},
+        counts = {NULL_CHECK_TRAP, "= 3", STORE_UNKNOWN_INLINE, "= 1"})
+    public Object test18(Object[] array, Object v1) {
+        Object v2 = array[0];
+        test18_helper(array, v1);
+        return v2;
+    }
+
+    @Run(test = "test18")
+    @Warmup(10000)
+    public void test18_verifier() {
+        test18_helper(testValue1Array, testValue1); // pollute profile
+        test18(testIntegerArray, 42);
+        test18(testIntegerArray, null);
+        testIntegerArray[0] = 42;
+        test18(testLongArray, 42L);
+    }
+
+    // maybe null free, not flat
+
+    @Test
+    @IR(applyIf = {"UseArrayLoadStoreProfile", "true"},
+        failOn = {LOAD_UNKNOWN_INLINE})
+    @IR(applyIf = {"UseArrayLoadStoreProfile", "false"},
+        counts = {LOAD_UNKNOWN_INLINE, "= 1"})
+    public Object test19(Object[] array) {
+        return array[0];
+    }
+
+    @Run(test = "test19")
+    @Warmup(10000)
+    public void test19_verifier() {
+        Object o = test19(testIntegerArray);
+        Asserts.assertEQ(o, 42);
+        o = test19(testNotFlattenableArray);
+        Asserts.assertEQ(o, notFlattenable);
+    }
+
+    @Test
+    @IR(applyIf = {"UseArrayLoadStoreProfile", "true"},
+        failOn = {STORE_UNKNOWN_INLINE})
+    @IR(applyIf = {"UseArrayLoadStoreProfile", "false"},
+        counts = {STORE_UNKNOWN_INLINE, "= 1"})
+    public void test20(Object[] array, Object o) {
+        array[0] = o;
+    }
+
+    @Run(test = "test20")
+    @Warmup(10000)
+    public void test20_verifier() {
+        test20(testIntegerArray, 42);
+        test20(testNotFlattenableArray, notFlattenable);
+    }
+
+    // acmp tests
+
+    // branch frequency profiling causes not equal branch to be optimized out
+    @Test
+    @IR(counts = {IRNode.UNSTABLE_IF_TRAP, " = 1"})
+    public boolean test21(Object o1, Object o2) {
+        return o1 == o2;
+    }
+
+    @Run(test = "test21")
+    @Warmup(10000)
+    public void test21_verifier() {
+        test21(obj, obj);
+        test21(testValue1, testValue1);
+    }
+
+    // Input profiled non null
+    @Test
+    @IR(applyIf = {"UseACmpProfile", "true"},
+        failOn = {SUBSTITUTABILITY_TEST},
+        counts = {NULL_ASSERT_TRAP, "= 1"})
+    @IR(applyIf = {"UseACmpProfile", "false"},
+        counts = {SUBSTITUTABILITY_TEST, "= 1"})
+    public boolean test22(Object o1, Object o2) {
+        return o1 == o2;
+    }
+
+    @Run(test = "test22")
+    @Warmup(10000)
+    public void test22_verifier(RunInfo info) {
+        test22(obj, null);
+        test22(otherObj, null);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertCompiledByC2(m);
+            test22(obj, otherObj);
+            if (WBFlags.UseACmpProfile) {
+                TestFramework.assertDeoptimizedByC2(m);
+            }
+        }
+    }
+
+    @Test
+    @IR(applyIfOr = {"UseACmpProfile", "true", "TypeProfileLevel", "= 222"},
+        failOn = {SUBSTITUTABILITY_TEST},
+        counts = {NULL_ASSERT_TRAP, "= 1"})
+    @IR(applyIfAnd = {"UseACmpProfile", "false", "TypeProfileLevel", "!= 222"},
+        counts = {SUBSTITUTABILITY_TEST, "= 1"})
+    public boolean test23(Object o1, Object o2) {
+        return o1 == o2;
+    }
+
+    @Run(test = "test23")
+    @Warmup(10000)
+    public void test23_verifier(RunInfo info) {
+        test23(null, obj);
+        test23(null, otherObj);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertCompiledByC2(m);
+            test23(obj, otherObj);
+            if (WBFlags.UseACmpProfile || WBFlags.TypeProfileLevel != 0) {
+                TestFramework.assertDeoptimizedByC2(m);
+            }
+        }
+    }
+
+    @Test
+    @IR(applyIf = {"UseACmpProfile", "true"},
+        failOn = {SUBSTITUTABILITY_TEST},
+        counts = {NULL_ASSERT_TRAP, "= 1"})
+    @IR(applyIf = {"UseACmpProfile", "false"},
+        counts = {SUBSTITUTABILITY_TEST, "= 1"})
+    public boolean test24(Object o1, Object o2) {
+        return o1 != o2;
+    }
+
+    @Run(test = "test24")
+    @Warmup(10000)
+    public void test24_verifier(RunInfo info) {
+        test24(obj, null);
+        test24(otherObj, null);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertCompiledByC2(m);
+            test24(obj, otherObj);
+             if (WBFlags.UseACmpProfile) {
+                 TestFramework.assertDeoptimizedByC2(m);
+            }
+        }
+    }
+
+    @Test
+    @IR(applyIfOr = {"UseACmpProfile", "true", "TypeProfileLevel", "= 222"},
+        failOn = {SUBSTITUTABILITY_TEST},
+        counts = {NULL_ASSERT_TRAP, "= 1"})
+    @IR(applyIfAnd = {"UseACmpProfile", "false", "TypeProfileLevel", "!= 222"},
+        counts = {SUBSTITUTABILITY_TEST, "= 1"})
+    public boolean test25(Object o1, Object o2) {
+        return o1 != o2;
+    }
+
+    @Run(test = "test25")
+    @Warmup(10000)
+    public void test25_verifier(RunInfo info) {
+        test25(null, obj);
+        test25(null, otherObj);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertCompiledByC2(m);
+            test25(obj, otherObj);
+            if (WBFlags.UseACmpProfile || WBFlags.TypeProfileLevel != 0) {
+                TestFramework.assertDeoptimizedByC2(m);
+            }
+        }
+    }
+
+    // Input profiled not value class with known type
+    @Test
+    @IR(applyIfOr = {"UseACmpProfile", "true", "TypeProfileLevel", "= 222"},
+        failOn = {SUBSTITUTABILITY_TEST},
+        counts = {NULL_CHECK_TRAP, "= 1", CLASS_CHECK_TRAP, "= 1"})
+    @IR(applyIfAnd = {"UseACmpProfile", "false", "TypeProfileLevel", "!= 222"},
+        counts = {SUBSTITUTABILITY_TEST, "= 1"})
+    public boolean test26(Object o1, Object o2) {
+        return o1 == o2;
+    }
+
+    @Run(test = "test26")
+    @Warmup(10000)
+    public void test26_verifier(RunInfo info) {
+        test26(obj, obj);
+        test26(obj, otherObj);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertCompiledByC2(m);
+            for (int i = 0; i < 10; i++) {
+                test26(otherObj, obj);
+            }
+            if (WBFlags.UseACmpProfile || WBFlags.TypeProfileLevel != 0) {
+                TestFramework.assertDeoptimizedByC2(m);
+            }
+        }
+    }
+
+    @Test
+    @IR(applyIf = {"UseACmpProfile", "true"},
+        failOn = {SUBSTITUTABILITY_TEST},
+        counts = { NULL_CHECK_TRAP, "= 1", CLASS_CHECK_TRAP, "= 1"})
+    @IR(applyIf = {"UseACmpProfile", "false"},
+        counts = {SUBSTITUTABILITY_TEST, "= 1"})
+    public boolean test27(Object o1, Object o2) {
+        return o1 == o2;
+    }
+
+    @Run(test = "test27")
+    @Warmup(10000)
+    public void test27_verifier(RunInfo info) {
+        test27(obj, obj);
+        test27(otherObj, obj);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertCompiledByC2(m);
+            for (int i = 0; i < 10; i++) {
+                test27(obj, otherObj);
+            }
+            if (WBFlags.UseACmpProfile) {
+                TestFramework.assertDeoptimizedByC2(m);
+            }
+        }
+    }
+
+    @Test
+    @IR(applyIfOr = {"UseACmpProfile", "true", "TypeProfileLevel", "= 222"},
+        failOn = {SUBSTITUTABILITY_TEST},
+        counts = {NULL_CHECK_TRAP, "= 1", CLASS_CHECK_TRAP, "= 1"})
+    @IR(applyIfAnd = {"UseACmpProfile", "false", "TypeProfileLevel", "!= 222"},
+        counts = {SUBSTITUTABILITY_TEST, "= 1"})
+    public boolean test28(Object o1, Object o2) {
+        return o1 != o2;
+    }
+
+    @Run(test = "test28")
+    @Warmup(10000)
+    public void test28_verifier(RunInfo info) {
+        test28(obj, obj);
+        test28(obj, otherObj);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertCompiledByC2(m);
+            for (int i = 0; i < 10; i++) {
+                test28(otherObj, obj);
+            }
+            if (WBFlags.UseACmpProfile || WBFlags.TypeProfileLevel != 0) {
+                TestFramework.assertDeoptimizedByC2(m);
+            }
+        }
+    }
+
+    @Test
+    @IR(applyIf = {"UseACmpProfile", "true"},
+        failOn = {SUBSTITUTABILITY_TEST},
+        counts = {NULL_CHECK_TRAP, "= 1", CLASS_CHECK_TRAP, "= 1"})
+    @IR(applyIf = {"UseACmpProfile", "false"},
+        counts = {SUBSTITUTABILITY_TEST, "= 1"})
+    public boolean test29(Object o1, Object o2) {
+        return o1 != o2;
+    }
+
+    @Run(test = "test29")
+    @Warmup(10000)
+    public void test29_verifier(RunInfo info) {
+        test29(obj, obj);
+        test29(otherObj, obj);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertCompiledByC2(m);
+            for (int i = 0; i < 10; i++) {
+                test29(obj, otherObj);
+            }
+            if (WBFlags.UseACmpProfile) {
+                TestFramework.assertDeoptimizedByC2(m);
+            }
+        }
+    }
+
+    @Test
+    @IR(applyIfOr = {"UseACmpProfile", "true", "TypeProfileLevel", "= 222"},
+        failOn = {SUBSTITUTABILITY_TEST, NULL_CHECK_TRAP},
+        counts = {CLASS_CHECK_TRAP, "= 1"})
+    @IR(applyIfAnd = {"UseACmpProfile", "false", "TypeProfileLevel", "!= 222"},
+        counts = {SUBSTITUTABILITY_TEST, "= 1"})
+    public boolean test30(Object o1, Object o2) {
+        return o1 == o2;
+    }
+
+    @Run(test = "test30")
+    @Warmup(10000)
+    public void test30_verifier(RunInfo info) {
+        test30(obj, obj);
+        test30(obj, otherObj);
+        test30(null, 42);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertCompiledByC2(m);
+            for (int i = 0; i < 10; i++) {
+                test30(otherObj, obj);
+            }
+            if (WBFlags.UseACmpProfile || WBFlags.TypeProfileLevel != 0) {
+                TestFramework.assertDeoptimizedByC2(m);
+            }
+        }
+    }
+
+    @Test
+    @IR(applyIf = {"UseACmpProfile", "true"},
+        failOn = {SUBSTITUTABILITY_TEST, NULL_CHECK_TRAP})
+    @IR(applyIf = {"UseACmpProfile", "false"},
+        counts = {SUBSTITUTABILITY_TEST, "= 1"})
+    public boolean test31(Object o1, Object o2) {
+        return o1 == o2;
+    }
+
+    @Run(test = "test31")
+    @Warmup(10000)
+    public void test31_verifier(RunInfo info) {
+        test31(obj, obj);
+        test31(otherObj, obj);
+        test31(obj, null);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertCompiledByC2(m);
+            for (int i = 0; i < 10; i++) {
+                test31(obj, otherObj);
+            }
+            if (WBFlags.UseACmpProfile) {
+                TestFramework.assertDeoptimizedByC2(m);
+            }
+        }
+    }
+
+    // Input profiled not value class with unknown type
+    @Test
+    @IR(applyIf = {"UseACmpProfile", "true"},
+        failOn = {SUBSTITUTABILITY_TEST},
+        counts = {NULL_CHECK_TRAP, "= 1", CLASS_CHECK_TRAP, "= 1"})
+    @IR(applyIf = {"UseACmpProfile", "false"},
+        counts = {SUBSTITUTABILITY_TEST, "= 1"})
+    public boolean test32(Object o1, Object o2) {
+        return o1 == o2;
+    }
+
+    @Run(test = "test32")
+    @Warmup(10000)
+    public void test32_verifier(RunInfo info) {
+        test32(obj, obj);
+        test32(obj, testValue1);
+        test32(otherObj, obj);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertCompiledByC2(m);
+            for (int i = 0; i < 10; i++) {
+                test32(testValue1, 42);
+            }
+            if (WBFlags.UseACmpProfile) {
+                TestFramework.assertDeoptimizedByC2(m);
+            }
+        }
+    }
+
+    @Test
+    @IR(applyIf = {"UseACmpProfile", "true"},
+        failOn = {SUBSTITUTABILITY_TEST},
+        counts = {NULL_CHECK_TRAP, "= 1", CLASS_CHECK_TRAP, "= 1"})
+    @IR(applyIf = {"UseACmpProfile", "false"},
+        counts = {SUBSTITUTABILITY_TEST, "= 1"})
+    public boolean test33(Object o1, Object o2) {
+        return o1 == o2;
+    }
+
+    @Run(test = "test33")
+    @Warmup(10000)
+    public void test33_verifier(RunInfo info) {
+        test33(obj, obj);
+        test33(testValue1, obj);
+        test33(obj, otherObj);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertCompiledByC2(m);
+            for (int i = 0; i < 10; i++) {
+                test33(obj, testValue1);
+            }
+            if (WBFlags.UseACmpProfile) {
+                TestFramework.assertDeoptimizedByC2(m);
+            }
+        }
+    }
+
+    @Test
+    @IR(applyIf = {"UseACmpProfile", "true"},
+        failOn = {SUBSTITUTABILITY_TEST},
+        counts = {NULL_CHECK_TRAP, "= 1", CLASS_CHECK_TRAP, "= 1"})
+    @IR(applyIf = {"UseACmpProfile", "false"},
+        counts = {SUBSTITUTABILITY_TEST, "= 1"})
+    public boolean test34(Object o1, Object o2) {
+        return o1 != o2;
+    }
+
+    @Run(test = "test34")
+    @Warmup(10000)
+    public void test34_verifier(RunInfo info) {
+        test34(obj, obj);
+        test34(obj, testValue1);
+        test34(otherObj, obj);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertCompiledByC2(m);
+            for (int i = 0; i < 10; i++) {
+                test34(testValue1, 42);
+            }
+            if (WBFlags.UseACmpProfile) {
+                TestFramework.assertDeoptimizedByC2(m);
+            }
+        }
+    }
+
+    @Test
+    @IR(applyIf = {"UseACmpProfile", "true"},
+        failOn = {SUBSTITUTABILITY_TEST},
+        counts = {NULL_CHECK_TRAP, "= 1", CLASS_CHECK_TRAP, "= 1"})
+    @IR(applyIf = {"UseACmpProfile", "false"},
+        counts = {SUBSTITUTABILITY_TEST, "= 1"})
+    public boolean test35(Object o1, Object o2) {
+        return o1 != o2;
+    }
+
+    @Run(test = "test35")
+    @Warmup(10000)
+    public void test35_verifier(RunInfo info) {
+        test35(obj, obj);
+        test35(testValue1, obj);
+        test35(obj, otherObj);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertCompiledByC2(m);
+            for (int i = 0; i < 10; i++) {
+                test35(obj, testValue1);
+            }
+            if (WBFlags.UseACmpProfile) {
+                TestFramework.assertDeoptimizedByC2(m);
+            }
+        }
+    }
+
+    @Test
+    @IR(applyIf = {"UseACmpProfile", "true"},
+        failOn = {SUBSTITUTABILITY_TEST, NULL_CHECK_TRAP},
+        counts = {CLASS_CHECK_TRAP, "= 1"})
+    @IR(applyIf = {"UseACmpProfile", "false"},
+        counts = {SUBSTITUTABILITY_TEST, "= 1"})
+    public boolean test36(Object o1, Object o2) {
+        return o1 == o2;
+    }
+
+    @Run(test = "test36")
+    @Warmup(10000)
+    public void test36_verifier(RunInfo info) {
+        test36(obj, otherObj);
+        test36(otherObj, testValue1);
+        test36(null, obj);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertCompiledByC2(m);
+            for (int i = 0; i < 10; i++) {
+                test36(testValue1, obj);
+            }
+            if (WBFlags.UseACmpProfile) {
+                TestFramework.assertDeoptimizedByC2(m);
+            }
+        }
+    }
+
+    @Test
+    @IR(applyIf = {"UseACmpProfile", "true"},
+        failOn = {SUBSTITUTABILITY_TEST, NULL_CHECK_TRAP})
+    @IR(applyIf = {"UseACmpProfile", "false"},
+        counts = {SUBSTITUTABILITY_TEST, "= 1"})
+    public boolean test37(Object o1, Object o2) {
+        return o1 == o2;
+    }
+
+    @Run(test = "test37")
+    @Warmup(10000)
+    public void test37_verifier(RunInfo info) {
+        test37(otherObj, obj);
+        test37(testValue1, otherObj);
+        test37(obj, null);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertCompiledByC2(m);
+            for (int i = 0; i < 10; i++) {
+                test37(obj, testValue1);
+            }
+            if (WBFlags.UseACmpProfile) {
+                TestFramework.assertDeoptimizedByC2(m);
+            }
+        }
+    }
+
+    // Test that acmp profile data that's unused at the acmp is fed to
+    // speculation and leverage later
+    @Test
+    @IR(applyIfOr = {"UseACmpProfile", "true", "TypeProfileLevel", "= 222"},
+        failOn = {SUBSTITUTABILITY_TEST},
+        counts = {CLASS_CHECK_TRAP, "= 2"})
+    @IR(applyIfAnd = {"UseACmpProfile", "false", "TypeProfileLevel", "!= 222"},
+        counts = {SUBSTITUTABILITY_TEST, "= 2"})
+    public void test38(Object o1, Object o2, Object o3) {
+        if (o1 == o2) {
+            test38_helper2();
+        }
+        test38_helper(o1, o3);
+    }
+
+    public void test38_helper(Object o1, Object o2) {
+        if (o1 == o2) {
+        }
+    }
+
+    public void test38_helper2() {
+    }
+
+    @Run(test = "test38")
+    @Warmup(10000)
+    public void test38_verifier() {
+        test38(obj, obj, obj);
+        test38_helper(testValue1, testValue2);
+    }
+
+    @Test
+    @IR(applyIfOr = {"UseACmpProfile", "true", "TypeProfileLevel", "= 222"},
+        failOn = {SUBSTITUTABILITY_TEST},
+        counts = {CLASS_CHECK_TRAP, "= 2"})
+    @IR(applyIfAnd = {"UseACmpProfile", "false", "TypeProfileLevel", "!= 222"},
+        counts = {SUBSTITUTABILITY_TEST, "= 2"})
+    public void test39(Object o1, Object o2, Object o3) {
+        if (o1 == o2) {
+            test39_helper2();
+        }
+        test39_helper(o2, o3);
+    }
+
+    public void test39_helper(Object o1, Object o2) {
+        if (o1 == o2) {
+        }
+    }
+
+    public void test39_helper2() {
+    }
+
+    @Run(test = "test39")
+    @Warmup(10000)
+    public void test39_verifier() {
+        test39(obj, obj, obj);
+        test39_helper(testValue1, testValue2);
+    }
+
+    // Test array access with polluted array type profile
+    static abstract value class Test40Abstract { }
+    static value class Test40Class extends Test40Abstract { }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test40Inline extends Test40Abstract { }
+
+    @ForceInline
+    public Object test40_access(Object[] array) {
+        return array[0];
+    }
+
+    @Test
+    public Object test40(Test40Abstract[] array) {
+        return test40_access(array);
+    }
+
+    @Run(test = "test40")
+    @Warmup(10000)
+    public void test40_verifier(RunInfo info) {
+        // Make sure multiple implementors of Test40Abstract are loaded
+        Test40Inline tmp1 = new Test40Inline();
+        Test40Class tmp2 = new Test40Class();
+        if (info.isWarmUp()) {
+            // Pollute profile with Object[] (exact)
+            test40_access(new Object[1]);
+        } else {
+            // When inlining test40_access, profiling contradicts actual type of array
+            test40(new Test40Class[1]);
+        }
+    }
+
+    // Same as test40 but with array store
+    @ForceInline
+    public void test41_access(Object[] array, Object val) {
+        array[0] = val;
+    }
+
+    @Test
+    public void test41(Test40Inline[] array, Object val) {
+        test41_access(array, val);
+    }
+
+    @Run(test = "test41")
+    @Warmup(10000)
+    public void test41_verifier(RunInfo info) {
+        // Make sure multiple implementors of Test40Abstract are loaded
+        Test40Inline tmp1 = new Test40Inline();
+        Test40Class tmp2 = new Test40Class();
+        if (info.isWarmUp()) {
+            // Pollute profile with exact Object[]
+            test41_access(new Object[1], new Object());
+        } else {
+            // When inlining test41_access, profiling contradicts actual type of array
+            Test40Inline[] array = (Test40Inline[])ValueClass.newNullRestrictedArray(Test40Inline.class, 1);
+            test41(array, new Test40Inline());
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLarvalState.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLarvalState.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLarvalState.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2023, Arm Limited. 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.
+ *
+ * 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 8303416
+ * @summary Fix JVM crash at Unsafe_FinishPrivateBuffer
+ * @library /test/lib
+ * @enablePreview
+ * @modules java.base/jdk.internal.misc
+ * @run main/othervm compiler.valhalla.inlinetypes.TestLarvalState
+ */
+
+package compiler.valhalla.inlinetypes;
+
+import java.lang.reflect.*;
+import java.util.Random;
+
+import jdk.internal.misc.Unsafe;
+import jdk.test.lib.Asserts;
+import jdk.test.lib.Utils;
+
+public class TestLarvalState {
+    private static int LENGTH = 10000;
+
+    private static final Random RD = Utils.getRandomInstance();
+
+    static byte[] arr = new byte[LENGTH];
+
+    static {
+        for (int i = 0; i < LENGTH; i++) {
+            arr[i] = (byte) RD.nextInt(127);
+        }
+    }
+
+    public static byte test(byte b) {
+        Value obj = new Value();
+        obj = Unsafe.getUnsafe().makePrivateBuffer(obj);
+        Unsafe.getUnsafe().putByte(obj, obj.offset, b);
+        obj = Unsafe.getUnsafe().finishPrivateBuffer(obj);
+        return Unsafe.getUnsafe().getByte(obj, obj.offset);
+    }
+
+    public static void main(String[] args) {
+        byte actual = 0;
+        for (int i = 0; i < LENGTH; i++) {
+            actual += test(arr[i]);
+        }
+
+        byte expected = 0;
+        for (int i = 0; i < LENGTH; i++) {
+            expected += arr[i];
+        }
+        Asserts.assertEquals(expected, actual);
+    }
+
+    static value class Value {
+        byte field = 0;
+
+        static long offset = fieldOffset();
+
+        private static long fieldOffset() {
+            try {
+                var f = Value.class.getDeclaredField("field");
+                return Unsafe.getUnsafe().objectFieldOffset(f);
+            } catch (Exception e) {
+                System.out.println(e);
+            }
+            return -1L;
+        }
+    }
+}
+
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLoopUnswitchingWithFlatArrayCheck.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLoopUnswitchingWithFlatArrayCheck.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLoopUnswitchingWithFlatArrayCheck.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ *
+ * 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 8275276
+ * @summary Test loop unswitching with flat array checks.
+ * @enablePreview
+ * @run main/othervm -Xcomp -XX:CompileCommand=compileonly,TestLoopUnswitchingWithFlatArrayCheck::test TestLoopUnswitchingWithFlatArrayCheck
+ */
+
+public class TestLoopUnswitchingWithFlatArrayCheck {
+
+    Object[] objectArray = new Object[1000];
+
+    boolean test(Object o) {
+        return false;
+    }
+
+    private Object test(int start, int end) {
+        Object res = null;
+        Object[] array = objectArray;
+        if (array == null) {
+           return null;
+        }
+        for (int i = start; ; i++) {
+            if (test(array[i])) {
+                continue;
+            }
+            if (i == end) {
+                break;
+            }
+        }
+        for (int i = 0; i < 100; i++) {
+            for (; i < 100; i++) {
+                res = array[i];
+            }
+        }
+        return res;
+    }
+
+    public static void main(String[] args) {
+        TestLoopUnswitchingWithFlatArrayCheck t = new TestLoopUnswitchingWithFlatArrayCheck();
+        t.test(0, 10);
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMethodHandles.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMethodHandles.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMethodHandles.java
@@ -0,0 +1,534 @@
+/*
+ * Copyright (c) 2017, 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.*;
+import jdk.test.lib.Asserts;
+import jdk.test.whitebox.WhiteBox;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import static compiler.valhalla.inlinetypes.InlineTypeIRNode.*;
+import static compiler.valhalla.inlinetypes.InlineTypes.*;
+
+/*
+ * @test
+ * @key randomness
+ * @summary Test method handle support for inline types
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm/timeout=300 compiler.valhalla.inlinetypes.TestMethodHandles
+ */
+
+@ForceCompileClassInitializer
+public class TestMethodHandles {
+
+    static {
+        try {
+            Class<?> clazz = TestMethodHandles.class;
+            MethodHandles.Lookup lookup = MethodHandles.lookup();
+
+            MethodType mt = MethodType.methodType(MyValue3.class);
+            test1_mh = lookup.findVirtual(clazz, "test1_target", mt);
+            test2_mh = lookup.findVirtual(clazz, "test2_target", mt);
+            test3_mh = lookup.findVirtual(clazz, "test3_target", mt);
+
+            MethodType test4_mt1 = MethodType.methodType(int.class, MyValue1.class);
+            MethodType test4_mt2 = MethodType.methodType(MyValue1.class);
+            MethodHandle test4_mh1 = lookup.findStatic(clazz, "test4_helper1", test4_mt1);
+            MethodHandle test4_mh2 = lookup.findStatic(clazz, "test4_helper2", test4_mt2);
+            test4_mh = MethodHandles.filterReturnValue(test4_mh2, test4_mh1);
+
+            MethodType test5_mt = MethodType.methodType(int.class, MyValue1.class);
+            test5_mh = lookup.findVirtual(clazz, "test5_target", test5_mt);
+
+            MethodType test6_mt = MethodType.methodType(MyValue3.class);
+            MethodHandle test6_mh1 = lookup.findVirtual(clazz, "test6_target1", test6_mt);
+            MethodHandle test6_mh2 = lookup.findVirtual(clazz, "test6_target2", test6_mt);
+            MethodType boolean_mt = MethodType.methodType(boolean.class);
+            MethodHandle test6_mh_test = lookup.findVirtual(clazz, "test6_test", boolean_mt);
+            test6_mh = MethodHandles.guardWithTest(test6_mh_test, test6_mh1, test6_mh2);
+
+            MethodType myvalue2_mt = MethodType.methodType(MyValue2.class);
+            test7_mh1 = lookup.findStatic(clazz, "test7_target1", myvalue2_mt);
+            MethodHandle test7_mh2 = lookup.findStatic(clazz, "test7_target2", myvalue2_mt);
+            MethodHandle test7_mh_test = lookup.findStatic(clazz, "test7_test", boolean_mt);
+            test7_mh = MethodHandles.guardWithTest(test7_mh_test,
+                                                    MethodHandles.invoker(myvalue2_mt),
+                                                    MethodHandles.dropArguments(test7_mh2, 0, MethodHandle.class));
+
+            MethodHandle test8_mh1 = lookup.findStatic(clazz, "test8_target1", myvalue2_mt);
+            test8_mh2 = lookup.findStatic(clazz, "test8_target2", myvalue2_mt);
+            MethodHandle test8_mh_test = lookup.findStatic(clazz, "test8_test", boolean_mt);
+            test8_mh = MethodHandles.guardWithTest(test8_mh_test,
+                                                    MethodHandles.dropArguments(test8_mh1, 0, MethodHandle.class),
+                                                    MethodHandles.invoker(myvalue2_mt));
+
+            MethodType test9_mt = MethodType.methodType(MyValue3.class);
+            MethodHandle test9_mh1 = lookup.findVirtual(clazz, "test9_target1", test9_mt);
+            MethodHandle test9_mh2 = lookup.findVirtual(clazz, "test9_target2", test9_mt);
+            MethodHandle test9_mh3 = lookup.findVirtual(clazz, "test9_target3", test9_mt);
+            MethodType test9_mt2 = MethodType.methodType(boolean.class);
+            MethodHandle test9_mh_test1 = lookup.findVirtual(clazz, "test9_test1", test9_mt2);
+            MethodHandle test9_mh_test2 = lookup.findVirtual(clazz, "test9_test2", test9_mt2);
+            test9_mh = MethodHandles.guardWithTest(test9_mh_test1,
+                                                    test9_mh1,
+                                                    MethodHandles.guardWithTest(test9_mh_test2, test9_mh2, test9_mh3));
+
+            MethodType test10_mt = MethodType.methodType(MyValue2.class);
+            MethodHandle test10_mh1 = lookup.findStatic(clazz, "test10_target1", test10_mt);
+            test10_mh2 = lookup.findStatic(clazz, "test10_target2", test10_mt);
+            test10_mh3 = lookup.findStatic(clazz, "test10_target3", test10_mt);
+            MethodType test10_mt2 = MethodType.methodType(boolean.class);
+            MethodType test10_mt3 = MethodType.methodType(MyValue2.class);
+            MethodHandle test10_mh_test1 = lookup.findStatic(clazz, "test10_test1", test10_mt2);
+            MethodHandle test10_mh_test2 = lookup.findStatic(clazz, "test10_test2", test10_mt2);
+            test10_mh = MethodHandles.guardWithTest(test10_mh_test1,
+                                                    MethodHandles.dropArguments(test10_mh1, 0, MethodHandle.class, MethodHandle.class),
+                                                    MethodHandles.guardWithTest(test10_mh_test2,
+                                                                                MethodHandles.dropArguments(MethodHandles.invoker(test10_mt3), 1, MethodHandle.class),
+                                                                                MethodHandles.dropArguments(MethodHandles.invoker(test10_mt3), 0, MethodHandle.class))
+                                                    );
+
+            MethodHandle test11_mh1 = lookup.findStatic(clazz, "test11_target1", myvalue2_mt);
+            test11_mh2 = lookup.findStatic(clazz, "test11_target2", myvalue2_mt);
+            MethodHandle test11_mh_test = lookup.findStatic(clazz, "test11_test", boolean_mt);
+            test11_mh = MethodHandles.guardWithTest(test11_mh_test,
+                                                    MethodHandles.dropArguments(test11_mh1, 0, MethodHandle.class),
+                                                    MethodHandles.invoker(myvalue2_mt));
+        } catch (NoSuchMethodException | IllegalAccessException e) {
+            e.printStackTrace();
+            throw new RuntimeException("Method handle lookup failed");
+        }
+    }
+
+    public static void main(String[] args) {
+        Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS;
+
+        // Prevent inlining through MethodHandle linkTo adapters to stress the calling convention
+        scenarios[2].addFlags("-DVerifyIR=false",
+                              "-XX:CompileCommand=dontinline,java.lang.invoke.DirectMethodHandle::internalMemberName");
+        scenarios[4].addFlags("-XX:CompileCommand=dontinline,java.lang.invoke.DirectMethodHandle::internalMemberName");
+
+        InlineTypes.getFramework()
+                   .addScenarios(scenarios)
+                   .addFlags("-XX:CompileCommand=inline,java.lang.invoke.MethodHandleImpl::*")
+                   .addHelperClasses(MyValue1.class,
+                                     MyValue2.class,
+                                     MyValue2Inline.class,
+                                     MyValue3.class,
+                                     MyValue3Inline.class)
+                   .start();
+    }
+
+    // Everything inlined
+    @NullRestricted
+    final MyValue3 test1_vt = MyValue3.create();
+
+    @ForceInline
+    MyValue3 test1_target() {
+        return test1_vt;
+    }
+
+    static final MethodHandle test1_mh;
+
+    @Test
+    @IR(applyIf = {"InlineTypeReturnedAsFields", "true"},
+        failOn = {ALLOC, STORE, CALL})
+    @IR(applyIf = {"InlineTypeReturnedAsFields", "false"},
+        counts = {ALLOC, "= 1", STORE, "= 14"})
+    public MyValue3 test1() throws Throwable {
+        return (MyValue3)test1_mh.invokeExact(this);
+    }
+
+    @Run(test = "test1")
+    @Warmup(10000)
+    public void test1_verifier() throws Throwable {
+        MyValue3 vt = test1();
+        test1_vt.verify(vt);
+    }
+
+    // Leaf method not inlined but returned type is known
+    @NullRestricted
+    final MyValue3 test2_vt = MyValue3.create();
+
+    @DontInline
+    MyValue3 test2_target() {
+        return test2_vt;
+    }
+
+    static final MethodHandle test2_mh;
+
+    @Test
+    public MyValue3 test2() throws Throwable {
+        return (MyValue3)test2_mh.invokeExact(this);
+    }
+
+    @Run(test = "test2")
+    public void test2_verifier(RunInfo info) throws Throwable {
+        if (!info.isWarmUp()) {
+            Method helper_m = getClass().getDeclaredMethod("test2_target");
+            if (!TestFramework.isCompiled(helper_m)) {
+                TestFramework.compile(helper_m, CompLevel.C2);
+            }
+        }
+        MyValue3 vt = test2();
+        test2_vt.verify(vt);
+    }
+
+    // Leaf method not inlined and returned type not known
+    @NullRestricted
+    final MyValue3 test3_vt = MyValue3.create();
+
+    @DontInline
+    MyValue3 test3_target() {
+        return test3_vt;
+    }
+
+    static final MethodHandle test3_mh;
+
+    @Test
+    public MyValue3 test3() throws Throwable {
+        return (MyValue3)test3_mh.invokeExact(this);
+    }
+
+    @Run(test = "test3")
+    public void test3_verifier(RunInfo info) throws Throwable {
+        // hack so C2 doesn't know the target of the invoke call
+        Class c = Class.forName("java.lang.invoke.DirectMethodHandle");
+        Method m = c.getDeclaredMethod("internalMemberName", Object.class);
+        WhiteBox.getWhiteBox().testSetDontInlineMethod(m, info.isWarmUp());
+        MyValue3 vt = test3();
+        test3_vt.verify(vt);
+    }
+
+    // When test75_helper1 is inlined in test75, the method handle
+    // linker that called it is passed a pointer to a copy of vt
+    // stored in memory. The method handle linker needs to load the
+    // fields from memory before it inlines test75_helper1.
+    static public int test4_helper1(MyValue1 vt) {
+        return vt.x;
+    }
+
+    @NullRestricted
+    static MyValue1 test4_vt = MyValue1.createWithFieldsInline(rI, rL);
+
+    static public MyValue1 test4_helper2() {
+        return test4_vt;
+    }
+
+    static final MethodHandle test4_mh;
+
+    @Test
+    public int test4() throws Throwable {
+        return (int)test4_mh.invokeExact();
+    }
+
+    @Run(test = "test4")
+    public void test4_verifier() throws Throwable {
+        int i = test4();
+        Asserts.assertEQ(i, test4_vt.x);
+    }
+
+    // Test method handle call with inline type argument
+    public int test5_target(MyValue1 vt) {
+        return vt.x;
+    }
+
+    static final MethodHandle test5_mh;
+
+    @NullRestricted
+    MyValue1 test5_vt = MyValue1.createWithFieldsInline(rI, rL);
+
+    @Test
+    public int test5() throws Throwable {
+        return (int)test5_mh.invokeExact(this, test5_vt);
+    }
+
+    @Run(test = "test5")
+    public void test5_verifier() throws Throwable {
+        int i = test5();
+        Asserts.assertEQ(i, test5_vt.x);
+    }
+
+    // Return of target1 and target2 merged in a Lambda Form as an
+    // Object. Shouldn't cause any allocation
+    @NullRestricted
+    final MyValue3 test6_vt1 = MyValue3.create();
+
+    @ForceInline
+    MyValue3 test6_target1() {
+        return test6_vt1;
+    }
+
+    @NullRestricted
+    final MyValue3 test6_vt2 = MyValue3.create();
+
+    @ForceInline
+    MyValue3 test6_target2() {
+        return test6_vt2;
+    }
+
+    boolean test6_bool = true;
+    @ForceInline
+    boolean test6_test() {
+        return test6_bool;
+    }
+
+    static final MethodHandle test6_mh;
+
+    @Test
+    @IR(applyIf = {"InlineTypeReturnedAsFields", "true"},
+        failOn = {ALLOC, ALLOCA, STORE, STORE_INLINE_FIELDS})
+    public MyValue3 test6() throws Throwable {
+        return (MyValue3)test6_mh.invokeExact(this);
+    }
+
+    @Run(test = "test6")
+    public void test6_verifier() throws Throwable {
+        test6_bool = !test6_bool;
+        MyValue3 vt = test6();
+        vt.verify(test6_bool ? test6_vt1 : test6_vt2);
+    }
+
+    // Similar as above but with the method handle for target1 not
+    // constant. Shouldn't cause any allocation.
+    @ForceInline
+    static MyValue2 test7_target1() {
+        return MyValue2.createWithFieldsInline(rI, rD);
+    }
+
+    @ForceInline
+    static MyValue2 test7_target2() {
+        return MyValue2.createWithFieldsInline(rI+1, rD+1);
+    }
+
+    static boolean test7_bool = true;
+    @ForceInline
+    static boolean test7_test() {
+        return test7_bool;
+    }
+
+    static final MethodHandle test7_mh;
+    static MethodHandle test7_mh1;
+
+    @Test
+    public long test7() throws Throwable {
+        return ((MyValue2)test7_mh.invokeExact(test7_mh1)).hash();
+    }
+
+    @Run(test = "test7")
+    public void test7_verifier() throws Throwable {
+        test7_bool = !test7_bool;
+        long hash = test7();
+        Asserts.assertEQ(hash, MyValue2.createWithFieldsInline(rI+(test7_bool ? 0 : 1), rD+(test7_bool ? 0 : 1)).hash());
+    }
+
+    // Same as above but with the method handle for target2 not
+    // constant. Shouldn't cause any allocation.
+    @ForceInline
+    static MyValue2 test8_target1() {
+        return MyValue2.createWithFieldsInline(rI, rD);
+    }
+
+    @ForceInline
+    static MyValue2 test8_target2() {
+        return MyValue2.createWithFieldsInline(rI+1, rD+1);
+    }
+
+    static boolean test8_bool = true;
+    @ForceInline
+    static boolean test8_test() {
+        return test8_bool;
+    }
+
+    static final MethodHandle test8_mh;
+    static MethodHandle test8_mh2;
+
+    @Test
+    public long test8() throws Throwable {
+        return ((MyValue2)test8_mh.invokeExact(test8_mh2)).hash();
+    }
+
+    @Run(test = "test8")
+    public void test8_verifier() throws Throwable {
+        test8_bool = !test8_bool;
+        long hash = test8();
+        Asserts.assertEQ(hash, MyValue2.createWithFieldsInline(rI+(test8_bool ? 0 : 1), rD+(test8_bool ? 0 : 1)).hash());
+    }
+
+    // Return of target1, target2 and target3 merged in Lambda Forms
+    // as an Object. Shouldn't cause any allocation
+    @NullRestricted
+    final MyValue3 test9_vt1 = MyValue3.create();
+
+    @ForceInline
+    MyValue3 test9_target1() {
+        return test9_vt1;
+    }
+
+    @NullRestricted
+    final MyValue3 test9_vt2 = MyValue3.create();
+
+    @ForceInline
+    MyValue3 test9_target2() {
+        return test9_vt2;
+    }
+
+    @NullRestricted
+    final MyValue3 test9_vt3 = MyValue3.create();
+
+    @ForceInline
+    MyValue3 test9_target3() {
+        return test9_vt3;
+    }
+
+    boolean test9_bool1 = true;
+    @ForceInline
+    boolean test9_test1() {
+        return test9_bool1;
+    }
+
+    boolean test9_bool2 = true;
+    @ForceInline
+    boolean test9_test2() {
+        return test9_bool2;
+    }
+
+    static final MethodHandle test9_mh;
+
+    @Test
+    @IR(applyIf = {"InlineTypeReturnedAsFields", "true"},
+        failOn = {ALLOC, ALLOCA, STORE, STORE_INLINE_FIELDS})
+   public MyValue3 test9() throws Throwable {
+        return (MyValue3)test9_mh.invokeExact(this);
+    }
+
+    static int test9_i = 0;
+    @Run(test = "test9")
+    public void test9_verifier() throws Throwable {
+        test9_i++;
+        test9_bool1 = (test9_i % 2) == 0;
+        test9_bool2 = (test9_i % 3) == 0;
+        MyValue3 vt = test9();
+        vt.verify(test9_bool1 ? test9_vt1 : (test9_bool2 ? test9_vt2 : test9_vt3));
+    }
+
+    // Same as above but with non constant target2 and target3
+    @ForceInline
+    static MyValue2 test10_target1() {
+        return MyValue2.createWithFieldsInline(rI, rD);
+    }
+
+    @ForceInline
+    static MyValue2 test10_target2() {
+        return MyValue2.createWithFieldsInline(rI+1, rD+1);
+    }
+
+    @ForceInline
+    static MyValue2 test10_target3() {
+        return MyValue2.createWithFieldsInline(rI+2, rD+2);
+    }
+
+    static boolean test10_bool1 = true;
+    @ForceInline
+    static boolean test10_test1() {
+        return test10_bool1;
+    }
+
+    static boolean test10_bool2 = true;
+    @ForceInline
+    static boolean test10_test2() {
+        return test10_bool2;
+    }
+
+    static final MethodHandle test10_mh;
+    static MethodHandle test10_mh2;
+    static MethodHandle test10_mh3;
+
+    @Test
+    public long test10() throws Throwable {
+        return ((MyValue2)test10_mh.invokeExact(test10_mh2, test10_mh3)).hash();
+    }
+
+    static int test10_i = 0;
+
+    @Run(test = "test10")
+    public void test10_verifier() throws Throwable {
+        test10_i++;
+        test10_bool1 = (test10_i % 2) == 0;
+        test10_bool2 = (test10_i % 3) == 0;
+        long hash = test10();
+        int i = rI + (test10_bool1 ? 0 : (test10_bool2 ? 1 : 2));
+        double d = rD + (test10_bool1 ? 0 : (test10_bool2 ? 1 : 2));
+        Asserts.assertEQ(hash, MyValue2.createWithFieldsInline(i, d).hash());
+    }
+
+    static int test11_i = 0;
+
+    @ForceInline
+    static MyValue2 test11_target1() {
+        return MyValue2.createWithFieldsInline(rI+test11_i, rD+test11_i);
+    }
+
+    @ForceInline
+    static MyValue2 test11_target2() {
+        return MyValue2.createWithFieldsInline(rI-test11_i, rD-test11_i);
+    }
+
+    @ForceInline
+    static boolean test11_test() {
+        return (test11_i % 100) == 0;
+    }
+
+    static final MethodHandle test11_mh;
+    static MethodHandle test11_mh2;
+
+    // Check that a buffered inline type returned by a compiled lambda form
+    // is properly handled by the caller.
+    @Test
+    public long test11() throws Throwable {
+        return ((MyValue2)test11_mh.invokeExact(test11_mh2)).hash();
+    }
+
+    @Run(test = "test11")
+    @Warmup(11000)
+    public void test11_verifier() throws Throwable {
+        test11_i++;
+        long hash = test11();
+        boolean b = (test11_i % 100) == 0;
+        Asserts.assertEQ(hash, MyValue2.createWithFieldsInline(rI+test11_i * (b ? 1 : -1), rD+test11_i * (b ? 1 : -1)).hash());
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMismatchHandling.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMismatchHandling.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMismatchHandling.java
@@ -0,0 +1,210 @@
+/*
+ * 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
+ * @bug 8301007
+ * @key randomness
+ * @summary Verify that mismatches of the preload attribute are properly handled in the calling convention.
+ * @library /test/lib /compiler/whitebox /
+ * @enablePreview
+ * @compile TestMismatchHandling.jcod TestMismatchHandling.java
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xbatch
+ *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:-Inline -XX:-InlineAccessors -XX:-UseBimorphicInlining -XX:-UseCHA -XX:-UseTypeProfile
+ *                   -XX:CompileCommand=compileonly,TestMismatchHandling::test*
+ *                   TestMismatchHandling
+ * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xbatch
+ *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:-Inline -XX:-InlineAccessors -XX:-UseBimorphicInlining -XX:-UseCHA -XX:-UseTypeProfile
+ *                   -XX:CompileCommand=compileonly,*::method
+ *                   TestMismatchHandling
+ * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xbatch
+ *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:-Inline -XX:-InlineAccessors -XX:-UseBimorphicInlining -XX:-UseCHA -XX:-UseTypeProfile
+ *                   TestMismatchHandling
+ * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xbatch
+ *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:-Inline -XX:-InlineAccessors -XX:-UseBimorphicInlining -XX:-UseCHA -XX:-UseTypeProfile
+ *                   -XX:-InlineTypePassFieldsAsArgs
+ *                   TestMismatchHandling
+ * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xbatch
+ *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:-Inline -XX:-InlineAccessors -XX:-UseBimorphicInlining -XX:-UseCHA -XX:-UseTypeProfile
+ *                   -XX:-InlineTypeReturnedAsFields
+ *                   TestMismatchHandling
+ * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xbatch
+ *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:+DeoptimizeNMethodBarriersALot
+ *                   TestMismatchHandling
+ * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xbatch
+ *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   TestMismatchHandling
+ */
+
+// ##################################### WARNING ######################################
+// Use below script to re-generate TestMismatchHandling.jcod, don't modify it manually.
+// Be careful when changing anything (even the order) in this test and related files.
+// ##################################### WARNING ######################################
+
+/*
+  #!/bin/bash
+  export PATH=/oracle/valhalla/build/fastdebug/jdk/bin/:$PATH
+  ASMTOOLS=/oracle/valhalla/open/test/lib
+
+  # With preload attribute
+  javac --enable-preview --source 22 TestMismatchHandlingGenerator.java
+  java -cp $ASMTOOLS org.openjdk.asmtools.Main jdec MyValue1.class MyValue2.class MyValue3.class MyValue4.class MyValue5.class MyValue6.class MyValue7.class Verifiable.class B.class I3.class I4.class E.class G.class J.class K.class L.class P.class Q.class R.class S.class TestMismatchHandlingHelper.class > TestMismatchHandling.jcod
+
+  # Without preload attribute
+  sed -i 's/value class MyValue/class MyValue/g' TestMismatchHandlingGenerator.java
+  javac TestMismatchHandlingGenerator.java
+  java -cp $ASMTOOLS org.openjdk.asmtools.Main jdec A.class C.class I1.class I2.class D.class F.class H.class I5.class M.class N.class O.class I6.class P.class >> TestMismatchHandling.jcod
+
+  sed -i 's/class MyValue/value class MyValue/g' TestMismatchHandlingGenerator.java
+*/
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import jdk.test.lib.Utils;
+import jdk.test.whitebox.WhiteBox;
+
+public class TestMismatchHandling {
+    public static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+
+    public static void main(String[] args) throws Exception {
+        M m = new M();
+        // Make sure M::method is C1 compiled once with unloaded MyValue4 and not re-compiled
+        for (int i = 0; i < 1000; ++i) {
+            TestMismatchHandlingHelper.test4(m, true);
+        }
+        Method disable = M.class.getDeclaredMethod("method", boolean.class);
+        WHITE_BOX.makeMethodNotCompilable(disable, 1, false);
+        WHITE_BOX.makeMethodNotCompilable(disable, 2, false);
+        WHITE_BOX.makeMethodNotCompilable(disable, 3, false);
+        WHITE_BOX.makeMethodNotCompilable(disable, 4, false);
+
+        // Sometimes, exclude some methods from compilation with C2 to stress test the calling convention
+        // WARNING: This triggers class loading of argument/return types of all methods!
+        if (Utils.getRandomInstance().nextBoolean()) {
+            ArrayList<Method> methods = new ArrayList<Method>();
+            Collections.addAll(methods, TestMismatchHandlingHelper.class.getDeclaredMethods());
+            Collections.addAll(methods, A.class.getDeclaredMethods());
+            Collections.addAll(methods, B.class.getDeclaredMethods());
+            Collections.addAll(methods, C.class.getDeclaredMethods());
+            Collections.addAll(methods, E.class.getDeclaredMethods());
+            Collections.addAll(methods, F.class.getDeclaredMethods());
+            Collections.addAll(methods, G.class.getDeclaredMethods());
+            Collections.addAll(methods, H.class.getDeclaredMethods());
+            Collections.addAll(methods, J.class.getDeclaredMethods());
+            Collections.addAll(methods, K.class.getDeclaredMethods());
+            Collections.addAll(methods, L.class.getDeclaredMethods());
+            // Don't do this because it would load MyValue5
+            // Collections.addAll(methods, N.class.getDeclaredMethods());
+            System.out.println("Excluding methods from C2 compilation:");
+            for (Method method : methods) {
+                if (Utils.getRandomInstance().nextBoolean()) {
+                    System.out.println(method);
+                    WHITE_BOX.makeMethodNotCompilable(method, 4, false);
+                }
+            }
+        }
+
+        A a = new A();
+        B b = new B();
+        C c = new C();
+        D d = new D();
+        E e = new E();
+        H h = new H();
+        J j = new J();
+        K k = new K();
+        N n = new N();
+        O o = new O();
+        P p = new P();
+        Q q = new Q();
+        R r = new R();
+
+        // Warmup
+        for (int i = 0; i < 50_000; ++i) {
+            TestMismatchHandlingHelper.test1(a, a, a, b, c, b, b, c);
+            TestMismatchHandlingHelper.test1(b, a, a, b, c, b, b, c);
+            TestMismatchHandlingHelper.test1(c, b, a, b, c, c, b, c);
+            TestMismatchHandlingHelper.test2(d, d, d, d, d, d,  d, d, d, d, d, d,  e, e, e, e, e, e,  e, e, e, e, e, e,  d, e);
+            TestMismatchHandlingHelper.test2(d, d, d, d, d, d,  d, d, d, d, d, d,  e, e, e, e, e, e,  e, e, e, e, e, e,  d, e);
+            TestMismatchHandlingHelper.test3(h, h, h,  j, k, j, k, j,  h, k);
+            TestMismatchHandlingHelper.test3(h, h, h,  j, k, j, k, k,  h, k);
+            TestMismatchHandlingHelper.test4(m, true);
+            TestMismatchHandlingHelper.test5(n, true);
+            TestMismatchHandlingHelper.test7(o, true);
+            TestMismatchHandlingHelper.test8(p, p, p,  q, r, q, r, q,  p, r);
+            TestMismatchHandlingHelper.test8(p, p, p,  q, r, q, r, r,  p, r);
+        }
+
+        // Only load these now
+        F f = new F();
+        G g = new G();
+        L l = new L();
+        S s = new S();
+
+        for (int i = 0; i < 50_000; ++i) {
+            TestMismatchHandlingHelper.test1(a, a, a, b, c, b, b, c);
+            TestMismatchHandlingHelper.test1(b, a, a, b, c, b, b, c);
+            TestMismatchHandlingHelper.test1(c, b, a, b, c, c, b, c);
+            TestMismatchHandlingHelper.test2(d, f, g, d, f, d,  d, f, g, d, f, d,  e, f, g, e, f, g,  e, f, g, e, f, g,  d, e);
+            TestMismatchHandlingHelper.test2(d, f, g, d, f, f,  d, f, g, d, f, f,  e, f, g, e, f, f,  e, f, g, e, f, f,  d, e);
+            TestMismatchHandlingHelper.test2(d, f, g, f, g, g,  d, f, g, f, g, g,  e, f, g, f, g, g,  e, f, g, f, g, g,  d, e);
+            TestMismatchHandlingHelper.test3(h, l, h,  j, k, j, k, j,  h, k);
+            TestMismatchHandlingHelper.test3(h, l, h,  j, k, k, k, k,  h, k);
+            TestMismatchHandlingHelper.test3(h, l, l,  j, k, k, l, l,  h, l);
+            TestMismatchHandlingHelper.test4(m, false);
+            TestMismatchHandlingHelper.test5(n, false);
+            TestMismatchHandlingHelper.test6(f, g, l);
+            TestMismatchHandlingHelper.test7TriggerCalleeCompilation(o);
+            TestMismatchHandlingHelper.test8(p, s, p,  q, r, q, r, q,  p, r);
+            TestMismatchHandlingHelper.test8(p, s, p,  q, r, r, r, r,  p, r);
+            TestMismatchHandlingHelper.test8(p, s, s,  q, r, r, s, s,  p, s);
+        }
+        TestMismatchHandlingHelper.test7(o, false).verify();
+
+        switch (Utils.getRandomInstance().nextInt() % 3) {
+        case 0:
+            TestMismatchHandlingHelper.test2(d, d, d, d, d, d,  d, d, d, d, d, d,  e, e, e, e, e, e,  e, e, e, e, e, e,  d, e);
+            TestMismatchHandlingHelper.test3(l, h, l,  k, l, l, j, j,  h, l);
+            TestMismatchHandlingHelper.test8(s, p, s,  r, s, s, q, q,  p, s);
+            break;
+        case 1:
+            TestMismatchHandlingHelper.test2(f, f, f, f, f, f,  f, f, f, f, f, f,  f, f, f, f, f, f,  f, f, f, f, f, f,  d, e);
+            TestMismatchHandlingHelper.test3(l, h, l,  l, j, j, k, l,  h, l);
+            TestMismatchHandlingHelper.test8(s, p, s,  s, q, q, r, s,  p, s);
+            break;
+        case 2:
+            TestMismatchHandlingHelper.test2(g, g, g, g, g, g,  g, g, g, g, g, g,  g, g, g, g, g, g,  g, g, g, g, g, g,  d, e);
+            TestMismatchHandlingHelper.test3(l, h, l,  j, k, k, l, j,  h, l);
+            TestMismatchHandlingHelper.test8(s, p, s,  q, r, r, s, q,  p, s);
+            break;
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMismatchHandling.jcod b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMismatchHandling.jcod
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMismatchHandling.jcod
@@ -0,0 +1,3743 @@
+/*
+ * 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.
+ */
+
+// ##################################### WARNING ########################################
+// Generated file, don't modify manually. See TestMismatchHandling.java for instructions.
+// ##################################### WARNING ########################################
+
+class MyValue1 {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Field #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #12; // #9
+    Utf8 "MyValue1"; // #10
+    Utf8 "x"; // #11
+    Utf8 "I"; // #12
+    class #14; // #13
+    Utf8 "java/lang/RuntimeException"; // #14
+    String #16; // #15
+    Utf8 "Verification failed"; // #16
+    Method #13 #18; // #17
+    NameAndType #5 #19; // #18
+    Utf8 "(Ljava/lang/String;)V"; // #19
+    Utf8 "Code"; // #20
+    Utf8 "LineNumberTable"; // #21
+    Utf8 "verify"; // #22
+    Utf8 "StackMapTable"; // #23
+    Utf8 "SourceFile"; // #24
+    Utf8 "TestMismatchHandlingGenerator.java"; // #25
+  } // Constant Pool
+
+  0x0020; // access
+  #8;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0000; // access
+      #11; // name_index
+      #12; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#20) { // Code
+          2; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB700012A102AB5;
+            0x0007B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#21) { // LineNumberTable
+              [] { // line_number_table
+                0  24;
+                4  25;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0000; // access
+      #22; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#20) { // Code
+          3; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB40007102A9F00;
+            0x0DBB000D59120FB7;
+            0x0011BFB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#21) { // LineNumberTable
+              [] { // line_number_table
+                0  28;
+                9  29;
+                19  31;
+              }
+            } // end LineNumberTable
+            ;
+            Attr(#23) { // StackMapTable
+              [] { // 
+                19b; // same_frame
+              }
+            } // end StackMapTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#24) { // SourceFile
+      #25;
+    } // end SourceFile
+  } // Attributes
+} // end class MyValue1
+class MyValue2 {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Field #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #12; // #9
+    Utf8 "MyValue2"; // #10
+    Utf8 "x"; // #11
+    Utf8 "I"; // #12
+    class #14; // #13
+    Utf8 "java/lang/RuntimeException"; // #14
+    String #16; // #15
+    Utf8 "Verification failed"; // #16
+    Method #13 #18; // #17
+    NameAndType #5 #19; // #18
+    Utf8 "(Ljava/lang/String;)V"; // #19
+    Utf8 "Code"; // #20
+    Utf8 "LineNumberTable"; // #21
+    Utf8 "verify"; // #22
+    Utf8 "StackMapTable"; // #23
+    Utf8 "SourceFile"; // #24
+    Utf8 "TestMismatchHandlingGenerator.java"; // #25
+  } // Constant Pool
+
+  0x0020; // access
+  #8;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0000; // access
+      #11; // name_index
+      #12; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#20) { // Code
+          2; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB700012A102AB5;
+            0x0007B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#21) { // LineNumberTable
+              [] { // line_number_table
+                0  34;
+                4  35;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0000; // access
+      #22; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#20) { // Code
+          3; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB40007102A9F00;
+            0x0DBB000D59120FB7;
+            0x0011BFB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#21) { // LineNumberTable
+              [] { // line_number_table
+                0  38;
+                9  39;
+                19  41;
+              }
+            } // end LineNumberTable
+            ;
+            Attr(#23) { // StackMapTable
+              [] { // 
+                19b; // same_frame
+              }
+            } // end StackMapTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#24) { // SourceFile
+      #25;
+    } // end SourceFile
+  } // Attributes
+} // end class MyValue2
+class MyValue3 {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Field #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #12; // #9
+    Utf8 "MyValue3"; // #10
+    Utf8 "x"; // #11
+    Utf8 "I"; // #12
+    class #14; // #13
+    Utf8 "java/lang/RuntimeException"; // #14
+    String #16; // #15
+    Utf8 "Verification failed"; // #16
+    Method #13 #18; // #17
+    NameAndType #5 #19; // #18
+    Utf8 "(Ljava/lang/String;)V"; // #19
+    Utf8 "Code"; // #20
+    Utf8 "LineNumberTable"; // #21
+    Utf8 "verify"; // #22
+    Utf8 "StackMapTable"; // #23
+    Utf8 "SourceFile"; // #24
+    Utf8 "TestMismatchHandlingGenerator.java"; // #25
+  } // Constant Pool
+
+  0x0020; // access
+  #8;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0000; // access
+      #11; // name_index
+      #12; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#20) { // Code
+          2; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB700012A102AB5;
+            0x0007B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#21) { // LineNumberTable
+              [] { // line_number_table
+                0  44;
+                4  45;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0000; // access
+      #22; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#20) { // Code
+          3; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB40007102A9F00;
+            0x0DBB000D59120FB7;
+            0x0011BFB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#21) { // LineNumberTable
+              [] { // line_number_table
+                0  48;
+                9  49;
+                19  51;
+              }
+            } // end LineNumberTable
+            ;
+            Attr(#23) { // StackMapTable
+              [] { // 
+                19b; // same_frame
+              }
+            } // end StackMapTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#24) { // SourceFile
+      #25;
+    } // end SourceFile
+  } // Attributes
+} // end class MyValue3
+class MyValue4 {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Field #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #12; // #9
+    Utf8 "MyValue4"; // #10
+    Utf8 "x"; // #11
+    Utf8 "I"; // #12
+    class #14; // #13
+    Utf8 "java/lang/RuntimeException"; // #14
+    String #16; // #15
+    Utf8 "Verification failed"; // #16
+    Method #13 #18; // #17
+    NameAndType #5 #19; // #18
+    Utf8 "(Ljava/lang/String;)V"; // #19
+    Method #8 #3; // #20
+    Utf8 "Code"; // #21
+    Utf8 "LineNumberTable"; // #22
+    Utf8 "verify"; // #23
+    Utf8 "StackMapTable"; // #24
+    Utf8 "make"; // #25
+    Utf8 "()LMyValue4;"; // #26
+    Utf8 "SourceFile"; // #27
+    Utf8 "TestMismatchHandlingGenerator.java"; // #28
+  } // Constant Pool
+
+  0x0020; // access
+  #8;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0000; // access
+      #11; // name_index
+      #12; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#21) { // Code
+          2; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB700012A102AB5;
+            0x0007B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#22) { // LineNumberTable
+              [] { // line_number_table
+                0  54;
+                4  55;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #23; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#21) { // Code
+          3; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB40007102A9F00;
+            0x0DBB000D59120FB7;
+            0x0011BFB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#22) { // LineNumberTable
+              [] { // line_number_table
+                0  58;
+                9  59;
+                19  61;
+              }
+            } // end LineNumberTable
+            ;
+            Attr(#24) { // StackMapTable
+              [] { // 
+                19b; // same_frame
+              }
+            } // end StackMapTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0008; // access
+      #25; // name_index
+      #26; // descriptor_index
+      [] { // Attributes
+        Attr(#21) { // Code
+          2; // max_stack
+          0; // max_locals
+          Bytes[]{
+            0xBB000859B70014B0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#22) { // LineNumberTable
+              [] { // line_number_table
+                0  64;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#27) { // SourceFile
+      #28;
+    } // end SourceFile
+  } // Attributes
+} // end class MyValue4
+class MyValue5 {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Field #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #12; // #9
+    Utf8 "MyValue5"; // #10
+    Utf8 "x"; // #11
+    Utf8 "I"; // #12
+    class #14; // #13
+    Utf8 "java/lang/RuntimeException"; // #14
+    String #16; // #15
+    Utf8 "Verification failed"; // #16
+    Method #13 #18; // #17
+    NameAndType #5 #19; // #18
+    Utf8 "(Ljava/lang/String;)V"; // #19
+    Method #8 #3; // #20
+    class #22; // #21
+    Utf8 "Verifiable"; // #22
+    Utf8 "Code"; // #23
+    Utf8 "LineNumberTable"; // #24
+    Utf8 "verify"; // #25
+    Utf8 "StackMapTable"; // #26
+    Utf8 "make"; // #27
+    Utf8 "()LMyValue5;"; // #28
+    Utf8 "SourceFile"; // #29
+    Utf8 "TestMismatchHandlingGenerator.java"; // #30
+  } // Constant Pool
+
+  0x0020; // access
+  #8;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+    #21;
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0000; // access
+      #11; // name_index
+      #12; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#23) { // Code
+          2; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB700012A102AB5;
+            0x0007B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#24) { // LineNumberTable
+              [] { // line_number_table
+                0  72;
+                4  73;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #25; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#23) { // Code
+          3; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB40007102A9F00;
+            0x0DBB000D59120FB7;
+            0x0011BFB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#24) { // LineNumberTable
+              [] { // line_number_table
+                0  77;
+                9  78;
+                19  80;
+              }
+            } // end LineNumberTable
+            ;
+            Attr(#26) { // StackMapTable
+              [] { // 
+                19b; // same_frame
+              }
+            } // end StackMapTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0008; // access
+      #27; // name_index
+      #28; // descriptor_index
+      [] { // Attributes
+        Attr(#23) { // Code
+          2; // max_stack
+          0; // max_locals
+          Bytes[]{
+            0xBB000859B70014B0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#24) { // LineNumberTable
+              [] { // line_number_table
+                0  83;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#29) { // SourceFile
+      #30;
+    } // end SourceFile
+  } // Attributes
+} // end class MyValue5
+class MyValue6 {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Field #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #12; // #9
+    Utf8 "MyValue6"; // #10
+    Utf8 "x"; // #11
+    Utf8 "I"; // #12
+    class #14; // #13
+    Utf8 "java/lang/RuntimeException"; // #14
+    String #16; // #15
+    Utf8 "Verification failed"; // #16
+    Method #13 #18; // #17
+    NameAndType #5 #19; // #18
+    Utf8 "(Ljava/lang/String;)V"; // #19
+    Method #8 #3; // #20
+    class #22; // #21
+    Utf8 "Verifiable"; // #22
+    Utf8 "Code"; // #23
+    Utf8 "LineNumberTable"; // #24
+    Utf8 "verify"; // #25
+    Utf8 "StackMapTable"; // #26
+    Utf8 "make"; // #27
+    Utf8 "()LMyValue6;"; // #28
+    Utf8 "SourceFile"; // #29
+    Utf8 "TestMismatchHandlingGenerator.java"; // #30
+  } // Constant Pool
+
+  0x0020; // access
+  #8;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+    #21;
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0000; // access
+      #11; // name_index
+      #12; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#23) { // Code
+          2; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB700012A102AB5;
+            0x0007B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#24) { // LineNumberTable
+              [] { // line_number_table
+                0  87;
+                4  88;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #25; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#23) { // Code
+          3; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB40007102A9F00;
+            0x0DBB000D59120FB7;
+            0x0011BFB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#24) { // LineNumberTable
+              [] { // line_number_table
+                0  92;
+                9  93;
+                19  95;
+              }
+            } // end LineNumberTable
+            ;
+            Attr(#26) { // StackMapTable
+              [] { // 
+                19b; // same_frame
+              }
+            } // end StackMapTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0008; // access
+      #27; // name_index
+      #28; // descriptor_index
+      [] { // Attributes
+        Attr(#23) { // Code
+          2; // max_stack
+          0; // max_locals
+          Bytes[]{
+            0xBB000859B70014B0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#24) { // LineNumberTable
+              [] { // line_number_table
+                0  98;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#29) { // SourceFile
+      #30;
+    } // end SourceFile
+  } // Attributes
+} // end class MyValue6
+class MyValue7 {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Field #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #12; // #9
+    Utf8 "MyValue7"; // #10
+    Utf8 "x"; // #11
+    Utf8 "I"; // #12
+    class #14; // #13
+    Utf8 "java/lang/RuntimeException"; // #14
+    String #16; // #15
+    Utf8 "Verification failed"; // #16
+    Method #13 #18; // #17
+    NameAndType #5 #19; // #18
+    Utf8 "(Ljava/lang/String;)V"; // #19
+    Utf8 "Code"; // #20
+    Utf8 "LineNumberTable"; // #21
+    Utf8 "verify"; // #22
+    Utf8 "StackMapTable"; // #23
+    Utf8 "SourceFile"; // #24
+    Utf8 "TestMismatchHandlingGenerator.java"; // #25
+  } // Constant Pool
+
+  0x0020; // access
+  #8;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0000; // access
+      #11; // name_index
+      #12; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#20) { // Code
+          2; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB700012A102AB5;
+            0x0007B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#21) { // LineNumberTable
+              [] { // line_number_table
+                0  102;
+                4  103;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0000; // access
+      #22; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#20) { // Code
+          3; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB40007102A9F00;
+            0x0DBB000D59120FB7;
+            0x0011BFB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#21) { // LineNumberTable
+              [] { // line_number_table
+                0  106;
+                9  107;
+                19  109;
+              }
+            } // end LineNumberTable
+            ;
+            Attr(#23) { // StackMapTable
+              [] { // 
+                19b; // same_frame
+              }
+            } // end StackMapTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#24) { // SourceFile
+      #25;
+    } // end SourceFile
+  } // Attributes
+} // end class MyValue7
+class Verifiable {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1
+    Utf8 "Verifiable"; // #2
+    class #4; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "verify"; // #5
+    Utf8 "()V"; // #6
+    Utf8 "SourceFile"; // #7
+    Utf8 "TestMismatchHandlingGenerator.java"; // #8
+  } // Constant Pool
+
+  0x0600; // access
+  #1;// this_cpx
+  #3;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0401; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#7) { // SourceFile
+      #8;
+    } // end SourceFile
+  } // Attributes
+} // end class Verifiable
+file "B.class" {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "A"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #6; // #9
+    Utf8 "MyValue1"; // #10
+    Utf8 "verify"; // #11
+    class #13; // #12
+    Utf8 "B"; // #13
+    Utf8 "Code"; // #14
+    Utf8 "LineNumberTable"; // #15
+    Utf8 "method"; // #16
+    Utf8 "(LMyValue1;)LMyValue1;"; // #17
+    Utf8 "SourceFile"; // #18
+    Utf8 "TestMismatchHandlingGenerator.java"; // #19
+  } // Constant Pool
+
+  0x0020; // access
+  #12;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#14) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#15) { // LineNumberTable
+              [] { // line_number_table
+                0  119;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #16; // name_index
+      #17; // descriptor_index
+      [] { // Attributes
+        Attr(#14) { // Code
+          1; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x2BB600072BB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#15) { // LineNumberTable
+              [] { // line_number_table
+                0  122;
+                4  123;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#18) { // SourceFile
+      #19;
+    } // end SourceFile
+  } // Attributes
+} // end class B
+class I3 {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1
+    Utf8 "I3"; // #2
+    class #4; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "method"; // #5
+    Utf8 "(LMyValue2;)LMyValue2;"; // #6
+    Utf8 "SourceFile"; // #7
+    Utf8 "TestMismatchHandlingGenerator.java"; // #8
+  } // Constant Pool
+
+  0x0600; // access
+  #1;// this_cpx
+  #3;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0401; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#7) { // SourceFile
+      #8;
+    } // end SourceFile
+  } // Attributes
+} // end class I3
+class I4 {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1
+    Utf8 "I4"; // #2
+    class #4; // #3
+    Utf8 "java/lang/Object"; // #4
+    class #6; // #5
+    Utf8 "I3"; // #6
+    Utf8 "method"; // #7
+    Utf8 "(LMyValue2;)LMyValue2;"; // #8
+    Utf8 "SourceFile"; // #9
+    Utf8 "TestMismatchHandlingGenerator.java"; // #10
+  } // Constant Pool
+
+  0x0600; // access
+  #1;// this_cpx
+  #3;// super_cpx
+
+  [] { // Interfaces
+    #5;
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0401; // access
+      #7; // name_index
+      #8; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#9) { // SourceFile
+      #10;
+    } // end SourceFile
+  } // Attributes
+} // end class I4
+class E {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #6; // #9
+    Utf8 "MyValue2"; // #10
+    Utf8 "verify"; // #11
+    class #13; // #12
+    Utf8 "E"; // #13
+    class #15; // #14
+    Utf8 "I4"; // #15
+    Utf8 "Code"; // #16
+    Utf8 "LineNumberTable"; // #17
+    Utf8 "method"; // #18
+    Utf8 "(LMyValue2;)LMyValue2;"; // #19
+    Utf8 "SourceFile"; // #20
+    Utf8 "TestMismatchHandlingGenerator.java"; // #21
+  } // Constant Pool
+
+  0x0020; // access
+  #12;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+    #14;
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#16) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#17) { // LineNumberTable
+              [] { // line_number_table
+                0  160;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #18; // name_index
+      #19; // descriptor_index
+      [] { // Attributes
+        Attr(#16) { // Code
+          1; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x2BB600072BB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#17) { // LineNumberTable
+              [] { // line_number_table
+                0  163;
+                4  164;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#20) { // SourceFile
+      #21;
+    } // end SourceFile
+  } // Attributes
+} // end class E
+class G {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #6; // #9
+    Utf8 "MyValue2"; // #10
+    Utf8 "verify"; // #11
+    class #13; // #12
+    Utf8 "G"; // #13
+    class #15; // #14
+    Utf8 "I2"; // #15
+    class #17; // #16
+    Utf8 "I4"; // #17
+    Utf8 "Code"; // #18
+    Utf8 "LineNumberTable"; // #19
+    Utf8 "method"; // #20
+    Utf8 "(LMyValue2;)LMyValue2;"; // #21
+    Utf8 "SourceFile"; // #22
+    Utf8 "TestMismatchHandlingGenerator.java"; // #23
+  } // Constant Pool
+
+  0x0020; // access
+  #12;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+    #14;
+    #16;
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#18) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#19) { // LineNumberTable
+              [] { // line_number_table
+                0  177;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #20; // name_index
+      #21; // descriptor_index
+      [] { // Attributes
+        Attr(#18) { // Code
+          1; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x2BB600072BB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#19) { // LineNumberTable
+              [] { // line_number_table
+                0  180;
+                4  181;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#22) { // SourceFile
+      #23;
+    } // end SourceFile
+  } // Attributes
+} // end class G
+class J {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #6; // #9
+    Utf8 "MyValue3"; // #10
+    Utf8 "verify"; // #11
+    class #13; // #12
+    Utf8 "J"; // #13
+    Utf8 "Code"; // #14
+    Utf8 "LineNumberTable"; // #15
+    Utf8 "method"; // #16
+    Utf8 "(LMyValue3;)LMyValue3;"; // #17
+    Utf8 "SourceFile"; // #18
+    Utf8 "TestMismatchHandlingGenerator.java"; // #19
+  } // Constant Pool
+
+  0x0020; // access
+  #12;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#14) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#15) { // LineNumberTable
+              [] { // line_number_table
+                0  197;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #16; // name_index
+      #17; // descriptor_index
+      [] { // Attributes
+        Attr(#14) { // Code
+          1; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x2BB600072BB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#15) { // LineNumberTable
+              [] { // line_number_table
+                0  199;
+                4  200;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#18) { // SourceFile
+      #19;
+    } // end SourceFile
+  } // Attributes
+} // end class J
+class K {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "J"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #6; // #9
+    Utf8 "MyValue3"; // #10
+    Utf8 "verify"; // #11
+    class #13; // #12
+    Utf8 "K"; // #13
+    Utf8 "Code"; // #14
+    Utf8 "LineNumberTable"; // #15
+    Utf8 "method"; // #16
+    Utf8 "(LMyValue3;)LMyValue3;"; // #17
+    Utf8 "SourceFile"; // #18
+    Utf8 "TestMismatchHandlingGenerator.java"; // #19
+  } // Constant Pool
+
+  0x0020; // access
+  #12;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#14) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#15) { // LineNumberTable
+              [] { // line_number_table
+                0  204;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #16; // name_index
+      #17; // descriptor_index
+      [] { // Attributes
+        Attr(#14) { // Code
+          1; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x2BB600072BB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#15) { // LineNumberTable
+              [] { // line_number_table
+                0  207;
+                4  208;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#18) { // SourceFile
+      #19;
+    } // end SourceFile
+  } // Attributes
+} // end class K
+file "L.class" {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "K"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #6; // #9
+    Utf8 "MyValue3"; // #10
+    Utf8 "verify"; // #11
+    class #13; // #12
+    Utf8 "L"; // #13
+    class #15; // #14
+    Utf8 "I5"; // #15
+    Utf8 "Code"; // #16
+    Utf8 "LineNumberTable"; // #17
+    Utf8 "method"; // #18
+    Utf8 "(LMyValue3;)LMyValue3;"; // #19
+    Utf8 "SourceFile"; // #20
+    Utf8 "TestMismatchHandlingGenerator.java"; // #21
+  } // Constant Pool
+
+  0x0020; // access
+  #12;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+    #14;
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#16) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#17) { // LineNumberTable
+              [] { // line_number_table
+                0  212;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #18; // name_index
+      #19; // descriptor_index
+      [] { // Attributes
+        Attr(#16) { // Code
+          1; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x2BB600072BB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#17) { // LineNumberTable
+              [] { // line_number_table
+                0  215;
+                4  216;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#20) { // SourceFile
+      #21;
+    } // end SourceFile
+  } // Attributes
+} // end class L
+class P {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #6; // #9
+    Utf8 "MyValue7"; // #10
+    Utf8 "verify"; // #11
+    class #13; // #12
+    Utf8 "P"; // #13
+    class #15; // #14
+    Utf8 "I6"; // #15
+    Utf8 "Code"; // #16
+    Utf8 "LineNumberTable"; // #17
+    Utf8 "method"; // #18
+    Utf8 "(LMyValue7;)LMyValue7;"; // #19
+    Utf8 "SourceFile"; // #20
+    Utf8 "TestMismatchHandlingGenerator.java"; // #21
+  } // Constant Pool
+
+  0x0020; // access
+  #12;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+    #14;
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#16) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#17) { // LineNumberTable
+              [] { // line_number_table
+                0  263;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #18; // name_index
+      #19; // descriptor_index
+      [] { // Attributes
+        Attr(#16) { // Code
+          1; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x2BB600072BB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#17) { // LineNumberTable
+              [] { // line_number_table
+                0  266;
+                4  267;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#20) { // SourceFile
+      #21;
+    } // end SourceFile
+  } // Attributes
+} // end class P
+class Q {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #6; // #9
+    Utf8 "MyValue7"; // #10
+    Utf8 "verify"; // #11
+    class #13; // #12
+    Utf8 "Q"; // #13
+    Utf8 "Code"; // #14
+    Utf8 "LineNumberTable"; // #15
+    Utf8 "method"; // #16
+    Utf8 "(LMyValue7;)LMyValue7;"; // #17
+    Utf8 "SourceFile"; // #18
+    Utf8 "TestMismatchHandlingGenerator.java"; // #19
+  } // Constant Pool
+
+  0x0020; // access
+  #12;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#14) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#15) { // LineNumberTable
+              [] { // line_number_table
+                0  271;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0000; // access
+      #16; // name_index
+      #17; // descriptor_index
+      [] { // Attributes
+        Attr(#14) { // Code
+          1; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x2BB600072BB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#15) { // LineNumberTable
+              [] { // line_number_table
+                0  273;
+                4  274;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#18) { // SourceFile
+      #19;
+    } // end SourceFile
+  } // Attributes
+} // end class Q
+class R {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "Q"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #6; // #9
+    Utf8 "MyValue7"; // #10
+    Utf8 "verify"; // #11
+    class #13; // #12
+    Utf8 "R"; // #13
+    Utf8 "Code"; // #14
+    Utf8 "LineNumberTable"; // #15
+    Utf8 "method"; // #16
+    Utf8 "(LMyValue7;)LMyValue7;"; // #17
+    Utf8 "SourceFile"; // #18
+    Utf8 "TestMismatchHandlingGenerator.java"; // #19
+  } // Constant Pool
+
+  0x0020; // access
+  #12;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#14) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#15) { // LineNumberTable
+              [] { // line_number_table
+                0  278;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0000; // access
+      #16; // name_index
+      #17; // descriptor_index
+      [] { // Attributes
+        Attr(#14) { // Code
+          1; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x2BB600072BB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#15) { // LineNumberTable
+              [] { // line_number_table
+                0  281;
+                4  282;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#18) { // SourceFile
+      #19;
+    } // end SourceFile
+  } // Attributes
+} // end class R
+class S {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "R"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #6; // #9
+    Utf8 "MyValue7"; // #10
+    Utf8 "verify"; // #11
+    class #13; // #12
+    Utf8 "S"; // #13
+    class #15; // #14
+    Utf8 "I6"; // #15
+    Utf8 "Code"; // #16
+    Utf8 "LineNumberTable"; // #17
+    Utf8 "method"; // #18
+    Utf8 "(LMyValue7;)LMyValue7;"; // #19
+    Utf8 "SourceFile"; // #20
+    Utf8 "TestMismatchHandlingGenerator.java"; // #21
+  } // Constant Pool
+
+  0x0020; // access
+  #12;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+    #14;
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#16) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#17) { // LineNumberTable
+              [] { // line_number_table
+                0  286;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #18; // name_index
+      #19; // descriptor_index
+      [] { // Attributes
+        Attr(#16) { // Code
+          1; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x2BB600072BB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#17) { // LineNumberTable
+              [] { // line_number_table
+                0  289;
+                4  290;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#20) { // SourceFile
+      #21;
+    } // end SourceFile
+  } // Attributes
+} // end class S
+class TestMismatchHandlingHelper {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    class #8; // #7
+    Utf8 "MyValue1"; // #8
+    Method #7 #3; // #9
+    Method #11 #12; // #10
+    class #13; // #11
+    NameAndType #14 #15; // #12
+    Utf8 "A"; // #13
+    Utf8 "method"; // #14
+    Utf8 "(LMyValue1;)LMyValue1;"; // #15
+    Method #7 #17; // #16
+    NameAndType #18 #6; // #17
+    Utf8 "verify"; // #18
+    Method #20 #12; // #19
+    class #21; // #20
+    Utf8 "B"; // #21
+    Method #23 #12; // #22
+    class #24; // #23
+    Utf8 "C"; // #24
+    class #26; // #25
+    Utf8 "MyValue2"; // #26
+    Method #25 #3; // #27
+    InterfaceMethod #29 #30; // #28
+    class #31; // #29
+    NameAndType #14 #32; // #30
+    Utf8 "I1"; // #31
+    Utf8 "(LMyValue2;)LMyValue2;"; // #32
+    Method #25 #17; // #33
+    InterfaceMethod #35 #30; // #34
+    class #36; // #35
+    Utf8 "I2"; // #36
+    Method #38 #30; // #37
+    class #39; // #38
+    Utf8 "D"; // #39
+    InterfaceMethod #41 #30; // #40
+    class #42; // #41
+    Utf8 "I3"; // #42
+    InterfaceMethod #44 #30; // #43
+    class #45; // #44
+    Utf8 "I4"; // #45
+    Method #47 #30; // #46
+    class #48; // #47
+    Utf8 "E"; // #48
+    class #50; // #49
+    Utf8 "MyValue3"; // #50
+    Method #49 #3; // #51
+    InterfaceMethod #53 #54; // #52
+    class #55; // #53
+    NameAndType #14 #56; // #54
+    Utf8 "I5"; // #55
+    Utf8 "(LMyValue3;)LMyValue3;"; // #56
+    Method #49 #17; // #57
+    Method #59 #54; // #58
+    class #60; // #59
+    Utf8 "H"; // #60
+    Method #62 #54; // #61
+    class #63; // #62
+    Utf8 "J"; // #63
+    Method #65 #54; // #64
+    class #66; // #65
+    Utf8 "K"; // #66
+    Method #68 #69; // #67
+    class #70; // #68
+    NameAndType #14 #71; // #69
+    Utf8 "M"; // #70
+    Utf8 "(Z)LMyValue4;"; // #71
+    Field #73 #74; // #72
+    class #75; // #73
+    NameAndType #76 #77; // #74
+    Utf8 "MyValue4"; // #75
+    Utf8 "x"; // #76
+    Utf8 "I"; // #77
+    class #79; // #78
+    Utf8 "java/lang/RuntimeException"; // #79
+    String #81; // #80
+    Utf8 "Verification failed"; // #81
+    Method #78 #83; // #82
+    NameAndType #5 #84; // #83
+    Utf8 "(Ljava/lang/String;)V"; // #84
+    Method #86 #87; // #85
+    class #88; // #86
+    NameAndType #14 #89; // #87
+    Utf8 "N"; // #88
+    Utf8 "(Z)LMyValue5;"; // #89
+    InterfaceMethod #91 #17; // #90
+    class #92; // #91
+    Utf8 "Verifiable"; // #92
+    Method #94 #30; // #93
+    class #95; // #94
+    Utf8 "F"; // #95
+    Method #97 #30; // #96
+    class #98; // #97
+    Utf8 "G"; // #98
+    Method #100 #54; // #99
+    class #101; // #100
+    Utf8 "L"; // #101
+    Method #103 #104; // #102
+    class #105; // #103
+    NameAndType #14 #106; // #104
+    Utf8 "O"; // #105
+    Utf8 "(Z)LMyValue6;"; // #106
+    Method #108 #17; // #107
+    class #109; // #108
+    Utf8 "MyValue6"; // #109
+    class #111; // #110
+    Utf8 "MyValue7"; // #111
+    Method #110 #3; // #112
+    InterfaceMethod #114 #115; // #113
+    class #116; // #114
+    NameAndType #14 #117; // #115
+    Utf8 "I6"; // #116
+    Utf8 "(LMyValue7;)LMyValue7;"; // #117
+    Method #110 #17; // #118
+    Method #120 #115; // #119
+    class #121; // #120
+    Utf8 "P"; // #121
+    Method #123 #115; // #122
+    class #124; // #123
+    Utf8 "Q"; // #124
+    Method #126 #115; // #125
+    class #127; // #126
+    Utf8 "R"; // #127
+    class #129; // #128
+    Utf8 "TestMismatchHandlingHelper"; // #129
+    Utf8 "Code"; // #130
+    Utf8 "LineNumberTable"; // #131
+    Utf8 "test1"; // #132
+    Utf8 "(LA;LA;LA;LA;LA;LB;LB;LC;)V"; // #133
+    Utf8 "test2"; // #134
+    Utf8 "(LI1;LI1;LI1;LI1;LI1;LI1;LI2;LI2;LI2;LI2;LI2;LI2;LI3;LI3;LI3;LI3;LI3;LI3;LI4;LI4;LI4;LI4;LI4;LI4;LD;LE;)V"; // #135
+    Utf8 "test3"; // #136
+    Utf8 "(LI5;LI5;LI5;LJ;LJ;LJ;LJ;LJ;LH;LK;)V"; // #137
+    Utf8 "test4"; // #138
+    Utf8 "(LM;Z)V"; // #139
+    Utf8 "StackMapTable"; // #140
+    Utf8 "test5"; // #141
+    Utf8 "(LN;Z)V"; // #142
+    Utf8 "test6"; // #143
+    Utf8 "(LF;LG;LL;)V"; // #144
+    Utf8 "test7"; // #145
+    Utf8 "(LO;Z)LVerifiable;"; // #146
+    Utf8 "test7TriggerCalleeCompilation"; // #147
+    Utf8 "(LO;)V"; // #148
+    Utf8 "test8"; // #149
+    Utf8 "(LI6;LI6;LI6;LQ;LQ;LQ;LQ;LQ;LP;LR;)V"; // #150
+    Utf8 "SourceFile"; // #151
+    Utf8 "TestMismatchHandlingGenerator.java"; // #152
+  } // Constant Pool
+
+  0x0020; // access
+  #128;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#130) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#131) { // LineNumberTable
+              [] { // line_number_table
+                0  294;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0009; // access
+      #132; // name_index
+      #133; // descriptor_index
+      [] { // Attributes
+        Attr(#130) { // Code
+          3; // max_stack
+          8; // max_locals
+          Bytes[]{
+            0x2ABB000759B70009;
+            0xB6000AB600102BBB;
+            0x000759B70009B600;
+            0x0AB600102CBB0007;
+            0x59B70009B6000AB6;
+            0x00102DBB000759B7;
+            0x0009B6000AB60010;
+            0x1904BB000759B700;
+            0x09B6000AB6001019;
+            0x05BB000759B70009;
+            0xB60013B600101906;
+            0xBB000759B70009B6;
+            0x0013B600101907BB;
+            0x000759B70009B600;
+            0x16B60010B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#131) { // LineNumberTable
+              [] { // line_number_table
+                0  300;
+                14  301;
+                28  302;
+                42  303;
+                56  304;
+                71  306;
+                86  307;
+                101  308;
+                116  309;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0009; // access
+      #134; // name_index
+      #135; // descriptor_index
+      [] { // Attributes
+        Attr(#130) { // Code
+          3; // max_stack
+          26; // max_locals
+          Bytes[]{
+            0x2ABB001959B7001B;
+            0xB9001C0200B60021;
+            0x2BBB001959B7001B;
+            0xB9001C0200B60021;
+            0x2CBB001959B7001B;
+            0xB9001C0200B60021;
+            0x2DBB001959B7001B;
+            0xB9001C0200B60021;
+            0x1904BB001959B700;
+            0x1BB9001C0200B600;
+            0x211905BB001959B7;
+            0x001BB9001C0200B6;
+            0x00211906BB001959;
+            0xB7001BB900220200;
+            0xB600211907BB0019;
+            0x59B7001BB9002202;
+            0x00B600211908BB00;
+            0x1959B7001BB90022;
+            0x0200B600211909BB;
+            0x001959B7001BB900;
+            0x220200B60021190A;
+            0xBB001959B7001BB9;
+            0x00220200B6002119;
+            0x0BBB001959B7001B;
+            0xB900220200B60021;
+            0x1918BB001959B700;
+            0x1BB60025B6002119;
+            0x0CBB001959B7001B;
+            0xB900280200B60021;
+            0x190DBB001959B700;
+            0x1BB900280200B600;
+            0x21190EBB001959B7;
+            0x001BB900280200B6;
+            0x0021190FBB001959;
+            0xB7001BB900280200;
+            0xB600211910BB0019;
+            0x59B7001BB9002802;
+            0x00B600211911BB00;
+            0x1959B7001BB90028;
+            0x0200B600211912BB;
+            0x001959B7001BB900;
+            0x2B0200B600211913;
+            0xBB001959B7001BB9;
+            0x002B0200B6002119;
+            0x14BB001959B7001B;
+            0xB9002B0200B60021;
+            0x1915BB001959B700;
+            0x1BB9002B0200B600;
+            0x211916BB001959B7;
+            0x001BB9002B0200B6;
+            0x00211917BB001959;
+            0xB7001BB9002B0200;
+            0xB600211919BB0019;
+            0x59B7001BB6002EB6;
+            0x0021B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#131) { // LineNumberTable
+              [] { // line_number_table
+                0  318;
+                16  319;
+                32  320;
+                48  321;
+                64  322;
+                81  323;
+                98  324;
+                115  325;
+                132  326;
+                149  327;
+                166  328;
+                183  329;
+                200  330;
+                215  332;
+                232  333;
+                249  334;
+                266  335;
+                283  336;
+                300  337;
+                317  338;
+                334  339;
+                351  340;
+                368  341;
+                385  342;
+                402  343;
+                419  344;
+                434  345;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0009; // access
+      #136; // name_index
+      #137; // descriptor_index
+      [] { // Attributes
+        Attr(#130) { // Code
+          3; // max_stack
+          10; // max_locals
+          Bytes[]{
+            0x2ABB003159B70033;
+            0xB900340200B60039;
+            0x2BBB003159B70033;
+            0xB900340200B60039;
+            0x2CBB003159B70033;
+            0xB900340200B60039;
+            0x1908BB003159B700;
+            0x33B6003AB600392D;
+            0xBB003159B70033B6;
+            0x003DB600391904BB;
+            0x003159B70033B600;
+            0x3DB600391905BB00;
+            0x3159B70033B6003D;
+            0xB600391906BB0031;
+            0x59B70033B6003DB6;
+            0x00391907BB003159;
+            0xB70033B6003DB600;
+            0x391909BB003159B7;
+            0x0033B60040B60039;
+            0xB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#131) { // LineNumberTable
+              [] { // line_number_table
+                0  353;
+                16  354;
+                32  355;
+                48  356;
+                63  358;
+                77  359;
+                92  360;
+                107  361;
+                122  362;
+                137  363;
+                152  364;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0009; // access
+      #138; // name_index
+      #139; // descriptor_index
+      [] { // Attributes
+        Attr(#130) { // Code
+          3; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x1B99000C2A1BB600;
+            0x4357A7001A2A1BB6;
+            0x0043B40048102A9F;
+            0x000DBB004E591250;
+            0xB70052BFB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#131) { // LineNumberTable
+              [] { // line_number_table
+                0  369;
+                4  370;
+                13  372;
+                26  373;
+                36  376;
+              }
+            } // end LineNumberTable
+            ;
+            Attr(#140) { // StackMapTable
+              [] { // 
+                13b; // same_frame
+                22b; // same_frame
+              }
+            } // end StackMapTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0009; // access
+      #141; // name_index
+      #142; // descriptor_index
+      [] { // Attributes
+        Attr(#130) { // Code
+          2; // max_stack
+          3; // max_locals
+          Bytes[]{
+            0x2A1BB600554D1B9A;
+            0x00092CB9005A0100;
+            0xB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#131) { // LineNumberTable
+              [] { // line_number_table
+                0  381;
+                6  382;
+                10  383;
+                16  385;
+              }
+            } // end LineNumberTable
+            ;
+            Attr(#140) { // StackMapTable
+              [] { // 
+                252b, 16, []z{O,91}; // append_frame 1
+              }
+            } // end StackMapTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0009; // access
+      #143; // name_index
+      #144; // descriptor_index
+      [] { // Attributes
+        Attr(#130) { // Code
+          3; // max_stack
+          3; // max_locals
+          Bytes[]{
+            0x2ABB001959B7001B;
+            0xB6005D572BBB0019;
+            0x59B7001BB6006057;
+            0x2CBB003159B70033;
+            0xB6006357B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#131) { // LineNumberTable
+              [] { // line_number_table
+                0  389;
+                12  390;
+                24  391;
+                36  392;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0009; // access
+      #145; // name_index
+      #146; // descriptor_index
+      [] { // Attributes
+        Attr(#130) { // Code
+          2; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x2A1BB60066B0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#131) { // LineNumberTable
+              [] { // line_number_table
+                0  397;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0009; // access
+      #147; // name_index
+      #148; // descriptor_index
+      [] { // Attributes
+        Attr(#130) { // Code
+          2; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2A04B60066572A03;
+            0xB60066B6006BB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#131) { // LineNumberTable
+              [] { // line_number_table
+                0  401;
+                6  402;
+                14  403;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0009; // access
+      #149; // name_index
+      #150; // descriptor_index
+      [] { // Attributes
+        Attr(#130) { // Code
+          3; // max_stack
+          10; // max_locals
+          Bytes[]{
+            0x2ABB006E59B70070;
+            0xB900710200B60076;
+            0x2BBB006E59B70070;
+            0xB900710200B60076;
+            0x2CBB006E59B70070;
+            0xB900710200B60076;
+            0x1908BB006E59B700;
+            0x70B60077B600762D;
+            0xBB006E59B70070B6;
+            0x007AB600761904BB;
+            0x006E59B70070B600;
+            0x7AB600761905BB00;
+            0x6E59B70070B6007A;
+            0xB600761906BB006E;
+            0x59B70070B6007AB6;
+            0x00761907BB006E59;
+            0xB70070B6007AB600;
+            0x761909BB006E59B7;
+            0x0070B6007DB60076;
+            0xB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#131) { // LineNumberTable
+              [] { // line_number_table
+                0  412;
+                16  413;
+                32  414;
+                48  415;
+                63  417;
+                77  418;
+                92  419;
+                107  420;
+                122  421;
+                137  422;
+                152  423;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#151) { // SourceFile
+      #152;
+    } // end SourceFile
+  } // Attributes
+} // end class TestMismatchHandlingHelper
+class A {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #6; // #9
+    Utf8 "MyValue1"; // #10
+    Utf8 "verify"; // #11
+    class #13; // #12
+    Utf8 "A"; // #13
+    Utf8 "Code"; // #14
+    Utf8 "LineNumberTable"; // #15
+    Utf8 "method"; // #16
+    Utf8 "(LMyValue1;)LMyValue1;"; // #17
+    Utf8 "SourceFile"; // #18
+    Utf8 "TestMismatchHandlingGenerator.java"; // #19
+  } // Constant Pool
+
+  0x0020; // access
+  #12;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#14) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#15) { // LineNumberTable
+              [] { // line_number_table
+                0  112;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #16; // name_index
+      #17; // descriptor_index
+      [] { // Attributes
+        Attr(#14) { // Code
+          1; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x2BB600072BB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#15) { // LineNumberTable
+              [] { // line_number_table
+                0  114;
+                4  115;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#18) { // SourceFile
+      #19;
+    } // end SourceFile
+  } // Attributes
+} // end class A
+class C {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "B"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #6; // #9
+    Utf8 "MyValue1"; // #10
+    Utf8 "verify"; // #11
+    class #13; // #12
+    Utf8 "C"; // #13
+    Utf8 "Code"; // #14
+    Utf8 "LineNumberTable"; // #15
+    Utf8 "method"; // #16
+    Utf8 "(LMyValue1;)LMyValue1;"; // #17
+    Utf8 "SourceFile"; // #18
+    Utf8 "TestMismatchHandlingGenerator.java"; // #19
+  } // Constant Pool
+
+  0x0020; // access
+  #12;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#14) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#15) { // LineNumberTable
+              [] { // line_number_table
+                0  127;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #16; // name_index
+      #17; // descriptor_index
+      [] { // Attributes
+        Attr(#14) { // Code
+          1; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x2BB600072BB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#15) { // LineNumberTable
+              [] { // line_number_table
+                0  130;
+                4  131;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#18) { // SourceFile
+      #19;
+    } // end SourceFile
+  } // Attributes
+} // end class C
+class I1 {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1
+    Utf8 "I1"; // #2
+    class #4; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "method"; // #5
+    Utf8 "(LMyValue2;)LMyValue2;"; // #6
+    Utf8 "SourceFile"; // #7
+    Utf8 "TestMismatchHandlingGenerator.java"; // #8
+  } // Constant Pool
+
+  0x0600; // access
+  #1;// this_cpx
+  #3;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0401; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#7) { // SourceFile
+      #8;
+    } // end SourceFile
+  } // Attributes
+} // end class I1
+class I2 {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1
+    Utf8 "I2"; // #2
+    class #4; // #3
+    Utf8 "java/lang/Object"; // #4
+    class #6; // #5
+    Utf8 "I1"; // #6
+    Utf8 "method"; // #7
+    Utf8 "(LMyValue2;)LMyValue2;"; // #8
+    Utf8 "SourceFile"; // #9
+    Utf8 "TestMismatchHandlingGenerator.java"; // #10
+  } // Constant Pool
+
+  0x0600; // access
+  #1;// this_cpx
+  #3;// super_cpx
+
+  [] { // Interfaces
+    #5;
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0401; // access
+      #7; // name_index
+      #8; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#9) { // SourceFile
+      #10;
+    } // end SourceFile
+  } // Attributes
+} // end class I2
+file "D.class" {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #6; // #9
+    Utf8 "MyValue2"; // #10
+    Utf8 "verify"; // #11
+    class #13; // #12
+    Utf8 "D"; // #13
+    class #15; // #14
+    Utf8 "I2"; // #15
+    Utf8 "Code"; // #16
+    Utf8 "LineNumberTable"; // #17
+    Utf8 "method"; // #18
+    Utf8 "(LMyValue2;)LMyValue2;"; // #19
+    Utf8 "SourceFile"; // #20
+    Utf8 "TestMismatchHandlingGenerator.java"; // #21
+  } // Constant Pool
+
+  0x0020; // access
+  #12;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+    #14;
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#16) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#17) { // LineNumberTable
+              [] { // line_number_table
+                0  152;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #18; // name_index
+      #19; // descriptor_index
+      [] { // Attributes
+        Attr(#16) { // Code
+          1; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x2BB600072BB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#17) { // LineNumberTable
+              [] { // line_number_table
+                0  155;
+                4  156;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#20) { // SourceFile
+      #21;
+    } // end SourceFile
+  } // Attributes
+} // end class D
+file "F.class" {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #6; // #9
+    Utf8 "MyValue2"; // #10
+    Utf8 "verify"; // #11
+    class #13; // #12
+    Utf8 "F"; // #13
+    class #15; // #14
+    Utf8 "I2"; // #15
+    class #17; // #16
+    Utf8 "I4"; // #17
+    Utf8 "Code"; // #18
+    Utf8 "LineNumberTable"; // #19
+    Utf8 "method"; // #20
+    Utf8 "(LMyValue2;)LMyValue2;"; // #21
+    Utf8 "SourceFile"; // #22
+    Utf8 "TestMismatchHandlingGenerator.java"; // #23
+  } // Constant Pool
+
+  0x0020; // access
+  #12;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+    #14;
+    #16;
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#18) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#19) { // LineNumberTable
+              [] { // line_number_table
+                0  169;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #20; // name_index
+      #21; // descriptor_index
+      [] { // Attributes
+        Attr(#18) { // Code
+          1; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x2BB600072BB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#19) { // LineNumberTable
+              [] { // line_number_table
+                0  172;
+                4  173;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#22) { // SourceFile
+      #23;
+    } // end SourceFile
+  } // Attributes
+} // end class F
+class H {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #6; // #9
+    Utf8 "MyValue3"; // #10
+    Utf8 "verify"; // #11
+    class #13; // #12
+    Utf8 "H"; // #13
+    class #15; // #14
+    Utf8 "I5"; // #15
+    Utf8 "Code"; // #16
+    Utf8 "LineNumberTable"; // #17
+    Utf8 "method"; // #18
+    Utf8 "(LMyValue3;)LMyValue3;"; // #19
+    Utf8 "SourceFile"; // #20
+    Utf8 "TestMismatchHandlingGenerator.java"; // #21
+  } // Constant Pool
+
+  0x0020; // access
+  #12;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+    #14;
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#16) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#17) { // LineNumberTable
+              [] { // line_number_table
+                0  189;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #18; // name_index
+      #19; // descriptor_index
+      [] { // Attributes
+        Attr(#16) { // Code
+          1; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x2BB600072BB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#17) { // LineNumberTable
+              [] { // line_number_table
+                0  192;
+                4  193;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#20) { // SourceFile
+      #21;
+    } // end SourceFile
+  } // Attributes
+} // end class H
+class I5 {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1
+    Utf8 "I5"; // #2
+    class #4; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "method"; // #5
+    Utf8 "(LMyValue3;)LMyValue3;"; // #6
+    Utf8 "SourceFile"; // #7
+    Utf8 "TestMismatchHandlingGenerator.java"; // #8
+  } // Constant Pool
+
+  0x0600; // access
+  #1;// this_cpx
+  #3;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0401; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#7) { // SourceFile
+      #8;
+    } // end SourceFile
+  } // Attributes
+} // end class I5
+class M {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Field #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #12; // #9
+    Utf8 "M"; // #10
+    Utf8 "val"; // #11
+    Utf8 "I"; // #12
+    Method #14 #15; // #13
+    class #16; // #14
+    NameAndType #17 #18; // #15
+    Utf8 "MyValue4"; // #16
+    Utf8 "make"; // #17
+    Utf8 "()LMyValue4;"; // #18
+    Utf8 "Code"; // #19
+    Utf8 "LineNumberTable"; // #20
+    Utf8 "method"; // #21
+    Utf8 "(Z)LMyValue4;"; // #22
+    Utf8 "StackMapTable"; // #23
+    Utf8 "SourceFile"; // #24
+    Utf8 "TestMismatchHandlingGenerator.java"; // #25
+  } // Constant Pool
+
+  0x0020; // access
+  #8;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0000; // access
+      #11; // name_index
+      #12; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#19) { // Code
+          2; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB700012A03B500;
+            0x07B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#20) { // LineNumberTable
+              [] { // line_number_table
+                0  220;
+                4  221;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #21; // name_index
+      #22; // descriptor_index
+      [] { // Attributes
+        Attr(#19) { // Code
+          3; // max_stack
+          4; // max_locals
+          Bytes[]{
+            0x1B99000501B0B800;
+            0x0D4D033E1D100AA2;
+            0x00132A59B4000704;
+            0x60B50007840301A7;
+            0xFFED2CB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#20) { // LineNumberTable
+              [] { // line_number_table
+                0  224;
+                4  225;
+                6  227;
+                10  229;
+                18  230;
+                28  229;
+                34  232;
+              }
+            } // end LineNumberTable
+            ;
+            Attr(#23) { // StackMapTable
+              [] { // 
+                6b; // same_frame
+                253b, 5, []z{O,14; I}; // append_frame 2
+                250b, 21; // chop_frame 1
+              }
+            } // end StackMapTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#24) { // SourceFile
+      #25;
+    } // end SourceFile
+  } // Attributes
+} // end class M
+file "N.class" {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #12; // #9
+    Utf8 "MyValue5"; // #10
+    Utf8 "make"; // #11
+    Utf8 "()LMyValue5;"; // #12
+    class #14; // #13
+    Utf8 "N"; // #14
+    Utf8 "Code"; // #15
+    Utf8 "LineNumberTable"; // #16
+    Utf8 "method"; // #17
+    Utf8 "(Z)LMyValue5;"; // #18
+    Utf8 "StackMapTable"; // #19
+    Utf8 "SourceFile"; // #20
+    Utf8 "TestMismatchHandlingGenerator.java"; // #21
+  } // Constant Pool
+
+  0x0020; // access
+  #13;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#15) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#16) { // LineNumberTable
+              [] { // line_number_table
+                0  237;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #17; // name_index
+      #18; // descriptor_index
+      [] { // Attributes
+        Attr(#15) { // Code
+          1; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x1B99000501B0B800;
+            0x07B0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#16) { // LineNumberTable
+              [] { // line_number_table
+                0  239;
+                4  240;
+                6  242;
+              }
+            } // end LineNumberTable
+            ;
+            Attr(#19) { // StackMapTable
+              [] { // 
+                6b; // same_frame
+              }
+            } // end StackMapTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#20) { // SourceFile
+      #21;
+    } // end SourceFile
+  } // Attributes
+} // end class N
+file "O.class" {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #12; // #9
+    Utf8 "MyValue6"; // #10
+    Utf8 "make"; // #11
+    Utf8 "()LMyValue6;"; // #12
+    class #14; // #13
+    Utf8 "O"; // #14
+    Utf8 "Code"; // #15
+    Utf8 "LineNumberTable"; // #16
+    Utf8 "method"; // #17
+    Utf8 "(Z)LMyValue6;"; // #18
+    Utf8 "StackMapTable"; // #19
+    Utf8 "SourceFile"; // #20
+    Utf8 "TestMismatchHandlingGenerator.java"; // #21
+  } // Constant Pool
+
+  0x0020; // access
+  #13;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#15) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#16) { // LineNumberTable
+              [] { // line_number_table
+                0  247;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #17; // name_index
+      #18; // descriptor_index
+      [] { // Attributes
+        Attr(#15) { // Code
+          1; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x1B99000501B0B800;
+            0x07B0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#16) { // LineNumberTable
+              [] { // line_number_table
+                0  249;
+                4  250;
+                6  252;
+              }
+            } // end LineNumberTable
+            ;
+            Attr(#19) { // StackMapTable
+              [] { // 
+                6b; // same_frame
+              }
+            } // end StackMapTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#20) { // SourceFile
+      #21;
+    } // end SourceFile
+  } // Attributes
+} // end class O
+class I6 {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1
+    Utf8 "I6"; // #2
+    class #4; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "method"; // #5
+    Utf8 "(LMyValue7;)LMyValue7;"; // #6
+    Utf8 "Code"; // #7
+    Utf8 "LineNumberTable"; // #8
+    Utf8 "SourceFile"; // #9
+    Utf8 "TestMismatchHandlingGenerator.java"; // #10
+  } // Constant Pool
+
+  0x0600; // access
+  #1;// this_cpx
+  #3;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0001; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#7) { // Code
+          1; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x01B0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#8) { // LineNumberTable
+              [] { // line_number_table
+                0  259;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#9) { // SourceFile
+      #10;
+    } // end SourceFile
+  } // Attributes
+} // end class I6
+class P {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #6; // #9
+    Utf8 "MyValue7"; // #10
+    Utf8 "verify"; // #11
+    class #13; // #12
+    Utf8 "P"; // #13
+    class #15; // #14
+    Utf8 "I6"; // #15
+    Utf8 "Code"; // #16
+    Utf8 "LineNumberTable"; // #17
+    Utf8 "method"; // #18
+    Utf8 "(LMyValue7;)LMyValue7;"; // #19
+    Utf8 "SourceFile"; // #20
+    Utf8 "TestMismatchHandlingGenerator.java"; // #21
+  } // Constant Pool
+
+  0x0020; // access
+  #12;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+    #14;
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#16) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#17) { // LineNumberTable
+              [] { // line_number_table
+                0  263;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0001; // access
+      #18; // name_index
+      #19; // descriptor_index
+      [] { // Attributes
+        Attr(#16) { // Code
+          1; // max_stack
+          2; // max_locals
+          Bytes[]{
+            0x2BB600072BB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#17) { // LineNumberTable
+              [] { // line_number_table
+                0  266;
+                4  267;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#20) { // SourceFile
+      #21;
+    } // end SourceFile
+  } // Attributes
+} // end class P
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMismatchHandlingGenerator.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMismatchHandlingGenerator.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMismatchHandlingGenerator.java
@@ -0,0 +1,424 @@
+/*
+ * 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.
+ */
+
+value class MyValue1 {
+    int x = 42;
+
+    void verify() {
+        if (x != 42) {
+            throw new RuntimeException("Verification failed");
+        }
+    }
+}
+
+value class MyValue2 {
+    int x = 42;
+
+    void verify() {
+        if (x != 42) {
+            throw new RuntimeException("Verification failed");
+        }
+    }
+}
+
+value class MyValue3 {
+    int x = 42;
+
+    void verify() {
+        if (x != 42) {
+            throw new RuntimeException("Verification failed");
+        }
+    }
+}
+
+value class MyValue4 {
+    int x = 42;
+
+    public void verify() {
+        if (x != 42) {
+            throw new RuntimeException("Verification failed");
+        }
+    }
+
+    static MyValue4 make() {
+        return new MyValue4();
+    }
+}
+
+interface Verifiable {
+    public void verify();
+}
+
+value class MyValue5 implements Verifiable {
+    int x = 42;
+
+    @Override
+    public void verify() {
+        if (x != 42) {
+            throw new RuntimeException("Verification failed");
+        }
+    }
+
+    static MyValue5 make() {
+        return new MyValue5();
+    }
+}
+
+value class MyValue6 implements Verifiable {
+    int x = 42;
+
+    @Override
+    public void verify() {
+        if (x != 42) {
+            throw new RuntimeException("Verification failed");
+        }
+    }
+
+    static MyValue6 make() {
+        return new MyValue6();
+    }
+}
+
+value class MyValue7 {
+    int x = 42;
+
+    void verify() {
+        if (x != 42) {
+            throw new RuntimeException("Verification failed");
+        }
+    }
+}
+
+class A {
+    public MyValue1 method(MyValue1 arg) {
+        arg.verify();
+        return arg;
+    }
+}
+
+class B extends A {
+    @Override
+    public MyValue1 method(MyValue1 arg) {
+        arg.verify();
+        return arg;
+    }
+}
+
+class C extends B {
+    @Override
+    public MyValue1 method(MyValue1 arg) {
+        arg.verify();
+        return arg;
+    }
+}
+
+interface I1 {
+    public MyValue2 method(MyValue2 arg);
+}
+
+interface I2 extends I1 {
+    public MyValue2 method(MyValue2 arg);
+}
+
+interface I3 {
+    public MyValue2 method(MyValue2 arg);
+}
+
+interface I4 extends I3 {
+    public MyValue2 method(MyValue2 arg);
+}
+
+
+class D implements I2 {
+    @Override
+    public MyValue2 method(MyValue2 arg) {
+        arg.verify();
+        return arg;
+    }
+}
+
+class E implements I4 {
+    @Override
+    public MyValue2 method(MyValue2 arg) {
+        arg.verify();
+        return arg;
+    }
+}
+
+
+class F implements I2, I4 {
+    @Override
+    public MyValue2 method(MyValue2 arg) {
+        arg.verify();
+        return arg;
+    }
+}
+
+class G implements I2, I4 {
+    @Override
+    public MyValue2 method(MyValue2 arg) {
+        arg.verify();
+        return arg;
+    }
+}
+
+interface I5 {
+    public MyValue3 method(MyValue3 arg);
+}
+
+class H implements I5 {
+    @Override
+    public MyValue3 method(MyValue3 arg) {
+        arg.verify();
+        return arg;
+    }
+}
+
+class J {
+    public MyValue3 method(MyValue3 arg) {
+        arg.verify();
+        return arg;
+    }
+}
+
+class K extends J {
+    @Override
+    public MyValue3 method(MyValue3 arg) {
+        arg.verify();
+        return arg;
+    }
+}
+
+class L extends K implements I5 {
+    @Override
+    public MyValue3 method(MyValue3 arg) {
+        arg.verify();
+        return arg;
+    }
+}
+
+class M {
+    int val = 0;
+
+    public MyValue4 method(boolean warmup) {
+        if (warmup) {
+            return null;
+        } else {
+            MyValue4 res = MyValue4.make();
+            // Do something here to "corrupt" registers
+            for (int i = 0; i < 10; ++i) {
+                val++;
+            }
+            return res;
+        }
+    }
+}
+
+class N {
+    public MyValue5 method(boolean warmup) {
+        if (warmup) {
+            return null;
+        } else {
+            return MyValue5.make();
+        }
+    }
+}
+
+class O {
+    public MyValue6 method(boolean warmup) {
+        if (warmup) {
+            return null;
+        } else {
+            return MyValue6.make();
+        }
+    }
+}
+
+interface I6 {
+    default MyValue7 method(MyValue7 arg) {
+        return null;
+    }
+}
+
+class P implements I6 {
+    @Override
+    public MyValue7 method(MyValue7 arg) {
+        arg.verify();
+        return arg;
+    }
+}
+
+class Q {
+    MyValue7 method(MyValue7 arg) {
+        arg.verify();
+        return arg;
+    }
+}
+
+class R extends Q {
+    @Override
+    MyValue7 method(MyValue7 arg) {
+        arg.verify();
+        return arg;
+    }
+}
+
+class S extends R implements I6 {
+    @Override
+    public MyValue7 method(MyValue7 arg) {
+        arg.verify();
+        return arg;
+    }
+}
+
+class TestMismatchHandlingHelper {
+    // * = has preload attribute for MyValue*
+
+    // With C <: B* <: A
+    public static void test1(A a1, A a2, A a3, A a4, A a5, B b1, B b2, C c) {
+        // Non-scalarized virtual call site, mismatching on B
+        a1.method(new MyValue1()).verify();
+        a2.method(new MyValue1()).verify();
+        a3.method(new MyValue1()).verify();
+        a4.method(new MyValue1()).verify();
+        a5.method(new MyValue1()).verify();
+        // Scalarized virtual call sites, mismatching on C
+        b1.method(new MyValue1()).verify();
+        b2.method(new MyValue1()).verify();
+        c.method(new MyValue1()).verify();
+    }
+
+    // D  <: I2  <: I1
+    // E* <: I4* <: I3*
+    // Loaded later, combine both hierachies and introduce a mismatch:
+    // F  <: I2, I4*
+    // G* <: I2, I4*
+    public static void test2(I1 i11, I1 i12, I1 i13, I1 i14, I1 i15, I1 i16, I2 i21, I2 i22, I2 i23, I2 i24, I2 i25, I2 i26, I3 i31, I3 i32, I3 i33, I3 i34, I3 i35, I3 i36, I4 i41, I4 i42, I4 i43, I4 i44, I4 i45, I4 i46, D d, E e) {
+        // Non-scalarized virtual call sites, mismatching on E
+        i11.method(new MyValue2()).verify();
+        i12.method(new MyValue2()).verify();
+        i13.method(new MyValue2()).verify();
+        i14.method(new MyValue2()).verify();
+        i15.method(new MyValue2()).verify();
+        i16.method(new MyValue2()).verify();
+        i21.method(new MyValue2()).verify();
+        i22.method(new MyValue2()).verify();
+        i23.method(new MyValue2()).verify();
+        i24.method(new MyValue2()).verify();
+        i25.method(new MyValue2()).verify();
+        i26.method(new MyValue2()).verify();
+        d.method(new MyValue2()).verify();
+        // Scalarized virtual call sites, mismatching on D
+        i31.method(new MyValue2()).verify();
+        i32.method(new MyValue2()).verify();
+        i33.method(new MyValue2()).verify();
+        i34.method(new MyValue2()).verify();
+        i35.method(new MyValue2()).verify();
+        i36.method(new MyValue2()).verify();
+        i41.method(new MyValue2()).verify();
+        i42.method(new MyValue2()).verify();
+        i43.method(new MyValue2()).verify();
+        i44.method(new MyValue2()).verify();
+        i45.method(new MyValue2()).verify();
+        i46.method(new MyValue2()).verify();
+        e.method(new MyValue2()).verify();
+    }
+
+    // H  <: I5
+    // K* <: J*
+    // Loaded later, combines both hierachies and introduces a mismatch:
+    // L* <: K*, I5
+    public static void test3(I5 i51, I5 i52, I5 i53, J j1, J j2, J j3, J j4, J j5, H h, K k) {
+        // Non-scalarized virtual call sites, mismatching on L
+        i51.method(new MyValue3()).verify();
+        i52.method(new MyValue3()).verify();
+        i53.method(new MyValue3()).verify();
+        h.method(new MyValue3()).verify();
+        // Scalarized virtual call sites
+        j1.method(new MyValue3()).verify();
+        j2.method(new MyValue3()).verify();
+        j3.method(new MyValue3()).verify();
+        j4.method(new MyValue3()).verify();
+        j5.method(new MyValue3()).verify();
+        k.method(new MyValue3()).verify();
+    }
+
+    // Test that a C1 compiled method returns in scalarized form if the method holder class M
+    // is loaded but the value class return type is not due to a missing preload attribute.
+    public static void test4(M m, boolean warmup) {
+        if (warmup) {
+            m.method(warmup);
+        } else {
+            if (m.method(warmup).x != 42) {
+                throw new RuntimeException("Verification failed");
+            }
+        }
+    }
+
+    // Test that C1 correctly handles scalarized returns at calls if the method holder class N
+    // is loaded but the value class return type is not due to a missing preload attribute.
+    public static void test5(N n, boolean warmup) {
+        Verifiable res = n.method(warmup);
+        if (!warmup) {
+            res.verify();
+        }
+    }
+
+    // Test direct calls
+    public static void test6(F f, G g, L l) {
+        f.method(new MyValue2());
+        g.method(new MyValue2());
+        l.method(new MyValue3());
+    }
+
+    // Test scalarized return from C2 compiled callee to C2 compiled caller with an unloaded
+    // return type at caller compile time due to a missing preload attribute.
+    public static Verifiable test7(O o, boolean warmup) {
+        return o.method(warmup);
+    }
+
+    public static void test7TriggerCalleeCompilation(O o) {
+        o.method(true);
+        o.method(false).verify();
+    }
+
+    // Same as test3 but with default method in interface and package private methods
+    // P  <: I6
+    // R* <: Q*
+    // Loaded later, combines both hierachies and introduces a mismatch:
+    // S* <: R*, I6
+    public static void test8(I6 i61, I6 i62, I6 i63, Q q1, Q q2, Q q3, Q q4, Q q5, P p, R r) {
+        // Non-scalarized virtual call sites, mismatching on S
+        i61.method(new MyValue7()).verify();
+        i62.method(new MyValue7()).verify();
+        i63.method(new MyValue7()).verify();
+        p.method(new MyValue7()).verify();
+        // Scalarized virtual call sites
+        q1.method(new MyValue7()).verify();
+        q2.method(new MyValue7()).verify();
+        q3.method(new MyValue7()).verify();
+        q4.method(new MyValue7()).verify();
+        q5.method(new MyValue7()).verify();
+        r.method(new MyValue7()).verify();
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNativeClone.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNativeClone.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNativeClone.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ *
+ * 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 8209702
+ * @summary Verify that the native clone intrinsic handles value objects.
+ * @library /test/lib
+ * @modules java.base/java.lang:+open
+ * @enablePreview
+ * @run main/othervm -Xbatch -XX:-UseTypeProfile
+ *                   -XX:CompileCommand=compileonly,compiler.valhalla.inlinetypes.MyValue::*
+ *                   -XX:CompileCommand=compileonly,compiler.valhalla.inlinetypes.TestNativeClone::test*
+ *                   -XX:CompileCommand=compileonly,jdk.internal.reflect.GeneratedMethodAccessor1::invoke
+ *                   -XX:CompileCommand=dontinline,jdk.internal.reflect.GeneratedMethodAccessor1::invoke
+ *                   compiler.valhalla.inlinetypes.TestNativeClone
+ */
+
+package compiler.valhalla.inlinetypes;
+
+import java.lang.invoke.*;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import jdk.test.lib.Asserts;
+
+value class MyValue {
+    public int x;
+
+    public MyValue(int x) {
+        this.x = x;
+    }
+}
+
+public class TestNativeClone {
+
+    public static void test1(Method clone, Object obj) {
+        try {
+            clone.invoke(obj);
+        } catch (InvocationTargetException e) {
+            // Expected
+            Asserts.assertTrue(e.getCause() instanceof CloneNotSupportedException, "Unexpected exception thrown");
+            return;
+        } catch (Exception e) {
+            throw new RuntimeException("Unexpected exception thrown", e);
+        }
+        throw new RuntimeException("No exception thrown");
+    }
+
+    public static void main(String[] args) throws Throwable {
+        MyValue vt = new MyValue(42);
+        Method clone = Object.class.getDeclaredMethod("clone");
+        clone.setAccessible(true);
+        for (int i = 0; i < 20_000; ++i) {
+            test1(clone, vt);
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNestmateAccess.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNestmateAccess.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNestmateAccess.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2020, 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 compiler.valhalla.inlinetypes;
+
+import jdk.test.lib.Asserts;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+
+/**
+ * @test
+ * @bug 8253416
+ * @summary Test nestmate access to flattened field if nest-host is not loaded.
+ * @library /test/lib
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm -Xcomp
+ *                   -XX:CompileCommand=compileonly,compiler.valhalla.inlinetypes.Test*::<init>
+ *                   compiler.valhalla.inlinetypes.TestNestmateAccess
+ * @run main/othervm -Xcomp -XX:TieredStopAtLevel=1
+ *                   -XX:CompileCommand=compileonly,compiler.valhalla.inlinetypes.Test*::<init>
+ *                   compiler.valhalla.inlinetypes.TestNestmateAccess
+ * @run main/othervm compiler.valhalla.inlinetypes.TestNestmateAccess
+ */
+
+interface MyInterface {
+    int hash();
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue implements MyInterface {
+    int x = 42;
+    int y = 43;
+
+    @Override
+    public int hash() { return x + y; }
+}
+
+// Test load from flattened field in nestmate when nest-host is not loaded.
+class Test1 {
+    @NullRestricted
+    private MyValue vt;
+
+    public Test1(final MyValue vt) {
+        this.vt = vt;
+    }
+
+    public MyInterface test() {
+        return new MyInterface() {
+            // The vt field load does not link.
+            private int x = (Test1.this).vt.hash();
+
+            @Override
+            public int hash() { return x; }
+        };
+    }
+}
+
+// Same as Test1 but outer class is a value class
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class Test2 {
+    @NullRestricted
+    private MyValue vt;
+
+    public Test2(final MyValue vt) {
+        this.vt = vt;
+    }
+
+    public MyInterface test() {
+        return new MyInterface() {
+            // Delayed flattened load of Test2.this.
+            // The vt field load does not link.
+            private int x = (Test2.this).vt.hash();
+
+            @Override
+            public int hash() { return x; }
+        };
+    }
+}
+
+// Test store to flattened field in nestmate when nest-host is not loaded.
+class Test3 {
+    @NullRestricted
+    private MyValue vt;
+
+    public MyInterface test(MyValue init) {
+        return new MyInterface() {
+            // Store to the vt field does not link.
+            private MyValue tmp = (vt = init);
+
+            @Override
+            public int hash() { return tmp.hash() + vt.hash(); }
+        };
+    }
+}
+
+// Same as Test1 but with static field
+class Test4 {
+    @NullRestricted
+    private static MyValue vt;
+
+    public Test4(final MyValue vt) {
+        this.vt = vt;
+    }
+
+    public MyInterface test() {
+        return new MyInterface() {
+            // The vt field load does not link.
+            private int x = (Test4.this).vt.hash();
+
+            @Override
+            public int hash() { return x; }
+        };
+    }
+}
+
+// Same as Test2 but with static field
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class Test5 {
+    @NullRestricted
+    private static MyValue vt;
+
+    public Test5(final MyValue vt) {
+        this.vt = vt;
+    }
+
+    public MyInterface test() {
+        return new MyInterface() {
+            // Delayed flattened load of Test5.this.
+            // The vt field load does not link.
+            private int x = (Test5.this).vt.hash();
+
+            @Override
+            public int hash() { return x; }
+        };
+    }
+}
+
+// Same as Test3 but with static field
+class Test6 {
+    @NullRestricted
+    private static MyValue vt;
+
+    public MyInterface test(MyValue init) {
+        return new MyInterface() {
+            // Store to the vt field does not link.
+            private MyValue tmp = (vt = init);
+
+            @Override
+            public int hash() { return tmp.hash() + vt.hash(); }
+        };
+    }
+}
+
+// Same as Test6 but outer class is a value class
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class Test7 {
+    @NullRestricted
+    private static MyValue vt;
+
+    public MyInterface test(MyValue init) {
+        return new MyInterface() {
+            // Store to the vt field does not link.
+            private MyValue tmp = (vt = init);
+
+            @Override
+            public int hash() { return tmp.hash() + vt.hash(); }
+        };
+    }
+}
+
+public class TestNestmateAccess {
+
+    public static void main(String[] args) {
+        Test1 t1 = new Test1(new MyValue());
+        int res = t1.test().hash();
+        Asserts.assertEQ(res, 85);
+
+        Test2 t2 = new Test2(new MyValue());
+        res = t2.test().hash();
+        Asserts.assertEQ(res, 85);
+
+        Test3 t3 = new Test3();
+        res = t3.test(new MyValue()).hash();
+        Asserts.assertEQ(res, 170);
+
+        Test4 t4 = new Test4(new MyValue());
+        res = t4.test().hash();
+        Asserts.assertEQ(res, 85);
+
+        Test5 t5 = new Test5(new MyValue());
+        res = t5.test().hash();
+        Asserts.assertEQ(res, 85);
+
+        Test6 t6 = new Test6();
+        res = t6.test(new MyValue()).hash();
+        Asserts.assertEQ(res, 170);
+
+        Test7 t7 = new Test7();
+        res = t7.test(new MyValue()).hash();
+        Asserts.assertEQ(res, 170);
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNewAcmp.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNewAcmp.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNewAcmp.java
@@ -0,0 +1,1911 @@
+/*
+ * 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.
+ *
+ * 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 TestNewAcmp
+ * @summary Verifies correctness of the acmp bytecode with value object operands.
+ * @library /testlibrary /test/lib /compiler/whitebox /
+ * @enablePreview
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm/timeout=300 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                               compiler.valhalla.inlinetypes.TestNewAcmp
+ */
+
+package compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.CompLevel;
+import compiler.lib.ir_framework.TestFramework;
+import jdk.test.lib.Asserts;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.invoke.*;
+import java.lang.reflect.Method;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import jdk.test.whitebox.WhiteBox;
+
+interface MyInterface {
+
+}
+
+abstract value class MyAbstract implements MyInterface {
+
+}
+
+value class MyValue1 extends MyAbstract {
+    int x;
+
+    MyValue1(int x) {
+        this.x = x;
+    }
+
+    static MyValue1 createDefault() {
+        return new MyValue1(0);
+    }
+
+    static MyValue1 setX(MyValue1 v, int x) {
+        return new MyValue1(x);
+    }
+}
+
+value class MyValue2 extends MyAbstract {
+    int x;
+
+    MyValue2(int x) {
+        this.x = x;
+    }
+
+    static MyValue2 createDefault() {
+        return new MyValue2(0);
+    }
+
+    static MyValue2 setX(MyValue2 v, int x) {
+        return new MyValue2(x);
+    }
+}
+
+class MyObject extends MyAbstract {
+    int x;
+}
+
+// Mark test methods that return false if the argument is null
+@Retention(RetentionPolicy.RUNTIME)
+@interface FalseIfNull { }
+
+// Mark test methods that return true if the argument is null
+@Retention(RetentionPolicy.RUNTIME)
+@interface TrueIfNull { }
+
+public class TestNewAcmp {
+
+    public boolean testEq01_1(Object u1, Object u2) {
+        return get(u1) == u2; // new acmp
+    }
+
+    public boolean testEq01_2(Object u1, Object u2) {
+        return u1 == get(u2); // new acmp
+    }
+
+    public boolean testEq01_3(Object u1, Object u2) {
+        return get(u1) == get(u2); // new acmp
+    }
+
+    @FalseIfNull
+    public boolean testEq01_4(Object u1, Object u2) {
+        return getNotNull(u1) == u2; // new acmp without null check
+    }
+
+    @FalseIfNull
+    public boolean testEq01_5(Object u1, Object u2) {
+        return u1 == getNotNull(u2); // new acmp without null check
+    }
+
+    @FalseIfNull
+    public boolean testEq01_6(Object u1, Object u2) {
+        return getNotNull(u1) == getNotNull(u2); // new acmp without null check
+    }
+
+    public boolean testEq02_1(MyValue1 v1, MyValue1 v2) {
+        return get(v1) == (Object)v2; // only true if both null
+    }
+
+    public boolean testEq02_2(MyValue1 v1, MyValue1 v2) {
+        return (Object)v1 == get(v2); // only true if both null
+    }
+
+    public boolean testEq02_3(MyValue1 v1, MyValue1 v2) {
+        return get(v1) == get(v2); // only true if both null
+    }
+
+    public boolean testEq03_1(MyValue1 v, Object u) {
+        return get(v) == u; // only true if both null
+    }
+
+    public boolean testEq03_2(MyValue1 v, Object u) {
+        return (Object)v == get(u); // only true if both null
+    }
+
+    public boolean testEq03_3(MyValue1 v, Object u) {
+        return get(v) == get(u); // only true if both null
+    }
+
+    public boolean testEq04_1(Object u, MyValue1 v) {
+        return get(u) == (Object)v; // only true if both null
+    }
+
+    public boolean testEq04_2(Object u, MyValue1 v) {
+        return u == get(v); // only true if both null
+    }
+
+    public boolean testEq04_3(Object u, MyValue1 v) {
+        return get(u) == get(v); // only true if both null
+    }
+
+    public boolean testEq05_1(MyObject o, MyValue1 v) {
+        return get(o) == (Object)v; // only true if both null
+    }
+
+    public boolean testEq05_2(MyObject o, MyValue1 v) {
+        return o == get(v); // only true if both null
+    }
+
+    public boolean testEq05_3(MyObject o, MyValue1 v) {
+        return get(o) == get(v); // only true if both null
+    }
+
+    public boolean testEq06_1(MyValue1 v, MyObject o) {
+        return get(v) == o; // only true if both null
+    }
+
+    public boolean testEq06_2(MyValue1 v, MyObject o) {
+        return (Object)v == get(o); // only true if both null
+    }
+
+    public boolean testEq06_3(MyValue1 v, MyObject o) {
+        return get(v) == get(o); // only true if both null
+    }
+
+    public boolean testEq07_1(MyValue1 v1, MyValue1 v2) {
+        return getNotNull(v1) == (Object)v2; // false
+    }
+
+    public boolean testEq07_2(MyValue1 v1, MyValue1 v2) {
+        return (Object)v1 == getNotNull(v2); // false
+    }
+
+    public boolean testEq07_3(MyValue1 v1, MyValue1 v2) {
+        return getNotNull(v1) == getNotNull(v2); // false
+    }
+
+    public boolean testEq08_1(MyValue1 v, Object u) {
+        return getNotNull(v) == u; // false
+    }
+
+    public boolean testEq08_2(MyValue1 v, Object u) {
+        return (Object)v == getNotNull(u); // false
+    }
+
+    public boolean testEq08_3(MyValue1 v, Object u) {
+        return getNotNull(v) == getNotNull(u); // false
+    }
+
+    public boolean testEq09_1(Object u, MyValue1 v) {
+        return getNotNull(u) == (Object)v; // false
+    }
+
+    public boolean testEq09_2(Object u, MyValue1 v) {
+        return u == getNotNull(v); // false
+    }
+
+    public boolean testEq09_3(Object u, MyValue1 v) {
+        return getNotNull(u) == getNotNull(v); // false
+    }
+
+    public boolean testEq10_1(MyObject o, MyValue1 v) {
+        return getNotNull(o) == (Object)v; // false
+    }
+
+    public boolean testEq10_2(MyObject o, MyValue1 v) {
+        return o == getNotNull(v); // false
+    }
+
+    public boolean testEq10_3(MyObject o, MyValue1 v) {
+        return getNotNull(o) == getNotNull(v); // false
+    }
+
+    public boolean testEq11_1(MyValue1 v, MyObject o) {
+        return getNotNull(v) == o; // false
+    }
+
+    public boolean testEq11_2(MyValue1 v, MyObject o) {
+        return (Object)v == getNotNull(o); // false
+    }
+
+    public boolean testEq11_3(MyValue1 v, MyObject o) {
+        return getNotNull(v) == getNotNull(o); // false
+    }
+
+    public boolean testEq12_1(MyObject o1, MyObject o2) {
+        return get(o1) == o2; // old acmp
+    }
+
+    public boolean testEq12_2(MyObject o1, MyObject o2) {
+        return o1 == get(o2); // old acmp
+    }
+
+    public boolean testEq12_3(MyObject o1, MyObject o2) {
+        return get(o1) == get(o2); // old acmp
+    }
+
+    public boolean testEq13_1(Object u, MyObject o) {
+        return get(u) == o; // old acmp
+    }
+
+    public boolean testEq13_2(Object u, MyObject o) {
+        return u == get(o); // old acmp
+    }
+
+    public boolean testEq13_3(Object u, MyObject o) {
+        return get(u) == get(o); // old acmp
+    }
+
+    public boolean testEq14_1(MyObject o, Object u) {
+        return get(o) == u; // old acmp
+    }
+
+    public boolean testEq14_2(MyObject o, Object u) {
+        return o == get(u); // old acmp
+    }
+
+    public boolean testEq14_3(MyObject o, Object u) {
+        return get(o) == get(u); // old acmp
+    }
+
+    public boolean testEq15_1(Object[] a, Object u) {
+        return get(a) == u; // old acmp
+    }
+
+    public boolean testEq15_2(Object[] a, Object u) {
+        return a == get(u); // old acmp
+    }
+
+    public boolean testEq15_3(Object[] a, Object u) {
+        return get(a) == get(u); // old acmp
+    }
+
+    public boolean testEq16_1(Object u, Object[] a) {
+        return get(u) == a; // old acmp
+    }
+
+    public boolean testEq16_2(Object u, Object[] a) {
+        return u == get(a); // old acmp
+    }
+
+    public boolean testEq16_3(Object u, Object[] a) {
+        return get(u) == get(a); // old acmp
+    }
+
+    public boolean testEq17_1(Object[] a, MyValue1 v) {
+        return get(a) == (Object)v; // only true if both null
+    }
+
+    public boolean testEq17_2(Object[] a, MyValue1 v) {
+        return a == get(v); // only true if both null
+    }
+
+    public boolean testEq17_3(Object[] a, MyValue1 v) {
+        return get(a) == get(v); // only true if both null
+    }
+
+    public boolean testEq18_1(MyValue1 v, Object[] a) {
+        return get(v) == a; // only true if both null
+    }
+
+    public boolean testEq18_2(MyValue1 v, Object[] a) {
+        return (Object)v == get(a); // only true if both null
+    }
+
+    public boolean testEq18_3(MyValue1 v, Object[] a) {
+        return get(v) == get(a); // only true if both null
+    }
+
+    public boolean testEq19_1(Object[] a, MyValue1 v) {
+        return getNotNull(a) == (Object)v; // false
+    }
+
+    public boolean testEq19_2(Object[] a, MyValue1 v) {
+        return a == getNotNull(v); // false
+    }
+
+    public boolean testEq19_3(Object[] a, MyValue1 v) {
+        return getNotNull(a) == getNotNull(v); // false
+    }
+
+    public boolean testEq20_1(MyValue1 v, Object[] a) {
+        return getNotNull(v) == a; // false
+    }
+
+    public boolean testEq20_2(MyValue1 v, Object[] a) {
+        return (Object)v == getNotNull(a); // false
+    }
+
+    public boolean testEq20_3(MyValue1 v, Object[] a) {
+        return getNotNull(v) == getNotNull(a); // false
+    }
+
+    public boolean testEq21_1(MyInterface u1, MyInterface u2) {
+        return get(u1) == u2; // new acmp
+    }
+
+    public boolean testEq21_2(MyInterface u1, MyInterface u2) {
+        return u1 == get(u2); // new acmp
+    }
+
+    public boolean testEq21_3(MyInterface u1, MyInterface u2) {
+        return get(u1) == get(u2); // new acmp
+    }
+
+    @FalseIfNull
+    public boolean testEq21_4(MyInterface u1, MyInterface u2) {
+        return getNotNull(u1) == u2; // new acmp without null check
+    }
+
+    @FalseIfNull
+    public boolean testEq21_5(MyInterface u1, MyInterface u2) {
+        return u1 == getNotNull(u2); // new acmp without null check
+    }
+
+    @FalseIfNull
+    public boolean testEq21_6(MyInterface u1, MyInterface u2) {
+        return getNotNull(u1) == getNotNull(u2); // new acmp without null check
+    }
+
+    public boolean testEq21_7(MyAbstract u1, MyAbstract u2) {
+        return get(u1) == u2; // new acmp
+    }
+
+    public boolean testEq21_8(MyAbstract u1, MyAbstract u2) {
+        return u1 == get(u2); // new acmp
+    }
+
+    public boolean testEq21_9(MyAbstract u1, MyAbstract u2) {
+        return get(u1) == get(u2); // new acmp
+    }
+
+    @FalseIfNull
+    public boolean testEq21_10(MyAbstract u1, MyAbstract u2) {
+        return getNotNull(u1) == u2; // new acmp without null check
+    }
+
+    @FalseIfNull
+    public boolean testEq21_11(MyAbstract u1, MyAbstract u2) {
+        return u1 == getNotNull(u2); // new acmp without null check
+    }
+
+    @FalseIfNull
+    public boolean testEq21_12(MyAbstract u1, MyAbstract u2) {
+        return getNotNull(u1) == getNotNull(u2); // new acmp without null check
+    }
+
+    public boolean testEq22_1(MyValue1 v, MyInterface u) {
+        return get(v) == u; // only true if both null
+    }
+
+    public boolean testEq22_2(MyValue1 v, MyInterface u) {
+        return (Object)v == get(u); // only true if both null
+    }
+
+    public boolean testEq22_3(MyValue1 v, MyInterface u) {
+        return get(v) == get(u); // only true if both null
+    }
+
+    public boolean testEq22_4(MyValue1 v, MyAbstract u) {
+        return get(v) == u; // only true if both null
+    }
+
+    public boolean testEq22_5(MyValue1 v, MyAbstract u) {
+        return (Object)v == get(u); // only true if both null
+    }
+
+    public boolean testEq22_6(MyValue1 v, MyAbstract u) {
+        return get(v) == get(u); // only true if both null
+    }
+
+    public boolean testEq23_1(MyInterface u, MyValue1 v) {
+        return get(u) == (Object)v; // only true if both null
+    }
+
+    public boolean testEq23_2(MyInterface u, MyValue1 v) {
+        return u == get(v); // only true if both null
+    }
+
+    public boolean testEq23_3(MyInterface u, MyValue1 v) {
+        return get(u) == get(v); // only true if both null
+    }
+
+    public boolean testEq23_4(MyAbstract u, MyValue1 v) {
+        return get(u) == (Object)v; // only true if both null
+    }
+
+    public boolean testEq23_5(MyAbstract u, MyValue1 v) {
+        return u == get(v); // only true if both null
+    }
+
+    public boolean testEq23_6(MyAbstract u, MyValue1 v) {
+        return get(u) == get(v); // only true if both null
+    }
+
+    public boolean testEq24_1(MyValue1 v, MyInterface u) {
+        return getNotNull(v) == u; // false
+    }
+
+    public boolean testEq24_2(MyValue1 v, MyInterface u) {
+        return (Object)v == getNotNull(u); // false
+    }
+
+    public boolean testEq24_3(MyValue1 v, MyInterface u) {
+        return getNotNull(v) == getNotNull(u); // false
+    }
+
+    public boolean testEq24_4(MyValue1 v, MyAbstract u) {
+        return getNotNull(v) == u; // false
+    }
+
+    public boolean testEq24_5(MyValue1 v, MyAbstract u) {
+        return (Object)v == getNotNull(u); // false
+    }
+
+    public boolean testEq24_6(MyValue1 v, MyAbstract u) {
+        return getNotNull(v) == getNotNull(u); // false
+    }
+
+    public boolean testEq25_1(MyInterface u, MyValue1 v) {
+        return getNotNull(u) == (Object)v; // false
+    }
+
+    public boolean testEq25_2(MyInterface u, MyValue1 v) {
+        return u == getNotNull(v); // false
+    }
+
+    public boolean testEq25_3(MyInterface u, MyValue1 v) {
+        return getNotNull(u) == getNotNull(v); // false
+    }
+
+    public boolean testEq25_4(MyAbstract u, MyValue1 v) {
+        return getNotNull(u) == (Object)v; // false
+    }
+
+    public boolean testEq25_5(MyAbstract u, MyValue1 v) {
+        return u == getNotNull(v); // false
+    }
+
+    public boolean testEq25_6(MyAbstract u, MyValue1 v) {
+        return getNotNull(u) == getNotNull(v); // false
+    }
+
+    public boolean testEq26_1(MyInterface u, MyObject o) {
+        return get(u) == o; // old acmp
+    }
+
+    public boolean testEq26_2(MyInterface u, MyObject o) {
+        return u == get(o); // old acmp
+    }
+
+    public boolean testEq26_3(MyInterface u, MyObject o) {
+        return get(u) == get(o); // old acmp
+    }
+
+    public boolean testEq26_4(MyAbstract u, MyObject o) {
+        return get(u) == o; // old acmp
+    }
+
+    public boolean testEq26_5(MyAbstract u, MyObject o) {
+        return u == get(o); // old acmp
+    }
+
+    public boolean testEq26_6(MyAbstract u, MyObject o) {
+        return get(u) == get(o); // old acmp
+    }
+
+    public boolean testEq27_1(MyObject o, MyInterface u) {
+        return get(o) == u; // old acmp
+    }
+
+    public boolean testEq27_2(MyObject o, MyInterface u) {
+        return o == get(u); // old acmp
+    }
+
+    public boolean testEq27_3(MyObject o, MyInterface u) {
+        return get(o) == get(u); // old acmp
+    }
+
+    public boolean testEq27_4(MyObject o, MyAbstract u) {
+        return get(o) == u; // old acmp
+    }
+
+    public boolean testEq27_5(MyObject o, MyAbstract u) {
+        return o == get(u); // old acmp
+    }
+
+    public boolean testEq27_6(MyObject o, MyAbstract u) {
+        return get(o) == get(u); // old acmp
+    }
+
+    public boolean testEq28_1(MyInterface[] a, MyInterface u) {
+        return get(a) == u; // old acmp
+    }
+
+    public boolean testEq28_2(MyInterface[] a, MyInterface u) {
+        return a == get(u); // old acmp
+    }
+
+    public boolean testEq28_3(MyInterface[] a, MyInterface u) {
+        return get(a) == get(u); // old acmp
+    }
+
+    public boolean testEq28_4(MyAbstract[] a, MyAbstract u) {
+        return get(a) == u; // old acmp
+    }
+
+    public boolean testEq28_5(MyAbstract[] a, MyAbstract u) {
+        return a == get(u); // old acmp
+    }
+
+    public boolean testEq28_6(MyAbstract[] a, MyAbstract u) {
+        return get(a) == get(u); // old acmp
+    }
+
+    public boolean testEq29_1(MyInterface u, MyInterface[] a) {
+        return get(u) == a; // old acmp
+    }
+
+    public boolean testEq29_2(MyInterface u, MyInterface[] a) {
+        return u == get(a); // old acmp
+    }
+
+    public boolean testEq29_3(MyInterface u, MyInterface[] a) {
+        return get(u) == get(a); // old acmp
+    }
+
+    public boolean testEq29_4(MyAbstract u, MyAbstract[] a) {
+        return get(u) == a; // old acmp
+    }
+
+    public boolean testEq29_5(MyAbstract u, MyAbstract[] a) {
+        return u == get(a); // old acmp
+    }
+
+    public boolean testEq29_6(MyAbstract u, MyAbstract[] a) {
+        return get(u) == get(a); // old acmp
+    }
+
+    public boolean testEq30_1(MyInterface[] a, MyValue1 v) {
+        return get(a) == (Object)v; // only true if both null
+    }
+
+    public boolean testEq30_2(MyInterface[] a, MyValue1 v) {
+        return a == get(v); // only true if both null
+    }
+
+    public boolean testEq30_3(MyInterface[] a, MyValue1 v) {
+        return get(a) == get(v); // only true if both null
+    }
+
+    public boolean testEq30_4(MyAbstract[] a, MyValue1 v) {
+        return get(a) == (Object)v; // only true if both null
+    }
+
+    public boolean testEq30_5(MyAbstract[] a, MyValue1 v) {
+        return a == get(v); // only true if both null
+    }
+
+    public boolean testEq30_6(MyAbstract[] a, MyValue1 v) {
+        return get(a) == get(v); // only true if both null
+    }
+
+    public boolean testEq31_1(MyValue1 v, MyInterface[] a) {
+        return get(v) == a; // only true if both null
+    }
+
+    public boolean testEq31_2(MyValue1 v, MyInterface[] a) {
+        return (Object)v == get(a); // only true if both null
+    }
+
+    public boolean testEq31_3(MyValue1 v, MyInterface[] a) {
+        return get(v) == get(a); // only true if both null
+    }
+
+    public boolean testEq31_4(MyValue1 v, MyAbstract[] a) {
+        return get(v) == a; // only true if both null
+    }
+
+    public boolean testEq31_5(MyValue1 v, MyAbstract[] a) {
+        return (Object)v == get(a); // only true if both null
+    }
+
+    public boolean testEq31_6(MyValue1 v, MyAbstract[] a) {
+        return get(v) == get(a); // only true if both null
+    }
+
+    public boolean testEq32_1(MyInterface[] a, MyValue1 v) {
+        return getNotNull(a) == (Object)v; // false
+    }
+
+    public boolean testEq32_2(MyInterface[] a, MyValue1 v) {
+        return a == getNotNull(v); // false
+    }
+
+    public boolean testEq32_3(MyInterface[] a, MyValue1 v) {
+        return getNotNull(a) == getNotNull(v); // false
+    }
+
+    public boolean testEq32_4(MyAbstract[] a, MyValue1 v) {
+        return getNotNull(a) == (Object)v; // false
+    }
+
+    public boolean testEq32_5(MyAbstract[] a, MyValue1 v) {
+        return a == getNotNull(v); // false
+    }
+
+    public boolean testEq32_6(MyAbstract[] a, MyValue1 v) {
+        return getNotNull(a) == getNotNull(v); // false
+    }
+
+    public boolean testEq33_1(MyValue1 v, MyInterface[] a) {
+        return getNotNull(v) == a; // false
+    }
+
+    public boolean testEq33_2(MyValue1 v, MyInterface[] a) {
+        return (Object)v == getNotNull(a); // false
+    }
+
+    public boolean testEq33_3(MyValue1 v, MyInterface[] a) {
+        return getNotNull(v) == getNotNull(a); // false
+    }
+
+    public boolean testEq33_4(MyValue1 v, MyAbstract[] a) {
+        return getNotNull(v) == a; // false
+    }
+
+    public boolean testEq33_5(MyValue1 v, MyAbstract[] a) {
+        return (Object)v == getNotNull(a); // false
+    }
+
+    public boolean testEq33_6(MyValue1 v, MyAbstract[] a) {
+        return getNotNull(v) == getNotNull(a); // false
+    }
+
+
+    // Null tests
+
+    public boolean testNull01_1(MyValue1 v) {
+        return (Object)v == null; // old acmp
+    }
+
+    public boolean testNull01_2(MyValue1 v) {
+        return get(v) == null; // old acmp
+    }
+
+    public boolean testNull01_3(MyValue1 v) {
+        return (Object)v == get((Object)null); // old acmp
+    }
+
+    public boolean testNull01_4(MyValue1 v) {
+        return get(v) == get((Object)null); // old acmp
+    }
+
+    public boolean testNull02_1(MyValue1 v) {
+        return null == (Object)v; // old acmp
+    }
+
+    public boolean testNull02_2(MyValue1 v) {
+        return get((Object)null) == (Object)v; // old acmp
+    }
+
+    public boolean testNull02_3(MyValue1 v) {
+        return null == get(v); // old acmp
+    }
+
+    public boolean testNull02_4(MyValue1 v) {
+        return get((Object)null) == get(v); // old acmp
+    }
+
+    public boolean testNull03_1(Object u) {
+        return u == null; // old acmp
+    }
+
+    public boolean testNull03_2(Object u) {
+        return get(u) == null; // old acmp
+    }
+
+    public boolean testNull03_3(Object u) {
+        return u == get((Object)null); // old acmp
+    }
+
+    public boolean testNull03_4(Object u) {
+        return get(u) == get((Object)null); // old acmp
+    }
+
+    public boolean testNull04_1(Object u) {
+        return null == u; // old acmp
+    }
+
+    public boolean testNull04_2(Object u) {
+        return get((Object)null) == u; // old acmp
+    }
+
+    public boolean testNull04_3(Object u) {
+        return null == get(u); // old acmp
+    }
+
+    public boolean testNull04_4(Object u) {
+        return get((Object)null) == get(u); // old acmp
+    }
+
+    public boolean testNull05_1(MyObject o) {
+        return o == null; // old acmp
+    }
+
+    public boolean testNull05_2(MyObject o) {
+        return get(o) == null; // old acmp
+    }
+
+    public boolean testNull05_3(MyObject o) {
+        return o == get((Object)null); // old acmp
+    }
+
+    public boolean testNull05_4(MyObject o) {
+        return get(o) == get((Object)null); // old acmp
+    }
+
+    public boolean testNull06_1(MyObject o) {
+        return null == o; // old acmp
+    }
+
+    public boolean testNull06_2(MyObject o) {
+        return get((Object)null) == o; // old acmp
+    }
+
+    public boolean testNull06_3(MyObject o) {
+        return null == get(o); // old acmp
+    }
+
+    public boolean testNull06_4(MyObject o) {
+        return get((Object)null) == get(o); // old acmp
+    }
+
+    public boolean testNull07_1(MyInterface u) {
+        return u == null; // old acmp
+    }
+
+    public boolean testNull07_2(MyInterface u) {
+        return get(u) == null; // old acmp
+    }
+
+    public boolean testNull07_3(MyInterface u) {
+        return u == get((Object)null); // old acmp
+    }
+
+    public boolean testNull07_4(MyInterface u) {
+        return get(u) == get((Object)null); // old acmp
+    }
+
+    public boolean testNull07_5(MyAbstract u) {
+        return u == null; // old acmp
+    }
+
+    public boolean testNull07_6(MyAbstract u) {
+        return get(u) == null; // old acmp
+    }
+
+    public boolean testNull07_7(MyAbstract u) {
+        return u == get((Object)null); // old acmp
+    }
+
+    public boolean testNull07_8(MyAbstract u) {
+        return get(u) == get((Object)null); // old acmp
+    }
+
+    public boolean testNull08_1(MyInterface u) {
+        return null == u; // old acmp
+    }
+
+    public boolean testNull08_2(MyInterface u) {
+        return get((Object)null) == u; // old acmp
+    }
+
+    public boolean testNull08_3(MyInterface u) {
+        return null == get(u); // old acmp
+    }
+
+    public boolean testNull08_4(MyInterface u) {
+        return get((Object)null) == get(u); // old acmp
+    }
+
+    public boolean testNull08_5(MyAbstract u) {
+        return null == u; // old acmp
+    }
+
+    public boolean testNull08_6(MyAbstract u) {
+        return get((Object)null) == u; // old acmp
+    }
+
+    public boolean testNull08_7(MyAbstract u) {
+        return null == get(u); // old acmp
+    }
+
+    public boolean testNull08_8(MyAbstract u) {
+        return get((Object)null) == get(u); // old acmp
+    }
+
+    // Same tests as above but negated
+
+    public boolean testNotEq01_1(Object u1, Object u2) {
+        return get(u1) != u2; // new acmp
+    }
+
+    public boolean testNotEq01_2(Object u1, Object u2) {
+        return u1 != get(u2); // new acmp
+    }
+
+    public boolean testNotEq01_3(Object u1, Object u2) {
+        return get(u1) != get(u2); // new acmp
+    }
+
+    @TrueIfNull
+    public boolean testNotEq01_4(Object u1, Object u2) {
+        return getNotNull(u1) != u2; // new acmp without null check
+    }
+
+    @TrueIfNull
+    public boolean testNotEq01_5(Object u1, Object u2) {
+        return u1 != getNotNull(u2); // new acmp without null check
+    }
+
+    @TrueIfNull
+    public boolean testNotEq01_6(Object u1, Object u2) {
+        return getNotNull(u1) != getNotNull(u2); // new acmp without null check
+    }
+
+    public boolean testNotEq02_1(MyValue1 v1, MyValue1 v2) {
+        return get(v1) != (Object)v2; // only false if both null
+    }
+
+    public boolean testNotEq02_2(MyValue1 v1, MyValue1 v2) {
+        return (Object)v1 != get(v2); // only false if both null
+    }
+
+    public boolean testNotEq02_3(MyValue1 v1, MyValue1 v2) {
+        return get(v1) != get(v2); // only false if both null
+    }
+
+    public boolean testNotEq03_1(MyValue1 v, Object u) {
+        return get(v) != u; // only false if both null
+    }
+
+    public boolean testNotEq03_2(MyValue1 v, Object u) {
+        return (Object)v != get(u); // only false if both null
+    }
+
+    public boolean testNotEq03_3(MyValue1 v, Object u) {
+        return get(v) != get(u); // only false if both null
+    }
+
+    public boolean testNotEq04_1(Object u, MyValue1 v) {
+        return get(u) != (Object)v; // only false if both null
+    }
+
+    public boolean testNotEq04_2(Object u, MyValue1 v) {
+        return u != get(v); // only false if both null
+    }
+
+    public boolean testNotEq04_3(Object u, MyValue1 v) {
+        return get(u) != get(v); // only false if both null
+    }
+
+    public boolean testNotEq05_1(MyObject o, MyValue1 v) {
+        return get(o) != (Object)v; // only false if both null
+    }
+
+    public boolean testNotEq05_2(MyObject o, MyValue1 v) {
+        return o != get(v); // only false if both null
+    }
+
+    public boolean testNotEq05_3(MyObject o, MyValue1 v) {
+        return get(o) != get(v); // only false if both null
+    }
+
+    public boolean testNotEq06_1(MyValue1 v, MyObject o) {
+        return get(v) != o; // only false if both null
+    }
+
+    public boolean testNotEq06_2(MyValue1 v, MyObject o) {
+        return (Object)v != get(o); // only false if both null
+    }
+
+    public boolean testNotEq06_3(MyValue1 v, MyObject o) {
+        return get(v) != get(o); // only false if both null
+    }
+
+    public boolean testNotEq07_1(MyValue1 v1, MyValue1 v2) {
+        return getNotNull(v1) != (Object)v2; // true
+    }
+
+    public boolean testNotEq07_2(MyValue1 v1, MyValue1 v2) {
+        return (Object)v1 != getNotNull(v2); // true
+    }
+
+    public boolean testNotEq07_3(MyValue1 v1, MyValue1 v2) {
+        return getNotNull(v1) != getNotNull(v2); // true
+    }
+
+    public boolean testNotEq08_1(MyValue1 v, Object u) {
+        return getNotNull(v) != u; // true
+    }
+
+    public boolean testNotEq08_2(MyValue1 v, Object u) {
+        return (Object)v != getNotNull(u); // true
+    }
+
+    public boolean testNotEq08_3(MyValue1 v, Object u) {
+        return getNotNull(v) != getNotNull(u); // true
+    }
+
+    public boolean testNotEq09_1(Object u, MyValue1 v) {
+        return getNotNull(u) != (Object)v; // true
+    }
+
+    public boolean testNotEq09_2(Object u, MyValue1 v) {
+        return u != getNotNull(v); // true
+    }
+
+    public boolean testNotEq09_3(Object u, MyValue1 v) {
+        return getNotNull(u) != getNotNull(v); // true
+    }
+
+    public boolean testNotEq10_1(MyObject o, MyValue1 v) {
+        return getNotNull(o) != (Object)v; // true
+    }
+
+    public boolean testNotEq10_2(MyObject o, MyValue1 v) {
+        return o != getNotNull(v); // true
+    }
+
+    public boolean testNotEq10_3(MyObject o, MyValue1 v) {
+        return getNotNull(o) != getNotNull(v); // true
+    }
+
+    public boolean testNotEq11_1(MyValue1 v, MyObject o) {
+        return getNotNull(v) != o; // true
+    }
+
+    public boolean testNotEq11_2(MyValue1 v, MyObject o) {
+        return (Object)v != getNotNull(o); // true
+    }
+
+    public boolean testNotEq11_3(MyValue1 v, MyObject o) {
+        return getNotNull(v) != getNotNull(o); // true
+    }
+
+    public boolean testNotEq12_1(MyObject o1, MyObject o2) {
+        return get(o1) != o2; // old acmp
+    }
+
+    public boolean testNotEq12_2(MyObject o1, MyObject o2) {
+        return o1 != get(o2); // old acmp
+    }
+
+    public boolean testNotEq12_3(MyObject o1, MyObject o2) {
+        return get(o1) != get(o2); // old acmp
+    }
+
+    public boolean testNotEq13_1(Object u, MyObject o) {
+        return get(u) != o; // old acmp
+    }
+
+    public boolean testNotEq13_2(Object u, MyObject o) {
+        return u != get(o); // old acmp
+    }
+
+    public boolean testNotEq13_3(Object u, MyObject o) {
+        return get(u) != get(o); // old acmp
+    }
+
+    public boolean testNotEq14_1(MyObject o, Object u) {
+        return get(o) != u; // old acmp
+    }
+
+    public boolean testNotEq14_2(MyObject o, Object u) {
+        return o != get(u); // old acmp
+    }
+
+    public boolean testNotEq14_3(MyObject o, Object u) {
+        return get(o) != get(u); // old acmp
+    }
+
+    public boolean testNotEq15_1(Object[] a, Object u) {
+        return get(a) != u; // old acmp
+    }
+
+    public boolean testNotEq15_2(Object[] a, Object u) {
+        return a != get(u); // old acmp
+    }
+
+    public boolean testNotEq15_3(Object[] a, Object u) {
+        return get(a) != get(u); // old acmp
+    }
+
+    public boolean testNotEq16_1(Object u, Object[] a) {
+        return get(u) != a; // old acmp
+    }
+
+    public boolean testNotEq16_2(Object u, Object[] a) {
+        return u != get(a); // old acmp
+    }
+
+    public boolean testNotEq16_3(Object u, Object[] a) {
+        return get(u) != get(a); // old acmp
+    }
+
+    public boolean testNotEq17_1(Object[] a, MyValue1 v) {
+        return get(a) != (Object)v; // only false if both null
+    }
+
+    public boolean testNotEq17_2(Object[] a, MyValue1 v) {
+        return a != get(v); // only false if both null
+    }
+
+    public boolean testNotEq17_3(Object[] a, MyValue1 v) {
+        return get(a) != get(v); // only false if both null
+    }
+
+    public boolean testNotEq18_1(MyValue1 v, Object[] a) {
+        return get(v) != a; // only false if both null
+    }
+
+    public boolean testNotEq18_2(MyValue1 v, Object[] a) {
+        return (Object)v != get(a); // only false if both null
+    }
+
+    public boolean testNotEq18_3(MyValue1 v, Object[] a) {
+        return get(v) != get(a); // only false if both null
+    }
+
+    public boolean testNotEq19_1(Object[] a, MyValue1 v) {
+        return getNotNull(a) != (Object)v; // true
+    }
+
+    public boolean testNotEq19_2(Object[] a, MyValue1 v) {
+        return a != getNotNull(v); // true
+    }
+
+    public boolean testNotEq19_3(Object[] a, MyValue1 v) {
+        return getNotNull(a) != getNotNull(v); // true
+    }
+
+    public boolean testNotEq20_1(MyValue1 v, Object[] a) {
+        return getNotNull(v) != a; // true
+    }
+
+    public boolean testNotEq20_2(MyValue1 v, Object[] a) {
+        return (Object)v != getNotNull(a); // true
+    }
+
+    public boolean testNotEq20_3(MyValue1 v, Object[] a) {
+        return getNotNull(v) != getNotNull(a); // true
+    }
+
+    public boolean testNotEq21_1(MyInterface u1, MyInterface u2) {
+        return get(u1) != u2; // new acmp
+    }
+
+    public boolean testNotEq21_2(MyInterface u1, MyInterface u2) {
+        return u1 != get(u2); // new acmp
+    }
+
+    public boolean testNotEq21_3(MyInterface u1, MyInterface u2) {
+        return get(u1) != get(u2); // new acmp
+    }
+
+    @TrueIfNull
+    public boolean testNotEq21_4(MyInterface u1, MyInterface u2) {
+        return getNotNull(u1) != u2; // new acmp without null check
+    }
+
+    @TrueIfNull
+    public boolean testNotEq21_5(MyInterface u1, MyInterface u2) {
+        return u1 != getNotNull(u2); // new acmp without null check
+    }
+
+    @TrueIfNull
+    public boolean testNotEq21_6(MyInterface u1, MyInterface u2) {
+        return getNotNull(u1) != getNotNull(u2); // new acmp without null check
+    }
+
+    public boolean testNotEq21_7(MyAbstract u1, MyAbstract u2) {
+        return get(u1) != u2; // new acmp
+    }
+
+    public boolean testNotEq21_8(MyAbstract u1, MyAbstract u2) {
+        return u1 != get(u2); // new acmp
+    }
+
+    public boolean testNotEq21_9(MyAbstract u1, MyAbstract u2) {
+        return get(u1) != get(u2); // new acmp
+    }
+
+    @TrueIfNull
+    public boolean testNotEq21_10(MyAbstract u1, MyAbstract u2) {
+        return getNotNull(u1) != u2; // new acmp without null check
+    }
+
+    @TrueIfNull
+    public boolean testNotEq21_11(MyAbstract u1, MyAbstract u2) {
+        return u1 != getNotNull(u2); // new acmp without null check
+    }
+
+    @TrueIfNull
+    public boolean testNotEq21_12(MyAbstract u1, MyAbstract u2) {
+        return getNotNull(u1) != getNotNull(u2); // new acmp without null check
+    }
+
+    public boolean testNotEq22_1(MyValue1 v, MyInterface u) {
+        return get(v) != u; // only false if both null
+    }
+
+    public boolean testNotEq22_2(MyValue1 v, MyInterface u) {
+        return (Object)v != get(u); // only false if both null
+    }
+
+    public boolean testNotEq22_3(MyValue1 v, MyInterface u) {
+        return get(v) != get(u); // only false if both null
+    }
+
+    public boolean testNotEq22_4(MyValue1 v, MyAbstract u) {
+        return get(v) != u; // only false if both null
+    }
+
+    public boolean testNotEq22_5(MyValue1 v, MyAbstract u) {
+        return (Object)v != get(u); // only false if both null
+    }
+
+    public boolean testNotEq22_6(MyValue1 v, MyAbstract u) {
+        return get(v) != get(u); // only false if both null
+    }
+
+    public boolean testNotEq23_1(MyInterface u, MyValue1 v) {
+        return get(u) != (Object)v; // only false if both null
+    }
+
+    public boolean testNotEq23_2(MyInterface u, MyValue1 v) {
+        return u != get(v); // only false if both null
+    }
+
+    public boolean testNotEq23_3(MyInterface u, MyValue1 v) {
+        return get(u) != get(v); // only false if both null
+    }
+
+    public boolean testNotEq23_4(MyAbstract u, MyValue1 v) {
+        return get(u) != (Object)v; // only false if both null
+    }
+
+    public boolean testNotEq23_5(MyAbstract u, MyValue1 v) {
+        return u != get(v); // only false if both null
+    }
+
+    public boolean testNotEq23_6(MyAbstract u, MyValue1 v) {
+        return get(u) != get(v); // only false if both null
+    }
+
+    public boolean testNotEq24_1(MyValue1 v, MyInterface u) {
+        return getNotNull(v) != u; // true
+    }
+
+    public boolean testNotEq24_2(MyValue1 v, MyInterface u) {
+        return (Object)v != getNotNull(u); // true
+    }
+
+    public boolean testNotEq24_3(MyValue1 v, MyInterface u) {
+        return getNotNull(v) != getNotNull(u); // true
+    }
+
+    public boolean testNotEq24_4(MyValue1 v, MyAbstract u) {
+        return getNotNull(v) != u; // true
+    }
+
+    public boolean testNotEq24_5(MyValue1 v, MyAbstract u) {
+        return (Object)v != getNotNull(u); // true
+    }
+
+    public boolean testNotEq24_6(MyValue1 v, MyAbstract u) {
+        return getNotNull(v) != getNotNull(u); // true
+    }
+
+    public boolean testNotEq25_1(MyInterface u, MyValue1 v) {
+        return getNotNull(u) != (Object)v; // true
+    }
+
+    public boolean testNotEq25_2(MyInterface u, MyValue1 v) {
+        return u != getNotNull(v); // true
+    }
+
+    public boolean testNotEq25_3(MyInterface u, MyValue1 v) {
+        return getNotNull(u) != getNotNull(v); // true
+    }
+
+    public boolean testNotEq25_4(MyAbstract u, MyValue1 v) {
+        return getNotNull(u) != (Object)v; // true
+    }
+
+    public boolean testNotEq25_5(MyAbstract u, MyValue1 v) {
+        return u != getNotNull(v); // true
+    }
+
+    public boolean testNotEq25_6(MyAbstract u, MyValue1 v) {
+        return getNotNull(u) != getNotNull(v); // true
+    }
+
+    public boolean testNotEq26_1(MyInterface u, MyObject o) {
+        return get(u) != o; // old acmp
+    }
+
+    public boolean testNotEq26_2(MyInterface u, MyObject o) {
+        return u != get(o); // old acmp
+    }
+
+    public boolean testNotEq26_3(MyInterface u, MyObject o) {
+        return get(u) != get(o); // old acmp
+    }
+
+    public boolean testNotEq26_4(MyAbstract u, MyObject o) {
+        return get(u) != o; // old acmp
+    }
+
+    public boolean testNotEq26_5(MyAbstract u, MyObject o) {
+        return u != get(o); // old acmp
+    }
+
+    public boolean testNotEq26_6(MyAbstract u, MyObject o) {
+        return get(u) != get(o); // old acmp
+    }
+
+    public boolean testNotEq27_1(MyObject o, MyInterface u) {
+        return get(o) != u; // old acmp
+    }
+
+    public boolean testNotEq27_2(MyObject o, MyInterface u) {
+        return o != get(u); // old acmp
+    }
+
+    public boolean testNotEq27_3(MyObject o, MyInterface u) {
+        return get(o) != get(u); // old acmp
+    }
+
+    public boolean testNotEq27_4(MyObject o, MyAbstract u) {
+        return get(o) != u; // old acmp
+    }
+
+    public boolean testNotEq27_5(MyObject o, MyAbstract u) {
+        return o != get(u); // old acmp
+    }
+
+    public boolean testNotEq27_6(MyObject o, MyAbstract u) {
+        return get(o) != get(u); // old acmp
+    }
+
+    public boolean testNotEq28_1(MyInterface[] a, MyInterface u) {
+        return get(a) != u; // old acmp
+    }
+
+    public boolean testNotEq28_2(MyInterface[] a, MyInterface u) {
+        return a != get(u); // old acmp
+    }
+
+    public boolean testNotEq28_3(MyInterface[] a, MyInterface u) {
+        return get(a) != get(u); // old acmp
+    }
+
+    public boolean testNotEq28_4(MyAbstract[] a, MyAbstract u) {
+        return get(a) != u; // old acmp
+    }
+
+    public boolean testNotEq28_5(MyAbstract[] a, MyAbstract u) {
+        return a != get(u); // old acmp
+    }
+
+    public boolean testNotEq28_6(MyAbstract[] a, MyAbstract u) {
+        return get(a) != get(u); // old acmp
+    }
+
+    public boolean testNotEq29_1(MyInterface u, MyInterface[] a) {
+        return get(u) != a; // old acmp
+    }
+
+    public boolean testNotEq29_2(MyInterface u, MyInterface[] a) {
+        return u != get(a); // old acmp
+    }
+
+    public boolean testNotEq29_3(MyInterface u, MyInterface[] a) {
+        return get(u) != get(a); // old acmp
+    }
+
+    public boolean testNotEq29_4(MyAbstract u, MyAbstract[] a) {
+        return get(u) != a; // old acmp
+    }
+
+    public boolean testNotEq29_5(MyAbstract u, MyAbstract[] a) {
+        return u != get(a); // old acmp
+    }
+
+    public boolean testNotEq29_6(MyAbstract u, MyAbstract[] a) {
+        return get(u) != get(a); // old acmp
+    }
+
+    public boolean testNotEq30_1(MyInterface[] a, MyValue1 v) {
+        return get(a) != (Object)v; // only false if both null
+    }
+
+    public boolean testNotEq30_2(MyInterface[] a, MyValue1 v) {
+        return a != get(v); // only false if both null
+    }
+
+    public boolean testNotEq30_3(MyInterface[] a, MyValue1 v) {
+        return get(a) != get(v); // only false if both null
+    }
+
+    public boolean testNotEq30_4(MyAbstract[] a, MyValue1 v) {
+        return get(a) != (Object)v; // only false if both null
+    }
+
+    public boolean testNotEq30_5(MyAbstract[] a, MyValue1 v) {
+        return a != get(v); // only false if both null
+    }
+
+    public boolean testNotEq30_6(MyAbstract[] a, MyValue1 v) {
+        return get(a) != get(v); // only false if both null
+    }
+
+    public boolean testNotEq31_1(MyValue1 v, MyInterface[] a) {
+        return get(v) != a; // only false if both null
+    }
+
+    public boolean testNotEq31_2(MyValue1 v, MyInterface[] a) {
+        return (Object)v != get(a); // only false if both null
+    }
+
+    public boolean testNotEq31_3(MyValue1 v, MyInterface[] a) {
+        return get(v) != get(a); // only false if both null
+    }
+
+    public boolean testNotEq31_4(MyValue1 v, MyAbstract[] a) {
+        return get(v) != a; // only false if both null
+    }
+
+    public boolean testNotEq31_5(MyValue1 v, MyAbstract[] a) {
+        return (Object)v != get(a); // only false if both null
+    }
+
+    public boolean testNotEq31_6(MyValue1 v, MyAbstract[] a) {
+        return get(v) != get(a); // only false if both null
+    }
+
+    public boolean testNotEq32_1(MyInterface[] a, MyValue1 v) {
+        return getNotNull(a) != (Object)v; // true
+    }
+
+    public boolean testNotEq32_2(MyInterface[] a, MyValue1 v) {
+        return a != getNotNull(v); // true
+    }
+
+    public boolean testNotEq32_3(MyInterface[] a, MyValue1 v) {
+        return getNotNull(a) != getNotNull(v); // true
+    }
+
+    public boolean testNotEq32_4(MyAbstract[] a, MyValue1 v) {
+        return getNotNull(a) != (Object)v; // true
+    }
+
+    public boolean testNotEq32_5(MyAbstract[] a, MyValue1 v) {
+        return a != getNotNull(v); // true
+    }
+
+    public boolean testNotEq32_6(MyAbstract[] a, MyValue1 v) {
+        return getNotNull(a) != getNotNull(v); // true
+    }
+
+    public boolean testNotEq33_1(MyValue1 v, MyInterface[] a) {
+        return getNotNull(v) != a; // true
+    }
+
+    public boolean testNotEq33_2(MyValue1 v, MyInterface[] a) {
+        return (Object)v != getNotNull(a); // true
+    }
+
+    public boolean testNotEq33_3(MyValue1 v, MyInterface[] a) {
+        return getNotNull(v) != getNotNull(a); // true
+    }
+
+    public boolean testNotEq33_4(MyValue1 v, MyAbstract[] a) {
+        return getNotNull(v) != a; // true
+    }
+
+    public boolean testNotEq33_5(MyValue1 v, MyAbstract[] a) {
+        return (Object)v != getNotNull(a); // true
+    }
+
+    public boolean testNotEq33_6(MyValue1 v, MyAbstract[] a) {
+        return getNotNull(v) != getNotNull(a); // true
+    }
+
+    // Null tests
+
+    public boolean testNotNull01_1(MyValue1 v) {
+        return (Object)v != null; // old acmp
+    }
+
+    public boolean testNotNull01_2(MyValue1 v) {
+        return get(v) != null; // old acmp
+    }
+
+    public boolean testNotNull01_3(MyValue1 v) {
+        return (Object)v != get((Object)null); // old acmp
+    }
+
+    public boolean testNotNull01_4(MyValue1 v) {
+        return get(v) != get((Object)null); // old acmp
+    }
+
+    public boolean testNotNull02_1(MyValue1 v) {
+        return null != (Object)v; // old acmp
+    }
+
+    public boolean testNotNull02_2(MyValue1 v) {
+        return get((Object)null) != (Object)v; // old acmp
+    }
+
+    public boolean testNotNull02_3(MyValue1 v) {
+        return null != get(v); // old acmp
+    }
+
+    public boolean testNotNull02_4(MyValue1 v) {
+        return get((Object)null) != get(v); // old acmp
+    }
+
+    public boolean testNotNull03_1(Object u) {
+        return u != null; // old acmp
+    }
+
+    public boolean testNotNull03_2(Object u) {
+        return get(u) != null; // old acmp
+    }
+
+    public boolean testNotNull03_3(Object u) {
+        return u != get((Object)null); // old acmp
+    }
+
+    public boolean testNotNull03_4(Object u) {
+        return get(u) != get((Object)null); // old acmp
+    }
+
+    public boolean testNotNull04_1(Object u) {
+        return null != u; // old acmp
+    }
+
+    public boolean testNotNull04_2(Object u) {
+        return get((Object)null) != u; // old acmp
+    }
+
+    public boolean testNotNull04_3(Object u) {
+        return null != get(u); // old acmp
+    }
+
+    public boolean testNotNull04_4(Object u) {
+        return get((Object)null) != get(u); // old acmp
+    }
+
+    public boolean testNotNull05_1(MyObject o) {
+        return o != null; // old acmp
+    }
+
+    public boolean testNotNull05_2(MyObject o) {
+        return get(o) != null; // old acmp
+    }
+
+    public boolean testNotNull05_3(MyObject o) {
+        return o != get((Object)null); // old acmp
+    }
+
+    public boolean testNotNull05_4(MyObject o) {
+        return get(o) != get((Object)null); // old acmp
+    }
+
+    public boolean testNotNull06_1(MyObject o) {
+        return null != o; // old acmp
+    }
+
+    public boolean testNotNull06_2(MyObject o) {
+        return get((Object)null) != o; // old acmp
+    }
+
+    public boolean testNotNull06_3(MyObject o) {
+        return null != get(o); // old acmp
+    }
+
+    public boolean testNotNull06_4(MyObject o) {
+        return get((Object)null) != get(o); // old acmp
+    }
+
+    public boolean testNotNull07_1(MyInterface u) {
+        return u != null; // old acmp
+    }
+
+    public boolean testNotNull07_2(MyInterface u) {
+        return get(u) != null; // old acmp
+    }
+
+    public boolean testNotNull07_3(MyInterface u) {
+        return u != get((Object)null); // old acmp
+    }
+
+    public boolean testNotNull07_4(MyInterface u) {
+        return get(u) != get((Object)null); // old acmp
+    }
+
+    public boolean testNotNull07_5(MyAbstract u) {
+        return u != null; // old acmp
+    }
+
+    public boolean testNotNull07_6(MyAbstract u) {
+        return get(u) != null; // old acmp
+    }
+
+    public boolean testNotNull07_7(MyAbstract u) {
+        return u != get((Object)null); // old acmp
+    }
+
+    public boolean testNotNull07_8(MyAbstract u) {
+        return get(u) != get((Object)null); // old acmp
+    }
+
+    public boolean testNotNull08_1(MyInterface u) {
+        return null != u; // old acmp
+    }
+
+    public boolean testNotNull08_2(MyInterface u) {
+        return get((Object)null) != u; // old acmp
+    }
+
+    public boolean testNotNull08_3(MyInterface u) {
+        return null != get(u); // old acmp
+    }
+
+    public boolean testNotNull08_4(MyInterface u) {
+        return get((Object)null) != get(u); // old acmp
+    }
+
+    public boolean testNotNull08_5(MyAbstract u) {
+        return null != u; // old acmp
+    }
+
+    public boolean testNotNull08_6(MyAbstract u) {
+        return get((Object)null) != u; // old acmp
+    }
+
+    public boolean testNotNull08_7(MyAbstract u) {
+        return null != get(u); // old acmp
+    }
+
+    public boolean testNotNull08_8(MyAbstract u) {
+        return get((Object)null) != get(u); // old acmp
+    }
+
+    // The following methods are used with -XX:+AlwaysIncrementalInline to hide exact types during parsing
+
+    public Object get(Object u) {
+        return u;
+    }
+
+    public Object getNotNull(Object u) {
+        return (u != null) ? u : new Object();
+    }
+
+    public Object get(MyValue1 v) {
+        return v;
+    }
+
+    public Object getNotNull(MyValue1 v) {
+        return ((Object)v != null) ? v : MyValue1.createDefault();
+    }
+
+    public Object get(MyObject o) {
+        return o;
+    }
+
+    public Object getNotNull(MyObject o) {
+        return (o != null) ? o : MyValue1.createDefault();
+    }
+
+    public Object get(Object[] a) {
+        return a;
+    }
+
+    public Object getNotNull(Object[] a) {
+        return (a != null) ? a : new Object[1];
+    }
+
+    public boolean trueIfNull(Method m) {
+        return m.isAnnotationPresent(TrueIfNull.class);
+    }
+
+    public boolean falseIfNull(Method m) {
+        return m.isAnnotationPresent(FalseIfNull.class);
+    }
+
+    public boolean isNegated(Method m) {
+        return m.getName().startsWith("testNot");
+    }
+
+    // Tests with profiling
+    public boolean cmpAlwaysEqual1(Object a, Object b) {
+        return a == b;
+    }
+
+    public boolean cmpAlwaysEqual2(Object a, Object b) {
+        return a != b;
+    }
+
+    public boolean cmpAlwaysEqual3(Object a) {
+        return a == a;
+    }
+
+    public boolean cmpAlwaysEqual4(Object a) {
+        return a != a;
+    }
+
+    public boolean cmpAlwaysUnEqual1(Object a, Object b) {
+        return a == b;
+    }
+
+    public boolean cmpAlwaysUnEqual2(Object a, Object b) {
+        return a != b;
+    }
+
+    public boolean cmpAlwaysUnEqual3(Object a) {
+        return a == a;
+    }
+
+    public boolean cmpAlwaysUnEqual4(Object a) {
+        return a != a;
+    }
+
+    public boolean cmpSometimesEqual1(Object a) {
+        return a == a;
+    }
+
+    public boolean cmpSometimesEqual2(Object a) {
+        return a != a;
+    }
+
+    static int get_full_opt_level() {
+        int n = (int)TieredStopAtLevel;
+        if (n >= 4) {
+            n = 4;
+        }
+        return n;
+    }
+    protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+    protected static final long TieredStopAtLevel = (Long)WHITE_BOX.getVMFlag("TieredStopAtLevel");
+    protected static final int COMP_LEVEL_FULL_OPTIMIZATION = get_full_opt_level();
+
+    public void runTest(Method m, Object[] args, int warmup, int nullMode, boolean[][] equalities) throws Exception {
+        Class<?>[] parameterTypes = m.getParameterTypes();
+        int parameterCount = parameterTypes.length;
+        // Nullness mode for first argument
+        // 0: default, 1: never null, 2: always null
+        int start = (nullMode != 1) ? 0 : 1;
+        int end = (nullMode != 2) ? args.length : 1;
+        for (int i = start; i < end; ++i) {
+            if (args[i] != null && !parameterTypes[0].isInstance(args[i])) {
+                continue;
+            }
+            if (args[i] == null && parameterTypes[0] == MyValue1.class) {
+                continue;
+            }
+            if (parameterCount == 1) {
+                // Null checks
+                System.out.print("Testing " + m.getName() + "(" + args[i] + ")");
+                // Avoid acmp in the computation of the expected result!
+                boolean expected = isNegated(m) ? (i != 0) : (i == 0);
+                for (int run = 0; run < warmup; ++run) {
+                    Boolean result = (Boolean)m.invoke(this, args[i]);
+                    if (result != expected && WHITE_BOX.isMethodCompiled(m, false)) {
+                        System.out.println(" = " + result);
+                        throw new RuntimeException("Test failed: should return " + expected);
+                    }
+                }
+                System.out.println(" = " + expected);
+            } else {
+                // Equality checks
+                for (int j = 0; j < args.length; ++j) {
+                    if (args[j] != null && !parameterTypes[1].isInstance(args[j])) {
+                        continue;
+                    }
+                    if (args[j] == null && parameterTypes[1] == MyValue1.class) {
+                        continue;
+                    }
+                    System.out.print("Testing " + m.getName() + "(" + args[i] + ", " + args[j] + ")");
+                    // Avoid acmp in the computation of the expected result!
+                    boolean equal = equalities[i][j];
+                    equal = isNegated(m) ? !equal : equal;
+                    boolean expected = ((i == 0 || j == 0) && trueIfNull(m)) || (equal && !(i == 0 && falseIfNull(m)));
+                    for (int run = 0; run < warmup; ++run) {
+                        Boolean result = (Boolean)m.invoke(this, args[i], args[j]);
+                        if (result != expected && WHITE_BOX.isMethodCompiled(m, false) && warmup == 1) {
+                            System.out.println(" = " + result);
+                            throw new RuntimeException("Test failed: should return " + expected);
+                        }
+                    }
+                    System.out.println(" = " + expected);
+                }
+            }
+        }
+    }
+
+    public void run(int nullMode) throws Exception {
+        // Prepare test arguments
+        Object[] args =  { null,
+                           new Object(),
+                           new MyObject(),
+                           MyValue1.setX(MyValue1.createDefault(), 42),
+                           new Object[10],
+                           new MyObject[10],
+                           MyValue1.setX(MyValue1.createDefault(), 0x42),
+                           MyValue1.setX(MyValue1.createDefault(), 42),
+                           MyValue2.setX(MyValue2.createDefault(), 42), };
+
+        boolean[][] equalities = { { true,  false, false, false, false, false, false, false, false },
+                                   { false, true,  false, false, false, false, false, false, false },
+                                   { false, false, true,  false, false, false, false, false, false },
+                                   { false, false, false, true,  false, false, false, true,  false },
+                                   { false, false, false, false, true,  false, false, false, false },
+                                   { false, false, false, false, false, true,  false, false, false },
+                                   { false, false, false, false, false, false, true,  false, false },
+                                   { false, false, false, true,  false, false, false, true,  false },
+                                   { false, false, false, false, false, false, false, false, true  } };
+
+        // Run tests
+        for (Method m : getClass().getMethods()) {
+            if (m.getName().startsWith("test")) {
+                // Do some warmup runs
+                runTest(m, args, 1000, nullMode, equalities);
+                // Make sure method is compiled
+                TestFramework.compile(m, CompLevel.ANY);
+                Asserts.assertTrue(WHITE_BOX.isMethodCompiled(m, false), m + " not compiled");
+                // Run again to verify correctness of compiled code
+                runTest(m, args, 1, nullMode, equalities);
+            }
+        }
+
+        Method cmpAlwaysUnEqual3_m = getClass().getMethod("cmpAlwaysUnEqual3", Object.class);
+        Method cmpAlwaysUnEqual4_m = getClass().getMethod("cmpAlwaysUnEqual4", Object.class);
+        Method cmpSometimesEqual1_m = getClass().getMethod("cmpSometimesEqual1", Object.class);
+        Method cmpSometimesEqual2_m = getClass().getMethod("cmpSometimesEqual2", Object.class);
+
+        for (int i = 0; i < 20_000; ++i) {
+            Asserts.assertTrue(cmpAlwaysEqual1(args[1], args[1]));
+            Asserts.assertFalse(cmpAlwaysEqual2(args[1], args[1]));
+            Asserts.assertTrue(cmpAlwaysEqual3(args[1]));
+            Asserts.assertFalse(cmpAlwaysEqual4(args[1]));
+
+            Asserts.assertFalse(cmpAlwaysUnEqual1(args[1], args[2]));
+            Asserts.assertTrue(cmpAlwaysUnEqual2(args[1], args[2]));
+            boolean res = cmpAlwaysUnEqual3(args[3]);
+            Asserts.assertTrue(res);
+            res = cmpAlwaysUnEqual4(args[3]);
+            Asserts.assertFalse(res);
+
+            int idx = i % args.length;
+            res = cmpSometimesEqual1(args[idx]);
+            Asserts.assertTrue(res);
+            res = cmpSometimesEqual2(args[idx]);
+            Asserts.assertFalse(res);
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        if (args.length == 0) {
+            enumerateVMOptions();
+        } else {
+            int nullMode = Integer.valueOf(args[0]);
+            TestNewAcmp t = new TestNewAcmp();
+            t.run(nullMode);
+        }
+    }
+
+    private static String[] addOptions(String prefix[], String... extra) {
+        ArrayList<String> list = new ArrayList<String>();
+        if (prefix != null) {
+            for (String s : prefix) {
+                list.add(s);
+            }
+        }
+        if (extra != null) {
+            for (String s : extra) {
+                System.out.println("    " + s);
+                list.add(s);
+            }
+        }
+
+        return list.toArray(new String[list.size()]);
+    }
+
+    private static void enumerateVMOptions() throws Exception {
+        String[] baseOptions = {
+            "--enable-preview",
+            "-Xbootclasspath/a:.",
+            "-XX:+UnlockDiagnosticVMOptions",
+            "-XX:+WhiteBoxAPI",
+            "-Xbatch",
+            "-XX:TypeProfileLevel=222",
+            "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED",
+            "-XX:CompileCommand=dontinline,compiler.valhalla.inlinetypes.TestNewAcmp::test*",
+            "-XX:CompileCommand=dontinline,compiler.valhalla.inlinetypes.TestNewAcmp::cmp*"};
+
+        String SCENARIOS = System.getProperty("Scenarios", "");
+        List<String> scenarios = null;
+        if (!SCENARIOS.isEmpty()) {
+           scenarios = Arrays.asList(SCENARIOS.split(","));
+        }
+
+        int scenario = -1;
+        for (int nullMode = 0; nullMode <= 2; nullMode++) {          // null mode
+            for (int incrInline = 0; incrInline < 2; incrInline++) { // 0 = default, 1 = -XX:+AlwaysIncrementalInline
+                scenario++;
+                System.out.println("Scenario #" + scenario + " -------------------");
+                String[] cmds = baseOptions;
+                if (incrInline != 0) {
+                    cmds = addOptions(cmds, "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+AlwaysIncrementalInline");
+                }
+
+                cmds = addOptions(cmds, "compiler.valhalla.inlinetypes.TestNewAcmp");
+                cmds = addOptions(cmds, Integer.toString(nullMode));
+
+                if (scenarios != null && !scenarios.contains(Integer.toString(scenario))) {
+                    System.out.println("Scenario #" + scenario + " is skipped due to -Dscenarios=" + SCENARIOS);
+                    continue;
+                }
+
+                OutputAnalyzer oa = ProcessTools.executeTestJava(cmds);
+                String output = oa.getOutput();
+                oa.shouldHaveExitValue(0);
+                System.out.println(output);
+            }
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java
@@ -0,0 +1,3300 @@
+/*
+ * 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.
+ *
+ * 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 compiler.valhalla.inlinetypes;
+
+import jdk.test.lib.Asserts;
+import compiler.lib.ir_framework.*;
+import static compiler.valhalla.inlinetypes.InlineTypes.rI;
+import static compiler.valhalla.inlinetypes.InlineTypes.rL;
+import static compiler.valhalla.inlinetypes.InlineTypes.rD;
+import static compiler.valhalla.inlinetypes.InlineTypeIRNode.*;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+/*
+ * @test
+ * @key randomness
+ * @summary Test nullable value class arrays.
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm/timeout=300 compiler.valhalla.inlinetypes.TestNullableArrays
+ */
+
+@ForceCompileClassInitializer
+public class TestNullableArrays {
+
+    public static void main(String[] args) {
+
+        Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS;
+        scenarios[2].addFlags("-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast", "-XX:+StressArrayCopyMacroNode");
+        scenarios[3].addFlags("-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast");
+        scenarios[4].addFlags("-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast");
+        scenarios[5].addFlags("-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast", "-XX:+StressArrayCopyMacroNode");
+
+        InlineTypes.getFramework()
+                   .addScenarios(scenarios)
+                   .addHelperClasses(MyValue1.class,
+                                     MyValue2.class,
+                                     MyValue2Inline.class)
+                   .start();
+    }
+
+    static {
+        // Make sure RuntimeException is loaded to prevent uncommon traps in IR verified tests
+        RuntimeException tmp = new RuntimeException("42");
+    }
+
+    // Helper methods
+
+    protected long hash() {
+        return hash(rI, rL);
+    }
+
+    protected long hash(int x, long y) {
+        return MyValue1.createWithFieldsInline(x, y).hash();
+    }
+
+    private static final MyValue1 testValue1 = MyValue1.createWithFieldsInline(rI, rL);
+
+    // Test nullable value class array creation and initialization
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        counts = {ALLOCA, "= 1"})
+    @IR(applyIf = {"FlatArrayElementMaxSize", "!= -1"},
+        counts = {ALLOCA, "= 1"},
+        failOn = LOAD)
+    public MyValue1[] test1(int len) {
+        MyValue1[] va = new MyValue1[len];
+        if (len > 0) {
+            va[0] = null;
+        }
+        for (int i = 1; i < len; ++i) {
+            va[i] = MyValue1.createWithFieldsDontInline(rI, rL);
+        }
+        return va;
+    }
+
+    @Run(test = "test1")
+    public void test1_verifier() {
+        int len = Math.abs(rI % 10);
+        MyValue1[] va = test1(len);
+        if (len > 0) {
+            Asserts.assertEQ(va[0], null);
+        }
+        for (int i = 1; i < len; ++i) {
+            Asserts.assertEQ(va[i].hash(), hash());
+        }
+    }
+
+    // Test creation of a value class array and element access
+    @Test
+    @IR(failOn = {ALLOC, ALLOCA, LOOP, LOAD, STORE, TRAP})
+    public long test2() {
+        MyValue1[] va = new MyValue1[1];
+        va[0] = MyValue1.createWithFieldsInline(rI, rL);
+        return va[0].hash();
+    }
+
+    @Run(test = "test2")
+    public void test2_verifier() {
+        long result = test2();
+        Asserts.assertEQ(result, hash());
+    }
+
+    // Test receiving a value class array from the interpreter,
+    // updating its elements in a loop and computing a hash.
+    @Test
+    @IR(failOn = {ALLOCA})
+    public long test3(MyValue1[] va) {
+        long result = 0;
+        for (int i = 0; i < 10; ++i) {
+            if (va[i] != null) {
+                result += va[i].hash();
+            }
+            va[i] = MyValue1.createWithFieldsInline(rI + 1, rL + 1);
+        }
+        va[0] = null;
+        return result;
+    }
+
+    @Run(test = "test3")
+    public void test3_verifier() {
+        MyValue1[] va = new MyValue1[10];
+        long expected = 0;
+        for (int i = 1; i < 10; ++i) {
+            va[i] = MyValue1.createWithFieldsDontInline(rI + i, rL + i);
+            expected += va[i].hash();
+        }
+        long result = test3(va);
+        Asserts.assertEQ(expected, result);
+        Asserts.assertEQ(va[0], null);
+        for (int i = 1; i < 10; ++i) {
+            if (va[i].hash() != hash(rI + 1, rL + 1)) {
+                Asserts.assertEQ(va[i].hash(), hash(rI + 1, rL + 1));
+            }
+        }
+    }
+
+    // Test returning a value class array received from the interpreter
+    @Test
+    @IR(failOn = {ALLOC, ALLOCA, LOAD, STORE, LOOP, TRAP})
+    public MyValue1[] test4(MyValue1[] va) {
+        return va;
+    }
+
+    @Run(test = "test4")
+    public void test4_verifier() {
+        MyValue1[] va = new MyValue1[10];
+        for (int i = 0; i < 10; ++i) {
+            va[i] = MyValue1.createWithFieldsDontInline(rI + i, rL + i);
+        }
+        va = test4(va);
+        for (int i = 0; i < 10; ++i) {
+            Asserts.assertEQ(va[i].hash(), hash(rI + i, rL + i));
+        }
+    }
+
+    // Merge value class arrays created from two branches
+    @Test
+    public MyValue1[] test5(boolean b) {
+        MyValue1[] va;
+        if (b) {
+            va = new MyValue1[5];
+            for (int i = 0; i < 5; ++i) {
+                va[i] = MyValue1.createWithFieldsInline(rI, rL);
+            }
+            va[4] = null;
+        } else {
+            va = new MyValue1[10];
+            for (int i = 0; i < 10; ++i) {
+                va[i] = MyValue1.createWithFieldsInline(rI + i, rL + i);
+            }
+            va[9] = null;
+        }
+        long sum = va[0].hashInterpreted();
+        if (b) {
+            va[0] = MyValue1.createWithFieldsDontInline(rI, sum);
+        } else {
+            va[0] = MyValue1.createWithFieldsDontInline(rI + 1, sum + 1);
+        }
+        return va;
+    }
+
+    @Run(test = "test5")
+    public void test5_verifier() {
+        MyValue1[] va = test5(true);
+        Asserts.assertEQ(va.length, 5);
+        Asserts.assertEQ(va[0].hash(), hash(rI, hash()));
+        for (int i = 1; i < 4; ++i) {
+            Asserts.assertEQ(va[i].hash(), hash());
+        }
+        Asserts.assertEQ(va[4], null);
+        va = test5(false);
+        Asserts.assertEQ(va.length, 10);
+        Asserts.assertEQ(va[0].hash(), hash(rI + 1, hash(rI, rL) + 1));
+        for (int i = 1; i < 9; ++i) {
+            Asserts.assertEQ(va[i].hash(), hash(rI + i, rL + i));
+        }
+        Asserts.assertEQ(va[9], null);
+    }
+
+    // Test creation of value class array with single element
+    @Test
+    @IR(failOn = {ALLOC, ALLOCA, LOOP, LOAD, STORE, TRAP})
+    public MyValue1 test6() {
+        MyValue1[] va = new MyValue1[1];
+        return va[0];
+    }
+
+    @Run(test = "test6")
+    public void test6_verifier() {
+        MyValue1[] va = new MyValue1[1];
+        MyValue1 v = test6();
+        Asserts.assertEQ(v, null);
+    }
+
+    // Test default initialization of value class arrays
+    @Test
+    @IR(failOn = LOAD)
+    public MyValue1[] test7(int len) {
+        return new MyValue1[len];
+    }
+
+    @Run(test = "test7")
+    public void test7_verifier() {
+        int len = Math.abs(rI % 10);
+        MyValue1[] va = test7(len);
+        for (int i = 0; i < len; ++i) {
+            Asserts.assertEQ(va[i], null);
+            va[i] = null;
+        }
+    }
+
+    // Test creation of value class array with zero length
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE, LOOP, TRAP})
+    public MyValue1[] test8() {
+        return new MyValue1[0];
+    }
+
+    @Run(test = "test8")
+    public void test8_verifier() {
+        MyValue1[] va = test8();
+        Asserts.assertEQ(va.length, 0);
+    }
+
+    static MyValue1[] test9_va;
+
+    // Test that value class array loaded from field has correct type
+    @Test
+    @IR(failOn = LOOP)
+    public long test9() {
+        return test9_va[0].hash();
+    }
+
+    @Run(test = "test9")
+    public void test9_verifier() {
+        test9_va = new MyValue1[1];
+        test9_va[0] = testValue1;
+        long result = test9();
+        Asserts.assertEQ(result, hash());
+    }
+
+    // Multi-dimensional arrays
+    @Test
+    public MyValue1[][][] test10(int len1, int len2, int len3) {
+        MyValue1[][][] arr = new MyValue1[len1][len2][len3];
+        for (int i = 0; i < len1; i++) {
+            for (int j = 0; j < len2; j++) {
+                for (int k = 0; k < len3; k++) {
+                    arr[i][j][k] = MyValue1.createWithFieldsDontInline(rI + i , rL + j + k);
+                    if (k == 0) {
+                        arr[i][j][k] = null;
+                    }
+                }
+            }
+        }
+        return arr;
+    }
+
+    @Run(test = "test10")
+    public void test10_verifier() {
+        MyValue1[][][] arr = test10(2, 3, 4);
+        for (int i = 0; i < 2; i++) {
+            for (int j = 0; j < 3; j++) {
+                for (int k = 0; k < 4; k++) {
+                    if (k == 0) {
+                        Asserts.assertEQ(arr[i][j][k], null);
+                    } else {
+                        Asserts.assertEQ(arr[i][j][k].hash(), MyValue1.createWithFieldsDontInline(rI + i , rL + j + k).hash());
+                    }
+                    arr[i][j][k] = null;
+                }
+            }
+        }
+    }
+
+    @Test
+    public void test11(MyValue1[][][] arr, long[] res) {
+        int l = 0;
+        for (int i = 0; i < arr.length; i++) {
+            for (int j = 0; j < arr[i].length; j++) {
+                for (int k = 0; k < arr[i][j].length; k++) {
+                    if (arr[i][j][k] != null) {
+                        res[l] = arr[i][j][k].hash();
+                    }
+                    arr[i][j][k] = null;
+                    l++;
+                }
+            }
+        }
+    }
+
+    @Run(test = "test11")
+    public void test11_verifier() {
+        MyValue1[][][] arr = new MyValue1[2][3][4];
+        long[] res = new long[2*3*4];
+        long[] verif = new long[2*3*4];
+        int l = 0;
+        for (int i = 0; i < 2; i++) {
+            for (int j = 0; j < 3; j++) {
+                for (int k = 0; k < 4; k++) {
+                    if (j != 2) {
+                        arr[i][j][k] = MyValue1.createWithFieldsDontInline(rI + i, rL + j + k);
+                        verif[l] = arr[i][j][k].hash();
+                    }
+                    l++;
+                }
+            }
+        }
+        test11(arr, res);
+        for (int i = 0; i < verif.length; i++) {
+            Asserts.assertEQ(res[i], verif[i]);
+        }
+    }
+
+    // Array load out of bounds (upper bound) at compile time
+    @Test
+    public int test12() {
+        int arraySize = Math.abs(rI) % 10;
+        MyValue1[] va = new MyValue1[arraySize];
+
+        for (int i = 0; i < arraySize; i++) {
+            va[i] = MyValue1.createWithFieldsDontInline(rI + 1, rL);
+        }
+
+        try {
+            return va[arraySize + 1].x;
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return rI;
+        }
+    }
+
+    @Run(test = "test12")
+    public void test12_verifier() {
+        Asserts.assertEQ(test12(), rI);
+    }
+
+    // Array load  out of bounds (lower bound) at compile time
+    @Test
+    public int test13() {
+        int arraySize = Math.abs(rI) % 10;
+        MyValue1[] va = new MyValue1[arraySize];
+
+        for (int i = 0; i < arraySize; i++) {
+            va[i] = MyValue1.createWithFieldsDontInline(rI + i, rL);
+        }
+
+        try {
+            return va[-arraySize].x;
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return rI;
+        }
+    }
+
+    @Run(test = "test13")
+    public void test13_verifier() {
+        Asserts.assertEQ(test13(), rI);
+    }
+
+    // Array load out of bound not known to compiler (both lower and upper bound)
+    @Test
+    public int test14(MyValue1[] va, int index)  {
+        return va[index].x;
+    }
+
+    @Run(test = "test14")
+    public void test14_verifier() {
+        int arraySize = Math.abs(rI) % 10;
+        MyValue1[] va = new MyValue1[arraySize];
+
+        for (int i = 0; i < arraySize; i++) {
+            va[i] = MyValue1.createWithFieldsDontInline(rI, rL);
+        }
+
+        int result;
+        for (int i = -20; i < 20; i++) {
+            try {
+                result = test14(va, i);
+            } catch (ArrayIndexOutOfBoundsException e) {
+                result = rI;
+            }
+            Asserts.assertEQ(result, rI);
+        }
+    }
+
+    // Array store out of bounds (upper bound) at compile time
+    @Test
+    public int test15() {
+        int arraySize = Math.abs(rI) % 10;
+        MyValue1[] va = new MyValue1[arraySize];
+
+        try {
+            for (int i = 0; i <= arraySize; i++) {
+                va[i] = MyValue1.createWithFieldsDontInline(rI + 1, rL);
+            }
+            return rI - 1;
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return rI;
+        }
+    }
+
+    @Run(test = "test15")
+    public void test15_verifier() {
+        Asserts.assertEQ(test15(), rI);
+    }
+
+    // Array store out of bounds (lower bound) at compile time
+    @Test
+    public int test16() {
+        int arraySize = Math.abs(rI) % 10;
+        MyValue1[] va = new MyValue1[arraySize];
+
+        try {
+            for (int i = -1; i <= arraySize; i++) {
+                va[i] = MyValue1.createWithFieldsDontInline(rI + 1, rL);
+            }
+            return rI - 1;
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return rI;
+        }
+    }
+
+    @Run(test = "test16")
+    public void test16_verifier() {
+        Asserts.assertEQ(test16(), rI);
+    }
+
+    // Array store out of bound not known to compiler (both lower and upper bound)
+    @Test
+    public int test17(MyValue1[] va, int index, MyValue1 vt)  {
+        va[index] = vt;
+        return va[index].x;
+    }
+
+    @Run(test = "test17")
+    public void test17_verifier() {
+        int arraySize = Math.abs(rI) % 10;
+        MyValue1[] va = new MyValue1[arraySize];
+
+        for (int i = 0; i < arraySize; i++) {
+            va[i] = MyValue1.createWithFieldsDontInline(rI, rL);
+        }
+
+        MyValue1 vt = MyValue1.createWithFieldsDontInline(rI + 1, rL);
+        int result;
+        for (int i = -20; i < 20; i++) {
+            try {
+                result = test17(va, i, vt);
+            } catch (ArrayIndexOutOfBoundsException e) {
+                result = rI + 1;
+            }
+            Asserts.assertEQ(result, rI + 1);
+        }
+
+        for (int i = 0; i < arraySize; i++) {
+            Asserts.assertEQ(va[i].x, rI + 1);
+        }
+    }
+
+    // clone() as stub call
+    @Test
+    public MyValue1[] test18(MyValue1[] va) {
+        return va.clone();
+    }
+
+    @Run(test = "test18")
+    public void test18_verifier(RunInfo info) {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va1 = new MyValue1[len];
+        MyValue1[]  va2 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        for (int i = 1; i < len; ++i) {
+            va1[i] = testValue1;
+            va2[i] = testValue1;
+        }
+        MyValue1[] result1 = test18(va1);
+        if (len > 0) {
+            Asserts.assertEQ(result1[0], null);
+        }
+        for (int i = 1; i < len; ++i) {
+            Asserts.assertEQ(result1[i].hash(), va1[i].hash());
+        }
+        // make sure we do deopt: GraphKit::new_array assumes an
+        // array of references
+        for (int j = 0; j < 10; j++) {
+            MyValue1[] result2 = test18(va2);
+
+            for (int i = 0; i < len; ++i) {
+                Asserts.assertEQ(result2[i].hash(), va2[i].hash());
+            }
+        }
+        if (compile_and_run_again_if_deoptimized(info)) {
+            MyValue1[] result2 = test18(va2);
+            for (int i = 0; i < len; ++i) {
+                Asserts.assertEQ(result2[i].hash(), va2[i].hash());
+            }
+        }
+    }
+
+    // clone() as series of loads/stores
+    static MyValue1[] test19_orig = null;
+
+    @Test
+    public MyValue1[] test19() {
+        MyValue1[] va = new MyValue1[8];
+        for (int i = 1; i < va.length; ++i) {
+            va[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        test19_orig = va;
+
+        return va.clone();
+    }
+
+    @Run(test = "test19")
+    public void test19_verifier() {
+        MyValue1[] result = test19();
+        Asserts.assertEQ(result[0], null);
+        for (int i = 1; i < test19_orig.length; ++i) {
+            Asserts.assertEQ(result[i].hash(), test19_orig[i].hash());
+        }
+    }
+
+    // arraycopy() of value class array with oop fields
+    @Test
+    public void test20(MyValue1[] src, MyValue1[] dst) {
+        System.arraycopy(src, 0, dst, 0, src.length);
+    }
+
+    @Run(test = "test20")
+    public void test20_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] src1 = new MyValue1[len];
+        MyValue1[] src2 = new MyValue1[len];
+        MyValue1[]  src3 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        MyValue1[]  src4 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        MyValue1[] dst1 = new MyValue1[len];
+        MyValue1[]  dst2 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        MyValue1[] dst3 = new MyValue1[len];
+        MyValue1[]  dst4 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        if (len > 0) {
+            src2[0] = testValue1;
+        }
+        for (int i = 1; i < len; ++i) {
+            src1[i] = testValue1;
+            src2[i] = testValue1;
+            src3[i] = testValue1;
+            src4[i] = testValue1;
+        }
+        test20(src1, dst1);
+        test20(src2, dst2);
+        test20(src3, dst3);
+        test20(src4, dst4);
+        if (len > 0) {
+            Asserts.assertEQ(dst1[0], null);
+            Asserts.assertEQ(dst2[0].hash(), src2[0].hash());
+            Asserts.assertEQ(dst3[0].hash(), src3[0].hash());
+            Asserts.assertEQ(dst4[0].hash(), src4[0].hash());
+        }
+        for (int i = 1; i < len; ++i) {
+            Asserts.assertEQ(src1[i].hash(), dst1[i].hash());
+            Asserts.assertEQ(src2[i].hash(), dst2[i].hash());
+            Asserts.assertEQ(src3[i].hash(), dst3[i].hash());
+            Asserts.assertEQ(src4[i].hash(), dst4[i].hash());
+        }
+    }
+
+    // arraycopy() of value class array with no oop field
+    @Test
+    public void test21(MyValue2[] src, MyValue2[] dst) {
+        System.arraycopy(src, 0, dst, 0, src.length);
+    }
+
+    @Run(test = "test21")
+    public void test21_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue2[] src1 = new MyValue2[len];
+        MyValue2[] src2 = new MyValue2[len];
+        MyValue2[]  src3 = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, len);
+        MyValue2[]  src4 = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, len);
+        MyValue2[] dst1 = new MyValue2[len];
+        MyValue2[]  dst2 = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, len);
+        MyValue2[] dst3 = new MyValue2[len];
+        MyValue2[]  dst4 = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, len);
+        if (len > 0) {
+            src2[0] = MyValue2.createWithFieldsInline(rI, rD);
+        }
+        for (int i = 1; i < len; ++i) {
+            src1[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+            src2[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+            src3[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+            src4[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test21(src1, dst1);
+        test21(src2, dst2);
+        test21(src3, dst3);
+        test21(src4, dst4);
+        if (len > 0) {
+            Asserts.assertEQ(dst1[0], null);
+            Asserts.assertEQ(dst2[0].hash(), src2[0].hash());
+            Asserts.assertEQ(dst3[0].hash(), src3[0].hash());
+            Asserts.assertEQ(dst4[0].hash(), src4[0].hash());
+        }
+        for (int i = 1; i < len; ++i) {
+            Asserts.assertEQ(src1[i].hash(), dst1[i].hash());
+            Asserts.assertEQ(src2[i].hash(), dst2[i].hash());
+            Asserts.assertEQ(src3[i].hash(), dst3[i].hash());
+            Asserts.assertEQ(src4[i].hash(), dst4[i].hash());
+        }
+    }
+
+    // arraycopy() of value class array with oop field and tightly
+    // coupled allocation as dest
+    @Test
+    public MyValue1[] test22(MyValue1[] src) {
+        MyValue1[] dst = new MyValue1[src.length];
+        System.arraycopy(src, 0, dst, 0, src.length);
+        return dst;
+    }
+
+    @Run(test = "test22")
+    public void test22_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] src1 = new MyValue1[len];
+        MyValue1[]  src2 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        for (int i = 1; i < len; ++i) {
+            src1[i] = testValue1;
+            src2[i] = testValue1;
+        }
+        MyValue1[] dst1 = test22(src1);
+        MyValue1[] dst2 = test22(src2);
+        if (len > 0) {
+            Asserts.assertEQ(dst1[0], null);
+            Asserts.assertEQ(dst2[0].hash(), MyValue1.createDefaultInline().hash());
+        }
+        for (int i = 1; i < len; ++i) {
+            Asserts.assertEQ(src1[i].hash(), dst1[i].hash());
+            Asserts.assertEQ(src2[i].hash(), dst2[i].hash());
+        }
+    }
+
+    // arraycopy() of value class array with oop fields and tightly
+    // coupled allocation as dest
+    @Test
+    public MyValue1[] test23(MyValue1[] src) {
+        MyValue1[] dst = new MyValue1[src.length + 10];
+        System.arraycopy(src, 0, dst, 5, src.length);
+        return dst;
+    }
+
+    @Run(test = "test23")
+    public void test23_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] src1 = new MyValue1[len];
+        MyValue1[] src2 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        for (int i = 0; i < len; ++i) {
+            src1[i] = testValue1;
+            src2[i] = testValue1;
+        }
+        MyValue1[] dst1 = test23(src1);
+        MyValue1[] dst2 = test23(src2);
+        for (int i = 0; i < 5; ++i) {
+            Asserts.assertEQ(dst1[i], null);
+            Asserts.assertEQ(dst2[i], null);
+        }
+        for (int i = 5; i < len; ++i) {
+            Asserts.assertEQ(src1[i].hash(), dst1[i].hash());
+            Asserts.assertEQ(src2[i].hash(), dst2[i].hash());
+        }
+    }
+
+    // arraycopy() of value class array passed as Object
+    @Test
+    public void test24(MyValue1[] src, Object dst) {
+        System.arraycopy(src, 0, dst, 0, src.length);
+    }
+
+    @Run(test = "test24")
+    public void test24_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] src1 = new MyValue1[len];
+        MyValue1[] src2 = new MyValue1[len];
+        MyValue1[]  src3 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        MyValue1[]  src4 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        MyValue1[] dst1 = new MyValue1[len];
+        MyValue1[]  dst2 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        MyValue1[] dst3 = new MyValue1[len];
+        MyValue1[]  dst4 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        if (len > 0) {
+            src2[0] = testValue1;
+        }
+        for (int i = 1; i < len; ++i) {
+            src1[i] = testValue1;
+            src2[i] = testValue1;
+            src3[i] = testValue1;
+            src4[i] = testValue1;
+        }
+        test24(src1, dst1);
+        test24(src2, dst2);
+        test24(src3, dst3);
+        test24(src4, dst4);
+        if (len > 0) {
+            Asserts.assertEQ(dst1[0], null);
+            Asserts.assertEQ(dst2[0].hash(), src2[0].hash());
+            Asserts.assertEQ(dst3[0].hash(), src3[0].hash());
+            Asserts.assertEQ(dst4[0].hash(), src4[0].hash());
+        }
+        for (int i = 1; i < len; ++i) {
+            Asserts.assertEQ(src1[i].hash(), dst1[i].hash());
+            Asserts.assertEQ(src2[i].hash(), dst2[i].hash());
+            Asserts.assertEQ(src3[i].hash(), dst3[i].hash());
+            Asserts.assertEQ(src4[i].hash(), dst4[i].hash());
+        }
+    }
+
+    // short arraycopy() with no oop field
+    @Test
+    public void test25(MyValue2[] src, MyValue2[] dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test25")
+    public void test25_verifier() {
+        MyValue2[] src1 = new MyValue2[8];
+        MyValue2[] src2 = new MyValue2[8];
+        MyValue2[]  src3 = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 8);
+        MyValue2[]  src4 = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 8);
+        MyValue2[] dst1 = new MyValue2[8];
+        MyValue2[]  dst2 = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 8);
+        MyValue2[] dst3 = new MyValue2[8];
+        MyValue2[]  dst4 = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 8);
+        src2[0] = MyValue2.createWithFieldsInline(rI, rD);
+        for (int i = 1; i < 8; ++i) {
+            src1[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+            src2[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+            src3[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+            src4[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test25(src1, dst1);
+        test25(src2, dst2);
+        test25(src3, dst3);
+        test25(src4, dst4);
+        Asserts.assertEQ(dst1[0], null);
+        Asserts.assertEQ(dst2[0].hash(), src2[0].hash());
+        Asserts.assertEQ(dst3[0].hash(), src3[0].hash());
+        Asserts.assertEQ(dst4[0].hash(), src4[0].hash());
+        for (int i = 1; i < 8; ++i) {
+            Asserts.assertEQ(src1[i].hash(), dst1[i].hash());
+            Asserts.assertEQ(src2[i].hash(), dst2[i].hash());
+            Asserts.assertEQ(src3[i].hash(), dst3[i].hash());
+            Asserts.assertEQ(src4[i].hash(), dst4[i].hash());
+        }
+    }
+
+    // short arraycopy() with oop fields
+    @Test
+    public void test26(MyValue1[] src, MyValue1[] dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test26")
+    public void test26_verifier() {
+        MyValue1[] src1 = new MyValue1[8];
+        MyValue1[] src2 = new MyValue1[8];
+        MyValue1[]  src3 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+        MyValue1[]  src4 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+        MyValue1[] dst1 = new MyValue1[8];
+        MyValue1[]  dst2 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+        MyValue1[] dst3 = new MyValue1[8];
+        MyValue1[]  dst4 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+        src2[0] = testValue1;
+        for (int i = 1; i < 8; ++i) {
+            src1[i] = testValue1;
+            src2[i] = testValue1;
+            src3[i] = testValue1;
+            src4[i] = testValue1;
+        }
+        test26(src1, dst1);
+        test26(src2, dst2);
+        test26(src3, dst3);
+        test26(src4, dst4);
+        Asserts.assertEQ(dst1[0], null);
+        Asserts.assertEQ(dst2[0].hash(), src2[0].hash());
+        Asserts.assertEQ(dst3[0].hash(), src3[0].hash());
+        Asserts.assertEQ(dst4[0].hash(), src4[0].hash());
+        for (int i = 1; i < 8; ++i) {
+            Asserts.assertEQ(src1[i].hash(), dst1[i].hash());
+            Asserts.assertEQ(src2[i].hash(), dst2[i].hash());
+            Asserts.assertEQ(src3[i].hash(), dst3[i].hash());
+            Asserts.assertEQ(src4[i].hash(), dst4[i].hash());
+        }
+    }
+
+    // short arraycopy() with oop fields and offsets
+    @Test
+    public void test27(MyValue1[] src, MyValue1[] dst) {
+        System.arraycopy(src, 1, dst, 2, 6);
+    }
+
+    @Run(test = "test27")
+    public void test27_verifier() {
+        MyValue1[] src1 = new MyValue1[8];
+        MyValue1[] src2 = new MyValue1[8];
+        MyValue1[]  src3 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+        MyValue1[]  src4 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+        MyValue1[] dst1 = new MyValue1[8];
+        MyValue1[]  dst2 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+        MyValue1[] dst3 = new MyValue1[8];
+        MyValue1[]  dst4 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+        for (int i = 1; i < 8; ++i) {
+            src1[i] = testValue1;
+            src2[i] = testValue1;
+            src3[i] = testValue1;
+            src4[i] = testValue1;
+        }
+        test27(src1, dst1);
+        test27(src2, dst2);
+        test27(src3, dst3);
+        test27(src4, dst4);
+        for (int i = 0; i < 2; ++i) {
+            Asserts.assertEQ(dst1[i], null);
+            Asserts.assertEQ(dst2[i].hash(), MyValue1.createDefaultInline().hash());
+            Asserts.assertEQ(dst3[i], null);
+            Asserts.assertEQ(dst4[i].hash(), MyValue1.createDefaultInline().hash());
+        }
+        for (int i = 2; i < 8; ++i) {
+            Asserts.assertEQ(src1[i].hash(), dst1[i].hash());
+            Asserts.assertEQ(src2[i].hash(), dst2[i].hash());
+            Asserts.assertEQ(src3[i].hash(), dst3[i].hash());
+            Asserts.assertEQ(src4[i].hash(), dst4[i].hash());
+        }
+    }
+
+    // non escaping allocations
+    // TODO 8252027: Make sure this is optimized with ZGC
+    @Test
+    @IR(applyIf = {"UseZGC", "false"},
+        failOn = {ALLOC, ALLOCA, LOOP, LOAD, STORE, TRAP})
+    public MyValue2 test28() {
+        MyValue2[] src = new MyValue2[10];
+        src[0] = null;
+        MyValue2[] dst = (MyValue2[])src.clone();
+        return dst[0];
+    }
+
+    @Run(test = "test28")
+    public void test28_verifier() {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, rD);
+        MyValue2 result = test28();
+        Asserts.assertEQ(result, null);
+    }
+
+    // non escaping allocations
+    // TODO 8227588: shouldn't this have the same IR matching rules as test6?
+    @Test
+    @IR(failOn = {ALLOCA, LOOP, TRAP})
+    public MyValue2 test29(MyValue2[] src) {
+        MyValue2[] dst = new MyValue2[10];
+        System.arraycopy(src, 0, dst, 0, 10);
+        return dst[0];
+    }
+
+    @Run(test = "test29")
+    public void test29_verifier(RunInfo info) {
+        MyValue2[] src1 = new MyValue2[10];
+        MyValue2[] src2 = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 10);
+        for (int i = 0; i < 10; ++i) {
+            src1[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+            src2[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        MyValue2 v = test29(src1);
+        Asserts.assertEQ(src1[0].hash(), v.hash());
+        if (!info.isWarmUp()) {
+            v = test29(src2);
+            Asserts.assertEQ(src2[0].hash(), v.hash());
+        }
+    }
+
+    // non escaping allocation with uncommon trap that needs
+    // eliminated value class array element as debug info
+    @Test
+    public MyValue2 test30(MyValue2[] src, boolean flag) {
+        MyValue2[] dst = new MyValue2[10];
+        System.arraycopy(src, 0, dst, 0, 10);
+        if (flag) { }
+        return dst[0];
+    }
+
+    @Run(test = "test30")
+    @Warmup(10000)
+    public void test30_verifier(RunInfo info) {
+        MyValue2[] src1 = new MyValue2[10];
+        MyValue2[] src2 = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 10);
+        for (int i = 0; i < 10; ++i) {
+            src1[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+            src2[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        MyValue2 v = test30(src1, !info.isWarmUp());
+        Asserts.assertEQ(src1[0].hash(), v.hash());
+        if (!info.isWarmUp()) {
+            v = test30(src2, true);
+            Asserts.assertEQ(src2[0].hash(), v.hash());
+        }
+    }
+
+    // non escaping allocation with memory phi
+    @Test
+    // TODO 8227588
+    // @Test(failOn = ALLOC + ALLOCA + LOOP + LOAD + STORE + TRAP)
+    public long test31(boolean b, boolean deopt, Method m) {
+        MyValue2[] src = new MyValue2[1];
+        if (b) {
+            src[0] = MyValue2.createWithFieldsInline(rI, rD);
+        } else {
+            src[0] = MyValue2.createWithFieldsInline(rI+1, rD+1);
+        }
+        if (deopt) {
+            // uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return src[0].hash();
+    }
+
+    @Run(test = "test31")
+    public void test31_verifier(RunInfo info) {
+        MyValue2 v1 = MyValue2.createWithFieldsInline(rI, rD);
+        long result1 = test31(true, !info.isWarmUp(), info.getTest());
+        Asserts.assertEQ(result1, v1.hash());
+        MyValue2 v2 = MyValue2.createWithFieldsInline(rI+1, rD+1);
+        long result2 = test31(false, !info.isWarmUp(), info.getTest());
+        Asserts.assertEQ(result2, v2.hash());
+    }
+
+    // Tests with Object arrays and clone/arraycopy
+    // clone() as stub call
+    @Test
+    public Object[] test32(Object[] va) {
+        return va.clone();
+    }
+
+    @Run(test = "test32")
+    public void test32_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va1 = new MyValue1[len];
+        MyValue1[] va2 = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, len);
+        for (int i = 1; i < len; ++i) {
+            va1[i] = testValue1;
+            va2[i] = testValue1;
+        }
+        MyValue1[] result1 = (MyValue1[])test32(va1);
+        MyValue1[] result2 = (MyValue1[])test32(va2);
+        if (len > 0) {
+            Asserts.assertEQ(result1[0], null);
+            Asserts.assertEQ(result2[0].hash(), MyValue1.createDefaultInline().hash());
+        }
+        for (int i = 1; i < len; ++i) {
+            Asserts.assertEQ(((MyValue1)result1[i]).hash(), ((MyValue1)va1[i]).hash());
+            Asserts.assertEQ(((MyValue1)result2[i]).hash(), ((MyValue1)va2[i]).hash());
+        }
+    }
+
+    @Test
+    public Object[] test33(Object[] va) {
+        return va.clone();
+    }
+
+    @Run(test = "test33")
+    public void test33_verifier() {
+        int len = Math.abs(rI) % 10;
+        Object[] va = new Object[len];
+        for (int i = 0; i < len; ++i) {
+            va[i] = testValue1;
+        }
+        Object[] result = test33(va);
+        for (int i = 0; i < len; ++i) {
+            Asserts.assertEQ(((MyValue1)result[i]).hash(), ((MyValue1)va[i]).hash());
+        }
+    }
+
+    // clone() as series of loads/stores
+    static Object[] test34_orig = null;
+
+    @ForceInline
+    public Object[] test34_helper(boolean flag) {
+        Object[] va = null;
+        if (flag) {
+            va = new MyValue1[8];
+            for (int i = 0; i < va.length; ++i) {
+                va[i] = MyValue1.createWithFieldsDontInline(rI, rL);
+            }
+        } else {
+            va = new Object[8];
+        }
+        return va;
+    }
+
+    @Test
+    public Object[] test34(boolean flag) {
+        Object[] va = test34_helper(flag);
+        test34_orig = va;
+        return va.clone();
+    }
+
+    @Run(test = "test34")
+    public void test34_verifier(RunInfo info) {
+        test34(false);
+        for (int i = 0; i < 10; i++) { // make sure we do deopt
+            Object[] result = test34(true);
+            verify(test34_orig, result);
+        }
+        if (compile_and_run_again_if_deoptimized(info)) {
+            Object[] result = test34(true);
+            verify(test34_orig, result);
+        }
+    }
+
+    static void verify(Object[] src, Object[] dst) {
+        for (int i = 0; i < src.length; ++i) {
+            if (src[i] != null) {
+                Asserts.assertEQ(((MyInterface)src[i]).hash(), ((MyInterface)dst[i]).hash());
+            } else {
+                Asserts.assertEQ(dst[i], null);
+            }
+        }
+    }
+
+    static void verify(MyValue1[] src, MyValue1[] dst) {
+        for (int i = 0; i < src.length; ++i) {
+            if (src[i] != null) {
+                Asserts.assertEQ(src[i].hash(), dst[i].hash());
+            } else {
+                Asserts.assertEQ(dst[i], null);
+            }
+        }
+    }
+
+    static void verify(MyValue1[] src, Object[] dst) {
+        for (int i = 0; i < src.length; ++i) {
+            if (src[i] != null) {
+                Asserts.assertEQ(src[i].hash(), ((MyInterface)dst[i]).hash());
+            } else {
+                Asserts.assertEQ(dst[i], null);
+            }
+        }
+    }
+
+    static void verify(MyValue2[] src, MyValue2[] dst) {
+        for (int i = 0; i < src.length; ++i) {
+            if (src[i] != null) {
+                Asserts.assertEQ(src[i].hash(), dst[i].hash());
+            } else {
+                Asserts.assertEQ(dst[i], null);
+            }
+        }
+    }
+
+    static void verify(MyValue2[] src, Object[] dst) {
+        for (int i = 0; i < src.length; ++i) {
+            if (src[i] != null) {
+                Asserts.assertEQ(src[i].hash(), ((MyInterface)dst[i]).hash());
+            } else {
+                Asserts.assertEQ(dst[i], null);
+            }
+        }
+    }
+
+    static boolean compile_and_run_again_if_deoptimized(RunInfo info) {
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            if (TestFramework.isCompiled(m)) {
+                TestFramework.compile(m, CompLevel.C2);
+            }
+        }
+        return false;
+    }
+
+    // arraycopy() of value class array of unknown size
+    @Test
+    public void test35(Object src, Object dst, int len) {
+        System.arraycopy(src, 0, dst, 0, len);
+    }
+
+    @Run(test = "test35")
+    public void test35_verifier(RunInfo info) {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] src = new MyValue1[len];
+        MyValue1[] dst = new MyValue1[len];
+        for (int i = 1; i < len; ++i) {
+            src[i] = testValue1;
+        }
+        test35(src, dst, src.length);
+        verify(src, dst);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test35(src, dst, src.length);
+            verify(src, dst);
+        }
+    }
+
+    @Test
+    public void test36(Object src, MyValue2[] dst) {
+        System.arraycopy(src, 0, dst, 0, dst.length);
+    }
+
+    @Run(test = "test36")
+    public void test36_verifier(RunInfo info) {
+        int len = Math.abs(rI) % 10;
+        MyValue2[] src = new MyValue2[len];
+        MyValue2[] dst = new MyValue2[len];
+        for (int i = 1; i < len; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test36(src, dst);
+        verify(src, dst);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test36(src, dst);
+            verify(src, dst);
+        }
+    }
+
+    @Test
+    public void test37(MyValue2[] src, Object dst) {
+        System.arraycopy(src, 0, dst, 0, src.length);
+    }
+
+    @Run(test = "test37")
+    public void test37_verifier(RunInfo info) {
+        int len = Math.abs(rI) % 10;
+        MyValue2[] src = new MyValue2[len];
+        MyValue2[] dst = new MyValue2[len];
+        for (int i = 1; i < len; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test37(src, dst);
+        verify(src, dst);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test37(src, dst);
+            verify(src, dst);
+        }
+    }
+
+    @Test
+    public void test38(Object src, MyValue2[] dst) {
+        System.arraycopy(src, 0, dst, 0, dst.length);
+    }
+
+    @Run(test = "test38")
+    @Warmup(1) // Avoid early compilation
+    public void test38_verifier(RunInfo info) {
+        int len = Math.abs(rI) % 10;
+        Object[] src = new Object[len];
+        MyValue2[] dst = new MyValue2[len];
+        for (int i = 1; i < len; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test38(src, dst);
+        verify(dst, src);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertDeoptimizedByC2(m);
+            TestFramework.compile(m, CompLevel.C2);
+            test38(src, dst);
+            verify(dst, src);
+            TestFramework.assertCompiled(m);
+        }
+    }
+
+    @Test
+    public void test39(MyValue2[] src, Object dst) {
+        System.arraycopy(src, 0, dst, 0, src.length);
+    }
+
+    @Run(test = "test39")
+    public void test39_verifier(RunInfo info) {
+        int len = Math.abs(rI) % 10;
+        MyValue2[] src = new MyValue2[len];
+        Object[] dst = new Object[len];
+        for (int i = 1; i < len; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test39(src, dst);
+        verify(src, dst);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test39(src, dst);
+            verify(src, dst);
+        }
+    }
+
+    @Test
+    public void test40(Object[] src, Object dst) {
+        System.arraycopy(src, 0, dst, 0, src.length);
+    }
+
+    @Run(test = "test40")
+    @Warmup(1) // Avoid early compilation
+    public void test40_verifier(RunInfo info) {
+        int len = Math.abs(rI) % 10;
+        Object[] src = new Object[len];
+        MyValue2[] dst = new MyValue2[len];
+        for (int i = 1; i < len; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test40(src, dst);
+        verify(dst, src);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertDeoptimizedByC2(m);
+            TestFramework.compile(m, CompLevel.C2);
+            test40(src, dst);
+            verify(dst, src);
+            TestFramework.assertCompiled(m);
+        }
+    }
+
+    @Test
+    public void test41(Object src, Object[] dst) {
+        System.arraycopy(src, 0, dst, 0, dst.length);
+    }
+
+    @Run(test = "test41")
+    public void test41_verifier(RunInfo info) {
+        int len = Math.abs(rI) % 10;
+        MyValue2[] src = new MyValue2[len];
+        Object[] dst = new Object[len];
+        for (int i = 1; i < len; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test41(src, dst);
+        verify(src, dst);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test41(src, dst);
+            verify(src, dst);
+        }
+    }
+
+    @Test
+    public void test42(Object[] src, Object[] dst) {
+        System.arraycopy(src, 0, dst, 0, src.length);
+    }
+
+    @Run(test = "test42")
+    public void test42_verifier(RunInfo info) {
+        int len = Math.abs(rI) % 10;
+        Object[] src = new Object[len];
+        Object[] dst = new Object[len];
+        for (int i = 1; i < len; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test42(src, dst);
+        verify(src, dst);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertCompiled(m);
+        }
+    }
+
+    // short arraycopy()'s
+    @Test
+    public void test43(Object src, Object dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test43")
+    public void test43_verifier(RunInfo info) {
+        MyValue1[] src = new MyValue1[8];
+        MyValue1[] dst = new MyValue1[8];
+        for (int i = 1; i < 8; ++i) {
+            src[i] = testValue1;
+        }
+        test43(src, dst);
+        verify(src, dst);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test43(src, dst);
+            verify(src, dst);
+        }
+    }
+
+    @Test
+    public void test44(Object src, MyValue2[] dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test44")
+    public void test44_verifier(RunInfo info) {
+        MyValue2[] src = new MyValue2[8];
+        MyValue2[] dst = new MyValue2[8];
+        for (int i = 1; i < 8; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test44(src, dst);
+        verify(src, dst);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test44(src, dst);
+            verify(src, dst);
+        }
+    }
+
+    @Test
+    public void test45(MyValue2[] src, Object dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test45")
+    public void test45_verifier(RunInfo info) {
+        MyValue2[] src = new MyValue2[8];
+        MyValue2[] dst = new MyValue2[8];
+        for (int i = 1; i < 8; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test45(src, dst);
+        verify(src, dst);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test45(src, dst);
+            verify(src, dst);
+        }
+    }
+
+    @Test
+    public void test46(Object[] src, MyValue2[] dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test46")
+    @Warmup(1) // Avoid early compilation
+    public void test46_verifier(RunInfo info) {
+        Object[] src = new Object[8];
+        MyValue2[] dst = new MyValue2[8];
+        for (int i = 1; i < 8; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test46(src, dst);
+        verify(dst, src);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertDeoptimizedByC2(m);
+            TestFramework.compile(m, CompLevel.C2);
+            test46(src, dst);
+            verify(dst, src);
+            TestFramework.assertCompiled(m);
+        }
+    }
+
+    @Test
+    public void test47(MyValue2[] src, Object[] dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test47")
+    public void test47_verifier(RunInfo info) {
+        MyValue2[] src = new MyValue2[8];
+        Object[] dst = new Object[8];
+        for (int i = 1; i < 8; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test47(src, dst);
+        verify(src, dst);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test47(src, dst);
+            verify(src, dst);
+        }
+    }
+
+    @Test
+    public void test48(Object[] src, Object dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test48")
+    @Warmup(1) // Avoid early compilation
+    public void test48_verifier(RunInfo info) {
+        Object[] src = new Object[8];
+        MyValue2[] dst = new MyValue2[8];
+        for (int i = 1; i < 8; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test48(src, dst);
+        verify(dst, src);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertDeoptimizedByC2(m);
+            TestFramework.compile(m, CompLevel.C2);
+            test48(src, dst);
+            verify(dst, src);
+            TestFramework.assertCompiled(m);
+        }
+    }
+
+    @Test
+    public void test49(Object src, Object[] dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test49")
+    public void test49_verifier(RunInfo info) {
+        MyValue2[] src = new MyValue2[8];
+        Object[] dst = new Object[8];
+        for (int i = 1; i < 8; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test49(src, dst);
+        verify(src, dst);
+        if (compile_and_run_again_if_deoptimized(info)) {
+            test49(src, dst);
+            verify(src, dst);
+        }
+    }
+
+    @Test
+    public void test50(Object[] src, Object[] dst) {
+        System.arraycopy(src, 0, dst, 0, 8);
+    }
+
+    @Run(test = "test50")
+    public void test50_verifier(RunInfo info) {
+        Object[] src = new Object[8];
+        Object[] dst = new Object[8];
+        for (int i = 1; i < 8; ++i) {
+            src[i] = MyValue2.createWithFieldsInline(rI+i, rD+i);
+        }
+        test50(src, dst);
+        verify(src, dst);
+        if (!info.isWarmUp()) {
+            Method m = info.getTest();
+            TestFramework.assertCompiled(m);
+        }
+    }
+
+    @Test
+    public MyValue1[] test51(MyValue1[] va) {
+        return Arrays.copyOf(va, va.length, MyValue1[].class);
+    }
+
+    @Run(test = "test51")
+    public void test51_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = new MyValue1[len];
+        for (int i = 1; i < len; ++i) {
+            va[i] = testValue1;
+        }
+        MyValue1[] result = test51(va);
+        verify(va, result);
+    }
+
+    static final MyValue1[] test52_va = new MyValue1[8];
+
+    @Test
+    public MyValue1[] test52() {
+        return Arrays.copyOf(test52_va, 8, MyValue1[].class);
+    }
+
+    @Run(test = "test52")
+    public void test52_verifier() {
+        for (int i = 1; i < 8; ++i) {
+            test52_va[i] = testValue1;
+        }
+        MyValue1[] result = test52();
+        verify(test52_va, result);
+    }
+
+    @Test
+    public MyValue1[] test53(Object[] va) {
+        return Arrays.copyOf(va, va.length, MyValue1[].class);
+    }
+
+    @Run(test = "test53")
+    public void test53_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = new MyValue1[len];
+        for (int i = 1; i < len; ++i) {
+            va[i] = testValue1;
+        }
+        MyValue1[] result = test53(va);
+        verify(result, va);
+    }
+
+    @Test
+    public Object[] test54(MyValue1[] va) {
+        return Arrays.copyOf(va, va.length, Object[].class);
+    }
+
+    @Run(test = "test54")
+    public void test54_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = new MyValue1[len];
+        for (int i = 1; i < len; ++i) {
+            va[i] = testValue1;
+        }
+        Object[] result = test54(va);
+        verify(va, result);
+    }
+
+    @Test
+    public Object[] test55(Object[] va) {
+        return Arrays.copyOf(va, va.length, Object[].class);
+    }
+
+    @Run(test = "test55")
+    public void test55_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = new MyValue1[len];
+        for (int i = 1; i < len; ++i) {
+            va[i] = testValue1;
+        }
+        Object[] result = test55(va);
+        verify(va, result);
+    }
+
+    @Test
+    public MyValue1[] test56(Object[] va) {
+        return Arrays.copyOf(va, va.length, MyValue1[].class);
+    }
+
+    @Run(test = "test56")
+    public void test56_verifier() {
+        int len = Math.abs(rI) % 10;
+        Object[] va = new Object[len];
+        for (int i = 1; i < len; ++i) {
+            va[i] = testValue1;
+        }
+        MyValue1[] result = test56(va);
+        verify(result, va);
+    }
+
+   @Test
+    public Object[] test57(Object[] va, Class klass) {
+        return Arrays.copyOf(va, va.length, klass);
+    }
+
+    @Run(test = "test57")
+    public void test57_verifier() {
+        int len = Math.abs(rI) % 10;
+        Object[] va = new MyValue1[len];
+        for (int i = 1; i < len; ++i) {
+            va[i] = testValue1;
+        }
+        Object[] result = test57(va, MyValue1[].class);
+        verify(va, result);
+    }
+
+    @Test
+    public Object[] test58(MyValue1[] va, Class klass) {
+        return Arrays.copyOf(va, va.length, klass);
+    }
+
+    @Run(test = "test58")
+    public void test58_verifier(RunInfo info) {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = new MyValue1[len];
+        for (int i = 1; i < len; ++i) {
+            va[i] = testValue1;
+        }
+        for (int i = 1; i < 10; i++) {
+            Object[] result = test58(va, MyValue1[].class);
+            verify(va, result);
+        }
+        if (compile_and_run_again_if_deoptimized(info)) {
+            Object[] result = test58(va, MyValue1[].class);
+            verify(va, result);
+        }
+    }
+
+    @Test
+    public Object[] test59(MyValue1[] va) {
+        return Arrays.copyOf(va, va.length+1, MyValue1[].class);
+    }
+
+    @Run(test = "test59")
+    public void test59_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = new MyValue1[len];
+        MyValue1[] verif = new MyValue1[len+1];
+        for (int i = 1; i < len; ++i) {
+            va[i] = testValue1;
+            verif[i] = va[i];
+        }
+        Object[] result = test59(va);
+        verify(verif, result);
+    }
+
+    @Test
+    public Object[] test60(Object[] va, Class klass) {
+        return Arrays.copyOf(va, va.length+1, klass);
+    }
+
+    @Run(test = "test60")
+    public void test60_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = new MyValue1[len];
+        MyValue1[] verif = new MyValue1[len+1];
+        for (int i = 1; i < len; ++i) {
+            va[i] = testValue1;
+            verif[i] = (MyValue1)va[i];
+        }
+        Object[] result = test60(va, MyValue1[].class);
+        verify(verif, result);
+    }
+
+    @Test
+    public Object[] test61(Object[] va, Class klass) {
+        return Arrays.copyOf(va, va.length+1, klass);
+    }
+
+    @Run(test = "test61")
+    public void test61_verifier() {
+        int len = Math.abs(rI) % 10;
+        Object[] va = new NonValueClass[len];
+        for (int i = 1; i < len; ++i) {
+            va[i] = new NonValueClass(rI);
+        }
+        Object[] result = test61(va, NonValueClass[].class);
+        for (int i = 0; i < va.length; ++i) {
+            Asserts.assertEQ(va[i], result[i]);
+        }
+    }
+
+    @ForceInline
+    public Object[] test62_helper(int i, MyValue1[] va, NonValueClass[] oa) {
+        Object[] arr = null;
+        if (i == 10) {
+            arr = oa;
+        } else {
+            arr = va;
+        }
+        return arr;
+    }
+
+    @Test
+    public Object[] test62(MyValue1[] va, NonValueClass[] oa) {
+        int i = 0;
+        for (; i < 10; i++);
+
+        Object[] arr = test62_helper(i, va, oa);
+
+        return Arrays.copyOf(arr, arr.length+1, arr.getClass());
+    }
+
+    @Run(test = "test62")
+    public void test62_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = new MyValue1[len];
+        NonValueClass[] oa = new NonValueClass[len];
+        for (int i = 1; i < len; ++i) {
+            oa[i] = new NonValueClass(rI);
+        }
+        test62_helper(42, va, oa);
+        Object[] result = test62(va, oa);
+        for (int i = 0; i < va.length; ++i) {
+            Asserts.assertEQ(oa[i], result[i]);
+        }
+    }
+
+    @ForceInline
+    public Object[] test63_helper(int i, MyValue1[] va, NonValueClass[] oa) {
+        Object[] arr = null;
+        if (i == 10) {
+            arr = va;
+        } else {
+            arr = oa;
+        }
+        return arr;
+    }
+
+    @Test
+    public Object[] test63(MyValue1[] va, NonValueClass[] oa) {
+        int i = 0;
+        for (; i < 10; i++);
+
+        Object[] arr = test63_helper(i, va, oa);
+
+        return Arrays.copyOf(arr, arr.length+1, arr.getClass());
+    }
+
+    @Run(test = "test63")
+    public void test63_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = new MyValue1[len];
+        MyValue1[] verif = new MyValue1[len+1];
+        for (int i = 1; i < len; ++i) {
+            va[i] = testValue1;
+            verif[i] = va[i];
+        }
+        NonValueClass[] oa = new NonValueClass[len];
+        test63_helper(42, va, oa);
+        Object[] result = test63(va, oa);
+        verify(verif, result);
+    }
+
+    // Test default initialization of value class arrays: small array
+    @Test
+    public MyValue1[] test64() {
+        return new MyValue1[8];
+    }
+
+    @Run(test = "test64")
+    public void test64_verifier() {
+        MyValue1[] va = test64();
+        for (int i = 0; i < 8; ++i) {
+            Asserts.assertEQ(va[i], null);
+        }
+    }
+
+    // Test default initialization of value class arrays: large array
+    @Test
+    public MyValue1[] test65() {
+        return new MyValue1[32];
+    }
+
+    @Run(test = "test65")
+    public void test65_verifier() {
+        MyValue1[] va = test65();
+        for (int i = 0; i < 32; ++i) {
+            Asserts.assertEQ(va[i], null);
+        }
+    }
+
+    // Check init store elimination
+    @Test
+    @IR(counts = {ALLOCA, "= 1"})
+    public MyValue1[] test66(MyValue1 vt) {
+        MyValue1[] va = new MyValue1[1];
+        va[0] = vt;
+        return va;
+    }
+
+    @Run(test = "test66")
+    public void test66_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsDontInline(rI, rL);
+        MyValue1[] va = test66(vt);
+        Asserts.assertEQ(va[0].hashPrimitive(), vt.hashPrimitive());
+    }
+
+    // Zeroing elimination and arraycopy
+    @Test
+    public MyValue1[] test67(MyValue1[] src) {
+        MyValue1[] dst = new MyValue1[16];
+        System.arraycopy(src, 0, dst, 0, 13);
+        return dst;
+    }
+
+    @Run(test = "test67")
+    public void test67_verifier() {
+        MyValue1[] va = new MyValue1[16];
+        MyValue1[] var = test67(va);
+        for (int i = 0; i < 16; ++i) {
+            Asserts.assertEQ(var[i], null);
+        }
+    }
+
+    // A store with a default value can be eliminated
+    @Test
+    public MyValue1[] test68() {
+        MyValue1[] va = new MyValue1[2];
+        va[0] = va[1];
+        return va;
+    }
+
+    @Run(test = "test68")
+    public void test68_verifier() {
+        MyValue1[] va = test68();
+        for (int i = 0; i < 2; ++i) {
+            Asserts.assertEQ(va[i], null);
+        }
+    }
+
+    // Requires individual stores to init array
+    @Test
+    public MyValue1[] test69(MyValue1 vt) {
+        MyValue1[] va = new MyValue1[4];
+        va[0] = vt;
+        va[3] = vt;
+        return va;
+    }
+
+    @Run(test = "test69")
+    public void test69_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsDontInline(rI, rL);
+        MyValue1[] va = new MyValue1[4];
+        va[0] = vt;
+        va[3] = vt;
+        MyValue1[] var = test69(vt);
+        for (int i = 0; i < va.length; ++i) {
+            Asserts.assertEQ(va[i], var[i]);
+        }
+    }
+
+    // A store with a default value can be eliminated: same as test68
+    // but store is farther away from allocation
+    @Test
+    public MyValue1[] test70(MyValue1[] other) {
+        other[1] = other[0];
+        MyValue1[] va = new MyValue1[2];
+        other[0] = va[1];
+        va[0] = va[1];
+        return va;
+    }
+
+    @Run(test = "test70")
+    public void test70_verifier() {
+        MyValue1[] va = new MyValue1[2];
+        MyValue1[] var = test70(va);
+        for (int i = 0; i < 2; ++i) {
+            Asserts.assertEQ(va[i], var[i]);
+        }
+    }
+
+    // EA needs to consider oop fields in flattened arrays
+    @Test
+    public void test71() {
+        int len = 10;
+        MyValue2[] src = new MyValue2[len];
+        MyValue2[] dst = new MyValue2[len];
+        for (int i = 1; i < len; ++i) {
+            src[i] = MyValue2.createWithFieldsDontInline(rI+i, rD+i);
+        }
+        System.arraycopy(src, 0, dst, 0, src.length);
+        for (int i = 0; i < len; ++i) {
+            if (src[i] == null) {
+                Asserts.assertEQ(dst[i], null);
+            } else {
+                Asserts.assertEQ(src[i].hash(), dst[i].hash());
+            }
+        }
+    }
+
+    @Run(test = "test71")
+    public void test71_verifier() {
+        test71();
+    }
+
+    // Test EA with leaf call to 'store_unknown_value'
+    @Test
+    public void test72(Object[] o, boolean b, Object element) {
+        Object[] arr1 = new Object[10];
+        Object[] arr2 = new Object[10];
+        if (b) {
+            arr1 = o;
+        }
+        arr1[0] = element;
+        arr2[0] = element;
+    }
+
+    @Run(test = "test72")
+    public void test72_verifier() {
+        Object[] arr = new Object[1];
+        Object elem = new Object();
+        test72(arr, true, elem);
+        test72(arr, false, elem);
+    }
+
+    @Test
+    public void test73(Object[] oa, MyValue1 v, Object o) {
+        // TestLWorld.test38 use a C1 Phi node for the array. This test
+        // adds the case where the stored value is a C1 Phi node.
+        Object o2 = (o == null) ? v : o;
+        oa[0] = v;  // The stored value is known to be flattenable
+        oa[1] = o;  // The stored value may be flattenable
+        oa[2] = o2; // The stored value may be flattenable (a C1 Phi node)
+        oa[0] = oa; // The stored value is known to be not flattenable (an Object[])
+    }
+
+    @Run(test = "test73")
+    public void test73_verifier() {
+        MyValue1 v0 = MyValue1.createWithFieldsDontInline(rI, rL);
+        MyValue1 v1 = MyValue1.createWithFieldsDontInline(rI+1, rL+1);
+        MyValue1[] arr = new MyValue1[3];
+        try {
+            test73(arr, v0, v1);
+            throw new RuntimeException("ArrayStoreException expected");
+        } catch (ArrayStoreException t) {
+            // expected
+        }
+        Asserts.assertEQ(arr[0].hash(), v0.hash());
+        Asserts.assertEQ(arr[1].hash(), v1.hash());
+        Asserts.assertEQ(arr[2].hash(), v1.hash());
+    }
+
+    // Some more array clone tests
+    @ForceInline
+    public Object[] test74_helper(int i, MyValue1[] va, NonValueClass[] oa) {
+        Object[] arr = null;
+        if (i == 10) {
+            arr = oa;
+        } else {
+            arr = va;
+        }
+        return arr;
+    }
+
+    @Test
+    public Object[] test74(MyValue1[] va, NonValueClass[] oa) {
+        int i = 0;
+        for (; i < 10; i++);
+
+        Object[] arr = test74_helper(i, va, oa);
+        return arr.clone();
+    }
+
+    @Run(test = "test74")
+    public void test74_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = new MyValue1[len];
+        NonValueClass[] oa = new NonValueClass[len];
+        for (int i = 1; i < len; ++i) {
+            oa[i] = new NonValueClass(rI);
+        }
+        test74_helper(42, va, oa);
+        Object[] result = test74(va, oa);
+
+        for (int i = 0; i < va.length; ++i) {
+            Asserts.assertEQ(oa[i], result[i]);
+            // Check that array has correct properties (null-ok)
+            result[i] = null;
+        }
+    }
+
+    @ForceInline
+    public Object[] test75_helper(int i, MyValue1[] va, NonValueClass[] oa) {
+        Object[] arr = null;
+        if (i == 10) {
+            arr = va;
+        } else {
+            arr = oa;
+        }
+        return arr;
+    }
+
+    @Test
+    public Object[] test75(MyValue1[] va, NonValueClass[] oa) {
+        int i = 0;
+        for (; i < 10; i++);
+
+        Object[] arr = test75_helper(i, va, oa);
+        return arr.clone();
+    }
+
+    @Run(test = "test75")
+    public void test75_verifier() {
+        int len = Math.abs(rI) % 10;
+        MyValue1[] va = new MyValue1[len];
+        MyValue1[] verif = new MyValue1[len];
+        for (int i = 1; i < len; ++i) {
+            va[i] = testValue1;
+            verif[i] = va[i];
+        }
+        NonValueClass[] oa = new NonValueClass[len];
+        test75_helper(42, va, oa);
+        Object[] result = test75(va, oa);
+        verify(verif, result);
+        if (len > 0) {
+            // Check that array has correct properties (null-ok)
+            result[0] = null;
+        }
+    }
+
+    // Test mixing nullable and non-nullable arrays
+    @Test
+    public Object[] test76(MyValue1[] vva, MyValue1[] vba, MyValue1 vt, Object[] out, int n) {
+        Object[] result = null;
+        if (n == 0) {
+            result = vva;
+        } else if (n == 1) {
+            result = vba;
+        } else if (n == 2) {
+            result = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 42);
+        } else if (n == 3) {
+            result = new MyValue1[42];
+        }
+        result[0] = vt;
+        out[0] = result[1];
+        return result;
+    }
+
+    @Run(test = "test76")
+    public void test76_verifier() {
+        MyValue1 vt = testValue1;
+        Object[] out = new Object[1];
+        MyValue1[] vva = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 42);
+        MyValue1[] vva_r = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 42);
+        vva_r[0] = vt;
+        MyValue1[] vba = new MyValue1[42];
+        MyValue1[] vba_r = new MyValue1[42];
+        vba_r[0] = vt;
+        Object[] result = test76(vva, vba, vt, out, 0);
+        verify(result, vva_r);
+        Asserts.assertEQ(out[0], vva_r[1]);
+        result = test76(vva, vba, vt, out, 1);
+        verify(result, vba_r);
+        Asserts.assertEQ(out[0], vba_r[1]);
+        result = test76(vva, vba, vt, out, 2);
+        verify(result, vva_r);
+        Asserts.assertEQ(out[0], vva_r[1]);
+        result = test76(vva, vba, vt, out, 3);
+        verify(result, vba_r);
+        Asserts.assertEQ(out[0], vba_r[1]);
+    }
+
+    @Test
+    public Object[] test77(boolean b) {
+        Object[] va;
+        if (b) {
+            va = new MyValue1[5];
+            for (int i = 0; i < 5; ++i) {
+                va[i] = testValue1;
+            }
+        } else {
+            va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 10);
+            for (int i = 0; i < 10; ++i) {
+                va[i] = MyValue1.createWithFieldsInline(rI + i, rL + i);
+            }
+        }
+        long sum = ((MyValue1)va[0]).hashInterpreted();
+        if (b) {
+            va[0] = MyValue1.createWithFieldsDontInline(rI, sum);
+        } else {
+            va[0] = MyValue1.createWithFieldsDontInline(rI + 1, sum + 1);
+        }
+        return va;
+    }
+
+    @Run(test = "test77")
+    public void test77_verifier() {
+        Object[] va = test77(true);
+        Asserts.assertEQ(va.length, 5);
+        Asserts.assertEQ(((MyValue1)va[0]).hash(), hash(rI, hash()));
+        for (int i = 1; i < 5; ++i) {
+            Asserts.assertEQ(((MyValue1)va[i]).hash(), hash());
+        }
+        va = test77(false);
+        Asserts.assertEQ(va.length, 10);
+        Asserts.assertEQ(((MyValue1)va[0]).hash(), hash(rI + 1, hash(rI, rL) + 1));
+        for (int i = 1; i < 10; ++i) {
+            Asserts.assertEQ(((MyValue1)va[i]).hash(), hash(rI + i, rL + i));
+        }
+    }
+
+    // Same as test76 but with non value class array cases
+    @Test
+    public Object[] test78(MyValue1[] vva, MyValue1[] vba, Object val, Object[] out, int n) {
+        Object[] result = null;
+        if (n == 0) {
+            result = vva;
+        } else if (n == 1) {
+            result = vba;
+        } else if (n == 2) {
+            result = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 42);
+        } else if (n == 3) {
+            result = new MyValue1[42];
+        } else if (n == 4) {
+            result = new NonValueClass[42];
+        }
+        result[0] = val;
+        out[0] = result[1];
+        return result;
+    }
+
+    @Run(test = "test78")
+    public void test78_verifier() {
+        MyValue1 vt = testValue1;
+        NonValueClass obj = new NonValueClass(42);
+        Object[] out = new Object[1];
+        MyValue1[] vva = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 42);
+        MyValue1[] vva_r = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 42);
+        vva_r[0] = vt;
+        MyValue1[] vba = new MyValue1[42];
+        MyValue1[] vba_r = new MyValue1[42];
+        vba_r[0] = vt;
+        Object[] result = test78(vva, vba, vt, out, 0);
+        verify(result, vva_r);
+        Asserts.assertEQ(out[0], vva_r[1]);
+        result = test78(vva, vba, vt, out, 1);
+        verify(result, vba_r);
+        Asserts.assertEQ(out[0], vba_r[1]);
+        result = test78(vva, vba, vt, out, 2);
+        verify(result, vva_r);
+        Asserts.assertEQ(out[0], vva_r[1]);
+        result = test78(vva, vba, vt, out, 3);
+        verify(result, vba_r);
+        Asserts.assertEQ(out[0], vba_r[1]);
+        result = test78(vva, vba, obj, out, 4);
+        Asserts.assertEQ(result[0], obj);
+        Asserts.assertEQ(out[0], null);
+    }
+
+    // Test widening conversions from [Q to [L
+    @Test
+    @IR(failOn = {ALLOC, ALLOCA, LOOP, LOAD, STORE, TRAP})
+    public static MyValue1[] test79(MyValue1[] va) {
+        return va;
+    }
+
+    @Run(test = "test79")
+    public void test79_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        va[0] = testValue1;
+        MyValue1[] res = test79(va);
+        Asserts.assertEquals(res[0].hash(), testValue1.hash());
+        try {
+            res[0] = null;
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException npe) {
+            // Expected
+        }
+        res[0] = testValue1;
+        test79(null); // Should not throw NPE
+    }
+
+    // Same as test79 but with explicit cast and Object return
+    @Test
+    @IR(failOn = {ALLOC, ALLOCA, LOOP, LOAD, STORE, TRAP})
+    public static Object[] test80(MyValue1[] va) {
+        return (MyValue1[])va;
+    }
+
+    @Run(test = "test80")
+    public void test80_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        va[0] = testValue1;
+        Object[] res = test80(va);
+        Asserts.assertEquals(((MyValue1)res[0]).hash(), testValue1.hash());
+        try {
+            res[0] = null;
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException npe) {
+            // Expected
+        }
+        res[0] = testValue1;
+        test80(null); // Should not throw NPE
+    }
+
+    // Test mixing widened and boxed array type
+    @Test
+    public static long test81(MyValue1[] va1, MyValue1[] va2, MyValue1 vt, boolean b, boolean shouldThrow) {
+        MyValue1[] result = b ? va1 : va2;
+        try {
+            result[0] = vt;
+        } catch (NullPointerException npe) {
+            // Ignored
+        }
+        return result[1].hash();
+    }
+
+    @Run(test = "test81")
+    public void test81_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        MyValue1[] vaB = new MyValue1[2];
+        va[1] = testValue1;
+        vaB[1] = testValue1;
+        long res = test81(va, vaB, testValue1, true, true);
+        Asserts.assertEquals(va[0].hash(), testValue1.hash());
+        Asserts.assertEquals(res, testValue1.hash());
+        res = test81(va, vaB, testValue1, false, false);
+        Asserts.assertEquals(vaB[0].hash(), testValue1.hash());
+        Asserts.assertEquals(res, testValue1.hash());
+        res = test81(va, va, testValue1, false, true);
+        Asserts.assertEquals(va[0].hash(), testValue1.hash());
+        Asserts.assertEquals(res, testValue1.hash());
+    }
+
+    // Same as test81 but more cases and null writes
+    @Test
+    public static long test82(MyValue1[] va1, MyValue1[] va2, MyValue1 vt1, MyValue1 vt2, int i, boolean shouldThrow) {
+        MyValue1[] result = null;
+        if (i == 0) {
+            result = va1;
+        } else if (i == 1) {
+            result = va2;
+        } else if (i == 2) {
+            result = new MyValue1[2];
+            result[1] = vt1;
+        } else if (i == 3) {
+            result = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+            result[1] = vt1;
+        }
+        try {
+            result[0] = (i <= 1) ? null : vt2;
+            if (shouldThrow) {
+                throw new RuntimeException("NullPointerException expected");
+            }
+        } catch (NullPointerException npe) {
+            Asserts.assertTrue(shouldThrow, "NullPointerException thrown");
+        }
+        result[0] = vt1;
+        return result[1].hash();
+    }
+
+    @Run(test = "test82")
+    public void test82_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        MyValue1[] vaB = new MyValue1[2];
+        va[1] = testValue1;
+        vaB[1] = testValue1;
+        long res = test82(va, vaB, testValue1, testValue1, 0, true);
+        Asserts.assertEquals(va[0].hash(), testValue1.hash());
+        Asserts.assertEquals(res, testValue1.hash());
+        res = test82(va, vaB, testValue1, testValue1, 1, false);
+        Asserts.assertEquals(vaB[0].hash(), testValue1.hash());
+        Asserts.assertEquals(res, testValue1.hash());
+        res = test82(va, va, testValue1, testValue1, 1, true);
+        Asserts.assertEquals(va[0].hash(), testValue1.hash());
+        Asserts.assertEquals(res, testValue1.hash());
+        res = test82(va, va, testValue1, null, 2, false);
+        Asserts.assertEquals(va[0].hash(), testValue1.hash());
+        Asserts.assertEquals(res, testValue1.hash());
+        res = test82(va, va, testValue1, null, 3, true);
+        Asserts.assertEquals(va[0].hash(), testValue1.hash());
+        Asserts.assertEquals(res, testValue1.hash());
+    }
+
+    @Test
+    @IR(failOn = {ALLOC, ALLOCA, STORE})
+    public static long test83(MyValue1[] va) {
+        MyValue1[] result = va;
+        return result[0].hash();
+    }
+
+    @Run(test = "test83")
+    public void test83_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 42);
+        va[0] = testValue1;
+        long res = test83(va);
+        Asserts.assertEquals(res, testValue1.hash());
+    }
+
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        failOn = {ALLOC, LOOP, STORE, TRAP})
+    public static MyValue1[] test84(MyValue1 vt1, MyValue1 vt2) {
+        MyValue1[] result = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        result[0] = vt1;
+        result[1] = vt2;
+        return result;
+    }
+
+    @Run(test = "test84")
+    public void test84_verifier() {
+        MyValue1[] res = test84(testValue1, testValue1);
+        Asserts.assertEquals(res[0].hash(), testValue1.hash());
+        Asserts.assertEquals(res[1].hash(), testValue1.hash());
+        try {
+            test84(testValue1, null);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException npe) {
+            // Expected
+        }
+    }
+
+    @Test
+    public static long test85(MyValue1[] va, MyValue1 val) {
+        va[0] = val;
+        return va[1].hash();
+    }
+
+    @Run(test = "test85")
+    public void test85_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        MyValue1[] vab = new MyValue1[2];
+        va[1] = testValue1;
+        vab[1] = testValue1;
+        long res = test85(va, testValue1);
+        Asserts.assertEquals(res, testValue1.hash());
+        Asserts.assertEquals(va[0].hash(), testValue1.hash());
+        res = test85(vab, testValue1);
+        Asserts.assertEquals(res, testValue1.hash());
+        Asserts.assertEquals(vab[0].hash(), testValue1.hash());
+    }
+
+    // Same as test85 but with ref value
+    @Test
+    public static long test86(MyValue1[] va, MyValue1 val) {
+        va[0] = val;
+        return va[1].hash();
+    }
+
+    @Run(test = "test86")
+    public void test86_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        MyValue1[] vab = new MyValue1[2];
+        va[1] = testValue1;
+        vab[1] = testValue1;
+        long res = test86(va, testValue1);
+        Asserts.assertEquals(res, testValue1.hash());
+        Asserts.assertEquals(va[0].hash(), testValue1.hash());
+        try {
+            test86(va, null);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException npe) {
+            // Expected
+        }
+        res = test86(vab, testValue1);
+        Asserts.assertEquals(res, testValue1.hash());
+        Asserts.assertEquals(vab[0].hash(), testValue1.hash());
+        res = test86(vab, null);
+        Asserts.assertEquals(res, testValue1.hash());
+        Asserts.assertEquals(vab[0], null);
+    }
+
+    // Test initialization of nullable array with constant
+    @Test
+    public long test87() {
+        MyValue1[] va = new MyValue1[1];
+        va[0] = testValue1;
+        return va[0].hash();
+    }
+
+    @Run(test = "test87")
+    public void test87_verifier() {
+        long result = test87();
+        Asserts.assertEQ(result, hash());
+    }
+
+
+    // Test casting to null restricted array
+    @Test
+    @IR(failOn = {ALLOC, ALLOCA, LOOP, LOAD, STORE, TRAP})
+    public static MyValue1[] test88(Class c, MyValue1[] va) {
+        return (MyValue1[])c.cast(va);
+    }
+
+    @Run(test = "test88")
+    public void test88_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        va[0] = testValue1;
+        MyValue1[] res = test88(va.getClass(), va);
+        Asserts.assertEquals(res[0].hash(), testValue1.hash());
+        res[0] = testValue1;
+        test88(va.getClass(), null); // Should not throw NPE
+        try {
+            test88(va.getClass(), new MyValue1[1]);
+            throw new RuntimeException("ClassCastException expected");
+        } catch (ClassCastException cce) {
+            // Expected
+        }
+    }
+
+    // Same as test88 but with Object argument
+    @Test
+    @IR(failOn = {ALLOC, ALLOCA, LOOP, LOAD, STORE, TRAP})
+    public static MyValue1[] test89(Class c, Object[] va) {
+        return (MyValue1[])c.cast(va);
+    }
+
+    @Run(test = "test89")
+    public void test89_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        va[0] = testValue1;
+        MyValue1[] res = test89(va.getClass(), va);
+        Asserts.assertEquals(((MyValue1)res[0]).hash(), testValue1.hash());
+        res[0] = testValue1;
+        test89(va.getClass(), null); // Should not throw NPE
+        try {
+            test89(va.getClass(), new MyValue1[1]);
+            throw new RuntimeException("ClassCastException expected");
+        } catch (ClassCastException cce) {
+            // Expected
+        }
+    }
+
+    // More cast tests
+    @Test
+    public static MyValue1[] test90(Object va) {
+        return (MyValue1[])va;
+    }
+
+    @Run(test = "test90")
+    public void test90_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        MyValue1[] vab = new MyValue1[1];
+        try {
+          // Trigger some ClassCastExceptions so C2 does not add an uncommon trap
+          test90(new NonValueClass[0]);
+        } catch (ClassCastException cce) {
+          // Expected
+        }
+        test90(va);
+        test90(vab);
+        test90(null);
+    }
+
+    @Test
+    public static MyValue1[] test91(Object[] va) {
+        return (MyValue1[])va;
+    }
+
+    @Run(test = "test91")
+    public void test91_verifier() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        MyValue1[] vab = new MyValue1[1];
+        try {
+          // Trigger some ClassCastExceptions so C2 does not add an uncommon trap
+          test91(new NonValueClass[0]);
+        } catch (ClassCastException cce) {
+          // Expected
+        }
+        test91(va);
+        test91(vab);
+        test91(null);
+    }
+
+    // Test if arraycopy intrinsic correctly checks for flattened source array
+    @Test
+    public static void test92(MyValue1[] src, MyValue1[] dst) {
+        System.arraycopy(src, 0, dst, 0, 2);
+    }
+
+    @Run(test = "test92")
+    public void test92_verifier() {
+        MyValue1[]  va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        MyValue1[] vab = new MyValue1[2];
+        va[0] = testValue1;
+        vab[0] = testValue1;
+        test92(va, vab);
+        Asserts.assertEquals(va[0], vab[0]);
+        Asserts.assertEquals(va[1], vab[1]);
+    }
+
+    @Test
+    public static void test93(Object src, MyValue1[] dst) {
+        System.arraycopy(src, 0, dst, 0, 2);
+    }
+
+    @Run(test = "test93")
+    public void test93_verifier() {
+        MyValue1[]  va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        MyValue1[] vab = new MyValue1[2];
+        va[0] = testValue1;
+        vab[0] = testValue1;
+        test93(va, vab);
+        Asserts.assertEquals(va[0], vab[0]);
+        Asserts.assertEquals(va[1], vab[1]);
+    }
+
+    // Test non-escaping allocation with arraycopy
+    // that does not modify loaded array element.
+    @Test
+    public static long test94() {
+        MyValue1[] src = new MyValue1[8];
+        MyValue1[]  dst = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 8);
+        for (int i = 1; i < 8; ++i) {
+            src[i] = testValue1;
+        }
+        System.arraycopy(src, 1, dst, 2, 6);
+        return dst[0].hash();
+    }
+
+    @Run(test = "test94")
+    public static void test94_verifier() {
+        long result = test94();
+        Asserts.assertEquals(result, MyValue1.createDefaultInline().hash());
+    }
+
+    // Test meeting constant TypeInstPtr with InlineTypeNode
+    @ForceInline
+    public long test95_callee() {
+        MyValue1[] va = new MyValue1[1];
+        va[0] = testValue1;
+        return va[0].hashInterpreted();
+    }
+
+    @Test
+    public long test95() {
+        return test95_callee();
+    }
+
+    @Run(test = "test95")
+    @Warmup(0)
+    public void test95_verifier() {
+        long result = test95();
+        Asserts.assertEQ(result, hash());
+    }
+
+    // Matrix multiplication test to exercise type flow analysis with nullable value class arrays
+    static value class Complex {
+        private final double re;
+        private final double im;
+
+        Complex(double re, double im) {
+            this.re = re;
+            this.im = im;
+        }
+
+        public Complex add(Complex that) {
+            return new Complex(this.re + that.re, this.im + that.im);
+        }
+
+        public Complex mul(Complex that) {
+            return new Complex(this.re * that.re - this.im * that.im,
+                               this.re * that.im + this.im * that.re);
+        }
+    }
+
+    @Test
+    public Complex[][] test96(Complex[][] A, Complex[][] B) {
+        int size = A.length;
+        Complex[][] R = new Complex[size][size];
+        for (int i = 0; i < size; i++) {
+            for (int k = 0; k < size; k++) {
+                Complex aik = A[i][k];
+                for (int j = 0; j < size; j++) {
+                    R[i][j] = B[i][j].add(aik.mul((Complex)B[k][j]));
+                }
+            }
+        }
+        return R;
+    }
+
+    static Complex[][] test96_A = new Complex[10][10];
+    static Complex[][] test96_B = new Complex[10][10];
+    static Complex[][] test96_R;
+
+    static {
+        for (int i = 0; i < 10; i++) {
+            for (int j = 0; j < 10; j++) {
+                test96_A[i][j] = new Complex(rI, rI);
+                test96_B[i][j] = new Complex(rI, rI);
+            }
+        }
+    }
+
+    @Run(test = "test96")
+    public void test96_verifier() {
+        Complex[][] result = test96(test96_A, test96_B);
+        if (test96_R == null) {
+            test96_R = result;
+        }
+        for (int i = 0; i < 10; i++) {
+            for (int j = 0; j < 10; j++) {
+                Asserts.assertEQ(result[i][j], test96_R[i][j]);
+            }
+        }
+    }
+
+    // Test loads from vararg arrays
+    @Test
+    @IR(failOn = {LOAD_UNKNOWN_INLINE})
+    public static Object test97(Object... args) {
+        return args[0];
+    }
+
+    @Run(test = "test97")
+    public static void test97_verifier() {
+        Object obj = new Object();
+        Object result = test97(obj);
+        Asserts.assertEquals(result, obj);
+        NonValueClass[] myInt = new NonValueClass[1];
+        NonValueClass otherObj = new NonValueClass(rI);
+        myInt[0] = otherObj;
+        result = test97((Object[])myInt);
+        Asserts.assertEquals(result, otherObj);
+    }
+
+    @Test
+    public static Object test98(Object... args) {
+        return args[0];
+    }
+
+    @Run(test = "test98")
+    public static void test98_verifier(RunInfo info) {
+        Object obj = new Object();
+        Object result = test98(obj);
+        Asserts.assertEquals(result, obj);
+        NonValueClass[] myInt = new NonValueClass[1];
+        NonValueClass otherObj = new NonValueClass(rI);
+        myInt[0] = otherObj;
+        result = test98((Object[])myInt);
+        Asserts.assertEquals(result, otherObj);
+        if (!info.isWarmUp()) {
+            MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+            MyValue1[] vab = new MyValue1[1];
+            result = test98((Object[])va);
+            Asserts.assertEquals(((MyValue1)result).hash(), MyValue1.createDefaultInline().hash());
+            result = test98((Object[])vab);
+            Asserts.assertEquals(result, null);
+        }
+    }
+
+    @Test
+    public static Object test99(Object... args) {
+        return args[0];
+    }
+
+    @Run(test = "test99")
+    public static void test99_verifier(RunInfo info) {
+        Object obj = new Object();
+        Object result = test99(obj);
+        Asserts.assertEquals(result, obj);
+        NonValueClass[] myInt = new NonValueClass[1];
+        NonValueClass otherObj = new NonValueClass(rI);
+        myInt[0] = otherObj;
+        result = test99((Object[])myInt);
+        Asserts.assertEquals(result, otherObj);
+        if (!info.isWarmUp()) {
+            try {
+                test99((Object[])null);
+                throw new RuntimeException("No NPE thrown");
+            } catch (NullPointerException npe) {
+                // Expected
+            }
+        }
+    }
+
+    @Test
+    public static Object test100(Object... args) {
+        return args[0];
+    }
+
+    @Run(test = "test100")
+    public static void test100_verifier(RunInfo info) {
+        Object obj = new Object();
+        Object result = test100(obj);
+        Asserts.assertEquals(result, obj);
+        NonValueClass[] myInt = new NonValueClass[1];
+        NonValueClass otherObj = new NonValueClass(rI);
+        myInt[0] = otherObj;
+        result = test100((Object[])myInt);
+        Asserts.assertEquals(result, otherObj);
+        if (!info.isWarmUp()) {
+            try {
+                test100();
+                throw new RuntimeException("No AIOOBE thrown");
+            } catch (ArrayIndexOutOfBoundsException aioobe) {
+                // Expected
+            }
+        }
+    }
+
+    // Test stores to varag arrays
+    @Test
+    @IR(failOn = STORE_UNKNOWN_INLINE)
+    public static void test101(Object val, Object... args) {
+        args[0] = val;
+    }
+
+    @Run(test = "test101")
+    public static void test101_verifier() {
+        Object obj = new Object();
+        test101(obj, obj);
+        NonValueClass[] myInt = new NonValueClass[1];
+        NonValueClass otherObj = new NonValueClass(rI);
+        test101(otherObj, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], otherObj);
+        test101(null, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], null);
+    }
+
+    @Test
+    public static void test102(Object val, Object... args) {
+        args[0] = val;
+    }
+
+    @Run(test = "test102")
+    public static void test102_verifier(RunInfo info) {
+        Object obj = new Object();
+        test102(obj, obj);
+        NonValueClass[] myInt = new NonValueClass[1];
+        NonValueClass otherObj = new NonValueClass(rI);
+        test102(otherObj, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], otherObj);
+        test102(null, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], null);
+        if (!info.isWarmUp()) {
+            MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+            MyValue1[] vab = new MyValue1[1];
+            test102(testValue1, (Object[])va);
+            Asserts.assertEquals(va[0].hash(), testValue1.hash());
+            test102(testValue1, (Object[])vab);
+            Asserts.assertEquals(vab[0].hash(), testValue1.hash());
+            test102(null, (Object[])vab);
+            Asserts.assertEquals(vab[0], null);
+        }
+    }
+
+    @Test
+    public static void test103(Object val, Object... args) {
+        args[0] = val;
+    }
+
+    @Run(test = "test103")
+    public static void test103_verifier(RunInfo info) {
+        Object obj = new Object();
+        test103(obj, obj);
+        NonValueClass[] myInt = new NonValueClass[1];
+        NonValueClass otherObj = new NonValueClass(rI);
+        test103(otherObj, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], otherObj);
+        test103(null, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], null);
+        if (!info.isWarmUp()) {
+            MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+            try {
+                test103(null, (Object[])va);
+                throw new RuntimeException("No NPE thrown");
+            } catch (NullPointerException npe) {
+                // Expected
+            }
+        }
+    }
+
+    @Test
+    public static void test104(Object val, Object... args) {
+        args[0] = val;
+    }
+
+    @Run(test = "test104")
+    public static void test104_verifier(RunInfo info) {
+        Object obj = new Object();
+        test104(obj, obj);
+        NonValueClass[] myInt = new NonValueClass[1];
+        NonValueClass otherObj = new NonValueClass(rI);
+        test104(otherObj, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], otherObj);
+        test104(null, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], null);
+        if (!info.isWarmUp()) {
+            try {
+                test104(testValue1);
+                throw new RuntimeException("No AIOOBE thrown");
+            } catch (ArrayIndexOutOfBoundsException aioobe) {
+                // Expected
+            }
+        }
+    }
+
+    @Test
+    public static void test105(Object val, Object... args) {
+        args[0] = val;
+    }
+
+    @Run(test = "test105")
+    public static void test105_verifier(RunInfo info) {
+        Object obj = new Object();
+        test105(obj, obj);
+        NonValueClass[] myInt = new NonValueClass[1];
+        NonValueClass otherObj = new NonValueClass(rI);
+        test105(otherObj, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], otherObj);
+        test105(null, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], null);
+        if (!info.isWarmUp()) {
+            try {
+                test105(testValue1, (Object[])null);
+                throw new RuntimeException("No NPE thrown");
+            } catch (NullPointerException npe) {
+                // Expected
+            }
+        }
+    }
+
+    @Test
+    public static Object[] test106(Object[] dst, Object... args) {
+        // Access array to speculate on non-flatness
+        if (args[0] == null) {
+            args[0] = testValue1;
+        }
+        System.arraycopy(args, 0, dst, 0, args.length);
+        System.arraycopy(dst, 0, args, 0, dst.length);
+        Object[] clone = args.clone();
+        if (clone[0] == null) {
+            throw new RuntimeException("Unexpected null");
+        }
+        return Arrays.copyOf(args, args.length, Object[].class);
+    }
+
+    @Run(test = "test106")
+    public static void test106_verifier(RunInfo info) {
+        Object[] dst = new Object[1];
+        Object obj = new Object();
+        Object[] result = test106(dst, obj);
+        Asserts.assertEquals(result[0], obj);
+        NonValueClass[] myInt = new NonValueClass[1];
+        NonValueClass otherObj = new NonValueClass(rI);
+        myInt[0] = otherObj;
+        result = test106(myInt, (Object[])myInt);
+        Asserts.assertEquals(result[0], otherObj);
+        if (!info.isWarmUp()) {
+            MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+            MyValue1[] vab = new MyValue1[1];
+            result = test106(va, (Object[])va);
+            Asserts.assertEquals(((MyValue1)result[0]).hash(), MyValue1.createDefaultInline().hash());
+            result = test106(vab, (Object[])vab);
+            Asserts.assertEquals(((MyValue1)result[0]).hash(), testValue1.hash());
+        }
+    }
+
+    // TODO 8325632 Fails with "matching stack sizes" in Scenario 5 with -XX:TypeProfileLevel=222
+    /*
+    // Test that allocation is not replaced by non-dominating allocation
+    @ForceInline
+    public long test107_helper(MyValue1[] va, MyValue1 vt) {
+        try {
+            va[0] = vt;
+        } catch (NullPointerException npe) { }
+        return va[1].hash();
+    }
+
+    @Test
+    public void test107() {
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        MyValue1[] tmp = new MyValue1[2];
+        long res1 = test107_helper(va, testValue1);
+        long res2 = test107_helper(va, testValue1);
+        Asserts.assertEquals(va[0].hash(), testValue1.hash());
+        Asserts.assertEquals(res1, MyValue1.createDefaultInline().hash());
+        Asserts.assertEquals(res2, MyValue1.createDefaultInline().hash());
+    }
+
+    @Run(test = "test107")
+    public void test107_verifier() {
+        test107();
+    }
+    */
+
+    @Test
+    public Object test108(MyValue1[] src, boolean flag) {
+        MyValue1[] dst = new MyValue1[8];
+        System.arraycopy(src, 1, dst, 2, 6);
+        if (flag) {} // uncommon trap
+        return dst[2];
+    }
+
+    @Run(test = "test108")
+    @Warmup(10000)
+    public void test108_verifier(RunInfo info) {
+        MyValue1[] src = new MyValue1[8];
+        test108(src, !info.isWarmUp());
+    }
+
+    // Test LoadNode::can_see_arraycopy_value optimization
+    @Test
+    public static void test109() {
+        MyValue1[] src = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        MyValue1[] dst = new MyValue1[1];
+        src[0] = testValue1;
+        System.arraycopy(src, 0, dst, 0, 1);
+        Asserts.assertEquals(src[0], dst[0]);
+    }
+
+    @Run(test = "test109")
+    public void test109_verifier() {
+        test109();
+    }
+
+    // Same as test109 but with Object destination array
+    @Test
+    public static void test110() {
+        MyValue1[] src = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        Object[] dst = new Object[1];
+        src[0] = testValue1;
+        System.arraycopy(src, 0, dst, 0, 1);
+        Asserts.assertEquals(src[0], dst[0]);
+    }
+
+    @Run(test = "test110")
+    public void test110_verifier() {
+        test110();
+    }
+
+    // Same as test109 but with Arrays.copyOf
+    @Test
+    public static void test111() {
+        MyValue1[] src = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+        src[0] = testValue1;
+        MyValue1[] dst = Arrays.copyOf(src, src.length, MyValue1[].class);
+        Asserts.assertEquals(src[0], dst[0]);
+    }
+
+    @Run(test = "test111")
+    public void test111_verifier() {
+        test111();
+    }
+
+    static final MyValue1[] refArray = new MyValue1[2];
+    static final MyValue1[] flatArray = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 1);
+
+    // Test scalarization
+    @Test
+    @IR(failOn = {ALLOC_G, STORE, TRAP})
+    public int test112(boolean b) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b) {
+            val = refArray[0];
+        }
+        return val.x;
+    }
+
+    @Run(test = "test112")
+    public void test112_verifier(RunInfo info) {
+        refArray[0] = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test112(true), refArray[0].x);
+        Asserts.assertEquals(test112(false), testValue1.x);
+        if (!info.isWarmUp()) {
+            refArray[0] = null;
+            try {
+                Asserts.assertEquals(test112(false), testValue1.x);
+                test112(true);
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    // Same as test112 but with call to hash()
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test113(boolean b) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b) {
+            val = refArray[0];
+        }
+        return val.hash();
+    }
+
+    @Run(test = "test113")
+    public void test113_verifier(RunInfo info) {
+        refArray[0] = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test113(true), refArray[0].hash());
+        Asserts.assertEquals(test113(false), testValue1.hash());
+        if (!info.isWarmUp()) {
+            refArray[0] = null;
+            try {
+                Asserts.assertEquals(test113(false), testValue1.hash());
+                test113(true);
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    @Test
+    public MyValue1 test114(boolean b) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b) {
+            val = refArray[0];
+        }
+        return val;
+    }
+
+    @Run(test = "test114")
+    public void test114_verifier(RunInfo info) {
+        refArray[0] = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test114(true).hash(), refArray[0].hash());
+        Asserts.assertEquals(test114(false).hash(), testValue1.hash());
+        if (!info.isWarmUp()) {
+            refArray[0] = null;
+            Asserts.assertEquals(test114(true), null);
+        }
+    }
+
+    // Test scalarization when .ref is referenced in safepoint debug info
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public int test115(boolean b1, boolean b2, Method m) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b1) {
+            val = refArray[0];
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return val.x;
+    }
+
+    @Run(test = "test115")
+    public void test115_verifier(RunInfo info) {
+        refArray[0] = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test115(true, false, info.getTest()), refArray[0].x);
+        Asserts.assertEquals(test115(false, false, info.getTest()), testValue1.x);
+        if (!info.isWarmUp()) {
+            refArray[0] = null;
+            try {
+                Asserts.assertEquals(test115(false, false, info.getTest()), testValue1.x);
+                test115(true, false, info.getTest());
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+            refArray[0] = MyValue1.createWithFieldsInline(rI+1, rL+1);
+            Asserts.assertEquals(test115(true, true, info.getTest()), refArray[0].x);
+            Asserts.assertEquals(test115(false, true, info.getTest()), testValue1.x);
+        }
+    }
+
+    @Test
+    public MyValue1 test116(boolean b1, boolean b2, Method m) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b1) {
+            val = refArray[0];
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return val;
+    }
+
+    @Run(test = "test116")
+    public void test116_verifier(RunInfo info) {
+        refArray[0] = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test116(true, false, info.getTest()).hash(), refArray[0].hash());
+        Asserts.assertEquals(test116(false, false, info.getTest()).hash(), testValue1.hash());
+        if (!info.isWarmUp()) {
+            refArray[0] = null;
+            Asserts.assertEquals(test116(true, false, info.getTest()), null);
+            refArray[0] = MyValue1.createWithFieldsInline(rI+1, rL+1);
+            Asserts.assertEquals(test116(true, true, info.getTest()).hash(), refArray[0].hash());
+            Asserts.assertEquals(test116(false, true, info.getTest()).hash(), testValue1.hash());
+        }
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, STORE})
+    public int test117(boolean b) {
+        MyValue1 val = null;
+        if (b) {
+            val = refArray[0];
+        }
+        return val.x;
+    }
+
+    @Run(test = "test117")
+    public void test117_verifier() {
+        refArray[0] = testValue1;
+        Asserts.assertEquals(test117(true), testValue1.x);
+        try {
+            test117(false);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public MyValue1 test118(boolean b) {
+        MyValue1 val = null;
+        if (b) {
+            val = refArray[0];
+        }
+        return val;
+    }
+
+    @Run(test = "test118")
+    public void test118_verifier() {
+        refArray[0] = testValue1;
+        Asserts.assertEquals(test118(true).hash(), testValue1.hash());
+        Asserts.assertEquals(test118(false), null);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, STORE})
+    public int test119(boolean b) {
+        MyValue1 val = refArray[0];
+        if (b) {
+            val = null;
+        }
+        return val.x;
+    }
+
+    @Run(test = "test119")
+    public void test119_verifier() {
+        refArray[0] = testValue1;
+        Asserts.assertEquals(test119(false), testValue1.x);
+        try {
+            test119(true);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public MyValue1 test120(boolean b) {
+        MyValue1 val = refArray[0];
+        if (b) {
+            val = null;
+        }
+        return val;
+    }
+
+    @Run(test = "test120")
+    public void test120_verifier() {
+        refArray[0] = testValue1;
+        Asserts.assertEquals(test120(false).hash(), testValue1.hash());
+        Asserts.assertEquals(test120(true), null);
+    }
+
+    @ForceInline
+    public Object test121_helper() {
+        return flatArray[0];
+    }
+
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        failOn = {ALLOC_G, STORE})
+    public void test121(boolean b) {
+        Object o = null;
+        if (b) {
+            o = refArray[0];
+        } else {
+            o = test121_helper();
+        }
+        if (o == null) {
+            return;
+        }
+        flatArray[0] = (MyValue1)o;
+    }
+
+    @Run(test = "test121")
+    public void test121_verifier() {
+        refArray[0] = testValue1;
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        flatArray[0] = vt;
+        test121(false);
+        Asserts.assertEquals(flatArray[0].hash(), vt.hash());
+        test121(true);
+        Asserts.assertEquals(flatArray[0].hash(), testValue1.hash());
+    }
+
+    @ForceInline
+    public Object test122_helper() {
+        return refArray[0];
+    }
+
+    @Test
+    @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"},
+        failOn = {ALLOC_G, STORE})
+    public void test122(boolean b) {
+        Object o = null;
+        if (b) {
+            o = flatArray[0];
+        } else {
+            o = test122_helper();
+        }
+        if (o == null) {
+            return;
+        }
+        flatArray[0] = (MyValue1)o;
+    }
+
+    @Run(test = "test122")
+    public void test122_verifier() {
+        refArray[0] = testValue1;
+        test122(false);
+        Asserts.assertEquals(flatArray[0].hash(), testValue1.hash());
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        flatArray[0] = vt;
+        test122(true);
+        Asserts.assertEquals(flatArray[0].hash(), vt.hash());
+    }
+
+    @ForceInline
+    public Object test123_helper() {
+        return refArray[0];
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, STORE})
+    public long test123(boolean b, MyValue1 val, Method m, boolean deopt) {
+        MyValue1[] array = new MyValue1[1];
+        array[0] = val;
+        Object res = null;
+        if (b) {
+            res = array[0];
+        } else {
+            res = test123_helper();
+        }
+        if (deopt) {
+            // uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return ((MyValue1)res).hash();
+    }
+
+    @Run(test = "test123")
+    public void test123_verifier(RunInfo info) {
+        refArray[0] = MyValue1.createDefaultInline();
+        Asserts.assertEquals(test123(true, testValue1, info.getTest(), false), testValue1.hash());
+        Asserts.assertEquals(test123(false, testValue1, info.getTest(), false), MyValue1.createDefaultInline().hash());
+        if (!info.isWarmUp()) {
+            Asserts.assertEquals(test123(true, testValue1, info.getTest(), true), testValue1.hash());
+        }
+    }
+
+    @ForceInline
+    public Object test124_helper(MyValue2 val) {
+        MyValue2[] array = new MyValue2[1];
+        array[0] = val;
+        return array[0];
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, STORE})
+    public long test124(boolean b, MyValue2 val, Method m, boolean deopt) {
+        Object res = null;
+        if (b) {
+            res = MyValue2.createWithFieldsInline(rI+1, rD+1);
+        } else {
+            res = test124_helper(val);
+        }
+        if (deopt) {
+            // uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return ((MyValue2)res).hash();
+    }
+
+    @Run(test = "test124")
+    public void test124_verifier(RunInfo info) {
+        refArray[0] = MyValue1.createDefaultInline();
+        MyValue2 val1 = MyValue2.createWithFieldsInline(rI, rD);
+        MyValue2 val2 = MyValue2.createWithFieldsInline(rI+1, rD+1);
+        Asserts.assertEquals(test124(true, val1, info.getTest(), false), val2.hash());
+        Asserts.assertEquals(test124(false, val1, info.getTest(), false), val1.hash());
+        if (!info.isWarmUp()) {
+            Asserts.assertEquals(test124(true, val1, info.getTest(), true), val2.hash());
+        }
+    }
+
+    @ForceInline
+    public void test125_helper(Object[] array, MyValue2 val) {
+        array[0] = val;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, STORE})
+    public long test125(boolean b, MyValue2 val, Method m, boolean deopt) {
+        Object[] res = new MyValue2[1];
+        if (b) {
+            res[0] = MyValue2.createWithFieldsInline(rI+1, rD+1);
+        } else {
+            test125_helper(res, val);
+        }
+        val = ((MyValue2)res[0]);
+        if (deopt) {
+            // uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return val.hash();
+    }
+
+    @Run(test = "test125")
+    public void test125_verifier(RunInfo info) {
+        refArray[0] = MyValue1.createDefaultInline();
+        MyValue2 val1 = MyValue2.createWithFieldsInline(rI, rD);
+        MyValue2 val2 = MyValue2.createWithFieldsInline(rI+1, rD+1);
+        Asserts.assertEquals(test125(true, val1, info.getTest(), false), val2.hash());
+        Asserts.assertEquals(test125(false, val1, info.getTest(), false), val1.hash());
+        if (!info.isWarmUp()) {
+            Asserts.assertEquals(test125(true, val1, info.getTest(), true), val2.hash());
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableInlineTypes.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableInlineTypes.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableInlineTypes.java
@@ -0,0 +1,3293 @@
+/*
+ * 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.
+ *
+ * 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.*;
+import jdk.test.lib.Asserts;
+import jdk.experimental.bytecode.TypeTag;
+import test.java.lang.invoke.lib.OldInstructionHelper;
+
+import java.util.Objects;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import static compiler.valhalla.inlinetypes.InlineTypeIRNode.*;
+import static compiler.valhalla.inlinetypes.InlineTypes.*;
+
+/*
+ * @test
+ * @key randomness
+ * @summary Test correct handling of nullable value classes.
+ * @library /test/lib /test/jdk/lib/testlibrary/bytecode /test/jdk/java/lang/invoke/common /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.OldInstructionHelper
+ * @run main/othervm/timeout=300 compiler.valhalla.inlinetypes.TestNullableInlineTypes
+ */
+
+@ForceCompileClassInitializer
+public class TestNullableInlineTypes {
+
+    public static void main(String[] args) {
+
+        Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS;
+        scenarios[3].addFlags("-XX:-MonomorphicArrayCheck", "-XX:FlatArrayElementMaxSize=-1");
+        scenarios[4].addFlags("-XX:-MonomorphicArrayCheck");
+
+        InlineTypes.getFramework()
+                   .addScenarios(scenarios)
+                   .addHelperClasses(MyValue1.class,
+                                     MyValue2.class,
+                                     MyValue2Inline.class,
+                                     MyValue3.class,
+                                     MyValue3Inline.class)
+                   .start();
+    }
+
+    static {
+        // Make sure RuntimeException is loaded to prevent uncommon traps in IR verified tests
+        RuntimeException tmp = new RuntimeException("42");
+        try {
+            Class<?> clazz = TestNullableInlineTypes.class;
+            MethodHandles.Lookup lookup = MethodHandles.lookup();
+
+            MethodType test18_mt = MethodType.methodType(void.class, MyValue1.class);
+            test18_mh1 = lookup.findStatic(clazz, "test18_target1", test18_mt);
+            test18_mh2 = lookup.findStatic(clazz, "test18_target2", test18_mt);
+
+            MethodType test19_mt = MethodType.methodType(void.class, MyValue1.class);
+            test19_mh1 = lookup.findStatic(clazz, "test19_target1", test19_mt);
+            test19_mh2 = lookup.findStatic(clazz, "test19_target2", test19_mt);
+        } catch (NoSuchMethodException | IllegalAccessException e) {
+            e.printStackTrace();
+            throw new RuntimeException("Method handle lookup failed");
+        }
+    }
+
+    @NullRestricted
+    private static final MyValue1 testValue1 = MyValue1.createWithFieldsInline(rI, rL);
+
+    private static final MyValue1[] testValue1Array = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 3);
+    static {
+        for (int i = 0; i < 3; ++i) {
+            testValue1Array[i] = testValue1;
+        }
+    }
+
+    MyValue1 nullField;
+    @NullRestricted
+    MyValue1 valueField1 = testValue1;
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public long test1(MyValue1 vt) {
+        long result = 0;
+        try {
+            result = vt.hash();
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        return result;
+    }
+
+    @Run(test = "test1")
+    public void test1_verifier() {
+        long result = test1(null);
+        Asserts.assertEquals(result, 0L);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public long test2(MyValue1 vt) {
+        long result = 0;
+        try {
+            result = vt.hashInterpreted();
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        return result;
+    }
+
+    @Run(test = "test2")
+    public void test2_verifier() {
+        long result = test2(null);
+        Asserts.assertEquals(result, 0L);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public long test3() {
+        long result = 0;
+        try {
+            if ((Object)nullField != null) {
+                throw new RuntimeException("nullField should be null");
+            }
+            result = nullField.hash();
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        return result;
+    }
+
+    @Run(test = "test3")
+    public void test3_verifier() {
+        long result = test3();
+        Asserts.assertEquals(result, 0L);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public void test4() {
+        try {
+            valueField1 = nullField;
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Run(test = "test4")
+    public void test4_verifier() {
+        test4();
+    }
+
+    @Test
+    // TODO 8284443 When passing vt to test5_inline and incrementally inlining, we lose the oop
+    @IR(applyIfOr = {"InlineTypePassFieldsAsArgs", "false", "AlwaysIncrementalInline", "false"},
+        failOn = {ALLOC})
+    public MyValue1 test5(MyValue1 vt) {
+        Object o = vt;
+        vt = (MyValue1)o;
+        vt = test5_dontinline(vt);
+        vt = test5_inline(vt);
+        return vt;
+    }
+
+    @Run(test = "test5")
+    public void test5_verifier() {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        Asserts.assertEquals(test5(val), val);
+        Asserts.assertEquals(test5(null), null);
+    }
+
+    @DontInline
+    public MyValue1 test5_dontinline(MyValue1 vt) {
+        return vt;
+    }
+
+    @ForceInline
+    public MyValue1 test5_inline(MyValue1 vt) {
+        return vt;
+    }
+
+    @Test
+    public MyValue1 test6(Object obj) {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
+        try {
+            vt = (MyValue1)Objects.requireNonNull(obj);
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        return vt;
+    }
+
+    @Run(test = "test6")
+    public void test6_verifier() {
+        MyValue1 vt = test6(null);
+        Asserts.assertEquals(vt.hash(), testValue1.hash());
+    }
+
+    @ForceInline
+    public MyValue1 getNullInline() {
+        return null;
+    }
+
+    @DontInline
+    public MyValue1 getNullDontInline() {
+        return null;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public void test7() {
+        nullField = getNullInline();     // Should not throw
+        nullField = getNullDontInline(); // Should not throw
+        try {
+            valueField1 = getNullInline();
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        try {
+            valueField1 = getNullDontInline();
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Run(test = "test7")
+    public void test7_verifier() {
+        test7();
+    }
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public void test8() {
+        try {
+            valueField1 = nullField;
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Run(test = "test8")
+    public void test8_verifier() {
+        test8();
+    }
+
+    // Merge of two value objects, one being null
+    @Test
+    @IR(failOn = {ALLOC})
+    public void test9(boolean flag) {
+        MyValue1 v;
+        if (flag) {
+            v = valueField1;
+        } else {
+            v = nullField;
+        }
+        valueField1 = v;
+    }
+
+    @Run(test = "test9")
+    public void test9_verifier() {
+        test9(true);
+        try {
+            test9(false);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    // null constant
+    @Test
+    @IR(failOn = {ALLOC})
+    public void test10(boolean flag) {
+        MyValue1 val = flag ? valueField1 : null;
+        valueField1 = val;
+    }
+
+    @Run(test = "test10")
+    public void test10_verifier() {
+        test10(true);
+        try {
+            test10(false);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    // null constant
+    @Test
+    @IR(failOn = {ALLOC})
+    public void test11(boolean flag) {
+        MyValue1 val = flag ? null : valueField1;
+        valueField1 = val;
+    }
+
+    @Run(test = "test11")
+    public void test11_verifier() {
+        test11(false);
+        try {
+            test11(true);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    // null return
+    int test12_cnt;
+
+    @DontInline
+    public MyValue1 test12_helper() {
+        test12_cnt++;
+        return nullField;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public void test12() {
+        valueField1 = test12_helper();
+    }
+
+    @Run(test = "test12")
+    public void test12_verifier() {
+        try {
+            test12_cnt = 0;
+            test12();
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        if (test12_cnt != 1) {
+            throw new RuntimeException("call executed twice");
+        }
+    }
+
+    // null return at virtual call
+    class A {
+        public MyValue1 test13_helper() {
+            return nullField;
+        }
+    }
+
+    class B extends A {
+        public MyValue1 test13_helper() {
+            return nullField;
+        }
+    }
+
+    class C extends A {
+        public MyValue1 test13_helper() {
+            return nullField;
+        }
+    }
+
+    class D extends C {
+        public MyValue1 test13_helper() {
+            return nullField;
+        }
+    }
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public void test13(A a) {
+        valueField1 = a.test13_helper();
+    }
+
+    @Run(test = "test13")
+    public void test13_verifier() {
+        A a = new A();
+        A b = new B();
+        A c = new C();
+        A d = new D();
+        try {
+            test13(a);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        try {
+            test13(b);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        try {
+            test13(c);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        try {
+            test13(d);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    // Test writing null to a (flat) value class array
+    @ForceInline
+    public void test14_inline(Object[] oa, Object o, int index) {
+        oa[index] = o;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public void test14(MyValue1[] va, int index) {
+        test14_inline(va, nullField, index);
+    }
+
+    @Run(test = "test14")
+    public void test14_verifier() {
+        int index = Math.abs(rI) % 3;
+        try {
+            test14(testValue1Array, index);
+            throw new RuntimeException("No NPE thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        Asserts.assertEQ(testValue1Array[index].hash(), testValue1.hash());
+    }
+
+    @DontInline
+    MyValue1 getNullField1() {
+        return nullField;
+    }
+
+    @DontInline
+    MyValue1 getNullField2() {
+        return null;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public void test15() {
+        nullField = getNullField1(); // should not throw
+        try {
+            valueField1 = getNullField1();
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        try {
+            valueField1 = getNullField2();
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Run(test = "test15")
+    public void test15_verifier() {
+        test15();
+    }
+
+    @DontInline
+    public boolean test16_dontinline(MyValue1 vt) {
+        return vt == null;
+    }
+
+    // Test c2c call passing null for a value class
+    @Test
+    @IR(failOn = {ALLOC})
+    public boolean test16(Object arg) throws Exception {
+        Method test16method = getClass().getMethod("test16_dontinline", MyValue1.class);
+        return (boolean)test16method.invoke(this, arg);
+    }
+
+    @Run(test = "test16")
+    @Warmup(10000) // Warmup to make sure 'test17_dontinline' is compiled
+    public void test16_verifier() throws Exception {
+        boolean res = test16(null);
+        Asserts.assertTrue(res);
+    }
+
+    // Test scalarization of default value class with non-flattenable field
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    final value class Test17Value {
+        public final MyValue1 valueField;
+
+        @ForceInline
+        public Test17Value(MyValue1 valueField) {
+            this.valueField = valueField;
+        }
+    }
+
+    @Test
+    // TODO 8284443 When passing testValue1 to the constructor in scalarized form and incrementally inlining, we lose the oop
+    @IR(applyIfOr = {"InlineTypePassFieldsAsArgs", "false", "AlwaysIncrementalInline", "false"},
+        failOn = {ALLOC})
+    public Test17Value test17(boolean b) {
+        Test17Value vt1 = new Test17Value(null);
+        Test17Value vt2 = new Test17Value(testValue1);
+        return b ? vt1 : vt2;
+    }
+
+    @Run(test = "test17")
+    public void test17_verifier() {
+        test17(true);
+        test17(false);
+    }
+
+    static final MethodHandle test18_mh1;
+    static final MethodHandle test18_mh2;
+
+    static MyValue1 nullValue;
+
+    @DontInline
+    static void test18_target1(MyValue1 vt) {
+        nullValue = vt;
+    }
+
+    @ForceInline
+    static void test18_target2(MyValue1 vt) {
+        nullValue = vt;
+    }
+
+    // Test passing null for a value class
+    @Test
+    @IR(failOn = {ALLOC})
+    public void test18() throws Throwable {
+        test18_mh1.invokeExact(nullValue);
+        test18_mh2.invokeExact(nullValue);
+    }
+
+    @Run(test = "test18")
+    @Warmup(11000) // Make sure lambda forms get compiled
+    public void test18_verifier() {
+        try {
+            test18();
+        } catch (Throwable t) {
+            throw new RuntimeException("test18 failed", t);
+        }
+    }
+
+    static MethodHandle test19_mh1;
+    static MethodHandle test19_mh2;
+
+    @DontInline
+    static void test19_target1(MyValue1 vt) {
+        nullValue = vt;
+    }
+
+    @ForceInline
+    static void test19_target2(MyValue1 vt) {
+        nullValue = vt;
+    }
+
+    // Same as test12 but with non-final mh
+    @Test
+    @IR(failOn = {ALLOC})
+    public void test19() throws Throwable {
+        test19_mh1.invokeExact(nullValue);
+        test19_mh2.invokeExact(nullValue);
+    }
+
+    @Run(test = "test19")
+    @Warmup(11000) // Make sure lambda forms get compiled
+    public void test19_verifier() {
+        try {
+            test19();
+        } catch (Throwable t) {
+            throw new RuntimeException("test19 failed", t);
+        }
+    }
+
+    // Same as test12/13 but with constant null
+    @Test
+    @IR(failOn = {ALLOC})
+    public void test20(MethodHandle mh) throws Throwable {
+        mh.invoke(null);
+    }
+
+    @Run(test = "test20")
+    @Warmup(11000) // Make sure lambda forms get compiled
+    public void test20_verifier() {
+        try {
+            test20(test18_mh1);
+            test20(test18_mh2);
+            test20(test19_mh1);
+            test20(test19_mh2);
+        } catch (Throwable t) {
+            throw new RuntimeException("test20 failed", t);
+        }
+    }
+
+    // Test writing null to a flattenable/non-flattenable value class field in a value class
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    value class Test21Value {
+        MyValue1 valueField1;
+        @NullRestricted
+        MyValue1 valueField2;
+
+        @ForceInline
+        public Test21Value(MyValue1 valueField1, MyValue1 valueField2) {
+            this.valueField1 = valueField1;
+            this.valueField2 = valueField2;
+        }
+
+        @ForceInline
+        public Test21Value test1() {
+            return new Test21Value(null, this.valueField2); // Should not throw NPE
+        }
+
+        @ForceInline
+        public Test21Value test2() {
+            return new Test21Value(this.valueField1, null); // Should throw NPE
+        }
+    }
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public Test21Value test21(Test21Value vt) {
+        vt = vt.test1();
+        try {
+            vt = vt.test2();
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        return vt;
+    }
+
+    @Run(test = "test21")
+    public void test21_verifier() {
+        test21(new Test21Value(null, MyValue1.createDefaultInline()));
+    }
+
+    @DontInline
+    public MyValue1 test22_helper() {
+        return nullField;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public void test22() {
+        valueField1 = test22_helper();
+    }
+
+    @Run(test = "test22")
+    public void test22_verifier() {
+        try {
+            test22();
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    @IR(applyIfAnd = {"FlatArrayElementMaxSize", "= -1", "InlineTypePassFieldsAsArgs", "true"},
+        failOn = {ALLOC})
+    @IR(applyIfAnd = {"FlatArrayElementMaxSize", "= 0", "InlineTypePassFieldsAsArgs", "false"},
+        failOn = {ALLOC})
+    public void test23(MyValue1 val) {
+        MyValue1[] arr = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 2);
+        arr[0] = val;
+    }
+
+    @Run(test = "test23")
+    public void test23_verifier() {
+        MyValue1 val = null;
+        try {
+            test23(val);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    static MyValue1 nullBox;
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public MyValue1 test24() {
+        return Objects.requireNonNull(nullBox);
+    }
+
+    @Run(test = "test24")
+    public void test24_verifier() {
+        try {
+            test24();
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @DontInline
+    public void test25_callee(MyValue1 val) { }
+
+    // Test that when checkcasting from null-ok to null-free and back to null-ok we
+    // keep track of the information that the value object can never be null.
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public int test25(boolean b, MyValue1 vt1, MyValue1 vt2) {
+        vt1 = (MyValue1)vt1;
+        Object obj = b ? vt1 : vt2; // We should not allocate here
+        test25_callee((MyValue1) vt1);
+        return ((MyValue1)obj).x;
+    }
+
+    @Run(test = "test25")
+    public void test25_verifier(RunInfo info) {
+        int res = test25(true, testValue1, testValue1);
+        Asserts.assertEquals(res, testValue1.x);
+        res = test25(false, testValue1, testValue1);
+        Asserts.assertEquals(res, testValue1.x);
+        if (!info.isWarmUp()) {
+            try {
+                test25(true, null, testValue1);
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    // Test that chains of casts are folded and don't trigger an allocation
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public MyValue3 test26(MyValue3 vt) {
+        return ((MyValue3)((Object)((MyValue3)(MyValue3)((MyValue3)((Object)vt)))));
+    }
+
+    @Run(test = "test26")
+    public void test26_verifier() {
+        MyValue3 vt = MyValue3.create();
+        MyValue3 result = test26(vt);
+        Asserts.assertEquals(result, vt);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public MyValue3 test27(MyValue3 vt) {
+        return ((MyValue3)((Object)((MyValue3)(MyValue3)((MyValue3)((Object)vt)))));
+    }
+
+    @Run(test = "test27")
+    public void test27_verifier() {
+        MyValue3 vt = MyValue3.create();
+        MyValue3 result = (MyValue3) test27(vt);
+        Asserts.assertEquals(result, vt);
+    }
+
+    // Some more casting tests
+    @Test
+    public MyValue1 test28(MyValue1 vt, MyValue1 vtBox, int i) {
+        MyValue1 result = null;
+        if (i == 0) {
+            result = (MyValue1)vt;
+            result = null;
+        } else if (i == 1) {
+            result = (MyValue1)vt;
+        } else if (i == 2) {
+            result = vtBox;
+        }
+        return result;
+    }
+
+    @Run(test = "test28")
+    public void test28_verifier() {
+        MyValue1 result = test28(testValue1, null, 0);
+        Asserts.assertEquals(result, null);
+        result = test28(testValue1, testValue1, 1);
+        Asserts.assertEquals(result, testValue1);
+        result = test28(testValue1, null, 2);
+        Asserts.assertEquals(result, null);
+        result = test28(testValue1, testValue1, 2);
+        Asserts.assertEquals(result, testValue1);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public long test29(MyValue1 vt, MyValue1 vtBox) {
+        long result = 0;
+        for (int i = 0; i < 100; ++i) {
+            MyValue1 box;
+            if (i == 0) {
+                box = (MyValue1)vt;
+                box = null;
+            } else if (i < 99) {
+                box = (MyValue1)vt;
+            } else {
+                box = vtBox;
+            }
+            if (box != null) {
+                result += box.hash();
+            }
+        }
+        return result;
+    }
+
+    @Run(test = "test29")
+    public void test29_verifier() {
+        long result = test29(testValue1, null);
+        Asserts.assertEquals(result, testValue1.hash()*98);
+        result = test29(testValue1, testValue1);
+        Asserts.assertEquals(result, testValue1.hash()*99);
+    }
+
+    // Test null check of value object receiver with incremental inlining
+    public long test30_callee(MyValue1 vt) {
+        long result = 0;
+        try {
+            result = vt.hashInterpreted();
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        return result;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public long test30() {
+        return test30_callee(nullField);
+    }
+
+    @Run(test = "test30")
+    public void test30_verifier() {
+        long result = test30();
+        Asserts.assertEquals(result, 0L);
+    }
+
+    // Test casting null to unloaded value class
+    value class Test31Value {
+        private int i = 0;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public Object test31(Object o) {
+        return (Test31Value)o;
+    }
+
+    @Run(test = "test31")
+    public void test31_verifier() {
+        test31(null);
+    }
+
+    private static final MyValue1 constNullRefField = null;
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public MyValue1 test32() {
+        return constNullRefField;
+    }
+
+    @Run(test = "test32")
+    public void test32_verifier() {
+        MyValue1 result = test32();
+        Asserts.assertEquals(result, null);
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test33Value1 {
+        int x = 0;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test33Value2 {
+        Test33Value1 vt;
+
+        public Test33Value2() {
+            vt = new Test33Value1();
+        }
+    }
+
+    @NullRestricted
+    public static final Test33Value2 test33Val = new Test33Value2();
+
+    @Test
+    @IR(failOn = {ALLOC})
+    public Test33Value2 test33() {
+        return test33Val;
+    }
+
+    @Run(test = "test33")
+    public void test33_verifier() {
+        Test33Value2 result = test33();
+        Asserts.assertEquals(result, test33Val);
+    }
+
+    // Verify that static nullable inline-type fields are not
+    // treated as never-null by C2 when initialized at compile time.
+    private static MyValue1 test34Val;
+
+    @Test
+    public void test34(MyValue1 vt) {
+        if (test34Val == null) {
+            test34Val = vt;
+        }
+    }
+
+    @Run(test = "test34")
+    public void test34_verifier(RunInfo info) {
+        test34(testValue1);
+        if (!info.isWarmUp()) {
+            test34Val = null;
+            test34(testValue1);
+            Asserts.assertEquals(test34Val, testValue1);
+        }
+    }
+
+    // Same as test17 but with non-allocated value object
+    @Test
+    public Test17Value test35(boolean b) {
+        Test17Value vt1 = new Test17Value(null);
+        if ((Object)vt1.valueField != null) {
+            throw new RuntimeException("Should be null");
+        }
+        MyValue1 vt3 = MyValue1.createWithFieldsInline(rI, rL);
+        Test17Value vt2 = new Test17Value(vt3);
+        return b ? vt1 : vt2;
+    }
+
+    @Run(test = "test35")
+    public void test35_verifier() {
+        test35(true);
+        test35(false);
+    }
+
+    // Test that when explicitly null checking a value object, we keep
+    // track of the information that the value object can never be null.
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public int test37(boolean b, MyValue1 vt1, MyValue1 vt2) {
+        if (vt1 == null) {
+            return 0;
+        }
+        // vt1 should be scalarized because it's always non-null
+        Object obj = b ? vt1 : vt2; // We should not allocate vt2 here
+        test25_callee(vt1);
+        return ((MyValue1)obj).x;
+    }
+
+    @Run(test = "test37")
+    public void test37_verifier() {
+        int res = test37(true, testValue1, testValue1);
+        Asserts.assertEquals(res, testValue1.x);
+        res = test37(false, testValue1, testValue1);
+        Asserts.assertEquals(res, testValue1.x);
+    }
+
+    // Test that when explicitly null checking a value object receiver,
+    // we keep track of the information that the value object can never be null.
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public int test38(boolean b, MyValue1 vt1, MyValue1 vt2) {
+        vt1.hash(); // Inlined - Explicit null check
+        // vt1 should be scalarized because it's always non-null
+        Object obj = b ? vt1 : vt2; // We should not allocate vt2 here
+        test25_callee(vt1);
+        return ((MyValue1)obj).x;
+    }
+
+    @Run(test = "test38")
+    public void test38_verifier() {
+        int res = test38(true, testValue1, testValue1);
+        Asserts.assertEquals(res, testValue1.x);
+        res = test38(false, testValue1, testValue1);
+        Asserts.assertEquals(res, testValue1.x);
+    }
+
+    // Test that when implicitly null checking a value object receiver,
+    // we keep track of the information that the value object can never be null.
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public int test39(boolean b, MyValue1 vt1, MyValue1 vt2) {
+        vt1.hashInterpreted(); // Not inlined - Implicit null check
+        // vt1 should be scalarized because it's always non-null
+        Object obj = b ? vt1 : vt2; // We should not allocate vt2 here
+        test25_callee(vt1);
+        return ((MyValue1)obj).x;
+    }
+
+    @Run(test = "test39")
+    public void test39_verifier() {
+        int res = test39(true, testValue1, testValue1);
+        Asserts.assertEquals(res, testValue1.x);
+        res = test39(false, testValue1, testValue1);
+        Asserts.assertEquals(res, testValue1.x);
+    }
+
+    // Test NPE when casting constant null to a value class
+    @Test
+    @IR(failOn = {ALLOC})
+    public MyValue1 test40() {
+        Object NULL = null;
+        MyValue1 val = (MyValue1)NULL;
+        return Objects.requireNonNull(val);
+    }
+
+    @Run(test = "test40")
+    public void test40_verifier() {
+        try {
+            test40();
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    MyValue1 refField;
+    @NullRestricted
+    MyValue1 flatField;
+
+    // Test scalarization of .ref
+    @Test
+    @IR(failOn = {ALLOC_G, STORE, TRAP})
+    public int test41(boolean b) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b) {
+            val = refField;
+        }
+        return val.x;
+    }
+
+    @Run(test = "test41")
+    public void test41_verifier(RunInfo info) {
+        refField = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test41(true), refField.x);
+        Asserts.assertEquals(test41(false), testValue1.x);
+        if (!info.isWarmUp()) {
+            refField = null;
+            try {
+                Asserts.assertEquals(test41(false), testValue1.x);
+                test41(true);
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    // Same as test41 but with call to hash()
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test42(boolean b) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b) {
+            val = refField;
+        }
+        return val.hash();
+    }
+
+    @Run(test = "test42")
+    public void test42_verifier(RunInfo info) {
+        refField = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test42(true), refField.hash());
+        Asserts.assertEquals(test42(false), testValue1.hash());
+        if (!info.isWarmUp()) {
+            refField = null;
+            try {
+                Asserts.assertEquals(test42(false), testValue1.hash());
+                test42(true);
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    @Test
+    public MyValue1 test43(boolean b) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b) {
+            val = refField;
+        }
+        return val;
+    }
+
+    @Run(test = "test43")
+    public void test43_verifier(RunInfo info) {
+        refField = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test43(true).hash(), refField.hash());
+        Asserts.assertEquals(test43(false).hash(), testValue1.hash());
+        if (!info.isWarmUp()) {
+            refField = null;
+            Asserts.assertEquals(test43(true), null);
+        }
+    }
+
+    // Test scalarization when .ref is referenced in safepoint debug info
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public int test44(boolean b1, boolean b2, Method m) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b1) {
+            val = refField;
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return val.x;
+    }
+
+    @Run(test = "test44")
+    public void test44_verifier(RunInfo info) {
+        refField = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test44(true, false, info.getTest()), refField.x);
+        Asserts.assertEquals(test44(false, false, info.getTest()), testValue1.x);
+        if (!info.isWarmUp()) {
+            refField = null;
+            try {
+                Asserts.assertEquals(test44(false, false, info.getTest()), testValue1.x);
+                test44(true, false, info.getTest());
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+            refField = MyValue1.createWithFieldsInline(rI+1, rL+1);
+            Asserts.assertEquals(test44(true, true, info.getTest()), refField.x);
+            Asserts.assertEquals(test44(false, true, info.getTest()), testValue1.x);
+        }
+    }
+
+    @Test
+    public MyValue1 test45(boolean b1, boolean b2, Method m) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b1) {
+            val = refField;
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return val;
+    }
+
+    @Run(test = "test45")
+    public void test45_verifier(RunInfo info) {
+        refField = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test45(true, false, info.getTest()).hash(), refField.hash());
+        Asserts.assertEquals(test45(false, false, info.getTest()).hash(), testValue1.hash());
+        if (!info.isWarmUp()) {
+            refField = null;
+            Asserts.assertEquals(test45(true, false, info.getTest()), null);
+            refField = MyValue1.createWithFieldsInline(rI+1, rL+1);
+            Asserts.assertEquals(test45(true, true, info.getTest()).hash(), refField.hash());
+            Asserts.assertEquals(test45(false, true, info.getTest()).hash(), testValue1.hash());
+        }
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD, STORE, TRAP})
+    public int test46(boolean b) {
+        MyValue1 val = null;
+        if (b) {
+            val = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        return val.x;
+    }
+
+    @Run(test = "test46")
+    public void test46_verifier() {
+        Asserts.assertEquals(test46(true), testValue1.x);
+        try {
+            test46(false);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public MyValue1 test47(boolean b) {
+        MyValue1 val = null;
+        if (b) {
+            val = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        return val;
+    }
+
+    @Run(test = "test47")
+    public void test47_verifier() {
+        Asserts.assertEquals(test47(true).hash(), testValue1.hash());
+        Asserts.assertEquals(test47(false), null);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD, STORE, TRAP})
+    public int test48(boolean b) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b) {
+            val = null;
+        }
+        return val.x;
+    }
+
+    @Run(test = "test48")
+    public void test48_verifier() {
+        Asserts.assertEquals(test48(false), testValue1.x);
+        try {
+            test48(true);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public MyValue1 test49(boolean b) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b) {
+            val = null;
+        }
+        return val;
+    }
+
+    @Run(test = "test49")
+    public void test49_verifier() {
+        Asserts.assertEquals(test49(false).hash(), testValue1.hash());
+        Asserts.assertEquals(test49(true), null);
+    }
+
+    @ForceInline
+    public Object test50_helper() {
+        return flatField;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, TRAP})
+    public void test50(boolean b) {
+        Object o = null;
+        if (b) {
+            o = testValue1;
+        } else {
+            o = test50_helper();
+        }
+        flatField = (MyValue1)o;
+    }
+
+    @Run(test = "test50")
+    public void test50_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        flatField = vt;
+        test50(false);
+        Asserts.assertEquals(flatField.hash(), vt.hash());
+        test50(true);
+        Asserts.assertEquals(flatField.hash(), testValue1.hash());
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue1Wrapper {
+        MyValue1 vt;
+
+        @ForceInline
+        public MyValue1Wrapper(MyValue1 vt) {
+            this.vt = vt;
+        }
+
+        @ForceInline
+        public long hash() {
+            return (vt != null) ? vt.hash() : 0;
+        }
+    }
+
+    @NullRestricted
+    MyValue1Wrapper wrapperField;
+
+    @Test
+    @IR(failOn = {ALLOC_G, STORE, TRAP})
+    public long test51(boolean b) {
+        MyValue1Wrapper val = new MyValue1Wrapper(null);
+        if (b) {
+            val = wrapperField;
+        }
+        return val.hash();
+    }
+
+    @Run(test = "test51")
+    public void test51_verifier() {
+        wrapperField = new MyValue1Wrapper(testValue1);
+        Asserts.assertEquals(test51(true), wrapperField.hash());
+        Asserts.assertEquals(test51(false), (new MyValue1Wrapper(null)).hash());
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD, STORE, TRAP})
+    public boolean test52(boolean b) {
+        MyValue1 val = MyValue1.createDefaultInline();
+        if (b) {
+            val = null;
+        }
+        MyValue1Wrapper w = new MyValue1Wrapper(val);
+        return w.vt == null;
+    }
+
+    @Run(test = "test52")
+    public void test52_verifier() {
+        Asserts.assertTrue(test52(true));
+        Asserts.assertFalse(test52(false));
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD, STORE, TRAP})
+    public boolean test53(boolean b) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b) {
+            val = null;
+        }
+        MyValue1Wrapper w = new MyValue1Wrapper(val);
+        return w.vt == null;
+    }
+
+    @Run(test = "test53")
+    public void test53_verifier() {
+        Asserts.assertTrue(test53(true));
+        Asserts.assertFalse(test53(false));
+    }
+
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE, TRAP})
+    public long test54(boolean b1, boolean b2) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b1) {
+            val = null;
+        }
+        MyValue1Wrapper w = new MyValue1Wrapper(null);
+        if (b2) {
+            w = new MyValue1Wrapper(val);
+        }
+        return w.hash();
+    }
+
+    @Run(test = "test54")
+    public void test54_verifier() {
+        MyValue1Wrapper w = new MyValue1Wrapper(MyValue1.createWithFieldsInline(rI, rL));
+        Asserts.assertEquals(test54(false, false), (new MyValue1Wrapper(null)).hash());
+        Asserts.assertEquals(test54(false, true), w.hash());
+        Asserts.assertEquals(test54(true, false), (new MyValue1Wrapper(null)).hash());
+        Asserts.assertEquals(test54(true, true), 0L);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, STORE, TRAP})
+    public int test55(boolean b) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1Wrapper w = new MyValue1Wrapper(val);
+        if (b) {
+            w = new MyValue1Wrapper(refField);
+        }
+        return w.vt.x;
+    }
+
+    @Run(test = "test55")
+    public void test55_verifier(RunInfo info) {
+        refField = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test55(true), refField.x);
+        Asserts.assertEquals(test55(false), testValue1.x);
+        if (!info.isWarmUp()) {
+            refField = null;
+            try {
+                Asserts.assertEquals(test55(false), testValue1.x);
+                test55(true);
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    @Test
+    @IR(failOn = {ALLOC, STORE, TRAP})
+    public long test56(boolean b) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1Wrapper w = new MyValue1Wrapper(val);
+        if (b) {
+            w = new MyValue1Wrapper(refField);
+        }
+        return w.vt.hash();
+    }
+
+    @Run(test = "test56")
+    public void test56_verifier(RunInfo info) {
+        refField = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test56(true), refField.hash());
+        Asserts.assertEquals(test56(false), testValue1.hash());
+        if (!info.isWarmUp()) {
+            refField = null;
+            try {
+                Asserts.assertEquals(test56(false), testValue1.hash());
+                test56(true);
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    @Test
+    public MyValue1 test57(boolean b) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1Wrapper w = new MyValue1Wrapper(val);
+        if (b) {
+            w = new MyValue1Wrapper(refField);
+        }
+        return w.vt;
+    }
+
+    @Run(test = "test57")
+    public void test57_verifier(RunInfo info) {
+        refField = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test57(true).hash(), refField.hash());
+        Asserts.assertEquals(test57(false).hash(), testValue1.hash());
+        if (!info.isWarmUp()) {
+            refField = null;
+            Asserts.assertEquals(test57(true), null);
+        }
+    }
+
+    // Test scalarization when .ref is referenced in safepoint debug info
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public int test58(boolean b1, boolean b2, Method m) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1Wrapper w = new MyValue1Wrapper(val);
+        if (b1) {
+            w = new MyValue1Wrapper(refField);
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return w.vt.x;
+    }
+
+    @Run(test = "test58")
+    public void test58_verifier(RunInfo info) {
+        refField = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test58(true, false, info.getTest()), refField.x);
+        Asserts.assertEquals(test58(false, false, info.getTest()), testValue1.x);
+        if (!info.isWarmUp()) {
+            refField = null;
+            try {
+                Asserts.assertEquals(test58(false, false, info.getTest()), testValue1.x);
+                test58(true, false, info.getTest());
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+            refField = MyValue1.createWithFieldsInline(rI+1, rL+1);
+            Asserts.assertEquals(test58(true, true, info.getTest()), refField.x);
+            Asserts.assertEquals(test58(false, true, info.getTest()), testValue1.x);
+        }
+    }
+
+    @Test
+    public MyValue1 test59(boolean b1, boolean b2, Method m) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1Wrapper w = new MyValue1Wrapper(val);
+        if (b1) {
+            w = new MyValue1Wrapper(refField);
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return w.vt;
+    }
+
+    @Run(test = "test59")
+    public void test59_verifier(RunInfo info) {
+        refField = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test59(true, false, info.getTest()).hash(), refField.hash());
+        Asserts.assertEquals(test59(false, false, info.getTest()).hash(), testValue1.hash());
+        if (!info.isWarmUp()) {
+            refField = null;
+            Asserts.assertEquals(test59(true, false, info.getTest()), null);
+            refField = MyValue1.createWithFieldsInline(rI+1, rL+1);
+            Asserts.assertEquals(test59(true, true, info.getTest()).hash(), refField.hash());
+            Asserts.assertEquals(test59(false, true, info.getTest()).hash(), testValue1.hash());
+        }
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD, STORE, TRAP})
+    public int test60(boolean b) {
+        MyValue1Wrapper w = new MyValue1Wrapper(null);
+        if (b) {
+            MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+            w = new MyValue1Wrapper(val);
+        }
+        return w.vt.x;
+    }
+
+    @Run(test = "test60")
+    public void test60_verifier() {
+        Asserts.assertEquals(test60(true), testValue1.x);
+        try {
+            test60(false);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public MyValue1 test61(boolean b) {
+        MyValue1Wrapper w = new MyValue1Wrapper(null);
+        if (b) {
+            MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+            w = new MyValue1Wrapper(val);
+        }
+        return w.vt;
+    }
+
+    @Run(test = "test61")
+    public void test61_verifier() {
+        Asserts.assertEquals(test61(true).hash(), testValue1.hash());
+        Asserts.assertEquals(test61(false), null);
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD, STORE, TRAP})
+    public int test62(boolean b) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1Wrapper w = new MyValue1Wrapper(val);
+        if (b) {
+            w = new MyValue1Wrapper(null);
+        }
+        return w.vt.x;
+    }
+
+    @Run(test = "test62")
+    public void test62_verifier() {
+        Asserts.assertEquals(test62(false), testValue1.x);
+        try {
+            test62(true);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public MyValue1 test63(boolean b) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1Wrapper w = new MyValue1Wrapper(val);
+        if (b) {
+            w = new MyValue1Wrapper(null);
+        }
+        return w.vt;
+    }
+
+    @Run(test = "test63")
+    public void test63_verifier() {
+        Asserts.assertEquals(test63(false).hash(), testValue1.hash());
+        Asserts.assertEquals(test63(true), null);
+    }
+
+    @ForceInline
+    public MyValue1 test64_helper() {
+        return flatField;
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, TRAP})
+    public void test64(boolean b) {
+        MyValue1Wrapper w = new MyValue1Wrapper(null);
+        if (b) {
+            w = new MyValue1Wrapper(testValue1);
+        } else {
+            w = new MyValue1Wrapper(test64_helper());
+        }
+        flatField = w.vt;
+    }
+
+    @Run(test = "test64")
+    public void test64_verifier() {
+        MyValue1 vt = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        flatField = vt;
+        test64(false);
+        Asserts.assertEquals(flatField.hash(), vt.hash());
+        test64(true);
+        Asserts.assertEquals(flatField.hash(), testValue1.hash());
+    }
+
+    @Test
+    @IR(failOn = {ALLOC_G, LOAD, STORE, TRAP})
+    public long test65(boolean b) {
+        MyValue1 val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b) {
+            val = null;
+        }
+        if (val != null) {
+            return val.hashPrimitive();
+        }
+        return 42;
+    }
+
+    @Run(test = "test65")
+    public void test65_verifier() {
+        Asserts.assertEquals(test65(true), 42L);
+        Asserts.assertEquals(test65(false), MyValue1.createWithFieldsInline(rI, rL).hashPrimitive());
+    }
+
+    @ForceInline
+    public Object test66_helper(Object arg) {
+        return arg;
+    }
+
+    // Test that .ref arg does not block scalarization
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public int test66(boolean b1, boolean b2, MyValue1 arg, Method m) {
+        Object val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b1) {
+            val = test66_helper(arg);
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return ((MyValue1)val).x;
+    }
+
+    @Run(test = "test66")
+    public void test66_verifier(RunInfo info) {
+        MyValue1 arg = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test66(true, false, arg, info.getTest()), arg.x);
+        Asserts.assertEquals(test66(false, false, arg, info.getTest()), testValue1.x);
+        if (!info.isWarmUp()) {
+            try {
+                Asserts.assertEquals(test66(false, false, arg, info.getTest()), testValue1.x);
+                test66(true, false, null, info.getTest());
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+            Asserts.assertEquals(test66(true, true, arg, info.getTest()), arg.x);
+            Asserts.assertEquals(test66(false, true, arg, info.getTest()), testValue1.x);
+        }
+    }
+
+    @DontInline
+    public MyValue1 test67_helper1() {
+        return refField;
+    }
+
+    @ForceInline
+    public Object test67_helper2() {
+        return test67_helper1();
+    }
+
+    // Test that .ref return does not block scalarization
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public long test67(boolean b1, boolean b2, Method m) {
+        Object val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b1) {
+            val = test67_helper2();
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return ((MyValue1)val).hash();
+    }
+
+    @Run(test = "test67")
+    public void test67_verifier(RunInfo info) {
+        refField = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test67(true, false, info.getTest()), refField.hash());
+        Asserts.assertEquals(test67(false, false, info.getTest()), testValue1.hash());
+        if (!info.isWarmUp()) {
+            refField = null;
+            try {
+                Asserts.assertEquals(test67(false, false, info.getTest()), testValue1.hash());
+                test67(true, false, info.getTest());
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+            refField = MyValue1.createWithFieldsInline(rI+1, rL+1);
+            Asserts.assertEquals(test67(true, true, info.getTest()), refField.hash());
+            Asserts.assertEquals(test67(false, true, info.getTest()), testValue1.hash());
+        }
+    }
+
+    @ForceInline
+    public Object test68_helper(Object arg) {
+        MyValue1 tmp = (MyValue1)arg; // Result of cast is unused
+        return arg;
+    }
+
+    // Test that scalarization enabled by cast is applied to parsing map
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public int test68(boolean b1, boolean b2, Object arg, Method m) {
+        Object val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b1) {
+            val = test68_helper(arg);
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return ((MyValue1)val).x;
+    }
+
+    @Run(test = "test68")
+    public void test68_verifier(RunInfo info) {
+        MyValue1 arg = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test68(true, false, arg, info.getTest()), arg.x);
+        Asserts.assertEquals(test68(false, false, arg, info.getTest()), testValue1.x);
+        if (!info.isWarmUp()) {
+            try {
+                Asserts.assertEquals(test68(false, false, arg, info.getTest()), testValue1.x);
+                test68(true, false, null, info.getTest());
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+            Asserts.assertEquals(test68(true, true, arg, info.getTest()), arg.x);
+            Asserts.assertEquals(test68(false, true, arg, info.getTest()), testValue1.x);
+        }
+    }
+
+    @ForceInline
+    public Object test69_helper(Object arg) {
+        MyValue1 tmp = (MyValue1)arg; // Result of cast is unused
+        return arg;
+    }
+
+    // Same as test68 but with ClassCastException
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public int test69(boolean b1, boolean b2, Object arg, Method m) {
+        Object val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b1) {
+            val = test69_helper(arg);
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return ((MyValue1)val).x;
+    }
+
+    @Run(test = "test69")
+    @Warmup(10000) // Make sure precise profile information is available
+    public void test69_verifier(RunInfo info) {
+        MyValue1 arg = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test69(true, false, arg, info.getTest()), arg.x);
+        Asserts.assertEquals(test69(false, false, arg, info.getTest()), testValue1.x);
+        try {
+            test69(true, false, 42, info.getTest());
+            throw new RuntimeException("ClassCastException expected");
+        } catch (ClassCastException e) {
+            // Expected
+        }
+        if (!info.isWarmUp()) {
+            try {
+                Asserts.assertEquals(test69(false, false, arg, info.getTest()), testValue1.x);
+                test69(true, false, null, info.getTest());
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+            Asserts.assertEquals(test69(true, true, arg, info.getTest()), arg.x);
+            Asserts.assertEquals(test69(false, true, arg, info.getTest()), testValue1.x);
+        }
+    }
+
+    @ForceInline
+    public Object test70_helper(Object arg) {
+        MyValue1 tmp = (MyValue1)arg; // Result of cast is unused
+        return arg;
+    }
+
+    // Same as test68 but with ClassCastException and frequent NullPointerException
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public int test70(boolean b1, boolean b2, Object arg, Method m) {
+        Object val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b1) {
+            val = test70_helper(arg);
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return ((MyValue1)val).x;
+    }
+
+    @Run(test = "test70")
+    @Warmup(10000) // Make sure precise profile information is available
+    public void test70_verifier(RunInfo info) {
+        MyValue1 arg = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test70(true, false, arg, info.getTest()), arg.x);
+        Asserts.assertEquals(test70(false, false, arg, info.getTest()), testValue1.x);
+        try {
+            test70(true, false, 42, info.getTest());
+            throw new RuntimeException("ClassCastException expected");
+        } catch (ClassCastException e) {
+            // Expected
+        }
+        try {
+            Asserts.assertEquals(test70(false, false, arg, info.getTest()), testValue1.x);
+            test70(true, false, null, info.getTest());
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        if (!info.isWarmUp()) {
+            Asserts.assertEquals(test70(true, true, arg, info.getTest()), arg.x);
+            Asserts.assertEquals(test70(false, true, arg, info.getTest()), testValue1.x);
+        }
+    }
+
+    @ForceInline
+    public Object test71_helper(Object arg) {
+        MyValue1 tmp = (MyValue1)arg; // Result of cast is unused
+        return arg;
+    }
+
+    // Same as test68 but with .ref cast
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public int test71(boolean b1, boolean b2, Object arg, Method m) {
+        Object val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b1) {
+            val = test71_helper(arg);
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return ((MyValue1)val).x;
+    }
+
+    @Run(test = "test71")
+    public void test71_verifier(RunInfo info) {
+        MyValue1 arg = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test71(true, false, arg, info.getTest()), arg.x);
+        Asserts.assertEquals(test71(false, false, arg, info.getTest()), testValue1.x);
+        if (!info.isWarmUp()) {
+            try {
+                Asserts.assertEquals(test71(false, false, arg, info.getTest()), testValue1.x);
+                test71(true, false, null, info.getTest());
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+            Asserts.assertEquals(test71(true, true, arg, info.getTest()), arg.x);
+            Asserts.assertEquals(test71(false, true, arg, info.getTest()), testValue1.x);
+        }
+    }
+
+    @ForceInline
+    public Object test72_helper(Object arg) {
+        MyValue1 tmp = (MyValue1)arg; // Result of cast is unused
+        return arg;
+    }
+
+    // Same as test71 but with ClassCastException and hash() call
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public long test72(boolean b1, boolean b2, Object arg, Method m) {
+        Object val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b1) {
+            val = test72_helper(arg);
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return ((MyValue1)val).hash();
+    }
+
+    @Run(test = "test72")
+    @Warmup(10000) // Make sure precise profile information is available
+    public void test72_verifier(RunInfo info) {
+        MyValue1 arg = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test72(true, false, arg, info.getTest()), arg.hash());
+        Asserts.assertEquals(test72(false, false, arg, info.getTest()), testValue1.hash());
+        try {
+            test72(true, false, 42, info.getTest());
+            throw new RuntimeException("ClassCastException expected");
+        } catch (ClassCastException e) {
+            // Expected
+        }
+        if (!info.isWarmUp()) {
+            try {
+                Asserts.assertEquals(test72(false, false, arg, info.getTest()), testValue1.hash());
+                test72(true, false, null, info.getTest());
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+            Asserts.assertEquals(test72(true, true, arg, info.getTest()), arg.hash());
+            Asserts.assertEquals(test72(false, true, arg, info.getTest()), testValue1.hash());
+        }
+    }
+
+    @ForceInline
+    public Object test73_helper(Object arg) {
+        MyValue1 tmp = (MyValue1)arg; // Result of cast is unused
+        return arg;
+    }
+
+    // Same as test71 but with ClassCastException and frequent NullPointerException
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public int test73(boolean b1, boolean b2, Object arg, Method m) {
+        Object val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b1) {
+            val = test73_helper(arg);
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return ((MyValue1)val).x;
+    }
+
+    @Run(test = "test73")
+    @Warmup(10000) // Make sure precise profile information is available
+    public void test73_verifier(RunInfo info) {
+        MyValue1 arg = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test73(true, false, arg, info.getTest()), arg.x);
+        Asserts.assertEquals(test73(false, false, arg, info.getTest()), testValue1.x);
+        try {
+            test73(true, false, 42, info.getTest());
+            throw new RuntimeException("ClassCastException expected");
+        } catch (ClassCastException e) {
+            // Expected
+        }
+        try {
+            Asserts.assertEquals(test73(false, false, arg, info.getTest()), testValue1.x);
+            test73(true, false, null, info.getTest());
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        if (!info.isWarmUp()) {
+            Asserts.assertEquals(test73(true, true, arg, info.getTest()), arg.x);
+            Asserts.assertEquals(test73(false, true, arg, info.getTest()), testValue1.x);
+        }
+    }
+
+    @ForceInline
+    public Object test74_helper(Object arg) {
+        return (MyValue1)arg;
+    }
+
+    // Same as test73 but result of cast is used and hash() is called
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public long test74(boolean b1, boolean b2, Object arg, Method m) {
+        Object val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b1) {
+            val = test74_helper(arg);
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return ((MyValue1)val).hash();
+    }
+
+    @Run(test = "test74")
+    @Warmup(10000) // Make sure precise profile information is available
+    public void test74_verifier(RunInfo info) {
+        MyValue1 arg = MyValue1.createWithFieldsInline(rI+1, rL+1);
+        Asserts.assertEquals(test74(true, false, arg, info.getTest()), arg.hash());
+        Asserts.assertEquals(test74(false, false, arg, info.getTest()), testValue1.hash());
+        try {
+            test74(true, false, 42, info.getTest());
+            throw new RuntimeException("ClassCastException expected");
+        } catch (ClassCastException e) {
+            // Expected
+        }
+        try {
+            Asserts.assertEquals(test74(false, false, arg, info.getTest()), testValue1.hash());
+            test74(true, false, null, info.getTest());
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        if (!info.isWarmUp()) {
+            Asserts.assertEquals(test74(true, true, arg, info.getTest()), arg.hash());
+            Asserts.assertEquals(test74(false, true, arg, info.getTest()), testValue1.hash());
+        }
+    }
+
+    // Test new merge path being added for exceptional control flow
+    @Test
+    @IR(failOn = {ALLOC})
+    public MyValue1 test75(MyValue1 vt, Object obj) {
+        try {
+            vt = (MyValue1)obj;
+            throw new RuntimeException("ClassCastException expected");
+        } catch (ClassCastException e) {
+            // Expected
+        }
+        return vt;
+    }
+
+    @Run(test = "test75")
+    public void test75_verifier() {
+        MyValue1 vt = testValue1;
+        MyValue1 result = test75(vt, Integer.valueOf(rI));
+        Asserts.assertEquals(result.hash(), vt.hash());
+    }
+
+    @ForceInline
+    public Object test76_helper() {
+        return constNullRefField;
+    }
+
+    // Test that constant null field does not block scalarization
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE})
+    public long test76(boolean b1, boolean b2, Method m) {
+        Object val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b1) {
+            val = test76_helper();
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        val = Objects.requireNonNull(val);
+        return ((MyValue1)val).hash();
+    }
+
+    @Run(test = "test76")
+    public void test76_verifier(RunInfo info) {
+        Asserts.assertEquals(test76(false, false, info.getTest()), testValue1.hash());
+        try {
+            test76(true, false, info.getTest());
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        if (!info.isWarmUp()) {
+            Asserts.assertEquals(test76(false, true, info.getTest()), testValue1.hash());
+            try {
+                test76(true, true, info.getTest());
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    private static final Object constObjectValField = MyValue1.createWithFieldsInline(rI+1, rL+1);
+
+    @ForceInline
+    public Object test77_helper() {
+        return constObjectValField;
+    }
+
+    // Test that constant object field with value class content does not block scalarization
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE})
+    public long test77(boolean b1, boolean b2, Method m) {
+        Object val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b1) {
+            val = test77_helper();
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return ((MyValue1)val).hash();
+    }
+
+    @Run(test = "test77")
+    public void test77_verifier(RunInfo info) {
+        Asserts.assertEquals(test77(true, false, info.getTest()), ((MyValue1)constObjectValField).hash());
+        Asserts.assertEquals(test77(false, false, info.getTest()), testValue1.hash());
+        if (!info.isWarmUp()) {
+          Asserts.assertEquals(test77(true, false, info.getTest()), ((MyValue1)constObjectValField).hash());
+          Asserts.assertEquals(test77(false, false, info.getTest()), testValue1.hash());
+        }
+    }
+
+    @ForceInline
+    public Object test78_helper() {
+        return null;
+    }
+
+    // Test that constant null does not block scalarization
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE})
+    public long test78(boolean b1, boolean b2, Method m) {
+        Object val = MyValue1.createWithFieldsInline(rI, rL);
+        if (b1) {
+            val = test78_helper();
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        val = Objects.requireNonNull(val);
+        return ((MyValue1)val).hash();
+    }
+
+    @Run(test = "test78")
+    public void test78_verifier(RunInfo info) {
+        Asserts.assertEquals(test78(false, false, info.getTest()), testValue1.hash());
+        try {
+            test78(true, false, info.getTest());
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        if (!info.isWarmUp()) {
+            Asserts.assertEquals(test78(false, true, info.getTest()), testValue1.hash());
+            try {
+                test78(true, true, info.getTest());
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    @ForceInline
+    public Object test79_helper() {
+        return null;
+    }
+
+    // Same as test78 but will trigger different order of PhiNode inputs
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE})
+    public long test79(boolean b1, boolean b2, Method m) {
+        Object val = test79_helper();
+        if (b1) {
+            val = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        if (b2) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        val = Objects.requireNonNull(val);
+        return ((MyValue1)val).hash();
+    }
+
+    @Run(test = "test79")
+    public void test79_verifier(RunInfo info) {
+        Asserts.assertEquals(test79(true, false, info.getTest()), testValue1.hash());
+        try {
+            test79(false, false, info.getTest());
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+        if (!info.isWarmUp()) {
+            Asserts.assertEquals(test79(true, true, info.getTest()), testValue1.hash());
+            try {
+                test79(false, true, info.getTest());
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException e) {
+                // Expected
+            }
+        }
+    }
+
+    @ForceInline
+    public Object test80_helper(Object obj, int i) {
+        if ((i % 2) == 0) {
+            return MyValue1.createWithFieldsInline(i, i);
+        }
+        return obj;
+    }
+
+    // Test that phi nodes referencing themselves (loops) do not block scalarization
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE})
+    public long test80() {
+        Object val = MyValue1.createWithFieldsInline(rI, rL);
+        for (int i = 0; i < 100; ++i) {
+            val = test80_helper(val, i);
+        }
+        return ((MyValue1)val).hash();
+    }
+
+    private long test80Result = 0;
+
+    @Run(test = "test80")
+    public void test80_verifier() {
+        if (test80Result == 0) {
+            test80Result = test80();
+        }
+        Asserts.assertEquals(test80(), test80Result);
+    }
+
+// TODO 8325106 Fails because they are not compilable with Scenario 3, probably we run out of nodes ...
+/*
+    @ForceInline
+    public Object test81_helper(Object obj, int i) {
+        if ((i % 2) == 0) {
+            return MyValue1.createWithFieldsInline(i, i);
+        }
+        return obj;
+    }
+
+    // Test nested loops
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE})
+    public long test81() {
+        Object val = null;
+        for (int i = 0; i < 10; ++i) {
+            for (int j = 0; j < 10; ++j) {
+                for (int k = 0; k < 10; ++k) {
+                    val = test81_helper(val, i + j + k);
+                }
+                val = test81_helper(val, i + j);
+            }
+            val = test81_helper(val, i);
+        }
+        return ((MyValue1)val).hash();
+    }
+
+    private long test81Result = 0;
+
+    @Run(test = "test81")
+    public void test81_verifier() {
+        if (test81Result == 0) {
+            test81Result = test81();
+        }
+        Asserts.assertEquals(test81(), test81Result);
+    }
+
+    @ForceInline
+    public Object test82_helper(Object obj, int i) {
+        if ((i % 2) == 0) {
+            return MyValue1.createWithFieldsInline(i, i);
+        }
+        return obj;
+    }
+
+    // Test loops with casts
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE})
+    public long test82() {
+        Object val = null;
+        for (int i = 0; i < 10; ++i) {
+            for (int j = 0; j < 10; ++j) {
+                for (int k = 0; k < 10; ++k) {
+                    val = test82_helper(val, i + j + k);
+                }
+                if (val != null) {
+                    val = test82_helper(val, i + j);
+                }
+            }
+            val = test82_helper(val, i);
+        }
+        return ((MyValue1)val).hash();
+    }
+
+    private long test82Result = 0;
+
+    @Run(test = "test82")
+    public void test82_verifier() {
+        if (test82Result == 0) {
+            test82Result = test81();
+        }
+        Asserts.assertEquals(test82(), test82Result);
+    }
+*/
+
+    @ForceInline
+    public Object test83_helper(boolean b) {
+        if (b) {
+            return MyValue1.createWithFieldsInline(rI, rL);
+        }
+        return null;
+    }
+
+    // Test that CastPP does not block sclarization in safepoints
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE})
+    public long test83(boolean b, Method m) {
+        Object val = test83_helper(b);
+        if (val != null) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+            return ((MyValue1)val).hash();
+        }
+        return 0;
+    }
+
+    @Run(test = "test83")
+    public void test83_verifier(RunInfo info) {
+        Asserts.assertEquals(test83(false, info.getTest()), 0L);
+        if (!info.isWarmUp()) {
+            Asserts.assertEquals(test83(true, info.getTest()), testValue1.hash());
+        }
+    }
+
+    @ForceInline
+    public Object test84_helper(Object obj, int i) {
+        if ((i % 2) == 0) {
+            return new MyValue1Wrapper(MyValue1.createWithFieldsInline(i, i));
+        }
+        return obj;
+    }
+
+    // Same as test80 but with wrapper
+    @Test
+    @IR(failOn = {ALLOC, LOAD, STORE})
+    public long test84() {
+        Object val = new MyValue1Wrapper(MyValue1.createWithFieldsInline(rI, rL));
+        for (int i = 0; i < 100; ++i) {
+            val = test84_helper(val, i);
+        }
+        return ((MyValue1Wrapper)val).vt.hash();
+    }
+
+    private long test84Result = 0;
+
+    @Run(test = "test84")
+    public void test84_verifier() {
+        if (test84Result == 0) {
+            test84Result = test84();
+        }
+        Asserts.assertEquals(test84(), test84Result);
+    }
+
+    @ForceInline
+    public Object test85_helper(Object obj, int i) {
+        if ((i % 2) == 0) {
+            return new MyValue1Wrapper(MyValue1.createWithFieldsInline(i, i));
+        }
+        return obj;
+    }
+
+// TODO 8325106 Fails because they are not compilable with Scenario 3, probably we run out of nodes ...
+/*
+    // Same as test81 but with wrapper
+    @Test
+    // TODO 8325106 Fails with Scenario 5
+    // @IR(failOn = {ALLOC, LOAD, STORE})
+    public long test85() {
+        Object val = new MyValue1Wrapper(null);
+        for (int i = 0; i < 10; ++i) {
+            for (int j = 0; j < 10; ++j) {
+                for (int k = 0; k < 10; ++k) {
+                    val = test85_helper(val, i + j + k);
+                }
+                val = test85_helper(val, i + j);
+            }
+            val = test85_helper(val, i);
+        }
+        MyValue1 vt = ((MyValue1Wrapper)val).vt;
+        vt = Objects.requireNonNull(vt);
+        return vt.hash();
+    }
+
+    private long test85Result = 0;
+
+    @Run(test = "test85")
+    public void test85_verifier() {
+        if (test85Result == 0) {
+            test85Result = test85();
+        }
+        Asserts.assertEquals(test85(), test85Result);
+    }
+*/
+
+    static final class ObjectWrapper {
+        public Object obj;
+
+        @ForceInline
+        public ObjectWrapper(Object obj) {
+            this.obj = obj;
+        }
+    }
+
+    // Test scalarization with phi referencing itself
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
+        failOn = {ALLOC, STORE},
+        counts = {LOAD, " = 4"}) // 4 loads from the non-flattened MyValue1.v4 fields
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+        failOn = {ALLOC, STORE})
+    public long test86(MyValue1 vt) {
+        ObjectWrapper val = new ObjectWrapper(vt);
+        for (int i = 0; i < 10; ++i) {
+            for (int j = 0; j < 10; ++j) {
+                val.obj = val.obj;
+            }
+        }
+        return ((MyValue1)val.obj).hash();
+    }
+
+    @Run(test = "test86")
+    public void test86_verifier() {
+        test86(testValue1);
+        Asserts.assertEquals(test86(testValue1), testValue1.hash());
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    public static value class Test87C0 {
+        int x = 0;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    public static value class Test87C1 {
+        @NullRestricted
+        Test87C0 field = new Test87C0();
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    public static value class Test87C2 {
+        @NullRestricted
+        Test87C1 field = new Test87C1();
+    }
+
+    // Test merging field loads in return
+    @Test
+    public Test87C1 test87(boolean b, Test87C2 v1, Test87C2 v2) {
+        if (b) {
+            return v1.field;
+        } else {
+            return v2.field;
+        }
+    }
+
+    @Run(test = "test87")
+    public void test87_verifier() {
+        Test87C2 v = new Test87C2();
+        Asserts.assertEQ(test87(true, v, v), v.field);
+        Asserts.assertEQ(test87(false, v, v), v.field);
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test88Value {
+        int x = 0;
+    }
+
+    static class Test88MyClass {
+        int x = 0;
+        int y = rI;
+    }
+
+    @ForceInline
+    Object test88Helper() {
+        return new Test88Value();
+    }
+
+    // Test LoadNode::Identity optimization with always failing checkcast
+    @Test
+    public int test88() {
+        Object obj = test88Helper();
+        return ((Test88MyClass)obj).y;
+    }
+
+    @Run(test = "test88")
+    public void test88_verifier() {
+        try {
+            test88();
+            throw new RuntimeException("No ClassCastException thrown");
+        } catch (ClassCastException e) {
+            // Expected
+        }
+    }
+
+    // Same as test88 but with Phi
+    @Test
+    public int test89(boolean b) {
+        Test88MyClass obj = b ? (Test88MyClass)test88Helper() : (Test88MyClass)test88Helper();
+        return obj.y;
+    }
+
+    @Run(test = "test89")
+    public void test89_verifier() {
+        try {
+            test89(false);
+            throw new RuntimeException("No ClassCastException thrown");
+        } catch (ClassCastException e) {
+            // Expected
+        }
+        try {
+            test89(true);
+            throw new RuntimeException("No ClassCastException thrown");
+        } catch (ClassCastException e) {
+            // Expected
+        }
+    }
+
+    @ForceInline
+    public boolean test90_inline(MyValue1 vt) {
+        return vt == null;
+    }
+
+    // Test scalarization with speculative NULL type
+    @Test
+    @IR(failOn = {ALLOC})
+    public boolean test90(Method m) throws Exception {
+        Object arg = null;
+        return (boolean)m.invoke(this, arg);
+    }
+
+    @Run(test = "test90")
+    @Warmup(10000)
+    public void test90_verifier() throws Exception {
+        Method m = getClass().getMethod("test90_inline", MyValue1.class);
+        Asserts.assertTrue(test90(m));
+    }
+
+    // Test that scalarization does not introduce redundant/unused checks
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+        failOn = {ALLOC, CMPP})
+    public Object test91(MyValue1 vt) {
+        return vt;
+    }
+
+    @Run(test = "test91")
+    public void test91_verifier() {
+        Asserts.assertEQ(test91(testValue1), testValue1);
+    }
+
+    MyValue1 test92Field = testValue1;
+
+    // Same as test91 but with field access
+    @Test
+    @IR(failOn = {ALLOC, CMPP})
+    public Object test92() {
+        return test92Field;
+    }
+
+    @Run(test = "test92")
+    public void test92_verifier() {
+        Asserts.assertEQ(test92(), testValue1);
+    }
+
+    private static final MethodHandle refCheckCast = OldInstructionHelper.loadCode(MethodHandles.lookup(),
+        "refCheckCast",
+        MethodType.methodType(MyValue2.class, TestNullableInlineTypes.class, MyValue1.class),
+        CODE -> {
+            CODE.
+            aload_1().
+            checkcast(MyValue2.class).
+            return_(TypeTag.A);
+        });
+
+    // Test checkcast that only passes with null
+    @Test
+    public Object test93(MyValue1 vt) throws Throwable {
+        return refCheckCast.invoke(this, vt);
+    }
+
+    @Run(test = "test93")
+    @Warmup(10000)
+    public void test93_verifier() throws Throwable {
+        Asserts.assertEQ(test93(null), null);
+    }
+
+    @DontInline
+    public MyValue1 test94_helper1(MyValue1 vt) {
+        return vt;
+    }
+
+    @ForceInline
+    public MyValue1 test94_helper2(MyValue1 vt) {
+        return test94_helper1(vt);
+    }
+
+    @ForceInline
+    public MyValue1 test94_helper3(Object vt) {
+        return test94_helper2((MyValue1)vt);
+    }
+
+    // Test that calling convention optimization prevents buffering of arguments
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
+        counts = {ALLOC_G, " = 2"}) // 1 MyValue2 allocation + 1 Integer allocation
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+        counts = {ALLOC_G, " = 3"}) // 1 MyValue1 allocation + 1 MyValue2 allocation + 1 Integer allocation
+    public MyValue1 test94(MyValue1 vt) {
+        MyValue1 res = test94_helper1(vt);
+        vt = MyValue1.createWithFieldsInline(rI, rL);
+        test94_helper1(vt);
+        test94_helper2(vt);
+        test94_helper3(vt);
+        return res;
+    }
+
+    @Run(test = "test94")
+    public void test94_verifier() {
+        Asserts.assertEQ(test94(testValue1), testValue1);
+        Asserts.assertEQ(test94(null), null);
+    }
+
+    @DontInline
+    public static MyValue1 test95_helper1(MyValue1 vt) {
+        return vt;
+    }
+
+    @ForceInline
+    public static MyValue1 test95_helper2(MyValue1 vt) {
+        return test95_helper1(vt);
+    }
+
+    @ForceInline
+    public static MyValue1 test95_helper3(Object vt) {
+        return test95_helper2((MyValue1)vt);
+    }
+
+    // Same as test94 but with static methods to trigger simple adapter logic
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
+        counts = {ALLOC_G, " = 2"}) // 1 MyValue2 allocation + 1 Integer allocation
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+        counts = {ALLOC_G, " = 3"}) // 1 MyValue1 allocation + 1 MyValue2 allocation + 1 Integer allocation
+    public static MyValue1 test95(MyValue1 vt) {
+        MyValue1 res = test95_helper1(vt);
+        vt = MyValue1.createWithFieldsInline(rI, rL);
+        test95_helper1(vt);
+        test95_helper2(vt);
+        test95_helper3(vt);
+        return res;
+    }
+
+    @Run(test = "test95")
+    public void test95_verifier() {
+        Asserts.assertEQ(test95(testValue1), testValue1);
+        Asserts.assertEQ(test95(null), null);
+    }
+
+    @DontInline
+    public MyValue2 test96_helper1(boolean b) {
+        return b ? null : MyValue2.createWithFieldsInline(rI, rD);
+    }
+
+    @ForceInline
+    public MyValue2 test96_helper2() {
+        return null;
+    }
+
+    @ForceInline
+    public MyValue2 test96_helper3(boolean b) {
+        return b ? null : MyValue2.createWithFieldsInline(rI, rD);
+    }
+
+    // Test that calling convention optimization prevents buffering of return values
+    @Test
+    @IR(applyIf = {"InlineTypeReturnedAsFields", "true"},
+        failOn = {ALLOC_G})
+    @IR(applyIf = {"InlineTypeReturnedAsFields", "false"},
+        counts = {ALLOC_G, " = 1"})
+    public MyValue2 test96(int c, boolean b) {
+        MyValue2 res = null;
+        if (c == 1) {
+            res = test96_helper1(b);
+        } else if (c == 2) {
+            res = test96_helper2();
+        } else if (c == 3) {
+            res = test96_helper3(b);
+        }
+        return res;
+    }
+
+    @Run(test = "test96")
+    public void test96_verifier() {
+        Asserts.assertEQ(test96(0, false), null);
+        Asserts.assertEQ(test96(1, false).hash(), MyValue2.createWithFieldsInline(rI, rD).hash());
+        Asserts.assertEQ(test96(1, true), null);
+        Asserts.assertEQ(test96(2, false), null);
+        Asserts.assertEQ(test96(3, false).hash(), MyValue2.createWithFieldsInline(rI, rD).hash());
+        Asserts.assertEQ(test96(3, true), null);
+    }
+
+    @DontInline
+    public MyValue3 test97_helper1(boolean b) {
+        return b ? null: MyValue3.create();
+    }
+
+    @ForceInline
+    public MyValue3 test97_helper2() {
+        return null;
+    }
+
+    @ForceInline
+    public MyValue3 test97_helper3(boolean b) {
+        return b ? null: MyValue3.create();
+    }
+
+    @NullRestricted
+    MyValue3 test97_res1;
+    @NullRestricted
+    MyValue3 test97_res3;
+
+    // Same as test96 but with MyValue3 return
+    @Test
+    @IR(applyIf = {"InlineTypeReturnedAsFields", "true"},
+        counts = {ALLOC_G, " = 1"}) // 1 Object allocation
+    @IR(applyIf = {"InlineTypeReturnedAsFields", "false"},
+        counts = {ALLOC_G, " = 2"}) // 1 MyValue3 allocation + 1 Object allocation
+    public MyValue3 test97(int c, boolean b) {
+        MyValue3 res = null;
+        if (c == 1) {
+            res = test97_helper1(b);
+            if (res != null) {
+                test97_res1 = res;
+            }
+        } else if (c == 2) {
+            res = test97_helper2();
+        } else if (c == 3) {
+            res = test97_helper3(b);
+            if (res != null) {
+                test97_res3 = res;
+            }
+        }
+        return res;
+    }
+
+    @Run(test = "test97")
+    public void test97_verifier() {
+        Asserts.assertEQ(test97(0, false), null);
+        Asserts.assertEQ(test97(1, false), test97_res1);
+        Asserts.assertEQ(test97(1, true), null);
+        Asserts.assertEQ(test97(2, false), null);
+        Asserts.assertEQ(test97(3, false), test97_res3);
+        Asserts.assertEQ(test97(3, true), null);
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class CircularValue1 {
+        CircularValue1 val;
+        int x;
+
+        @ForceInline
+        public CircularValue1(CircularValue1 val) {
+            this.val = val;
+            this.x = rI;
+        }
+    }
+
+    // Test scalarization of value class with circularity in fields
+    @Test
+    public CircularValue1 test98(CircularValue1 val) {
+        return new CircularValue1(val);
+    }
+
+    @Run(test = "test98")
+    public void test98_verifier()  {
+        CircularValue1 val = new CircularValue1(null);
+        CircularValue1 res = test98(val);
+        Asserts.assertEQ(res.x, rI);
+        Asserts.assertEQ(res.val, val);
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class CircularValue2 {
+        @NullRestricted
+        CircularValue1 val;
+
+        @ForceInline
+        public CircularValue2(CircularValue1 val) {
+            this.val = val;
+        }
+    }
+
+    // Same as test98 but with circularity in class of flattened field
+    @Test
+    public CircularValue2 test99(CircularValue2 val) {
+        return new CircularValue2(val.val);
+    }
+
+    @Run(test = "test99")
+    public void test99_verifier()  {
+        CircularValue1 val1 = new CircularValue1(null);
+        CircularValue2 val2 = new CircularValue2(val1);
+        CircularValue2 res = test99(val2);
+        Asserts.assertEQ(res.val, val1);
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class CircularValue3 {
+        CircularValue4 val;
+        int x;
+
+        @ForceInline
+        public CircularValue3(CircularValue4 val, int x) {
+            this.val = val;
+            this.x = x;
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class CircularValue4 {
+        @NullRestricted
+        CircularValue3 val;
+
+        @ForceInline
+        public CircularValue4(CircularValue3 val) {
+            this.val = val;
+        }
+    }
+
+    // Same as test94 but with "indirect" circularity through field of flattened field
+    @Test
+    public CircularValue4 test100(CircularValue4 val) {
+        return new CircularValue4(new CircularValue3(val, rI));
+    }
+
+    @Run(test = "test100")
+    public void test100_verifier()  {
+        CircularValue3 val3 = new CircularValue3(null, 42);
+        CircularValue4 val4 = new CircularValue4(val3);
+        CircularValue4 res = test100(val4);
+        Asserts.assertEQ(res.val, new CircularValue3(val4, rI));
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class CircularValue5 {
+        @NullRestricted
+        CircularValue6 val;
+        int x;
+
+        @ForceInline
+        public CircularValue5(CircularValue6 val, int x) {
+            this.val = val;
+            this.x = x;
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class CircularValue6 {
+        CircularValue5 val;
+
+        @ForceInline
+        public CircularValue6(CircularValue5 val) {
+            this.val = val;
+        }
+    }
+
+    // Same as test100 but with different combination of field types
+    @Test
+    public CircularValue6 test101(CircularValue6 val) {
+        return new CircularValue6(new CircularValue5(val, rI));
+    }
+
+    @Run(test = "test101")
+    public void test101_verifier()  {
+        CircularValue5 val5 = new CircularValue5(new CircularValue6(null), 42);
+        CircularValue6 val6 = new CircularValue6(val5);
+        CircularValue6 res = test101(val6);
+        Asserts.assertEQ(res.val, new CircularValue5(val6, rI));
+    }
+
+    // Test merging of fields with different scalarization depth
+    @Test
+    public CircularValue1 test102(boolean b) {
+        CircularValue1 val = new CircularValue1(new CircularValue1(null));
+        if (b) {
+            val = null;
+        }
+        return val;
+    }
+
+    @Run(test = "test102")
+    public void test102_verifier() {
+        Asserts.assertEQ(test102(false), new CircularValue1(new CircularValue1(null)));
+        Asserts.assertEQ(test102(true), null);
+    }
+
+    // Might be incrementally inlined
+    public static Object hide(Object obj) {
+        return (MyValue1)obj;
+    }
+
+    // Test that the ConstraintCastNode::Ideal transformation propagates null-free information
+    @Test
+    public MyValue1 test103() {
+        Object obj = hide(null);
+        return (MyValue1)obj;
+    }
+
+    @Run(test = "test103")
+    public void test103_verifier() {
+        Asserts.assertEQ(test103(), null);
+    }
+
+    // Test null restricted fields
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue104 {
+        @NullRestricted
+        static MyValue105 field1;
+
+        @NullRestricted
+        MyValue105 field2;
+
+        @NullRestricted
+        static MyValueEmpty field3;
+
+        @NullRestricted
+        MyValueEmpty field4;
+
+        @ForceInline
+        public MyValue104() {
+            this.field1 = new MyValue105();
+            this.field2 = new MyValue105();
+            this.field3 = new MyValueEmpty();
+            this.field4 = new MyValueEmpty();
+        }
+
+        @ForceInline
+        public MyValue104(MyValue105 val1, MyValue105 val2, MyValueEmpty val3, MyValueEmpty val4) {
+            this.field1 = val1;
+            this.field2 = val2;
+            this.field3 = val3;
+            this.field4 = val4;
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue105 {
+        int x = 42;
+    }
+
+    @NullRestricted
+    static MyValue104 field1;
+
+    @NullRestricted
+    MyValue104 field2;
+
+    @NullRestricted
+    static MyValueEmpty field3;
+
+    @NullRestricted
+    MyValueEmpty field4;
+
+    @Test
+    void test105(MyValue104 arg) {
+        field1 = arg;
+    }
+
+    @Run(test = "test105")
+    public void test105_verifier() {
+        try {
+            test105(null);
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    void test106() {
+        field1 = null;
+    }
+
+    @Run(test = "test106")
+    public void test106_verifier() {
+        try {
+            test106();
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    void test107(MyValue104 arg) {
+        field2 = arg;
+    }
+
+    @Run(test = "test107")
+    public void test107_verifier() {
+        try {
+            test107(null);
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    void test108(TestNullableInlineTypes t, MyValue104 arg) {
+        t.field2 = arg;
+    }
+
+    @Run(test = "test108")
+    public void test108_verifier() {
+        try {
+            test108(null, new MyValue104());
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    void test109(MyValue104 arg) {
+        TestNullableInlineTypes t = null;
+        t.field2 = null;
+    }
+
+    @Run(test = "test109")
+    public void test109_verifier() {
+        try {
+            test109(new MyValue104());
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    void test110() {
+        field2 = null;
+    }
+
+    @Run(test = "test110")
+    public void test110_verifier() {
+        try {
+            test110();
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    void test111(MyValueEmpty arg) {
+        field3 = arg;
+    }
+
+    @Run(test = "test111")
+    public void test111_verifier() {
+        try {
+            test111(null);
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    void test112() {
+        field3 = null;
+    }
+
+    @Run(test = "test112")
+    public void test112_verifier() {
+        try {
+            test112();
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    void test113(MyValueEmpty arg) {
+        field4 = arg;
+    }
+
+    @Run(test = "test113")
+    public void test113_verifier() {
+        try {
+            test113(null);
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    void test114(TestNullableInlineTypes t, MyValueEmpty arg) {
+        t.field4 = arg;
+    }
+
+    @Run(test = "test114")
+    public void test114_verifier() {
+        try {
+            test114(null, new MyValueEmpty());
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    void test115(MyValueEmpty arg) {
+        TestNullableInlineTypes t = null;
+        t.field4 = arg;
+    }
+
+    @Run(test = "test115")
+    public void test115_verifier() {
+        try {
+            test115(new MyValueEmpty());
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    void test116() {
+        field4 = null;
+    }
+
+    @Run(test = "test116")
+    public void test116_verifier() {
+        try {
+            test116();
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    MyValue104 test117(MyValue105 val1, MyValue105 val2, MyValueEmpty val3, MyValueEmpty val4) {
+        return new MyValue104(val1, val2, val3, val4);
+    }
+
+    @Run(test = "test117")
+    public void test117_verifier() {
+        try {
+            test117(null, new MyValue105(), new MyValueEmpty(), new MyValueEmpty());
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    MyValue104 test118(MyValue105 val1, MyValue105 val2, MyValueEmpty val3, MyValueEmpty val4) {
+        return new MyValue104(val1, val2, val3, val4);
+    }
+
+    @Run(test = "test118")
+    public void test118_verifier() {
+        try {
+            test118(new MyValue105(), null, new MyValueEmpty(), new MyValueEmpty());
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    MyValue104 test119(MyValue105 val1, MyValue105 val2, MyValueEmpty val3, MyValueEmpty val4) {
+        return new MyValue104(val1, val2, val3, val4);
+    }
+
+    @Run(test = "test119")
+    public void test119_verifier() {
+        try {
+            test119(new MyValue105(), new MyValue105(), null, new MyValueEmpty());
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    MyValue104 test120(MyValue105 val1, MyValue105 val2, MyValueEmpty val3, MyValueEmpty val4) {
+        return new MyValue104(val1, val2, val3, val4);
+    }
+
+    @Run(test = "test120")
+    public void test120_verifier() {
+        try {
+            test120(new MyValue105(), new MyValue105(), new MyValueEmpty(), null);
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    MyValue104 test121(MyValue105 val2, MyValueEmpty val3, MyValueEmpty val4) {
+        return new MyValue104(null, val2, val3, val4);
+    }
+
+    @Run(test = "test121")
+    public void test121_verifier() {
+        try {
+            test121(new MyValue105(), new MyValueEmpty(), new MyValueEmpty());
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    MyValue104 test122(MyValue105 val1, MyValueEmpty val3, MyValueEmpty val4) {
+        return new MyValue104(val1, null, val3, val4);
+    }
+
+    @Run(test = "test122")
+    public void test122_verifier() {
+        try {
+            test122(new MyValue105(), new MyValueEmpty(), new MyValueEmpty());
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    MyValue104 test123(MyValue105 val1, MyValue105 val2, MyValueEmpty val4) {
+        return new MyValue104(val1, val2, null, val4);
+    }
+
+    @Run(test = "test123")
+    public void test123_verifier() {
+        try {
+            test123(new MyValue105(), new MyValue105(), new MyValueEmpty());
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    MyValue104 test124(MyValue105 val1, MyValue105 val2, MyValueEmpty val3) {
+        return new MyValue104(val1, val2, val3, null);
+    }
+
+    @Run(test = "test124")
+    public void test124_verifier() {
+        try {
+            test124(new MyValue105(), new MyValue105(), new MyValueEmpty());
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOnStackReplacement.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOnStackReplacement.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOnStackReplacement.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2017, 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.*;
+import jdk.test.lib.Asserts;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import static compiler.valhalla.inlinetypes.InlineTypeIRNode.*;
+import static compiler.valhalla.inlinetypes.InlineTypes.rI;
+import static compiler.valhalla.inlinetypes.InlineTypes.rL;
+
+/*
+ * @test
+ * @key randomness
+ * @summary Test on stack replacement (OSR) with value classes.
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm/timeout=300 compiler.valhalla.inlinetypes.TestOnStackReplacement
+ */
+
+public class TestOnStackReplacement {
+
+    public static void main(String[] args) throws Throwable {
+        Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS;
+        scenarios[3].addFlags("-XX:FlatArrayElementMaxSize=0");
+
+        InlineTypes.getFramework()
+                   .addScenarios(scenarios)
+                   .addHelperClasses(MyValue1.class,
+                                     MyValue2.class,
+                                     MyValue2Inline.class,
+                                     MyValue3.class,
+                                     MyValue3Inline.class)
+                   .start();
+    }
+
+    // Helper methods
+
+    protected long hash() {
+        return hash(rI, rL);
+    }
+
+    protected long hash(int x, long y) {
+        return MyValue1.createWithFieldsInline(x, y).hash();
+    }
+
+    // Test OSR compilation
+    @Test(compLevel = CompLevel.WAIT_FOR_COMPILATION)
+    public long test1() {
+        MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
+        MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, Math.abs(rI) % 3);
+        for (int i = 0; i < va.length; ++i) {
+            va[i] = MyValue1.createWithFieldsInline(rI, rL);
+        }
+        long result = 0;
+        // Long loop to trigger OSR compilation
+        for (int i = 0; i < 50_000; ++i) {
+            // Reference local value object in interpreter state
+            result = v.hash();
+            for (int j = 0; j < va.length; ++j) {
+                result += va[j].hash();
+            }
+        }
+        return result;
+    }
+
+    @Run(test = "test1")
+    @Warmup(0)
+    public void test1_verifier() {
+        long result = test1();
+        Asserts.assertEQ(result, ((Math.abs(rI) % 3) + 1) * hash());
+    }
+
+    // Test loop peeling
+    @Test(compLevel = CompLevel.WAIT_FOR_COMPILATION)
+    @IR(failOn = {ALLOC, LOAD, STORE})
+    public void test2() {
+        MyValue1 v = MyValue1.createWithFieldsInline(0, 1);
+        // Trigger OSR compilation and loop peeling
+        for (int i = 0; i < 50_000; ++i) {
+            if (v.x != i || v.y != i + 1) {
+                // Uncommon trap
+                throw new RuntimeException("test2 failed");
+            }
+            v = MyValue1.createWithFieldsInline(i + 1, i + 2);
+        }
+    }
+
+    @Run(test = "test2")
+    @Warmup(0)
+    public void test2_verifier() {
+        test2();
+    }
+
+    // TODO: Should be fixed with JDK-8327465.
+    // Test loop peeling and unrolling
+    //@Test(compLevel = CompLevel.WAIT_FOR_COMPILATION)
+    public void test3() {
+        MyValue1 v1 = MyValue1.createWithFieldsInline(0, 0);
+        MyValue1 v2 = MyValue1.createWithFieldsInline(1, 1);
+        // Trigger OSR compilation and loop peeling
+        for (int i = 0; i < 50_000; ++i) {
+            if (v1.x != 2*i || v2.x != i+1 || v2.y != i+1) {
+                // Uncommon trap
+                throw new RuntimeException("test3 failed");
+            }
+            v1 = MyValue1.createWithFieldsInline(2*(i+1), 0);
+            v2 = MyValue1.createWithFieldsInline(i+2, i+2);
+        }
+    }
+
+    //@Run(test = "test3")
+    //@Warmup(0)
+    public void test3_verifier() {
+        test3();
+    }
+
+    // OSR compilation with Object local
+    @DontCompile
+    public Object test4_init() {
+        return MyValue1.createWithFieldsInline(rI, rL);
+    }
+
+    @DontCompile
+    public Object test4_body() {
+        return MyValue1.createWithFieldsInline(rI, rL);
+    }
+
+    @Test(compLevel = CompLevel.WAIT_FOR_COMPILATION)
+    public Object test4() {
+        Object vt = test4_init();
+        for (int i = 0; i < 50_000; i++) {
+            if (i % 2 == 1) {
+                vt = test4_body();
+            }
+        }
+        return vt;
+    }
+
+    @Run(test = "test4")
+    @Warmup(0)
+    public void test4_verifier() {
+        test4();
+    }
+
+    // OSR compilation with null value class local
+
+    MyValue1 nullField;
+
+    @Test(compLevel = CompLevel.WAIT_FOR_COMPILATION)
+    public void test5() {
+        MyValue1 vt = nullField;
+        for (int i = 0; i < 50_000; i++) {
+            if (vt != null) {
+                throw new RuntimeException("test5 failed: vt should be null");
+            }
+        }
+    }
+
+    @Run(test = "test5")
+    @Warmup(0)
+    public void test5_verifier() {
+        test5();
+    }
+
+    // Test OSR in method with value class receiver
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    value class Test6Value {
+        public int f = 0;
+
+        public int test() {
+            int res = 0;
+            for (int i = 1; i < 20_000; ++i) {
+                res -= i;
+            }
+            return res;
+        }
+    }
+
+    @Test(compLevel = CompLevel.WAIT_FOR_COMPILATION)
+    public void test6() {
+        Test6Value tmp = new Test6Value();
+        for (int i = 0; i < 100; ++i) {
+            tmp.test();
+        }
+    }
+
+    @Run(test = "test6")
+    @Warmup(0)
+    public void test6_verifier() {
+        test6();
+    }
+
+    // Similar to test6 but with more fields and reserved stack entry
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test7Value1 {
+        public int i1 = rI;
+        public int i2 = rI;
+        public int i3 = rI;
+        public int i4 = rI;
+        public int i5 = rI;
+        public int i6 = rI;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test7Value2 {
+        public int i1 = rI;
+        public int i2 = rI;
+        public int i3 = rI;
+        public int i4 = rI;
+        public int i5 = rI;
+        public int i6 = rI;
+        public int i7 = rI;
+        public int i8 = rI;
+        public int i9 = rI;
+        public int i10 = rI;
+        public int i11 = rI;
+        public int i12 = rI;
+        public int i13 = rI;
+        public int i14 = rI;
+        public int i15 = rI;
+        public int i16 = rI;
+        public int i17 = rI;
+        public int i18 = rI;
+        public int i19 = rI;
+        public int i20 = rI;
+        public int i21 = rI;
+
+        @NullRestricted
+        public Test7Value1 vt = new Test7Value1();
+
+        public int test(String[] args) {
+            int res = 0;
+            for (int i = 1; i < 20_000; ++i) {
+                res -= i;
+            }
+            return res;
+        }
+    }
+
+    @Test(compLevel = CompLevel.WAIT_FOR_COMPILATION)
+    public void test7() {
+        Test7Value2 tmp = new Test7Value2();
+        for (int i = 0; i < 10; ++i) {
+            tmp.test(null);
+        }
+    }
+
+    @Run(test = "test7")
+    @Warmup(0)
+    public void test7_verifier() {
+        test7();
+    }
+
+    // Test OSR with scalarized value class return
+    @NullRestricted
+    MyValue3 test8_vt;
+
+    @DontInline
+    public MyValue3 test8_callee(int len) {
+        test8_vt = MyValue3.create();
+        int val = 0;
+        for (int i = 0; i < len; ++i) {
+            val = i;
+        }
+        test8_vt = test8_vt.setI(test8_vt, val);
+        return test8_vt;
+    }
+
+    @Test
+    public int test8(int start) {
+        MyValue3 vt = test8_callee(start);
+        test8_vt.verify(vt);
+        int result = 0;
+        for (int i = 0; i < 50_000; ++i) {
+            result += i;
+        }
+        return result;
+    }
+
+    @Run(test = "test8")
+    @Warmup(2)
+    public void test8_verifier() {
+        test8(1);
+        test8(50_000);
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOptimizeKlassCmp.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOptimizeKlassCmp.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOptimizeKlassCmp.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ *
+ * 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 8209687
+ * @summary Verify that Parse::optimize_cmp_with_klass() works with value classes.
+ * @library /test/lib
+ * @enablePreview
+ * @run main/othervm -Xbatch compiler.valhalla.inlinetypes.TestOptimizeKlassCmp
+ */
+
+package compiler.valhalla.inlinetypes;
+
+import jdk.test.lib.Asserts;
+
+value class MyValue {
+    public int x;
+
+    public MyValue(int x) {
+        this.x = x;
+    }
+}
+
+public class TestOptimizeKlassCmp {
+
+    public static boolean test1(MyValue v1, MyValue v2) {
+        return v1.equals(v2);
+    }
+
+    public static boolean test2(MyValue v1, MyValue v2) {
+        return v1.getClass().equals(v2.getClass());
+    }
+
+    public static boolean test3(Object o1, Object o2) {
+        return o1.getClass().equals(o2.getClass());
+    }
+
+    public static void main(String[] args) {
+        MyValue v1 = new MyValue(0);
+        MyValue v2 = new MyValue(1);
+        for (int i = 0; i < 10_000; ++i) {
+            Asserts.assertFalse(test1(v1, v2));
+            Asserts.assertTrue(test1(v1, v1));
+            Asserts.assertTrue(test2(v1, v2));
+            Asserts.assertTrue(test2(v1, v1));
+            Asserts.assertTrue(test3(v1, v2));
+            Asserts.assertTrue(test3(v1, v1));
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestSafepointAtPollReturn.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestSafepointAtPollReturn.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestSafepointAtPollReturn.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2022, 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 that oop fields of value classes are preserved over safepoints at returns.
+ * @enablePreview
+ * @run main/othervm -XX:CompileCommand=dontinline,TestSafepointAtPollReturn::test* -XX:+UnlockDiagnosticVMOptions
+ *                   -XX:+SafepointALot -XX:-TieredCompilation TestSafepointAtPollReturn
+ */
+
+public class TestSafepointAtPollReturn {
+    static Integer INT_VAL = 0;
+
+    static value class MyValue {
+        Integer val = INT_VAL;
+    }
+
+    static public MyValue testValueCallee(boolean b) {
+        return b ? null : new MyValue();
+    }
+
+    static public MyValue testValue(boolean b) {
+        return testValueCallee(b);
+    }
+
+    public static void main(String[] args) {
+        for (int i = 0; i < 1_000_000_000; ++i) {
+            INT_VAL = i;
+            boolean b = (i % 2) == 0;
+            MyValue val = testValue(b);
+            if (b) {
+                if (val != null) {
+                    throw new RuntimeException("testValue failed: result should be null");
+                }
+            } else {
+                int res = val.val;
+                if (res != i) {
+                    throw new RuntimeException("testValue failed: " + res + " != " + i);
+                }
+            }
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestStressReturnBuffering.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestStressReturnBuffering.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestStressReturnBuffering.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ *
+ * 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 8233415
+ * @summary Verify that TLAB allocated buffer initialization when returning a value object works properly with oops.
+ * @library /test/lib
+ * @enablePreview
+ * @run main/othervm -XX:CompileCommand=exclude,compiler.valhalla.inlinetypes.TestStressReturnBuffering::caller -Xmx4m
+ *                   compiler.valhalla.inlinetypes.TestStressReturnBuffering
+ */
+
+package compiler.valhalla.inlinetypes;
+
+import jdk.test.lib.Asserts;
+
+value class MyValue {
+    public Integer o1;
+    public Integer o2;
+    public Integer o3;
+    public Integer o4;
+    public Integer o5;
+
+    public MyValue(Integer o) {
+        this.o1 = o;
+        this.o2 = o;
+        this.o3 = o;
+        this.o4 = o;
+        this.o5 = o;
+    }
+}
+
+public class TestStressReturnBuffering {
+
+    static Integer integer = 42;
+
+    public static MyValue callee() {
+        return new MyValue(integer);
+    }
+
+    public static int caller() {
+        int res = 0;
+        for (int i = 0; i < 100_000; ++i) {
+            MyValue vt = callee();
+            res += vt.o1 + vt.o2 + vt.o3 + vt.o4 + vt.o5;
+        }
+        return res;
+    }
+
+    public static void main(String[] args) {
+        System.gc();
+        int res = caller();
+        Asserts.assertEQ(res, 100_000*5*42, "Unexpected result");
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestTrivialMethods.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestTrivialMethods.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestTrivialMethods.java
@@ -0,0 +1,192 @@
+/*
+ * 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.
+ *
+ * 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 8275825 8289686
+ * @summary Verify that trivial accessor methods operating on an inline type
+ *          field are C2 compiled to enable scalarization of the arg/return value.
+ * @requires vm.compiler2.enabled
+ * @library /test/lib /compiler/whitebox /
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @build jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:.
+ *                   -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch
+ *                   -XX:+InlineTypePassFieldsAsArgs -XX:+InlineTypeReturnedAsFields
+ *                   -XX:+IgnoreUnrecognizedVMOptions -XX:-StressCallingConvention
+ *                   -XX:CompileCommand=dontinline,*::getter* -XX:CompileCommand=dontinline,*::setter*
+ *                   -XX:CompileCommand=dontinline,*::constantGetter*
+ *                   compiler.valhalla.inlinetypes.TestTrivialMethods
+ */
+
+package compiler.valhalla.inlinetypes;
+
+import compiler.whitebox.CompilerWhiteBoxTest;
+
+import java.lang.reflect.Method;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.Utils;
+
+import jdk.test.whitebox.WhiteBox;
+
+public class TestTrivialMethods {
+    public static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+
+    @NullRestricted
+    static MyValue3 staticField = MyValue3.create();
+    static MyValue3 staticFieldRef = MyValue3.create();
+    @NullRestricted
+    MyValue3 field = MyValue3.create();
+    MyValue3 fieldRef = MyValue3.create();
+    Object objField = null;
+
+    public MyValue3 getter1() {
+        return staticField;
+    }
+
+    public static MyValue3 getter2() {
+        return staticField;
+    }
+
+    public MyValue3 getter3() {
+        return field;
+    }
+
+    public Object getter4(MyValue3 unusedArg) {
+        return objField;
+    }
+
+    public int constantGetter(MyValue3 unusedArg) {
+        return 0;
+    }
+
+    public MyValue3 getter1Ref() {
+        return staticFieldRef;
+    }
+
+    public static MyValue3 getter2Ref() {
+        return staticFieldRef;
+    }
+
+    public MyValue3 getter3Ref() {
+        return fieldRef;
+    }
+
+    public Object getter4Ref(MyValue3 unusedArg) {
+        return objField;
+    }
+
+    public int constantGetterRef(MyValue3 unusedArg) {
+        return 0;
+    }
+
+    public void setter1(MyValue3 val) {
+        staticField = val;
+    }
+
+    public static void setter2(MyValue3 val) {
+        staticField = val;
+    }
+
+    public void setter3(MyValue3 val) {
+        field = val;
+    }
+
+    public void setter1Ref(MyValue3 val) {
+        staticFieldRef = val;
+    }
+
+    public static void setter2Ref(MyValue3 val) {
+        staticFieldRef = val;
+    }
+
+    public void setter3Ref(MyValue3 val) {
+        fieldRef = val;
+    }
+
+    public static void main(String[] args) throws Exception {
+        TestTrivialMethods t = new TestTrivialMethods();
+        // Warmup to trigger compilation
+        for (int i = 0; i < 100_000; ++i) {
+            t.getter1();
+            t.getter2();
+            t.getter3();
+            t.getter4(staticField);
+            t.constantGetter(staticField);
+            t.getter1Ref();
+            t.getter2Ref();
+            t.getter3Ref();
+            t.getter3Ref();
+            t.getter4Ref(null);
+            t.constantGetterRef(null);
+            t.setter1(staticField);
+            t.setter2(staticField);
+            t.setter3(staticField);
+            t.setter1Ref(staticField);
+            t.setter2Ref(staticField);
+            t.setter3Ref(staticField);
+        }
+        Method m = TestTrivialMethods.class.getMethod("getter1");
+        Asserts.assertEQ(WHITE_BOX.getMethodCompilationLevel(m, false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION, "getter1 is not C2 compiled");
+        m = TestTrivialMethods.class.getMethod("getter2");
+        Asserts.assertEQ(WHITE_BOX.getMethodCompilationLevel(m, false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION, "getter2 is not C2 compiled");
+        m = TestTrivialMethods.class.getMethod("getter3");
+        Asserts.assertEQ(WHITE_BOX.getMethodCompilationLevel(m, false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION, "getter3 is not C2 compiled");
+        m = TestTrivialMethods.class.getMethod("setter1", MyValue3.class);
+        m = TestTrivialMethods.class.getMethod("getter4", MyValue3.class);
+        Asserts.assertEQ(WHITE_BOX.getMethodCompilationLevel(m, false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION, "getter4 is not C2 compiled");
+        m = TestTrivialMethods.class.getMethod("constantGetter", MyValue3.class);
+        Asserts.assertEQ(WHITE_BOX.getMethodCompilationLevel(m, false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION, "constantGetter is not C2 compiled");
+        m = TestTrivialMethods.class.getMethod("getter1Ref");
+        Asserts.assertEQ(WHITE_BOX.getMethodCompilationLevel(m, false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION, "getter1Ref is not C2 compiled");
+        m = TestTrivialMethods.class.getMethod("getter2Ref");
+        Asserts.assertEQ(WHITE_BOX.getMethodCompilationLevel(m, false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION, "getter2Ref is not C2 compiled");
+        m = TestTrivialMethods.class.getMethod("getter3Ref");
+        Asserts.assertEQ(WHITE_BOX.getMethodCompilationLevel(m, false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION, "getter3Ref is not C2 compiled");
+        m = TestTrivialMethods.class.getMethod("getter4Ref", MyValue3.class);
+        Asserts.assertEQ(WHITE_BOX.getMethodCompilationLevel(m, false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION, "getter4Ref is not C2 compiled");
+        m = TestTrivialMethods.class.getMethod("constantGetterRef", MyValue3.class);
+        Asserts.assertEQ(WHITE_BOX.getMethodCompilationLevel(m, false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION, "constantGetterRef is not C2 compiled");
+        m = TestTrivialMethods.class.getMethod("setter1", MyValue3.class);
+        Asserts.assertEQ(WHITE_BOX.getMethodCompilationLevel(m, false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION, "setter1 is not C2 compiled");
+        m = TestTrivialMethods.class.getMethod("setter2", MyValue3.class);
+        Asserts.assertEQ(WHITE_BOX.getMethodCompilationLevel(m, false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION, "setter2 is not C2 compiled");
+        m = TestTrivialMethods.class.getMethod("setter3", MyValue3.class);
+        Asserts.assertEQ(WHITE_BOX.getMethodCompilationLevel(m, false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION, "setter3 is not C2 compiled");
+        m = TestTrivialMethods.class.getMethod("setter1Ref", MyValue3.class);
+        Asserts.assertEQ(WHITE_BOX.getMethodCompilationLevel(m, false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION, "setter1Ref is not C2 compiled");
+        m = TestTrivialMethods.class.getMethod("setter2Ref", MyValue3.class);
+        Asserts.assertEQ(WHITE_BOX.getMethodCompilationLevel(m, false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION, "setter2Ref is not C2 compiled");
+        m = TestTrivialMethods.class.getMethod("setter3Ref", MyValue3.class);
+        Asserts.assertEQ(WHITE_BOX.getMethodCompilationLevel(m, false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION, "setter3Ref is not C2 compiled");
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestUnexpectedMemBar.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestUnexpectedMemBar.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestUnexpectedMemBar.java
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ *
+ * 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 8270995
+ * @summary Membars of non-escaping value class buffer allocations should be removed.
+ * @library /test/lib /
+ * @enablePreview
+ * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
+ *                   -XX:-TieredCompilation -XX:-ReduceInitialCardMarks
+ *                   -XX:+AlwaysIncrementalInline -Xbatch -XX:CompileCommand=compileonly,*TestUnexpectedMemBar::test*
+ *                   -XX:+StressIGVN -XX:+StressGCM -XX:+StressLCM -XX:StressSeed=851121348
+ *                   compiler.valhalla.inlinetypes.TestUnexpectedMemBar
+ * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
+ *                   -XX:-TieredCompilation -XX:-ReduceInitialCardMarks -XX:+AlwaysIncrementalInline
+ *                   -Xbatch -XX:CompileCommand=compileonly,*TestUnexpectedMemBar::test*
+ *                   -XX:+StressIGVN -XX:+StressGCM -XX:+StressLCM
+ *                   compiler.valhalla.inlinetypes.TestUnexpectedMemBar
+ * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
+ *                   -Xbatch -XX:CompileCommand=compileonly,*TestUnexpectedMemBar::test*
+ *                   -XX:+StressIGVN -XX:+StressGCM -XX:+StressLCM
+ *                   compiler.valhalla.inlinetypes.TestUnexpectedMemBar
+ */
+
+package compiler.valhalla.inlinetypes;
+
+import jdk.test.lib.Asserts;
+
+value class MyValue1 {
+    int a = 0;
+    int b = 0;
+    int c = 0;
+    int d = 0;
+    int e = 0;
+
+    Integer i;
+    int[] array;
+
+    public MyValue1(Integer i, int[] array) {
+        this.i = i;
+        this.array = array;
+    }
+}
+
+value class MyValue2 {
+    int a = 0;
+    int b = 0;
+    int c = 0;
+    int d = 0;
+    int e = 0;
+
+    NonValueClass obj;
+    int[] array;
+
+    public MyValue2(NonValueClass obj, int[] array) {
+        this.obj = obj;
+        this.array = array;
+    }
+}
+
+public class TestUnexpectedMemBar {
+
+    public static int test1(Integer i) {
+        int[] array = new int[1];
+        MyValue1 vt = new MyValue1(i, array);
+        vt = new MyValue1(vt.i, vt.array);
+        return vt.i + vt.array[0];
+    }
+
+    public static int test2(Integer i) {
+        int[] array = {i};
+        MyValue1 vt = new MyValue1(i, array);
+        vt = new MyValue1(vt.i, vt.array);
+        return vt.i + vt.array[0];
+    }
+
+    public static int test3(NonValueClass obj) {
+        int[] array = new int[1];
+        MyValue2 vt = new MyValue2(obj, array);
+        vt = new MyValue2(vt.obj, vt.array);
+        return vt.obj.x + vt.array[0];
+    }
+
+    public static int test4(NonValueClass obj) {
+        int[] array = {obj.x};
+        MyValue2 vt = new MyValue2(obj, array);
+        vt = new MyValue2(vt.obj, vt.array);
+        return vt.obj.x + vt.array[0];
+    }
+
+    public static void main(String[] args) {
+        for (int i = 0; i < 100_000; ++i) {
+            int res = test1(i);
+            Asserts.assertEquals(res, i, "test1 failed");
+            res = test2(i);
+            Asserts.assertEquals(res, 2*i, "test2 failed");
+            res = test3(new NonValueClass(i));
+            Asserts.assertEquals(res, i, "test3 failed");
+            res = test4(new NonValueClass(i));
+            Asserts.assertEquals(res, 2*i, "test4 failed");
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestUninitializedValueClass.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestUninitializedValueClass.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestUninitializedValueClass.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2022, 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 compiler.valhalla.inlinetypes;
+
+/**
+ * @test
+ * @bug 8282569
+ * @summary Test that uninitialized default value class is properly handled by C2.
+ * @enablePreview
+ * @run main/othervm -XX:CompileCommand=compileonly,*::<init> -Xcomp -XX:-TieredCompilation
+ *                   compiler.valhalla.inlinetypes.TestUninitializedValueClass
+ */
+
+value class MyValue {
+    static final MyValue EMPTY = new MyValue();
+    int value = 0;
+}
+
+public class TestUninitializedValueClass {
+
+    public static void main(String[] args) {
+        MyValue unused = new MyValue();
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestUnloadedInlineTypeArray.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestUnloadedInlineTypeArray.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestUnloadedInlineTypeArray.java
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2023, Red Hat, 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
+ * 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 8182997 8214898
+ * @library /test/lib
+ * @summary Test the handling of arrays of unloaded value classes.
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm -Xcomp
+ *                   -XX:CompileCommand=compileonly,TestUnloadedInlineTypeArray::test*
+ *                   TestUnloadedInlineTypeArray
+ * @run main/othervm -Xcomp -XX:FlatArrayElementMaxSize=0
+ *                   -XX:CompileCommand=compileonly,TestUnloadedInlineTypeArray::test*
+ *                   TestUnloadedInlineTypeArray
+ * @run main/othervm -Xcomp
+ *                   TestUnloadedInlineTypeArray
+ * @run main/othervm -Xcomp -XX:FlatArrayElementMaxSize=0
+ *                   TestUnloadedInlineTypeArray
+ * @run main/othervm -Xcomp -XX:-TieredCompilation
+ *                   -XX:CompileCommand=compileonly,TestUnloadedInlineTypeArray::test*
+ *                   TestUnloadedInlineTypeArray
+ * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:FlatArrayElementMaxSize=0
+ *                   -XX:CompileCommand=compileonly,TestUnloadedInlineTypeArray::test*
+ *                   TestUnloadedInlineTypeArray
+ * @run main/othervm -Xcomp -XX:-TieredCompilation
+ *                   TestUnloadedInlineTypeArray
+ * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:FlatArrayElementMaxSize=0
+ *                   TestUnloadedInlineTypeArray
+ */
+
+import jdk.test.lib.Asserts;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue1 {
+    int foo;
+
+    private MyValue1() {
+        foo = 0x42;
+    }
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue2 {
+    int foo;
+
+    public MyValue2(int n) {
+        foo = n;
+    }
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue3 {
+    int foo;
+
+    public MyValue3(int n) {
+        foo = n;
+    }
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue4 {
+    int foo;
+
+    public MyValue4(int n) {
+        foo = n;
+    }
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue5 {
+    int foo;
+
+    public MyValue5(int n) {
+        foo = n;
+    }
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue6 {
+    int foo;
+
+    public MyValue6(int n) {
+        foo = n;
+    }
+
+    public MyValue6(MyValue6 v, MyValue6[] dummy) {
+        foo = v.foo + 1;
+    }
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue7 {
+    int foo;
+
+    public MyValue7(int n) {
+        foo = n;
+    }
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue8 {
+    int foo = 123;
+    static {
+        TestUnloadedInlineTypeArray.MyValue8_inited = true;
+    }
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue9 {
+    int foo = 123;
+    static {
+        TestUnloadedInlineTypeArray.MyValue9_inited = true;
+    }
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue10 {
+    int foo = 42;
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValue11 {
+    int foo = 42;
+}
+
+public class TestUnloadedInlineTypeArray {
+    static boolean MyValue8_inited = false;
+    static boolean MyValue9_inited = false;
+
+    static MyValue1[] target1() {
+        return (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, 10);
+    }
+
+    static void test1() {
+        target1();
+    }
+
+    static MyValue1[] target1Nullable() {
+        return new MyValue1[10];
+    }
+
+    static void test1Nullable() {
+        target1Nullable();
+    }
+
+    static int test2(MyValue2[] arr) {
+        if (arr != null) {
+            return arr[1].foo;
+        } else {
+            return 1234;
+        }
+    }
+
+    static void verifyTest2() {
+        int n = 50000;
+
+        int m = 9999;
+        for (int i = 0; i < n; i++) {
+            m = test2(null);
+        }
+        Asserts.assertEQ(m, 1234);
+
+        MyValue2[] arr = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 2);
+        arr[1] = new MyValue2(5678);
+        m = 9999;
+        for (int i = 0; i < n; i++) {
+            m = test2(arr);
+        }
+        Asserts.assertEQ(m, 5678);
+    }
+
+    static int test2Nullable(MyValue2[] arr) {
+        if (arr != null) {
+            return arr[1].foo;
+        } else {
+            return 1234;
+        }
+    }
+
+    static void verifyTest2Nullable() {
+        int n = 50000;
+
+        int m = 9999;
+        for (int i = 0; i < n; i++) {
+            m = test2Nullable(null);
+        }
+        Asserts.assertEQ(m, 1234);
+
+        MyValue2[] arr = new MyValue2[2];
+        arr[1] = new MyValue2(5678);
+        m = 9999;
+        for (int i = 0; i < n; i++) {
+            m = test2Nullable(arr);
+        }
+        Asserts.assertEQ(m, 5678);
+    }
+
+    static void test3(MyValue3[] arr) {
+        if (arr != null) {
+            arr[1] = new MyValue3(2345);
+        }
+    }
+
+    static void verifyTest3() {
+        int n = 50000;
+
+        for (int i = 0; i < n; i++) {
+            test3(null);
+        }
+
+        MyValue3[] arr = (MyValue3[])ValueClass.newNullRestrictedArray(MyValue3.class, 2);
+        for (int i = 0; i < n; i++) {
+            test3(arr);
+        }
+        Asserts.assertEQ(arr[1].foo, 2345);
+    }
+
+    static void test3Nullable(MyValue3[] arr) {
+        if (arr != null) {
+            arr[0] = null;
+            arr[1] = new MyValue3(2345);
+        }
+    }
+
+    static void verifyTest3Nullable() {
+        int n = 50000;
+
+        for (int i = 0; i < n; i++) {
+            test3Nullable(null);
+        }
+
+        MyValue3[] arr = new MyValue3[2];
+        for (int i = 0; i < n; i++) {
+            test3Nullable(arr);
+        }
+        Asserts.assertEQ(arr[0], null);
+        Asserts.assertEQ(arr[1].foo, 2345);
+    }
+
+    static MyValue4[] test4(boolean b) {
+        // range check elimination
+        if (b) {
+            MyValue4[] arr = (MyValue4[])ValueClass.newNullRestrictedArray(MyValue4.class, 10);
+            arr[1] = new MyValue4(2345);
+            return arr;
+        } else {
+            return null;
+        }
+    }
+
+    static void verifyTest4() {
+        int n = 50000;
+
+        for (int i = 0; i < n; i++) {
+            test4(false);
+        }
+
+        MyValue4[] arr = null;
+        for (int i = 0; i < n; i++) {
+            arr = test4(true);
+        }
+        Asserts.assertEQ(arr[1].foo, 2345);
+    }
+
+    static MyValue4[] test4Nullable(boolean b) {
+        // range check elimination
+        if (b) {
+            MyValue4[] arr = new MyValue4[10];
+            arr[0] = null;
+            arr[1] = new MyValue4(2345);
+            return arr;
+        } else {
+            return null;
+        }
+    }
+
+    static void verifyTest4Nullable() {
+        int n = 50000;
+
+        for (int i = 0; i < n; i++) {
+            test4Nullable(false);
+        }
+
+        MyValue4[] arr = null;
+        for (int i = 0; i < n; i++) {
+            arr = test4Nullable(true);
+        }
+        Asserts.assertEQ(arr[0], null);
+        Asserts.assertEQ(arr[1].foo, 2345);
+        arr[3] = null;
+    }
+
+    static Object[] test5(int n) {
+        if (n == 0) {
+            return null;
+        } else if (n == 1) {
+            MyValue5[] arr = (MyValue5[])ValueClass.newNullRestrictedArray(MyValue5.class, 10);
+            arr[1] = new MyValue5(12345);
+            return arr;
+        } else {
+            MyValue5[] arr = new MyValue5[10];
+            arr[1] = new MyValue5(22345);
+            return arr;
+        }
+    }
+
+    static void verifyTest5() {
+        int n = 50000;
+
+        for (int i = 0; i < n; i++) {
+            test5(0);
+        }
+
+        {
+            MyValue5[] arr = null;
+            for (int i = 0; i < n; i++) {
+                arr = (MyValue5[])test5(1);
+            }
+            Asserts.assertEQ(arr[1].foo, 12345);
+        }
+        {
+            MyValue5[] arr = null;
+            for (int i = 0; i < n; i++) {
+                arr = (MyValue5[])test5(2);
+            }
+            Asserts.assertEQ(arr[1].foo, 22345);
+        }
+    }
+
+    static Object test6() {
+        return new MyValue6(new MyValue6(123), null);
+    }
+
+    static void verifyTest6() {
+        Object n = test6();
+        Asserts.assertEQ(n.toString(), "MyValue6@" + Integer.toHexString(n.hashCode()));
+    }
+
+    static int test7(MyValue7[][] arr) {
+        if (arr != null) {
+            return arr[0][1].foo;
+        } else {
+            return 1234;
+        }
+    }
+
+    static void verifyTest7() {
+        int n = 50000;
+
+        int m = 9999;
+        for (int i = 0; i < n; i++) {
+            m = test7(null);
+        }
+        Asserts.assertEQ(m, 1234);
+
+        MyValue7[][] arr = { (MyValue7[])ValueClass.newNullRestrictedArray(MyValue7.class, 2),
+                             (MyValue7[])ValueClass.newNullRestrictedArray(MyValue7.class, 2) };
+        Object[] oa = arr[1];
+        Asserts.assertNE(oa[0], null);
+
+        arr[0][1] = new MyValue7(5678);
+        m = 9999;
+        for (int i = 0; i < n; i++) {
+            m = test7(arr);
+        }
+        Asserts.assertEQ(m, 5678);
+    }
+
+    static int test7Nullable(MyValue7[][] arr) {
+        if (arr != null) {
+            arr[0][0] = null;
+            return arr[0][1].foo;
+        } else {
+            return 1234;
+        }
+    }
+
+    static void verifyTest7Nullable() {
+        int n = 50000;
+
+        int m = 9999;
+        for (int i = 0; i < n; i++) {
+            m = test7Nullable(null);
+        }
+        Asserts.assertEQ(m, 1234);
+
+        MyValue7[][] arr = new MyValue7[2][2];
+        Object[] oa = arr[1];
+        Asserts.assertEQ(oa[0], null);
+
+        arr[0][1] = new MyValue7(5678);
+        m = 9999;
+        for (int i = 0; i < n; i++) {
+            m = test7Nullable(arr);
+        }
+        Asserts.assertEQ(m, 5678);
+        Asserts.assertEQ(arr[0][0], null);
+    }
+
+    static void test8() {
+        MyValue8 a[] = new MyValue8[0];
+        Asserts.assertEQ(MyValue8_inited, false);
+
+        MyValue8 b[] = (MyValue8[])ValueClass.newNullRestrictedArray(MyValue8.class, 0);
+        Asserts.assertEQ(MyValue8_inited, true);
+    }
+
+    static void test9() {
+        MyValue9 a[][] = new MyValue9[10][0];
+        Asserts.assertEQ(MyValue9_inited, false);
+
+        a[0] = (MyValue9[])ValueClass.newNullRestrictedArray(MyValue9.class, 0);
+        Asserts.assertEQ(MyValue9_inited, true);
+    }
+
+    static void test10(MyValue10 dummy) {
+        MyValue10[][] a = { (MyValue10[])ValueClass.newNullRestrictedArray(MyValue10.class, 1) };
+        if (a[0][0].equals(null)) throw new RuntimeException("test10 failed");
+        Asserts.assertNE(a[0][0], null);
+    }
+
+    static void test11(MyValue10 dummy) {
+        MyValue11[][] a = new MyValue11[1][1];
+        Asserts.assertEQ(a[0][0], null);
+    }
+
+    static public void main(String[] args) {
+        test1();
+        test1Nullable();
+        verifyTest2();
+        verifyTest2Nullable();
+        verifyTest3();
+        verifyTest3Nullable();
+        verifyTest4();
+        verifyTest4Nullable();
+        verifyTest5();
+        verifyTest6();
+        verifyTest7();
+        verifyTest7Nullable();
+        test8();
+        test9();
+        test10(null);
+        test11(null);
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestUnloadedInlineTypeField.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestUnloadedInlineTypeField.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestUnloadedInlineTypeField.java
@@ -0,0 +1,1086 @@
+/*
+ * 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.
+ *
+ * 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.*;
+import jdk.test.lib.Asserts;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import static compiler.valhalla.inlinetypes.InlineTypes.rI;
+
+/*
+ * @test
+ * @key randomness
+ * @summary Test the handling of fields of unloaded value classes.
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @compile hack/GetUnresolvedInlineFieldWrongSignature.java
+ * @compile TestUnloadedInlineTypeField.java
+ * @run main/othervm/timeout=300 compiler.valhalla.inlinetypes.TestUnloadedInlineTypeField
+ */
+
+public class TestUnloadedInlineTypeField {
+    // Only prevent loading of classes when testing with C1. Load classes
+    // early when executing with C2 to prevent uncommon traps. It's still
+    // beneficial to execute this test with C2 because it also checks handling
+    // of type mismatches.
+
+    public static void main(String[] args) {
+        final Scenario[] scenarios = {
+                new Scenario(0),
+                new Scenario(1, "-XX:InlineFieldMaxFlatSize=0"),
+                new Scenario(2, "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+PatchALot"),
+                new Scenario(3, "-XX:InlineFieldMaxFlatSize=0",
+                                "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+PatchALot")
+        };
+        InlineTypes.getFramework()
+                   .addScenarios(scenarios)
+                   .addFlags("--enable-preview",
+                             // Prevent IR Test Framework from loading classes
+                             "-DIgnoreCompilerControls=true",
+                             // Some tests trigger frequent re-compilation. Don't mark them as non-compilable.
+                             "-XX:PerMethodRecompilationCutoff=-1", "-XX:PerBytecodeRecompilationCutoff=-1")
+                   .start();
+    }
+
+    // Test case 1:
+    // The value class field class has been loaded, but the holder class has not been loaded.
+    //
+    //     aload_0
+    //     getfield  MyValue1Holder.v:LMyValue1;
+    //               ^ not loaded      ^ already loaded
+    //
+    // MyValue1 has already been loaded, because it's in the preload attribute of
+    // TestUnloadedInlineTypeField, due to TestUnloadedInlineTypeField.test1_precondition().
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue1 {
+        int foo;
+
+        MyValue1() {
+            foo = rI;
+        }
+    }
+
+    static class MyValue1Holder {
+        @NullRestricted
+        MyValue1 v;
+
+        public MyValue1Holder() {
+            v = new MyValue1();
+        }
+    }
+
+    static MyValue1 test1_precondition() {
+        return new MyValue1();
+    }
+
+    @Test
+    public int test1(Object holder) {
+        if (holder != null) {
+            // Don't use MyValue1Holder in the signature, it might trigger class loading
+            return ((MyValue1Holder)holder).v.foo;
+        } else {
+            return 0;
+        }
+    }
+
+    @Run(test = "test1")
+    public void test1_verifier(RunInfo info) {
+        if (info.isWarmUp() && !info.isC2CompilationEnabled()) {
+            test1(null);
+        } else {
+            MyValue1Holder holder = new MyValue1Holder();
+            Asserts.assertEQ(test1(holder), rI);
+        }
+    }
+
+    // Test case 2:
+    // Both the value class field class, and the holder class have not been loaded.
+    //
+    //     aload_0
+    //     getfield  MyValue2Holder.v:LMyValue2;
+    //               ^ not loaded     ^ not loaded
+    //
+    // MyValue2 has not been loaded, because it is not explicitly referenced by
+    // TestUnloadedInlineTypeField.
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue2 {
+        int foo;
+
+        public MyValue2(int n) {
+            foo = n;
+        }
+    }
+
+    static class MyValue2Holder {
+        @NullRestricted
+        MyValue2 v;
+
+        public MyValue2Holder() {
+            v = new MyValue2(rI);
+        }
+    }
+
+    @Test
+    public int test2(Object holder) {
+        if (holder != null) {
+            // Don't use MyValue2Holder in the signature, it might trigger class loading
+            return ((MyValue2Holder)holder).v.foo;
+        } else {
+            return 0;
+        }
+    }
+
+    @Run(test = "test2")
+    public void test2_verifier(RunInfo info) {
+        if (info.isWarmUp() && !info.isC2CompilationEnabled()) {
+            test2(null);
+        } else {
+            MyValue2Holder holder = new MyValue2Holder();
+            Asserts.assertEQ(test2(holder), rI);
+        }
+    }
+
+    // Test case 4:
+    // Same as case 1, except we use putfield instead of getfield.
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue4 {
+        int foo;
+
+        MyValue4(int n) {
+            foo = n;
+        }
+    }
+
+    static class MyValue4Holder {
+        @NullRestricted
+        MyValue4 v;
+
+        public MyValue4Holder() {
+            v = new MyValue4(0);
+        }
+    }
+
+    @Test
+    public void test4(Object holder, MyValue4 v) {
+        if (holder != null) {
+            // Don't use MyValue4Holder in the signature, it might trigger class loading
+            ((MyValue4Holder)holder).v = v;
+        }
+    }
+
+    @Run(test = "test4")
+    public void test4_verifier(RunInfo info) {
+        MyValue4 v = new MyValue4(rI);
+        if (info.isWarmUp() && !info.isC2CompilationEnabled()) {
+            test4(null, v);
+        } else {
+            MyValue4Holder holder = new MyValue4Holder();
+            test4(holder, v);
+            Asserts.assertEQ(holder.v.foo, rI);
+        }
+    }
+
+    // Test case 5:
+    // Same as case 2, except we use putfield instead of getfield.
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue5 {
+        int foo;
+
+        MyValue5(int n) {
+            foo = n;
+        }
+    }
+
+    static class MyValue5Holder {
+        @NullRestricted
+        MyValue5 v;
+
+        public MyValue5Holder() {
+            v = new MyValue5(0);
+        }
+
+        public Object make(int n) {
+            return new MyValue5(n);
+        }
+    }
+
+    @Test
+    public void test5(Object holder, Object o) {
+        if (holder != null) {
+            // Don't use MyValue5 and MyValue5Holder in the signature, it might trigger class loading
+            MyValue5 v = (MyValue5)o;
+            ((MyValue5Holder)holder).v = v;
+        }
+    }
+
+    @Run(test = "test5")
+    public void test5_verifier(RunInfo info) {
+        if (info.isWarmUp() && !info.isC2CompilationEnabled()) {
+            test5(null, null);
+        } else {
+            MyValue5Holder holder = new MyValue5Holder();
+            Object v = holder.make(rI);
+            test5(holder, v);
+            Asserts.assertEQ(holder.v.foo, rI);
+        }
+    }
+
+
+    // Test case 6: (same as test1, except we use getstatic instead of getfield)
+    // The value class field class has been loaded, but the holder class has not been loaded.
+    //
+    //     getstatic  MyValue6Holder.v:LMyValue1;
+    //                ^ not loaded       ^ already loaded
+    //
+    // MyValue6 has already been loaded, because it's in the preload attribute of
+    // TestUnloadedInlineTypeField, due to TestUnloadedInlineTypeField.test1_precondition().
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue6 {
+        int foo;
+
+        MyValue6() {
+            foo = rI;
+        }
+    }
+
+    static class MyValue6Holder {
+        @NullRestricted
+        static MyValue6 v = new MyValue6();
+    }
+
+    static MyValue6 test6_precondition() {
+        return new MyValue6();
+    }
+
+    @Test
+    public int test6(int n) {
+        if (n == 0) {
+            return 0;
+        } else {
+            return MyValue6Holder.v.foo + n;
+        }
+    }
+
+    @Run(test = "test6")
+    public void test6_verifier(RunInfo info) {
+        if (info.isWarmUp() && !info.isC2CompilationEnabled()) {
+            test6(0);
+        } else {
+            Asserts.assertEQ(test6(rI), 2*rI);
+        }
+    }
+
+
+    // Test case 7:  (same as test2, except we use getstatic instead of getfield)
+    // Both the value class field class, and the holder class have not been loaded.
+    //
+    //     getstatic  MyValue7Holder.v:LMyValue7;
+    //                ^ not loaded       ^ not loaded
+    //
+    // MyValue7 has not been loaded, because it is not explicitly referenced by
+    // TestUnloadedInlineTypeField.
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue7 {
+        int foo;
+
+        MyValue7(int n) {
+            foo = n;
+        }
+    }
+
+    static class MyValue7Holder {
+        @NullRestricted
+        static MyValue7 v = new MyValue7(rI);
+    }
+
+    @Test
+    public int test7(int n) {
+        if (n == 0) {
+            return 0;
+        } else {
+            return MyValue7Holder.v.foo + n;
+        }
+    }
+
+    @Run(test = "test7")
+    public void test7_verifier(RunInfo info) {
+        if (info.isWarmUp() && !info.isC2CompilationEnabled()) {
+            test7(0);
+        } else {
+            Asserts.assertEQ(test7(rI), 2*rI);
+        }
+    }
+
+    // Test case 8:
+    // Same as case 1, except holder is allocated in test method (-> no holder null check required)
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue8 {
+        int foo;
+
+        MyValue8() {
+            foo = rI;
+        }
+    }
+
+    static class MyValue8Holder {
+        @NullRestricted
+        MyValue8 v;
+
+        public MyValue8Holder() {
+            v = new MyValue8();
+        }
+    }
+
+    static MyValue8 test8_precondition() {
+        return new MyValue8();
+    }
+
+    @Test
+    public int test8(boolean warmup) {
+        if (!warmup) {
+            MyValue8Holder holder = new MyValue8Holder();
+            return holder.v.foo;
+        } else {
+            return 0;
+        }
+    }
+
+    @Run(test = "test8")
+    public void test8_verifier(RunInfo info) {
+        if (info.isWarmUp() && !info.isC2CompilationEnabled()) {
+            test8(true);
+        } else {
+            Asserts.assertEQ(test8(false), rI);
+        }
+    }
+
+    // Test case 9:
+    // Same as case 2, except holder is allocated in test method (-> no holder null check required)
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue9 {
+        int foo;
+
+        public MyValue9(int n) {
+            foo = n;
+        }
+    }
+
+    static class MyValue9Holder {
+        @NullRestricted
+        MyValue9 v;
+
+        public MyValue9Holder() {
+            v = new MyValue9(rI);
+        }
+    }
+
+    @Test
+    public int test9(boolean warmup) {
+        if (!warmup) {
+            MyValue9Holder holder = new MyValue9Holder();
+            return holder.v.foo;
+        } else {
+            return 0;
+        }
+    }
+
+    @Run(test = "test9")
+    public void test9_verifier(RunInfo info) {
+        if (info.isWarmUp() && !info.isC2CompilationEnabled()) {
+            test9(true);
+        } else {
+            Asserts.assertEQ(test9(false), rI);
+        }
+    }
+
+    // Test case 11:
+    // Same as case 4, except holder is allocated in test method (-> no holder null check required)
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue11 {
+        int foo;
+
+        MyValue11(int n) {
+            foo = n;
+        }
+    }
+
+    static class MyValue11Holder {
+        @NullRestricted
+        MyValue11 v;
+
+        public MyValue11Holder() {
+            v = new MyValue11(0);
+        }
+    }
+
+    @Test
+    public Object test11(boolean warmup, MyValue11 v) {
+        if (!warmup) {
+            MyValue11Holder holder = new MyValue11Holder();
+            holder.v = v;
+            return holder;
+        } else {
+            return null;
+        }
+    }
+
+    @Run(test = "test11")
+    public void test11_verifier(RunInfo info) {
+        MyValue11 v = new MyValue11(rI);
+        if (info.isWarmUp() && !info.isC2CompilationEnabled()) {
+            test11(true, v);
+        } else {
+            MyValue11Holder holder = (MyValue11Holder)test11(false, v);
+            Asserts.assertEQ(holder.v.foo, rI);
+        }
+    }
+
+    // Test case 12:
+    // Same as case 5, except holder is allocated in test method (-> no holder null check required)
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue12 {
+        int foo;
+
+        MyValue12(int n) {
+            foo = n;
+        }
+    }
+
+    static class MyValue12Holder {
+        @NullRestricted
+        MyValue12 v;
+
+        public MyValue12Holder() {
+            v = new MyValue12(0);
+        }
+    }
+
+    @Test
+    public Object test12(boolean warmup, Object o) {
+        if (!warmup) {
+            // Don't use MyValue12 in the signature, it might trigger class loading
+            MyValue12Holder holder = new MyValue12Holder();
+            holder.v = (MyValue12)o;
+            return holder;
+        } else {
+            return null;
+        }
+    }
+
+    @Run(test = "test12")
+    public void test12_verifier(RunInfo info) {
+        if (info.isWarmUp() && !info.isC2CompilationEnabled()) {
+            test12(true, null);
+        } else {
+            MyValue12 v = new MyValue12(rI);
+            MyValue12Holder holder = (MyValue12Holder)test12(false, v);
+            Asserts.assertEQ(holder.v.foo, rI);
+        }
+    }
+
+    // Test case 13:
+    // Same as case 10, except MyValue13 is allocated in test method
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue13 {
+        int foo;
+
+        public MyValue13() {
+            foo = rI;
+        }
+    }
+
+    static class MyValue13Holder {
+        @NullRestricted
+        MyValue13 v;
+
+        public MyValue13Holder() {
+            v = new MyValue13();
+        }
+    }
+
+    static MyValue13 test13_precondition() {
+        return new MyValue13();
+    }
+
+    @Test
+    public void test13(Object holder) {
+        // Don't use MyValue13Holder in the signature, it might trigger class loading
+        GetUnresolvedInlineFieldWrongSignature.test13(holder);
+    }
+
+    @Run(test = "test13")
+    public void test13_verifier(RunInfo info) {
+        if (info.isWarmUp() && !info.isC2CompilationEnabled()) {
+            test13(null);
+        } else {
+            // Make sure klass is resolved
+            for (int i = 0; i < 10; ++i) {
+                MyValue13Holder holder = new MyValue13Holder();
+                try {
+                    test13(holder);
+// TODO 8325106
+//                    Asserts.fail("Should have thrown InstantiationError");
+                } catch (InstantiationError e) {
+                    // OK
+                }
+            }
+        }
+    }
+
+    // Test case 15:
+    // Same as case 13, except MyValue15 is unloaded
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue15 {
+        int foo;
+
+        public MyValue15() {
+            foo = rI;
+        }
+    }
+
+    static class MyValue15Holder {
+        @NullRestricted
+        MyValue15 v;
+
+        public MyValue15Holder() {
+            v = new MyValue15();
+        }
+    }
+
+    @Test
+    public void test15(Object holder) {
+        // Don't use MyValue15Holder in the signature, it might trigger class loading
+        GetUnresolvedInlineFieldWrongSignature.test15(holder);
+    }
+
+    @Run(test = "test15")
+    public void test15_verifier(RunInfo info) {
+        if (info.isWarmUp() && !info.isC2CompilationEnabled()) {
+            test15(null);
+        } else {
+            // Make sure klass is resolved
+            for (int i = 0; i < 10; ++i) {
+                MyValue15Holder holder = new MyValue15Holder();
+                try {
+                    test15(holder);
+// TODO 8325106
+//                    Asserts.fail("Should have thrown InstantiationError");
+                } catch (InstantiationError e) {
+                    // OK
+                }
+            }
+        }
+    }
+
+    // Test case 16:
+    // aconst_init with type which is not a value class
+    static class MyValue16 {
+        int foo;
+
+        public MyValue16() {
+            foo = rI;
+        }
+    }
+
+    static MyValue16 test16_precondition() {
+        return new MyValue16();
+    }
+
+    @Test
+    public Object test16(boolean warmup) {
+        return GetUnresolvedInlineFieldWrongSignature.test16(warmup);
+    }
+
+    @Run(test = "test16")
+    public void test16_verifier(RunInfo info) {
+        if (info.isWarmUp() && !info.isC2CompilationEnabled()) {
+            test16(true);
+        } else {
+            // Make sure klass is resolved
+            for (int i = 0; i < 10; ++i) {
+                try {
+                    test16(false);
+// TODO 8325106
+//                    Asserts.fail("Should have thrown IncompatibleClassChangeError");
+                } catch (IncompatibleClassChangeError e) {
+                    // OK
+                }
+            }
+        }
+    }
+
+    // Test case 17:
+    // Same as test16 but with unloaded type at aconst_init
+    static class MyValue17 {
+        int foo;
+
+        public MyValue17() {
+            foo = rI;
+        }
+    }
+
+    @Test
+    public Object test17(boolean warmup) {
+        return GetUnresolvedInlineFieldWrongSignature.test17(warmup);
+    }
+
+    @Run(test = "test17")
+    public void test17_verifier(RunInfo info) {
+        if (info.isWarmUp() && !info.isC2CompilationEnabled()) {
+            test17(true);
+        } else {
+            // Make sure klass is resolved
+            for (int i = 0; i < 10; ++i) {
+                try {
+                    test17(false);
+// TODO 8325106
+//                    Asserts.fail("Should have thrown IncompatibleClassChangeError");
+                } catch (IncompatibleClassChangeError e) {
+                    // OK
+                }
+            }
+        }
+    }
+
+    // Test case 18:
+    // Same as test7 but with the holder being loaded
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue18 {
+        int foo;
+
+        MyValue18(int n) {
+            foo = n;
+        }
+    }
+
+    static class MyValue18Holder {
+        @NullRestricted
+        static MyValue18 v = new MyValue18(rI);
+    }
+
+    @Test
+    public int test18(int n) {
+        if (n == 0) {
+            return 0;
+        } else {
+            return MyValue18Holder.v.foo + n;
+        }
+    }
+
+    @Run(test = "test18")
+    public void test18_verifier(RunInfo info) {
+        // Make sure MyValue18Holder is loaded
+        MyValue18Holder holder = new MyValue18Holder();
+        if (info.isWarmUp() && !info.isC2CompilationEnabled()) {
+            test18(0);
+        } else {
+            Asserts.assertEQ(test18(rI), 2*rI);
+        }
+    }
+
+    // Test case 19:
+    // Same as test18 but uninitialized (null) static value class field
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue19 {
+        int foo;
+
+        MyValue19(int n) {
+            foo = n;
+        }
+    }
+
+    static class MyValue19Holder {
+        @NullRestricted
+        static MyValue19 v;
+    }
+
+    @Test
+    public int test19(int n) {
+        if (n == 0) {
+            return 0;
+        } else {
+            return MyValue19Holder.v.foo + n;
+        }
+    }
+
+    @Run(test = "test19")
+    public void test19_verifier(RunInfo info) {
+        // Make sure MyValue19Holder is loaded
+        MyValue19Holder holder = new MyValue19Holder();
+        if (info.isWarmUp() && !info.isC2CompilationEnabled()) {
+            test19(0);
+        } else {
+            Asserts.assertEQ(test19(rI), rI);
+        }
+    }
+
+    // Test case 20:
+    // Value class with object field of unloaded type.
+    static class MyObject20 {
+        int x = 42;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue20 {
+        MyObject20 obj;
+
+        MyValue20() {
+            this.obj = null;
+        }
+    }
+
+    @Test
+    public MyValue20 test20() {
+        return new MyValue20();
+    }
+
+    @Run(test = "test20")
+    public void test20_verifier() {
+        MyValue20 vt = test20();
+        Asserts.assertEQ(vt.obj, null);
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test21ClassA {
+        @NullRestricted
+        static Test21ClassB b;
+        @NullRestricted
+        static Test21ClassC c;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test21ClassB {
+        static int x = Test21ClassA.c.x;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test21ClassC {
+        int x = 42;
+    }
+
+    // Test access to static value class field with unloaded type
+    @Test
+    public Object test21() {
+        return new Test21ClassA();
+    }
+
+    @Run(test = "test21")
+    public void test21_verifier() {
+        Object ret = test21();
+        Asserts.assertEQ(Test21ClassA.b.x, 42);
+        Asserts.assertEQ(Test21ClassA.c.x, 42);
+    }
+
+    static boolean test22FailInit = true;
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test22ClassA {
+        int x = 0;
+        @NullRestricted
+        static Test22ClassB b;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test22ClassB {
+        int x = 0;
+        static {
+            if (test22FailInit) {
+                throw new RuntimeException("Init failed");
+            }
+        }
+    }
+
+    // Test that load from static field of uninitialized value class throws an exception
+    @Test
+    public Object test22() {
+        return Test22ClassA.b;
+    }
+
+    @Run(test = "test22")
+    public void test22_verifier() {
+        // Trigger initialization error in Test22ClassB
+        try {
+            Test22ClassB b = new Test22ClassB();
+            throw new RuntimeException("Should have thrown error during initialization");
+        } catch (ExceptionInInitializerError | NoClassDefFoundError e) {
+            // Expected
+        }
+        try {
+            test22();
+            throw new RuntimeException("Should have thrown NoClassDefFoundError");
+        } catch (NoClassDefFoundError e) {
+            // Expected
+        }
+    }
+
+    static boolean test23FailInit = true;
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test23ClassA {
+        int x = 0;
+        @NullRestricted
+        static Test23ClassB b;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test23ClassB {
+        static {
+            if (test23FailInit) {
+                throw new RuntimeException("Init failed");
+            }
+        }
+    }
+
+    // Same as test22 but with empty ClassB
+    @Test
+    public Object test23() {
+        return Test23ClassA.b;
+    }
+
+    @Run(test = "test23")
+    public void test23_verifier() {
+        // Trigger initialization error in Test23ClassB
+        try {
+            Test23ClassB b = new Test23ClassB();
+            throw new RuntimeException("Should have thrown error during initialization");
+        } catch (ExceptionInInitializerError | NoClassDefFoundError e) {
+            // Expected
+        }
+        try {
+            test23();
+            throw new RuntimeException("Should have thrown NoClassDefFoundError");
+        } catch (NoClassDefFoundError e) {
+            // Expected
+        }
+    }
+
+    static boolean test24FailInit = true;
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test24ClassA {
+        @NullRestricted
+        Test24ClassB b = new Test24ClassB();
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test24ClassB {
+        int x = 0;
+        static {
+            if (test24FailInit) {
+                throw new RuntimeException("Init failed");
+            }
+        }
+    }
+
+    // Test that access to non-static field of uninitialized value class throws an exception
+    @Test
+    public Object test24() {
+        return (new Test24ClassA()).b.x;
+    }
+
+    @Run(test = "test24")
+    public void test24_verifier() {
+        // Trigger initialization error in Test24ClassB
+        try {
+            Test24ClassB b = new Test24ClassB();
+            throw new RuntimeException("Should have thrown error during initialization");
+        } catch (ExceptionInInitializerError | NoClassDefFoundError e) {
+            // Expected
+        }
+        try {
+            test24();
+            throw new RuntimeException("Should have thrown NoClassDefFoundError");
+        } catch (NoClassDefFoundError e) {
+            // Expected
+        }
+    }
+
+    static boolean test25FailInit = true;
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test25ClassA {
+        @NullRestricted
+        Test25ClassB b = new Test25ClassB();
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test25ClassB {
+        int x = 24;
+        static {
+            if (test25FailInit) {
+                throw new RuntimeException("Init failed");
+            }
+        }
+    }
+
+    // Same as test24 but with field access outside of test method
+    @Test
+    public Test25ClassB test25() {
+        return (new Test25ClassA()).b;
+    }
+
+    @Run(test = "test25")
+    public void test25_verifier() {
+        // Trigger initialization error in Test25ClassB
+        try {
+            Test25ClassB b = new Test25ClassB();
+            throw new RuntimeException("Should have thrown error during initialization");
+        } catch (ExceptionInInitializerError | NoClassDefFoundError e) {
+            // Expected
+        }
+        try {
+            Test25ClassB res = test25();
+            Asserts.assertEQ(res.x, 0);
+            throw new RuntimeException("Should have thrown NoClassDefFoundError");
+        } catch (NoClassDefFoundError e) {
+            // Expected
+        }
+    }
+
+    static boolean test26FailInit = true;
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test26ClassA {
+        @NullRestricted
+        Test26ClassB b = new Test26ClassB();
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Test26ClassB {
+        static {
+            if (test26FailInit) {
+                throw new RuntimeException("Init failed");
+            }
+        }
+    }
+
+    // Same as test25 but with empty ClassB
+    @Test
+    public Object test26() {
+        return (new Test26ClassA()).b;
+    }
+
+    @Run(test = "test26")
+    public void test26_verifier() {
+        // Trigger initialization error in Test26ClassB
+        try {
+            Test26ClassB b = new Test26ClassB();
+            throw new RuntimeException("Should have thrown error during initialization");
+        } catch (ExceptionInInitializerError | NoClassDefFoundError e) {
+            // Expected
+        }
+        try {
+            test26();
+            throw new RuntimeException("Should have thrown NoClassDefFoundError");
+        } catch (NoClassDefFoundError e) {
+            // Expected
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue27 {
+        int foo = rI;
+    }
+
+    static class MyValue27Holder {
+        @NullRestricted
+        MyValue27 v;
+    }
+
+    // Make sure MyValue27Holder is loaded but MyValue27 is not
+    Class test27Class = MyValue27Holder.class;
+
+    // Test unloaded value class field load from loaded holder
+    @Test
+    public static int test27() {
+        MyValue27Holder holder = new MyValue27Holder();
+        return holder.v.foo;
+    }
+
+    @Run(test = "test27")
+    public void test27_verifier() {
+        Asserts.assertEQ(test27(), 0);
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue28 {
+        @NullRestricted
+        static MyValue28 field1;
+    }
+
+    // Test null store to null restricted field with unloaded holder
+    @Test
+    public static void test28() {
+        MyValue28.field1 = null;
+    }
+
+    @Run(test = "test28")
+    @Warmup(0) // Make sure that MyValue28 is not loaded
+    public void test28_verifier() {
+        try {
+            test28();
+            throw new RuntimeException("No exception thrown");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestUnloadedReturnTypes.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestUnloadedReturnTypes.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestUnloadedReturnTypes.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2022, 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 scalarization in returns with unloaded return types.
+ * @library /test/lib /compiler/whitebox /
+ * @enablePreview
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -Xbatch -XX:CompileCommand=dontinline,*::test*
+ *                   TestUnloadedReturnTypes
+ */
+
+import java.lang.reflect.Method;
+
+import jdk.test.whitebox.WhiteBox;
+
+value class MyValue1 {
+    int x;
+
+    public MyValue1(int x) {
+        this.x = x;
+    }
+}
+
+class MyClass {
+
+    static MyValue1 test(boolean b) {
+        return b ? new MyValue1(42) : null;
+    }
+}
+
+public class TestUnloadedReturnTypes {
+    public static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+
+    static Object res = null;
+
+    public static void test(boolean b) {
+        res = MyClass.test(b);
+    }
+
+    public static void main(String[] args) throws Exception {
+        // C1 compile caller method
+        Method m = TestUnloadedReturnTypes.class.getMethod("test", boolean.class);
+        WHITE_BOX.enqueueMethodForCompilation(m, 3);
+
+        // Make sure the callee method is C2 compiled
+        for (int i = 0; i < 100_000; ++i) {
+            MyClass.test((i % 2) == 0);
+        }
+
+        test(true);
+        if (((MyValue1)res).x != 42) {
+            throw new RuntimeException("Test failed");
+        }
+
+        test(false);
+        if (res != null) {
+            throw new RuntimeException("Test failed");
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestUnresolvedInlineClass.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestUnresolvedInlineClass.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestUnresolvedInlineClass.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2017, 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
+ * @bug 8187679
+ * @summary The VM should exit gracefully when unable to preload an inline type argument
+ * @library /test/lib
+ * @enablePreview
+ * @compile SimpleInlineType.java TestUnresolvedInlineClass.java
+ * @run main/othervm TestUnresolvedInlineClass
+ */
+
+import java.io.File;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class TestUnresolvedInlineClass {
+    final static String TEST_CLASSES = System.getProperty("test.classes") + File.separator;
+
+    // Method with unresolved inline type argument
+    static void test1(SimpleInlineType vt) {
+
+    }
+
+    static public void main(String[] args) throws Exception {
+        if (args.length == 0) {
+            // Delete SimpleInlineType.class
+            File unresolved = new File(TEST_CLASSES, "SimpleInlineType.class");
+            if (!unresolved.exists() || !unresolved.delete()) {
+                throw new RuntimeException("Could not delete: " + unresolved);
+            }
+
+            // Run test in new VM instance
+            String[] arg = {"--enable-preview", "-XX:+InlineTypePassFieldsAsArgs", "TestUnresolvedInlineClass", "run"};
+            OutputAnalyzer oa = ProcessTools.executeTestJava(arg);
+
+            // Verify that a warning is printed
+            String output = oa.getOutput();
+            oa.shouldContain("Preloading of class SimpleInlineType during linking of class TestUnresolvedInlineClass (cause: Preload attribute) failed");
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueClasses.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueClasses.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueClasses.java
@@ -0,0 +1,962 @@
+/*
+ * Copyright (c) 2022, 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 compiler.valhalla.inlinetypes;
+
+import compiler.lib.ir_framework.*;
+import jdk.test.lib.Asserts;
+import jdk.experimental.bytecode.TypeTag;
+
+import java.lang.reflect.Method;
+
+import static compiler.valhalla.inlinetypes.InlineTypeIRNode.*;
+import static compiler.valhalla.inlinetypes.InlineTypes.*;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+/*
+ * @test
+ * @key randomness
+ * @summary Test correct handling of value classes.
+ * @library /test/lib /test/jdk/lib/testlibrary/bytecode /test/jdk/java/lang/invoke/common /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm/timeout=300 compiler.valhalla.inlinetypes.TestValueClasses
+ */
+
+@ForceCompileClassInitializer
+public class TestValueClasses {
+
+    public static void main(String[] args) {
+        Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS;
+        // Don't generate bytecodes but call through runtime for reflective calls
+        scenarios[0].addFlags("-Dsun.reflect.inflationThreshold=10000");
+        scenarios[1].addFlags("-Dsun.reflect.inflationThreshold=10000");
+        scenarios[3].addFlags("-XX:-MonomorphicArrayCheck", "-XX:FlatArrayElementMaxSize=-1");
+        scenarios[4].addFlags("-XX:-UseTLAB", "-XX:-MonomorphicArrayCheck");
+
+        InlineTypes.getFramework()
+                   .addScenarios(scenarios)
+                   .addHelperClasses(MyValueClass1.class,
+                                     MyValueClass2.class,
+                                     MyValueClass2Inline.class)
+                   .start();
+    }
+
+    static {
+        // Make sure RuntimeException is loaded to prevent uncommon traps in IR verified tests
+        RuntimeException tmp = new RuntimeException("42");
+    }
+
+    private static final MyValueClass1 testValue1 = MyValueClass1.createWithFieldsInline(rI, rL);
+
+    MyValueClass1 nullValField = null;
+    MyValueClass1 testField1;
+    MyValueClass1 testField2;
+    MyValueClass1 testField3;
+    MyValueClass1 testField4;
+    static MyValueClass1 testField5;
+    static MyValueClass1 testField6;
+    static MyValueClass1 testField7;
+    static MyValueClass1 testField8;
+
+    // Test field loads
+    @Test
+    public long test1(boolean b) {
+        MyValueClass1 val1 = b ? testField3 : MyValueClass1.createWithFieldsInline(rI, rL);
+        MyValueClass1 val2 = b ? testField7 : MyValueClass1.createWithFieldsInline(rI, rL);
+        long res = 0;
+        res += testField1.hash();
+        res += ((Object)testField2 == null) ? 42 : testField2.hash();
+        res += val1.hash();
+        res += testField4.hash();
+
+        res += testField5.hash();
+        res += ((Object)testField6 == null) ? 42 : testField6.hash();
+        res += val2.hash();
+        res += testField8.hash();
+        return res;
+    }
+
+    @Run(test = "test1")
+    public void test1_verifier() {
+        testField1 = testValue1;
+        testField2 = nullValField;
+        testField3 = testValue1;
+        testField4 = testValue1;
+
+        testField5 = testValue1;
+        testField6 = nullValField;
+        testField7 = testValue1;
+        testField8 = testValue1;
+        long res = test1(true);
+        Asserts.assertEquals(res, 2*42 + 6*testValue1.hash());
+
+        testField2 = testValue1;
+        testField6 = testValue1;
+        res = test1(false);
+        Asserts.assertEquals(res, 8*testValue1.hash());
+    }
+
+    // Test field stores
+    @Test
+    public MyValueClass1 test2(MyValueClass1 val1) {
+        MyValueClass1 ret = MyValueClass1.createWithFieldsInline(rI, rL);
+        MyValueClass1 val2 = MyValueClass1.setV4(testValue1, null);
+        testField1 = testField4;
+        testField2 = val1;
+        testField3 = val2;
+
+        testField5 = ret;
+        testField6 = val1;
+        testField7 = val2;
+        testField8 = testField4;
+        return ret;
+    }
+
+    @Run(test = "test2")
+    public void test2_verifier() {
+        testField4 = testValue1;
+        MyValueClass1 ret = test2(null);
+        MyValueClass1 val2 = MyValueClass1.setV4(testValue1, null);
+        Asserts.assertEquals(testField1, testValue1);
+        Asserts.assertEquals(testField2, null);
+        Asserts.assertEquals(testField3, val2);
+
+        Asserts.assertEquals(testField5, ret);
+        Asserts.assertEquals(testField6, null);
+        Asserts.assertEquals(testField7, val2);
+        Asserts.assertEquals(testField8, testField4);
+
+        testField4 = null;
+        test2(null);
+        Asserts.assertEquals(testField1, testField4);
+        Asserts.assertEquals(testField8, testField4);
+    }
+
+    // Non-value class Wrapper
+    static class Test3Wrapper {
+        MyValueClass1 val;
+
+        public Test3Wrapper(MyValueClass1 val) {
+            this.val = val;
+        }
+    }
+
+    // Test scalarization in safepoint debug info and re-allocation on deopt
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public long test3(boolean deopt, boolean b1, boolean b2, Method m) {
+        MyValueClass1 ret = MyValueClass1.createWithFieldsInline(rI, rL);
+        if (b1) {
+            ret = null;
+        }
+        if (b2) {
+            ret = MyValueClass1.setV4(ret, null);
+        }
+        Test3Wrapper wrapper = new Test3Wrapper(ret);
+        if (deopt) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        long res = ((Object)ret != null && (Object)ret.v4 != null) ? ret.hash() : 42;
+        res += ((Object)wrapper.val != null && (Object)wrapper.val.v4 != null) ? wrapper.val.hash() : 0;
+        return res;
+    }
+
+    @Run(test = "test3")
+    public void test3_verifier(RunInfo info) {
+        Asserts.assertEquals(test3(false, false, false, info.getTest()), 2*testValue1.hash());
+        Asserts.assertEquals(test3(false, true, false, info.getTest()), 42L);
+        if (!info.isWarmUp()) {
+            switch (rI % 4) {
+            case 0:
+                Asserts.assertEquals(test3(true, false, false, info.getTest()), 2*testValue1.hash());
+                break;
+            case 1:
+                Asserts.assertEquals(test3(true, true, false, info.getTest()), 42L);
+                break;
+            case 2:
+                Asserts.assertEquals(test3(true, false, true, info.getTest()), 42L);
+                break;
+            case 3:
+                try {
+                    Asserts.assertEquals(test3(true, true, true, info.getTest()), 42L);
+                    throw new RuntimeException("NullPointerException expected");
+                } catch (NullPointerException e) {
+                    // Expected
+                }
+                break;
+            }
+        }
+    }
+
+    // Test scalarization in safepoint debug info and re-allocation on deopt
+    @Test
+    @IR(failOn = {ALLOC, STORE})
+    public boolean test4(boolean deopt, boolean b, Method m) {
+        MyValueClass1 val = b ? null : MyValueClass1.createWithFieldsInline(rI, rL);
+        Test3Wrapper wrapper = new Test3Wrapper(val);
+        if (deopt) {
+            // Uncommon trap
+            TestFramework.deoptimize(m);
+        }
+        return (Object)wrapper.val == null;
+    }
+
+    @Run(test = "test4")
+    public void test4_verifier(RunInfo info) {
+        Asserts.assertTrue(test4(false, true, info.getTest()));
+        Asserts.assertFalse(test4(false, false, info.getTest()));
+        if (!info.isWarmUp()) {
+            switch (rI % 2) {
+                case 0:
+                    Asserts.assertTrue(test4(true, true, info.getTest()));
+                    break;
+                case 1:
+                    Asserts.assertFalse(test4(false, false, info.getTest()));
+                    break;
+            }
+        }
+    }
+
+    static value class SmallNullable2 {
+        float f1;
+        double f2;
+
+        @ForceInline
+        public SmallNullable2() {
+            f1 = (float)rL;
+            f2 = (double)rL;
+        }
+    }
+
+    static value class SmallNullable1 {
+        char c;
+        byte b;
+        short s;
+        int i;
+        SmallNullable2 vt;
+
+        @ForceInline
+        public SmallNullable1(boolean useNull) {
+            c = (char)rL;
+            b = (byte)rL;
+            s = (short)rL;
+            i = (int)rL;
+            vt = useNull ? null : new SmallNullable2();
+        }
+    }
+
+    @DontCompile
+    public SmallNullable1 test5_interpreted(boolean b1, boolean b2) {
+        return b1 ? null : new SmallNullable1(b2);
+    }
+
+    @DontInline
+    public SmallNullable1 test5_compiled(boolean b1, boolean b2) {
+        return b1 ? null : new SmallNullable1(b2);
+    }
+
+    SmallNullable1 test5_field1;
+    SmallNullable1 test5_field2;
+
+    // Test scalarization in returns
+    @Test
+    public SmallNullable1 test5(boolean b1, boolean b2) {
+        SmallNullable1 ret = test5_interpreted(b1, b2);
+        if (b1 != ((Object)ret == null)) {
+            throw new RuntimeException("test5 failed");
+        }
+        test5_field1 = ret;
+        ret = test5_compiled(b1, b2);
+        if (b1 != ((Object)ret == null)) {
+            throw new RuntimeException("test5 failed");
+        }
+        test5_field2 = ret;
+        return ret;
+    }
+
+    @Run(test = "test5")
+    public void test5_verifier() {
+        SmallNullable1 vt = new SmallNullable1(false);
+        Asserts.assertEquals(test5(true, false), null);
+        Asserts.assertEquals(test5_field1, null);
+        Asserts.assertEquals(test5_field2, null);
+        Asserts.assertEquals(test5(false, false), vt);
+        Asserts.assertEquals(test5_field1, vt);
+        Asserts.assertEquals(test5_field2, vt);
+        vt = new SmallNullable1(true);
+        Asserts.assertEquals(test5(true, true), null);
+        Asserts.assertEquals(test5_field1, null);
+        Asserts.assertEquals(test5_field2, null);
+        Asserts.assertEquals(test5(false, true), vt);
+        Asserts.assertEquals(test5_field1, vt);
+        Asserts.assertEquals(test5_field2, vt);
+    }
+
+    static value class Empty2 {
+
+    }
+
+    static value class Empty1 {
+        Empty2 empty2 = new Empty2();
+    }
+
+    static value class Container {
+        int x = 0;
+        Empty1 empty1;
+        Empty2 empty2 = new Empty2();
+
+        @ForceInline
+        public Container(Empty1 val) {
+            empty1 = val;
+        }
+    }
+
+    @DontInline
+    public static Empty1 test6_helper1(Empty1 vt) {
+        return vt;
+    }
+
+    @DontInline
+    public static Empty2 test6_helper2(Empty2 vt) {
+        return vt;
+    }
+
+    @DontInline
+    public static Container test6_helper3(Container vt) {
+        return vt;
+    }
+
+    // Test scalarization in calls and returns with empty value classes
+    @Test
+    public Empty1 test6(Empty1 vt) {
+        Empty1 empty1 = test6_helper1(vt);
+        test6_helper2((empty1 != null) ? empty1.empty2 : null);
+        Container c = test6_helper3(new Container(empty1));
+        return c.empty1;
+    }
+
+    @Run(test = "test6")
+    @Warmup(10000) // Warmup to make sure helper methods are compiled as well
+    public void test6_verifier() {
+        Asserts.assertEQ(test6(new Empty1()), new Empty1());
+        Asserts.assertEQ(test6(null), null);
+    }
+
+    @DontCompile
+    public void test7_helper2(boolean doit) {
+        if (doit) {
+            // uncommon trap
+            try {
+                TestFramework.deoptimize(getClass().getDeclaredMethod("test7", boolean.class, boolean.class, boolean.class));
+            } catch (NoSuchMethodException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    // Test deoptimization at call return with value object returned in registers
+    @DontInline
+    public SmallNullable1 test7_helper1(boolean deopt, boolean b1, boolean b2) {
+        test7_helper2(deopt);
+        return b1 ? null : new SmallNullable1(b2);
+    }
+
+    @Test
+    public SmallNullable1 test7(boolean flag, boolean b1, boolean b2) {
+        return test7_helper1(flag, b1, b2);
+    }
+
+    @Run(test = "test7")
+    @Warmup(10000)
+    public void test7_verifier(RunInfo info) {
+        boolean b1 = ((rI % 3) == 0);
+        boolean b2 = ((rI % 3) == 1);
+        SmallNullable1 result = test7(!info.isWarmUp(), b1, b2);
+        SmallNullable1 vt = new SmallNullable1(b2);
+        Asserts.assertEQ(result, b1 ? null : vt);
+    }
+
+    // Test calling a method returning a value class as fields via reflection
+    @Test
+    public SmallNullable1 test8(boolean b1, boolean b2) {
+        return b1 ? null : new SmallNullable1(b2);
+    }
+
+    @Run(test = "test8")
+    public void test8_verifier() throws Exception {
+        Method m = getClass().getDeclaredMethod("test8", boolean.class, boolean.class);
+        Asserts.assertEQ(m.invoke(this, false, true), new SmallNullable1(true));
+        Asserts.assertEQ(m.invoke(this, false, false), new SmallNullable1(false));
+        Asserts.assertEQ(m.invoke(this, true, false), null);
+    }
+
+    // Test value classes as arg/return
+    @Test
+    public SmallNullable1 test9(MyValueClass1 vt1, MyValueClass1 vt2, boolean b1, boolean b2) {
+        Asserts.assertEQ(vt1, testValue1);
+        if (b1) {
+            Asserts.assertEQ(vt2, null);
+        } else {
+            Asserts.assertEQ(vt2, testValue1);
+        }
+        return b1 ? null : new SmallNullable1(b2);
+    }
+
+    @Run(test = "test9")
+    public void test9_verifier() {
+        Asserts.assertEQ(test9(testValue1, testValue1, false, true), new SmallNullable1(true));
+        Asserts.assertEQ(test9(testValue1, testValue1, false, false), new SmallNullable1(false));
+        Asserts.assertEQ(test9(testValue1, null, true, false), null);
+    }
+
+    // Class.cast
+    @Test
+    public Object test10(Class c, MyValueClass1 vt) {
+        return c.cast(vt);
+    }
+
+    @Run(test = "test10")
+    public void test10_verifier() {
+        Asserts.assertEQ(test10(MyValueClass1.class, testValue1), testValue1);
+        Asserts.assertEQ(test10(MyValueClass1.class, null), null);
+        Asserts.assertEQ(test10(Integer.class, null), null);
+        try {
+            test10(MyValueClass2.class, testValue1);
+            throw new RuntimeException("ClassCastException expected");
+        } catch (ClassCastException e) {
+            // Expected
+        }
+    }
+
+    // Test acmp
+    @Test
+    public boolean test12(MyValueClass1 vt1, MyValueClass1 vt2) {
+        return vt1 == vt2;
+    }
+
+    @Run(test = "test12")
+    public void test12_verifier() {
+        Asserts.assertTrue(test12(testValue1, testValue1));
+        Asserts.assertTrue(test12(null, null));
+        Asserts.assertFalse(test12(testValue1, null));
+        Asserts.assertFalse(test12(null, testValue1));
+        Asserts.assertFalse(test12(testValue1, MyValueClass1.createDefaultInline()));
+    }
+
+    // Same as test13 but with Object argument
+    @Test
+    public boolean test13(Object obj, MyValueClass1 vt2) {
+        return obj == vt2;
+    }
+
+    @Run(test = "test13")
+    public void test13_verifier() {
+        Asserts.assertTrue(test13(testValue1, testValue1));
+        Asserts.assertTrue(test13(null, null));
+        Asserts.assertFalse(test13(testValue1, null));
+        Asserts.assertFalse(test13(null, testValue1));
+        Asserts.assertFalse(test13(testValue1, MyValueClass1.createDefaultInline()));
+    }
+
+    static MyValueClass1 test14_field1;
+    static MyValueClass1 test14_field2;
+
+    // Test buffer checks emitted by acmp followed by buffering
+    @Test
+    public boolean test14(MyValueClass1 vt1, MyValueClass1 vt2) {
+        // Trigger buffer checks
+        if (vt1 != vt2) {
+            throw new RuntimeException("Should be equal");
+        }
+        if (vt2 != vt1) {
+            throw new RuntimeException("Should be equal");
+        }
+        // Trigger buffering
+        test14_field1 = vt1;
+        test14_field2 = vt2;
+        return vt1 == null;
+    }
+
+    @Run(test = "test14")
+    public void test14_verifier() {
+        Asserts.assertFalse(test14(testValue1, testValue1));
+        Asserts.assertTrue(test14(null, null));
+    }
+
+    @DontInline
+    public MyValueClass1 test15_helper1(MyValueClass1 vt) {
+        return vt;
+    }
+
+    @ForceInline
+    public MyValueClass1 test15_helper2(MyValueClass1 vt) {
+        return test15_helper1(vt);
+    }
+
+    @ForceInline
+    public MyValueClass1 test15_helper3(Object vt) {
+        return test15_helper2((MyValueClass1)vt);
+    }
+
+    // Test that calling convention optimization prevents buffering of arguments
+    @Test
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
+        counts = {ALLOC_G, " = 7"}) // 6 MyValueClass2/MyValueClass2Inline allocations + 1 Integer allocation
+    @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
+        counts = {ALLOC_G, " = 8"}) // 1 MyValueClass1 allocation + 6 MyValueClass2/MyValueClass2Inline allocations + 1 Integer allocation
+    public MyValueClass1 test15(MyValueClass1 vt) {
+        MyValueClass1 res = test15_helper1(vt);
+        vt = MyValueClass1.createWithFieldsInline(rI, rL);
+        test15_helper1(vt);
+        test15_helper2(vt);
+        test15_helper3(vt);
+        vt.dontInline(vt);
+        return res;
+    }
+
+    @Run(test = "test15")
+    public void test15_verifier() {
+        Asserts.assertEQ(test15(testValue1), testValue1);
+        Asserts.assertEQ(test15(null), null);
+    }
+
+    @DontInline
+    public MyValueClass2 test16_helper1(boolean b) {
+        return b ? null : MyValueClass2.createWithFieldsInline(rI, rD);
+    }
+
+    @ForceInline
+    public MyValueClass2 test16_helper2() {
+        return null;
+    }
+
+    @ForceInline
+    public MyValueClass2 test16_helper3(boolean b) {
+        return b ? null : MyValueClass2.createWithFieldsInline(rI, rD);
+    }
+
+    // Test that calling convention optimization prevents buffering of return values
+    @Test
+    @IR(applyIf = {"InlineTypeReturnedAsFields", "true"},
+        counts = {ALLOC_G, " = 1"}) // 1 MyValueClass2Inline allocation
+    @IR(applyIf = {"InlineTypeReturnedAsFields", "false"},
+        counts = {ALLOC_G, " = 2"}) // 1 MyValueClass2 + 1 MyValueClass2Inline allocation
+    public MyValueClass2 test16(int c, boolean b) {
+        MyValueClass2 res = null;
+        if (c == 1) {
+            res = test16_helper1(b);
+        } else if (c == 2) {
+            res = test16_helper2();
+        } else if (c == 3) {
+            res = test16_helper3(b);
+        }
+        return res;
+    }
+
+    @Run(test = "test16")
+    public void test16_verifier() {
+        Asserts.assertEQ(test16(0, false), null);
+        Asserts.assertEQ(test16(1, false).hash(), MyValueClass2.createWithFieldsInline(rI, rD).hash());
+        Asserts.assertEQ(test16(1, true), null);
+        Asserts.assertEQ(test16(2, false), null);
+        Asserts.assertEQ(test16(3, false).hash(), MyValueClass2.createWithFieldsInline(rI, rD).hash());
+        Asserts.assertEQ(test16(3, true), null);
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyPrimitive17 {
+        MyValueClass1 nonFlattened;
+
+        public MyPrimitive17(MyValueClass1 val) {
+            this.nonFlattened = val;
+        }
+    }
+
+    static value class MyValue17 {
+        @NullRestricted
+        MyPrimitive17 flattened;
+
+        public MyValue17(boolean b) {
+            this.flattened = new MyPrimitive17(b ? null : testValue1);
+        }
+    }
+
+    @DontCompile
+    public MyValue17 test17_interpreted(boolean b1, boolean b2) {
+        return b1 ? null : new MyValue17(b2);
+    }
+
+    @DontInline
+    public MyValue17 test17_compiled(boolean b1, boolean b2) {
+        return b1 ? null : new MyValue17(b2);
+    }
+
+    MyValue17 test17_field1;
+    MyValue17 test17_field2;
+
+    // Test handling of null when mixing nullable and null-restricted fields
+    @Test
+    public MyValue17 test17(boolean b1, boolean b2) {
+        MyValue17 ret = test17_interpreted(b1, b2);
+        if (b1 != ((Object)ret == null)) {
+            throw new RuntimeException("test17 failed");
+        }
+        test17_field1 = ret;
+        ret = test17_compiled(b1, b2);
+        if (b1 != ((Object)ret == null)) {
+            throw new RuntimeException("test17 failed");
+        }
+        test17_field2 = ret;
+        return ret;
+    }
+
+    @Run(test = "test17")
+    public void test17_verifier() {
+        MyValue17 vt = new MyValue17(false);
+        Asserts.assertEquals(test17(true, false), null);
+        Asserts.assertEquals(test17_field1, null);
+        Asserts.assertEquals(test17_field2, null);
+        Asserts.assertEquals(test17(false, false), vt);
+        Asserts.assertEquals(test17_field1, vt);
+        Asserts.assertEquals(test17_field2, vt);
+        vt = new MyValue17(true);
+        Asserts.assertEquals(test17(true, true), null);
+        Asserts.assertEquals(test17_field1, null);
+        Asserts.assertEquals(test17_field2, null);
+        Asserts.assertEquals(test17(false, true), vt);
+        Asserts.assertEquals(test17_field1, vt);
+        Asserts.assertEquals(test17_field2, vt);
+    }
+
+    // Uses all registers available for returning values on x86_64
+    static value class UseAllRegs {
+        long l1;
+        long l2;
+        long l3;
+        long l4;
+        long l5;
+        long l6;
+        double d1;
+        double d2;
+        double d3;
+        double d4;
+        double d5;
+        double d6;
+        double d7;
+        double d8;
+
+        @ForceInline
+        public UseAllRegs(long l1, long l2, long l3, long l4, long l5, long l6,
+                          double d1, double d2, double d3, double d4, double d5, double d6, double d7, double d8) {
+            this.l1 = l1;
+            this.l2 = l2;
+            this.l3 = l3;
+            this.l4 = l4;
+            this.l5 = l5;
+            this.l6 = l6;
+            this.d1 = d1;
+            this.d2 = d2;
+            this.d3 = d3;
+            this.d4 = d4;
+            this.d5 = d5;
+            this.d6 = d6;
+            this.d7 = d7;
+            this.d8 = d8;
+        }
+    }
+
+    @DontInline
+    public UseAllRegs test18_helper1(UseAllRegs val, long a, long b, long c, long d, long e, long f, long g, long h, long i, long j) {
+        Asserts.assertEquals(a & b & c & d & e & f & g & h & i & j, 0L);
+        return val;
+    }
+
+    @DontCompile
+    public UseAllRegs test18_helper2(UseAllRegs val, long a, long b, long c, long d, long e, long f, long g, long h, long i, long j) {
+        Asserts.assertEquals(a & b & c & d & e & f & g & h & i & j, 0L);
+        return val;
+    }
+
+    static boolean test18_b;
+
+    // Methods with no arguments (no stack slots reserved for incoming args)
+    @DontInline
+    public static UseAllRegs test18_helper3() {
+        return test18_b ? null : new UseAllRegs(rL + 1, rL + 2, rL + 3, rL + 4, rL + 5, rL + 6, rL + 7, rL + 8, rL + 9, rL + 10, rL + 11, rL + 12, rL + 13, rL + 14);
+    }
+
+    @DontCompile
+    public static UseAllRegs test18_helper4() {
+        return test18_b ? null : test18_helper3();
+    }
+
+    // Test proper register allocation of isInit projection of a call in C2
+    @Test
+    public UseAllRegs test18(boolean b, long val1, long l1, long val2, long l2, long val3, long l3, long val4, long l4, long val5, long l5, long val6, long l6,
+                             long val7, double d1, long val8, double d2, long val9, double d3, long val10, double d4, long val11, double d5, long val12, double d6, long val13, double d7, long val14, double d8, long val15) {
+        Asserts.assertEquals(val1, rL);
+        Asserts.assertEquals(val2, rL);
+        Asserts.assertEquals(val3, rL);
+        Asserts.assertEquals(val4, rL);
+        Asserts.assertEquals(val5, rL);
+        Asserts.assertEquals(val6, rL);
+        Asserts.assertEquals(val7, rL);
+        Asserts.assertEquals(val8, rL);
+        Asserts.assertEquals(val9, rL);
+        Asserts.assertEquals(val10, rL);
+        Asserts.assertEquals(val11, rL);
+        Asserts.assertEquals(val12, rL);
+        Asserts.assertEquals(val13, rL);
+        Asserts.assertEquals(val14, rL);
+        Asserts.assertEquals(val15, rL);
+        UseAllRegs val = b ? null : new UseAllRegs(l1, l2, l3, l4, l5, l6, d1, d2, d3, d4, d5, d6, d7, d8);
+        val = test18_helper1(val, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+        val = test18_helper2(val, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+        Asserts.assertEquals(val1, rL);
+        Asserts.assertEquals(val2, rL);
+        Asserts.assertEquals(val3, rL);
+        Asserts.assertEquals(val4, rL);
+        Asserts.assertEquals(val5, rL);
+        Asserts.assertEquals(val6, rL);
+        Asserts.assertEquals(val7, rL);
+        Asserts.assertEquals(val8, rL);
+        Asserts.assertEquals(val9, rL);
+        Asserts.assertEquals(val10, rL);
+        Asserts.assertEquals(val11, rL);
+        Asserts.assertEquals(val12, rL);
+        Asserts.assertEquals(val13, rL);
+        Asserts.assertEquals(val14, rL);
+        Asserts.assertEquals(val15, rL);
+        Asserts.assertEquals(test18_helper3(), val);
+        Asserts.assertEquals(test18_helper4(), val);
+        return val;
+    }
+
+    @Run(test = "test18")
+    public void test18_verifier() {
+        UseAllRegs val = new UseAllRegs(rL + 1, rL + 2, rL + 3, rL + 4, rL + 5, rL + 6, rL + 7, rL + 8, rL + 9, rL + 10, rL + 11, rL + 12, rL + 13, rL + 14);
+        test18_b = false;
+        Asserts.assertEquals(test18(false, rL, rL + 1, rL, rL + 2, rL, rL + 3, rL, rL + 4, rL, rL + 5, rL, rL + 6,
+                                    rL, rL + 7, rL, rL + 8, rL, rL + 9, rL, rL + 10, rL, rL + 11, rL, rL + 12, rL, rL + 13, rL, rL + 14, rL), val);
+        test18_b = true;
+        Asserts.assertEquals(test18(true, rL, rL + 1, rL, rL + 2, rL, rL + 3, rL, rL + 4, rL, rL + 5, rL, rL + 6,
+                                    rL, rL + 7, rL, rL + 8, rL, rL + 9, rL, rL + 10, rL, rL + 11, rL, rL + 12, rL, rL + 13, rL, rL + 14, rL), null);
+    }
+
+    @DontInline
+    static public UseAllRegs test19_helper() {
+        return new UseAllRegs(rL + 1, rL + 2, rL + 3, rL + 4, rL + 5, rL + 6, rL + 7, rL + 8, rL + 9, rL + 10, rL + 11, rL + 12, rL + 13, rL + 14);
+    }
+
+    // Test proper register allocation of isInit projection of a call in C2
+    @Test
+    static public void test19(long a, long b, long c, long d, long e, long f) {
+        if (test19_helper() == null) {
+            throw new RuntimeException("test19 failed: Unexpected null");
+        }
+        if ((a & b & c & d & e & f) != 0) {
+            throw new RuntimeException("test19 failed: Unexpected argument values");
+        }
+    }
+
+    @Run(test = "test19")
+    public void test19_verifier() {
+        test19(0, 0, 0, 0, 0, 0);
+    }
+
+    // Uses almost all registers available for returning values on x86_64
+    static value class UseAlmostAllRegs {
+        long l1;
+        long l2;
+        long l3;
+        long l4;
+        long l5;
+        double d1;
+        double d2;
+        double d3;
+        double d4;
+        double d5;
+        double d6;
+        double d7;
+
+        @ForceInline
+        public UseAlmostAllRegs(long l1, long l2, long l3, long l4, long l5,
+                                double d1, double d2, double d3, double d4, double d5, double d6, double d7) {
+            this.l1 = l1;
+            this.l2 = l2;
+            this.l3 = l3;
+            this.l4 = l4;
+            this.l5 = l5;
+            this.d1 = d1;
+            this.d2 = d2;
+            this.d3 = d3;
+            this.d4 = d4;
+            this.d5 = d5;
+            this.d6 = d6;
+            this.d7 = d7;
+        }
+    }
+
+    @DontInline
+    public UseAlmostAllRegs test20_helper1(UseAlmostAllRegs val, long a, long b, long c, long d, long e, long f, long g, long h, long i, long j) {
+        Asserts.assertEquals(a & b & c & d & e & f & g & h & i & j, 0L);
+        return val;
+    }
+
+    @DontCompile
+    public UseAlmostAllRegs test20_helper2(UseAlmostAllRegs val, long a, long b, long c, long d, long e, long f, long g, long h, long i, long j) {
+        Asserts.assertEquals(a & b & c & d & e & f & g & h & i & j, 0L);
+        return val;
+    }
+
+    static boolean test20_b;
+
+    // Methods with no arguments (no stack slots reserved for incoming args)
+    @DontInline
+    public static UseAlmostAllRegs test20_helper3() {
+        return test20_b ? null : new UseAlmostAllRegs(rL + 1, rL + 2, rL + 3, rL + 4, rL + 5, rL + 6, rL + 7, rL + 8, rL + 9, rL + 10, rL + 11, rL + 12);
+    }
+
+    @DontCompile
+    public static UseAlmostAllRegs test20_helper4() {
+        return test20_b ? null : test20_helper3();
+    }
+
+    // Test proper register allocation of isInit projection of a call in C2
+    @Test
+    public UseAlmostAllRegs test20(boolean b, long val1, long l1, long val2, long l2, long val3, long l3, long val4, long l4, long val5, long l5, long val6,
+                                   long val7, double d1, long val8, double d2, long val9, double d3, long val10, double d4, long val11, double d5, long val12, double d6, long val13, double d7, long val14, long val15) {
+        Asserts.assertEquals(val1, rL);
+        Asserts.assertEquals(val2, rL);
+        Asserts.assertEquals(val3, rL);
+        Asserts.assertEquals(val4, rL);
+        Asserts.assertEquals(val5, rL);
+        Asserts.assertEquals(val6, rL);
+        Asserts.assertEquals(val7, rL);
+        Asserts.assertEquals(val8, rL);
+        Asserts.assertEquals(val9, rL);
+        Asserts.assertEquals(val10, rL);
+        Asserts.assertEquals(val11, rL);
+        Asserts.assertEquals(val12, rL);
+        Asserts.assertEquals(val13, rL);
+        Asserts.assertEquals(val14, rL);
+        Asserts.assertEquals(val15, rL);
+        UseAlmostAllRegs val = b ? null : new UseAlmostAllRegs(l1, l2, l3, l4, l5, d1, d2, d3, d4, d5, d6, d7);
+        val = test20_helper1(val, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+        val = test20_helper2(val, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+        Asserts.assertEquals(val1, rL);
+        Asserts.assertEquals(val2, rL);
+        Asserts.assertEquals(val3, rL);
+        Asserts.assertEquals(val4, rL);
+        Asserts.assertEquals(val5, rL);
+        Asserts.assertEquals(val6, rL);
+        Asserts.assertEquals(val7, rL);
+        Asserts.assertEquals(val8, rL);
+        Asserts.assertEquals(val9, rL);
+        Asserts.assertEquals(val10, rL);
+        Asserts.assertEquals(val11, rL);
+        Asserts.assertEquals(val12, rL);
+        Asserts.assertEquals(val13, rL);
+        Asserts.assertEquals(val14, rL);
+        Asserts.assertEquals(val15, rL);
+        Asserts.assertEquals(test20_helper3(), val);
+        Asserts.assertEquals(test20_helper4(), val);
+        return val;
+    }
+
+    @Run(test = "test20")
+    public void test20_verifier() {
+        UseAlmostAllRegs val = new UseAlmostAllRegs(rL + 1, rL + 2, rL + 3, rL + 4, rL + 5, rL + 6, rL + 7, rL + 8, rL + 9, rL + 10, rL + 11, rL + 12);
+        test20_b = false;
+        Asserts.assertEquals(test20(false, rL, rL + 1, rL, rL + 2, rL, rL + 3, rL, rL + 4, rL, rL + 5, rL,
+                                    rL, rL + 6, rL, rL + 7, rL, rL + 8, rL, rL + 9, rL, rL + 10, rL, rL + 11, rL, rL + 12, rL, rL), val);
+        test20_b = true;
+        Asserts.assertEquals(test20(true, rL, rL + 1, rL, rL + 2, rL, rL + 3, rL, rL + 4, rL, rL + 5, rL,
+                                    rL, rL + 6, rL, rL + 7, rL, rL + 8, rL, rL + 9, rL, rL + 10, rL, rL + 11, rL, rL + 12, rL, rL), null);
+    }
+
+    @DontInline
+    static public UseAlmostAllRegs test21_helper() {
+        return new UseAlmostAllRegs(rL + 1, rL + 2, rL + 3, rL + 4, rL + 5, rL + 6, rL + 7, rL + 8, rL + 9, rL + 10, rL + 11, rL + 12);
+    }
+
+    // Test proper register allocation of isInit projection of a call in C2
+    @Test
+    static public void test21(long a, long b, long c, long d, long e, long f) {
+        if (test21_helper() == null) {
+            throw new RuntimeException("test21 failed: Unexpected null");
+        }
+        if ((a & b & c & d & e & f) != 0) {
+            throw new RuntimeException("test21 failed: Unexpected argument values");
+        }
+    }
+
+    @Run(test = "test21")
+    public void test21_verifier() {
+        test21(0, 0, 0, 0, 0, 0);
+    }
+
+    static value class ManyOopsValue {
+        Integer i1 = 1;
+        Integer i2 = 2;
+        Integer i3 = 3;
+        Integer i4 = 4;
+        Integer i5 = 5;
+        Integer i6 = 6;
+        Integer i7 = 7;
+        Integer i8 = 8;
+        Integer i9 = 9;
+        Integer i10 = 10;
+        Integer i11 = 11;
+        Integer i12 = 12;
+        Integer i13 = 13;
+        Integer i14 = 14;
+        Integer i15 = 15;
+
+        @DontInline
+        public int sum() {
+            return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + i10 + i11 + i12 + i13 + i14 + i15;
+        }
+    }
+
+    // Verify that C2 scratch buffer size is large enough to hold many GC barriers used by the entry points
+    @Test
+    static public int test22(ManyOopsValue val) {
+        return val.sum();
+    }
+
+    @Run(test = "test22")
+    @Warmup(10_000)
+    public void test22_verifier() {
+        Asserts.assertEquals(test22(new ManyOopsValue()), 120);
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueConstruction.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueConstruction.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueConstruction.java
@@ -0,0 +1,622 @@
+/*
+ * 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 compiler.valhalla.inlinetypes;
+
+import java.util.Random;
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.Utils;
+import jdk.test.whitebox.WhiteBox;
+
+/**
+ * @test TestValueConstruction
+ * @summary Test construction of value objects.
+ * @key randomness
+ * @library /testlibrary /test/lib /compiler/whitebox /
+ * @enablePreview
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch
+ *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
+ *                   compiler.valhalla.inlinetypes.TestValueConstruction
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:+DeoptimizeALot
+ *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
+ *                   compiler.valhalla.inlinetypes.TestValueConstruction
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:CompileCommand=compileonly,*TestValueConstruction::test* -Xbatch
+ *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
+ *                   compiler.valhalla.inlinetypes.TestValueConstruction
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:CompileCommand=dontinline,*MyValue*::<init> -Xbatch
+ *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
+ *                   compiler.valhalla.inlinetypes.TestValueConstruction
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:CompileCommand=dontinline,*Object::<init> -Xbatch
+ *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
+ *                   compiler.valhalla.inlinetypes.TestValueConstruction
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+IgnoreUnrecognizedVMOptions -XX:+DeoptimizeALot
+ *                   -XX:CompileCommand=dontinline,*Object::<init> -Xbatch
+ *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
+ *                   compiler.valhalla.inlinetypes.TestValueConstruction
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:CompileCommand=dontinline,*MyAbstract::<init> -Xbatch
+ *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
+ *                   compiler.valhalla.inlinetypes.TestValueConstruction
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch
+ *                   -XX:-TieredCompilation -XX:+StressIncrementalInlining
+ *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
+ *                   compiler.valhalla.inlinetypes.TestValueConstruction
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:-TieredCompilation -XX:+StressIncrementalInlining
+ *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
+ *                   -XX:CompileCommand=compileonly,*TestValueConstruction::test* -Xbatch
+ *                   compiler.valhalla.inlinetypes.TestValueConstruction
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:-TieredCompilation -XX:+StressIncrementalInlining
+ *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
+ *                   -XX:CompileCommand=dontinline,*MyValue*::<init> -Xbatch
+ *                   compiler.valhalla.inlinetypes.TestValueConstruction
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:-TieredCompilation -XX:+StressIncrementalInlining
+ *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
+ *                   -XX:CompileCommand=dontinline,*Object::<init> -Xbatch
+ *                   compiler.valhalla.inlinetypes.TestValueConstruction
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -XX:-TieredCompilation -XX:+StressIncrementalInlining
+ *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
+ *                   -XX:CompileCommand=dontinline,*MyAbstract::<init> -Xbatch
+ *                   compiler.valhalla.inlinetypes.TestValueConstruction
+ */
+
+public class TestValueConstruction {
+    static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+
+    static boolean VERBOSE = false;
+    static boolean deopt[] = new boolean[13];
+
+    static void reportDeopt(int deoptNum) {
+        System.out.println("Deopt " + deoptNum + " triggered");
+        if (VERBOSE) {
+            new Exception().printStackTrace(System.out);
+        }
+    }
+
+    // Trigger deopts at various places
+    static void checkDeopt(int deoptNum) {
+        if (deopt[deoptNum]) {
+            // C2 will add an uncommon trap here
+            reportDeopt(deoptNum);
+        }
+    }
+
+    static interface MyInterface {
+
+    }
+
+    static value class MyValue1 implements MyInterface {
+        int x;
+
+        public MyValue1(int x) {
+            checkDeopt(0);
+            this.x = x;
+            checkDeopt(1);
+            super();
+            checkDeopt(2);
+        }
+
+        public MyValue1(int x, int deoptNum1, int deoptNum2, int deoptNum3) {
+            checkDeopt(deoptNum1);
+            this.x = x;
+            checkDeopt(deoptNum2);
+            super();
+            checkDeopt(deoptNum3);
+        }
+
+        public String toString() {
+            return "x: " + x;
+        }
+    }
+
+    static abstract value class MyAbstract1 { }
+
+    static value class MyValue2 extends MyAbstract1 {
+        int x;
+
+        public MyValue2(int x) {
+            checkDeopt(0);
+            this.x = x;
+            checkDeopt(1);
+            super();
+            checkDeopt(2);
+        }
+
+        public String toString() {
+            return "x: " + x;
+        }
+    }
+
+    static abstract value class MyAbstract2 {
+        public MyAbstract2(int x) {
+            checkDeopt(0);
+        }
+    }
+
+    static value class MyValue3 extends MyAbstract2 {
+        int x;
+
+        public MyValue3(int x) {
+            checkDeopt(1);
+            this(x, 0);
+            helper1(this, x, 2); // 'this' escapes through argument
+            helper2(x, 3); // 'this' escapes through receiver
+            checkDeopt(4);
+        }
+
+        public MyValue3(int x, int unused) {
+            this.x = helper3(x, 5);
+            super(x);
+            helper1(this, x, 6); // 'this' escapes through argument
+            helper2(x, 7); // 'this' escapes through receiver
+            checkDeopt(8);
+        }
+
+        public static void helper1(MyValue3 obj, int x, int deoptNum) {
+            checkDeopt(deoptNum);
+            Asserts.assertEQ(obj.x, x);
+        }
+
+        public void helper2(int x, int deoptNum) {
+            checkDeopt(deoptNum);
+            Asserts.assertEQ(this.x, x);
+        }
+
+        public static int helper3(int x, int deoptNum) {
+            checkDeopt(deoptNum);
+            return x;
+        }
+
+        public String toString() {
+            return "x: " + x;
+        }
+    }
+
+    static value class MyValue4 {
+        Integer x;
+
+        public MyValue4(int x) {
+            checkDeopt(0);
+            this.x = x;
+            checkDeopt(1);
+            super();
+            checkDeopt(2);
+        }
+
+        public String toString() {
+            return "x: " + x;
+        }
+    }
+
+    static value class MyValue5 {
+        int x;
+
+        public MyValue5(int x, boolean b) {
+            checkDeopt(0);
+            if (b) {
+                checkDeopt(1);
+                this.x = 42;
+                checkDeopt(2);
+            } else {
+                checkDeopt(3);
+                this.x = x;
+                checkDeopt(4);
+            }
+            checkDeopt(5);
+            super();
+            checkDeopt(6);
+        }
+
+        public String toString() {
+            return "x: " + x;
+        }
+    }
+
+    static value class MyValue6 {
+        int x;
+        MyValue1 val1;
+        MyValue1 val2;
+
+        public MyValue6(int x) {
+            checkDeopt(0);
+            this.x = x;
+            checkDeopt(1);
+            this.val1 = new MyValue1(x, 2, 3, 4);
+            checkDeopt(5);
+            this.val2 = new MyValue1(x + 1, 6, 7, 8);
+            checkDeopt(9);
+            super();
+            checkDeopt(10);
+        }
+
+        public String toString() {
+            return "x: " + x + ", val1: [" + val1 + "], val2: [" + val2 + "]";
+        }
+    }
+
+    // Same as MyValue6 but unused MyValue1 construction
+    static value class MyValue7 {
+        int x;
+
+        public MyValue7(int x) {
+            checkDeopt(0);
+            this.x = x;
+            checkDeopt(1);
+            new MyValue1(42, 2, 3, 4);
+            checkDeopt(5);
+            new MyValue1(43, 6, 7, 8);
+            checkDeopt(9);
+            super();
+            checkDeopt(10);
+        }
+
+        public String toString() {
+            return "x: " + x;
+        }
+    }
+
+    // Constructor calling another constructor of the same value class with control flow dependent initialization
+    static value class MyValue8 {
+        int x;
+
+        public MyValue8(int x) {
+            checkDeopt(0);
+            this(x, 0);
+            checkDeopt(1);
+        }
+
+        public MyValue8(int x, int unused1) {
+            checkDeopt(2);
+            if ((x % 2) == 0) {
+                checkDeopt(3);
+                this.x = 42;
+                checkDeopt(4);
+            } else {
+                checkDeopt(5);
+                this.x = x;
+                checkDeopt(6);
+            }
+            checkDeopt(7);
+            super();
+            checkDeopt(8);
+        }
+
+        public MyValue8(int x, int unused1, int unused2) {
+            checkDeopt(3);
+            this.x = x;
+            checkDeopt(4);
+        }
+
+        public static MyValue8 valueOf(int x) {
+            checkDeopt(0);
+            if ((x % 2) == 0) {
+                checkDeopt(1);
+                return new MyValue8(42, 0, 0);
+            } else {
+                checkDeopt(2);
+                return new MyValue8(x, 0, 0);
+            }
+        }
+
+        public String toString() {
+            return "x: " + x;
+        }
+    }
+
+    // Constructor calling another constructor of a different value class
+    static value class MyValue9 {
+        MyValue8 val;
+
+        public MyValue9(int x) {
+            checkDeopt(9);
+            this(x, 0);
+            checkDeopt(10);
+        }
+
+        public MyValue9(int i, int unused1) {
+            checkDeopt(11);
+            val = new MyValue8(i);
+            checkDeopt(12);
+        }
+
+        public MyValue9(int x, int unused1, int unused2) {
+            checkDeopt(5);
+            this(x, 0, 0, 0);
+            checkDeopt(6);
+        }
+
+        public MyValue9(int i, int unused1, int unused2, int unused3) {
+            checkDeopt(7);
+            val = MyValue8.valueOf(i);
+            checkDeopt(8);
+        }
+
+        public String toString() {
+            return "val: [" + val + "]";
+        }
+    }
+
+    // Constructor with a loop
+    static value class MyValue10 {
+        int x;
+        int y;
+
+        public MyValue10(int x, int cnt) {
+            checkDeopt(0);
+            this.x = x;
+            checkDeopt(1);
+            int res = 0;
+            for (int i = 0; i < cnt; ++i) {
+                checkDeopt(2);
+                res += x;
+                checkDeopt(3);
+            }
+            checkDeopt(4);
+            this.y = res;
+            checkDeopt(5);
+            super();
+            checkDeopt(6);
+        }
+
+        public String toString() {
+            return "x: " + x + ", y: " + y;
+        }
+    }
+
+    // Value class with recursive field definitions
+    static value class MyValue11 {
+        int x;
+        MyValue11 val1;
+        MyValue11 val2;
+
+        public MyValue11(int x) {
+            checkDeopt(0);
+            this.x = x;
+            checkDeopt(1);
+            this.val1 = new MyValue11(x + 1, 2, 3, 4, 5);
+            checkDeopt(6);
+            this.val2 = new MyValue11(x + 2, 7, 8, 9, 10);
+            checkDeopt(11);
+        }
+
+        public MyValue11(int x, int deoptNum1, int deoptNum2, int deoptNum3, int deoptNum4) {
+            checkDeopt(deoptNum1);
+            this.x = x;
+            checkDeopt(deoptNum2);
+            this.val1 = null;
+            checkDeopt(deoptNum3);
+            this.val2 = null;
+            checkDeopt(deoptNum4);
+        }
+
+        public String toString() {
+            return "x: " + x + ", val1: [" + (val1 != this ? val1 : "this") + "], val2: [" + (val2 != this ? val2 : "this") + "]";
+        }
+    }
+
+    public static int test1(int x) {
+        MyValue1 val = new MyValue1(x);
+        checkDeopt(3);
+        return val.x;
+    }
+
+    public static MyValue1 helper1(int x) {
+        return new MyValue1(x);
+    }
+
+    public static Object test2(int x) {
+        return helper1(x);
+    }
+
+    public static Object test3(int limit) {
+        MyValue1 res = null;
+        for (int i = 0; i <= 10; ++i) {
+            res = new MyValue1(i);
+            checkDeopt(3);
+        }
+        return res;
+    }
+
+    public static MyValue1 test4(int x) {
+        MyValue1 v = new MyValue1(x);
+        checkDeopt(3);
+        v = new MyValue1(x);
+        return v;
+    }
+
+    public static int test5(int x) {
+        MyValue2 val = new MyValue2(x);
+        checkDeopt(3);
+        return val.x;
+    }
+
+    public static MyValue2 helper2(int x) {
+        return new MyValue2(x);
+    }
+
+    public static Object test6(int x) {
+        return helper2(x);
+    }
+
+    public static Object test7(int limit) {
+        MyValue2 res = null;
+        for (int i = 0; i <= 10; ++i) {
+            res = new MyValue2(i);
+            checkDeopt(3);
+        }
+        return res;
+    }
+
+    public static MyValue2 test8(int x) {
+        MyValue2 v = new MyValue2(x);
+        checkDeopt(3);
+        v = new MyValue2(x);
+        return v;
+    }
+
+    public static int test9(int x) {
+        MyValue3 val = new MyValue3(x);
+        checkDeopt(9);
+        return val.x;
+    }
+
+    public static MyValue3 helper3(int x) {
+        return new MyValue3(x);
+    }
+
+    public static Object test10(int x) {
+        return helper3(x);
+    }
+
+    public static Object test11(int limit) {
+        MyValue3 res = null;
+        for (int i = 0; i <= 10; ++i) {
+            checkDeopt(9);
+            res = new MyValue3(i);
+        }
+        return res;
+    }
+
+    public static MyValue3 test12(int x) {
+        MyValue3 v = new MyValue3(x);
+        checkDeopt(9);
+        v = new MyValue3(x);
+        return v;
+    }
+
+    public static MyValue4 test13(int x) {
+        return new MyValue4(x);
+    }
+
+    public static MyValue5 test14(int x, boolean b) {
+        return new MyValue5(x, b);
+    }
+
+    public static Object test15(int x) {
+        return new MyValue6(x);
+    }
+
+    public static Object test16(int x) {
+        return new MyValue7(x);
+    }
+
+    public static MyValue8 test17(int x) {
+        return new MyValue8(x);
+    }
+
+    public static MyValue8 test18(int x) {
+        return new MyValue8(x, 0);
+    }
+
+    public static MyValue8 test19(int x) {
+        return MyValue8.valueOf(x);
+    }
+
+    public static MyValue9 test20(int x) {
+        return new MyValue9(x);
+    }
+
+    public static MyValue9 test21(int x) {
+        return new MyValue9(x, 0);
+    }
+
+    public static MyValue9 test22(int x) {
+        return new MyValue9(x, 0, 0);
+    }
+
+    public static MyValue9 test23(int x) {
+        return new MyValue9(x, 0, 0, 0);
+    }
+
+    public static MyValue10 test24(int x, int cnt) {
+        return new MyValue10(x, cnt);
+    }
+
+    public static MyValue11 test25(int x) {
+        return new MyValue11(x);
+    }
+
+    public static void main(String[] args) throws Exception {
+        Random rand = Utils.getRandomInstance();
+
+        // Randomly exclude some constructors from inlining via the WhiteBox API because CompileCommands don't match on different signatures.
+        WHITE_BOX.testSetDontInlineMethod(MyValue1.class.getConstructor(int.class), rand.nextBoolean());
+        WHITE_BOX.testSetDontInlineMethod(MyValue1.class.getConstructor(int.class, int.class, int.class, int.class), rand.nextBoolean());
+        WHITE_BOX.testSetDontInlineMethod(MyValue3.class.getConstructor(int.class), rand.nextBoolean());
+        WHITE_BOX.testSetDontInlineMethod(MyValue3.class.getConstructor(int.class, int.class), rand.nextBoolean());
+        WHITE_BOX.testSetDontInlineMethod(MyValue8.class.getConstructor(int.class), rand.nextBoolean());
+        WHITE_BOX.testSetDontInlineMethod(MyValue8.class.getConstructor(int.class, int.class), rand.nextBoolean());
+        WHITE_BOX.testSetDontInlineMethod(MyValue8.class.getConstructor(int.class, int.class, int.class), rand.nextBoolean());
+        WHITE_BOX.testSetDontInlineMethod(MyValue9.class.getConstructor(int.class), rand.nextBoolean());
+        WHITE_BOX.testSetDontInlineMethod(MyValue9.class.getConstructor(int.class, int.class), rand.nextBoolean());
+        WHITE_BOX.testSetDontInlineMethod(MyValue9.class.getConstructor(int.class, int.class, int.class), rand.nextBoolean());
+        WHITE_BOX.testSetDontInlineMethod(MyValue9.class.getConstructor(int.class, int.class, int.class, int.class), rand.nextBoolean());
+        WHITE_BOX.testSetDontInlineMethod(MyValue11.class.getConstructor(int.class), rand.nextBoolean());
+        WHITE_BOX.testSetDontInlineMethod(MyValue11.class.getConstructor(int.class, int.class, int.class, int.class, int.class), rand.nextBoolean());
+
+        Integer deoptNum = Integer.getInteger("deoptNum");
+        if (deoptNum == null) {
+            deoptNum = rand.nextInt(deopt.length);
+        }
+        for (int x = 0; x <= 50_000; ++x) {
+            if (x == 50_000) {
+                // Last iteration, trigger deoptimization
+                deopt[deoptNum] = true;
+            }
+            Asserts.assertEQ(test1(x), x);
+            Asserts.assertEQ(test2(x), new MyValue1(x));
+            Asserts.assertEQ(test3(10), new MyValue1(10));
+            Asserts.assertEQ(test4(x), new MyValue1(x));
+            Asserts.assertEQ(test5(x), x);
+            Asserts.assertEQ(test6(x), new MyValue2(x));
+            Asserts.assertEQ(test7(10), new MyValue2(10));
+            Asserts.assertEQ(test8(x), new MyValue2(x));
+            Asserts.assertEQ(test9(x), x);
+            Asserts.assertEQ(test10(x), new MyValue3(x));
+            Asserts.assertEQ(test11(10), new MyValue3(10));
+            Asserts.assertEQ(test12(x), new MyValue3(x));
+            Asserts.assertEQ(test13(x), new MyValue4(x));
+            Asserts.assertEQ(test14(x, (x % 2) == 0), new MyValue5(x, (x % 2) == 0));
+            Asserts.assertEQ(test15(x), new MyValue6(x));
+            Asserts.assertEQ(test16(x), new MyValue7(x));
+            Asserts.assertEQ(test17(x), new MyValue8(x));
+            Asserts.assertEQ(test18(x), new MyValue8(x));
+            Asserts.assertEQ(test19(x), new MyValue8(x));
+            Asserts.assertEQ(test20(x), new MyValue9(x));
+            Asserts.assertEQ(test21(x), new MyValue9(x));
+            Asserts.assertEQ(test22(x), new MyValue9(x));
+            Asserts.assertEQ(test23(x), new MyValue9(x));
+            Asserts.assertEQ(test24(x, x % 10), new MyValue10(x, x % 10));
+            Asserts.assertEQ(test25(x), new MyValue11(x));
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueRematDuringTypeSharpening.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueRematDuringTypeSharpening.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueRematDuringTypeSharpening.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+package compiler.valhalla.inlinetypes;
+
+import jdk.test.lib.Asserts;
+import jdk.internal.misc.Unsafe;
+import compiler.lib.ir_framework.*;
+
+/*
+ * @test
+ * @summary Missing InlineTypeNode re-materialization during type sharpening.
+ * @library /test/lib /
+ * @enablePreview
+ * @modules java.base/jdk.internal.misc
+ * @run main/othervm/timeout=300 compiler.valhalla.inlinetypes.TestValueRematDuringTypeSharpening
+ */
+
+abstract value class topValue {
+}
+
+value class dummyValue1 extends topValue {
+   int field;
+   public dummyValue1(int val) {
+      field = val;
+   }
+}
+
+value class dummyValue2 extends topValue {
+   int field;
+   public dummyValue2(int val) {
+      field = val;
+   }
+}
+
+public class TestValueRematDuringTypeSharpening {
+
+   public static final Unsafe UNSAFE = Unsafe.getUnsafe();
+
+   @DontInline
+   public static int getUnsafeFieldValue(topValue obj, int incr) {
+       return UNSAFE.getInt(obj, 12) + incr;
+   }
+
+   @Test
+   @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {IRNode.INLINE_TYPE, " > 0 "})
+   public static int test(topValue obj) {
+       int val = 0;
+       if (obj.getClass() != dummyValue1.class) {
+           val += 10;
+       } else if (obj.getClass() != dummyValue2.class) {
+           val += 20;
+       }
+       return getUnsafeFieldValue(obj, val);
+   }
+
+   @Run(test = {"test"}, mode = RunMode.NORMAL)
+   public static void kernel() {
+       test(new dummyValue1(10));
+       test(new dummyValue2(20));
+   }
+
+   public static void main(String [] args) {
+       TestFramework.runWithFlags("-XX:-TieredCompilation", "--enable-preview", "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED");
+       System.out.println("PASS");
+   }
+}
+
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestWithSpeculativeTypes.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestWithSpeculativeTypes.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestWithSpeculativeTypes.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2022, 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
+ * @bug 8280440
+ * @summary Test that speculative types are properly handled by scalarization.
+ * @library /test/lib
+ * @enablePreview
+ * @run main/othervm -XX:CompileCommand=dontinline,TestWithSpeculativeTypes::*
+ *                   -XX:TypeProfileLevel=222 -XX:-TieredCompilation -Xbatch
+ *                   TestWithSpeculativeTypes
+ */
+
+import jdk.test.lib.Asserts;
+
+public class TestWithSpeculativeTypes {
+
+    static value class MyValue {
+        int x = 0;
+    }
+
+    static MyValue getNull() {
+        return null;
+    }
+
+    // Return value has speculative type NULL
+    static boolean test1() {
+        return getNull() == null;
+    }
+
+    // Argument has speculative type NULL
+    static boolean test2(MyValue vt) {
+        return vt == null;
+    }
+
+    public static void main(String[] args) {
+        // Make sure class is loaded
+        MyValue val = new MyValue();
+        for (int i = 0; i < 100_000; ++i) {
+            Asserts.assertTrue(test1());
+            Asserts.assertTrue(test2(null));
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestWrongFlatArrayCopyStubWithZGC.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestWrongFlatArrayCopyStubWithZGC.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestWrongFlatArrayCopyStubWithZGC.java
@@ -0,0 +1,106 @@
+/*
+ * 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
+ * @bug 8313667
+ * @summary Test that GenZ uses correct array copy stub for flat value class arrays when expanding ArrayCopyNode.
+ * @requires vm.gc.ZSinglegen
+ * @library /test/lib
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @run main/othervm -Xbatch -XX:+UseZGC -XX:-ZGenerational
+ *                   -XX:CompileCommand=exclude,compiler.valhalla.inlinetypes.TestWrongFlatArrayCopyStubWithZGC::check
+ *                   -XX:CompileCommand=dontinline,compiler.valhalla.inlinetypes.TestWrongFlatArrayCopyStubWithZGC::test*
+ *                   compiler.valhalla.inlinetypes.TestWrongFlatArrayCopyStubWithZGC
+ */
+
+package compiler.valhalla.inlinetypes;
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.Utils;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+public class TestWrongFlatArrayCopyStubWithZGC {
+
+    public static void main(String[] args) {
+        ValueWithLong[] arrWithLong = (ValueWithLong[])ValueClass.newNullRestrictedArray(ValueWithLong.class, 3);
+        arrWithLong[0] = new ValueWithLong(0x408BE000000fffffL);
+        arrWithLong[1] = new ValueWithLong(0x408BE0000000000L);
+        long randomValue = Utils.getRandomInstance().nextLong();
+        arrWithLong[2] = new ValueWithLong(randomValue);
+
+        for (int i = 0; i < 10000; i++) {
+            ValueWithLong[] result = testLong(arrWithLong);
+            check(result[0].l, 0x408BE000000fffffL);
+            check(result[1].l, 0x408BE0000000000L);
+            check(result[2].l, randomValue);
+        }
+
+        ValueWithOop[] arrWithOop = (ValueWithOop[])ValueClass.newNullRestrictedArray(ValueWithOop.class, 2);
+        arrWithOop[0] = new ValueWithOop();
+        arrWithOop[1] = new ValueWithOop();
+
+        for (int i = 0; i < 10000; i++) {
+            testOop(arrWithOop);
+        }
+    }
+
+    static void check(long result, long expected) {
+        Asserts.assertEQ(result, expected);
+    }
+
+    static ValueWithLong[] testLong(ValueWithLong[] arr) {
+        return arr.clone();
+    }
+
+    static ValueWithOop[] testOop(ValueWithOop[] arr) {
+        return arr.clone();
+    }
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class ValueWithLong {
+    long l;
+
+    public ValueWithLong(long l) {
+        this.l = l;
+    }
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class ValueWithOop {
+    Object v;
+
+    public ValueWithOop() {
+        this.v = new Object();
+    }
+}
+
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/bootstrap/InstallBootstrapClasses.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/bootstrap/InstallBootstrapClasses.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/bootstrap/InstallBootstrapClasses.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2022, 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.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+
+import jdk.test.lib.Utils;
+
+// Copy classes into a separate folder to put them on the bootclasspath
+public class InstallBootstrapClasses {
+
+    private static void copyClass(String name) throws IOException {
+        Path source = Path.of(Utils.TEST_CLASSES).resolve(name);
+        Path dest = Path.of("boot");
+        Path target = dest.resolve(name);
+        Files.createDirectories(dest);
+        Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
+    }
+
+    public static void main(String[] args) throws IOException {
+        copyClass(ValueOnBootclasspath.class.getSimpleName() + ".class");
+        copyClass(MyClass.class.getSimpleName() + ".class");
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/bootstrap/TestBootClassloader.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/bootstrap/TestBootClassloader.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/bootstrap/TestBootClassloader.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2022, 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.reflect.Method;
+import jdk.test.lib.Asserts;
+import jdk.test.whitebox.WhiteBox;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+/*
+ * @test
+ * @key randomness
+ * @bug 8280006
+ * @summary Test that field flattening works as expected if value classes of
+ *          holder and field were loaded by different class loaders (bootstrap + app).
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @compile ValueOnBootclasspath.java InstallBootstrapClasses.java TestBootClassloader.java
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm InstallBootstrapClasses
+ * @run main/othervm -Xbootclasspath/a:boot -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   -Xbatch -XX:-TieredCompilation -XX:CompileCommand=compileonly,TestBootClassloader::test*
+ *                   -XX:CompileCommand=inline,*::get* TestBootClassloader
+ */
+
+public class TestBootClassloader {
+    private static final WhiteBox WB = WhiteBox.getWhiteBox();
+    private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4;
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Wrapper1 {
+        @NullRestricted
+        ValueOnBootclasspath val; // Type will be loaded by boot classloader
+
+        public Wrapper1(ValueOnBootclasspath val) {
+            this.val = val;
+        }
+
+        Object get() {
+            return val.get();
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Wrapper2 {
+        @NullRestricted
+        Wrapper1 val;
+
+        public Wrapper2(Wrapper1 val) {
+            this.val = val;
+        }
+
+        Object get() {
+            return val.get();
+        }
+    }
+
+    static Object test1(Wrapper1 w) {
+        return w.get();
+    }
+
+    static Object test2(Wrapper2 w) {
+        return w.get();
+    }
+
+    public static void main(String[] args) throws Exception {
+        Wrapper1 wrapper1 = new Wrapper1(new ValueOnBootclasspath());
+        Wrapper2 wrapper2 = new Wrapper2(wrapper1);
+        for (int i = 0; i < 50_000; ++i) {
+            test1(wrapper1);
+            test2(wrapper2);
+        }
+        Method method = TestBootClassloader.class.getDeclaredMethod("test1", Wrapper1.class);
+        Asserts.assertTrue(WB.isMethodCompilable(method, COMP_LEVEL_FULL_OPTIMIZATION, false), "Test1 method not compilable");
+        Asserts.assertTrue(WB.isMethodCompiled(method), "Test1 method not compiled");
+
+        method = TestBootClassloader.class.getDeclaredMethod("test2", Wrapper2.class);
+        Asserts.assertTrue(WB.isMethodCompilable(method, COMP_LEVEL_FULL_OPTIMIZATION, false), "Test2 method not compilable");
+        Asserts.assertTrue(WB.isMethodCompiled(method), "Test2 method not compiled");
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/bootstrap/ValueOnBootclasspath.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/bootstrap/ValueOnBootclasspath.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/bootstrap/ValueOnBootclasspath.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2022, 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.
+ */
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+
+class MyClass {
+
+}
+
+// Loaded by boot classloader
+@ImplicitlyConstructible
+@LooselyConsistentValue
+public value class ValueOnBootclasspath {
+    private MyClass field = new MyClass();
+
+    private MyClass getField() {
+        return field;
+    }
+
+    public Object get() {
+        return getField();
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/hack/GetUnresolvedInlineFieldWrongSignature.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/hack/GetUnresolvedInlineFieldWrongSignature.java
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/hack/GetUnresolvedInlineFieldWrongSignature.java
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ *
+ * 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 compiler.valhalla.inlinetypes;
+
+class TestUnloadedInlineTypeField {
+    static class MyValue3 {
+        int foo;
+    }
+
+    static class MyValue3Holder {
+        MyValue3 v;
+    }
+
+    static class MyValue10 {
+        int foo;
+    }
+
+    static class MyValue10Holder {
+        MyValue10 v1;
+        MyValue10 v2;
+    }
+
+    static class MyValue13 {
+        int foo;
+    }
+
+    static class MyValue13Holder {
+        MyValue13 v;
+    }
+
+    static class MyValue14 {
+        int foo;
+    }
+
+    static class MyValue14Holder {
+        MyValue14 v;
+    }
+
+    static class MyValue15 {
+        int foo;
+    }
+
+    static class MyValue15Holder {
+        MyValue15 v;
+    }
+
+    static value class MyValue16 {
+        int foo = 42;
+    }
+
+    static value class MyValue17 {
+        int foo = 42;
+    }
+}
+
+class GetUnresolvedInlineFieldWrongSignature {
+
+    static void test13(Object holder) {
+        if (holder != null) {
+            // Don't use MyValue13Holder in the signature, it might trigger class loading
+            ((TestUnloadedInlineTypeField.MyValue13Holder)holder).v = new TestUnloadedInlineTypeField.MyValue13();
+        }
+    }
+
+    static void test15(Object holder) {
+        if (holder != null) {
+            // Don't use MyValue15Holder in the signature, it might trigger class loading
+            ((TestUnloadedInlineTypeField.MyValue15Holder)holder).v = new TestUnloadedInlineTypeField.MyValue15();
+        }
+    }
+
+    static Object test16(boolean warmup) {
+        if (!warmup) {
+            return new TestUnloadedInlineTypeField.MyValue16();
+        } else {
+            return null;
+        }
+    }
+
+    static Object test17(boolean warmup) {
+        if (!warmup) {
+            return new TestUnloadedInlineTypeField.MyValue17();
+        } else {
+            return null;
+        }
+    }
+}
diff a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/libTestJNICalls.c b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/libTestJNICalls.c
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/libTestJNICalls.c
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ *
+ * 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 <jni.h>
+
+JNIEXPORT jobject JNICALL
+Java_compiler_valhalla_inlinetypes_TestJNICalls_testMethod1(JNIEnv *env, jobject receiver, jobject vt) {
+    jclass cls = (*env)->GetObjectClass(env, receiver);
+    jmethodID mid = (*env)->GetMethodID(env, cls, "test1", "(Lcompiler/valhalla/inlinetypes/MyValue1;Z)Lcompiler/valhalla/inlinetypes/MyValue1;");
+    return (*env)->CallObjectMethod(env, receiver, mid, vt, JNI_TRUE);
+}
+
+JNIEXPORT jlong JNICALL
+Java_compiler_valhalla_inlinetypes_TestJNICalls_testMethod2(JNIEnv *env, jobject receiver, jobject vt) {
+    jclass cls = (*env)->GetObjectClass(env, vt);
+    jmethodID mid = (*env)->GetMethodID(env, cls, "hash", "()J");
+    return (*env)->CallLongMethod(env, vt, mid);
+}
+
+JNIEXPORT jint JNICALL
+Java_compiler_valhalla_inlinetypes_TestJNICalls_00024MyValueWithNative_testMethod3(JNIEnv *env, jobject receiver) {
+    jclass cls = (*env)->GetObjectClass(env, receiver);
+    jfieldID fid = (*env)->GetFieldID(env, cls, "x", "I");
+    return (*env)->GetIntField(env, receiver, fid);
+}
diff a/test/hotspot/jtreg/gc/TestBigObj.java b/test/hotspot/jtreg/gc/TestBigObj.java
--- a/test/hotspot/jtreg/gc/TestBigObj.java
+++ b/test/hotspot/jtreg/gc/TestBigObj.java
@@ -25,10 +25,11 @@
 
 /*
  * @test TestBigObj
  * @bug 6845368
  * @summary ensure gc updates references > 64K bytes from the start of the obj
+ * @compile -XDnoTopInterfaceInjection TestBigObj.java
  * @run main/othervm/timeout=720 -Xmx256m -verbose:gc gc.TestBigObj
  */
 
 // Allocate an object with a block of reference fields that starts more
 // than 64K bytes from the start of the object.  This is done with
diff a/test/hotspot/jtreg/gc/stress/gcbasher/Bytecode.java b/test/hotspot/jtreg/gc/stress/gcbasher/Bytecode.java
--- a/test/hotspot/jtreg/gc/stress/gcbasher/Bytecode.java
+++ b/test/hotspot/jtreg/gc/stress/gcbasher/Bytecode.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2022, 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.
@@ -40,10 +40,12 @@
     public static final int ANEWARRAY          = 189;
     public static final int CHECKCAST          = 192;
     public static final int INSTANCEOF         = 193;
     public static final int MULTIANEWARRAY     = 197;
     public static final int WIDE               = 196;
+    public static final int ACONSTINIT         = 203;
+    public static final int WITHFIELD          = 204;
 
     private static final int lengths[] = {
         1,
         1,
         1,
@@ -244,11 +246,13 @@
         4,
         3,
         3,
         5,
         5,
-        1
+        1,
+        3,
+        3
     };
 
     public static int getLength(int bc) throws IllegalArgumentException {
         if ((bc < 0) || (bc >= lengths.length)) {
             throw new IllegalArgumentException("Unknown bytecode " + bc);
diff a/test/hotspot/jtreg/runtime/ClassFile/ClassAccessFlagsRawTest.java b/test/hotspot/jtreg/runtime/ClassFile/ClassAccessFlagsRawTest.java
--- a/test/hotspot/jtreg/runtime/ClassFile/ClassAccessFlagsRawTest.java
+++ b/test/hotspot/jtreg/runtime/ClassFile/ClassAccessFlagsRawTest.java
@@ -21,14 +21,16 @@
  * questions.
  *
  */
 
 /**
+ * @ignore Fix 8328162
  * @test
- * @bug 8291360
+ * @bug 8291360 8293448
  * @summary Test getting a class's raw access flags using java.lang.Class API
  * @modules java.base/java.lang:open
+ * @enablePreview
  * @compile classAccessFlagsRaw.jcod
  * @run main/othervm ClassAccessFlagsRawTest
  */
 
 import java.lang.reflect.*;
@@ -49,30 +51,33 @@
     public static void main(String argv[]) throws Throwable {
         Class<?> cl = java.lang.Class.class;
         m = cl.getDeclaredMethod("getClassAccessFlagsRaw", new Class[0]);
         m.setAccessible(true);
 
-        testIt("SUPERset", 0x21);  // ACC_SUPER 0x20 + ACC_PUBLIC 0x1
-        testIt("SUPERnotset", Modifier.PUBLIC);
+        testIt("SUPERset", Modifier.PUBLIC | Modifier.IDENTITY);
+        // Because of the repurposing of ACC_SUPER into ACC_IDENTITY by JEP 401, the VM now fixes missing ACC_IDENTITY flags in old class files
+        testIt("SUPERnotset", Modifier.PUBLIC | Modifier.IDENTITY);
 
         // test primitive array.  should return ACC_ABSTRACT | ACC_FINAL | ACC_PUBLIC.
         int flags = (int)m.invoke((new int[3]).getClass());
         if (flags != (Modifier.ABSTRACT | Modifier.FINAL | Modifier.PUBLIC)) {
             throw new RuntimeException(
                 "expected 0x411, got 0x" + Integer.toHexString(flags) + " for primitive array");
         }
 
         // test object array.  should return flags of component.
         flags = (int)m.invoke((new SUPERnotset[2]).getClass());
-        if (flags != Modifier.PUBLIC) {
+        // Because of the repurposing of ACC_SUPER into ACC_IDENTITY by JEP 401, the VM now fixes missing ACC_IDENTITY flags in old class files
+        if (flags != (Modifier.PUBLIC | Modifier.IDENTITY)) {
             throw new RuntimeException(
                 "expected 0x1, got 0x" + Integer.toHexString(flags) + " for object array");
         }
 
         // test multi-dimensional object array.  should return flags of component.
         flags = (int)m.invoke((new SUPERnotset[4][2]).getClass());
-        if (flags != Modifier.PUBLIC) {
+        // Because of the repurposing of ACC_SUPER into ACC_IDENTITY by JEP 401, the VM now fixes missing ACC_IDENTITY flags in old class files
+        if (flags != (Modifier.PUBLIC | Modifier.IDENTITY)) {
             throw new RuntimeException(
                 "expected 0x1, got 0x" + Integer.toHexString(flags) + " for object array");
         }
     }
 }
diff a/test/hotspot/jtreg/runtime/ErrorHandling/MachCodeFramesInErrorFile.java b/test/hotspot/jtreg/runtime/ErrorHandling/MachCodeFramesInErrorFile.java
--- a/test/hotspot/jtreg/runtime/ErrorHandling/MachCodeFramesInErrorFile.java
+++ b/test/hotspot/jtreg/runtime/ErrorHandling/MachCodeFramesInErrorFile.java
@@ -161,11 +161,11 @@
             System.out.println("Could not find \"" + preCodeBlobSectionHeader + "\" in " + hsErrPath);
             System.out.println("Looks like hs-err is truncated - exiting with success");
             return;
         }
 
-        Matcher matcher = Pattern.compile("\\[MachCode\\]\\s*\\[Verified Entry Point\\]\\s*  # \\{method\\} \\{[^}]*\\} '([^']+)' '([^']+)' in '([^']+)'", Pattern.DOTALL).matcher(hsErr);
+        Matcher matcher = Pattern.compile("\\[MachCode\\]\\s[^{]+\\{method\\} \\{[^}]*\\} '([^']+)' '([^']+)' in '([^']+)'", Pattern.DOTALL).matcher(hsErr);
         List<String> machCodeHeaders = matcher.results().map(mr -> String.format("'%s' '%s' in '%s'", mr.group(1), mr.group(2), mr.group(3))).collect(Collectors.toList());
         int minExpectedMachCodeSections = Math.max(1, compiledJavaFrames);
         if (machCodeHeaders.size() < minExpectedMachCodeSections) {
             Asserts.fail(machCodeHeaders.size() + " < " + minExpectedMachCodeSections);
         }
diff a/test/hotspot/jtreg/runtime/cds/appcds/ArchiveRelocationTest.java b/test/hotspot/jtreg/runtime/cds/appcds/ArchiveRelocationTest.java
--- a/test/hotspot/jtreg/runtime/cds/appcds/ArchiveRelocationTest.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/ArchiveRelocationTest.java
@@ -66,16 +66,16 @@
         String logArg = "-Xlog:cds=debug,cds+reloc=debug,cds+heap";
         String unlockArg = "-XX:+UnlockDiagnosticVMOptions";
         String nmtArg = "-XX:NativeMemoryTracking=detail";
 
         OutputAnalyzer out = TestCommon.dump(appJar,
-                                             TestCommon.list(mainClass),
-                                             unlockArg, logArg, nmtArg);
+                TestCommon.list(mainClass),
+                unlockArg, logArg, nmtArg);
         out.shouldContain("Relocating archive from");
 
         TestCommon.run("-cp", appJar, unlockArg, runRelocArg, logArg,  mainClass)
-            .assertNormalExit(output -> {
+                .assertNormalExit(output -> {
                     if (run_reloc) {
                         output.shouldContain("Try to map archive(s) at an alternative address");
                     }
                 });
     }
diff a/test/hotspot/jtreg/runtime/cds/appcds/HelloInlineClassTest.java b/test/hotspot/jtreg/runtime/cds/appcds/HelloInlineClassTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/HelloInlineClassTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ *
+ * 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 Hello World test for using inline classes with CDS
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @compile test-classes/HelloInlineClassApp.java
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar hello_inline.jar
+ *          HelloInlineClassApp HelloInlineClassApp$Point
+ *          HelloInlineClassApp$Rectangle HelloInlineClassApp$ValueRecord
+ * @run main/othervm HelloInlineClassTest
+ */
+
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.process.OutputAnalyzer;
+public class HelloInlineClassTest {
+    public static void main(String[] args) throws Exception {
+        String appJar = ClassFileInstaller.getJarPath("hello_inline.jar");
+        String mainClass = "HelloInlineClassApp";
+        OutputAnalyzer output =
+            TestCommon.dump(appJar, TestCommon.list(mainClass,
+                                                    "HelloInlineClassApp$Point",
+                                                    "HelloInlineClassApp$Rectangle"),
+                            "--enable-preview");
+        output.shouldHaveExitValue(0);
+
+        TestCommon.run("--enable-preview",
+                       "-Xint", "-cp", appJar,  mainClass)
+            .assertNormalExit();
+
+        TestCommon.run("--enable-preview",
+                       "-cp", appJar,  mainClass)
+            .assertNormalExit();
+
+        String compFlag = "-XX:CompileCommand=compileonly,HelloInlineClassApp*::*";
+
+        TestCommon.run("--enable-preview",
+                       "-Xcomp", compFlag,
+                       "-cp", appJar,  mainClass)
+            .assertNormalExit();
+
+        TestCommon.run("--enable-preview",
+                       "-Xcomp", compFlag,
+                       "-XX:TieredStopAtLevel=1",
+                       "-XX:+TieredCompilation",
+                       "-XX:-Inline",
+                       "-cp", appJar,  mainClass)
+            .assertNormalExit();
+
+        TestCommon.run("--enable-preview",
+                       "-Xcomp", compFlag,
+                       "-XX:TieredStopAtLevel=4",
+                       "-XX:-TieredCompilation",
+                       "-XX:-Inline",
+                       "-cp", appJar,  mainClass)
+            .assertNormalExit();
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/RewriteBytecodesInlineTest.java b/test/hotspot/jtreg/runtime/cds/appcds/RewriteBytecodesInlineTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/RewriteBytecodesInlineTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, 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 Use Lookup.defineClass() to load a class with rewritten bytecode. Make sure
+ *          the archived class with the same name is not loaded.
+ * @requires vm.cds
+ * @library /test/lib
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @compile test-classes/RewriteBytecodesInline.java test-classes/Util.java test-classes/Point.java test-classes/WithInlinedField.java RewriteBytecodesInlineTest.java
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run driver RewriteBytecodesInlineTest
+ */
+
+import java.io.File;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class RewriteBytecodesInlineTest {
+  public static void main(String[] args) throws Exception {
+    String wbJar = JarBuilder.build(true, "WhiteBox", "jdk/test/whitebox/WhiteBox");
+    String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar;
+
+    String appJar = JarBuilder.build("dynamic_define", "RewriteBytecodesInline", "Util", "Point", "WithInlinedField");
+    String superClsFile = (new File(System.getProperty("test.classes", "."), "Point.class")).getPath();
+
+    TestCommon.dump(appJar, TestCommon.list("RewriteBytecodesInline", "Point", "WithInlinedField"),
+                    // command-line arguments ...
+                    use_whitebox_jar,
+                    "--enable-preview");
+
+    OutputAnalyzer output = TestCommon.exec(appJar,
+                    // command-line arguments ...
+                    "--enable-preview",
+                    use_whitebox_jar,
+                    "-XX:+UnlockDiagnosticVMOptions",
+                    "-XX:+WhiteBoxAPI",
+                    "RewriteBytecodesInline", superClsFile);
+    TestCommon.checkExec(output);
+  }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/condy/CondyHello.jasm b/test/hotspot/jtreg/runtime/cds/appcds/condy/CondyHello.jasm
--- a/test/hotspot/jtreg/runtime/cds/appcds/condy/CondyHello.jasm
+++ b/test/hotspot/jtreg/runtime/cds/appcds/condy/CondyHello.jasm
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2022, 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,11 +23,11 @@
 
 /*
  * This test uses ldc to load an integer value via a condy bootstrap method.
  */
 
-class CondyHello
+super class CondyHello
         version 55:0
 {
 
 public static Method condyBSM:"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;"
         throws java/lang/Throwable
diff a/test/hotspot/jtreg/runtime/cds/appcds/customLoader/ClassListFormatE.java b/test/hotspot/jtreg/runtime/cds/appcds/customLoader/ClassListFormatE.java
--- a/test/hotspot/jtreg/runtime/cds/appcds/customLoader/ClassListFormatE.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/customLoader/ClassListFormatE.java
@@ -73,11 +73,11 @@
             "TESTCASE E3: specifying an interface that's not implemented by the class",
             appJar, classlist(
                 "Hello",
                 "java/lang/Object id: 1",
                 "CustomInterface2_ia id: 2 super: 1 source: " + customJarPath,
-                "CustomLoadee id: 2 super: 1 interfaces: 2 source: " + customJarPath
+                "CustomLoadee id: 3 super: 1 interfaces: 2 source: " + customJarPath
             ),
             "The number of interfaces (1) specified in class list does not match the class file (0)");
 
         dumpShouldFail(
             "TESTCASE E4: repeating an ID in the interfaces: keyword",
diff a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DynamicArchiveRelocationTest.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DynamicArchiveRelocationTest.java
--- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DynamicArchiveRelocationTest.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DynamicArchiveRelocationTest.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2019, 2022, 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.
@@ -28,13 +28,16 @@
  * @requires vm.cds
  * @summary Testing relocation of dynamic CDS archive (during both dump time and run time)
  * @comment JDK-8231610 Relocate the CDS archive if it cannot be mapped to the requested address
  * @bug 8231610
  * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/test-classes
- * @build Hello
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @compile ../test-classes/HelloInlineClassApp.java ../test-classes/HelloRelocation.java
  * @build jdk.test.whitebox.WhiteBox
- * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar hello.jar Hello
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar hello.jar HelloRelocation HelloInlineClassApp HelloInlineClassApp$Point HelloInlineClassApp$Rectangle  HelloInlineClassApp$ValueRecord
  * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
  * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. DynamicArchiveRelocationTest
  */
 
 import jdk.test.lib.process.OutputAnalyzer;
@@ -76,11 +79,11 @@
                            + ", top_reloc = " + dump_top_reloc
                            + ", run = " + run_reloc);
         System.out.println("============================================================");
 
         String appJar = ClassFileInstaller.getJarPath("hello.jar");
-        String mainClass = "Hello";
+        String mainClass = "HelloRelocation";
         String forceRelocation = "-XX:ArchiveRelocationMode=1";
         String dumpTopRelocArg  = dump_top_reloc  ? forceRelocation : "-showversion";
         String runRelocArg      = run_reloc       ? forceRelocation : "-showversion";
         String logArg = "-Xlog:cds=debug,cds+reloc=debug";
 
@@ -90,16 +93,17 @@
         String runtimeMsg = "Try to map archive(s) at an alternative address";
         String unlockArg = "-XX:+UnlockDiagnosticVMOptions";
 
         // (1) Dump base archive (static)
 
-        TestCommon.dumpBaseArchive(baseArchiveName, unlockArg, logArg)
+        TestCommon.dumpBaseArchive(baseArchiveName, "--enable-preview", unlockArg, logArg)
           .shouldContain("Relocating archive from");
 
         // (2) Dump top archive (dynamic)
 
         dump2(baseArchiveName, topArchiveName,
+              "--enable-preview",
               unlockArg,
               dumpTopRelocArg,
               logArg,
               "-cp", appJar, mainClass)
             .assertNormalExit(output -> {
@@ -107,10 +111,11 @@
                         output.shouldContain(runtimeMsg);
                     }
                 });
 
         run2(baseArchiveName, topArchiveName,
+             "--enable-preview",
              unlockArg,
              runRelocArg,
              logArg,
             "-cp", appJar, mainClass)
             .assertNormalExit(output -> {
diff a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/HelloDynamicInlineClass.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/HelloDynamicInlineClass.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/HelloDynamicInlineClass.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ *
+ * 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 Hello World test for dynamic archive
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/test-classes
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @compile ../test-classes/HelloInlineClassApp.java
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar hello_inline.jar HelloInlineClassApp HelloInlineClassApp$Point HelloInlineClassApp$Rectangle HelloInlineClassApp$ValueRecord
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox jdk.test.whitebox.WhiteBox$WhiteBoxPermission
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. HelloDynamicInlineClass
+ */
+
+import jdk.test.lib.helpers.ClassFileInstaller;
+
+public class HelloDynamicInlineClass extends DynamicArchiveTestBase {
+    public static void main(String[] args) throws Exception {
+        runTest(HelloDynamicInlineClass::test);
+    }
+
+    static void test() throws Exception {
+        String topArchiveName = getNewArchiveName("top");
+        String baseArchiveName = getNewArchiveName("base");
+        TestCommon.dumpBaseArchive(baseArchiveName, "--enable-preview", "-Xlog:cds");
+        doTest(baseArchiveName, topArchiveName);
+    }
+
+    private static void doTest(String baseArchiveName, String topArchiveName) throws Exception {
+        String appJar = ClassFileInstaller.getJarPath("hello_inline.jar");
+        String mainClass = "HelloInlineClassApp";
+        dump2(baseArchiveName, topArchiveName,
+             "--enable-preview",
+             "-Xlog:cds",
+             "-Xlog:cds+dynamic=debug",
+             "-cp", appJar, mainClass)
+            .assertNormalExit(output -> {
+                    output.shouldContain("Written dynamic archive 0x");
+                });
+        run2(baseArchiveName, topArchiveName,
+            "--enable-preview",
+            "-Xlog:class+load",
+            "-Xlog:cds+dynamic=debug,cds=debug",
+            "-cp", appJar, mainClass)
+            .assertNormalExit(output -> {
+                    output.shouldContain("HelloInlineClassApp$Point source: shared objects file")
+                          .shouldHaveExitValue(0);
+              });
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/javaldr/LockDuringDump.java b/test/hotspot/jtreg/runtime/cds/appcds/javaldr/LockDuringDump.java
--- a/test/hotspot/jtreg/runtime/cds/appcds/javaldr/LockDuringDump.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/javaldr/LockDuringDump.java
@@ -55,11 +55,11 @@
                                         agentClasses);
 
         String appJar =
             ClassFileInstaller.writeJar("LockDuringDumpApp.jar", appClasses);
 
-        for (int i = 0; i < 3; i++) {
+        for (int i = 0; i < 2; i++) {
             // i = 0 -- dump without agent
             // i = 1 -- dump with agent
 
             String agentArg   = (i == 0) ? "-showversion" : "-javaagent:" + agentJar;
             String agentArg2  = (i == 0) ? "-showversion" : "-XX:+AllowArchivingWithJavaAgent";
diff a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/ModuleOption.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/ModuleOption.java
--- a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/ModuleOption.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/ModuleOption.java
@@ -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.
@@ -38,10 +38,11 @@
         final String moduleOption = "jdk.httpserver/sun.net.httpserver.simpleserver.Main";
         final String incubatorModule = "jdk.incubator.vector";
         final String loggingOption = "-Xlog:cds=debug,cds+module=debug,cds+heap=info,module=trace";
         final String versionPattern = "java.[0-9][0-9][-].*";
         final String subgraphCannotBeUsed = "subgraph jdk.internal.module.ArchivedBootLayer cannot be used because full module graph is disabled";
+        final String noOptimizedModuleHandling = "optimized module handling: disabled because archive was created without optimized module handling";
         String archiveName = TestCommon.getNewArchiveName("module-option");
         TestCommon.setCurrentArchiveName(archiveName);
 
         // dump a base archive with -m jdk.httpserver
         OutputAnalyzer oa = TestCommon.dumpBaseArchive(
@@ -56,29 +57,35 @@
             loggingOption,
             "-m", moduleOption,
             "-version");
         oa.shouldHaveExitValue(0)
           // version of the jdk.httpserver module, e.g. java 22-ea
-          .shouldMatch(versionPattern)
-          .shouldMatch("cds,module.*Restored from archive: entry.0x.*name jdk.httpserver");
+          .shouldMatch(versionPattern);
+        if (!oa.contains(noOptimizedModuleHandling)) {
+            oa.shouldMatch("cds,module.*Restored from archive: entry.0x.*name jdk.httpserver");
+        }
 
         // different module specified during runtime
         oa = TestCommon.execCommon(
             loggingOption,
             "-m", "jdk.compiler/com.sun.tools.javac.Main",
             "-version");
         oa.shouldHaveExitValue(0)
-          .shouldContain("Mismatched modules: runtime jdk.compiler dump time jdk.httpserver")
-          .shouldContain(subgraphCannotBeUsed);
+          .shouldContain("Mismatched modules: runtime jdk.compiler dump time jdk.httpserver");
+        if (!oa.contains(noOptimizedModuleHandling)) {
+            oa.shouldContain(subgraphCannotBeUsed);
+        }
 
         // no module specified during runtime
         oa = TestCommon.execCommon(
             loggingOption,
             "-version");
         oa.shouldHaveExitValue(0)
-          .shouldContain("Module jdk.httpserver specified during dump time but not during runtime")
-          .shouldContain(subgraphCannotBeUsed);
+          .shouldContain("Module jdk.httpserver specified during dump time but not during runtime");
+        if (!oa.contains(noOptimizedModuleHandling)) {
+            oa.shouldContain(subgraphCannotBeUsed);
+        }
 
         // dump an archive without the module option
         archiveName = TestCommon.getNewArchiveName("no-module-option");
         TestCommon.setCurrentArchiveName(archiveName);
         oa = TestCommon.dumpBaseArchive(
@@ -93,12 +100,14 @@
             "-m", moduleOption,
             "-version");
         oa.shouldHaveExitValue(0)
           .shouldContain("Module jdk.httpserver specified during runtime but not during dump time")
           // version of the jdk.httpserver module, e.g. java 22-ea
-          .shouldMatch(versionPattern)
-          .shouldContain(subgraphCannotBeUsed);
+          .shouldMatch(versionPattern);
+        if (!oa.contains(noOptimizedModuleHandling)) {
+            oa.shouldContain(subgraphCannotBeUsed);
+        }
 
         // dump an archive with an incubator module, -m jdk.incubator.vector
         archiveName = TestCommon.getNewArchiveName("incubator-module");
         TestCommon.setCurrentArchiveName(archiveName);
         oa = TestCommon.dumpBaseArchive(
diff a/test/hotspot/jtreg/runtime/cds/appcds/test-classes/HelloInlineClassApp.java b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/HelloInlineClassApp.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/HelloInlineClassApp.java
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ *
+ * 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 jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+public class HelloInlineClassApp {
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Point {
+        int x, y;
+
+        public String toString() {
+            return "(" + x + ", " + y + ")";
+        }
+
+        public Point(int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+
+        Point add(Point p1) {
+            return new Point(x + p1.x, y + p1.y);
+        }
+
+        Point add(Point p1, Point p2) {
+            return new Point(x + p1.x + p2.x, y + p1.y + p2.y);
+        }
+
+        Point add(Point p1, int x2, int y2, Point p3) {
+            return new Point(x + p1.x + x2 + p3.x, y + p1.y + y2 + p3.y);
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Rectangle {
+        @NullRestricted
+        Point p0 = new Point(0,0);
+        @NullRestricted
+        Point p1 = new Point(1,1);
+    }
+
+    @NullRestricted
+    Point point;
+
+    @NullRestricted
+    static Rectangle rectangle;
+
+    static value record ValueRecord(int i, String name) {}
+
+    public static void main(String[] args) throws Exception {
+        Point p = new Point(0, 123);
+        System.out.println("Point = " + p);
+        String req = "(0, 123)";
+        if (!p.toString().equals(req)) {
+            throw new RuntimeException("Expected " + req + " but got " + p);
+        }
+
+        Point p1 = new Point(1, 1);
+        Point p2 = new Point(2, 2);
+        Point p3 = new Point(3, 3);
+        int x2 = 200;
+        int y2 = 200;
+
+        int loops = 100000;
+        for (int i=0; i<loops; i++) {
+            p = p.add(p1);
+            p = p.add(p1, p2);
+            p = p.add(p1, x2, y2, p3);
+        }
+
+        int expectedX = 0 +
+            loops * p1.x +
+            loops * (p1.x + p2.x) +
+            loops * (p1.x + x2 + p3.x);
+
+        int expectedY = 123 +
+            loops * p1.y +
+            loops * (p1.y + p2.y) +
+            loops * (p1.y + y2 + p3.y);
+
+        System.out.println("Point (2) = " + p);
+
+        if (p.x != expectedX || p.y != expectedY) {
+            throw new RuntimeException("Expected (" + expectedX + ", " + expectedY + " but got " + p);
+        }
+
+        Point pzero = new Point(0,0);
+        if (HelloInlineClassApp.rectangle.p0 != pzero || HelloInlineClassApp.rectangle.p1 != pzero) {
+            throw new RuntimeException("Static field rectangle not as expected");
+        }
+
+        HelloInlineClassApp app = new HelloInlineClassApp();
+        if (app.point != pzero) {
+            throw new RuntimeException("Non-static field point not as expected");
+        }
+
+        ValueRecord valueRec = new ValueRecord(30, "thirty");
+        if (!valueRec.toString().equals("ValueRecord[i=30, name=thirty]")) {
+            throw new RuntimeException("ValueRecord toString unexpected value: " + valueRec.toString());
+        }
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/test-classes/HelloRelocation.java b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/HelloRelocation.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/HelloRelocation.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019, 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 HelloRelocation {
+    public static void main(String args[]) throws Exception {
+        System.out.println("Hello World");
+        HelloInlineClassApp.main(args);
+    }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/test-classes/Point.java b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/Point.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/Point.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015, 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 jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+public value class Point {
+  int i = 0, j = 0;
+
+  public String msg() {
+    return "___xxx___";
+  }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/test-classes/RewriteBytecodesInline.java b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/RewriteBytecodesInline.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/RewriteBytecodesInline.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015, 2019, 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.lang.invoke.MethodHandles;
+import jdk.test.whitebox.WhiteBox;
+
+public class RewriteBytecodesInline {
+  public static void main(String args[]) throws Throwable {
+    String from = "___xxx___";
+    String to   = "___yyy___";
+    File clsFile = new File(args[0]);
+    Class fieldClass = Util.defineModifiedClass(MethodHandles.lookup(), clsFile, from, to);
+
+    WithInlinedField wif = new WithInlinedField();
+
+    if (wif.p.getClass() != fieldClass) {
+      throw new RuntimeException("Mismatched field class");
+    }
+
+    // Even if the Point class is not loaded from the CDS archive, make sure the WithInlinedField class
+    // can still be loaded successfully, and properly get the rewritten version of Point.
+    // The archived version of WithInlinedField must not be loaded, because it references the archived
+    // version of Point, but a different version of Point has been loaded.
+
+    if (!wif.p.msg().equals(to)) {
+      throw new RuntimeException("Wrong output, expected: " + to + ", but got: " + wif.p.msg());
+    }
+
+    WhiteBox wb = WhiteBox.getWhiteBox();
+    if (wb.isSharedClass(fieldClass)) {
+      throw new RuntimeException("wb.isSharedClass(superClass) should be false");
+    }
+    if (wb.isSharedClass(wif.p.getClass())) {
+      throw new RuntimeException("wb.isSharedClass(child.getClass()) should be false");
+    }
+  }
+}
diff a/test/hotspot/jtreg/runtime/cds/appcds/test-classes/WithInlinedField.java b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/WithInlinedField.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/WithInlinedField.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015, 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 jdk.internal.vm.annotation.NullRestricted;
+
+public class WithInlinedField {
+  @NullRestricted
+  Point p;
+}
diff a/test/hotspot/jtreg/runtime/clinit/ClassInitBarrier.java b/test/hotspot/jtreg/runtime/clinit/ClassInitBarrier.java
--- a/test/hotspot/jtreg/runtime/clinit/ClassInitBarrier.java
+++ b/test/hotspot/jtreg/runtime/clinit/ClassInitBarrier.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2022, 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,10 +24,11 @@
 /*
  * @test
  * @library /test/lib
  *
  * @requires !vm.graal.enabled
+ * @enablePreview
  *
  * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -Xint                   -DTHROW=false -Xcheck:jni ClassInitBarrier
  * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -Xint                   -DTHROW=true  -Xcheck:jni ClassInitBarrier
  *
  * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false -Xcheck:jni ClassInitBarrier
@@ -65,11 +66,20 @@
 
     static native boolean init();
 
     static final boolean THROW = Boolean.getBoolean("THROW");
 
+    static value class MyValue {
+        int x = 42;
+
+        void verify() {
+            Asserts.assertEquals(x, 42);
+        }
+    }
+
     static class Test {
+
         static class A {
             static {
                 if (!init(B.class)) {
                     throw new Error("init failed");
                 }
@@ -83,13 +93,13 @@
                 maybeThrow();    // fail initialization if needed
 
                 changePhase(Phase.FINISHED);
             }
 
-            static              void staticM(Runnable action) { action.run(); }
-            static synchronized void staticS(Runnable action) { action.run(); }
-            static native       void staticN(Runnable action);
+            static              void staticM(Runnable action, MyValue val) { action.run(); val.verify(); }
+            static synchronized void staticS(Runnable action, MyValue val) { action.run(); val.verify(); }
+            static native       void staticN(Runnable action, MyValue val);
 
             static int staticF;
 
             int f;
             void m() {}
@@ -97,35 +107,35 @@
             static native boolean init(Class<B> cls);
         }
 
         static class B extends A {}
 
-        static void testInvokeStatic(Runnable action)       { A.staticM(action); }
-        static void testInvokeStaticSync(Runnable action)   { A.staticS(action); }
-        static void testInvokeStaticNative(Runnable action) { A.staticN(action); }
+        static void testInvokeStatic(Runnable action, MyValue val)       { A.staticM(action, val); }
+        static void testInvokeStaticSync(Runnable action, MyValue val)   { A.staticS(action, val); }
+        static void testInvokeStaticNative(Runnable action, MyValue val) { A.staticN(action, val); }
 
-        static int  testGetStatic(Runnable action)    { int v = A.staticF; action.run(); return v;   }
-        static void testPutStatic(Runnable action)    { A.staticF = 1;     action.run(); }
-        static A    testNewInstanceA(Runnable action) { A obj = new A();   action.run(); return obj; }
-        static B    testNewInstanceB(Runnable action) { B obj = new B();   action.run(); return obj; }
+        static int  testGetStatic(Runnable action, MyValue val)    { int v = A.staticF; action.run(); val.verify(); return v;   }
+        static void testPutStatic(Runnable action, MyValue val)    { A.staticF = 1;     action.run(); val.verify(); }
+        static A    testNewInstanceA(Runnable action, MyValue val) { A obj = new A();   action.run(); val.verify(); return obj; }
+        static B    testNewInstanceB(Runnable action, MyValue val) { B obj = new B();   action.run(); val.verify(); return obj; }
 
-        static int  testGetField(A recv, Runnable action)      { int v = recv.f; action.run(); return v; }
-        static void testPutField(A recv, Runnable action)      { recv.f = 1;     action.run(); }
-        static void testInvokeVirtual(A recv, Runnable action) { recv.m();       action.run(); }
+        static int  testGetField(A recv, Runnable action, MyValue val)      { int v = recv.f; action.run(); val.verify(); return v; }
+        static void testPutField(A recv, Runnable action, MyValue val)      { recv.f = 1;     action.run(); val.verify(); }
+        static void testInvokeVirtual(A recv, Runnable action, MyValue val) { recv.m();       action.run(); val.verify(); }
 
-        static native void testInvokeStaticJNI(Runnable action);
-        static native void testInvokeStaticSyncJNI(Runnable action);
-        static native void testInvokeStaticNativeJNI(Runnable action);
+        static native void testInvokeStaticJNI(Runnable action, MyValue val);
+        static native void testInvokeStaticSyncJNI(Runnable action, MyValue val);
+        static native void testInvokeStaticNativeJNI(Runnable action, MyValue val);
 
-        static native int  testGetStaticJNI(Runnable action);
-        static native void testPutStaticJNI(Runnable action);
-        static native A    testNewInstanceAJNI(Runnable action);
-        static native B    testNewInstanceBJNI(Runnable action);
+        static native int  testGetStaticJNI(Runnable action, MyValue val);
+        static native void testPutStaticJNI(Runnable action, MyValue val);
+        static native A    testNewInstanceAJNI(Runnable action, MyValue val);
+        static native B    testNewInstanceBJNI(Runnable action, MyValue val);
 
-        static native int  testGetFieldJNI(A recv, Runnable action);
-        static native void testPutFieldJNI(A recv, Runnable action);
-        static native void testInvokeVirtualJNI(A recv, Runnable action);
+        static native int  testGetFieldJNI(A recv, Runnable action, MyValue val);
+        static native void testPutFieldJNI(A recv, Runnable action, MyValue val);
+        static native void testInvokeVirtualJNI(A recv, Runnable action, MyValue val);
 
         static void runTests() {
             checkBlockingAction(Test::testInvokeStatic);       // invokestatic
             checkBlockingAction(Test::testInvokeStaticSync);   // invokestatic
             checkBlockingAction(Test::testInvokeStaticNative); // invokestatic
@@ -138,11 +148,11 @@
             checkNonBlockingAction(Test::testInvokeStaticNativeJNI); // invokestatic
             checkNonBlockingAction(Test::testGetStaticJNI);          // getstatic
             checkNonBlockingAction(Test::testPutStaticJNI);          // putstatic
             checkBlockingAction(Test::testNewInstanceAJNI);          // new
 
-            A recv = testNewInstanceB(NON_BLOCKING.get());  // trigger B initialization
+            A recv = testNewInstanceB(NON_BLOCKING.get(), new MyValue());  // trigger B initialization
             checkNonBlockingAction(Test::testNewInstanceB); // new: NO BLOCKING: same thread: A being initialized, B fully initialized
 
             checkNonBlockingAction(recv, Test::testGetField);      // getfield
             checkNonBlockingAction(recv, Test::testPutField);      // putfield
             checkNonBlockingAction(recv, Test::testInvokeVirtual); // invokevirtual
@@ -152,22 +162,23 @@
             checkNonBlockingAction(recv, Test::testPutFieldJNI);      // putfield
             checkNonBlockingAction(recv, Test::testInvokeVirtualJNI); // invokevirtual
         }
 
         static void warmup() {
+            MyValue val = new MyValue();
             for (int i = 0; i < 20_000; i++) {
-                testInvokeStatic(      NON_BLOCKING_WARMUP);
-                testInvokeStaticNative(NON_BLOCKING_WARMUP);
-                testInvokeStaticSync(  NON_BLOCKING_WARMUP);
-                testGetStatic(         NON_BLOCKING_WARMUP);
-                testPutStatic(         NON_BLOCKING_WARMUP);
-                testNewInstanceA(      NON_BLOCKING_WARMUP);
-                testNewInstanceB(      NON_BLOCKING_WARMUP);
-
-                testGetField(new B(),      NON_BLOCKING_WARMUP);
-                testPutField(new B(),      NON_BLOCKING_WARMUP);
-                testInvokeVirtual(new B(), NON_BLOCKING_WARMUP);
+                testInvokeStatic(      NON_BLOCKING_WARMUP, val);
+                testInvokeStaticNative(NON_BLOCKING_WARMUP, val);
+                testInvokeStaticSync(  NON_BLOCKING_WARMUP, val);
+                testGetStatic(         NON_BLOCKING_WARMUP, val);
+                testPutStatic(         NON_BLOCKING_WARMUP, val);
+                testNewInstanceA(      NON_BLOCKING_WARMUP, val);
+                testNewInstanceB(      NON_BLOCKING_WARMUP, val);
+
+                testGetField(new B(),      NON_BLOCKING_WARMUP, val);
+                testPutField(new B(),      NON_BLOCKING_WARMUP, val);
+                testInvokeVirtual(new B(), NON_BLOCKING_WARMUP, val);
             }
         }
 
         static void run() {
             execute(ExceptionInInitializerError.class, () -> triggerInitialization(A.class));
@@ -263,15 +274,15 @@
 
         dumpInfo();
     }
 
     interface TestCase0 {
-        void run(Runnable runnable);
+        void run(Runnable runnable, MyValue val);
     }
 
     interface TestCase1<T> {
-        void run(T arg, Runnable runnable);
+        void run(T arg, Runnable runnable, MyValue val);
     }
 
     enum Phase { BEFORE_INIT, IN_PROGRESS, FINISHED, INIT_FAILURE }
 
     static volatile Phase phase = Phase.BEFORE_INIT;
@@ -337,47 +348,48 @@
     static final AtomicInteger BLOCKING_COUNTER = new AtomicInteger(0);
     static final AtomicInteger BLOCKING_ACTIONS = new AtomicInteger(0);
     static final Factory<Runnable> BLOCKING     = () -> disposableAction(Phase.FINISHED, BLOCKING_COUNTER, BLOCKING_ACTIONS);
 
     static void checkBlockingAction(TestCase0 r) {
+        MyValue val = new MyValue();
         switch (phase) {
             case IN_PROGRESS: {
                 // Barrier during class initalization.
-                r.run(NON_BLOCKING.get());             // initializing thread
+                r.run(NON_BLOCKING.get(), val);             // initializing thread
                 checkBlocked(ON_BLOCK, ON_FAILURE, r); // different thread
                 break;
             }
             case FINISHED: {
                 // No barrier after class initalization is over.
-                r.run(NON_BLOCKING.get()); // initializing thread
+                r.run(NON_BLOCKING.get(), val); // initializing thread
                 checkNotBlocked(r);        // different thread
                 break;
             }
             case INIT_FAILURE: {
                 // Exception is thrown after class initialization failed.
-                TestCase0 test = action -> execute(NoClassDefFoundError.class, () -> r.run(action));
+                TestCase0 test = (action, valarg) -> execute(NoClassDefFoundError.class, () -> r.run(action, valarg));
 
-                test.run(NON_BLOCKING.get()); // initializing thread
+                test.run(NON_BLOCKING.get(), val); // initializing thread
                 checkNotBlocked(test);        // different thread
                 break;
             }
             default: throw new Error("wrong phase: " + phase);
         }
     }
 
     static void checkNonBlockingAction(TestCase0 r) {
-        r.run(NON_BLOCKING.get()); // initializing thread
+        r.run(NON_BLOCKING.get(), new MyValue()); // initializing thread
         checkNotBlocked(r);        // different thread
     }
 
     static <T> void checkNonBlockingAction(T recv, TestCase1<T> r) {
-        r.run(recv, NON_BLOCKING.get());                  // initializing thread
-        checkNotBlocked((action) -> r.run(recv, action)); // different thread
+        r.run(recv, NON_BLOCKING.get(), new MyValue());                  // initializing thread
+        checkNotBlocked((action, val) -> r.run(recv, action, val)); // different thread
     }
 
     static void checkFailingAction(TestCase0 r) {
-        r.run(NON_BLOCKING.get()); // initializing thread
+        r.run(NON_BLOCKING.get(), new MyValue()); // initializing thread
         checkNotBlocked(r);        // different thread
     }
 
     static void triggerInitialization(Class<?> cls) {
         try {
@@ -391,11 +403,11 @@
     }
 
     static void checkBlocked(Consumer<Thread> onBlockHandler, Thread.UncaughtExceptionHandler onException, TestCase0 r) {
         Thread thr = new Thread(() -> {
             try {
-                r.run(BLOCKING.get());
+                r.run(BLOCKING.get(), new MyValue());
                 System.out.println("Thread " + Thread.currentThread() + ": Finished successfully");
             } catch(Throwable e) {
                 System.out.println("Thread " + Thread.currentThread() + ": Exception thrown: " + e);
                 if (!THROW) {
                     e.printStackTrace();
@@ -419,11 +431,11 @@
             throw new Error(e);
         }
     }
 
     static void checkNotBlocked(TestCase0 r) {
-        final Thread thr = new Thread(() -> r.run(NON_BLOCKING.get()));
+        final Thread thr = new Thread(() -> r.run(NON_BLOCKING.get(), new MyValue()));
         final Throwable[] ex = new Throwable[1];
         thr.setUncaughtExceptionHandler((t, e) -> {
             if (thr != t) {
                 ex[0] = new Error("wrong thread: " + thr + " vs " + t);
             } else {
diff a/test/hotspot/jtreg/runtime/clinit/libClassInitBarrier.cpp b/test/hotspot/jtreg/runtime/clinit/libClassInitBarrier.cpp
--- a/test/hotspot/jtreg/runtime/clinit/libClassInitBarrier.cpp
+++ b/test/hotspot/jtreg/runtime/clinit/libClassInitBarrier.cpp
@@ -52,17 +52,17 @@
         if (test_class_A == NULL)  return JNI_FALSE;
 
         test_class_B = (jclass)env->NewGlobalRef(arg1);
         if (test_class_B == NULL)  return JNI_FALSE;
 
-        test_staticM_id = env->GetStaticMethodID(test_class_A, "staticM", "(Ljava/lang/Runnable;)V");
+        test_staticM_id = env->GetStaticMethodID(test_class_A, "staticM", "(Ljava/lang/Runnable;LClassInitBarrier$MyValue;)V");
         if (test_staticM_id == NULL)  return JNI_FALSE;
 
-        test_staticS_id = env->GetStaticMethodID(test_class_A, "staticS", "(Ljava/lang/Runnable;)V");
+        test_staticS_id = env->GetStaticMethodID(test_class_A, "staticS", "(Ljava/lang/Runnable;LClassInitBarrier$MyValue;)V");
         if (test_staticS_id == NULL)  return JNI_FALSE;
 
-        test_staticN_id = env->GetStaticMethodID(test_class_A, "staticN", "(Ljava/lang/Runnable;)V");
+        test_staticN_id = env->GetStaticMethodID(test_class_A, "staticN", "(Ljava/lang/Runnable;LClassInitBarrier$MyValue;)V");
         if (test_staticN_id == NULL)  return JNI_FALSE;
 
         test_A_m_id = env->GetMethodID(test_class_A, "m", "()V");
         if (test_A_m_id == NULL)  return JNI_FALSE;
 
@@ -77,20 +77,20 @@
 
     JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_00024A_staticN(JNIEnv* env, jclass cls, jobject action) {
         env->CallVoidMethod(action, methodId);
     }
 
-    JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testInvokeStaticJNI(JNIEnv* env, jclass cls, jobject action) {
-        env->CallStaticVoidMethod(test_class_A, test_staticM_id, action);
+    JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testInvokeStaticJNI(JNIEnv* env, jclass cls, jobject action, jobject val) {
+        env->CallStaticVoidMethod(test_class_A, test_staticM_id, action, val);
     }
 
-    JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testInvokeStaticSyncJNI(JNIEnv* env, jclass cls, jobject action) {
-        env->CallStaticVoidMethod(test_class_A, test_staticS_id, action);
+    JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testInvokeStaticSyncJNI(JNIEnv* env, jclass cls, jobject action, jobject val) {
+        env->CallStaticVoidMethod(test_class_A, test_staticS_id, action, val);
     }
 
-    JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testInvokeStaticNativeJNI(JNIEnv* env, jclass cls, jobject action) {
-        env->CallStaticVoidMethod(test_class_A, test_staticN_id, action);
+    JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testInvokeStaticNativeJNI(JNIEnv* env, jclass cls, jobject action, jobject val) {
+        env->CallStaticVoidMethod(test_class_A, test_staticN_id, action, val);
     }
 
     JNIEXPORT jint JNICALL Java_ClassInitBarrier_00024Test_testGetStaticJNI(JNIEnv* env, jclass cls, jobject action) {
         jint v = env->GetStaticIntField(test_class_A, test_staticF_id); // int v = A.staticF;
         env->CallVoidMethod(action, methodId);                          // action.run();
diff a/test/hotspot/jtreg/runtime/condy/CondyBadBSMArrayTest.java b/test/hotspot/jtreg/runtime/condy/CondyBadBSMArrayTest.java
--- a/test/hotspot/jtreg/runtime/condy/CondyBadBSMArrayTest.java
+++ b/test/hotspot/jtreg/runtime/condy/CondyBadBSMArrayTest.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2022, 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.
@@ -55,11 +55,11 @@
         try {
             Class newClass = Class.forName("CondyEmptyBSMArray1");
             throw new RuntimeException("Expected ClassFormatError exception not thrown");
         } catch (java.lang.ClassFormatError e) {
             if (!e.getMessage().contains("Short length on BootstrapMethods in class file")) {
-                throw new RuntimeException("ClassFormatError thrown, incorrect message");
+                throw new RuntimeException("ClassFormatError thrown, incorrect message: " + e.getMessage());
             }
             System.out.println("Test CondyEmptyBSMArray1 passed: " + e.getMessage());
         } catch (Throwable e) {
             throw new RuntimeException("Expected ClassFormatError exception not thrown");
         }
diff a/test/hotspot/jtreg/runtime/condy/CondyBadBSMIndex.jcod b/test/hotspot/jtreg/runtime/condy/CondyBadBSMIndex.jcod
--- a/test/hotspot/jtreg/runtime/condy/CondyBadBSMIndex.jcod
+++ b/test/hotspot/jtreg/runtime/condy/CondyBadBSMIndex.jcod
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2022, 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.
@@ -69,11 +69,11 @@
     Utf8 "BootstrapMethods"; // #23    
     Utf8 "CondyBadBSMIndex"; // #24    
     class #24; // #25    
   } // Constant Pool
 
-  0x0000; // access
+  0x0020; // access ACC_SUPER
   #25;// this_cpx
   #2;// super_cpx
 
   [] { // Interfaces
   } // Interfaces
diff a/test/hotspot/jtreg/runtime/condy/CondyBadLDC.jasm b/test/hotspot/jtreg/runtime/condy/CondyBadLDC.jasm
--- a/test/hotspot/jtreg/runtime/condy/CondyBadLDC.jasm
+++ b/test/hotspot/jtreg/runtime/condy/CondyBadLDC.jasm
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2022, 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,11 +24,11 @@
 /*
  * This test contains a incorrect ldc instruction of a condy whose loadable
  * constant is a double. VerifyError expected.
  */
 
-class CondyBadLDC
+super class CondyBadLDC
 	version 55:0
 {
 
 
 public Method "<init>":"()V"
diff a/test/hotspot/jtreg/runtime/condy/CondyBadLDC2_W.jasm b/test/hotspot/jtreg/runtime/condy/CondyBadLDC2_W.jasm
--- a/test/hotspot/jtreg/runtime/condy/CondyBadLDC2_W.jasm
+++ b/test/hotspot/jtreg/runtime/condy/CondyBadLDC2_W.jasm
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2022, 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,11 +23,11 @@
 
 /*
  * This test contains a ldc2_w instruction of a condy which returns a loadable float
  * constant.  VerifyError expected.
  */
-class CondyBadLDC2_W
+super class CondyBadLDC2_W
 	version 55:0
 {
 
 public Method "<init>":"()V"
 	stack 1 locals 1
diff a/test/hotspot/jtreg/runtime/condy/CondyEmptyBSMArray1.jcod b/test/hotspot/jtreg/runtime/condy/CondyEmptyBSMArray1.jcod
--- a/test/hotspot/jtreg/runtime/condy/CondyEmptyBSMArray1.jcod
+++ b/test/hotspot/jtreg/runtime/condy/CondyEmptyBSMArray1.jcod
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2022, 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.
@@ -69,11 +69,11 @@
     Utf8 "BootstrapMethods"; // #23    
     Utf8 "CondyEmptyBSMArray1"; // #24    
     class #24; // #25    
   } // Constant Pool
 
-  0x0000; // access
+  0x0020; // access ACC_SUPER
   #25;// this_cpx
   #2;// super_cpx
 
   [] { // Interfaces
   } // Interfaces
diff a/test/hotspot/jtreg/runtime/condy/CondyNoBSMArray.jcod b/test/hotspot/jtreg/runtime/condy/CondyNoBSMArray.jcod
--- a/test/hotspot/jtreg/runtime/condy/CondyNoBSMArray.jcod
+++ b/test/hotspot/jtreg/runtime/condy/CondyNoBSMArray.jcod
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2022, 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.
@@ -69,11 +69,11 @@
     Utf8 "BootstrapMethods"; // #23    
     Utf8 "CondyNoBSMArray"; // #24    
     class #24; // #25    
   } // Constant Pool
 
-  0x0000; // access
+  0x0020; // access ACC_SUPER
   #25;// this_cpx
   #2;// super_cpx
 
   [] { // Interfaces
   } // Interfaces
diff a/test/hotspot/jtreg/runtime/condy/CondyUseLDC_W.jasm b/test/hotspot/jtreg/runtime/condy/CondyUseLDC_W.jasm
--- a/test/hotspot/jtreg/runtime/condy/CondyUseLDC_W.jasm
+++ b/test/hotspot/jtreg/runtime/condy/CondyUseLDC_W.jasm
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2022, 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,11 +23,11 @@
 
 /*
  * This test contains ldc_w instructions of condy's who generate dynamic constants
  * of the following types: byte, char, short, int, float, boolean.
  */
-class CondyUseLDC_W
+super class CondyUseLDC_W
 	version 55:0
 {
 
 public Method "<init>":"()V"
 	stack 1 locals 1
diff a/test/hotspot/jtreg/runtime/condy/CondyUsesIndyBSM.jcod b/test/hotspot/jtreg/runtime/condy/CondyUsesIndyBSM.jcod
--- a/test/hotspot/jtreg/runtime/condy/CondyUsesIndyBSM.jcod
+++ b/test/hotspot/jtreg/runtime/condy/CondyUsesIndyBSM.jcod
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2022, 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.
@@ -159,11 +159,11 @@
     Utf8 "BootstrapMethods"; // #86    
     Utf8 "CondyUsesIndyBSM"; // #87    
     class #87; // #88    
   } // Constant Pool
 
-  0x0000; // access
+  0x0020; // access ACC_SUPER
   #88;// this_cpx
   #2;// super_cpx
 
   [] { // Interfaces
   } // Interfaces
diff a/test/hotspot/jtreg/runtime/condy/IndyUsesCondyBSM.jcod b/test/hotspot/jtreg/runtime/condy/IndyUsesCondyBSM.jcod
--- a/test/hotspot/jtreg/runtime/condy/IndyUsesCondyBSM.jcod
+++ b/test/hotspot/jtreg/runtime/condy/IndyUsesCondyBSM.jcod
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2022, 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.
@@ -158,11 +158,11 @@
     Utf8 "java/lang/Integer"; // #85     at 0x04E8
     NameAndType #83 #45; // #86     at 0x04FC
     NameAndType #40 #35; // #87     at 0x0501
   } // Constant Pool
 
-  0x0000; // access [ ]
+  0x0020; // access ACC_SUPER
   #5;// this_cpx
   #62;// super_cpx
 
   [0] { // Interfaces
   } // Interfaces
diff a/test/hotspot/jtreg/runtime/condy/staticInit/Example.jasm b/test/hotspot/jtreg/runtime/condy/staticInit/Example.jasm
--- a/test/hotspot/jtreg/runtime/condy/staticInit/Example.jasm
+++ b/test/hotspot/jtreg/runtime/condy/staticInit/Example.jasm
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2022, 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,11 +23,11 @@
 
 // This class gets an initialization error in a condy invokestatic.  Need jasm so that StaticInit isn't
 // initialized before the condy call.
 // Test that second invocation gets same error as first.
 
-public class Example
+super public class Example
         version 55:0
 {
 
 
 static Method $jacocoInit:"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;"
diff a/test/hotspot/jtreg/runtime/exceptionMsgs/NullPointerException/NullPointerExceptionTest.java b/test/hotspot/jtreg/runtime/exceptionMsgs/NullPointerException/NullPointerExceptionTest.java
--- a/test/hotspot/jtreg/runtime/exceptionMsgs/NullPointerException/NullPointerExceptionTest.java
+++ b/test/hotspot/jtreg/runtime/exceptionMsgs/NullPointerException/NullPointerExceptionTest.java
@@ -302,11 +302,11 @@
             System.out.println(oa1[0]);
             Asserts.fail();
         } catch (NullPointerException e) {
             checkMessage(e, "oa1[0] = new Object();", e.getMessage(),
                          "Cannot store to object array because " +
-                         (hasDebugInfo ? "\"oa1\"" : "\"<local3>\"") + " is null");
+                         (hasDebugInfo ? "\"oa1\"" : "\"<local3>\"") + " is null or is a null-free array and there's an attempt to store null in it");
         }
         // bastore (boolean)
         try {
             za1[0] = false;
             System.out.println(za1[0]);
diff a/test/hotspot/jtreg/runtime/modules/PatchModule/PatchModuleDupModule.java b/test/hotspot/jtreg/runtime/modules/PatchModule/PatchModuleDupModule.java
--- a/test/hotspot/jtreg/runtime/modules/PatchModule/PatchModuleDupModule.java
+++ b/test/hotspot/jtreg/runtime/modules/PatchModule/PatchModuleDupModule.java
@@ -21,11 +21,11 @@
  * questions.
  */
 
 /*
  * @test
- * @summary Module system initialization exception results if a module is specificed twice to --patch-module.
+ * @summary If a module is specificed twice to --patch-module, it should print an error
  * @requires vm.flagless
  * @modules java.base/jdk.internal.misc
  * @library /test/lib
  * @run driver PatchModuleDupModule
  */
@@ -33,18 +33,18 @@
 import jdk.test.lib.process.OutputAnalyzer;
 import jdk.test.lib.process.ProcessTools;
 
 public class PatchModuleDupModule {
 
-  // The module system initialization should generate an ExceptionInInitializerError
   // if --patch-module is specified with the same module more than once.
+  // The launcher should print an error.
 
   public static void main(String args[]) throws Exception {
     ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
       "--patch-module=module_one=module_one_dir",
       "--patch-module=module_one=module_one_dir",
       "-version");
     OutputAnalyzer output = new OutputAnalyzer(pb.start());
-    output.shouldContain("java.lang.ExceptionInInitializerError");
+    output.shouldContain("Cannot specify a module more than once to --patch-module");
     output.shouldHaveExitValue(1);
   }
 }
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/AbstractValueClassTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/AbstractValueClassTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/AbstractValueClassTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2022, 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
+ * @bug 8293134
+ * @summary Test that a super abstract value class is allowed.
+ * @enablePreview
+ * @run main AbstractValueClassTest
+ */
+
+public class AbstractValueClassTest {
+
+    public static void main(String[] args) {
+        A a = new A();
+        a.doit(a);
+    }
+    public static abstract value class T {
+        public abstract void doit(T t);
+    }
+
+    public static final value class A extends T {
+        public void doit(T t) {
+            System.out.println(t);
+        }
+    }
+    public static final value class B extends T {
+        public void doit(T t) {
+            System.out.println(t);
+        }
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/AnnotationsTests.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/AnnotationsTests.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/AnnotationsTests.java
@@ -0,0 +1,365 @@
+/*
+ * 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 jdk.test.lib.Asserts;
+import java.lang.reflect.Method;
+import java.lang.reflect.Field;
+import jdk.internal.misc.Unsafe;
+import jdk.internal.vm.annotation.NullRestricted;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+
+
+
+/*
+ * @test
+ * @summary Test of ImplicitlyConstructible, NullRestricted and LooselyConsistentValue annotations
+ * @modules java.base/jdk.internal.misc
+ *          java.base/jdk.internal.vm.annotation
+ * @library /test/lib
+ * @enablePreview
+ * @compile AnnotationsTests.java
+ * @run main/othervm AnnotationsTests
+ */
+
+
+ public class AnnotationsTests {
+
+    private static final Unsafe UNSAFE = Unsafe.getUnsafe();
+
+    public static void main(String[] args) {
+        AnnotationsTests tests = new AnnotationsTests();
+        Class c = tests.getClass();
+        for (Method m : c.getDeclaredMethods()) {
+            if (m.getName().startsWith("test_")) {
+                try {
+                    System.out.println("Running " + m.getName());
+                  m.invoke(tests);
+                } catch (Throwable t) {
+                  t.printStackTrace();
+                  throw new RuntimeException(t);
+                }
+            }
+        }
+    }
+
+    static class BadClass0 {
+        @NullRestricted
+        String s;
+    }
+
+    // Test detection of illegal usage of NullRestricted on an identity field
+    void test_0() {
+        Throwable exception = null;
+        try {
+            BadClass0 bc = new BadClass0();
+        } catch (IncompatibleClassChangeError e) {
+            exception = e;
+            System.out.println("Received " + e);
+        }
+        Asserts.assertNotNull(exception, "Failed to detect illegal use of @NullRestricted");
+    }
+
+    // Test detection of mismatch between a @NullRestricted field and its class that is not @ImplicitlyConstructible
+    static value class ValueClass1 {
+        int i = 0;
+        int j = 0;
+    }
+
+    static class BadClass1 {
+        @NullRestricted
+        ValueClass1 vc;
+    }
+
+    void test_1() {
+        Throwable exception = null;
+        try {
+            BadClass1 tc = new BadClass1();
+        } catch (IncompatibleClassChangeError e) {
+            exception = e;
+            System.out.println("Received " + e);
+        }
+        Asserts.assertNotNull(exception, "Failed to detect illegal use of @NullRestricted");
+    }
+
+    // Test a valid @NullRestricted field with a class that is @ImplicitlyConstructible
+    @ImplicitlyConstructible
+    static value class ValueClass2 {
+        int i = 0;
+        int j = 0;
+    }
+
+    static class GoodClass2 {
+        @NullRestricted
+        ValueClass2 vc;
+    }
+
+    void test_2() {
+        Throwable exception = null;
+        try {
+            GoodClass2 tc = new GoodClass2();
+        } catch (IncompatibleClassChangeError e) {
+            exception = e;
+            System.out.println("Received " + e);
+        }
+        Asserts.assertNull(exception, "Unexpected exception: " + exception);
+    }
+
+    // Invalid usage of @ImplicitlyConstructible on an identity class
+    @ImplicitlyConstructible
+    static class BadClass3 {
+
+    }
+
+    void test_3() {
+        Throwable exception = null;
+        try {
+            BadClass3 tc = new BadClass3();
+        } catch (ClassFormatError e) {
+            exception = e;
+            System.out.println("Received " + e);
+        }
+        Asserts.assertNotNull(exception, "Failed to detect illegal use of @ImplicitlyConstructible");
+    }
+
+    // Test invalid usage of @LooselyConsistentValue on an identity class
+    @LooselyConsistentValue
+    static class BadClass4 {
+
+    }
+
+    void test_4() {
+        Throwable exception = null;
+        try {
+            BadClass4 tc = new BadClass4();
+        } catch (ClassFormatError e) {
+            exception = e;
+            System.out.println("Received " + e);
+        }
+        Asserts.assertNotNull(exception, "Failed to detect illegal use of @LooselyConsistentValue");
+    }
+
+    // Test field flattening of @NullRestricted annotated fields
+
+    @ImplicitlyConstructible
+    static value class ValueClass5 {
+      int i = 0;
+    }
+
+    static class GoodClass5 {
+      ValueClass5 f0 = new ValueClass5();
+
+      @NullRestricted
+      ValueClass5 f1 = new ValueClass5();
+    }
+
+    void test_5() {
+        Throwable exception = null;
+        try {
+            GoodClass5 vc = new GoodClass5();
+            Field f0 = vc.getClass().getDeclaredField("f0");
+            Asserts.assertFalse(UNSAFE.isFlatField(f0), "Unexpected flat field");
+            Field f1 = vc.getClass().getDeclaredField("f1");
+            Asserts.assertTrue(UNSAFE.isFlatField(f1), "Flat field expected, but field is not flat");
+        } catch (IncompatibleClassChangeError e) {
+            exception = e;
+            System.out.println("Received " + e);
+        } catch(NoSuchFieldException e) {
+            Asserts.fail("Test error");
+        }
+        Asserts.assertNull(exception, "Unexpected exception: " + exception);
+    }
+
+
+    // Test detection/handling of circularity
+
+    @ImplicitlyConstructible
+    static value class ValueClass6a {
+        @NullRestricted
+        ValueClass6b val = new ValueClass6b();
+    }
+
+    @ImplicitlyConstructible
+    static value class ValueClass6b {
+        @NullRestricted
+        ValueClass6a val = new ValueClass6a();
+    }
+
+    static class BadClass6 {
+        @NullRestricted
+        ValueClass6a val = new ValueClass6a();
+    }
+
+    void test_6() {
+        Throwable exception = null;
+        try {
+            BadClass6 bc = new BadClass6();
+        } catch (ClassCircularityError e) {
+            exception = e;
+            System.out.println("Received " + e);
+        }
+        Asserts.assertNotNull(exception, "Failed to detect circularity");
+    }
+
+    // Test null restricted static field
+    @ImplicitlyConstructible
+    static value class ValueClass7 {
+        int i = 0;
+    }
+
+    static class GoodClass7 {
+        @NullRestricted
+        static ValueClass7 sval;
+    }
+
+    void test_7() {
+        Throwable exception = null;
+        try {
+            ValueClass7 val = GoodClass7.sval;
+            Asserts.assertNotNull(val, "Unexpected null value");
+        } catch (Throwable e) {
+            exception = e;
+            System.out.println("Received " + e);
+        }
+        Asserts.assertNull(exception, "Unexpected exception: " + exception);
+    }
+
+    // Test circularity on static fields
+    @ImplicitlyConstructible
+    static value class ValueClass8 {
+        @NullRestricted
+        static ValueClass8 sval;
+    }
+
+    void test_8() {
+        Throwable exception = null;
+        try {
+            ValueClass8 val = ValueClass8.sval;
+            Asserts.assertNotNull(val, "Unexpected null value");
+        } catch (Throwable e) {
+            exception = e;
+            System.out.println("Received " + e);
+        }
+        Asserts.assertNull(exception, "Unexpected exception: " + exception);
+    }
+
+    // Test that writing null to a @NullRestricted non-static field throws an exception
+    @ImplicitlyConstructible
+    static value class ValueClass9 {
+        int i = 0;
+    }
+
+    static class GoodClass9 {
+        @NullRestricted
+        ValueClass9 val = new ValueClass9();
+    }
+
+    void test_9() {
+        Throwable exception = null;
+        try {
+            GoodClass9 gc = new GoodClass9();
+            gc.val = null;
+        } catch(NullPointerException e) {
+            exception = e;
+            System.out.println("Received " + e);
+        }
+        Asserts.assertNotNull(exception, "Expected NullPointerException not received");
+    }
+
+    // Test that writing null to a @NullRestricted static field throws an exception
+    @ImplicitlyConstructible
+    static value class ValueClass10 {
+        @NullRestricted
+        static ValueClass10 sval;
+    }
+
+    void test_10() {
+        Throwable exception = null;
+        try {
+            ValueClass10.sval = null;
+        } catch(NullPointerException e) {
+            exception = e;
+            System.out.println("Received " + e);
+        }
+        Asserts.assertNotNull(exception, "Expected NullPointerException not received");
+    }
+
+    // Test uninitialized static null restricted field with a class not implicitly constructible
+    static value class ValueClass11 {
+        int i = 0;
+        int j = 0;
+    }
+
+    static class BadClass11 {
+        @NullRestricted
+        static ValueClass11 val;
+    }
+
+    void test_11() {
+        Throwable exception = null;
+        try {
+            ValueClass11 val = BadClass11.val;
+            System.out.println(val);
+        } catch(IncompatibleClassChangeError e) {
+            exception = e;
+            System.out.println("Received " + e);
+        }
+        Asserts.assertNotNull(exception, "Expected IncompatibleClassChangerError not received");
+    }
+
+    // Test illegal use of @NullRestricted on a primitive field
+    static class BadClass12 {
+        @NullRestricted
+        int i;
+    }
+    void test_12() {
+        Throwable exception = null;
+        try {
+            BadClass12 val = new BadClass12();
+            System.out.println(val);
+        } catch(ClassFormatError e) {
+            exception = e;
+            System.out.println("Received " + e);
+        }
+        Asserts.assertNotNull(exception, "Expected ClassFormatError not received");
+    }
+
+    // Test illegal use of @NullRestricted on an array field
+    static class BadClass13 {
+        @NullRestricted
+        int Integer[];
+    }
+    void test_13() {
+        Throwable exception = null;
+        try {
+            BadClass13 val = new BadClass13();
+            System.out.println(val);
+        } catch(ClassFormatError e) {
+            exception = e;
+            System.out.println("Received " + e);
+        }
+        Asserts.assertNotNull(exception, "Expected ClassFormatError not received");
+    }
+
+ }
+
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/CircularityTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/CircularityTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/CircularityTest.java
@@ -0,0 +1,208 @@
+/*
+ * 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.
+ *
+ * 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 runtime.valhalla.inlinetypes;
+
+import jdk.test.lib.Asserts;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+/*
+ * @test
+ * @summary Test initialization of static inline fields with circularity
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @compile CircularityTest.java
+ * @run main/othervm runtime.valhalla.inlinetypes.CircularityTest
+ */
+
+
+public class CircularityTest {
+    static boolean b = true;
+    static int counter = 0;
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class A {
+        @NullRestricted
+        static B b;
+        @NullRestricted
+        static C c;
+        int i = 0;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class B {
+        static {
+            Asserts.assertNotNull(A.c, "Should have returned C's default value");
+        }
+        int i = 0;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class C {
+        int i;
+        public C(int i) {
+            this.i = i;
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class D {
+        @NullRestricted
+        static C c;
+        int i = 0;
+        static {
+            if (CircularityTest.b) {
+                // throw an exception to cause D's initialization to fail
+                throw new RuntimeException();
+            }
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class E {
+        @NullRestricted
+        static F f;
+        @NullRestricted
+        static C c;
+        int i = 0;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class F {
+        int i = 0;
+        static {
+            E.c = new C(5);
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class G {
+        @NullRestricted
+        static H h;
+        int i = 0;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class H {
+        int i = 0;
+        static {
+            if (CircularityTest.b) {
+                // throw an exception to cause H's initialization to fail
+                throw new RuntimeException();
+            }
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class I {
+        @NullRestricted
+        static J j;
+        @NullRestricted
+        static H h;
+        int i = 0;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class J {
+        int i = 0;
+        static {
+            CircularityTest.counter = 1;
+            H h = I.h;
+            CircularityTest.counter = 2;
+        }
+    }
+
+    static public void main(String[] args) {
+        Throwable exception = null;
+        // Test 1:
+        // Initialization of A will trigger initialization of B which, in its static
+        // initializer will access a static inline field c of A that has not been initialized
+        // yet. The access must succeed (no exception) because the field is being
+        // accessed during the initialization of D, by the thread initializing D,
+        // and the value must be the default value of C (not null).
+        try {
+            A a = new A();
+        } catch (Throwable t) {
+            exception = t;
+        }
+        Asserts.assertNull(exception, "Circularity should not have caused exceptions");
+
+        // Test 2:
+        // Class D will fail to initialized (exception thrown in its static initializer).
+        // Attempt to access a static inline field of D *after* its failed initialization
+        // should trigger an exception.
+        exception = null;
+        try {
+            D d = new D();
+        } catch (Throwable t) {
+            // ignoring the exception thrown to cause initialization failure
+        }
+        try {
+            C c = D.c;
+        } catch (Throwable t) {
+            exception = t;
+        }
+        Asserts.assertNotNull(exception, "Accessing static fields of a class which failed to initialized should throw an exception");
+        Asserts.assertEquals(exception.getClass(), java.lang.NoClassDefFoundError.class, "Wrong exception class");
+        // Test 3:
+        // Initialization of E will trigger the initialization of F which, in its static initalizer,
+        // will initialized a static inline field of F before the JVM does. The JVM must not
+        // overwrite the value set by user code.
+        E e = new E();
+        Asserts.assertEquals(E.c.i, 5, "JVM must not overwrite fields initialized by user code");
+
+        // Test 4:
+        // Initialization of G should fail because its static inline field h
+        exception = null;
+        try {
+            G g = new G();
+        } catch(Throwable t) {
+            exception = t;
+        }
+        Asserts.assertNotNull(exception, "G's initialization should have failed");
+        Asserts.assertEquals(exception.getClass(), java.lang.ExceptionInInitializerError.class, "Wrong exception");
+
+        // Test 5:
+        // Initialization of of I should fail when J tries to access I.h
+        exception = null;
+        try {
+            I i = new I();
+        } catch(Throwable t) {
+            exception = t;
+        }
+        Asserts.assertNotNull(exception, "I's initialization should have failed");
+        Asserts.assertEquals(exception.getClass(), java.lang.NoClassDefFoundError.class, "Wrong exception");
+        Asserts.assertEquals(CircularityTest.counter, 1, "Didn't failed at the right place");
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ClassInitializationFailuresTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ClassInitializationFailuresTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ClassInitializationFailuresTest.java
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ *
+ * 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 runtime.valhalla.inlinetypes;
+
+import jdk.internal.value.ValueClass;
+import jdk.test.lib.Asserts;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+/*
+ * @test
+ * @summary Test several scenarios of class initialization failures
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ *          java.base/jdk.internal.value
+ * @enablePreview
+ * @compile ClassInitializationFailuresTest.java
+ * @run main/othervm runtime.valhalla.inlinetypes.ClassInitializationFailuresTest
+*/
+public class ClassInitializationFailuresTest {
+    static boolean failingInitialization = true;
+    static Object bo = null;
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class BadOne {
+        int i = 0;
+        static {
+            if (ClassInitializationFailuresTest.failingInitialization) {
+                throw new RuntimeException("Failing initialization");
+            }
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class TestClass1 {
+        @NullRestricted
+        BadOne badField = new BadOne();
+    }
+
+    // Test handling of errors during the initialization of a value class
+    // Initialization of TestClass1 triggers the initialization of classes
+    // of all its value class typed fields, in this case BadOne
+    // Static initializer of BadOne throws an exception, so BadOne's initialization
+    // fails, which must caused the initialization of TestClass1 to fail too
+    // First attempt to instantiate TestClass1 must fail with an ExceptionInInitializerError
+    // because an exception has been thrown during the initialization process
+    // Second attempt to instantiate TestClass1 must fail with a NoClassDefFoundError
+    // because TestClass1 must already be in a failed initialization state (so no new
+    // attempt to initialize the class)
+    static void testClassInitialization() {
+        Throwable e = null;
+        try {
+            TestClass1 t1 = new TestClass1();
+        } catch(Throwable t) {
+            e = t;
+        }
+        Asserts.assertNotNull(e, "Exception should have been thrown");
+        Asserts.assertTrue(e.getClass() == ExceptionInInitializerError.class, "Must be an ExceptionInInitializerError");
+        Asserts.assertTrue(e.getCause().getClass() == RuntimeException.class, "Must be the exception thown in the static initializer of BadOne");
+        // Second attempt because it doesn't fail the same way
+        e = null;
+        try {
+            TestClass1 t1 = new TestClass1();
+        } catch(Throwable t) {
+            e = t;
+        }
+        Asserts.assertNotNull(e, "Error should have been thrown");
+        Asserts.assertTrue(e.getClass() == NoClassDefFoundError.class, "Must be a NoClassDefFoundError");
+        Asserts.assertTrue(e.getCause().getClass() == ExceptionInInitializerError.class, "Must be an ExceptionInInitializerError");
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class BadTwo {
+        int i = 0;
+        static {
+            if (ClassInitializationFailuresTest.failingInitialization) {
+                throw new RuntimeException("Failing initialization");
+            }
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class BadThree {
+        int i = 0;
+        static {
+            if (ClassInitializationFailuresTest.failingInitialization) {
+                throw new RuntimeException("Failing initialization");
+            }
+        }
+    }
+
+    // Same test as above, but for arrays of value objects
+    static void testArrayInitialization() {
+        // Testing anewarray when the value element class fails to initialize properly
+        Throwable e = null;
+        try {
+            BadTwo[] array = (BadTwo[]) ValueClass.newNullRestrictedArray(BadTwo.class, 10);
+        } catch(Throwable t) {
+            e = t;
+        }
+        Asserts.assertNotNull(e, "Error should have been thrown");
+        Asserts.assertTrue(e.getClass() == ExceptionInInitializerError.class, " Must be an ExceptionInInitializerError");
+        // Second attempt because it doesn't fail the same way
+        try {
+            BadTwo[] array = (BadTwo[]) ValueClass.newNullRestrictedArray(BadTwo.class, 10);
+        } catch(Throwable t) {
+            e = t;
+        }
+        Asserts.assertNotNull(e, "Error should have been thrown");
+        Asserts.assertTrue(e.getClass() == NoClassDefFoundError.class, "Must be a NoClassDefFoundError");
+        Asserts.assertTrue(e.getCause().getClass() == ExceptionInInitializerError.class, "Must be an ExceptionInInitializerError");
+        /*
+        Transition model (annotations and array factory) doesn't permit multi-dimentional arrays tests
+        Disabling those tests for now
+        Testing multianewarray when the value element class fails to initialize properly
+        try {
+            BadThree[][] array = new BadThree[10][20];
+        } catch(Throwable t) {
+            e = t;
+        }
+        Asserts.assertNotNull(e, "Error should have been thrown");
+        Asserts.assertTrue(e.getClass() == ExceptionInInitializerError.class, " Must be an ExceptionInInitializerError");
+        // Second attempt because it doesn't fail the same way
+        try {
+            BadThree[][][] array = new BadThree[10][30][10];
+        } catch(Throwable t) {
+            e = t;
+        }
+        Asserts.assertNotNull(e, "Error should have been thrown");
+        Asserts.assertTrue(e.getClass() == NoClassDefFoundError.class, "Must be a NoClassDefFoundError");
+        Asserts.assertTrue(e.getCause().getClass() == ExceptionInInitializerError.class, "Must be an ExceptionInInitializerError");
+        */
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class BadFour {
+        int i = 0;
+        static BadFour[] array;
+        static {
+            array = (BadFour[]) ValueClass.newNullRestrictedArray(BadFour.class, 10);
+            if (ClassInitializationFailuresTest.failingInitialization) {
+                throw new RuntimeException("Failing initialization");
+            }
+        }
+    }
+
+    // Even if a value class fails to initialize properly, some instances
+    // of this class can escape and be accessible. The JVM must be able to
+    // deal with those instances without crashes. The test below checks that
+    // escaped values stored in an array are handled correctly
+    static void testEscapedValueInArray() {
+        Throwable e = null;
+        try {
+            BadFour bt = new BadFour();
+        } catch (Throwable t) {
+            e = t;
+        }
+        Asserts.assertNotNull(e, "Error must have been thrown");
+        Asserts.assertTrue(e.getClass() == ExceptionInInitializerError.class, " Must be an ExceptionInInitializerError");
+        e = null;
+        try {
+            BadFour t = BadFour.array[0];
+        } catch(Throwable t) {
+            e = t;
+        }
+        Asserts.assertNotNull(e, "Error should have been thrown");
+        Asserts.assertTrue(e.getClass() == NoClassDefFoundError.class, "Must be a NoClassDefFoundError");
+        Asserts.assertTrue(e.getCause().getClass() == ExceptionInInitializerError.class, "Must be an ExceptionInInitializerError");
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class BadFive {
+        int i = 0;
+        static {
+            ClassInitializationFailuresTest.bo = new BadSix();
+            if (ClassInitializationFailuresTest.failingInitialization) {
+                throw new RuntimeException("Failing initialization");
+            }
+        }
+    }
+
+    static class BadSix {
+        BadFive bf = new BadFive();
+    }
+
+    // Same test as above, but escaped values are stored in an object
+    static void testEscapedValueInObject() {
+        Throwable e = null;
+        try {
+            BadSix bt = new BadSix();
+        } catch (Throwable t) {
+            e = t;
+        }
+        Asserts.assertNotNull(e, "Error must have been thrown");
+        Asserts.assertNotNull(ClassInitializationFailuresTest.bo, "bo object should have been set");
+        BadFive bf = ((BadSix)ClassInitializationFailuresTest.bo).bf;
+    }
+
+    public static void main(String[] args) {
+        testClassInitialization();
+        testArrayInitialization();
+        testEscapedValueInArray();
+        testEscapedValueInObject();
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ClassPrintLayoutDcmd.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ClassPrintLayoutDcmd.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ClassPrintLayoutDcmd.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2021, Alibaba Group Holding Limited. 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 runtime.valhalla.inlinetypes;
+
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.JDKToolFinder;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.NullRestricted;
+
+/*
+ * @test
+ * @summary Test the VM.class_print_layout command
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @compile ClassPrintLayoutDcmd.java
+ * @run main/othervm runtime.valhalla.inlinetypes.ClassPrintLayoutDcmd
+ */
+
+public value class ClassPrintLayoutDcmd {
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Point {
+        int i = 0;
+        int j = 0;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Line {
+        @NullRestricted
+        Point p1;
+        @NullRestricted
+        Point p2;
+        Line() {
+            this.p1 = this.p2 = new Point();
+        }
+    }
+    static {
+        try {
+            Class.forName(Line.class.getName());
+        } catch(Throwable e) {
+            throw new RuntimeException("failed to find class");
+        }
+    }
+    static ProcessBuilder pb = new ProcessBuilder();
+
+    static void testCmd(String arg, int expectExitCode, String... expectStrings) throws Exception {
+        // Grab my own PID
+        String pid = Long.toString(ProcessTools.getProcessId());
+
+        pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.class_print_layout", arg });
+        OutputAnalyzer output = new OutputAnalyzer(pb.start());
+        output.shouldHaveExitValue(expectExitCode);
+        for (String expectString : expectStrings) {
+            output.shouldContain(expectString);
+        }
+    }
+
+    public static void main(String args[]) throws Exception {
+        testCmd("foo/bar", 0, "");
+        testCmd("", 1, "IllegalArgumentException", "mandatory");
+        testCmd("java/lang/Object", 0, "java/lang/Object", "@bootstrap");
+        testCmd("java/lang/Class", 0, "java/lang/Class", "@bootstrap");
+        testCmd("runtime/valhalla/inlinetypes/ClassPrintLayoutDcmd$Line", 0, "@app", "p1", "p2");
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/EmptyInlineTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/EmptyInlineTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/EmptyInlineTest.java
@@ -0,0 +1,177 @@
+/*
+ * 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.
+ *
+ * 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 runtime.valhalla.inlinetypes;
+
+import jdk.test.lib.Asserts;
+
+import java.lang.reflect.Field;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+
+/*
+ * @test
+ * @summary Test support for empty inline types (no instance fields)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ *          java.base/jdk.internal.value
+ * @enablePreview
+ * @compile EmptyInlineTest.java
+ * @run main/othervm -XX:InlineFieldMaxFlatSize=128 runtime.valhalla.inlinetypes.EmptyInlineTest
+ */
+
+public class EmptyInlineTest {
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class EmptyInline {
+        public boolean isEmpty() {
+            return true;
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class EmptyField {
+        @NullRestricted
+        EmptyInline empty;
+
+        EmptyField() {
+            this.empty = new EmptyInline();
+        }
+    }
+
+    static class WithInt {
+        int i;
+    }
+
+    static class WithEmptyField extends WithInt  {
+        // With current layout strategy for reference classs, the empty
+        // inline field would be placed between the int and the Object
+        // fields, along with some padding.
+        Object o;
+        @NullRestricted
+        EmptyInline empty;
+    }
+
+    public static void main(String[] args) {
+        // Create an empty inline
+        EmptyInline empty = new EmptyInline();
+        Asserts.assertTrue(empty.isEmpty());
+
+        // Create an inline with an empty inline field
+        EmptyField emptyField = new EmptyField();
+        Asserts.assertEquals(emptyField.empty.getClass(), EmptyInline.class);
+        Asserts.assertTrue(emptyField.empty.isEmpty());
+        System.out.println(emptyField.empty.isEmpty());
+
+        // Regular instance with an empty field inside
+        WithEmptyField w = new WithEmptyField();
+        Asserts.assertEquals(w.empty.getClass(), EmptyInline.class);
+        Asserts.assertTrue(w.empty.isEmpty());
+        w.empty = new EmptyInline();
+        Asserts.assertEquals(w.empty.getClass(), EmptyInline.class);
+        Asserts.assertTrue(w.empty.isEmpty());
+
+        // Create an array of empty inlines
+        EmptyInline[] emptyArray = (EmptyInline[])ValueClass.newNullRestrictedArray(EmptyInline.class, 100);
+        for(EmptyInline element : emptyArray) {
+            Asserts.assertEquals(element.getClass(), EmptyInline.class);
+            Asserts.assertTrue(element.isEmpty());
+        }
+
+        // Testing arrayCopy
+        EmptyInline[] array2 = (EmptyInline[])ValueClass.newNullRestrictedArray(EmptyInline.class, 100);
+        // with two arrays
+        System.arraycopy(emptyArray, 10, array2, 20, 50);
+        for(EmptyInline element : array2) {
+            Asserts.assertEquals(element.getClass(), EmptyInline.class);
+            Asserts.assertTrue(element.isEmpty());
+        }
+        // single array, no overlap
+        System.arraycopy(emptyArray, 10, emptyArray, 50, 20);
+        for(EmptyInline element : emptyArray) {
+            Asserts.assertEquals(element.getClass(), EmptyInline.class);
+            Asserts.assertTrue(element.isEmpty());
+        }
+        // single array with overlap
+        System.arraycopy(emptyArray, 10, emptyArray, 20, 50);
+        for(EmptyInline element : emptyArray) {
+            Asserts.assertEquals(element.getClass(), EmptyInline.class);
+            Asserts.assertTrue(element.isEmpty());
+        }
+
+        // Passing an empty inline in argument
+        assert isEmpty(empty);
+
+        // Returning an empty inline
+        assert getEmpty().isEmpty();
+
+        // Checking fields with reflection
+        Class<?> c = empty.getClass();
+        try {
+            Field[] fields = c.getDeclaredFields();
+            Asserts.assertTrue(fields.length == 0);
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw t;
+        }
+        WithEmptyField w0 = new WithEmptyField();
+        Class<?> c2 = w0.getClass();
+        try {
+            Field emptyfield = c2.getDeclaredField("empty");
+            EmptyInline e = (EmptyInline)emptyfield.get(w0);
+            Asserts.assertEquals(e.getClass(), EmptyInline.class);
+            Asserts.assertTrue(e.isEmpty());
+            emptyfield.set(w0, new EmptyInline());
+            e = (EmptyInline)emptyfield.get(w0);
+            Asserts.assertEquals(e.getClass(), EmptyInline.class);
+            Asserts.assertTrue(e.isEmpty());
+        } catch(Throwable t) {
+            t.printStackTrace();
+            throw new RuntimeException("Reflection tests failed: " + t);
+        }
+
+        // Testing JIT compiler
+        // for(int i=0; i < 100000; i++) {
+        //     test();
+        // }
+    }
+
+    static boolean isEmpty(EmptyInline empty) {
+        return empty.isEmpty();
+    }
+
+    static EmptyInline getEmpty() {
+        return new EmptyInline();
+    }
+
+    static void test() {
+        for(int i=0; i < 10000; i++) {
+            Asserts.assertTrue(getEmpty().isEmpty());
+        }
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/FlattenableSemanticTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/FlattenableSemanticTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/FlattenableSemanticTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2018, 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 runtime.valhalla.inlinetypes;
+
+
+import jdk.test.lib.Asserts;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.NullRestricted;
+
+/*
+ * @test
+ * @summary Flattenable field semantic test
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @compile FlattenableSemanticTest.java
+ * @run main/othervm -XX:InlineFieldMaxFlatSize=64 runtime.valhalla.inlinetypes.FlattenableSemanticTest
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:ForceNonTearable=* runtime.valhalla.inlinetypes.FlattenableSemanticTest
+ */
+public class FlattenableSemanticTest {
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Point {
+        final int x = 0;
+        final int y = 0;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    public value class JumboInline {
+        final long l0 = 0;
+        final long l1 = 1;
+        final long l2 = 2;
+        final long l3 = 3;
+        final long l4 = 4;
+        final long l5 = 5;
+        final long l6 = 6;
+        final long l7 = 7;
+        final long l8 = 8;
+        final long l9 = 9;
+        final long l10 = 10;
+        final long l11 = 11;
+        final long l12 = 12;
+        final long l13 = 13;
+        final long l14 = 14;
+        final long l15 = 15;
+        final long l16 = 16;
+        final long l17 = 17;
+        final long l18 = 18;
+        final long l19 = 19;
+    }
+
+    static Point nfsp;
+    @NullRestricted
+    static Point fsp;
+
+    Point nfip;
+    @NullRestricted
+    Point fip;
+
+    static JumboInline nfsj;
+    @NullRestricted
+    static JumboInline fsj;
+
+    JumboInline nfij;
+    @NullRestricted
+    JumboInline fij;
+
+    static Object getNull() {
+        return null;
+    }
+
+    FlattenableSemanticTest() { }
+
+    public static void main(String[] args) {
+        FlattenableSemanticTest test = new FlattenableSemanticTest();
+
+        // Uninitialized inline fields must be null for non flattenable fields
+        Asserts.assertNull(nfsp, "Invalid non null value for unitialized non flattenable field");
+        Asserts.assertNull(nfsj, "Invalid non null value for unitialized non flattenable field");
+        Asserts.assertNull(test.nfip, "Invalid non null value for unitialized non flattenable field");
+        Asserts.assertNull(test.nfij, "Invalid non null value for unitialized non flattenable field");
+
+        // fsp.equals(null);
+
+        // Uninitialized inline fields must be non null for flattenable fields
+        Asserts.assertNotNull(fsp, "Invalid null value for unitialized flattenable field");
+        Asserts.assertNotNull(fsj, "Invalid null value for unitialized flattenable field");
+        Asserts.assertNotNull(test.fip, "Invalid null value for unitialized flattenable field");
+        Asserts.assertNotNull(test.fij, "Invalid null value for unitialized flattenable field");
+
+        // Assigning null must be allowed for non flattenable inline fields
+        boolean exception = true;
+        try {
+            nfsp = (Point)getNull();
+            nfsp = null;
+            exception = false;
+        } catch (NullPointerException e) {
+            exception = true;
+        }
+        Asserts.assertFalse(exception, "Invalid NPE when assigning null to a non flattenable field");
+
+        try {
+            nfsj = (JumboInline)getNull();
+            nfsj = null;
+            exception = false;
+        } catch (NullPointerException e) {
+            exception = true;
+        }
+        Asserts.assertFalse(exception, "Invalid NPE when assigning null to a non flattenable field");
+
+        try {
+            test.nfip = (Point)getNull();
+            test.nfip = null;
+            exception = false;
+        } catch (NullPointerException e) {
+            exception = true;
+        }
+        Asserts.assertFalse(exception, "Invalid NPE when assigning null to a non flattenable field");
+
+        try {
+            test.nfij = (JumboInline)getNull();
+            test.nfij = null;
+            exception = false;
+        } catch (NullPointerException e) {
+            exception = true;
+        }
+        Asserts.assertFalse(exception, "Invalid NPE when assigning null to a non flattenable field");
+
+        // Assigning null to a flattenable inline field must trigger a NPE
+        exception = false;
+        try {
+            fsp = (Point)getNull();
+        } catch(NullPointerException e) {
+            exception = true;
+        }
+        Asserts.assertTrue(exception, "NPE not thrown when assigning null to a flattenable field");
+        exception = false;
+        try {
+            fsj = (JumboInline)getNull();
+        } catch(NullPointerException e) {
+            exception = true;
+        }
+        Asserts.assertTrue(exception, "NPE not thrown when assigning null to a flattenable field");
+        exception = false;
+        try {
+            test.fip = (Point)getNull();
+        } catch(NullPointerException e) {
+            exception = true;
+        }
+        Asserts.assertTrue(exception, "NPE not thrown when assigning null to a flattenable field");
+        exception = false;
+        try {
+            test.fij = (JumboInline)getNull();
+        } catch(NullPointerException e) {
+            exception = true;
+        }
+        Asserts.assertTrue(exception, "NPE not thrown when assigning null to a flattenable field");
+        exception = false;
+    }
+
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/HiddenInlineClassTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/HiddenInlineClassTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/HiddenInlineClassTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020, 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 a hidden inline class.
+ * @library /test/lib
+ * @enablePreview
+ * @run main/othervm HiddenInlineClassTest
+ */
+
+import java.io.File;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class HiddenInlineClassTest {
+    static final Path CLASSES_DIR = Paths.get(System.getProperty("test.classes", "."));
+
+    static byte[] readClassFile(String classFileName) throws Exception {
+       Path path = CLASSES_DIR.resolve(classFileName.replace('.', File.separatorChar) + ".class");
+       return Files.readAllBytes(path);
+    }
+
+    public static void main(String[] args) throws Throwable {
+        Lookup lookup = MethodHandles.lookup();
+        byte[] bytes = readClassFile("HiddenPoint");
+
+        // Define a hidden class that is an inline type.
+        Class<?> c = lookup.defineHiddenClass(bytes, true).lookupClass();
+        Object hp = c.newInstance();
+        String s = (String)c.getMethod("getValue").invoke(hp);
+        if (!s.equals("x: 0, y: 0")) {
+            throw new RuntimeException(
+                "wrong value returned by method getValue() in inline hidden class: " + s);
+        }
+    }
+
+}
+
+value class HiddenPoint {
+    int x;
+    int y;
+
+    HiddenPoint() {
+        this.x = 0;
+        this.y = 0;
+    }
+
+    public String getValue() {
+        return "x: " + x + ", y: " + y;
+    }
+}
+
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/Ifacmp.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/Ifacmp.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/Ifacmp.java
@@ -0,0 +1,218 @@
+/*
+ * 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.
+ *
+ * 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 runtime.valhalla.inlinetypes;
+
+import java.lang.ref.*;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+
+
+/*
+ * @test Ifacmp
+ * @requires vm.gc == null
+ * @summary if_acmpeq/ne bytecode test
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @compile --source 23 Ifacmp.java
+ * @run main/othervm -Xms16m -Xmx16m -XX:+UseSerialGC runtime.valhalla.inlinetypes.Ifacmp
+ */
+public class Ifacmp {
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue {
+        int value;
+        public MyValue(int v) { this.value = v; }
+    };
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue2 {
+        int value;
+        public MyValue2(int v) { this.value = v; }
+    };
+
+    boolean acmpModeInlineAlwaysFalse = false;
+
+    Object aNull = null;
+    Object bNull = null;
+
+    Object aObject = new String("Hi");
+    Object bObject = new String("Hi");
+
+    Object aValue = new MyValue(1);
+    Object bValue = new MyValue(1);
+    Object cValue = new MyValue(0);
+    Object aValue2 = new MyValue2(4711);
+
+    Object[][] equalUseCases = {
+        { aNull, bNull },
+        { aObject, aObject },
+        { aValue, bValue },
+        { cValue, cValue },
+        { aValue2, aValue2 }
+    };
+
+    int objectEqualsUseCases = 2; // Nof object equals use cases
+
+    // Would just generate these fail case from the "equal set" above,
+    // but to do so needs ==, so write out by hand it is...
+    Object[][] notEqualUseCases = {
+        { aNull, aObject },
+        { aNull, bObject },
+        { aNull, aValue },
+        { aNull, bValue },
+        { aNull, cValue },
+        { aNull, aValue2 },
+        { aObject, bObject },
+        { aObject, aValue },
+        { aObject, bValue },
+        { aObject, cValue },
+        { aObject, aValue2 },
+        { bObject, cValue },
+        { bObject, aValue2 },
+        { aValue, cValue },
+        { aValue, aValue2 },
+    };
+
+    public Ifacmp() { this(false); }
+    public Ifacmp(boolean acmpModeInlineAlwaysFalse) {
+        this.acmpModeInlineAlwaysFalse = acmpModeInlineAlwaysFalse;
+        if (acmpModeInlineAlwaysFalse) {
+            System.out.println("ifacmp always false for inline types");
+        } else {
+            System.out.println("ifacmp substitutability inline types");
+        }
+    }
+
+    public void test() {
+        testAllUseCases();
+    }
+
+    public void testUntilGc(int nofGc) {
+        for (int i = 0; i < nofGc; i++) {
+            System.out.println("GC num " + (i + 1));
+            testUntilGc();
+        }
+    }
+
+    public void testUntilGc() {
+        Reference ref = new WeakReference<Object>(new Object(), new ReferenceQueue<>());
+        do {
+            test();
+        } while (ref.get() != null);
+    }
+
+    public void testAllUseCases() {
+        int useCase = 0;
+        for (Object[] pair : equalUseCases) {
+            useCase++;
+            boolean equal = acmpModeInlineAlwaysFalse ? (useCase <= objectEqualsUseCases) : true;
+            checkEqual(pair[0], pair[1], equal);
+        }
+        for (Object[] pair : notEqualUseCases) {
+            checkEqual(pair[0], pair[1], false);
+        }
+        testLocalValues();
+        testAlot();
+    }
+
+    public void testValues() {
+        checkEqual(aValue, bValue, true);
+
+        checkEqual(aValue, cValue, false);
+        checkEqual(aValue, aValue2, false);
+        checkEqual(aValue2, bValue, false);
+        checkEqual(aValue2, cValue, false);
+        testLocalValues();
+    }
+
+    public void testLocalValues() {
+        // "aload + ifacmp" should be same as "aaload + ifamcp"
+        // but let's be paranoid...
+        MyValue a = new MyValue(11);
+        MyValue b = new MyValue(11);
+        MyValue c = a;
+        MyValue a1 = new MyValue(7);
+        MyValue2 a2 = new MyValue2(13);
+
+        if (acmpModeInlineAlwaysFalse) {
+            if (a == b) throw new RuntimeException("Always false fail " + a + " == " + b);
+            if (a == c) throw new RuntimeException("Always false fail " + a + " == " + c);
+        } else {
+            if (a != b) throw new RuntimeException("Substitutability test failed" + a + " != " + b);
+            if (a != c) throw new RuntimeException("Substitutability test failed");
+        }
+        if (a == a1) throw new RuntimeException();
+        checkEqual(a, a2, false);
+    }
+
+    public void testAlot() {
+        MyValue a = new MyValue(4711);
+        Reference ref = new WeakReference<Object>(new Object(), new ReferenceQueue<>());
+        do {
+            for (int i = 0; i < 1000; i++) {
+                MyValue b = new MyValue(4711);
+                if (acmpModeInlineAlwaysFalse) {
+                    if (a == b) throw new RuntimeException("Always false fail " + a + " == " + b);
+                } else {
+                    if (a != b) throw new RuntimeException("Substitutability test failed" + a + " != " + b);
+                }
+            }
+            System.gc();
+        } while (ref.get() != null);
+    }
+
+    boolean shouldEqualSelf(Object a) {
+        return acmpModeInlineAlwaysFalse ? (!(a != null && a.getClass().isValue())) : true;
+    }
+
+    void checkEqual(Object a, Object b, boolean isEqual) {
+        testEquals(a, a, shouldEqualSelf(a));
+        testEquals(b, b, shouldEqualSelf(b));
+        testEquals(a, b, isEqual);
+        testNotEquals(a, b, !isEqual);
+    }
+
+    public static void testEquals(Object a, Object b, boolean expected) {
+        boolean isEqual = (a == b);
+        if (isEqual != expected) {
+            throw new RuntimeException("Expected " + expected + " : "
+                                       + a + " == " + b);
+        }
+    }
+
+    public static void testNotEquals(Object a, Object b, boolean expected) {
+        boolean isNotEqual = (a != b);
+        if (isNotEqual != expected) {
+            throw new RuntimeException("Expected " + expected + " : "
+                                       + a + " != " + b);
+        }
+    }
+
+    public static void main(String[] args) {
+        boolean inlineTypesAlwaysFalse = (args.length > 0) && args[0].equals("alwaysFalse");
+        new Ifacmp(inlineTypesAlwaysFalse).test();
+        new Ifacmp(inlineTypesAlwaysFalse).testUntilGc(3);
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineOops.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineOops.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineOops.java
@@ -0,0 +1,709 @@
+/*
+ * Copyright (c) 2018, 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 runtime.valhalla.inlinetypes;
+
+import java.lang.constant.ClassDesc;
+import java.lang.constant.MethodTypeDesc;
+import java.lang.invoke.*;
+import java.lang.ref.*;
+import java.util.concurrent.*;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import static jdk.test.lib.Asserts.*;
+import jdk.test.lib.Utils;
+import jdk.test.whitebox.WhiteBox;
+import runtime.valhalla.inlinetypes.InlineOops.FooValue;
+import test.java.lang.invoke.lib.InstructionHelper;
+import static test.java.lang.invoke.lib.InstructionHelper.classDesc;
+
+/**
+ * @test id=Serial
+ * @requires vm.gc.Serial
+ * @summary Test embedding oops into Inline types
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @library /test/lib /test/jdk/java/lang/invoke/common
+ * @build test.java.lang.invoke.lib.InstructionHelper
+ * @enablePreview
+ * @compile Person.java InlineOops.java
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ *                   jdk.test.whitebox.WhiteBox$WhiteBoxPermission
+ * @run main/othervm -XX:+UseSerialGC -Xmx128m -XX:InlineFieldMaxFlatSize=128
+ *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   runtime.valhalla.inlinetypes.InlineOops
+ */
+
+/**
+ * @test id=G1
+ * @requires vm.gc.G1
+ * @summary Test embedding oops into Inline types
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @library /test/lib /test/jdk/java/lang/invoke/common
+ * @build test.java.lang.invoke.lib.InstructionHelper
+ * @enablePreview
+ * @compile Person.java InlineOops.java
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ *                   jdk.test.whitebox.WhiteBox$WhiteBoxPermission
+ * @run main/othervm -XX:+UseG1GC -Xmx128m -XX:InlineFieldMaxFlatSize=128
+ *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   runtime.valhalla.inlinetypes.InlineOops 20
+ */
+
+/**
+ * @test id=Parallel
+ * @requires vm.gc.Parallel
+ * @summary Test embedding oops into Inline types
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @library /test/lib /test/jdk/java/lang/invoke/common
+ * @build test.java.lang.invoke.lib.InstructionHelper
+ * @enablePreview
+ * @compile Person.java InlineOops.java
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ *                   jdk.test.whitebox.WhiteBox$WhiteBoxPermission
+ * @run main/othervm -XX:+UseParallelGC -Xmx128m -XX:InlineFieldMaxFlatSize=128
+ *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   runtime.valhalla.inlinetypes.InlineOops
+ */
+
+/**
+ * @test id=Z
+ * @requires vm.gc.Z
+ * @summary Test embedding oops into Inline types
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @library /test/lib /test/jdk/java/lang/invoke/common
+ * @build test.java.lang.invoke.lib.InstructionHelper
+ * @enablePreview
+ * @compile Person.java InlineOops.java
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ *                   jdk.test.whitebox.WhiteBox$WhiteBoxPermission
+ * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xmx128m
+ *                   -XX:+UnlockDiagnosticVMOptions -XX:+ZVerifyViews -XX:InlineFieldMaxFlatSize=128
+ *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   runtime.valhalla.inlinetypes.InlineOops
+ */
+
+/**
+ * @test id=ZGen
+ * @requires vm.gc.Z & vm.opt.final.ZGenerational
+ * @summary Test embedding oops into Inline types
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @library /test/lib /test/jdk/java/lang/invoke/common
+ * @build test.java.lang.invoke.lib.InstructionHelper
+ * @enablePreview
+ * @compile Person.java InlineOops.java
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ *                   jdk.test.whitebox.WhiteBox$WhiteBoxPermission
+ * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -XX:+ZGenerational -Xmx128m
+ *                   -XX:+UnlockDiagnosticVMOptions -XX:+ZVerifyViews -XX:InlineFieldMaxFlatSize=128
+ *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                   runtime.valhalla.inlinetypes.InlineOops
+ */
+public class InlineOops {
+
+    // Extra debug: -XX:+VerifyOops -XX:+VerifyStack -XX:+VerifyLastFrame -XX:+VerifyBeforeGC -XX:+VerifyAfterGC -XX:+VerifyDuringGC -XX:VerifySubSet=threads,heap
+    // Even more debugging: -XX:+TraceNewOopMapGeneration -Xlog:gc*=info
+
+    static final int NOF_PEOPLE = 10000; // Exercise arrays of this size
+
+    static int MIN_ACTIVE_GC_COUNT = 10; // Run active workload for this number of GC passes
+
+    static int MED_ACTIVE_GC_COUNT = 4;  // Medium life span in terms of GC passes
+
+    static final String TEST_STRING1 = "Test String 1";
+    static final String TEST_STRING2 = "Test String 2";
+
+    static WhiteBox WB = WhiteBox.getWhiteBox();
+
+    static boolean USE_COMPILER = WB.getBooleanVMFlag("UseCompiler");
+
+    static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+
+    public static void main(String[] args) {
+        if (args.length > 0) {
+            MIN_ACTIVE_GC_COUNT = Integer.parseInt(args[0]);
+        }
+        testClassLoad();
+        testValues();
+
+        if (!USE_COMPILER) {
+            testOopMaps();
+        }
+
+        // Check we survive GC...
+        testOverGc();   // Exercise root scan / oopMap
+        testActiveGc(); // Brute force
+    }
+
+    /**
+     * Test ClassFileParser can load inline types with reference fields
+     */
+    public static void testClassLoad() {
+        String s = Person.class.toString();
+        new Bar();
+        new BarWithValue();
+        s = BarValue.class.toString();
+        s = ObjectWithObjectValue.class.toString();
+        s = ObjectWithObjectValues.class.toString();
+    }
+
+
+    static class Couple {
+        @NullRestricted
+        public Person onePerson;
+        @NullRestricted
+        public Person otherPerson;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Composition {
+        @NullRestricted
+        public Person onePerson;
+        @NullRestricted
+        public Person otherPerson;
+
+        public Composition(Person onePerson, Person otherPerson) {
+            this.onePerson = onePerson;
+            this.otherPerson = otherPerson;
+        }
+    }
+
+    /**
+     * Check inline type operations with "Valhalla Inline Types" (VVT)
+     */
+    public static void testValues() {
+        // Exercise creation, getfield, vreturn with null refs
+        validateDefaultPerson(createDefaultPerson());
+
+        // anewarray, aaload, aastore
+        int index = 7;
+        Person[] array = (Person[])ValueClass.newNullRestrictedArray(Person.class, NOF_PEOPLE);
+        validateDefaultPerson(array[index]);
+
+        // Now with refs...
+        validateIndexedPerson(createIndexedPerson(index), index);
+        array[index] = createIndexedPerson(index);
+        validateIndexedPerson(array[index], index);
+
+        // Check the neighbours
+        validateDefaultPerson(array[index - 1]);
+        validateDefaultPerson(array[index + 1]);
+
+        // getfield/putfield
+        Couple couple = new Couple();
+        validateDefaultPerson(couple.onePerson);
+        validateDefaultPerson(couple.otherPerson);
+
+        couple.onePerson = createIndexedPerson(index);
+        validateIndexedPerson(couple.onePerson, index);
+
+        Composition composition = new Composition(couple.onePerson, couple.onePerson);
+        validateIndexedPerson(composition.onePerson, index);
+        validateIndexedPerson(composition.otherPerson, index);
+    }
+
+    /**
+     * Check oop map generation for klass layout and frame...
+     */
+    public static void testOopMaps() {
+        Object[] objects = WB.getObjectsViaKlassOopMaps(new Couple());
+        assertTrue(objects.length == 4, "Expected 4 oops");
+        for (int i = 0; i < objects.length; i++) {
+            assertTrue(objects[i] == null, "not-null");
+        }
+
+        String fn1 = "Sam";
+        String ln1 = "Smith";
+        String fn2 = "Jane";
+        String ln2 = "Jones";
+        Couple couple = new Couple();
+        couple.onePerson = new Person(0, fn1, ln1);
+        couple.otherPerson = new Person(1, fn2, ln2);
+        objects = WB.getObjectsViaKlassOopMaps(couple);
+        assertTrue(objects.length == 4, "Expected 4 oops");
+        assertTrue(objects[0] == fn1, "Bad oop fn1");
+        assertTrue(objects[1] == ln1, "Bad oop ln1");
+        assertTrue(objects[2] == fn2, "Bad oop fn2");
+        assertTrue(objects[3] == ln2, "Bad oop ln2");
+
+        objects = WB.getObjectsViaOopIterator(couple);
+        assertTrue(objects.length == 4, "Expected 4 oops");
+        assertTrue(objects[0] == fn1, "Bad oop fn1");
+        assertTrue(objects[1] == ln1, "Bad oop ln1");
+        assertTrue(objects[2] == fn2, "Bad oop fn2");
+        assertTrue(objects[3] == ln2, "Bad oop ln2");
+
+        // Array..
+        objects = WB.getObjectsViaOopIterator(createPeople());
+        assertTrue(objects.length == NOF_PEOPLE * 2, "Unexpected length: " + objects.length);
+        int o = 0;
+        for (int i = 0; i < NOF_PEOPLE; i++) {
+            assertTrue(objects[o++].equals(firstName(i)), "Bad firstName");
+            assertTrue(objects[o++].equals(lastName(i)), "Bad lastName");
+        }
+
+        // Sanity check, FixMe need more test cases
+        objects = testFrameOops(couple);
+        assertTrue(objects.length == 5, "Number of frame oops incorrect = " + objects.length);
+        assertTrue(objects[0] == couple, "Bad oop 0");
+        assertTrue(objects[1] == fn1, "Bad oop 1");
+        assertTrue(objects[2] == ln1, "Bad oop 2");
+        assertTrue(objects[3] == TEST_STRING1, "Bad oop 3");
+        assertTrue(objects[4] == TEST_STRING2, "Bad oop 4");
+
+        testFrameOopsVBytecodes();
+    }
+
+    static final String GET_OOP_MAP_NAME = "getOopMap";
+    static final MethodTypeDesc GET_OOP_MAP_DESC = MethodTypeDesc.ofDescriptor("()[Ljava/lang/Object;");
+
+    public static Object[] getOopMap() {
+        Object[] oopMap = WB.getObjectsViaFrameOopIterator(2);
+        /* Remove this frame (class mirror for this method), and above class mirror */
+        Object[] trimmedOopMap = new Object[oopMap.length - 2];
+        System.arraycopy(oopMap, 2, trimmedOopMap, 0, trimmedOopMap.length);
+        return trimmedOopMap;
+    }
+
+    // Expecting Couple couple, Person couple.onePerson, and Person (created here)
+    public static Object[] testFrameOops(Couple couple) {
+        int someId = 89898;
+        Person person = couple.onePerson;
+        assertTrue(person.getId() == 0, "Bad Person");
+        Person anotherPerson = new Person(someId, TEST_STRING1, TEST_STRING2);
+        assertTrue(anotherPerson.getId() == someId, "Bad Person");
+        return getOopMap();
+    }
+
+    // Debug...
+    static void dumpOopMap(Object[] oopMap) {
+        System.out.println("Oop Map len: " + oopMap.length);
+        for (int i = 0; i < oopMap.length; i++) {
+            System.out.println("[" + i + "] = " + oopMap[i]);
+        }
+    }
+
+    /**
+     * Just some check sanity checks with aconst_init, withfield, astore and aload
+     *
+     * Changes to javac slot usage may well break this test
+     */
+    public static void testFrameOopsVBytecodes() {
+        int nofOopMaps = 4;
+        Object[][] oopMaps = new Object[nofOopMaps][];
+        String[] inputArgs = new String[] { "aName", "aDescription", "someNotes" };
+
+        FooValue.testFrameOopsDefault(oopMaps);
+
+        // Test-D0 Slots=R Stack=Q(RRR)RV
+        assertTrue(oopMaps[0].length == 5 &&
+                oopMaps[0][1] == null &&
+                oopMaps[0][2] == null &&
+                oopMaps[0][3] == null, "Test-D0 incorrect");
+
+        // Test-D1 Slots=R Stack=RV
+        assertTrue(oopMaps[1].length == 2, "Test-D1 incorrect");
+
+        // Test-D2 Slots=RQ(RRR) Stack=RV
+        assertTrue(oopMaps[2].length == 5 &&
+                oopMaps[2][1] == null &&
+                oopMaps[2][2] == null &&
+                oopMaps[2][3] == null, "Test-D2 incorrect");
+
+        // Test-D3 Slots=R Stack=Q(RRR)RV
+        assertTrue(oopMaps[3].length == 6 &&
+                oopMaps[3][1] == null &&
+                oopMaps[3][2] == null &&
+                oopMaps[3][3] == null &&
+                oopMaps[3][4] == null, "Test-D3 incorrect");
+
+        // With ref fields...
+        String name = "TestName";
+        String desc = "TestDesc";
+        String note = "TestNotes";
+        FooValue.testFrameOopsRefs(name, desc, note, oopMaps);
+
+        // Test-R0 Slots=RR Stack=Q(RRR)RV
+        assertTrue(oopMaps[0].length == 6 &&
+                oopMaps[0][2] == name &&
+                oopMaps[0][3] == desc &&
+                oopMaps[0][4] == note, "Test-R0 incorrect");
+
+        /**
+         * TODO: vwithfield from method handle cooked from anonymous class within the inline class
+         *       even with "MethodHandles.privateLookupIn()" will fail final putfield rules
+         */
+    }
+
+    /**
+     * Check forcing GC for combination of VT on stack/LVT etc works
+     */
+    public static void testOverGc() {
+        try {
+            Class<?> vtClass = Person.class;
+
+            System.out.println("vtClass="+vtClass);
+
+            doGc();
+
+            // VT on stack and lvt, null refs, see if GC flies
+            MethodHandle moveValueThroughStackAndLvt = InstructionHelper.buildMethodHandle(
+                    LOOKUP,
+                    "gcOverPerson",
+                    MethodType.methodType(vtClass, vtClass),
+                    CODE->{
+                        CODE
+                        .aload(0)
+                        .invokestatic(classDesc(InlineOops.class), "doGc", MethodTypeDesc.ofDescriptor("()V")) // Stack
+                        .astore(0)
+                        .invokestatic(classDesc(InlineOops.class), "doGc", MethodTypeDesc.ofDescriptor("()V")) // LVT
+                        .aload(0)
+                        .astore(1024) // LVT wide index
+                        .aload(1024)
+                        .iconst_1()  // push a litte further down
+                        .invokestatic(classDesc(InlineOops.class), "doGc", MethodTypeDesc.ofDescriptor("()V")) // Stack,LVT
+                        .pop()
+                        .areturn();
+                    });
+            Person person = (Person) moveValueThroughStackAndLvt.invokeExact(createDefaultPerson());
+            validateDefaultPerson(person);
+            doGc();
+
+            int index = 4711;
+            person = (Person) moveValueThroughStackAndLvt.invokeExact(createIndexedPerson(index));
+            validateIndexedPerson(person, index);
+            doGc();
+            person = createDefaultPerson();
+            doGc();
+        }
+        catch (Throwable t) { fail("testOverGc", t); }
+    }
+
+    static void submitNewWork(ForkJoinPool fjPool) {
+        for (int j = 0; j < 100; j++) {
+            fjPool.execute(InlineOops::testValues);
+        }
+    }
+
+    static void sleepNoThrow(long ms) {
+        try {
+            Thread.sleep(ms);
+        }
+        catch (Throwable t) {}
+    }
+
+    /**
+     * Run some workloads with different object/value life times...
+     */
+    public static void testActiveGc() {
+        try {
+            int nofThreads = 1;
+
+            Object longLivedObjects = createLongLived();
+            Object longLivedPeople = createPeople();
+
+            Object medLivedObjects = createLongLived();
+            Object medLivedPeople = createPeople();
+
+            doGc();
+
+            // Setup some background work, where GC roots are stack local only, short lifetimes...
+            ForkJoinPool fjPool = new ForkJoinPool(nofThreads, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);
+
+            // Work on this stack's long and medium lived objects
+            for (int nofActiveGc = 0; nofActiveGc < MIN_ACTIVE_GC_COUNT; nofActiveGc++) {
+                // Medium lifetime, check and renew
+                if (nofActiveGc % MED_ACTIVE_GC_COUNT == 0) {
+                    validateLongLived(medLivedObjects);
+                    validatePeople(medLivedPeople);
+
+                    medLivedObjects = createLongLived();
+                    medLivedPeople = createPeople();
+                }
+                // More short lived background, if needed
+                if (!fjPool.hasQueuedSubmissions()) {
+                    submitNewWork(fjPool);
+                }
+                // Forced, synchronous GC
+                doGc();
+            }
+
+            fjPool.shutdown();
+
+            validateLongLived(medLivedObjects);
+            validatePeople(medLivedPeople);
+            medLivedObjects = null;
+            medLivedPeople = null;
+
+            validateLongLived(longLivedObjects);
+            validatePeople(longLivedPeople);
+
+            longLivedObjects = null;
+            longLivedPeople = null;
+
+            doGc();
+        }
+        catch (Throwable t) { fail("testActiveGc", t); }
+    }
+
+    static final ReferenceQueue<Object> REFQ = new ReferenceQueue<>();
+
+    public static void doGc() {
+        WB.fullGC();
+    }
+
+    static void validatePerson(Person person, int id, String fn, String ln, boolean equals) {
+        assertTrue(person.id == id);
+        if (equals) {
+            assertTrue(fn.equals(person.getFirstName()), "Invalid field firstName");
+            assertTrue(ln.equals(person.getLastName()), "Invalid  field lastName");
+        }
+        else {
+            assertTrue(person.getFirstName() == fn, "Invalid field firstName");
+            assertTrue(person.getLastName() == ln, "Invalid  field lastName");
+        }
+    }
+
+    static Person createIndexedPerson(int i) {
+        return new Person(i, firstName(i), lastName(i));
+    }
+
+    static void validateIndexedPerson(Person person, int i) {
+        validatePerson(person, i, firstName(i), lastName(i), true);
+    }
+
+    static Person createDefaultPerson() {
+        return (Person)ValueClass.newNullRestrictedArray(Person.class, 1)[0];
+    }
+
+    static void validateDefaultPerson(Person person) {
+        validatePerson(person, 0, null, null, false);
+    }
+
+    static String firstName(int i) {
+        return "FirstName-" + i;
+    }
+
+    static String lastName(int i) {
+        return "LastName-" + i;
+    }
+
+    static Object createLongLived()  throws Throwable {
+        Object[] population = new Object[1];
+        population[0] = createPeople();
+        return population;
+    }
+
+    static void validateLongLived(Object pop) throws Throwable {
+        Object[] population = (Object[]) pop;
+        validatePeople(population[0]);
+    }
+
+    static Object createPeople() {
+        int arrayLength = NOF_PEOPLE;
+        Person[] people = new Person[arrayLength];
+        for (int i = 0; i < arrayLength; i++) {
+            people[i] = createIndexedPerson(i);
+        }
+        return people;
+    }
+
+    static void validatePeople(Object array) {
+        Person[] people = (Person[]) array;
+        int arrayLength = people.length;
+        assertTrue(arrayLength == NOF_PEOPLE);
+        for (int i = 0; i < arrayLength; i++) {
+            validateIndexedPerson(people[i], i);
+        }
+    }
+
+    // Various field layouts...sanity testing, see MVTCombo testing for full-set
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class ObjectValue {
+        final Object object;
+
+        private ObjectValue(Object obj) {
+            object = obj;
+        }
+    }
+
+    static class ObjectWithObjectValue {
+        ObjectValue value1;
+        Object      ref1;
+    }
+
+    static class ObjectWithObjectValues {
+        ObjectValue value1;
+        ObjectValue value2;
+        Object      ref1;
+    }
+
+    static class Foo {
+        int id;
+        String name;
+        String description;
+        long timestamp;
+        String notes;
+    }
+
+    static class Bar extends Foo {
+        long extendedId;
+        String moreNotes;
+        int count;
+        String otherStuff;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    public static value class FooValue {
+        public final int id;
+        public final String name;
+        public final String description;
+        public final long timestamp;
+        public final String notes;
+
+        public FooValue() {
+            id = 0;
+            name = null;
+            description = null;
+            timestamp = 0L;
+            notes = null;
+        }
+
+        public FooValue(int id, String name, String description, long timestamp, String notes) {
+            this.id = id;
+            this.name = name;
+            this.description = description;
+            this.timestamp = timestamp;
+            this.notes = notes;
+        }
+
+        public static void testFrameOopsDefault(Object[][] oopMaps) {
+            MethodType mt = MethodType.methodType(Void.TYPE, oopMaps.getClass());
+            int oopMapsSlot   = 0;
+            int vtSlot        = 1;
+
+            // Slots 1=oopMaps
+            // OopMap Q=RRR (.name .description .someNotes)
+            try {
+                InstructionHelper.buildMethodHandle(
+                        LOOKUP, "exerciseVBytecodeExprStackWithDefault", mt,
+                        CODE->{
+                            CODE
+                            .new_(classDesc(FooValue.class))
+                            .dup()
+                            .invokespecial(classDesc(FooValue.class), "<init>", MethodTypeDesc.ofDescriptor("()V"))
+                            .aload(oopMapsSlot)
+                            .iconst_0()  // Test-D0 Slots=R Stack=Q(RRR)RV
+                            .invokestatic(classDesc(InlineOops.class), GET_OOP_MAP_NAME, GET_OOP_MAP_DESC)
+                            .aastore()
+                            .pop()
+                            .aload(oopMapsSlot)
+                            .iconst_1()  // Test-D1 Slots=R Stack=RV
+                            .invokestatic(classDesc(InlineOops.class), GET_OOP_MAP_NAME, GET_OOP_MAP_DESC)
+                            .aastore()
+                            .new_(classDesc(FooValue.class))
+                            .dup()
+                            .invokespecial(classDesc(FooValue.class), "<init>", MethodTypeDesc.ofDescriptor("()V"))
+                            .astore(vtSlot)
+                            .aload(oopMapsSlot)
+                            .iconst_2()  // Test-D2 Slots=RQ(RRR) Stack=RV
+                            .invokestatic(classDesc(InlineOops.class), GET_OOP_MAP_NAME, GET_OOP_MAP_DESC)
+                            .aastore()
+                            .aload(vtSlot)
+                            .aconst_null()
+                            .astore(vtSlot) // Storing null over the Q slot won't remove the ref, but should be single null ref
+                            .aload(oopMapsSlot)
+                            .iconst_3()  // Test-D3 Slots=R Stack=Q(RRR)RV
+                            .invokestatic(classDesc(InlineOops.class), GET_OOP_MAP_NAME, GET_OOP_MAP_DESC)
+                            .aastore()
+                            .pop()
+                            .return_();
+                        }).invoke(oopMaps);
+            } catch (Throwable t) { fail("exerciseVBytecodeExprStackWithDefault", t); }
+        }
+
+        public static void testFrameOopsRefs(String name, String description, String notes, Object[][] oopMaps) {
+            FooValue f = new FooValue(4711, name, description, 9876543231L, notes);
+            FooValue[] fa = (FooValue[])ValueClass.newNullRestrictedArray(FooValue.class, 1);
+            fa[0] = f;
+            MethodType mt = MethodType.methodType(Void.TYPE, fa.getClass(), oopMaps.getClass());
+            int fooArraySlot  = 0;
+            int oopMapsSlot   = 1;
+            try {
+                InstructionHelper.buildMethodHandle(LOOKUP, "exerciseVBytecodeExprStackWithRefs", mt,
+                        CODE->{
+                            CODE
+                            .aload(fooArraySlot)
+                            .iconst_0()
+                            .aaload()
+                            .aload(oopMapsSlot)
+                            .iconst_0()  // Test-R0 Slots=RR Stack=Q(RRR)RV
+                            .invokestatic(classDesc(InlineOops.class), GET_OOP_MAP_NAME, GET_OOP_MAP_DESC)
+                            .aastore()
+                            .pop()
+                            .return_();
+                        }).invoke(fa, oopMaps);
+            } catch (Throwable t) { fail("exerciseVBytecodeExprStackWithRefs", t); }
+        }
+    }
+
+    static class BarWithValue {
+        FooValue foo;
+        long extendedId;
+        String moreNotes;
+        int count;
+        String otherStuff;
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class BarValue {
+        @NullRestricted
+        FooValue foo;
+        long extendedId;
+        String moreNotes;
+        int count;
+        String otherStuff;
+
+        private BarValue(FooValue f, long extId, String mNotes, int c, String other) {
+            foo = f;
+            extendedId = extId;
+            moreNotes = mNotes;
+            count = c;
+            otherStuff = other;
+        }
+    }
+
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineTypeArray.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineTypeArray.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineTypeArray.java
@@ -0,0 +1,601 @@
+/*
+ * Copyright (c) 2018, 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 runtime.valhalla.inlinetypes;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+
+import static jdk.test.lib.Asserts.*;
+
+/*
+ * @test InlineTypeArray
+ * @summary Plain array test for Inline Types
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @library /test/lib
+ * @enablePreview
+ * @compile --source 23 InlineTypeArray.java Point.java Long8Inline.java Person.java
+ * @run main/othervm -XX:FlatArrayElementMaxSize=-1 runtime.valhalla.inlinetypes.InlineTypeArray
+ * @run main/othervm -XX:FlatArrayElementMaxSize=0 runtime.valhalla.inlinetypes.InlineTypeArray
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:ForceNonTearable=* runtime.valhalla.inlinetypes.InlineTypeArray
+ */
+public class InlineTypeArray {
+    public static void main(String[] args) {
+        InlineTypeArray inlineTypeArray = new InlineTypeArray();
+        inlineTypeArray.run();
+    }
+
+    public void run() {
+        testClassForName();
+        testSimplePointArray();
+        testLong8Array();
+        testMixedPersonArray();
+        testMultiDimPointArray();
+        testComposition();
+
+        testSanityCheckcasts();
+        testObjectArrayOfInlines();
+
+        testReflectArray();
+        testUtilArrays();
+
+        testInlineArrayOom();
+    }
+
+    void testClassForName() {
+        String arrayClsName = "[Lruntime.valhalla.inlinetypes.Point;";
+        try {
+            Class<?> arrayCls = Class.forName(arrayClsName);
+            assertTrue(arrayCls.isArray(), "Expected an array class");
+
+            arrayClsName = "[" + arrayClsName;
+            Class<?> mulArrayCls = Class.forName(arrayClsName);
+            assertTrue(mulArrayCls.isArray());
+            assertTrue(mulArrayCls.getComponentType() == arrayCls);
+        }
+        catch (ClassNotFoundException cnfe) {
+            fail("Class.forName(" + arrayClsName + ") failed", cnfe);
+        }
+    }
+
+    void testSimplePointArray() {
+        Point[] defaultPoint = (Point[])ValueClass.newNullRestrictedArray(Point.class, 1);
+        Point p = defaultPoint[0];
+        assertEquals(p.x, 0, "invalid default loaded from array");
+        assertEquals(p.y, 0, "invalid default loaded from array");
+        boolean gotNpe = false;
+        try {
+            defaultPoint[0] = (Point) getNull();
+        } catch (NullPointerException npe) {
+            gotNpe = true;
+        }
+        assertTrue(gotNpe, "Expected NullPointerException");
+
+        Point[] points = createSimplePointArray();
+        System.gc(); // check that VTs survive GC
+        checkSimplePointArray(points);
+        assertTrue(points instanceof Point[], "Instance of");
+
+        testSimplePointArrayCopy();
+
+        // Locked/unlocked flat array type checks
+        points = createSimplePointArray();
+        Point[] pointsCopy = (Point[])ValueClass.newNullRestrictedArray(Point.class, points.length);
+        synchronized (points) {
+            assertTrue(points instanceof Point[], "Instance of");
+            checkSimplePointArray(points);
+            System.arraycopy(points, 0, pointsCopy, 0, points.length);
+            synchronized (pointsCopy) {
+                assertTrue(pointsCopy instanceof Point[], "Instance of");
+                checkSimplePointArray(pointsCopy);
+                System.gc();
+            }
+            System.gc();
+        }
+        assertTrue(pointsCopy instanceof Point[], "Instance of");
+        checkSimplePointArray(pointsCopy);
+    }
+
+    void testSimplePointArrayCopy() {
+        Point[] points = createSimplePointArray();
+        Point[] pointsCopy = (Point[])ValueClass.newNullRestrictedArray(Point.class, points.length);
+        System.arraycopy(points, 0, pointsCopy, 0, points.length);
+        checkSimplePointArray(pointsCopy);
+
+        // Conjoint, overlap...left
+        System.arraycopy(points, 0, points, 1, 2);
+        checkArrayElementsEqual(points, new Point[] { pointsCopy[0], pointsCopy[0], pointsCopy[1], pointsCopy[3] });
+
+        // Conjoint, overlap...right
+        points = createSimplePointArray();
+        System.arraycopy(points, 2, points, 1, 2);
+        checkArrayElementsEqual(points, new Point[] { pointsCopy[0], pointsCopy[2], pointsCopy[3], pointsCopy[3] });
+    }
+
+    static Point[] createSimplePointArray() {
+        Point[] ps = (Point[])ValueClass.newNullRestrictedArray(Point.class, 4);
+        assertEquals(ps.length, 4, "Length");
+        ps.toString();
+        ps[0] = new Point(1, 2);
+        ps[1] = new Point(3, 4);
+        ps[2] = new Point(5, 6);
+        ps[3] = new Point(7, 8);
+        boolean sawOob = false;
+        try {
+            ps[ps.length] = new Point(0, 0);
+        } catch (ArrayIndexOutOfBoundsException aioobe) { sawOob = true; }
+        assertTrue(sawOob, "Didn't see AIOOBE");
+        return ps;
+    }
+
+    static void checkSimplePointArray(Point[] points) {
+        assertEquals(points[0].x, 1, "invalid 0 point x value");
+        assertEquals(points[0].y, 2, "invalid 0 point y value");
+        assertEquals(points[1].x, 3, "invalid 1 point x value");
+        assertEquals(points[1].y, 4, "invalid 1 point y value");
+        assertEquals(points[2].x, 5, "invalid 2 point x value");
+        assertEquals(points[2].y, 6, "invalid 2 point y value");
+        assertEquals(points[3].x, 7, "invalid 3 point x value");
+        assertEquals(points[3].y, 8, "invalid 3 point y value");
+    }
+
+    void testLong8Array() {
+        Long8Inline[] values = (Long8Inline[])ValueClass.newNullRestrictedArray(Long8Inline.class, 3);
+        assertEquals(values.length, 3, "length");
+        values.toString();
+        Long8Inline value = values[1];
+        long zl = 0;
+        Long8Inline.check(value, zl, zl, zl, zl, zl, zl, zl, zl);
+        values[1] = new Long8Inline(1, 2, 3, 4, 5, 6, 7, 8);
+        value = values[1];
+        Long8Inline.check(value, 1, 2, 3, 4, 5, 6, 7, 8);
+
+        Long8Inline[] copy = (Long8Inline[])ValueClass.newNullRestrictedArray(Long8Inline.class, values.length);
+        System.arraycopy(values, 0, copy, 0, values.length);
+        value = copy[1];
+        Long8Inline.check(value, 1, 2, 3, 4, 5, 6, 7, 8);
+    }
+
+    void testMixedPersonArray() {
+        Person[] people = (Person[])ValueClass.newNullRestrictedArray(Person.class, 3);
+
+        people[0] = new Person(1, "First", "Last");
+        assertEquals(people[0].getId(), 1, "Invalid Id person");
+        assertEquals(people[0].getFirstName(), "First", "Invalid First Name");
+        assertEquals(people[0].getLastName(), "Last", "Invalid Last Name");
+
+        people[1] = new Person(2, "Jane", "Wayne");
+        people[2] = new Person(3, "Bob", "Dobalina");
+
+        Person[] peopleCopy = (Person[])ValueClass.newNullRestrictedArray(Person.class, people.length);
+        System.arraycopy(people, 0, peopleCopy, 0, people.length);
+        assertEquals(peopleCopy[2].getId(), 3, "Invalid Id");
+        assertEquals(peopleCopy[2].getFirstName(), "Bob", "Invalid First Name");
+        assertEquals(peopleCopy[2].getLastName(), "Dobalina", "Invalid Last Name");
+    }
+
+    void testMultiDimPointArray() {
+        /*
+        Point[][][] multiPoints = new Point[2][3][4];
+        assertEquals(multiPoints.length, 2, "1st dim length");
+        assertEquals(multiPoints[0].length, 3, "2st dim length");
+        assertEquals(multiPoints[0][0].length, 4, "3rd dim length");
+
+        Point defaultPoint = multiPoints[1][2][3];
+        assertEquals(defaultPoint.x, 0, "invalid point x value");
+        assertEquals(defaultPoint.y, 0, "invalid point x value");
+        */
+    }
+
+    void testReflectArray() {
+        // Check the java.lang.reflect.Array.newInstance methods...
+        Class<?> cls = (Class<?>) Point[].class;
+        Point[][] array = (Point[][]) Array.newInstance(cls, 1);
+        assertEquals(array.length, 1, "Incorrect length");
+        assertTrue(array[0] == null, "Expected NULL");
+
+        Point[][][] array3 = (Point[][][]) Array.newInstance(cls, 1, 2);
+        assertEquals(array3.length, 1, "Incorrect length");
+        assertEquals(array3[0].length, 2, "Incorrect length");
+        assertTrue(array3[0][0] == null, "Expected NULL");
+
+        // Now create ObjArrays of InlineArray...
+        Point[][] barray = (Point[][]) Array.newInstance(Point.class, 1, 2);
+        assertEquals(barray.length, 1, "Incorrect length");
+        assertEquals(barray[0].length, 2, "Incorrect length");
+        barray[0][1] = new Point(1, 2);
+        Point pb = barray[0][1];
+        int x = pb.getX();
+        assertEquals(x, 1, "Bad Point Value");
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyInt implements Comparable<MyInt> {
+        int value;
+
+        private MyInt() { this(0); }
+        private MyInt(int v) { value = v; }
+        public int getValue() { return value; }
+        public String toString() { return "MyInt: " + getValue(); }
+        public int compareTo(MyInt that) { return Integer.compare(this.getValue(), that.getValue()); }
+        public boolean equals(Object o) {
+            if (o instanceof MyInt) {
+                return this.getValue() == ((MyInt) o).getValue();
+            }
+            return false;
+        }
+
+        public static MyInt create(int v) {
+            return new MyInt(v);
+        }
+
+        // Null-able fields here are a temp hack to avoid ClassCircularityError
+        public static final MyInt MIN = MyInt.create(Integer.MIN_VALUE);
+        public static final MyInt ZERO = MyInt.create(0);
+        public static final MyInt MAX = MyInt.create(Integer.MAX_VALUE);
+    }
+
+    static MyInt staticMyInt;
+    static MyInt[] staticMyIntArray;
+    static MyInt[][] staticMyIntArrayArray;
+
+    static {
+        staticMyInt = MyInt.create(-1);
+        staticMyIntArray = (MyInt[])ValueClass.newNullRestrictedArray(MyInt.class, 1);
+        staticMyIntArray[0] = staticMyInt;
+        staticMyIntArrayArray = new MyInt[][] { staticMyIntArray, staticMyIntArray };
+    }
+
+    static interface SomeSecondaryType {
+        default String hi() { return "Hi"; }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyOtherInt implements SomeSecondaryType {
+        final int value;
+        private MyOtherInt() { value = 0; }
+    }
+
+    void testSanityCheckcasts() {
+        MyInt[] myInts = (MyInt[])ValueClass.newNullRestrictedArray(MyInt.class, 1);
+        assertTrue(myInts instanceof Object[]);
+        assertTrue(myInts instanceof Comparable[]);
+        assertTrue(myInts instanceof MyInt[]);
+
+        Class<?> cls = MyInt.class;
+        assertTrue(cls.isValue());
+        Object arrObj = Array.newInstance(cls, 1);
+        assertTrue(arrObj instanceof Object[], "Not Object array");
+        assertTrue(arrObj instanceof Comparable[], "Not Comparable array");
+        assertTrue(arrObj instanceof MyInt[], "Not MyInt array");
+
+        Object[] arr = (Object[]) arrObj;
+        assertTrue(arr instanceof Comparable[], "Not Comparable array");
+        assertTrue(arr instanceof MyInt[], "Not MyInt array");
+        Comparable[] comparables = (Comparable[])arr;
+        MyInt[] myIntArr = (MyInt[]) arr;
+
+        // multi-dim, check secondary array types are setup...
+        MyOtherInt[][] matrix = new MyOtherInt[1][1];
+        assertTrue(matrix[0] instanceof MyOtherInt[]);
+        assertTrue(matrix[0] instanceof SomeSecondaryType[]);
+        assertTrue(matrix[0] instanceof MyOtherInt[]);
+
+        // Box types vs Inline...
+        MyInt[] myValueRefs = new MyInt[1];
+        assertTrue(myValueRefs instanceof MyInt[]);
+        assertTrue(myValueRefs instanceof Object[]);
+        assertTrue(myValueRefs instanceof Comparable[]);
+
+        MyInt[][] myMdValueRefs = new MyInt[1][1];
+        assertTrue(myMdValueRefs[0] instanceof MyInt[]);
+        assertTrue(myMdValueRefs[0] instanceof Object[]);
+        assertTrue(myMdValueRefs[0] instanceof Comparable[]);
+
+        // Did we break checkcast...
+        MyInt[]     va1 = (MyInt[])null;
+        MyInt[]     va2 = null;
+        MyInt[][]   va3 = (MyInt[][])null;
+        MyInt[][][] va4 = (MyInt[][][])null;
+    }
+
+
+    void testUtilArrays() {
+        // Sanity check j.u.Arrays
+
+        // cast to q-type temp effect of avoiding circularity error (decl static MyInt.ref)
+        MyInt[] myInts = (MyInt[])ValueClass.newNullRestrictedArray(MyInt.class, 2);
+        myInts[0] = (MyInt) MyInt.MAX;
+        myInts[1] = (MyInt) MyInt.MIN;
+
+        // Sanity sort another copy
+        MyInt[] copyMyInts = (MyInt[]) Arrays.copyOf(myInts, myInts.length + 1);
+        MyInt[] expected = (MyInt[])ValueClass.newNullRestrictedArray(MyInt.class, 3);
+        expected[0] = myInts[0];
+        expected[1] = myInts[1];
+        expected[2] = (MyInt) MyInt.ZERO;
+        checkArrayElementsEqual(copyMyInts, expected);
+
+        Arrays.sort(copyMyInts);
+        expected = (MyInt[])ValueClass.newNullRestrictedArray(MyInt.class, 3);
+        expected[0] = (MyInt) MyInt.MIN;
+        expected[1] = (MyInt) MyInt.ZERO;
+        expected[2] = (MyInt) MyInt.MAX;
+        checkArrayElementsEqual(copyMyInts, expected);
+
+        List myIntList = Arrays.asList(copyMyInts);
+
+        MyInt[] dest = (MyInt[])ValueClass.newNullRestrictedArray(MyInt.class, copyMyInts.length);
+        checkArrayElementsEqual(copyMyInts, myIntList.toArray(dest));
+        // This next line needs testMixedLayoutArrays to work
+        checkArrayElementsEqual(copyMyInts, myIntList.toArray());
+
+        // Sanity check j.u.ArrayList
+        ArrayList<MyInt> aList = new ArrayList<MyInt>(Arrays.asList(copyMyInts));
+        assertTrue(aList.indexOf(MyInt.MIN) == 0, "Bad Index");
+        assertTrue(aList.indexOf(MyInt.ZERO) == 1, "Bad Index");
+        assertTrue(aList.indexOf(MyInt.MAX) == 2, "Bad Index");
+
+        aList.remove(2);
+        aList.add(MyInt.create(5));
+    }
+
+
+    void testObjectArrayOfInlines() {
+        testSanityObjectArrays();
+        testMixedLayoutArrays();
+    }
+
+    void testSanityObjectArrays() {
+        Object[] objects = new Object[2];
+        assertTrue(objects[0] == null && objects[1] == null, "Not null ?");
+
+        objects[0] = MyInt.create(1);
+        objects[1] = Integer.valueOf(2);
+        assertTrue(objects[0].equals(MyInt.create(1)), "Bad Value");
+        assertTrue(objects[1].equals(Integer.valueOf(2)), "Bad Object");
+
+        Comparable[] copyComparables = new Comparable[objects.length];
+        System.arraycopy(objects, 0, copyComparables, 0, objects.length);
+        checkArrayElementsEqual(objects, copyComparables);
+
+        objects[0] = null;
+        objects[1] = null;
+        assertTrue(objects[0] == null && objects[1] == null, "Not null ?");
+
+        Comparable[] comparables = new Comparable[2];
+        assertTrue(comparables[0] == null && comparables[1] == null, "Not null ?");
+        comparables[0] = MyInt.create(3);
+        comparables[1] = Integer.valueOf(4);
+        assertTrue(comparables[0].equals(MyInt.create(3)), "Bad Value");
+        assertTrue(comparables[1].equals(Integer.valueOf(4)), "Bad Object");
+
+        Object[] copyObjects = new Object[2];
+        System.arraycopy(comparables, 0, copyObjects, 0, comparables.length);
+        checkArrayElementsEqual(comparables, copyObjects);
+
+        comparables[0] = null;
+        comparables[1] = null;
+        assertTrue(comparables[0] == null && comparables[1] == null, "Not null ?");
+
+        MyInt[] myIntRefArray = new MyInt[1];
+        assertTrue(myIntRefArray[0] == null, "Got: " + myIntRefArray[0]);
+        myIntRefArray[0] = null;
+
+        MyInt[] srcNulls = new MyInt[2];
+        MyInt[] dstNulls = new MyInt[2];
+        System.arraycopy(srcNulls, 0, dstNulls, 0, 2);
+        checkArrayElementsEqual(srcNulls, dstNulls);
+        srcNulls[1] = MyInt.create(1);
+        System.arraycopy(srcNulls, 0, dstNulls, 0, 2);
+        checkArrayElementsEqual(srcNulls, dstNulls);
+
+
+        // Locked/unlocked flat array type checks
+        synchronized (srcNulls) {
+            System.arraycopy(srcNulls, 0, dstNulls, 0, 2);
+            checkArrayElementsEqual(srcNulls, dstNulls);
+            System.gc();
+        }
+        System.gc();
+        checkArrayElementsEqual(srcNulls, dstNulls);
+    }
+
+    void testMixedLayoutArrays() {
+        Object[] objArray = new Object[3];
+        Comparable[] compArray = new Comparable[3];
+        MyInt[] valArray = new MyInt[] { (MyInt) MyInt.MIN, (MyInt) MyInt.ZERO, (MyInt) MyInt.MAX };
+
+        arrayCopy(valArray, 0, objArray, 0, 3);
+        checkArrayElementsEqual(valArray, objArray);
+        arrayCopy(valArray, 0, objArray, 0, 3);
+
+        objArray = new Object[3];
+        System.arraycopy(valArray, 0, objArray, 0, 3);
+        checkArrayElementsEqual(valArray, objArray);
+
+        System.arraycopy(valArray, 0, compArray, 0, 3);
+        checkArrayElementsEqual(valArray, compArray);
+
+        valArray = (MyInt[])ValueClass.newNullRestrictedArray(MyInt.class, 3);
+        valArray[0] = (MyInt) MyInt.ZERO;
+        valArray[1] = (MyInt) MyInt.ZERO;
+        valArray[2] = (MyInt) MyInt.ZERO;
+        System.arraycopy(compArray, 0, valArray, 0, 3);
+        checkArrayElementsEqual(valArray, compArray);
+
+        valArray = (MyInt[])ValueClass.newNullRestrictedArray(MyInt.class, 3);
+        valArray[0] = (MyInt) MyInt.ZERO;
+        valArray[1] = (MyInt) MyInt.ZERO;
+        valArray[2] = (MyInt) MyInt.ZERO;
+        System.arraycopy(objArray, 0, valArray, 0, 3);
+        checkArrayElementsEqual(valArray, objArray);
+
+        // Sanity check dst == src
+        System.arraycopy(valArray, 0, valArray, 0, 3);
+        checkArrayElementsEqual(valArray, objArray);
+
+        objArray[0] = "Not an inline object";
+        try {
+            System.arraycopy(objArray, 0, valArray, 0, 3);
+            throw new RuntimeException("Expected ArrayStoreException");
+        } catch (ArrayStoreException ase) {}
+
+        MyInt[] myIntRefArray = new MyInt[3];
+        System.arraycopy(valArray, 0, myIntRefArray, 0, 3);
+        checkArrayElementsEqual(valArray, myIntRefArray);
+
+        myIntRefArray[0] = null;
+        try {
+            System.arraycopy(myIntRefArray, 0, valArray, 0, 3);
+            throw new RuntimeException("Expected NullPointerException");
+        } catch (NullPointerException npe) {}
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyPoint {
+        @NullRestricted
+        MyInt x;
+        @NullRestricted
+        MyInt y;
+
+        private MyPoint() { this(0, 0); }
+        private MyPoint(int x, int y) {
+            this.x = new MyInt(x);
+            this.y = new MyInt(y);
+        }
+        public boolean equals(Object that) {
+            if (that instanceof MyPoint) {
+                MyPoint thatPoint = (MyPoint) that;
+                return x.equals(thatPoint.x) && java.util.Objects.equals(y, thatPoint.y);
+            }
+            return false;
+        }
+        static MyPoint create(int x) {
+            return new MyPoint(x, x);
+        }
+        static MyPoint create(int x, int y) {
+            return new MyPoint(x, y);
+        }
+        @NullRestricted
+        static final MyPoint ORIGIN = create(0);
+    }
+
+    void testComposition() {
+        // Test array operations with compostion of inline types, check element payload is correct...
+        MyPoint a = MyPoint.create(1, 2);
+        MyPoint b = MyPoint.create(7, 21);
+        MyPoint c = MyPoint.create(Integer.MAX_VALUE, Integer.MIN_VALUE);
+
+        MyPoint[] pts = (MyPoint[])ValueClass.newNullRestrictedArray(MyPoint.class, 3);
+        if (!pts[0].equals(MyPoint.ORIGIN)) {
+            throw new RuntimeException("Equals failed: " + pts[0] + " vs " + MyPoint.ORIGIN);
+        }
+        pts = (MyPoint[])ValueClass.newNullRestrictedArray(MyPoint.class, 3);
+        pts[0] = a;
+        pts[1] = b;
+        pts[2] = c;
+        checkArrayElementsEqual(pts, new Object[] { a, b, c});
+        Object[] oarr = new Object[3];
+
+        arrayCopy(pts, 0, oarr, 0, 3);
+        checkArrayElementsEqual(pts, oarr);
+
+        oarr = new Object[3];
+        System.arraycopy(pts, 0, oarr, 0, 3);
+        checkArrayElementsEqual(pts, oarr);
+
+        System.arraycopy(oarr, 0, pts, 0, 3);
+        checkArrayElementsEqual(pts, oarr);
+
+        oarr = new Object[3];
+        try {
+            System.arraycopy(oarr, 0, pts, 0, 3);
+            throw new RuntimeException("Expected NPE");
+        }
+        catch (NullPointerException npe) {}
+
+        oarr = new Object[3];
+        oarr[0] = new Object();
+        try {
+            System.arraycopy(oarr, 0, pts, 0, 3);
+            throw new RuntimeException("Expected ASE");
+        }
+        catch (ArrayStoreException ase) {}
+    }
+
+    void checkArrayElementsEqual(MyInt[] arr1, Object[] arr2) {
+        assertTrue(arr1.length == arr2.length, "Bad length");
+        for (int i = 0; i < arr1.length; i++) {
+            assertTrue(java.util.Objects.equals(arr1[i], arr2[i]), "Element " + i + " not equal");
+        }
+    }
+
+    void checkArrayElementsEqual(MyPoint[] arr1, Object[] arr2) {
+        assertTrue(arr1.length == arr2.length, "Bad length");
+        for (int i = 0; i < arr1.length; i++) {
+            assertTrue(java.util.Objects.equals(arr1[i], arr2[i]), "Element " + i + " not equal");
+        }
+    }
+
+    void checkArrayElementsEqual(Object[] arr1, Object[] arr2) {
+        assertTrue(arr1.length == arr2.length, "Bad length");
+        for (int i = 0; i < arr1.length; i++) {
+            assertTrue(java.util.Objects.equals(arr1[i], arr2[i]), "Element " + i + " not equal");
+        }
+    }
+
+    void arrayCopy(MyInt[] src, int srcPos, Object[] dst, int dstPos, int length) {
+        for (int i = 0; i < length ; i++) {
+            dst[dstPos++] = src[srcPos++];
+        }
+    }
+    void arrayCopy(MyPoint[] src, int srcPos, Object[] dst, int dstPos, int length) {
+        for (int i = 0; i < length ; i++) {
+            dst[dstPos++] = src[srcPos++];
+        }
+    }
+
+    Object getNull() { return null; }
+
+
+    void testInlineArrayOom() {
+        int size = Integer.MAX_VALUE;
+        try {
+            MyPoint[] pts = new MyPoint[size];
+            throw new RuntimeException("Excepted OOM");
+        } catch (OutOfMemoryError oom) {}
+    }
+
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineTypeCreation.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineTypeCreation.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineTypeCreation.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2017, 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 runtime.valhalla.inlinetypes;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+import jdk.test.lib.Asserts;
+
+/*
+ * @test InlineTypeCreation
+ * @summary Inline Type creation test
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @compile InlineTypeCreation.java Point.java Long8Inline.java Person.java
+ * @run main/othervm runtime.valhalla.inlinetypes.InlineTypeCreation
+ */
+public class InlineTypeCreation {
+    public static void main(String[] args) {
+        InlineTypeCreation inlineTypeCreation = new InlineTypeCreation();
+        inlineTypeCreation.run();
+    }
+
+    public void run() {
+        testPoint();
+        testLong8();
+        testPerson();
+        StaticSelf.test();
+        testUnresolvedAndResolvedNew();
+    }
+
+    void testPoint() {
+        Point p = new Point(1, 2);
+        Asserts.assertEquals(p.x, 1, "invalid point x value");
+        Asserts.assertEquals(p.y, 2, "invalid point y value");
+        Point p2 = clonePoint(p);
+        Asserts.assertEquals(p2.x, 1, "invalid point clone x value");
+        Asserts.assertEquals(p2.y, 2, "invalid point clone y value");
+    }
+
+    static Point clonePoint(Point p) {
+        Point q = p;
+        return q;
+    }
+
+    void testLong8() {
+        Long8Inline long8Inline = new Long8Inline(1, 2, 3, 4, 5, 6, 7, 8);
+        Asserts.assertEquals(long8Inline.getLongField1(), 1L, "Field 1 incorrect");
+        Asserts.assertEquals(long8Inline.getLongField8(), 8L, "Field 8 incorrect");
+        Long8Inline.check(long8Inline, 1, 2, 3, 4, 5, 6, 7, 8);
+    }
+
+    void testPerson() {
+        Person person = new Person(1, "John", "Smith");
+        Asserts.assertEquals(person.getId(), 1, "Id field incorrect");
+        Asserts.assertEquals(person.getFirstName(), "John", "First name incorrect");
+        Asserts.assertEquals(person.getLastName(), "Smith", "Last name incorrect");
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class StaticSelf {
+
+        static final StaticSelf DEFAULT = new StaticSelf(0);
+        int f1;
+
+        public StaticSelf(int f1) { this.f1 = f1; }
+        public String toString() { return "StaticSelf f1=" + f1; }
+
+        public static void test() {
+            String s = DEFAULT.toString();
+        }
+
+    }
+
+    static value class MyPoint {
+         int x,y;
+         MyPoint(int x, int y) {
+             this.x = x;
+             this.y = y;
+         }
+     }
+
+    // Two instantiations of the same class to exercise both the unresolved and resolved paths
+    // in bytecode 'new' implementation
+    void testUnresolvedAndResolvedNew(){
+         MyPoint p1 = new MyPoint(10, 20);
+         MyPoint p2 = new MyPoint(20, 20);
+     }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineTypeDensity.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineTypeDensity.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineTypeDensity.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 2017, 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.MemoryPoolMXBean;
+
+import com.sun.jdi.NativeMethodException;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+import jdk.test.lib.Asserts;
+import jdk.test.whitebox.WhiteBox;
+
+
+/**
+ * @test InlineTypeDensity
+ * @summary Heap density test for InlineTypes
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ *          java.base/jdk.internal.value
+ * @enablePreview
+ * @compile InlineTypeDensity.java
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -XX:FlatArrayElementMaxSize=-1 -XX:+UseCompressedOops
+ *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
+ *                    -XX:+WhiteBoxAPI InlineTypeDensity
+ * @run main/othervm -XX:FlatArrayElementMaxSize=-1 -XX:-UseCompressedOops
+ *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
+ *                    -XX:+WhiteBoxAPI InlineTypeDensity
+ * @run main/othervm -XX:FlatArrayElementMaxSize=-1
+ *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
+ *                   -XX:+WhiteBoxAPI InlineTypeDensity
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:FlatArrayElementMaxSize=-1
+ *                   -Xbootclasspath/a:. -XX:ForceNonTearable=*
+ *                   -XX:+WhiteBoxAPI InlineTypeDensity
+ */
+
+public class InlineTypeDensity {
+
+    private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+    private static final boolean VM_FLAG_FORCENONTEARABLE = WHITE_BOX.getStringVMFlag("ForceNonTearable").equals("*");
+
+    public InlineTypeDensity() {
+        if (WHITE_BOX.getIntxVMFlag("FlatArrayElementMaxSize") != -1) {
+            throw new IllegalStateException("FlatArrayElementMaxSize should be -1");
+        }
+    }
+
+    interface LocalDate {
+        public int getYear();
+        public short getMonth();
+        public short getDay();
+    }
+
+    interface LocalTime {
+        public byte getHour();
+        public byte getMinute();
+        public byte getSecond();
+        public int getNano();
+    }
+
+    interface LocalDateTime extends LocalDate, LocalTime {}
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class LocalDateValue implements LocalDate {
+        final int   year;
+        final short month;
+        final short day;
+
+        public LocalDateValue(int year, short month, short day) {
+            this.year = year;
+            this.month = month;
+            this.day = day;
+        }
+
+        public int   getYear()  { return year; }
+        public short getMonth() { return month; }
+        public short getDay()   { return day; }
+
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class LocalTimeValue implements LocalTime {
+        final byte hour;
+        final byte minute;
+        final byte second;
+        final int nano;
+
+        public LocalTimeValue(byte hour, byte minute, byte second, int nano) {
+            this.hour = hour;
+            this.minute = minute;
+            this.second = second;
+            this.nano = nano;
+        }
+
+        public byte getHour()   { return hour; }
+        public byte getMinute() { return minute; }
+        public byte getSecond() { return second; }
+        public int getNano()    { return nano; }
+
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class LocalDateTimeValue implements LocalDateTime {
+        @NullRestricted
+        LocalDateValue date;
+        @NullRestricted
+        LocalTimeValue time;
+
+        public LocalDateTimeValue(LocalDateValue date, LocalTimeValue time) {
+            this.date = date;
+            this.time = time;
+        }
+
+        public int   getYear()  { return date.year; }
+        public short getMonth() { return date.month; }
+        public short getDay()   { return date.day; }
+
+        public byte getHour()   { return time.hour; }
+        public byte getMinute() { return time.minute; }
+        public byte getSecond() { return time.second; }
+        public int getNano()    { return time.nano; }
+
+    }
+
+    static final class LocalDateClass implements LocalDate {
+        final int   year;
+        final short month;
+        final short day;
+
+        LocalDateClass(int year, short month, short day) {
+            this.year  = year;
+            this.month = month;
+            this.day   = day;
+        }
+
+        public int   getYear()  { return year; }
+        public short getMonth() { return month; }
+        public short getDay()   { return day; }
+    }
+
+    static final class LocalTimeClass implements LocalTime {
+        final byte hour;
+        final byte minute;
+        final byte second;
+        final int nano;
+
+        LocalTimeClass(byte hour, byte minute, byte second, int nano) {
+            this.hour   = hour;
+            this.minute = minute;
+            this.second = second;
+            this.nano   = nano;
+        }
+
+        public byte getHour()   { return hour; }
+        public byte getMinute() { return minute; }
+        public byte getSecond() { return second; }
+        public int getNano()    { return nano; }
+    }
+
+    static final class LocalDateTimeClass implements LocalDateTime {
+        final LocalDateClass date;
+        final LocalTimeClass time;
+
+        LocalDateTimeClass(LocalDateClass date, LocalTimeClass time) {
+            this.date = date;
+            this.time = time;
+        }
+
+        public LocalDateClass getDate() { return date; }
+        public LocalTimeClass getTime() { return time; }
+
+        public int   getYear()  { return date.year; }
+        public short getMonth() { return date.month; }
+        public short getDay()   { return date.day; }
+
+        public byte getHour()   { return time.hour; }
+        public byte getMinute() { return time.minute; }
+        public byte getSecond() { return time.second; }
+        public int getNano()    { return time.nano; }
+    }
+
+    public void ensureArraySizeWin() {
+        int arrayLength = 1000;
+        System.out.println("ensureArraySizeWin for length " + arrayLength);
+        LocalDateTimeClass[] objectArray = new LocalDateTimeClass[arrayLength];
+        for (int i = 0; i < arrayLength; i++) {
+            objectArray[i] = new LocalDateTimeClass(new LocalDateClass(0, (short)0, (short)0),
+                    new LocalTimeClass((byte)0, (byte)0, (byte)0, 0));
+        }
+
+        long objectArraySize = WHITE_BOX.getObjectSize(objectArray);
+        System.out.println("Empty object array size: " + objectArraySize);
+        objectArraySize += (arrayLength *
+                (WHITE_BOX.getObjectSize(objectArray[0]) +
+                        WHITE_BOX.getObjectSize(objectArray[0].getDate()) +
+                        WHITE_BOX.getObjectSize(objectArray[0].getTime())));
+
+        LocalDateTimeValue[] flatArray = new LocalDateTimeValue[arrayLength];
+        // CMH: add "isFlatValueArray" to WhiteBox API, to ensure we are correctly account size
+
+        long flatArraySize = WHITE_BOX.getObjectSize(flatArray);
+        System.out.println("Object array and elements: " + objectArraySize + " versus Flat Array: " + flatArraySize);
+        Asserts.assertLessThan(flatArraySize, objectArraySize, "Flat array accounts for more heap than object array + elements !");
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyByte  { byte  v = 0; }
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyShort { short v = 0; }
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyInt   { int   v = 0; }
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyLong  { long  v = 0; }
+
+    void assertArraySameSize(Object a, Object b, int nofElements) {
+        long aSize = WHITE_BOX.getObjectSize(a);
+        long bSize = WHITE_BOX.getObjectSize(b);
+        Asserts.assertEquals(aSize, bSize,
+            a + "(" + aSize + " bytes) not equivalent size " +
+            b + "(" + bSize + " bytes)" +
+            (nofElements >= 0 ? " (array of " + nofElements + " elements)" : ""));
+    }
+
+    void testByteArraySizesSame(int[] testSizes) {
+        for (int testSize : testSizes) {
+            byte[] ba = new byte[testSize];
+            MyByte[] mba = (MyByte[])ValueClass.newNullRestrictedArray(MyByte.class, testSize);
+            assertArraySameSize(ba, mba, testSize);
+        }
+    }
+
+    void testShortArraySizesSame(int[] testSizes) {
+        for (int testSize : testSizes) {
+            short[] sa = new short[testSize];
+            MyShort[] msa = (MyShort[])ValueClass.newNullRestrictedArray(MyShort.class, testSize);
+            assertArraySameSize(sa, msa, testSize);
+        }
+    }
+
+    void testIntArraySizesSame(int[] testSizes) {
+        for (int testSize : testSizes) {
+            int[] ia = new int[testSize];
+            MyInt[] mia = (MyInt[])ValueClass.newNullRestrictedArray(MyInt.class, testSize);
+            assertArraySameSize(ia, mia, testSize);
+        }
+    }
+
+    void testLongArraySizesSame(int[] testSizes) {
+        for (int testSize : testSizes) {
+            long[] la = new long[testSize];
+            MyLong[] mla = (MyLong[])ValueClass.newNullRestrictedArray(MyLong.class, testSize);
+            assertArraySameSize(la, mla, testSize);
+        }
+    }
+
+    public void testPrimitiveArraySizesSame() {
+        int[] testSizes = new int[] { 0, 1, 2, 3, 4, 7, 10, 257 };
+        testByteArraySizesSame(testSizes);
+        testShortArraySizesSame(testSizes);
+        testIntArraySizesSame(testSizes);
+        testLongArraySizesSame(testSizes);
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class bbValue { byte b = 0; byte b2 = 0;}
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class bsValue { byte b = 0; short s = 0;}
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class siValue { short s = 0; int i = 0;}
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class ssiValue { short s = 0; short s2 = 0; int i = 0;}
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class blValue { byte b = 0; long l = 0; }
+
+    // Expect aligned array addressing to nearest pow2
+    void testAlignedSize() {
+        int testSize = 10;
+        assertArraySameSize(new short[testSize], ValueClass.newNullRestrictedArray(bbValue.class, testSize), testSize);
+        assertArraySameSize(new long[testSize], ValueClass.newNullRestrictedArray(siValue.class, testSize), testSize);
+        assertArraySameSize(new long[testSize], ValueClass.newNullRestrictedArray(ssiValue.class, testSize), testSize);
+        assertArraySameSize(new long[testSize*2], ValueClass.newNullRestrictedArray(blValue.class, testSize), testSize);
+        assertArraySameSize(new int[testSize], ValueClass.newNullRestrictedArray(bsValue.class, testSize), testSize);
+    }
+
+    public void test() {
+        ensureArraySizeWin();
+        testPrimitiveArraySizesSame();
+        if (!VM_FLAG_FORCENONTEARABLE) {
+          testAlignedSize();
+        }
+    }
+
+    public static void main(String[] args) {
+        new InlineTypeDensity().test();
+    }
+
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineTypeGetField.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineTypeGetField.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineTypeGetField.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2017, 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 runtime.valhalla.inlinetypes;
+
+import jdk.internal.vm.annotation.NullRestricted;
+import jdk.test.lib.Asserts;
+
+/*
+ * @test InlineTypeGetField
+ * @summary Inline Type get field test
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @compile Point.java InlineTypeGetField.java
+ * @run main/othervm runtime.valhalla.inlinetypes.InlineTypeGetField
+ */
+public class InlineTypeGetField {
+
+    @NullRestricted
+    static Point staticPoint0;
+    @NullRestricted
+    static Point staticPoint1;
+    @NullRestricted
+    Point instancePoint0;
+    @NullRestricted
+    Point instancePoint1;
+
+    static {
+        staticPoint0 = new Point(358, 406);
+        staticPoint1 = new Point(101, 2653);
+    }
+
+    InlineTypeGetField() {
+        instancePoint0 = new Point(1890, 1918);
+        instancePoint1 = new Point(91, 102);
+    }
+
+    public static void main(String[] args) {
+        InlineTypeGetField inlineTypeGetField = new InlineTypeGetField();
+        System.gc(); // check that VTs survive GC
+        inlineTypeGetField.run();
+    }
+
+    public void run() {
+        // testing initial configuration
+        checkPoint(staticPoint0, 358, 406);
+        checkPoint(staticPoint1, 101, 2653);
+        checkPoint(instancePoint0, 1890, 1918);
+        checkPoint(instancePoint1, 91, 102);
+        // swapping static fields
+        Point p = staticPoint1;
+        staticPoint1 = staticPoint0;
+        staticPoint0 = p;
+        System.gc();
+        checkPoint(staticPoint0, 101, 2653);
+        checkPoint(staticPoint1, 358, 406);
+        //swapping instance fields
+        p = instancePoint1;
+        instancePoint1 = instancePoint0;
+        instancePoint0 = p;
+        System.gc();
+        checkPoint(instancePoint0, 91, 102);
+        checkPoint(instancePoint1, 1890, 1918);
+        // instance to static
+        staticPoint0 = instancePoint0;
+        System.gc();
+        checkPoint(staticPoint0, 91, 102);
+        // static to instance
+        instancePoint1 = staticPoint1;
+        System.gc();
+        checkPoint(instancePoint1, 358, 406);
+    }
+
+    static void checkPoint(Point p , int x, int y) {
+        Asserts.assertEquals(p.x, x, "invalid x value");
+        Asserts.assertEquals(p.y, y, "invalid y value");
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineTypesTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineTypesTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineTypesTest.java
@@ -0,0 +1,505 @@
+/*
+ * Copyright (c) 2017, 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 runtime.valhalla.inlinetypes;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.constant.ClassDesc;
+import java.lang.constant.MethodTypeDesc;
+import java.lang.invoke.*;
+import java.lang.ref.*;
+import java.nio.ByteBuffer;
+import java.time.chrono.ThaiBuddhistChronology;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.*;
+
+import static jdk.test.lib.Asserts.*;
+
+import java.lang.classfile.Label;
+import java.lang.classfile.TypeKind;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+import jdk.test.lib.Platform;
+
+import javax.tools.*;
+
+import test.java.lang.invoke.lib.InstructionHelper;
+import static test.java.lang.invoke.lib.InstructionHelper.classDesc;
+
+/**
+ * @test InlineTypesTest
+ * @summary Test data movement with inline types
+ * @modules java.base/jdk.internal.value
+ * @library /test/lib /test/jdk/java/lang/invoke/common
+ * @modules java.base/jdk.internal.vm.annotation
+ * @build test.java.lang.invoke.lib.InstructionHelper
+ * @enablePreview
+ * @compile InlineTypesTest.java
+ * @run main/othervm -Xmx128m -XX:+ExplicitGCInvokesConcurrent
+ *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
+ *                   runtime.valhalla.inlinetypes.InlineTypesTest
+ * @run main/othervm -Xmx128m -XX:+ExplicitGCInvokesConcurrent
+ *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
+ *                   -XX:ForceNonTearable=*
+ *                   runtime.valhalla.inlinetypes.InlineTypesTest
+ */
+
+ final class ContainerValue1 {
+    static TestValue1 staticInlineField;
+    @NullRestricted
+    TestValue1 nonStaticInlineField;
+    TestValue1[] valueArray;
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class TestValue1 {
+
+    static TestValue1 staticValue = getInstance();
+
+    final int i;
+    final String name;
+
+    public TestValue1() {
+        int now =  (int)System.nanoTime();
+        i = now;
+        name = Integer.valueOf(now).toString();
+    }
+
+    public TestValue1(int i) {
+        this.i = i;
+        name = Integer.valueOf(i).toString();
+    }
+
+    public static TestValue1 getInstance() {
+        return new TestValue1();
+    }
+
+    public static TestValue1 getNonBufferedInstance() {
+        return (TestValue1) staticValue;
+    }
+
+    public boolean verify() {
+        if (name == null) return i == 0;
+        return Integer.valueOf(i).toString().compareTo(name) == 0;
+    }
+}
+
+final class ContainerValue2 {
+    static TestValue2 staticInlineField;
+    @NullRestricted
+    TestValue2 nonStaticInlineField;
+    TestValue2[] valueArray;
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class TestValue2 {
+    static TestValue2 staticValue = getInstance();
+
+    final long l;
+    final double d;
+    final String s;
+
+    public TestValue2() {
+        long now = System.nanoTime();
+        l = now;
+        String stringNow = Long.valueOf(now).toString();
+        s = stringNow;
+        d = Double.parseDouble(stringNow);
+    }
+
+    public TestValue2(long l) {
+        this.l = l;
+        String txt = Long.valueOf(l).toString();
+        s = txt;
+        d = Double.parseDouble(txt);
+    }
+
+    public static TestValue2 getInstance() {
+        return new TestValue2();
+    }
+
+    public static TestValue2 getNonBufferedInstance() {
+        return (TestValue2) staticValue;
+    }
+
+    public boolean verify() {
+        if (s == null) {
+            return d == 0 && l == 0;
+        }
+        return Long.valueOf(l).toString().compareTo(s) == 0
+                && Double.parseDouble(s) == d;
+    }
+}
+
+final class ContainerValue3 {
+    static TestValue3 staticInlineField;
+    @NullRestricted
+    TestValue3 nonStaticInlineField;
+    TestValue3[] valueArray;
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class TestValue3 {
+
+    static TestValue3 staticValue = getInstance();
+
+    final byte b;
+
+    public TestValue3() {
+        b = 123;
+    }
+
+    public TestValue3(byte b) {
+        this.b = b;
+    }
+
+    public static TestValue3 getInstance() {
+        return new TestValue3();
+    }
+
+    public static TestValue3 getNonBufferedInstance() {
+        return (TestValue3) staticValue;
+    }
+
+    public boolean verify() {
+        return b == 0 || b == 123;
+    }
+}
+
+final class ContainerValue4 {
+    static TestValue4 staticInlineField;
+    @NullRestricted
+    TestValue4 nonStaticInlineField;
+    TestValue4[] valueArray;
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class TestValue4 {
+
+    static TestValue4 staticValue = getInstance();
+
+    final byte b1;
+    final byte b2;
+    final byte b3;
+    final byte b4;
+    final short s1;
+    final short s2;
+    final int i;
+    final long l;
+    final String val;
+
+    public TestValue4() {
+        this((int) System.nanoTime());
+    }
+
+    public TestValue4(int i) {
+        this.i = i;
+        val = Integer.valueOf(i).toString();
+        ByteBuffer bf = ByteBuffer.allocate(8);
+        bf.putInt(0, i);
+        bf.putInt(4, i);
+        l = bf.getLong(0);
+        s1 = bf.getShort(2);
+        s2 = bf.getShort(0);
+        b1 = bf.get(3);
+        b2 = bf.get(2);
+        b3 = bf.get(1);
+        b4 = bf.get(0);
+    }
+
+    public static TestValue4 getInstance() {
+        return new TestValue4();
+    }
+
+    public static TestValue4 getNonBufferedInstance() {
+        return (TestValue4) staticValue;
+    }
+
+    public boolean verify() {
+        if (val == null) {
+            return i == 0 && l == 0 && b1 == 0 && b2 == 0 && b3 == 0 && b4 == 0
+                    && s1 == 0 && s2 == 0;
+        }
+        ByteBuffer bf = ByteBuffer.allocate(8);
+        bf.putInt(0, i);
+        bf.putInt(4, i);
+        long nl =  bf.getLong(0);
+        bf.clear();
+        bf.putShort(0, s2);
+        bf.putShort(2, s1);
+        int from_s = bf.getInt(0);
+        bf.clear();
+        bf.put(0, b4);
+        bf.put(1, b3);
+        bf.put(2, b2);
+        bf.put(3, b1);
+        int from_b = bf.getInt(0);
+        return l == nl && Integer.valueOf(i).toString().compareTo(val) == 0
+                && from_s == i && from_b == i;
+    }
+}
+
+public class InlineTypesTest {
+
+    public static void main(String[] args) {
+        Class<?> inlineClass = runtime.valhalla.inlinetypes.TestValue1.class;
+        Class<?> testClasses[] = {
+                runtime.valhalla.inlinetypes.TestValue1.class,
+                runtime.valhalla.inlinetypes.TestValue2.class,
+                runtime.valhalla.inlinetypes.TestValue3.class,
+                runtime.valhalla.inlinetypes.TestValue4.class
+        };
+        Class<?> containerClasses[] = {
+                runtime.valhalla.inlinetypes.ContainerValue1.class,
+                runtime.valhalla.inlinetypes.ContainerValue2.class,
+                runtime.valhalla.inlinetypes.ContainerValue3.class,
+                runtime.valhalla.inlinetypes.ContainerValue4.class
+        };
+
+        for (int i = 0; i < testClasses.length; i++) {
+            try {
+                testExecutionStackToLocalVariable(testClasses[i]);
+                testExecutionStackToFields(testClasses[i], containerClasses[i]);
+                testExecutionStackToInlineArray(testClasses[i], containerClasses[i]);
+            } catch (Throwable t) {
+                t.printStackTrace();
+                throw new RuntimeException(t);
+            }
+        }
+    }
+
+    static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+
+    static void testExecutionStackToLocalVariable(Class<?> inlineClass) throws Throwable {
+        String sig = "()L" + inlineClass.getName().replace('.', '/') + ";";
+        final MethodTypeDesc voidReturnClass = MethodTypeDesc.ofDescriptor(sig);
+        final ClassDesc systemClassDesc = classDesc(System.class);
+        final ClassDesc inlineClassDesc = classDesc(inlineClass);
+        MethodHandle fromExecStackToLocalVar = InstructionHelper.buildMethodHandle(
+                LOOKUP,
+                "execStackToLocalVar",
+                MethodType.methodType(boolean.class),
+                CODE -> {
+                    CODE.invokestatic(systemClassDesc, "gc", MethodTypeDesc.ofDescriptor("()V"));
+                    int n = -1;
+                    while (n < 1024) {
+                        n++;
+                        CODE
+                        .invokestatic(inlineClassDesc, "getInstance", voidReturnClass)
+                        .astore(n);
+                        n++;
+                        CODE
+                        .invokestatic(inlineClassDesc, "getNonBufferedInstance", voidReturnClass)
+                        .astore(n);
+                    }
+                    CODE.invokestatic(systemClassDesc, "gc", MethodTypeDesc.ofDescriptor("()V"));
+                    Label endLabel = CODE.newLabel();
+                    while (n > 0) {
+                        CODE
+                        .aload(n)
+                        .invokevirtual(inlineClassDesc, "verify", MethodTypeDesc.ofDescriptor("()Z"))
+                        .iconst_1()
+                        .if_icmpne(endLabel);
+                        n--;
+                    }
+                    CODE
+                    .iconst_1()
+                    .returnInstruction(TypeKind.BooleanType)
+                    .labelBinding(endLabel)
+                    .iconst_0()
+                    .returnInstruction(TypeKind.BooleanType);
+                });
+        boolean result = (boolean) fromExecStackToLocalVar.invokeExact();
+        System.out.println(result);
+        assertTrue(result, "Invariant");
+    }
+
+    static void testExecutionStackToFields(Class<?> inlineClass, Class<?> containerClass) throws Throwable {
+        final int ITERATIONS = Platform.isDebugBuild() ? 3 : 512;
+        String sig = "()L" + inlineClass.getName().replace('.', '/') + ";";
+        final MethodTypeDesc voidReturnClass = MethodTypeDesc.ofDescriptor(sig);
+        final ClassDesc systemClassDesc = classDesc(System.class);
+        final ClassDesc inlineClassDesc = classDesc(inlineClass);
+        final ClassDesc containerClassDesc = classDesc(containerClass);
+
+        MethodHandle fromExecStackToFields = InstructionHelper.buildMethodHandle(
+                LOOKUP,
+                "execStackToFields",
+                MethodType.methodType(boolean.class),
+                CODE -> {
+                    Label loop = CODE.newLabel();
+                    Label end = CODE.newLabel();
+                    Label failed = CODE.newLabel();
+                    CODE
+                    .invokestatic(systemClassDesc, "gc", MethodTypeDesc.ofDescriptor("()V"), false)
+                    .new_(containerClassDesc)
+                    .dup()
+                    .invokespecial(containerClassDesc, "<init>", MethodTypeDesc.ofDescriptor("()V"))
+                    .astore(1)
+                    .iconst_m1()
+                    .istore(2)
+                    .labelBinding(loop)
+                    .iload(2)
+                    .ldc(ITERATIONS)
+                    .if_icmpeq(end)
+                    .aload(1)
+                    .invokestatic(inlineClassDesc, "getInstance", voidReturnClass)
+                    .putfield(containerClassDesc, "nonStaticInlineField", inlineClassDesc)
+                    .invokestatic(systemClassDesc, "gc", MethodTypeDesc.ofDescriptor("()V"))
+                    .aload(1)
+                    .getfield(containerClassDesc, "nonStaticInlineField", inlineClassDesc)
+                    .invokevirtual(inlineClassDesc, "verify", MethodTypeDesc.ofDescriptor("()Z"))
+                    .iconst_1()
+                    .if_icmpne(failed)
+                    .aload(1)
+                    .invokestatic(inlineClassDesc, "getNonBufferedInstance", voidReturnClass)
+                    .putfield(containerClassDesc, "nonStaticInlineField", inlineClassDesc)
+                    .invokestatic(systemClassDesc, "gc", MethodTypeDesc.ofDescriptor("()V"))
+                    .aload(1)
+                    .getfield(containerClassDesc, "nonStaticInlineField", inlineClassDesc)
+                    .invokevirtual(inlineClassDesc, "verify", MethodTypeDesc.ofDescriptor("()Z"))
+                    .iconst_1()
+                    .if_icmpne(failed)
+                    .invokestatic(inlineClassDesc, "getInstance", voidReturnClass)
+                    .putstatic(containerClassDesc, "staticInlineField", inlineClassDesc)
+                    .invokestatic(systemClassDesc, "gc", MethodTypeDesc.ofDescriptor("()V"))
+                    .getstatic(containerClassDesc, "staticInlineField", inlineClassDesc)
+                    .checkcast(inlineClassDesc)
+                    .invokevirtual(inlineClassDesc, "verify", MethodTypeDesc.ofDescriptor("()Z"))
+                    .iconst_1()
+                    .if_icmpne(failed)
+                    .invokestatic(inlineClassDesc, "getNonBufferedInstance", voidReturnClass)
+                    .putstatic(containerClassDesc, "staticInlineField", inlineClassDesc)
+                    .invokestatic(systemClassDesc, "gc", MethodTypeDesc.ofDescriptor("()V"))
+                    .getstatic(containerClassDesc, "staticInlineField", inlineClassDesc)
+                    .checkcast(inlineClassDesc)
+                    .invokevirtual(inlineClassDesc, "verify", MethodTypeDesc.ofDescriptor("()Z"))
+                    .iconst_1()
+                    .if_icmpne(failed)
+                    .iinc(2, 1)
+                    .goto_(loop)
+                    .labelBinding(end)
+                    .iconst_1()
+                    .returnInstruction(TypeKind.BooleanType)
+                    .labelBinding(failed)
+                    .iconst_0()
+                    .returnInstruction(TypeKind.BooleanType);
+                });
+        boolean result = (boolean) fromExecStackToFields.invokeExact();
+        System.out.println(result);
+        assertTrue(result, "Invariant");
+    }
+
+    static void testExecutionStackToInlineArray(Class<?> inlineClass, Class<?> containerClass) throws Throwable {
+        final int ITERATIONS = Platform.isDebugBuild() ? 3 : 100;
+        String sig = "()L" + inlineClass.getName().replace('.', '/') + ";";
+        final MethodTypeDesc voidReturnClass = MethodTypeDesc.ofDescriptor(sig);
+        final ClassDesc systemClassDesc = classDesc(System.class);
+        final ClassDesc inlineClassDesc = classDesc(inlineClass);
+        final ClassDesc containerClassDesc = classDesc(containerClass);
+
+        MethodHandle fromExecStackToInlineArray = InstructionHelper.buildMethodHandle(
+                LOOKUP,
+                "execStackToInlineArray",
+                MethodType.methodType(boolean.class),
+                CODE -> {
+                    Label loop1 = CODE.newLabel();
+                    Label loop2 = CODE.newLabel();
+                    Label end1 = CODE.newLabel();
+                    Label end2 = CODE.newLabel();
+                    Label failed = CODE.newLabel();
+                    CODE
+                    .invokestatic(systemClassDesc, "gc", MethodTypeDesc.ofDescriptor("()V"))
+                    .new_(containerClassDesc)
+                    .dup()
+                    .invokespecial(containerClassDesc, "<init>", MethodTypeDesc.ofDescriptor("()V"))
+                    .astore(1)
+                    .ldc(ITERATIONS * 3)
+                    .anewarray(inlineClassDesc)
+                    .astore(2)
+                    .aload(2)
+                    .aload(1)
+                    .swap()
+                    .putfield(containerClassDesc, "valueArray", inlineClassDesc.arrayType())
+                    .iconst_0()
+                    .istore(3)
+                    .labelBinding(loop1)
+                    .iload(3)
+                    .ldc(ITERATIONS *3)
+                    .if_icmpge(end1)
+                    .aload(2)
+                    .iload(3)
+                    .invokestatic(inlineClassDesc, "getInstance", voidReturnClass)
+                    .aastore()
+                    .iinc(3, 1)
+                    .aload(2)
+                    .iload(3)
+                    .invokestatic(inlineClassDesc, "getNonBufferedInstance", voidReturnClass)
+                    .aastore()
+                    .iinc(3, 1)
+                    .aload(2)
+                    .iload(3)
+                    .new_(inlineClassDesc)
+                    .dup()
+                    .invokespecial(inlineClassDesc, "<init>", MethodTypeDesc.ofDescriptor("()V"))
+                    .aastore()
+                    .iinc(3, 1)
+                    .goto_(loop1)
+                    .labelBinding(end1)
+                    .invokestatic(systemClassDesc, "gc", MethodTypeDesc.ofDescriptor("()V"))
+                    .iconst_0()
+                    .istore(3)
+                    .labelBinding(loop2)
+                    .iload(3)
+                    .ldc(ITERATIONS * 3)
+                    .if_icmpge(end2)
+                    .aload(2)
+                    .iload(3)
+                    .aaload()
+                    .invokevirtual(inlineClassDesc, "verify", MethodTypeDesc.ofDescriptor("()Z"))
+                    .iconst_1()
+                    .if_icmpne(failed)
+                    .iinc(3, 1)
+                    .goto_(loop2)
+                    .labelBinding(end2)
+                    .iconst_1()
+                    .returnInstruction(TypeKind.BooleanType)
+                    .labelBinding(failed)
+                    .iconst_0()
+                    .returnInstruction(TypeKind.BooleanType);
+                });
+        boolean result = (boolean) fromExecStackToInlineArray.invokeExact();
+        System.out.println(result);
+        assertTrue(result, "Invariant");
+    }
+
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineWithJni.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineWithJni.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/InlineWithJni.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2018, 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 runtime.valhalla.inlinetypes;
+
+/* @test
+ * @summary test JNI functions with inline types
+ * @enablePreview
+ * @run main/othervm/native runtime.valhalla.inlinetypes.InlineWithJni
+ */
+public value class InlineWithJni {
+
+    static {
+        System.loadLibrary("InlineWithJni");
+    }
+
+    public static void main(String[] args) {
+        testJniMonitorOps();
+    }
+
+    final int x;
+
+    public InlineWithJni(int x) {
+        this.x = x;
+    }
+
+    public native void doJniMonitorEnter();
+    public native void doJniMonitorExit();
+
+    public static void testJniMonitorOps() {
+        boolean sawImse = false;
+        try {
+            new InlineWithJni(0).doJniMonitorEnter();
+        } catch (Throwable t) {
+            sawImse = checkImse(t);
+        }
+        if (!sawImse) {
+            throw new RuntimeException("JNI MonitorEnter did not fail");
+        }
+        sawImse = false;
+        try {
+            new InlineWithJni(0).doJniMonitorExit();
+        } catch (Throwable t) {
+            sawImse = checkImse(t);
+        }
+        if (!sawImse) {
+            throw new RuntimeException("JNI MonitorExit did not fail");
+        }
+    }
+
+    static boolean checkImse(Throwable t) {
+        if (t instanceof IllegalMonitorStateException) {
+            return true;
+        }
+        throw new RuntimeException(t);
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/JumboInline.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/JumboInline.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/JumboInline.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2018, 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 runtime.valhalla.inlinetypes;
+
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+public value class JumboInline {
+    final long l0;
+    final long l1;
+    final long l2;
+    final long l3;
+    final long l4;
+    final long l5;
+    final long l6;
+    final long l7;
+    final long l8;
+    final long l9;
+    final long l10;
+    final long l11;
+    final long l12;
+    final long l13;
+    final long l14;
+    final long l15;
+    final long l16;
+    final long l17;
+    final long l18;
+    final long l19;
+
+    public JumboInline(long l0Val, long l1Val) {
+        l0 = l0Val;
+        l1 = l1Val;
+        l2 = l0Val+1;
+        l3 = l1Val+2;
+        l4 = l0Val+3;
+        l5 = l1Val+4;
+        l6 = l0Val+5;
+        l7 = l1Val+6;
+        l8 = l0Val+7;
+        l9 = l1Val+8;
+        l10 = l0Val+9;
+        l11 = l1Val+10;
+        l12 = l0Val+11;
+        l13 = l1Val+12;
+        l14 = l0Val+13;
+        l15 = l1Val+14;
+        l16 = l0Val+15;
+        l17 = l1Val+16;
+        l18 = l0Val+17;
+        l19 = l1Val+18;
+    }
+
+    public boolean verify() {
+        return (l2 == (l0 + 1)  &&  l3 == (l1 + 2) && l4 == (l0 + 3)
+            && l5 == (l1 + 4) && l6 == (l0 + 5) && l7 == (l1 + 6) && l8 == (l0 + 7)
+            && l9 == (l1 + 8) && l10 == (l0 + 9) && l11 == (l1 + 10)
+            && l12 == (l0 + 11) && l13 == (l1 + 12) && l14 == (l1 + 13)
+            && l15 == (l1 + 14) && l16 == (l0 + l15) && l17 == (l1 + 16)
+            && l18 == (l0 + 17) && l19 == (l1 + 18));
+    }
+
+    public boolean equals(Object o) {
+        if(o instanceof JumboInline) {
+            JumboInline j = (JumboInline)o;
+            return (l0 == j.l0 && l1 == j.l1 && l2 == j.l2 && l3 == j.l3
+                    && l4 == j.l4 && l5 == j.l5 && l6 == j.l6 && l7 == j.l7
+                    && l8 == j.l8 && l9 == j.l9 && l10 == j.l10 && l11 == j.l11
+                    && l12 == j.l12 && l13 == j.l13 && l14 == j.l14 && l15 == j.l15
+                    && l16 == j.l16 && l17 == j.l17 && l18 == j.l18 && l19 == j.l19);
+        } else {
+            return false;
+        }
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/Long8Inline.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/Long8Inline.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/Long8Inline.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015, 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 runtime.valhalla.inlinetypes;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.test.lib.Asserts;
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+public final value class Long8Inline {
+
+    final long longField1;
+    final long longField2;
+    final long longField3;
+    final long longField4;
+    final long longField5;
+    final long longField6;
+    final long longField7;
+    final long longField8;
+
+    public Long8Inline(long l1, long l2, long l3, long l4, long l5, long l6, long l7, long l8) {
+        longField1 = l1;
+        longField2 = l2;
+        longField3 = l3;
+        longField4 = l4;
+        longField5 = l5;
+        longField6 = l6;
+        longField7 = l7;
+        longField8 = l8;
+    }
+
+    public long getLongField1() { return longField1; }
+    public long getLongField2() { return longField2; }
+    public long getLongField3() { return longField3; }
+    public long getLongField4() { return longField4; }
+    public long getLongField5() { return longField5; }
+    public long getLongField6() { return longField6; }
+    public long getLongField7() { return longField7; }
+    public long getLongField8() { return longField8; }
+
+    static void check(Long8Inline value,
+            long long1,
+            long long2,
+            long long3,
+            long long4,
+            long long5,
+            long long6,
+            long long7,
+            long long8) {
+        Asserts.assertEquals(value.getLongField1(), long1, "Field 1 incorrect");
+        Asserts.assertEquals(value.getLongField2(), long2, "Field 2 incorrect");
+        Asserts.assertEquals(value.getLongField3(), long3, "Field 3 incorrect");
+        Asserts.assertEquals(value.getLongField4(), long4, "Field 4 incorrect");
+        Asserts.assertEquals(value.getLongField5(), long5, "Field 5 incorrect");
+        Asserts.assertEquals(value.getLongField6(), long6, "Field 6 incorrect");
+        Asserts.assertEquals(value.getLongField7(), long7, "Field 7 incorrect");
+        Asserts.assertEquals(value.getLongField8(), long8, "Field 8 incorrect");
+    }
+
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/NullRestrictedArrayTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/NullRestrictedArrayTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/NullRestrictedArrayTest.java
@@ -0,0 +1,176 @@
+/*
+ * 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 jdk.internal.value.ValueClass;
+import jdk.test.lib.Asserts;
+import java.lang.reflect.Method;
+import jdk.internal.misc.Unsafe;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+
+
+/*
+ * @test
+ * @summary Test of VM.newNullRestrictedArray API
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ *          java.base/jdk.internal.misc
+ *          java.base/jdk.internal.value
+ * @enablePreview
+ * @run main/othervm NullRestrictedArrayTest
+ */
+
+
+public class NullRestrictedArrayTest {
+
+  private static final Unsafe UNSAFE = Unsafe.getUnsafe();
+
+
+  public static void main(String[] args) {
+      NullRestrictedArrayTest tests = new NullRestrictedArrayTest();
+      Class c = tests.getClass();
+      for (Method m : c.getDeclaredMethods()) {
+        if (m.getName().startsWith("test_")) {
+          try {
+            System.out.println("Running " + m.getName());
+            m.invoke(tests);
+          } catch (Throwable t) {
+            t.printStackTrace();
+            throw new RuntimeException(t);
+          }
+        }
+      }
+  }
+
+  // Test illegal attempt to create a null restricted array with an identity class
+  public void test_0() {
+      Throwable exception = null;
+      try {
+        ValueClass.newNullRestrictedArray(String.class, 4);
+      } catch (IllegalArgumentException e) {
+        System.out.println("Received: " + e);
+        exception = e;
+      }
+      Asserts.assertNotNull(exception, "Expected IllegalArgumentException not received");
+  }
+
+  // Test illegal array length
+  @ImplicitlyConstructible
+  @LooselyConsistentValue
+  static value class ValueClass1 {
+    int i = 0;
+    int j = 0;
+  }
+
+  public void test_1() {
+      Throwable exception = null;
+      try {
+        ValueClass.newNullRestrictedArray(ValueClass1.class, -1);
+      } catch (IllegalArgumentException e) {
+        System.out.println("Received: " + e);
+        exception = e;
+      }
+      Asserts.assertNotNull(exception, "Expected IllegalArgumentException not received");
+  }
+
+  // Test illegal attempt to create a null restricted array with a value class not annotated with @ImplicitlyConstructible
+  static value class ValueClass2 {
+    int i = 0;
+    int j = 0;
+  }
+
+  public void test_2() {
+      Throwable exception = null;
+      try {
+        ValueClass.newNullRestrictedArray(ValueClass2.class, 8);
+      } catch (IllegalArgumentException e) {
+        System.out.println("Received: " + e);
+        exception = e;
+      }
+      Asserts.assertNotNull(exception, "Expected IllegalArgumentException not received");
+  }
+
+  // Test valid creation of a flat array
+  @ImplicitlyConstructible
+  @LooselyConsistentValue
+  static value class ValueClass3 {
+    int i = 0;
+    int j = 0;
+  }
+
+  public void test_3() {
+      Throwable exception = null;
+      try {
+        Object array = ValueClass.newNullRestrictedArray(ValueClass3.class, 8);
+        Asserts.assertTrue(UNSAFE.isFlatArray(array.getClass()), "Expecting flat array but array is not flat");
+      } catch (Throwable e) {
+        System.out.println("Received: " + e);
+        exception = e;
+      }
+      Asserts.assertNull(exception, "Unexpected exception: " + exception);
+  }
+
+  // Test that elements are not null
+  @ImplicitlyConstructible
+  @LooselyConsistentValue
+
+  static value class ValueClass4 {
+    int i = 0;
+    int j = 0;
+  }
+
+  public void test_4() {
+      Throwable exception = null;
+      try {
+        Object[] array = ValueClass.newNullRestrictedArray(ValueClass4.class, 8);
+        Asserts.assertNotNull(array[1], "Expecting non null element but null found instead");
+      } catch (Throwable e) {
+        System.out.println("Received: " + e);
+        exception = e;
+      }
+      Asserts.assertNull(exception, "Unexpected exception: " + exception);
+  }
+
+  // Test that writing null to a null restricted array throws an exception
+  @ImplicitlyConstructible
+  @LooselyConsistentValue
+  static value class ValueClass5 {
+    int i = 0;
+    int j = 0;
+  }
+
+  public void test_5() {
+      Throwable exception = null;
+      try {
+        Object[] array = ValueClass.newNullRestrictedArray(ValueClass4.class, 8);
+        array[1] = null;
+      } catch (NullPointerException e) {
+        System.out.println("Received: " + e);
+        exception = e;
+      }
+      Asserts.assertNotNull(exception, "Expected NullPointerException not received");
+  }
+
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/NullableFlatFieldTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/NullableFlatFieldTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/NullableFlatFieldTest.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2024, 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 NullableFlatFieldTest
+ * @requires vm.debug == true
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @run main/othervm -Xint -XX:+EnableNullableFieldFlattening -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlineLayout NullableFlatFieldTest
+ */
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import jdk.test.lib.Asserts;
+
+public class NullableFlatFieldTest {
+
+  @ImplicitlyConstructible
+  @LooselyConsistentValue
+  static value class Value0 {
+    long l;
+    int i;
+    short s;
+    byte b;
+
+    Value0() {
+      l = 0;
+      i = 0;
+      s = 0;
+      b = 0;
+    }
+
+    Value0(long l0, int i0, short s0, byte b0) {
+      l = l0;
+      i = i0;
+      s = s0;
+      b = b0;
+    }
+  }
+
+  static class Container0 {
+    Value0 val;
+    long l;
+    int i;
+  }
+
+  // Container0 had a external null marker located just between two Java fields,
+  // and test0 checks that updating the null marker doesn't corrupt
+  // the surrounding fields and vice-versa.
+  static void test0() {
+    Container0 c = new Container0();
+    Asserts.assertNull(c.val);
+    Asserts.assertEquals(c.l, 0L);
+    Asserts.assertEquals(c.i, 0);
+    c.l = -1L;
+    c.i = -1;
+    Asserts.assertNull(c.val);
+    Value0 v = new Value0(-1L, -1, (short)-1, (byte)-1);
+    c.val = v;
+    Asserts.assertEquals(c.l, -1L);
+    Asserts.assertEquals(c.i, -1);
+    Value0 vr = c.val;
+    Asserts.assertEquals(vr.l, -1L);
+    Asserts.assertEquals(vr.i, -1);
+    Asserts.assertEquals(vr.s, (short)-1);
+    Asserts.assertEquals(vr.b, (byte)-1);
+    c.val = null;
+    Asserts.assertEquals(c.l, -1L);
+    Asserts.assertEquals(c.i, -1);
+  }
+
+  @ImplicitlyConstructible
+  @LooselyConsistentValue
+  static value class Value1a {
+    long l;
+    short s;
+    byte b;
+
+    Value1a() {
+      l = 0;
+      s = 0;
+      b = 0;
+    }
+
+    Value1a(long l0, short s0, byte b0) {
+      l = l0;
+      s = s0;
+      b = b0;
+    }
+  }
+
+  @ImplicitlyConstructible
+  @LooselyConsistentValue
+  static value class Value1b {
+    @NullRestricted
+    Value1a vala;
+    long l;
+    int i;
+
+    Value1b() {
+      vala = new Value1a();
+      l = 1L;
+      i = 1;
+    }
+
+    Value1b(Value1a v0, long l0, int i0) {
+      vala = v0;
+      l = l0;
+      i = i0;
+    }
+  }
+
+  static class Container1 {
+    Value1b valb;
+  }
+
+  // Container1 has a nullable flat field with an internal null marker,
+  // test1 checks that updating the null marker doesn't corrupt the
+  // flat field's values
+  static void test1() {
+    Container1 c = new Container1();
+    Asserts.assertNull(c.valb);
+    Value1a va = new Value1a(-1L, (short)-1, (byte)-1);
+    Asserts.assertEquals(va.l, -1L);
+    Asserts.assertEquals(va.s, (short)-1);
+    Asserts.assertEquals(va.b, (byte)-1);
+    Value1b vb = new Value1b(va, -1L, -1);
+    Asserts.assertNotNull(vb.vala);
+    Asserts.assertEquals(vb.vala.l, -1L);
+    Asserts.assertEquals(vb.vala.s, (short)-1);
+    Asserts.assertEquals(vb.vala.b, (byte)-1);
+    c.valb = vb;
+    Asserts.assertNotNull(c.valb);
+    Asserts.assertEquals(c.valb, vb);
+    Asserts.assertEquals(c.valb.vala, va);
+    Asserts.assertEquals(c.valb.l, -1L);
+    Asserts.assertEquals(c.valb.i, -1);
+    c.valb = null;
+    Asserts.assertNull(c.valb);
+  }
+
+  @ImplicitlyConstructible
+  @LooselyConsistentValue
+  static value class Value2a {
+    long l;
+    int i;
+    byte b;
+    Value2a() {
+      l = 0;
+      i = 0;
+      b = 0;
+    }
+    Value2a(long l0, int i0, byte b0) {
+      l = l0;
+      i = i0;
+      b = b0;
+    }
+  }
+
+  @ImplicitlyConstructible
+  @LooselyConsistentValue
+  static value class Value2b {
+    long l;
+    Value2b() {
+      l = 0;
+    }
+    Value2b(long l0) {
+      l = l0;
+    }
+  }
+
+  static class Container2 {
+    Value2a vala;
+    Value2b valb0;
+    Value2b valb1;
+    int i;
+  }
+
+  // Container2 has 3 contiguous null markers,
+  // test2 checks that updating a null marker doesn't affect the other markers
+  public static void test2() {
+    Container2 c = new Container2();
+    Asserts.assertNull(c.vala);
+    Asserts.assertNull(c.valb0);
+    Asserts.assertNull(c.valb1);
+    Value2a va = new Value2a(-1L, -1, (byte)-1);
+    c.vala = va;
+    Asserts.assertNotNull(c.vala);
+    Asserts.assertNull(c.valb0);
+    Asserts.assertNull(c.valb1);
+    c.vala = null;
+    Asserts.assertNull(c.vala);
+    Asserts.assertNull(c.valb0);
+    Asserts.assertNull(c.valb1);
+    Value2b vb = new Value2b(-1L);
+    c.valb0 = vb;
+    Asserts.assertNull(c.vala);
+    Asserts.assertNotNull(c.valb0);
+    Asserts.assertNull(c.valb1);
+    c.valb0 = null;
+    Asserts.assertNull(c.vala);
+    Asserts.assertNull(c.valb0);
+    Asserts.assertNull(c.valb1);
+    c.valb1 = vb;
+    Asserts.assertNull(c.vala);
+    Asserts.assertNull(c.valb0);
+    Asserts.assertNotNull(c.valb1);
+    c.valb1 = null;
+    Asserts.assertNull(c.vala);
+    Asserts.assertNull(c.valb0);
+    Asserts.assertNull(c.valb1);
+    c.vala = va;
+    c.valb0 = vb;
+    c.valb1 = vb;
+    Asserts.assertNotNull(c.vala);
+    Asserts.assertNotNull(c.valb0);
+    Asserts.assertNotNull(c.valb1);
+    c.vala = null;
+    Asserts.assertNull(c.vala);
+    Asserts.assertNotNull(c.valb0);
+    Asserts.assertNotNull(c.valb1);
+    c.vala = va;
+    Asserts.assertNotNull(c.vala);
+    Asserts.assertNotNull(c.valb0);
+    Asserts.assertNotNull(c.valb1);
+    c.valb0 = null;
+    Asserts.assertNotNull(c.vala);
+    Asserts.assertNull(c.valb0);
+    Asserts.assertNotNull(c.valb1);
+    c.valb0 = vb;
+    Asserts.assertNotNull(c.vala);
+    Asserts.assertNotNull(c.valb0);
+    Asserts.assertNotNull(c.valb1);
+    c.valb1 = null;
+    Asserts.assertNotNull(c.vala);
+    Asserts.assertNotNull(c.valb0);
+    Asserts.assertNull(c.valb1);
+    c.valb1 = vb;
+    Asserts.assertNotNull(c.vala);
+    Asserts.assertNotNull(c.valb0);
+    Asserts.assertNotNull(c.valb1);
+  }
+
+  @ImplicitlyConstructible
+  @LooselyConsistentValue
+  static value class Value3 {
+    int i = 0;
+  }
+
+  static class Container3 {
+    Value3 val;
+  }
+
+  static Container3 getNullContainer3() {
+    return null;
+  }
+
+  public static void test3() {
+    NullPointerException npe = null;
+    Container3 c = getNullContainer3();
+    try {
+      Value3 v = c.val;
+    } catch(NullPointerException e) {
+      npe = e;
+    }
+    Asserts.assertNotNull(npe);
+  }
+
+  public static void main(String[] args) {
+    // All tests are run twice to exercise both the unresolved bytecodes and the rewritten ones
+    for (int i = 0; i < 2; i++) {
+      System.out.println("Pass " + i);
+      test0();
+      test1();
+      test2();
+      test3();
+    }
+  }
+}
+
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ObjectMethods.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ObjectMethods.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ObjectMethods.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2018, 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 runtime.valhalla.inlinetypes;
+
+import java.lang.invoke.*;
+
+import test.java.lang.invoke.lib.InstructionHelper;
+
+/*
+ * @test ObjectMethods
+ * @summary Check object methods implemented by the VM behave with value types
+ * @library /test/lib /test/jdk/java/lang/invoke/common
+ * @build test.java.lang.invoke.lib.InstructionHelper
+ * @enablePreview
+ * @compile ObjectMethods.java
+ * @run main/othervm -XX:+UseCompressedClassPointers runtime.valhalla.inlinetypes.ObjectMethods
+ * @run main/othervm -XX:-UseCompressedClassPointers runtime.valhalla.inlinetypes.ObjectMethods
+ * @run main/othervm -noverify runtime.valhalla.inlinetypes.ObjectMethods noverify
+ */
+
+public class ObjectMethods {
+
+    public static void main(String[] args) {
+        testObjectMethods((args.length > 0 && args[0].equals("noverify")));
+    }
+
+    public static void testObjectMethods(boolean verifierDisabled) {
+        MyInt val = new MyInt(7);
+        MyInt sameVal = new MyInt(7);
+
+        // Exercise all the Object native/VM methods...
+
+        if (verifierDisabled) { // Just noverifier...
+            checkMonitorExit(val);
+            return;
+        }
+
+        // getClass()
+        checkGetClass(val, MyInt.class);
+
+        //hashCode()/identityHashCode()
+        checkHashCodes(val, sameVal.hashCode());
+
+        // clone()
+        checkNotCloneable(val);
+
+        // synchronized
+        checkSynchronized(val);
+
+        // wait/notify()
+        checkWait(val);
+        checkNotify(val);
+
+        System.gc();
+    }
+
+
+    static void checkGetClass(Object val, Class<?> expectedClass) {
+        Class<?> clazz = val.getClass();
+        if (clazz == null) {
+            throw new RuntimeException("getClass return null");
+        } else if (clazz != expectedClass) {
+            throw new RuntimeException("getClass (" + clazz + ") doesn't match " + expectedClass);
+        }
+    }
+
+    // Just check we don't crash the VM
+    static void checkHashCodes(Object val, int expectedHashCode) {
+        int hash = val.hashCode();
+        if (hash != expectedHashCode) {
+            throw new RuntimeException("Hash code mismatch value: " + hash +
+                                       " expected: " + expectedHashCode);
+        }
+        hash = System.identityHashCode(val);
+        if (hash != expectedHashCode) {
+            throw new RuntimeException("Identity hash code mismatch value: " + hash +
+                                       " expected: " + expectedHashCode);
+        }
+    }
+
+    static void checkNotCloneable(MyInt val) {
+        boolean sawCnse = false;
+        try {
+            val.attemptClone();
+        } catch (CloneNotSupportedException cnse) {
+            sawCnse = true;
+        }
+        if (!sawCnse) {
+            throw new RuntimeException("clone() did not fail");
+        }
+        // Cloneable inline type checked by "BadInlineTypes" CFP tests
+    }
+
+    static void checkSynchronized(Object val) {
+        boolean sawImse = false;
+        try {
+            synchronized (val) {
+                throw new IllegalStateException("Unreachable code, reached");
+            }
+        } catch (IllegalMonitorStateException imse) {
+            sawImse = true;
+        }
+        if (!sawImse) {
+            throw new RuntimeException("monitorenter did not fail");
+        }
+        // synchronized method modifiers tested by "BadInlineTypes" CFP tests
+        // jni monitor ops tested by "InlineWithJni"
+    }
+
+    // Check we haven't broken the mismatched monitor block check...
+    static void checkMonitorExit(Object val) {
+        boolean sawImse = false;
+        try {
+            InstructionHelper.buildMethodHandle(MethodHandles.lookup(),
+                                                "mismatchedMonitorExit",
+                                                MethodType.methodType(Void.TYPE, Object.class),
+                                                CODE-> {
+                                                    CODE
+                                                    .aload(0)
+                                                    .monitorexit();
+                                                    CODE.return_();
+                                                }).invokeExact(val);
+            throw new IllegalStateException("Unreachable code, reached");
+        } catch (Throwable t) {
+            if (t instanceof IllegalMonitorStateException) {
+                sawImse = true;
+            } else {
+                throw new RuntimeException(t);
+            }
+        }
+        if (!sawImse) {
+            throw new RuntimeException("monitorexit did not fail");
+        }
+    }
+
+    static void checkWait(Object val) {
+        boolean sawImse = false;
+        try {
+            val.wait();
+        } catch (IllegalMonitorStateException imse) {
+            sawImse = true;
+        } catch (InterruptedException intExc) {
+            throw new RuntimeException(intExc);
+        }
+        if (!sawImse) {
+            throw new RuntimeException("wait() did not fail");
+        }
+
+        sawImse = false;
+        try {
+            val.wait(1l);
+        } catch (IllegalMonitorStateException imse) {
+            sawImse = true;
+        } catch (InterruptedException intExc) {
+            throw new RuntimeException(intExc);
+        }
+        if (!sawImse) {
+            throw new RuntimeException("wait() did not fail");
+        }
+
+        sawImse = false;
+        try {
+            val.wait(0l, 100);
+        } catch (IllegalMonitorStateException imse) {
+            sawImse = true;
+        } catch (InterruptedException intExc) {
+            throw new RuntimeException(intExc);
+        }
+        if (!sawImse) {
+            throw new RuntimeException("wait() did not fail");
+        }
+    }
+
+    static void checkNotify(Object val) {
+        boolean sawImse = false;
+        try {
+            val.notify();
+        } catch (IllegalMonitorStateException imse) {
+            sawImse = true;
+        }
+        if (!sawImse) {
+            throw new RuntimeException("notify() did not fail");
+        }
+
+        sawImse = false;
+        try {
+            val.notifyAll();
+        } catch (IllegalMonitorStateException imse) {
+            sawImse = true;
+        }
+        if (!sawImse) {
+            throw new RuntimeException("notifyAll() did not fail");
+        }
+    }
+
+    static value class MyInt {
+        int value;
+        public MyInt(int v) { value = v; }
+        public Object attemptClone() throws CloneNotSupportedException {
+            try { // Check it is not possible to clone...
+                MethodHandles.Lookup lookup = MethodHandles.lookup();
+                MethodHandle mh = lookup.findVirtual(getClass(),
+                                                     "clone",
+                                                     MethodType.methodType(Object.class));
+                return mh.invokeExact(this);
+            } catch (Throwable t) {
+                if (t instanceof CloneNotSupportedException) {
+                    throw (CloneNotSupportedException) t;
+                }
+                throw new RuntimeException(t);
+            }
+        }
+    }
+
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/Person.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/Person.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/Person.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2017, 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 runtime.valhalla.inlinetypes;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+public value class Person {
+
+    final int    id;
+    final String firstName;
+    final String lastName;
+
+    public Person(int id, String firstName, String lastName) {
+        this.id = id;
+        this.firstName = firstName;
+        this.lastName = lastName;
+    }
+
+    public int getId() { return id; }
+    public String getFirstName() { return firstName; }
+    public String getLastName() { return lastName; }
+
+    public String toString() {
+        return getFirstName() + " " + getLastName() + " (id=" + getId() + ")";
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/Point.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/Point.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/Point.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2017, 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 runtime.valhalla.inlinetypes;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+public value class Point {
+    final int x;
+    final int y;
+
+    public Point(int x, int y) {
+        this.x = x;
+        this.y = y;
+    }
+
+    public int getX() { return x; }
+    public int getY() { return y; }
+
+    public boolean isSamePoint(Point that) {
+        return this.getX() == that.getX() && this.getY() == that.getY();
+    }
+
+    public String toString() {
+        return "Point: x=" + getX() + " y=" + getY();
+    }
+
+    public boolean equals(Object o) {
+        if(o instanceof Point) {
+            return ((Point)o).x == x &&  ((Point)o).y == y;
+        } else {
+            return false;
+        }
+    }
+
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/PreloadCircularityTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/PreloadCircularityTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/PreloadCircularityTest.java
@@ -0,0 +1,494 @@
+/*
+ * 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 PreloadCircularityTest
+ * @modules java.base/jdk.internal.vm.annotation
+ *          java.base/jdk.internal.value
+ * @library /test/lib
+ * @enablePreview
+ * @compile PreloadCircularityTest.java
+ * @run main/othervm PreloadCircularityTest
+ */
+
+ import java.lang.reflect.Method;
+ import java.util.ArrayList;
+ import java.util.Collections;
+ import java.util.List;
+
+import javax.management.relation.RelationServiceNotRegisteredException;
+
+ import jdk.internal.value.ValueClass;
+ import jdk.internal.vm.annotation.ImplicitlyConstructible;
+ import jdk.internal.vm.annotation.LooselyConsistentValue;
+ import jdk.internal.vm.annotation.NullRestricted;
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.Utils;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class PreloadCircularityTest {
+
+    // Testing preload due to non-static fields
+
+    static value class Class0a {
+        @NullRestricted
+        Class0b vb = new Class0b();
+    }
+
+    @ImplicitlyConstructible
+    static value class Class0b {
+        @NullRestricted
+        Class0c vc = new Class0c();
+    }
+
+    @ImplicitlyConstructible
+    static value class Class0c {
+        int i = 0;
+    }
+
+    void test_0() throws Exception {
+        OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class0a");
+        out.shouldHaveExitValue(0);
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class0b during loading of class PreloadCircularityTest$Class0a. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class0c during loading of class PreloadCircularityTest$Class0b. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class0c during loading of class PreloadCircularityTest$Class0b (cause: null-free non-static field) succeeded");
+        out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class0b during loading of class PreloadCircularityTest$Class0a (cause: null-free non-static field) succeeded");
+        out.shouldNotContain("(cause: null-free non-static field) failed");
+    }
+
+    static value class Class1a {
+        @NullRestricted
+        Class1b vb = new Class1b();
+    }
+
+    @ImplicitlyConstructible
+    static value class Class1b {
+        Class1c vc = new Class1c();
+    }
+
+    static value class Class1c {
+        int i = 0;
+    }
+
+    void test_1() throws Exception {
+        OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class1a");
+        out.shouldHaveExitValue(0);
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class1b during loading of class PreloadCircularityTest$Class1a. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class1c during loading of class PreloadCircularityTest$Class1b. Cause: field type in Preload attribute");
+        out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class1c during loading of class PreloadCircularityTest$Class1b (cause: field type in Preload attribute) succeeded");
+        out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class1b during loading of class PreloadCircularityTest$Class1a (cause: null-free non-static field) succeeded");
+    }
+
+    static value class Class2a {
+        @NullRestricted
+        Class2b vb = new Class2b();
+    }
+
+    @ImplicitlyConstructible
+    static value class Class2b {
+        @NullRestricted
+        Class2c vc = new Class2c();
+    }
+
+    @ImplicitlyConstructible
+    static value class Class2c {
+        @NullRestricted
+        Class2b vb = new Class2b();
+    }
+
+    void test_2() throws Exception {
+        OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class2a");
+        out.shouldHaveExitValue(1);
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class2b during loading of class PreloadCircularityTest$Class2a. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class2c during loading of class PreloadCircularityTest$Class2b. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class2b during loading of class PreloadCircularityTest$Class2c. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[warning][class,preload] Preloading of class PreloadCircularityTest$Class2b during loading of class PreloadCircularityTest$Class2c (cause: null-free non-static field) failed: java/lang/ClassCircularityError");
+        out.shouldContain("[warning][class,preload] Preloading of class PreloadCircularityTest$Class2c during loading of class PreloadCircularityTest$Class2b (cause: null-free non-static field) failed: java/lang/ClassCircularityError");
+        out.shouldContain("[warning][class,preload] Preloading of class PreloadCircularityTest$Class2b during loading of class PreloadCircularityTest$Class2a (cause: null-free non-static field) failed: java/lang/ClassCircularityError");
+    }
+
+    static value class Class3a {
+        @NullRestricted
+        Class3b vb = new Class3b();
+    }
+
+    @ImplicitlyConstructible
+    static value class Class3b {
+        @NullRestricted
+        Class3c vc = new Class3c();
+    }
+
+    @ImplicitlyConstructible
+    static value class Class3c {
+        Class3b vb = new Class3b();
+    }
+
+    void test_3() throws Exception {
+        OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class3a");
+        out.shouldHaveExitValue(0);
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class3b during loading of class PreloadCircularityTest$Class3a. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class3c during loading of class PreloadCircularityTest$Class3b. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class3b during loading of class PreloadCircularityTest$Class3c. Cause: field type in Preload attribute");
+        out.shouldContain("[warning][class,preload] Preloading of class PreloadCircularityTest$Class3b during loading of class PreloadCircularityTest$Class3c (cause: field type in Preload attribute) failed : java/lang/ClassCircularityError");
+        out.shouldContain("[info   ][class,preload] Preloading of class PreloadCircularityTest$Class3c during loading of class PreloadCircularityTest$Class3b (cause: null-free non-static field) succeeded");
+        out.shouldContain("[info   ][class,preload] Preloading of class PreloadCircularityTest$Class3b during loading of class PreloadCircularityTest$Class3a (cause: null-free non-static field) succeeded");
+    }
+
+    static value class Class4a {
+        @NullRestricted
+        Class4b vb = new Class4b();
+    }
+
+    @ImplicitlyConstructible
+    static value class Class4b {
+        @NullRestricted
+        Class4a vc = new Class4a();
+    }
+
+    void test_4() throws Exception {
+        OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class4a");
+        out.shouldHaveExitValue(1);
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class4b during loading of class PreloadCircularityTest$Class4a. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class4a during loading of class PreloadCircularityTest$Class4b. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[warning][class,preload] Preloading of class PreloadCircularityTest$Class4a during loading of class PreloadCircularityTest$Class4b (cause: null-free non-static field) failed: java/lang/ClassCircularityError");
+        out.shouldContain("[warning][class,preload] Preloading of class PreloadCircularityTest$Class4b during loading of class PreloadCircularityTest$Class4a (cause: null-free non-static field) failed: java/lang/ClassCircularityError");
+    }
+
+    static value class Class5a {
+        Class5b vb = new Class5b();
+
+        @NullRestricted
+        Class5c vc = new Class5c();
+    }
+
+    static value class Class5b {
+        @NullRestricted
+        Class5d vd = new Class5d();
+    }
+
+    @ImplicitlyConstructible
+    static value class Class5c {
+        Class5b vb = new Class5b();
+    }
+
+    static value class Class5d {
+        @NullRestricted
+        Class5b vb = new Class5b();
+    }
+
+    void test_5() throws Exception {
+        OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class5a");
+        out.shouldHaveExitValue(0);
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class5b during loading of class PreloadCircularityTest$Class5a. Cause: field type in Preload attribute");
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class5d during loading of class PreloadCircularityTest$Class5b. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class5b during loading of class PreloadCircularityTest$Class5d. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[warning][class,preload] Preloading of class PreloadCircularityTest$Class5b during loading of class PreloadCircularityTest$Class5d (cause: null-free non-static field) failed: java/lang/ClassCircularityError");
+        out.shouldContain("[warning][class,preload] Preloading of class PreloadCircularityTest$Class5d during loading of class PreloadCircularityTest$Class5b (cause: null-free non-static field) failed: java/lang/ClassCircularityError");
+        out.shouldContain("[warning][class,preload] Preloading of class PreloadCircularityTest$Class5b during loading of class PreloadCircularityTest$Class5a (cause: field type in Preload attribute) failed : java/lang/ClassCircularityError");
+        out.shouldContain("[info   ][class,preload] Preloading class PreloadCircularityTest$Class5c during loading of class PreloadCircularityTest$Class5a. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[info   ][class,preload] Preloading class PreloadCircularityTest$Class5b during loading of class PreloadCircularityTest$Class5c. Cause: field type in Preload attribute");
+        out.shouldContain("[info   ][class,preload] Preloading class PreloadCircularityTest$Class5d during loading of class PreloadCircularityTest$Class5b. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[info   ][class,preload] Preloading class PreloadCircularityTest$Class5b during loading of class PreloadCircularityTest$Class5d. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[warning][class,preload] Preloading of class PreloadCircularityTest$Class5b during loading of class PreloadCircularityTest$Class5d (cause: null-free non-static field) failed: java/lang/ClassCircularityError");
+        out.shouldContain("[warning][class,preload] Preloading of class PreloadCircularityTest$Class5d during loading of class PreloadCircularityTest$Class5b (cause: null-free non-static field) failed: java/lang/ClassCircularityError");
+        out.shouldContain("[warning][class,preload] Preloading of class PreloadCircularityTest$Class5b during loading of class PreloadCircularityTest$Class5c (cause: field type in Preload attribute) failed : java/lang/ClassCircularityError");
+        out.shouldContain("[info   ][class,preload] Preloading of class PreloadCircularityTest$Class5c during loading of class PreloadCircularityTest$Class5a (cause: null-free non-static field) succeeded");
+    }
+
+    static value class Class6a {
+        @NullRestricted
+        Class6b vb = new Class6b();
+    }
+
+    @ImplicitlyConstructible
+    static value class Class6b {
+        Class6c vc = new Class6c();
+
+        @NullRestricted
+        Class6d vd = new Class6d();
+    }
+
+    static value class Class6c {
+        int i = 0;
+    }
+
+    @ImplicitlyConstructible
+    static value class Class6d {
+        @NullRestricted
+        Class6b vb = new Class6b();
+    }
+
+    void test_6() throws Exception {
+        OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class6a");
+        out.shouldHaveExitValue(1);
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class6b during loading of class PreloadCircularityTest$Class6a. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class6c during loading of class PreloadCircularityTest$Class6b. Cause: field type in Preload attribute");
+        out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class6c during loading of class PreloadCircularityTest$Class6b (cause: field type in Preload attribute) succeeded");
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class6d during loading of class PreloadCircularityTest$Class6b. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class6b during loading of class PreloadCircularityTest$Class6d. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[warning][class,preload] Preloading of class PreloadCircularityTest$Class6b during loading of class PreloadCircularityTest$Class6d (cause: null-free non-static field) failed: java/lang/ClassCircularityError");
+        out.shouldContain("[warning][class,preload] Preloading of class PreloadCircularityTest$Class6d during loading of class PreloadCircularityTest$Class6b (cause: null-free non-static field) failed: java/lang/ClassCircularityError");
+        out.shouldContain("[warning][class,preload] Preloading of class PreloadCircularityTest$Class6b during loading of class PreloadCircularityTest$Class6a (cause: null-free non-static field) failed: java/lang/ClassCircularityError");
+    }
+
+    @ImplicitlyConstructible
+    static value class Class7a {
+        @NullRestricted
+        Class7a va = new Class7a();
+    }
+
+    void test_7() throws Exception {
+        OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class7a");
+        out.shouldHaveExitValue(1);
+        out.shouldNotContain("[info][class,preload] Preloading class PreloadCircularityTest$Class7a during loading of class PreloadCircularityTest$Class7a. Cause: a null-free non-static field is declared with this type");
+    }
+
+    static value class Class8a {
+        Class8a va = new Class8a();
+    }
+
+    void test_8() throws Exception {
+        OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class8a");
+        out.shouldHaveExitValue(0);
+        out.shouldNotContain("[info][class,preload] Preloading class PreloadCircularityTest$Class8a during loading of class PreloadCircularityTest$Class8a. Cause: a null-free non-static field is declared with this type");
+    }
+
+    static value class Class9a {
+        @NullRestricted
+        Class9b vb = new Class9b();
+    }
+
+    static class Class9b { }
+
+    void test_9() throws Exception {
+        OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class9a");
+        out.shouldHaveExitValue(1);
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class9b during loading of class PreloadCircularityTest$Class9a. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("java.lang.IncompatibleClassChangeError: Class PreloadCircularityTest$Class9a expects class PreloadCircularityTest$Class9b to be a value class, but it is an identity class");
+    }
+
+    static value class Class10a {
+        @NullRestricted
+        Class10b vb = new Class10b();
+    }
+
+    static value class Class10b { }
+
+    void test_10() throws Exception {
+        OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class10a");
+        out.shouldHaveExitValue(1);
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class10b during loading of class PreloadCircularityTest$Class10a. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("java.lang.IncompatibleClassChangeError: class PreloadCircularityTest$Class10b is not implicitly constructible and it is used in a null restricted non-static field (not supported)");
+    }
+
+    // Testing preloading due to static fields
+
+    @ImplicitlyConstructible
+    static value class Class50a {
+        @NullRestricted
+        static Class50a sa;
+    }
+
+    void test_50() throws Exception {
+        OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class50a");
+        out.shouldHaveExitValue(0);
+        out.shouldNotContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class50a");
+    }
+
+    @ImplicitlyConstructible
+    static value class Class51a {
+        @NullRestricted
+        static Class51b sb;
+
+        @NullRestricted
+        static Class51c sc;
+    }
+
+    @ImplicitlyConstructible
+    static value class Class51b {
+        @NullRestricted
+        static Class51a sa;
+    }
+
+    @ImplicitlyConstructible
+    static value class Class51c {
+        @NullRestricted
+        Class51a sa = new Class51a();
+    }
+
+    void test_51() throws Exception {
+        OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class51a");
+        out.shouldHaveExitValue(0);
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class51b during linking of class PreloadCircularityTest$Class51a. Cause: a null-free static field is declared with this type");
+        out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class51b during linking of class PreloadCircularityTest$Class51a (cause: null-free static field) succeeded");
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class51c during linking of class PreloadCircularityTest$Class51a. Cause: a null-free static field is declared with this type");
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class51a during loading of class PreloadCircularityTest$Class51c. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class51a during loading of class PreloadCircularityTest$Class51c (cause: null-free non-static field) succeeded");
+        out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class51c during linking of class PreloadCircularityTest$Class51a (cause: null-free static field) succeeded");
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class51a during linking of class PreloadCircularityTest$Class51b. Cause: a null-free static field is declared with this type");
+        out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class51a during linking of class PreloadCircularityTest$Class51b (cause: null-free static field) succeeded");
+    }
+
+    static value class Class52a {
+        @NullRestricted
+        static Class52b vb;
+    }
+
+    @ImplicitlyConstructible
+    static value class Class52b {
+        @NullRestricted
+        Class52c vc = new Class52c();
+    }
+
+    @ImplicitlyConstructible
+    static value class Class52c {
+        @NullRestricted
+        Class52b vb = new Class52b();
+    }
+
+    void test_52() throws Exception {
+        OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class52a");
+        out.shouldHaveExitValue(1);
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class52b during linking of class PreloadCircularityTest$Class52a. Cause: a null-free static field is declared with this type");
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class52c during loading of class PreloadCircularityTest$Class52b. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class52b during loading of class PreloadCircularityTest$Class52c. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[warning][class,preload] Preloading of class PreloadCircularityTest$Class52b during loading of class PreloadCircularityTest$Class52c (cause: null-free non-static field) failed: java/lang/ClassCircularityError");
+        out.shouldContain("[warning][class,preload] Preloading of class PreloadCircularityTest$Class52c during loading of class PreloadCircularityTest$Class52b (cause: null-free non-static field) failed: java/lang/ClassCircularityError");
+    }
+
+    @ImplicitlyConstructible
+    static value class Class53a {
+        Class53b vb = new Class53b();
+
+        @NullRestricted
+        static Class53b sb;
+    }
+
+    @ImplicitlyConstructible
+    static value class Class53b {
+        @NullRestricted
+        Class53a va = new Class53a();
+    }
+
+    // In the following test, Class53a fails to optimistically load Class53b at load time, but successfully loads it at link time
+
+    void test_53() throws Exception {
+        OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class53a");
+        out.shouldHaveExitValue(0);
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class53b during loading of class PreloadCircularityTest$Class53a. Cause: field type in Preload attribute");
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class53a during loading of class PreloadCircularityTest$Class53b. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[warning][class,preload] Preloading of class PreloadCircularityTest$Class53a during loading of class PreloadCircularityTest$Class53b (cause: null-free non-static field) failed: java/lang/ClassCircularityError");
+        out.shouldContain("[warning][class,preload] Preloading of class PreloadCircularityTest$Class53b during loading of class PreloadCircularityTest$Class53a (cause: field type in Preload attribute) failed : java/lang/ClassCircularityError");
+        out.shouldContain("[info   ][class,preload] Preloading class PreloadCircularityTest$Class53b during linking of class PreloadCircularityTest$Class53a. Cause: a null-free static field is declared with this type");
+        out.shouldContain("[info   ][class,preload] Preloading class PreloadCircularityTest$Class53a during loading of class PreloadCircularityTest$Class53b. Cause: a null-free non-static field is declared with this type");
+        out.shouldContain("[info   ][class,preload] Preloading of class PreloadCircularityTest$Class53a during loading of class PreloadCircularityTest$Class53b (cause: null-free non-static field) succeeded");
+        out.shouldContain("[info   ][class,preload] Preloading of class PreloadCircularityTest$Class53b during linking of class PreloadCircularityTest$Class53a (cause: null-free static field) succeeded");
+    }
+
+    static value class Class54a {
+        @NullRestricted
+        static Class54b sb;
+    }
+
+    static class Class54b { }
+
+    void test_54() throws Exception {
+        OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class54a");
+        out.shouldHaveExitValue(1);
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class54b during linking of class PreloadCircularityTest$Class54a. Cause: a null-free static field is declared with this type");
+        out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class54b during linking of class PreloadCircularityTest$Class54a (cause: null-free static field) succeeded");
+        out.shouldContain("java.lang.IncompatibleClassChangeError: class PreloadCircularityTest$Class54a expects class PreloadCircularityTest$Class54b to be a value class but it is an identity class");
+    }
+
+    static class Class55a {
+        @NullRestricted
+        static Class55b sb;
+    }
+
+    static value class Class55b { }
+
+    void test_55() throws Exception {
+        OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class55a");
+        out.shouldHaveExitValue(1);
+        out.shouldContain("[info][class,preload] Preloading class PreloadCircularityTest$Class55b during linking of class PreloadCircularityTest$Class55a. Cause: a null-free static field is declared with this type");
+        out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class55b during linking of class PreloadCircularityTest$Class55a (cause: null-free static field) succeeded");
+        out.shouldContain("java.lang.IncompatibleClassChangeError: class PreloadCircularityTest$Class55b is not implicitly constructible and it is used in a null restricted static field (not supported)");
+    }
+
+    public static class TestHelper {
+        public static void main(String[] args) {
+            try {
+                Class c = Class.forName(args[0]);
+            } catch (Throwable ex) {
+                ex.printStackTrace();
+                System.exit(1);
+            }
+            System.exit(0);
+        }
+    }
+
+    static OutputAnalyzer tryLoadingClass(String className) throws Exception {
+        ProcessBuilder pb = exec("PreloadCircularityTest$TestHelper", className);
+        OutputAnalyzer out = new OutputAnalyzer(pb.start());
+        System.out.println(out.getOutput());
+        return out;
+    }
+
+    static ProcessBuilder exec(String... args) throws Exception {
+        List<String> argsList = new ArrayList<>();
+        Collections.addAll(argsList, "--enable-preview");
+        Collections.addAll(argsList, "-Dtest.class.path=" + System.getProperty("test.class.path", "."));
+        Collections.addAll(argsList, "-Xlog:class+preload=info");
+        Collections.addAll(argsList, args);
+        return ProcessTools.createTestJavaProcessBuilder(argsList);
+    }
+
+    public static void main(String[] args) throws Exception {
+        System.out.println("Crerating tests");
+        PreloadCircularityTest tests = new PreloadCircularityTest();
+        Class c = tests.getClass();
+        System.out.println("Iterating over test methods");
+        boolean hasFailedTest = false;
+        StringBuilder sb = new StringBuilder("Following tests have failed: ");
+        for (Method m : c.getDeclaredMethods()) {
+            if (m.getName().startsWith("test_")) {
+                boolean failed = false;
+                try {
+                    System.out.println("Running " + m.getName());
+                    m.invoke(tests);
+                } catch (Throwable t) {
+                    t.printStackTrace();
+                    failed = true;
+                }
+                System.out.println("Test " + m.getName() + " : " + (failed ? "FAILED" : "PASSED"));
+                hasFailedTest = failed ? true : hasFailedTest;
+                if (failed) sb.append(m.getName()).append(", ");
+            }
+        }
+        if (hasFailedTest) {
+            throw new RuntimeException(sb.toString());
+        }
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/PreloadValue0.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/PreloadValue0.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/PreloadValue0.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2022, 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.
+ */
+
+public value class PreloadValue0 { }
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/QuickeningTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/QuickeningTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/QuickeningTest.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2018, 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 runtime.valhalla.inlinetypes;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+import jdk.test.lib.Asserts;
+
+/*
+ * @test QuickeningTest
+ * @summary Test quickening of getfield and putfield applied to inline fields
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @compile QuickeningTest.java
+ * @run main/othervm runtime.valhalla.inlinetypes.QuickeningTest
+ */
+
+public class QuickeningTest {
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Point {
+        final int x;
+        final int y;
+
+        public Point(int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class JumboInline {
+        final long l0;
+        final long l1;
+        final long l2;
+        final long l3;
+        final long l4;
+        final long l5;
+        final long l6;
+        final long l7;
+        final long l8;
+        final long l9;
+        final long l10;
+        final long l11;
+        final long l12;
+        final long l13;
+        final long l14;
+        final long l15;
+        final long l16;
+        final long l17;
+        final long l18;
+        final long l19;
+
+        public JumboInline(long l0Val, long l1Val) {
+            l0 = l0Val;
+            l1 = l1Val;
+            l2 = l0Val+1;
+            l3 = l1Val+2;
+            l4 = l0Val+3;
+            l5 = l1Val+4;
+            l6 = l0Val+5;
+            l7 = l1Val+6;
+            l8 = l0Val+7;
+            l9 = l1Val+8;
+            l10 = l0Val+9;
+            l11 = l1Val+10;
+            l12 = l0Val+11;
+            l13 = l1Val+12;
+            l14 = l0Val+13;
+            l15 = l1Val+14;
+            l16 = l0Val+15;
+            l17 = l1Val+16;
+            l18 = l0Val+17;
+            l19 = l1Val+18;
+        }
+    }
+
+    static class Parent {
+    Point nfp;       /* Not flattenable inline field */
+    @NullRestricted
+    Point fp;         /* Flattenable and flattened inline field */
+    @NullRestricted
+    JumboInline fj;    /* Flattenable not flattened inline field */
+
+        public void setNfp(Point p) { nfp = p; }
+        public void setFp(Point p) { fp = p; }
+        public void setFj(JumboInline j) { fj = j; }
+    }
+
+    static class Child extends Parent {
+        // This class inherited fields from the Parent class
+        Point nfp2;      /* Not flattenable inline field */
+        @NullRestricted
+        Point fp2;        /* Flattenable and flattened inline field */
+        @NullRestricted
+        JumboInline fj2;   /* Flattenable not flattened inline field */
+
+        public void setNfp2(Point p) { nfp2 = p; }
+        public void setFp2(Point p)  { fp2 = p; }
+        public void setFj2(JumboInline j) { fj2 = j; }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Value {
+        final Point nfp;       /* Not flattenable inline field */
+        @NullRestricted
+        final Point fp;         /* Flattenable and flattened inline field */
+        @NullRestricted
+        final JumboInline fj;    /* Flattenable not flattened inline field */
+
+        private Value() {
+            nfp = null;
+            fp = new Point(0, 0);
+            fj = new JumboInline(0, 0);
+        }
+    }
+
+    static void testUninitializedFields() {
+        Parent p = new Parent();
+        Asserts.assertEquals(p.nfp, null, "invalid uninitialized not flattenable");
+        Asserts.assertEquals(p.fp.x, 0, "invalid value for uninitialized flattened field");
+        Asserts.assertEquals(p.fp.y, 0, "invalid value for uninitialized flattened field");
+        Asserts.assertEquals(p.fj.l0, 0L, "invalid value for uninitialized flattened field");
+        Asserts.assertEquals(p.fj.l1, 0L, "invalid value for uninitialized flattened field");
+
+        Child c = new Child();
+        Asserts.assertEquals(c.nfp, null, "invalid uninitialized not flattenable field");
+        Asserts.assertEquals(c.fp.x, 0, "invalid value for uninitialized flattened field");
+        Asserts.assertEquals(c.fp.y, 0, "invalid value for uninitialized flattened field");
+        Asserts.assertEquals(c.fj.l0, 0L, "invalid value for uninitialized flattened field");
+        Asserts.assertEquals(c.fj.l1, 0L, "invalid value for uninitialized flattened field");
+        Asserts.assertEquals(c.nfp2, null, "invalid uninitialized not flattenable");
+        Asserts.assertEquals(c.fp2.x, 0, "invalid value for uninitialized flattened field");
+        Asserts.assertEquals(c.fp2.y, 0, "invalid value for uninitialized flattened field");
+        Asserts.assertEquals(c.fj2.l0, 0L, "invalid value for uninitialized not flattened field");
+        Asserts.assertEquals(c.fj2.l1, 0L, "invalid value for uninitialized not flattened field");
+
+        Value v = new Value();
+        Asserts.assertEquals(v.nfp, null, "invalid uninitialized not flattenable");
+        Asserts.assertEquals(v.fp.x, 0, "invalid value for uninitialized flattened field");
+        Asserts.assertEquals(v.fp.y, 0, "invalid value for uninitialized flattened field");
+        Asserts.assertEquals(v.fj.l0, 0L, "invalid value for uninitialized not flattened field");
+        Asserts.assertEquals(v.fj.l1, 0L, "invalid value for uninitialized not flattened field");
+    }
+
+    static void testPutfieldAndGetField() {
+        Point p1 = new Point(16, 47);
+        Point p2 = new Point(32, 64);
+
+        JumboInline j1 = new JumboInline(4, 5);
+        JumboInline j2 = new JumboInline(7, 9);
+
+        Parent p = new Parent();
+        // executing each setter twice to test quickened bytecodes
+        p.setNfp(p1);
+        p.setNfp(p2);
+        p.setFp(p2);
+        p.setFp(p1);
+        p.setFj(j1);
+        p.setFj(j2);
+
+        Asserts.assertTrue(p.nfp.equals(p2), "invalid updated not flattenable field");
+        Asserts.assertEquals(p.fp.x, 16, "invalid value for updated flattened field");
+        Asserts.assertEquals(p.fp.y, 47, "invalid value for updated flattened field");
+        Asserts.assertTrue(p.fj.equals(j2), "invalid value for updated not flattened field");
+
+        Child c = new Child();
+        c.setNfp(p1);
+        c.setNfp(p2);
+        c.setFp(p2);
+        c.setFp(p1);
+        c.setFj(j1);
+        c.setFj(j2);
+        c.setNfp2(p2);
+        c.setNfp2(p1);
+        c.setFp2(p1);
+        c.setFp2(p2);
+        c.setFj2(j2);
+        c.setFj2(j1);
+
+        Asserts.assertTrue(c.nfp.equals(p2), "invalid updated not flattenable field");
+        Asserts.assertEquals(c.fp.x, 16, "invalid value for updated flattened field");
+        Asserts.assertEquals(c.fp.y, 47, "invalid value for updated flattened field");
+        Asserts.assertTrue(c.fj.equals(j2), "invalid value for updated not flattened field");
+
+        Asserts.assertTrue(c.nfp2.equals(p1), "invalid updated not flattenable field");
+        Asserts.assertEquals(c.fp2.x, 32, "invalid value for updated flattened field");
+        Asserts.assertEquals(c.fp2.y, 64, "invalid value for updated flattened field");
+        Asserts.assertTrue(c.fj2.equals(j1), "invalid value for updated not flattened field");
+    }
+
+    public static void main(String[] args) {
+        testUninitializedFields();
+        testUninitializedFields(); // run twice to test quickened bytecodes
+        testPutfieldAndGetField();
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/StaticFieldsTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/StaticFieldsTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/StaticFieldsTest.java
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ *
+ * 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 runtime.valhalla.inlinetypes;
+
+import jdk.test.lib.Asserts;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+/*
+ * @test
+ * @summary Test circularity in static fields
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @compile StaticFieldsTest.java
+ * @run main/othervm runtime.valhalla.inlinetypes.StaticFieldsTest
+ */
+
+public class StaticFieldsTest {
+
+
+    // ClassA and ClassB have a simple cycle in their static fields, but they should
+    // be able to load and initialize themselves successfully. Access to these
+    // static fields after their initialization should return the default value.
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class ClassA {
+        @NullRestricted
+        static ClassB b;
+        public int i;
+
+        public ClassA() {
+            i = 3;
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class ClassB {
+        @NullRestricted
+        static ClassA a;
+        public int i;
+
+        public ClassB() {
+            i = 700;
+        }
+    }
+
+    // ClassC has a reference to itself in its static field, but it should be able
+    // to initialize itelf successfully. Access to this static field after initialization
+    // should return the default value.
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class ClassC {
+        @NullRestricted
+        static ClassC c;
+        int i;
+
+        public ClassC() {
+            i = 42;
+        }
+    }
+
+
+    // ClassD and ClassE have circular references in their static fields, and they
+    // read these static fields during their initialization, the value read from
+    // these fields should be the default value. Both classes should initialize
+    // successfully.
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class ClassD {
+        @NullRestricted
+        static ClassE e;
+        int i;
+
+        static {
+            Asserts.assertEquals(e.i, 0, "Static field e.i incorrect");
+        }
+
+        public ClassD() {
+            i = 42;
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class ClassE {
+        @NullRestricted
+        static ClassD d;
+        int i;
+
+        static {
+            Asserts.assertEquals(d.i, 0, "Static field d.i incorrect");
+        }
+
+        public ClassE() {
+            i = 42;
+        }
+    }
+
+    // ClassF and ClassG have circular references in their static fields, and they
+    // create new instances of each other type to initialize these static fields
+    // during their initialization. Both classes should initialize successfully.
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class ClassF {
+        @NullRestricted
+        static ClassG g;
+        int i;
+
+        static {
+            g = new ClassG();
+            Asserts.assertEquals(g.i, 64, "Static field ClassF.g.i incorrect");
+        }
+
+        ClassF() {
+            i = 314;
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class ClassG {
+        @NullRestricted
+        static ClassF f;
+        int i;
+
+        static {
+            f = new ClassF();
+            Asserts.assertEquals(f.i, 314, "Static field ClassG.f.i incorrect");
+        }
+
+        ClassG() {
+            i = 64;
+        }
+    }
+
+    public static void main(String[] args) {
+        Asserts.assertEquals(ClassA.b.i, 0, "Static field ClassA.b.i incorrect");
+        Asserts.assertEquals(ClassB.a.i, 0, "Static field Classb.a.i incorrect");
+        Asserts.assertEquals(ClassC.c.i, 0, "Static field ClassC.c.i incorrect");
+        new ClassD();
+        new ClassF();
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/Test8186715.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/Test8186715.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/Test8186715.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2016, 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 runtime.valhalla.inlinetypes;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+
+/*
+ * @test Test8186715
+ * @summary test return of buffered inline type passed in argument by caller
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ *          java.base/jdk.internal.value
+ * @enablePreview
+ * @compile Test8186715.java
+ * @run main/othervm runtime.valhalla.inlinetypes.Test8186715
+ */
+
+public class Test8186715 {
+
+    public static void main(String[] args) {
+        MyValueType v = MyValueType.testDefault();
+
+        for (int i = 0; i < 1000000; i++) {
+            MyValueType.testBranchArg1(false, v);
+        }
+    }
+}
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class MyValueType {
+    final int i;
+    final int j;
+
+    private MyValueType(int i, int j) {
+        this.i = i;
+        this.j = j;
+    }
+
+    static MyValueType testDefault() {
+        MyValueType[] array = (MyValueType[])ValueClass.newNullRestrictedArray(MyValueType.class, 1);
+        return array[0];
+    }
+
+    static MyValueType testBranchArg1(boolean flag, MyValueType v1) {
+        if (flag) {
+            v1 = new MyValueType(3, 4);
+        }
+        return v1;
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/TestFieldNullability.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/TestFieldNullability.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/TestFieldNullability.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2018, 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 TestFieldNullability
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @compile TestFieldNullability.java
+ * @run main/othervm -Xmx128m -XX:InlineFieldMaxFlatSize=32
+ *                   runtime.valhalla.inlinetypes.TestFieldNullability
+ */
+
+package runtime.valhalla.inlinetypes;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+import jdk.test.lib.Asserts;
+
+
+public class TestFieldNullability {
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue {
+        int x;
+
+        public MyValue() {
+            x = 314;
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyBigValue {
+        long l0, l1, l2, l3, l4, l5, l6, l7, l8, l9;
+        long l10, l11, l12, l13, l14, l15, l16, l17, l18, l19;
+
+        public MyBigValue() {
+            l0 = l1 = l2 = l3 = l4 = l5 = l6 = l7 = l8 = l9 = 271;
+            l10 = l11 = l12 = l13 = l14 = l15 = l16 = l17 = l18 = l19 = 271;
+        }
+    }
+
+    static class TestIdentityClass {
+        MyValue nullableField;
+        @NullRestricted
+        MyValue nullfreeField;       // flattened
+        MyValue nullField;
+        @NullRestricted
+        MyBigValue nullfreeBigField; // not flattened
+        MyBigValue nullBigField;
+    }
+
+    public static void main(String[] args) {
+        TestIdentityClass that = new TestIdentityClass();
+        Asserts.assertNull(that.nullField, "Invalid non null value for uninitialized non flattenable field");
+        Asserts.assertNull(that.nullBigField, "Invalid non null value for uninitialized non flattenable field");
+        boolean NPE = false;
+        try {
+            that.nullableField = that.nullField;
+        } catch(NullPointerException e) {
+            NPE = true;
+        }
+        Asserts.assertFalse(NPE, "Invalid NPE when assigning null to a non flattenable field");
+        try {
+            that.nullfreeField = (MyValue) that.nullField;
+        } catch(NullPointerException e) {
+            NPE = true;
+        }
+        Asserts.assertTrue(NPE, "Missing NPE when assigning null to a flattened field");
+        try {
+            that.nullfreeBigField = (MyBigValue) that.nullBigField;
+        } catch(NullPointerException e) {
+            NPE = true;
+        }
+        Asserts.assertTrue(NPE, "Missing NPE when assigning null to a flattenable field");
+    }
+
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/TestInheritedInlineTypeFields.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/TestInheritedInlineTypeFields.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/TestInheritedInlineTypeFields.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2017, 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 runtime.valhalla.inlinetypes;
+
+import jdk.internal.vm.annotation.NullRestricted;
+import jdk.test.lib.Asserts;
+
+/*
+ * @test TestInheritedInlineTypeFields
+ * @summary Test if inline field klasses are correctly retrieved for inherited fields
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @compile Point.java TestInheritedInlineTypeFields.java
+ * @run main/othervm runtime.valhalla.inlinetypes.TestInheritedInlineTypeFields
+ */
+
+class A {
+    @NullRestricted
+    Point p;
+}
+
+class B extends A {
+
+}
+
+class C extends B {
+    int i;
+}
+
+class D {
+    long l;
+}
+
+class E extends D {
+    @NullRestricted
+    Point p1;
+}
+
+class F extends E {
+
+}
+
+class G extends F {
+    @NullRestricted
+    Point p2;
+}
+
+public class TestInheritedInlineTypeFields {
+
+    public static void main(String[] args) {
+        for (int i = 0; i < 100000; i++) {
+            run();
+        }
+    }
+
+    public static void run() {
+        B b = new B();
+        Asserts.assertEquals(b.p.x, 0);
+        Asserts.assertEquals(b.p.y, 0);
+        b.p = new Point(1,2);
+        Asserts.assertEquals(b.p.x, 1);
+        Asserts.assertEquals(b.p.y, 2);
+
+        G g = new G();
+        Asserts.assertEquals(g.p1.x, 0);
+        Asserts.assertEquals(g.p1.y, 0);
+        Asserts.assertEquals(g.p2.x, 0);
+        Asserts.assertEquals(g.p2.y, 0);
+        g.p1 = new Point(1,2);
+        g.p2 = new Point(3,4);
+        Asserts.assertEquals(g.p1.x, 1);
+        Asserts.assertEquals(g.p1.y, 2);
+        Asserts.assertEquals(g.p2.x, 3);
+        Asserts.assertEquals(g.p2.y, 4);
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/TestJNIIsSameObject.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/TestJNIIsSameObject.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/TestJNIIsSameObject.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2020, 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 jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.test.lib.Asserts;
+
+
+/*
+ * @test
+ * @summary Test JNI IsSameObject semantic with inline types
+ * @library /testlibrary /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
+ * @requires (os.family == "linux" | os.family == "mac")
+ * @enablePreview
+ * @compile TestJNIIsSameObject.java
+ * @run main/othervm/native TestJNIIsSameObject
+ */
+
+public class TestJNIIsSameObject {
+  @ImplicitlyConstructible
+  @LooselyConsistentValue
+  static value class Value {
+    int i;
+
+    public Value(int i) {
+      this.i = i;
+    }
+  }
+  native static boolean isSameObject(Object o0, Object o1);
+
+  static {
+    System.loadLibrary("JNIIsSameObject");
+  }
+
+  public static void main(String[] args) {
+    // Same value in different instances
+    Value v0 = new Value(3);
+    Value v1 = new Value(3);
+    Asserts.assertTrue(isSameObject(v0, v1));
+
+    // Different values
+    Value v2 = new Value(4);
+    Asserts.assertFalse(isSameObject(v0, v2));
+
+    // Same object
+    TestJNIIsSameObject t0 = new TestJNIIsSameObject();
+    Object o = t0;
+    Asserts.assertTrue(isSameObject(t0, o));
+
+    // Different objects
+    TestJNIIsSameObject t1 = new TestJNIIsSameObject();
+    Asserts.assertFalse(isSameObject(t0, t1));
+
+    // Comparing against null
+    Asserts.assertFalse(isSameObject(v0, null));
+    Asserts.assertFalse(isSameObject(null, v0));
+    Asserts.assertFalse(isSameObject(t0, null));
+    Asserts.assertFalse(isSameObject(null, t0));
+
+    // Object vs inline
+    Asserts.assertFalse(isSameObject(v0, t0));
+    Asserts.assertFalse(isSameObject(t0, v0));
+
+  }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/UninitializedInlineFieldsTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/UninitializedInlineFieldsTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/UninitializedInlineFieldsTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2017, 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 runtime.valhalla.inlinetypes;
+
+import jdk.internal.vm.annotation.NullRestricted;
+import jdk.test.lib.Asserts;
+
+/*
+ * @test
+ * @summary Uninitialized inline fields test
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @compile Point.java JumboInline.java UninitializedInlineFieldsTest.java
+ * @run main/othervm -XX:InlineFieldMaxFlatSize=64 runtime.valhalla.inlinetypes.UninitializedInlineFieldsTest
+ */
+public class UninitializedInlineFieldsTest {
+    static Point nonFlattenableStaticPoint;
+    @NullRestricted
+    static Point staticPoint;
+
+    @NullRestricted
+    Point instancePoint;
+
+    static JumboInline sj1;
+    @NullRestricted
+    static JumboInline sj2;
+
+    JumboInline j1;
+    @NullRestricted
+    JumboInline j2;
+
+    static Object getNull() {
+        return null;
+    }
+
+    UninitializedInlineFieldsTest() { }
+
+    public static void main(String[] args) {
+        checkUninitializedPoint(UninitializedInlineFieldsTest.staticPoint, 0, 0);
+        Asserts.assertEquals(nonFlattenableStaticPoint, null, "invalid non flattenable static inline field");
+        UninitializedInlineFieldsTest.staticPoint = new Point(456, 678);
+        checkUninitializedPoint(UninitializedInlineFieldsTest.staticPoint, 456, 678);
+        UninitializedInlineFieldsTest test = new UninitializedInlineFieldsTest();
+        checkUninitializedPoint(test.instancePoint, 0, 0);
+        test.instancePoint = new Point(123, 345);
+        checkUninitializedPoint(test.instancePoint, 123, 345);
+
+        Asserts.assertEquals(test.j1, null, "invalid non flattenable instance inline field");
+        Asserts.assertEquals(test.j2.l0, 0L, "invalid flattenable instance inline field");
+    }
+
+    static void checkUninitializedPoint(Point p, int x, int y) {
+        Asserts.assertEquals(p.x, x, "invalid x value");
+        Asserts.assertEquals(p.y, y, "invalid y value");
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/UnsafeTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/UnsafeTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/UnsafeTest.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2018, 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 runtime.valhalla.inlinetypes;
+
+
+
+/*
+ * @test UnsafeTest
+ * @requires vm.debug == true
+ * @summary unsafe get/put/with inline type
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @compile Point.java UnsafeTest.java
+ * @run main/othervm -XX:FlatArrayElementMaxSize=-1 -XX:InlineFieldMaxFlatSize=-1 -XX:+EnableNullableFieldFlattening runtime.valhalla.inlinetypes.UnsafeTest
+ */
+
+import jdk.internal.misc.Unsafe;
+import jdk.internal.misc.VM;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+import jdk.test.lib.Asserts;
+
+import java.lang.reflect.*;
+import java.util.List;
+import static jdk.test.lib.Asserts.*;
+
+public class UnsafeTest {
+    static final Unsafe U = Unsafe.getUnsafe();
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Value1 {
+        @NullRestricted
+        Point point;
+        Point[] array;
+        Value1(Point p, Point... points) {
+            this.point = p;
+            this.array = points;
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Value2 {
+        int i;
+        @NullRestricted
+        Value1 v;
+
+        Value2(Value1 v, int i) {
+            this.v = v;
+            this.i = i;
+        }
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class Value3 {
+        Object o;
+        @NullRestricted
+        Value2 v;
+
+        Value3(Value2 v, Object ref) {
+            this.v = v;
+            this.o = ref;
+        }
+
+    }
+
+
+    public static void test0() throws Throwable {
+        printValueClass(Value3.class, 0);
+
+        Value1 v1 = new Value1(new Point(10,10), new Point(20,20), new Point(30,30));
+        Value2 v2 = new Value2(v1, 20);
+        Value3 v3 = new Value3(v2, List.of("Value3"));
+        long off_o = U.objectFieldOffset(Value3.class, "o");
+        long off_v = U.objectFieldOffset(Value3.class, "v");
+        long off_i = U.objectFieldOffset(Value2.class, "i");
+        long off_v2 = U.objectFieldOffset(Value2.class, "v");
+
+        long off_point = U.objectFieldOffset(Value1.class, "point");
+
+        /*
+         * Layout of Value3
+         *
+         * | valueheader | o | i | x | y | array |
+         *                       ^-------^
+         *                        Point
+         *                       ^---------------^
+         *                        Value1
+         *
+         *                   ^-------------------^
+         *                    Value2
+         */
+        List<String> list = List.of("Value1", "Value2", "Value3");
+        Value3 v = v3;
+        try {
+            v = U.makePrivateBuffer(v);
+            // patch v3.o
+            U.putObject(v, off_o, list);
+            // patch v3.v.i;
+            U.putInt(v, off_v + off_i - U.valueHeaderSize(Value2.class), 999);
+            // patch v3.v.v.point
+            U.putValue(v, off_v + off_v2 - U.valueHeaderSize(Value2.class) + off_point - U.valueHeaderSize(Value1.class),
+                       Point.class, new Point(100, 100));
+        } finally {
+            v = U.finishPrivateBuffer(v);
+        }
+
+        assertEquals(v.v.v.point, new Point(100, 100));
+        assertEquals(v.v.i, 999);
+        assertEquals(v.o, list);
+        assertEquals(v.v.v.array, v1.array);
+
+        Value1 nv1 = new Value1(new Point(70,70), new Point(80,80), new Point(90,90));
+        Value2 nv2 = new Value2(nv1, 100);
+        Value3 nv3 = new Value3(nv2, list);
+
+        try {
+            v = U.makePrivateBuffer(v);
+            // patch v3.v
+            U.putValue(v, off_v2, Value2.class, nv2);
+        } finally {
+            v = U.finishPrivateBuffer(v);
+        }
+        assertEquals(v, nv3);
+    }
+
+    static void printValueClass(Class<?> vc, int level) {
+        String indent = "";
+        for (int i=0; i < level; i++) {
+            indent += "  ";
+        }
+        System.out.format("%s%s header size %d%n", indent, vc, U.valueHeaderSize(vc));
+        for (Field f : vc.getDeclaredFields()) {
+            System.out.format("%s%s: %s%s offset %d%n", indent, f.getName(),
+                              U.isFlatField(f) ? "flattened " : "", f.getType(),
+                              U.objectFieldOffset(vc, f.getName()));
+            if (U.isFlatField(f)) {
+                printValueClass(f.getType(), level+1);
+            }
+        }
+    }
+
+    static value class MyValue0 {
+        int val;
+
+        public MyValue0(int i) {
+            val = i;
+        }
+    }
+
+    static class Container0 {
+        MyValue0 v;
+    }
+
+    public static void test1() throws Throwable {
+        Container0 c = new Container0();
+        Class<?> cc = Container0.class;
+        Field[] fields = cc.getDeclaredFields();
+        Asserts.assertEquals(fields.length, 1);
+        Field f = fields[0];
+        System.out.println("Field found: " + f);
+        Asserts.assertTrue(U.isFlatField(f));
+        Asserts.assertTrue(U.hasNullMarker(f));
+        int nmOffset = U.nullMarkerOffset(f);
+        Asserts.assertNotEquals(nmOffset, -1);
+        byte nm = U.getByte(c, nmOffset);
+        Asserts.assertEquals(nm, (byte)0);
+        c.v = new MyValue0(42);
+        Asserts.assertNotNull(c.v);
+        nm = U.getByte(c, nmOffset);
+        Asserts.assertNotEquals(nm, 0);
+        U.getAndSetByteRelease(c, nmOffset, (byte)0);
+        Asserts.assertNull(c.v);
+    }
+
+    public static void main(String[] args) throws Throwable {
+        test0();
+        test1();
+    }
+
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ValuePreloadClient0.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ValuePreloadClient0.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ValuePreloadClient0.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2022, 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.
+ */
+
+public class ValuePreloadClient0 {
+    PreloadValue0 value;
+
+    public static void main(String[] args) {
+        System.out.print("Success");
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ValuePreloadClient1.jcod b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ValuePreloadClient1.jcod
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ValuePreloadClient1.jcod
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2022, 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.
+ */
+
+// The test class is based in the Java source code below, but the constant
+// pool entry #33 (used by the Preload attribute) has been modified to
+// contain the name of a non-existing class.
+//
+// public class ValuePreloadClient1 {
+//     PreloadValue0 value;
+//
+//     public static void main(String[] args) {
+//         System.out.print("Success");
+//     }
+// }
+
+ class ValuePreloadClient1 {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    Field #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #12; // #9
+    Utf8 "java/lang/System"; // #10
+    Utf8 "out"; // #11
+    Utf8 "Ljava/io/PrintStream;"; // #12
+    String #14; // #13
+    Utf8 "Success"; // #14
+    Method #16 #17; // #15
+    class #18; // #16
+    NameAndType #19 #20; // #17
+    Utf8 "java/io/PrintStream"; // #18
+    Utf8 "print"; // #19
+    Utf8 "(Ljava/lang/String;)V"; // #20
+    class #22; // #21
+    Utf8 "ValuePreloadClient1"; // #22
+    Utf8 "value"; // #23
+    Utf8 "LPreloadValue0;"; // #24
+    Utf8 "Code"; // #25
+    Utf8 "LineNumberTable"; // #26
+    Utf8 "main"; // #27
+    Utf8 "([Ljava/lang/String;)V"; // #28
+    Utf8 "SourceFile"; // #29
+    Utf8 "ValuePreloadClient1.java"; // #30
+    Utf8 "Preload"; // #31
+    class #33; // #32
+    Utf8 "PreloadValue1"; // #33
+  } // Constant Pool
+
+  0x0021; // access
+  #21;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0000; // access
+      #23; // name_index
+      #24; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0001; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#25) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#26) { // LineNumberTable
+              [] { // line_number_table
+                0  1;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0009; // access
+      #27; // name_index
+      #28; // descriptor_index
+      [] { // Attributes
+        Attr(#25) { // Code
+          2; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0xB20007120DB6000F;
+            0xB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#26) { // LineNumberTable
+              [] { // line_number_table
+                0  5;
+                8  6;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#29) { // SourceFile
+      #30;
+    } // end SourceFile
+    ;
+    Attr(#31) { // Preload
+      0x00010020;
+    } // end Preload
+  } // Attributes
+} // end class ValuePreloadClient1
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ValuePreloadTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ValuePreloadTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ValuePreloadTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2022, 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 ValuePreloadTest
+ * @library /test/lib
+ * @enablePreview
+ * @compile ValuePreloadClient0.java PreloadValue0.java ValuePreloadClient1.jcod
+ * @run driver ValuePreloadTest
+ */
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class ValuePreloadTest {
+
+    static ProcessBuilder exec(String... args) throws Exception {
+        List<String> argsList = new ArrayList<>();
+        Collections.addAll(argsList, "--enable-preview");
+        Collections.addAll(argsList, "-Dtest.class.path=" + System.getProperty("test.class.path", "."));
+        Collections.addAll(argsList, args);
+        return ProcessTools.createTestJavaProcessBuilder(argsList);
+    }
+
+    static void checkFor(ProcessBuilder pb, String expected) throws Exception {
+        OutputAnalyzer out = new OutputAnalyzer(pb.start());
+        out.shouldHaveExitValue(0);
+        out.shouldContain(expected);
+    }
+
+    public static void main(String[] args) throws Exception {
+        ProcessBuilder pb = exec("-Xlog:class+preload","ValuePreloadClient0");
+        checkFor(pb, "[info][class,preload] Preloading class PreloadValue0 during loading of class ValuePreloadClient0. Cause: field type in Preload attribute");
+
+        pb = exec("-Xlog:class+preload","ValuePreloadClient1");
+        checkFor(pb, "[warning][class,preload] Preloading of class PreloadValue1 during linking of class ValuePreloadClient1 (cause: Preload attribute) failed");
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ValueTearing.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ValueTearing.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ValueTearing.java
@@ -0,0 +1,282 @@
+/*
+ * 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.
+ *
+ * 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 runtime.valhalla.inlinetypes;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+import java.util.Optional;
+
+import jdk.internal.misc.Unsafe;
+import jdk.test.whitebox.WhiteBox;
+import static jdk.test.lib.Asserts.*;
+
+/*
+ * @ignore Fix JDK-8328353
+ * @test ValueTearing
+ * @summary Test tearing of inline fields and array elements
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib
+ * @enablePreview
+ * @compile ValueTearing.java
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:ForceNonTearable=
+ *                   -DSTEP_COUNT=10000 -XX:InlineFieldMaxFlatSize=128 -XX:FlatArrayElementMaxSize=-1
+ *                   -Xbootclasspath/a:. -XX:+WhiteBoxAPI
+ *                                   runtime.valhalla.inlinetypes.ValueTearing
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:ForceNonTearable=*
+ *                   -DSTEP_COUNT=10000 -XX:InlineFieldMaxFlatSize=128 -XX:FlatArrayElementMaxSize=-1
+ *                   -Xbootclasspath/a:. -XX:+WhiteBoxAPI
+ *                                   runtime.valhalla.inlinetypes.ValueTearing
+ * @run main/othervm -DSTEP_COUNT=10000000 -XX:InlineFieldMaxFlatSize=128 -XX:FlatArrayElementMaxSize=-1
+ *                   -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ *                                   runtime.valhalla.inlinetypes.ValueTearing
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:ForceNonTearable=
+ *                   -DTEAR_MODE=fieldonly -XX:InlineFieldMaxFlatSize=128 -XX:FlatArrayElementMaxSize=-1
+ *                   -Xbootclasspath/a:. -XX:+WhiteBoxAPI
+ *                                   runtime.valhalla.inlinetypes.ValueTearing
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:ForceNonTearable=
+ *                   -DTEAR_MODE=arrayonly -XX:InlineFieldMaxFlatSize=128 -XX:FlatArrayElementMaxSize=-1
+ *                   -Xbootclasspath/a:. -XX:+WhiteBoxAPI
+ *                                   runtime.valhalla.inlinetypes.ValueTearing
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:ForceNonTearable=*
+ *                   -DTEAR_MODE=both -XX:InlineFieldMaxFlatSize=128 -XX:FlatArrayElementMaxSize=-1
+ *                   -Xbootclasspath/a:. -XX:+WhiteBoxAPI
+ *                                   runtime.valhalla.inlinetypes.ValueTearing
+ */
+public class ValueTearing {
+    private static final Unsafe UNSAFE = Unsafe.getUnsafe();
+    private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+    private static final boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler");
+    private static final boolean ALWAYS_ATOMIC = WHITE_BOX.getStringVMFlag("ForceNonTearable").contains("*");
+    private static final String TEAR_MODE = System.getProperty("TEAR_MODE", "both");
+    private static final boolean TEAR_FIELD = !TEAR_MODE.equals("arrayonly");
+    private static final boolean TEAR_ARRAY = !TEAR_MODE.equals("fieldonly");
+    private static final int STEP_COUNT = Integer.getInteger("STEP_COUNT", 100_000);
+    private static final boolean TFIELD_FLAT, TARRAY_FLAT;
+    private static final boolean NTFIELD_FLAT, NTARRAY_FLAT;
+    static {
+        try {
+            Field TPB_field = TPointBox.class.getDeclaredField("field");
+            Field TPB_array = TPointBox.class.getDeclaredField("array");
+            Field NTPB_field = NTPointBox.class.getDeclaredField("field");
+            Field NTPB_array = NTPointBox.class.getDeclaredField("array");
+            TFIELD_FLAT = UNSAFE.isFlatField(TPB_field);
+            TARRAY_FLAT = UNSAFE.isFlattenedArray(TPB_array.getType());
+            NTFIELD_FLAT = UNSAFE.isFlatField(NTPB_field);
+            NTARRAY_FLAT = UNSAFE.isFlattenedArray(NTPB_array.getType());
+        } catch (ReflectiveOperationException ex) {
+            throw new AssertionError(ex);
+        }
+    }
+    private static final String SETTINGS =
+        String.format("USE_COMPILER=%s ALWAYS_ATOMIC=%s TEAR_MODE=%s STEP_COUNT=%s FLAT TF/TA=%s/%s NTF/NTA=%s/%s",
+                      USE_COMPILER, ALWAYS_ATOMIC, TEAR_MODE, STEP_COUNT,
+                      TFIELD_FLAT, TARRAY_FLAT, NTFIELD_FLAT, NTARRAY_FLAT);
+    private static final String NOTE_TORN_POINT = "Note: torn point";
+
+    public static void main(String[] args) throws Exception {
+        System.out.println(SETTINGS);
+        ValueTearing valueTearing = new ValueTearing();
+        valueTearing.run();
+        // Extra representation check:
+        assert(!NTFIELD_FLAT) : "NT field must be indirect not flat";
+        assert(!NTARRAY_FLAT) : "NT array must be indirect not flat";
+        if (ALWAYS_ATOMIC) {
+            assert(!TFIELD_FLAT) : "field must be indirect not flat";
+            assert(!TARRAY_FLAT) : "array must be indirect not flat";
+        }
+    }
+
+    // A normally tearable inline value.
+    static primitive class TPoint {
+        TPoint(long x, long y) { this.x = x; this.y = y; }
+        final long x, y;
+        public String toString() { return String.format("(%d,%d)", x, y); }
+    }
+
+    static class TooTearable extends AssertionError {
+        final Object badPoint;
+        TooTearable(String msg, Object badPoint) {
+            super(msg);
+            this.badPoint = badPoint;
+        }
+    }
+
+    interface PointBox {
+        void step();    // mutate inline value state
+        void check();   // check sanity of inline value state
+    }
+
+    class TPointBox implements PointBox {
+        TPoint field;
+        TPoint[] array = new TPoint[1];
+        // Step the points forward by incrementing their components
+        // "simultaneously".  A racing thread will catch flaws in the
+        // simultaneity.
+        TPoint step(TPoint p) {
+            return new TPoint(p.x + 1, p.y + 1);
+        }
+        public @Override
+        void step() {
+            if (TEAR_FIELD) {
+                field = step(field);
+            }
+            if (TEAR_ARRAY) {
+                array[0] = step(array[0]);
+            }
+            check();
+        }
+        // Invariant:  The components of each point are "always" equal.
+        // As long as simultaneity is preserved, this is true.
+        public @Override
+        void check() {
+            if (TEAR_FIELD) {
+                check(field, "field");
+            }
+            if (TEAR_ARRAY) {
+                check(array[0], "array element");
+            }
+        }
+        void check(TPoint p, String where) {
+            if (p.x == p.y)  return;
+            String msg = String.format("%s %s in %s; settings = %s",
+                                       NOTE_TORN_POINT,
+                                       p, where, SETTINGS);
+            throw new TooTearable(msg, p);
+        }
+        public String toString() {
+            return String.format("TPB[%s, {%s}]", field, array[0]);
+        }
+    }
+
+    // Add an indirection, as an extra test.
+    interface NT extends NonTearable { }
+
+    // A hardened, non-tearable version of TPoint.
+    static primitive class NTPoint implements NT {
+        NTPoint(long x, long y) { this.x = x; this.y = y; }
+        final long x, y;
+        public String toString() { return String.format("(%d,%d)", x, y); }
+    }
+
+    class NTPointBox implements PointBox {
+        NTPoint field;
+        NTPoint[] array = new NTPoint[1];
+        // Step the points forward by incrementing their components
+        // "simultaneously".  A racing thread will catch flaws in the
+        // simultaneity.
+        NTPoint step(NTPoint p) {
+            return new NTPoint(p.x + 1, p.y + 1);
+        }
+        public @Override
+        void step() {
+            field = step(field);
+            array[0] = step(array[0]);
+            check();
+        }
+        // Invariant:  The components of each point are "always" equal.
+        public @Override
+        void check() {
+            check(field, "field");
+            check(array[0], "array element");
+        }
+        void check(NTPoint p, String where) {
+            if (p.x == p.y)  return;
+            String msg = String.format("%s *NonTearable* %s in %s; settings = %s",
+                                       NOTE_TORN_POINT,
+                                       p, where, SETTINGS);
+            throw new TooTearable(msg, p);
+        }
+        public String toString() {
+            return String.format("NTPB[%s, {%s}]", field, array[0]);
+        }
+    }
+
+    class AsyncObserver extends Thread {
+        volatile boolean done;
+        long observationCount;
+        final PointBox pointBox;
+        volatile Object badPointObserved;
+        AsyncObserver(PointBox pointBox) {
+            this.pointBox = pointBox;
+        }
+        public void run() {
+            try {
+                while (!done) {
+                    observationCount++;
+                    pointBox.check();
+                }
+            } catch (TooTearable ex) {
+                done = true;
+                badPointObserved = ex.badPoint;
+                System.out.println(ex);
+                if (ALWAYS_ATOMIC || ex.badPoint instanceof NonTearable) {
+                    throw ex;
+                }
+            }
+        }
+    }
+
+    public void run() throws Exception {
+        System.out.println("Test for tearing of NTPoint, which must not happen...");
+        run(new NTPointBox(), false);
+        System.out.println("Test for tearing of TPoint, which "+
+                           (ALWAYS_ATOMIC ? "must not" : "is allowed to")+
+                           " happen...");
+        run(new TPointBox(), ALWAYS_ATOMIC ? false : true);
+    }
+    public void run(PointBox pointBox, boolean canTear) throws Exception {
+        var observer = new AsyncObserver(pointBox);
+        observer.start();
+        for (int i = 0; i < STEP_COUNT; i++) {
+            pointBox.step();
+            if (observer.done)  break;
+        }
+        observer.done = true;
+        observer.join();
+        var obCount = observer.observationCount;
+        var badPoint = observer.badPointObserved;
+        System.out.println(String.format("finished after %d observations at %s; %s",
+                                         obCount, pointBox,
+                                         (badPoint == null
+                                          ? "no tearing observed"
+                                          : "bad point = " + badPoint)));
+        if (canTear && badPoint == null) {
+            var complain = String.format("%s NOT observed after %d observations",
+                                         NOTE_TORN_POINT, obCount);
+            System.out.println("?????? "+complain);
+            if (STEP_COUNT >= 3_000_000) {
+                // If it's a small count, OK, but if it's big the test is broken.
+                throw new AssertionError(complain + ", but it should have been");
+            }
+        }
+        if (!canTear && badPoint != null) {
+            throw new AssertionError("should not reach here; other thread must throw");
+        }
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/VarArgsArray.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/VarArgsArray.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/VarArgsArray.java
@@ -0,0 +1,220 @@
+/*
+ * 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.
+ *
+ * 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 runtime.valhalla.inlinetypes;
+
+import java.lang.reflect.*;
+import static jdk.test.lib.Asserts.*;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+/*
+ * @test VarArgsArray
+ * @summary Test if JVM API using varargs work with inline type arrays
+ * @modules java.base/jdk.internal.value
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @compile VarArgsArray.java
+ * @run main/othervm runtime.valhalla.inlinetypes.VarArgsArray
+ */
+
+@ImplicitlyConstructible
+@LooselyConsistentValue
+value class IntValue {
+    int val;
+    public IntValue()       { this(0); }
+    public IntValue(int v)  { val = v; }
+    public int getInt()     { return val; }
+}
+
+
+class NewInstanceFromConstructor {
+
+    int value;
+    static int consCalls = 0;
+
+    public NewInstanceFromConstructor() {
+        this(0);
+    }
+
+    public NewInstanceFromConstructor(int v) {
+        value = v;
+        consCalls++;
+    }
+
+    public NewInstanceFromConstructor(IntValue v) {
+        this(v.getInt());
+    }
+
+    public NewInstanceFromConstructor(IntValue v1,
+                                      IntValue v2) {
+        this(v1.getInt() + v2.getInt());
+    }
+
+    public NewInstanceFromConstructor(IntValue v1,
+                                      String s) {
+        this(v1);
+        throw new RuntimeException(s);
+    }
+
+    public int getValue() { return value; }
+
+    public static int getConsCalls() { return consCalls; }
+}
+
+public class VarArgsArray {
+
+    static final int TOKEN_VALUE = 4711;
+
+    int methodACnt = 0;
+    int methodBCnt = 0;
+    int methodCCnt = 0;
+
+    public VarArgsArray() {
+    }
+
+    public void test() throws Throwable {
+        // test publicly accessable API in the VM...given an inline type array
+        testJvmInvokeMethod();
+        testJvmNewInstanceFromConstructor();
+    }
+
+    public void testJvmInvokeMethod() throws Throwable {
+        MyInt[] array0 = new MyInt[0];
+        MyInt[] array1 = new MyInt[] { new MyInt(TOKEN_VALUE) };
+        MyInt[] array2 = new MyInt[] { new MyInt(TOKEN_VALUE), new MyInt(TOKEN_VALUE) };
+
+        Method methodARef = getClass().getDeclaredMethod("methodA", MyInt.class);
+        Method methodBRef = getClass().getDeclaredMethod("methodB", MyInt.class, MyInt.class);
+        Method methodCRef = getClass().getDeclaredMethod("methodC", MyInt.class, String.class);
+
+        // Positive tests...
+        methodARef.invoke(this, (Object[])array1);
+        assertWithMsg(methodACnt == 1, "methodA did not invoke");
+
+        methodARef.invoke(this, array1[0]);
+        assertWithMsg(methodACnt == 2, "methodA did not invoke");
+
+        methodBRef.invoke(this, (Object[]) array2);
+        assertWithMsg(methodBCnt == 1, "methodB did not invoke");
+
+        methodBRef.invoke(this, array2[0], array2[1]);
+        assertWithMsg(methodBCnt == 2, "methodB did not invoke");
+
+        // Negative tests...
+        int argExCnt = 0;
+        try {
+            methodARef.invoke(this, (Object[]) array0);
+            throw new RuntimeException("Expected fail");
+        } catch (IllegalArgumentException argEx) { argExCnt++; }
+        try {
+            methodARef.invoke(this, (Object[]) array2);
+            throw new RuntimeException("Expected fail");
+        } catch (IllegalArgumentException argEx) { argExCnt++; }
+        try {
+            methodCRef.invoke(this, (Object[]) array2);
+            throw new RuntimeException("Expected fail");
+        } catch (IllegalArgumentException argEx) { argExCnt++; }
+        assertWithMsg(argExCnt == 3, "Did not see the correct number of exceptions");
+        assertWithMsg(methodACnt == 2, "methodA bad invoke count");
+        assertWithMsg(methodBCnt == 2, "methodB bad invoke count");
+        assertWithMsg(methodCCnt == 0, "methodC bad invoke count");
+    }
+
+    public void testJvmNewInstanceFromConstructor() throws Throwable {
+        // Inner classes use outer in param list, so these won't exercise inline type array
+        Class tc = NewInstanceFromConstructor.class;
+        Class pt = IntValue.class;
+        Constructor consARef = tc.getConstructor(pt);
+        Constructor consBRef = tc.getConstructor(pt, pt);
+        Constructor consCRef = tc.getConstructor(pt, String.class);
+        IntValue[] array0 = new IntValue[0];
+        IntValue[] array1 = new IntValue[] { new IntValue(TOKEN_VALUE) };
+        IntValue[] array2 = new IntValue[] { new IntValue(TOKEN_VALUE),
+                                             new IntValue(TOKEN_VALUE) };
+
+        // Positive tests...
+        consARef.newInstance((Object[])array1);
+        consARef.newInstance(array1[0]);
+        NewInstanceFromConstructor test = (NewInstanceFromConstructor)
+            consBRef.newInstance((Object[])array2);
+        assertWithMsg(test.getValue() == (2 * TOKEN_VALUE), "Param corrrupt");
+        consBRef.newInstance(array2[0], array2[1]);
+        assertWithMsg(NewInstanceFromConstructor.getConsCalls() == 4, "Constructor did not invoke");
+
+        // Negative tests...
+        int argExCnt = 0;
+        try {
+            consARef.newInstance((Object[])array0);
+            throw new RuntimeException("Expected fail");
+        } catch (IllegalArgumentException argEx) { argExCnt++; }
+        try {
+            consARef.newInstance((Object[])array2);
+            throw new RuntimeException("Expected fail");
+        } catch (IllegalArgumentException argEx) { argExCnt++; }
+        try {
+            consCRef.newInstance((Object[])array2);
+            throw new RuntimeException("Expected fail");
+        } catch (IllegalArgumentException argEx) { argExCnt++; }
+        assertWithMsg(argExCnt == 3, "Did not see the correct number of exceptions");
+        assertWithMsg(NewInstanceFromConstructor.getConsCalls() == 4, "Constructor should have been invoked");
+    }
+
+    public void methodA(MyInt a) {
+        assertWithMsg(a.value == TOKEN_VALUE, "Bad arg");
+        methodACnt++;
+    }
+
+    public void methodB(MyInt a, MyInt b) {
+        assertWithMsg(a.value == TOKEN_VALUE, "Bad arg");
+        assertWithMsg(b.value == TOKEN_VALUE, "Bad arg");
+        methodBCnt++;
+    }
+
+    public void methodC(MyInt a, String b) {
+        assertWithMsg(a.value == TOKEN_VALUE, "Bad arg");
+        methodCCnt++;
+    }
+
+    static void assertWithMsg(boolean expr, String msg) throws RuntimeException {
+        assertTrue(expr, msg);
+    }
+
+    public static void main(String[] args) throws Throwable {
+        new VarArgsArray().test();
+    }
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    value class MyInt {
+        int value;
+        public MyInt() { this(0); }
+        public MyInt(int v) { this.value = v; }
+    }
+
+
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/VolatileTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/VolatileTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/VolatileTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ *
+ * 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 runtime.valhalla.inlinetypes;
+
+/*
+ * @test VolatileTest
+ * @summary check effect of volatile keyword on flattenable fields
+ * @modules java.base/jdk.internal.misc
+ *          java.base/jdk.internal.vm.annotation
+ * @library /test/lib
+ * @enablePreview
+ * @compile VolatileTest.java
+ * @run main/othervm -XX:InlineFieldMaxFlatSize=128 runtime.valhalla.inlinetypes.VolatileTest
+ */
+
+import jdk.internal.misc.Unsafe;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import java.lang.reflect.*;
+import jdk.test.lib.Asserts;
+
+public class VolatileTest {
+    static final Unsafe U = Unsafe.getUnsafe();
+
+    @ImplicitlyConstructible
+    @LooselyConsistentValue
+    static value class MyValue {
+        int i = 0;
+        int j = 0;
+    }
+
+    static class MyContainer {
+        @NullRestricted
+        MyValue mv0;
+        @NullRestricted
+        volatile MyValue mv1;
+    }
+
+    static public void main (String[] args) {
+        Class<?> c = MyContainer.class;
+        Field f0 = null;
+        Field f1 = null;
+        try {
+            f0 = c.getDeclaredField("mv0");
+            f1 = c.getDeclaredField("mv1");
+        } catch(NoSuchFieldException e) {
+            e.printStackTrace();
+            return;
+        }
+        Asserts.assertTrue(U.isFlatField(f0), "mv0 should be flattened");
+        Asserts.assertFalse(U.isFlatField(f1), "mv1 should not be flattened");
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/classfileparser/ACCICCETests.jcod b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/classfileparser/ACCICCETests.jcod
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/classfileparser/ACCICCETests.jcod
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2022, 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 an abstract class that cannot be a super class for value classes
+// because ACC_IDENTITY is set in its class access flags.
+// It's based on the following source:
+/*
+public abstract class NonPVSuper {
+
+    static int x = 3;
+
+    public static void meth() {
+        System.out.println("hi");
+    }
+}
+*/
+class NonPVSuper {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [33] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1     at 0x0A
+    class #4; // #2     at 0x0F
+    NameAndType #5 #6; // #3     at 0x12
+    Utf8 "java/lang/Object"; // #4     at 0x17
+    Utf8 "<init>"; // #5     at 0x2A
+    Utf8 "()V"; // #6     at 0x33
+    Field #8 #9; // #7     at 0x39
+    class #10; // #8     at 0x3E
+    NameAndType #11 #12; // #9     at 0x41
+    Utf8 "java/lang/System"; // #10     at 0x46
+    Utf8 "out"; // #11     at 0x59
+    Utf8 "Ljava/io/PrintStream;"; // #12     at 0x5F
+    String #14; // #13     at 0x77
+    Utf8 "hi"; // #14     at 0x7A
+    Method #16 #17; // #15     at 0x7F
+    class #18; // #16     at 0x84
+    NameAndType #19 #20; // #17     at 0x87
+    Utf8 "java/io/PrintStream"; // #18     at 0x8C
+    Utf8 "println"; // #19     at 0xA2
+    Utf8 "(Ljava/lang/String;)V"; // #20     at 0xAC
+    Field #22 #23; // #21     at 0xC4
+    class #24; // #22     at 0xC9
+    NameAndType #25 #26; // #23     at 0xCC
+    Utf8 "NonPVSuper"; // #24     at 0xD1
+    Utf8 "x"; // #25     at 0xDE
+    Utf8 "I"; // #26     at 0xE2
+    Utf8 "Code"; // #27     at 0xE6
+    Utf8 "LineNumberTable"; // #28     at 0xED
+    Utf8 "meth"; // #29     at 0xFF
+    Utf8 "<clinit>"; // #30     at 0x0106
+    Utf8 "SourceFile"; // #31     at 0x0111
+    Utf8 "NonPVSuper.java"; // #32     at 0x011E
+  } // Constant Pool
+
+  0x0421; // access [ ACC_PUBLIC ACC_IDENTITY ACC_ABSTRACT ]
+  #22;// this_cpx
+  #2;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [1] { // Fields
+    {  // field at 0x013A
+      0x0008; // access
+      #25; // name_index       : x
+      #26; // descriptor_index : I
+      [0] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [3] { // Methods
+    {  // method at 0x0144
+      0x0001; // access
+      #5; // name_index       : <init>
+      #6; // descriptor_index : ()V
+      [1] { // Attributes
+        Attr(#27, 29) { // Code at 0x014C
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB70001B1;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#28, 6) { // LineNumberTable at 0x0163
+              [1] { // line_number_table
+                0  2; //  at 0x016F
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method at 0x016F
+      0x0009; // access
+      #29; // name_index       : meth
+      #6; // descriptor_index : ()V
+      [1] { // Attributes
+        Attr(#27, 37) { // Code at 0x0177
+          2; // max_stack
+          0; // max_locals
+          Bytes[9]{
+            0xB20007120DB6000F;
+            0xB1;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#28, 10) { // LineNumberTable at 0x0192
+              [2] { // line_number_table
+                0  7; //  at 0x019E
+                8  8; //  at 0x01A2
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method at 0x01A2
+      0x0008; // access
+      #30; // name_index       : <clinit>
+      #6; // descriptor_index : ()V
+      [1] { // Attributes
+        Attr(#27, 29) { // Code at 0x01AA
+          1; // max_stack
+          0; // max_locals
+          Bytes[5]{
+            0x06B30015B1;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#28, 6) { // LineNumberTable at 0x01C1
+              [1] { // line_number_table
+                0  4; //  at 0x01CD
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [1] { // Attributes
+    Attr(#31, 2) { // SourceFile at 0x01CF
+      #32;
+    } // end SourceFile
+  } // Attributes
+} // end class NonPVSuper
+
+
+// Dot is a value class that tries to inherit from a super class (NonPVSuper)
+// that has access flag ACC_IDENTITY set.
+// Dot is based on the following source:
+/*
+public value final class Dot extends NonPVSuper {
+    int x = 3;
+
+    public int getX() {
+        return x;
+    }
+}
+*/
+class Dot {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [18] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1     at 0x0A
+    Utf8 "Dot"; // #2     at 0x0D
+    Field #1 #4; // #3     at 0x13
+    NameAndType #5 #6; // #4     at 0x18
+    Utf8 "x"; // #5     at 0x1D
+    Utf8 "I"; // #6     at 0x21
+    class #8; // #7     at 0x25
+    Utf8 "NonPVSuper"; // #8     at 0x28
+    Utf8 "getX"; // #9     at 0x35
+    Utf8 "()I"; // #10     at 0x3C
+    Utf8 "Code"; // #11     at 0x42
+    Utf8 "LineNumberTable"; // #12     at 0x49
+    Utf8 "<vnew>"; // #13     at 0x5B
+    Utf8 "()LDot;"; // #14     at 0x64
+    Utf8 "SourceFile"; // #15     at 0x6E
+    Utf8 "Dot.java"; // #16     at 0x7B
+    Utf8 "Preload"; // #17     at 0x86
+  } // Constant Pool
+
+  0x0051; // access [ ACC_VALUE ACC_PUBLIC ACC_FINAL ]
+  #1;// this_cpx
+  #7;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [1] { // Fields
+    {  // field at 0x9A
+      0x0010; // access
+      #5; // name_index       : x
+      #6; // descriptor_index : I
+      [0] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [2] { // Methods
+    {  // method at 0xA4
+      0x0001; // access
+      #9; // name_index       : getX
+      #10; // descriptor_index : ()I
+      [1] { // Attributes
+        Attr(#11, 29) { // Code at 0xAC
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40003AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#12, 6) { // LineNumberTable at 0xC3
+              [1] { // line_number_table
+                0  5; //  at 0xCF
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method at 0xCF
+      0x0009; // access
+      #13; // name_index       : <vnew>
+      #14; // descriptor_index : ()LDot;
+      [1] { // Attributes
+        Attr(#11, 41) { // Code at 0xD7
+          2; // max_stack
+          1; // max_locals
+          Bytes[13]{
+            0xCB00014B062A5FCC;
+            0x00034B2AB0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#12, 10) { // LineNumberTable at 0xF6
+              [2] { // line_number_table
+                0  1; //  at 0x0102
+                4  2; //  at 0x0106
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [2] { // Attributes
+    Attr(#15, 2) { // SourceFile at 0x0108
+      #16;
+    } // end SourceFile
+    ;
+    Attr(#17, 4) { // Preload at 0x0110
+      0x00010001;
+    } // end Preload
+  } // Attributes
+} // end class Dot
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/classfileparser/ACC_ICCETest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/classfileparser/ACC_ICCETest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/classfileparser/ACC_ICCETest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2022, 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.
+ *
+ */
+
+/*
+ * @ignore Fix JDK-8328412
+ * @test ACC_ICCETest
+ * @bug 8281279
+ * @summary test that a value class cannot sub-class an identity class
+ * @enablePreview
+ * @compile ACCICCETests.jcod
+ * @run main/othervm ACC_ICCETest
+ */
+
+public class ACC_ICCETest {
+
+    public static void runTest(String test_name, String message) throws Exception {
+        System.out.println("Testing: " + test_name);
+        try {
+            Class newClass = Class.forName(test_name);
+        } catch (java.lang.IncompatibleClassChangeError e) {
+            if (!e.getMessage().contains(message)) {
+                throw new RuntimeException( "Wrong IncompatibleClassChangeError: " + e.getMessage());
+            }
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        runTest("Dot", "Value type Dot has an identity type as supertype");
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/classfileparser/BadInlineTypes.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/classfileparser/BadInlineTypes.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/classfileparser/BadInlineTypes.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ */
+/*
+ * @ignore Fix JDK-8328416
+ * @test
+ * @summary test that the right exceptions get thrown for bad inline type
+ *          class files.
+ * @enablePreview
+ * @compile cfpTests.jcod
+ * @run main/othervm -Xverify:remote BadInlineTypes
+ */
+
+public class BadInlineTypes {
+
+    public static void runTest(String test_name, String message) throws Exception {
+        System.out.println("Testing: " + test_name);
+        try {
+            Class newClass = Class.forName(test_name);
+        } catch (java.lang.ClassFormatError e) {
+            if (!e.getMessage().contains(message)) {
+                throw new RuntimeException( "Wrong ClassFormatError: " + e.getMessage());
+            }
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+
+        // Test that ACC_PRIMITIVE with ACC_ABSTRACT is illegal.
+        runTest("ValueAbstract", "Illegal class modifiers in class ValueAbstract");
+
+        // Test that ACC_PRIMITIVE with ACC_ENUM is illegal.
+        runTest("ValueEnum", "Illegal class modifiers in class ValueEnum");
+
+        // Test that inline type fields must be final.
+        runTest("ValueFieldNotFinal", "Illegal field modifiers in class ValueFieldNotFinal");
+
+        // Test that ACC_PRIMITIVE with ACC_INTERFACE is illegal.
+        runTest("ValueInterface", "Illegal class modifiers in class ValueInterface");
+
+        // Test that inline type instance methods cannot be synchronized.
+        runTest("ValueMethodSynch",
+                "Method getInt in class ValueMethodSynch (not an identity class) has illegal modifiers: 0x20");
+
+        // Test that a class with an old class file version cannot contain a Q signature.
+        runTest("OldClassWithQSig",
+                "Field \"this\" in class OldClassWithQSig has illegal signature \"QOldClassWithQSig;\"");
+
+        // Test that a class with an old class file version cannot contain an array Q signature.
+        runTest("OldClassWithQArraySig",
+                "Field \"ia\" in class OldClassWithQArraySig has illegal signature \"[Qjava/lang/Integer;\"");
+
+        // Test that ClassCircularityError gets detected for instance fields.
+        System.out.println("Testing ClassCircularityError for instance fields");
+        try {
+            Class newClass = Class.forName("Circ");
+            throw new RuntimeException( "java.lang.ClassCircularityError exception not thrown!");
+        } catch (java.lang.ClassCircularityError e) {
+             if (!e.getMessage().contains("Circ")) {
+                 throw new RuntimeException( "Wrong ClassCircularityError: " + e.getMessage());
+             }
+         }
+
+        // Test that ClassCircularityError isn't detected for static fields.
+        System.out.println("Testing ClassCircularityError for static fields");
+        try {
+            Class newClass = Class.forName("CircStaticB");
+        } catch (java.lang.ClassCircularityError e) {
+             throw new RuntimeException( "java.lang.ClassCircularityError exception thrown!");
+         }
+
+        runTest("ValueCloneable", "Inline Types do not support Cloneable");
+
+        runTest("SuperIsZero", "Invalid superclass index 0 in class file SuperIsZero");
+
+        runTest("QInOldClass",
+                "Class name contains illegal Q-signature in descriptor in class file QInOldClass");
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/classfileparser/IllegalFieldModifiers.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/classfileparser/IllegalFieldModifiers.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/classfileparser/IllegalFieldModifiers.java
@@ -0,0 +1,63 @@
+
+
+/*
+ * Copyright (c) 2024, 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 that illegal field modifiers are detected correctly
+ * @enablePreview
+ * @compile fieldModifiersTest.jcod
+ * @run main/othervm -Xverify:remote IllegalFieldModifiers
+ */
+
+
+public class IllegalFieldModifiers {
+
+  public static void runTest(String test_name, String message) throws Exception {
+      System.out.println("Testing: " + test_name);
+      try {
+          Class newClass = Class.forName(test_name);
+      } catch (java.lang.ClassFormatError e) {
+          if (!e.getMessage().contains(message)) {
+              throw new RuntimeException( "Wrong ClassFormatError: " + e.getMessage());
+          }
+      }
+  }
+
+  public static void main(String[] args) throws Exception {
+
+    // Test that ACC_FINAL with ACC_VOLATILE is illegal.
+    runTest("FinalAndVolatile", "Illegal field modifiers in class FinalAndVolatile");
+
+    // Test that ACC_STATIC with ACC_STRICT is illegal.
+    runTest("StrictAndStatic", "Illegal field modifiers in class StrictAndStatic");
+
+    // Test that ACC_STRICT without ACC_FINAL is illegal.
+    runTest("StrictNotFinal", "Illegal field modifiers in class StrictNotFinal");
+
+    // Test that a value class cannot have field without ACC_STATIC or ACC_STRICT
+    runTest("NotStaticNotStrict", "Illegal field modifiers in class NotStaticNotStrict");
+  }
+
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/classfileparser/cfpTests.jcod b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/classfileparser/cfpTests.jcod
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/classfileparser/cfpTests.jcod
@@ -0,0 +1,2696 @@
+/*
+ * Copyright (c) 2018, 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 file contains multiple illegal inline type classes that should cause
+// ClassFormatError exceptions when attempted to be loaded.
+//
+// Many of these test were originally generated from this Java file and then
+// modified to cause ClassFormatError or ClassCircularityError exceptions.  The
+// '(bad)' comments in most of the tests show where the modifications were made.
+//
+// final inline class Value {
+//     static final Value VT = makeValue(0x01234567);
+//     final int int_v;
+//     Value() {
+//         int_v = 1;
+//     }
+//     static Value makeValue(int x) {
+//         Value v = Value.default;
+//         v = __WithField(v.int_v, x);
+//         return v;
+//     }
+// }
+
+
+// Test that class modifiers cannot have both ACC_PRIMITIVE and ACC_ABSTRACT set.
+//
+class ValueAbstract {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [28] { // Constant Pool
+    ; // first element is empty
+    Method #7 #23; // #1     at 0x0A
+    Field #3 #24; // #2     at 0x0F
+    class #9; // #3     at 0x14
+    int 0x01234567; // #4     at 0x17
+    Method #3 #25; // #5     at 0x1C
+    Field #3 #26; // #6     at 0x21
+    class #27; // #7     at 0x26
+    Utf8 "VT"; // #8     at 0x29
+    Utf8 "ValueAbstract"; // #9     at 0x2E
+    Utf8 "ValueTypes"; // #10     at 0x36
+    Utf8 "LValueAbstract;"; // #11     at 0x43
+    Utf8 "int_v"; // #12     at 0x4D
+    Utf8 "I"; // #13     at 0x55
+    Utf8 "<init>"; // #14     at 0x59
+    Utf8 "()V"; // #15     at 0x62
+    Utf8 "Code"; // #16     at 0x68
+    Utf8 "LineNumberTable"; // #17     at 0x6F
+    Utf8 "makeValueAbstract"; // #18     at 0x81
+    Utf8 "(I)LValueAbstract;"; // #19     at 0x8D
+    Utf8 "<clinit>"; // #20     at 0x9A
+    Utf8 "SourceFile"; // #21     at 0xA5
+    Utf8 "ValueAbstract.java"; // #22     at 0xB2
+    NameAndType #14 #15; // #23     at 0xBF
+    NameAndType #12 #13; // #24     at 0xC4
+    NameAndType #18 #19; // #25     at 0xC9
+    NameAndType #8 #11; // #26     at 0xCE
+    Utf8 "java/lang/Object"; // #27     at 0xD3
+  } // Constant Pool
+
+  0x0C30; // access [  ACC_PRIMITIVE ACC_ABSTRACT(bad) ACC_SUPER ACC_FINAL ]
+  #3;// this_cpx
+  #7;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // fields
+    { // Member at 0xF0
+      0x0118; // access
+      #8; // name_cpx
+      #11; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0xF8
+      0x0010; // access
+      #12; // name_cpx
+      #13; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [3] { // methods
+    { // Member at 0x0102
+      0x0000; // access
+      #14; // name_cpx
+      #15; // sig_cpx
+      [1] { // Attributes
+        Attr(#16, 42) { // Code at 0x010A
+          2; // max_stack
+          1; // max_locals
+          Bytes[10]{
+            0x2AB700012A04B500;
+            0x02B1;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#17, 14) { // LineNumberTable at 0x0126
+              [3] { // LineNumberTable
+                0  4; //  at 0x0132
+                4  5; //  at 0x0136
+                9  6; //  at 0x013A
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x013A
+      0x0008; // access
+      #18; // name_cpx
+      #19; // sig_cpx
+      [1] { // Attributes
+        Attr(#16, 44) { // Code at 0x0142
+          2; // max_stack
+          2; // max_locals
+          Bytes[12]{
+            0xCB00034C2B1ACC00;
+            0x024C2BB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#17, 14) { // LineNumberTable at 0x0160
+              [3] { // LineNumberTable
+                0  8; //  at 0x016C
+                4  9; //  at 0x0170
+                10  10; //  at 0x0174
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0174
+      0x0008; // access
+      #20; // name_cpx
+      #15; // sig_cpx
+      [1] { // Attributes
+        Attr(#16, 33) { // Code at 0x017C
+          1; // max_stack
+          0; // max_locals
+          Bytes[9]{
+            0x1204B80005B30006;
+            0xB1;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#17, 6) { // LineNumberTable at 0x0197
+              [1] { // LineNumberTable
+                0  2; //  at 0x01A3
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [2] { // Attributes
+    Attr(#21, 2) { // SourceFile at 0x01A5
+      #22;
+    } // end SourceFile
+    ;
+    Attr(#10, 4) { // ValueTypes at 0x01AD
+      0x00010003;
+    } // end ValueTypes
+  } // Attributes
+} // end class ValueAbstract
+
+//////////////////////////////////////////////////////////////////////
+
+// Test that class modifiers cannot have both ACC_PRIMITIVE and ACC_ENUM set.
+//
+class ValueEnum {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [28] { // Constant Pool
+    ; // first element is empty
+    Method #7 #23; // #1     at 0x0A
+    Field #3 #24; // #2     at 0x0F
+    class #9; // #3     at 0x14
+    int 0x01234567; // #4     at 0x17
+    Method #3 #25; // #5     at 0x1C
+    Field #3 #26; // #6     at 0x21
+    class #27; // #7     at 0x26
+    Utf8 "VT"; // #8     at 0x29
+    Utf8 "ValueEnum"; // #9     at 0x2E
+    Utf8 "ValueTypes"; // #10     at 0x36
+    Utf8 "LValueEnum;"; // #11     at 0x43
+    Utf8 "int_v"; // #12     at 0x4D
+    Utf8 "I"; // #13     at 0x55
+    Utf8 "<init>"; // #14     at 0x59
+    Utf8 "()V"; // #15     at 0x62
+    Utf8 "Code"; // #16     at 0x68
+    Utf8 "LineNumberTable"; // #17     at 0x6F
+    Utf8 "makeValueEnum"; // #18     at 0x81
+    Utf8 "(I)LValueEnum;"; // #19     at 0x8D
+    Utf8 "<clinit>"; // #20     at 0x9A
+    Utf8 "SourceFile"; // #21     at 0xA5
+    Utf8 "ValueEnum.java"; // #22     at 0xB2
+    NameAndType #14 #15; // #23     at 0xBF
+    NameAndType #12 #13; // #24     at 0xC4
+    NameAndType #18 #19; // #25     at 0xC9
+    NameAndType #8 #11; // #26     at 0xCE
+    Utf8 "java/lang/Object"; // #27     at 0xD3
+  } // Constant Pool
+
+  0x04850; // access [  ACC_VALUE ACC_PRIMITIVE ACC_ENUM(bad) ACC_FINAL ]
+  #3;// this_cpx
+  #7;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // fields
+    { // Member at 0xF0
+      0x0118; // access
+      #8; // name_cpx
+      #11; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0xF8
+      0x0010; // access
+      #12; // name_cpx
+      #13; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [3] { // methods
+    { // Member at 0x0102
+      0x0000; // access
+      #14; // name_cpx
+      #15; // sig_cpx
+      [1] { // Attributes
+        Attr(#16, 42) { // Code at 0x010A
+          2; // max_stack
+          1; // max_locals
+          Bytes[10]{
+            0x2AB700012A04B500;
+            0x02B1;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#17, 14) { // LineNumberTable at 0x0126
+              [3] { // LineNumberTable
+                0  4; //  at 0x0132
+                4  5; //  at 0x0136
+                9  6; //  at 0x013A
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x013A
+      0x0008; // access
+      #18; // name_cpx
+      #19; // sig_cpx
+      [1] { // Attributes
+        Attr(#16, 44) { // Code at 0x0142
+          2; // max_stack
+          2; // max_locals
+          Bytes[12]{
+            0xCB00034C2B1ACC00;
+            0x024C2BB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#17, 14) { // LineNumberTable at 0x0160
+              [3] { // LineNumberTable
+                0  8; //  at 0x016C
+                4  9; //  at 0x0170
+                10  10; //  at 0x0174
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0174
+      0x0008; // access
+      #20; // name_cpx
+      #15; // sig_cpx
+      [1] { // Attributes
+        Attr(#16, 33) { // Code at 0x017C
+          1; // max_stack
+          0; // max_locals
+          Bytes[9]{
+            0x1204B80005B30006;
+            0xB1;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#17, 6) { // LineNumberTable at 0x0197
+              [1] { // LineNumberTable
+                0  2; //  at 0x01A3
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [2] { // Attributes
+    Attr(#21, 2) { // SourceFile at 0x01A5
+      #22;
+    } // end SourceFile
+    ;
+    Attr(#10, 4) { // ValueTypes at 0x01AD
+      0x00010003;
+    } // end ValueTypes
+  } // Attributes
+} // end class ValueEnum
+
+//////////////////////////////////////////////////////////////////////
+
+// Test that inline type fields must be final.
+//
+class ValueFieldNotFinal {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [28] { // Constant Pool
+    ; // first element is empty
+    Method #7 #23; // #1     at 0x0A
+    Field #3 #24; // #2     at 0x0F
+    class #9; // #3     at 0x14
+    int 0x01234567; // #4     at 0x17
+    Method #3 #25; // #5     at 0x1C
+    Field #3 #26; // #6     at 0x21
+    class #27; // #7     at 0x26
+    Utf8 "VT"; // #8     at 0x29
+    Utf8 "ValueFieldNotFinal"; // #9     at 0x2E
+    Utf8 "ValueTypes"; // #10     at 0x36
+    Utf8 "LValueFieldNotFinal;"; // #11     at 0x43
+    Utf8 "int_v"; // #12     at 0x4D
+    Utf8 "I"; // #13     at 0x55
+    Utf8 "<init>"; // #14     at 0x59
+    Utf8 "()V"; // #15     at 0x62
+    Utf8 "Code"; // #16     at 0x68
+    Utf8 "LineNumberTable"; // #17     at 0x6F
+    Utf8 "makeValueFieldNotFinal"; // #18     at 0x81
+    Utf8 "(I)LValueFieldNotFinal;"; // #19     at 0x8D
+    Utf8 "<clinit>"; // #20     at 0x9A
+    Utf8 "SourceFile"; // #21     at 0xA5
+    Utf8 "ValueFieldNotFinal.java"; // #22     at 0xB2
+    NameAndType #14 #15; // #23     at 0xBF
+    NameAndType #12 #13; // #24     at 0xC4
+    NameAndType #18 #19; // #25     at 0xC9
+    NameAndType #8 #11; // #26     at 0xCE
+    Utf8 "java/lang/Object"; // #27     at 0xD3
+  } // Constant Pool
+
+  0x0850; // access [ ACC_VALUE ACC_PRIMITIVE ACC_FINAL ]
+  #3;// this_cpx
+  #7;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // fields
+    { // Member at 0xF0
+      0x0118; // access
+      #8; // name_cpx
+      #11; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0xF8
+      0x0000; // access [ Field not ACC_FINAL(bad) ]
+      #12; // name_cpx
+      #13; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [3] { // methods
+    { // Member at 0x0102
+      0x0000; // access
+      #14; // name_cpx
+      #15; // sig_cpx
+      [1] { // Attributes
+        Attr(#16, 42) { // Code at 0x010A
+          2; // max_stack
+          1; // max_locals
+          Bytes[10]{
+            0x2AB700012A04B500;
+            0x02B1;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#17, 14) { // LineNumberTable at 0x0126
+              [3] { // LineNumberTable
+                0  4; //  at 0x0132
+                4  5; //  at 0x0136
+                9  6; //  at 0x013A
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x013A
+      0x0008; // access
+      #18; // name_cpx
+      #19; // sig_cpx
+      [1] { // Attributes
+        Attr(#16, 44) { // Code at 0x0142
+          2; // max_stack
+          2; // max_locals
+          Bytes[12]{
+            0xCB00034C2B1ACC00;
+            0x024C2BB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#17, 14) { // LineNumberTable at 0x0160
+              [3] { // LineNumberTable
+                0  8; //  at 0x016C
+                4  9; //  at 0x0170
+                10  10; //  at 0x0174
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0174
+      0x0008; // access
+      #20; // name_cpx
+      #15; // sig_cpx
+      [1] { // Attributes
+        Attr(#16, 33) { // Code at 0x017C
+          1; // max_stack
+          0; // max_locals
+          Bytes[9]{
+            0x1204B80005B30006;
+            0xB1;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#17, 6) { // LineNumberTable at 0x0197
+              [1] { // LineNumberTable
+                0  2; //  at 0x01A3
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [2] { // Attributes
+    Attr(#21, 2) { // SourceFile at 0x01A5
+      #22;
+    } // end SourceFile
+    ;
+    Attr(#10, 4) { // ValueTypes at 0x01AD
+      0x00010003;
+    } // end ValueTypes
+  } // Attributes
+} // end class ValueFieldNotFinal
+
+//////////////////////////////////////////////////////////////////////
+
+// Test that class modifiers cannot have both ACC_PRIMITIVE and ACC_INTERFACE set.
+//
+class ValueInterface {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [27] { // Constant Pool
+    ; // first element is empty
+    Method #7 #21; // #1     at 0x0A
+    Field #3 #22; // #2     at 0x0F
+    class #23; // #3     at 0x14
+    int 0x01234567; // #4     at 0x17
+    Method #3 #24; // #5     at 0x1C
+    Field #3 #25; // #6     at 0x21
+    class #26; // #7     at 0x26
+    Utf8 "VT"; // #8     at 0x29
+    Utf8 "LValueInterface;"; // #9     at 0x2E
+    Utf8 "int_v"; // #10     at 0x3C
+    Utf8 "I"; // #11     at 0x44
+    Utf8 "<init>"; // #12     at 0x48
+    Utf8 "()V"; // #13     at 0x51
+    Utf8 "Code"; // #14     at 0x57
+    Utf8 "LineNumberTable"; // #15     at 0x5E
+    Utf8 "makeValueInterface"; // #16     at 0x70
+    Utf8 "(I)LValueInterface;"; // #17     at 0x80
+    Utf8 "<clinit>"; // #18     at 0x91
+    Utf8 "SourceFile"; // #19     at 0x9C
+    Utf8 "ValueInterface.java"; // #20     at 0xA9
+    NameAndType #12 #13; // #21     at 0xBA
+    NameAndType #10 #11; // #22     at 0xBF
+    Utf8 "ValueInterface"; // #23     at 0xC4
+    NameAndType #16 #17; // #24     at 0xD0
+    NameAndType #8 #9; // #25     at 0xD5
+    Utf8 "java/lang/Object"; // #26     at 0xDA
+  } // Constant Pool
+
+  0x0A30; // access [ ACC_PRIMITIVE ACC_INTERFACE(bad) ACC_SUPER ACC_FINAL ]
+  #3;// this_cpx
+  #7;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // fields
+    { // Member at 0xF7
+      0x0018; // access
+      #8; // name_cpx
+      #9; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0xFF
+      0x0010; // access
+      #10; // name_cpx
+      #11; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [1] { // methods
+    { // Member at 0x0141
+      0x0008; // access
+      #16; // name_cpx
+      #17; // sig_cpx
+      [1] { // Attributes
+        Attr(#14, 44) { // Code at 0x0149
+          2; // max_stack
+          2; // max_locals
+          Bytes[12]{
+            0xCB00034C2B1ACC00;
+            0x024C2BB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#15, 14) { // LineNumberTable at 0x0167
+              [3] { // LineNumberTable
+                0  8; //  at 0x0173
+                4  9; //  at 0x0177
+                10  10; //  at 0x017B
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [1] { // Attributes
+    Attr(#19, 2) { // SourceFile at 0x01AC
+      #20;
+    } // end SourceFile
+  } // Attributes
+} // end class ValueInterface
+
+//////////////////////////////////////////////////////////////////////
+
+// Test that inline type instance methods cannot be synchronized.
+//
+// final inline class ValueMethodSynch {
+//     final int int_v;
+//
+//     ValueMethodSynch() { int_v = 1; }
+//
+//     int getInt(int x) { return x; }
+// }
+
+class ValueMethodSynch {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [46] { // Constant Pool
+    ; // first element is empty
+    class #23; // #1     at 0x0A
+    Field #1 #24; // #2     at 0x0D
+    InvokeDynamic 0s #27; // #3     at 0x12
+    InvokeDynamic 0s #28; // #4     at 0x17
+    InvokeDynamic 0s #29; // #5     at 0x1C
+    class #30; // #6     at 0x21
+    Utf8 "int_v"; // #7     at 0x24
+    Utf8 "I"; // #8     at 0x2C
+    Utf8 "getInt"; // #9     at 0x30
+    Utf8 "(I)I"; // #10     at 0x39
+    Utf8 "Code"; // #11     at 0x40
+    Utf8 "LineNumberTable"; // #12     at 0x47
+    Utf8 "hashCode"; // #13     at 0x59
+    Utf8 "()I"; // #14     at 0x64
+    Utf8 "equals"; // #15     at 0x6A
+    Utf8 "(Ljava/lang/Object;)Z"; // #16     at 0x73
+    Utf8 "toString"; // #17     at 0x8B
+    Utf8 "()Ljava/lang/String;"; // #18     at 0x96
+    Utf8 "<init>"; // #19     at 0xAD
+    Utf8 "()QValueMethodSynch;"; // #20     at 0xB6
+    Utf8 "SourceFile"; // #21     at 0xCD
+    Utf8 "ValueMethodSynch.java"; // #22     at 0xDA
+    Utf8 "ValueMethodSynch"; // #23     at 0xF2
+    NameAndType #7 #8; // #24     at 0x0105
+    Utf8 "BootstrapMethods"; // #25     at 0x010A
+    MethodHandle 6b #31; // #26     at 0x011D
+    NameAndType #13 #32; // #27     at 0x0121
+    NameAndType #15 #33; // #28     at 0x0126
+    NameAndType #17 #34; // #29     at 0x012B
+    Utf8 "java/lang/Object"; // #30     at 0x0130
+    Method #35 #36; // #31     at 0x0143
+    Utf8 "(QValueMethodSynch;)I"; // #32     at 0x0148
+    Utf8 "(QValueMethodSynch;Ljava/lang/Object;)Z"; // #33     at 0x0160
+    Utf8 "(QValueMethodSynch;)Ljava/lang/String;"; // #34     at 0x018A
+    class #37; // #35     at 0x01B3
+    NameAndType #38 #42; // #36     at 0x01B6
+    Utf8 "java/lang/invoke/ValueBootstrapMethods"; // #37     at 0x01BB
+    Utf8 "makeBootstrapMethod"; // #38     at 0x01E4
+    class #44; // #39     at 0x01FA
+    Utf8 "Lookup"; // #40     at 0x01FD
+    Utf8 "InnerClasses"; // #41     at 0x0206
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; // #42     at 0x0215
+    class #45; // #43     at 0x028B
+    Utf8 "java/lang/invoke/MethodHandles$Lookup"; // #44     at 0x028E
+    Utf8 "java/lang/invoke/MethodHandles"; // #45     at 0x02B6
+  } // Constant Pool
+
+  0x0850; // access [ ACC_VALUE ACC_PRIMITIVE ACC_FINAL ]
+  #1;// this_cpx
+  #6;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [1] { // fields
+    { // Member at 0x02E1
+      0x0010; // access
+      #7; // name_cpx
+      #8; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [5] { // methods
+    { // Member at 0x02EB
+      0x0020; // access [ ACC_SYNCHRONIZED(bad) ]
+      #9; // name_cpx
+      #10; // sig_cpx
+      [1] { // Attributes
+        Attr(#11, 26) { // Code at 0x02F3
+          1; // max_stack
+          2; // max_locals
+          Bytes[2]{
+            0x1BAC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#12, 6) { // LineNumberTable at 0x0307
+              [1] { // LineNumberTable
+                0  9; //  at 0x0313
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0313
+      0x0011; // access
+      #13; // name_cpx
+      #14; // sig_cpx
+      [1] { // Attributes
+        Attr(#11, 31) { // Code at 0x031B
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00030000AC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#12, 6) { // LineNumberTable at 0x0334
+              [1] { // LineNumberTable
+                0  1; //  at 0x0340
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0340
+      0x0011; // access
+      #15; // name_cpx
+      #16; // sig_cpx
+      [1] { // Attributes
+        Attr(#11, 32) { // Code at 0x0348
+          2; // max_stack
+          2; // max_locals
+          Bytes[8]{
+            0x2A2BBA00040000AC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#12, 6) { // LineNumberTable at 0x0362
+              [1] { // LineNumberTable
+                0  1; //  at 0x036E
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x036E
+      0x0011; // access
+      #17; // name_cpx
+      #18; // sig_cpx
+      [1] { // Attributes
+        Attr(#11, 31) { // Code at 0x0376
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00050000B0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#12, 6) { // LineNumberTable at 0x038F
+              [1] { // LineNumberTable
+                0  1; //  at 0x039B
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x039B
+      0x0008; // access
+      #19; // name_cpx
+      #20; // sig_cpx
+      [1] { // Attributes
+        Attr(#11, 45) { // Code at 0x03A3
+          2; // max_stack
+          1; // max_locals
+          Bytes[13]{
+            0xCB00014B042A5FCC;
+            0x00024B2AB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#12, 14) { // LineNumberTable at 0x03C2
+              [3] { // LineNumberTable
+                0  4; //  at 0x03CE
+                4  5; //  at 0x03D2
+                11  6; //  at 0x03D6
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [3] { // Attributes
+    Attr(#21, 2) { // SourceFile at 0x03D8
+      #22;
+    } // end SourceFile
+    ;
+    Attr(#41, 10) { // InnerClasses at 0x03E0
+      [1] { // InnerClasses
+        #39 #43 #40 25; //  at 0x03F0
+      }
+    } // end InnerClasses
+    ;
+    Attr(#25, 6) { // BootstrapMethods at 0x03F0
+      [1] { // bootstrap_methods
+        {  //  bootstrap_method
+          #26; // bootstrap_method_ref
+          [0] { // bootstrap_arguments
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+      }
+    } // end BootstrapMethods
+  } // Attributes
+} // end class ValueMethodSynch
+
+//////////////////////////////////////////////////////////////////////
+
+// Inline types Circ and Circ2 have fields of each other's type.  This should
+// cause a ClassCircularityError exception when one of them is being loaded.
+//
+// The inline types are based on these two Java classes:
+//
+// final inline class Circ {
+//     static final Circ VT = makeCirc(0x01234567);
+//     final int int_v;
+//     final Circ2 v2;
+//     Circ() {
+//         int_v = 1;
+//         v2 = Circ2.default;
+//     }
+//     static Circ makeCirc(int x) {
+//         Circ v = Circ.default;
+//         v = __WithField(v.int_v, x);
+//         return v;
+//     }
+// }
+//----------------------------------------------------------------
+// final inline class Circ2 {
+//     static final Circ2 VT = makeCirc2('\u0123');
+//     final char char_v;
+//     final Circ vv;
+//     Circ2() {
+//         char_v = 'z';
+//         vv = Circ.default;
+//     }
+//     static Circ2 makeCirc2(char c) {
+//         Circ2 v = Circ2.default;
+//         v = __WithField(v.char_v, c);
+//         return v;
+//     }
+// }
+
+class Circ {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [61] { // Constant Pool
+    ; // first element is empty
+    class #34; // #1     at 0x0A
+    Field #1 #35; // #2     at 0x0D
+    class #36; // #3     at 0x12
+    Field #1 #37; // #4     at 0x15
+    InvokeDynamic 0s #40; // #5     at 0x1A
+    InvokeDynamic 0s #41; // #6     at 0x1F
+    InvokeDynamic 0s #42; // #7     at 0x24
+    int 0x01234567; // #8     at 0x29
+    Method #1 #43; // #9     at 0x2E
+    Field #1 #44; // #10     at 0x33
+    class #45; // #11     at 0x38
+    Utf8 "VT"; // #12     at 0x3B
+    Utf8 "QCirc;"; // #13     at 0x40
+    Utf8 "int_v"; // #14     at 0x49
+    Utf8 "I"; // #15     at 0x51
+    Utf8 "v2"; // #16     at 0x55
+    Utf8 "QCirc2;"; // #17     at 0x5A
+    Utf8 "makeCirc"; // #18     at 0x65
+    Utf8 "(I)QCirc;"; // #19     at 0x6F
+    Utf8 "Code"; // #20     at 0x7B
+    Utf8 "LineNumberTable"; // #21     at 0x82
+    Utf8 "hashCode"; // #22     at 0x94
+    Utf8 "()I"; // #23     at 0x9F
+    Utf8 "equals"; // #24     at 0xA5
+    Utf8 "(Ljava/lang/Object;)Z"; // #25     at 0xAE
+    Utf8 "toString"; // #26     at 0xC6
+    Utf8 "()Ljava/lang/String;"; // #27     at 0xD1
+    Utf8 "<clinit>"; // #28     at 0xE8
+    Utf8 "()V"; // #29     at 0xF3
+    Utf8 "<vnew>"; // #30     at 0xF9
+    Utf8 "()QCirc;"; // #31     at 0x0102
+    Utf8 "SourceFile"; // #32     at 0x010D
+    Utf8 "Circ.java"; // #33     at 0x011A
+    Utf8 "Circ"; // #34     at 0x0126
+    NameAndType #14 #15; // #35     at 0x012D
+    Utf8 "Circ2"; // #36     at 0x0132
+    NameAndType #16 #17; // #37     at 0x013A
+    Utf8 "BootstrapMethods"; // #38     at 0x013F
+    MethodHandle 6b #46; // #39     at 0x0152
+    NameAndType #22 #47; // #40     at 0x0156
+    NameAndType #24 #48; // #41     at 0x015B
+    NameAndType #26 #49; // #42     at 0x0160
+    NameAndType #18 #19; // #43     at 0x0165
+    NameAndType #12 #13; // #44     at 0x016A
+    Utf8 "java/lang/Object"; // #45     at 0x016F
+    Method #50 #51; // #46     at 0x0182
+    Utf8 "(QCirc;)I"; // #47     at 0x0187
+    Utf8 "(QCirc;Ljava/lang/Object;)Z"; // #48     at 0x0193
+    Utf8 "(QCirc;)Ljava/lang/String;"; // #49     at 0x01B1
+    class #52; // #50     at 0x01CE
+    NameAndType #53 #57; // #51     at 0x01D1
+    Utf8 "java/lang/invoke/ValueBootstrapMethods"; // #52     at 0x01D6
+    Utf8 "makeBootstrapMethod"; // #53     at 0x01FF
+    class #59; // #54     at 0x0215
+    Utf8 "Lookup"; // #55     at 0x0218
+    Utf8 "InnerClasses"; // #56     at 0x0221
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; // #57     at 0x0230
+    class #60; // #58     at 0x02A6
+    Utf8 "java/lang/invoke/MethodHandles$Lookup"; // #59     at 0x02A9
+    Utf8 "java/lang/invoke/MethodHandles"; // #60     at 0x02D1
+  } // Constant Pool
+
+  0x0850; // access [ ACC_VALUE ACC_PRIMITIVE ACC_FINAL ]
+  #1;// this_cpx
+  #11;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [3] { // fields
+    { // Member at 0x02FC
+      0x0018; // access
+      #12; // name_cpx
+      #13; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0304
+      0x0010; // access
+      #14; // name_cpx
+      #15; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x030C
+      0x0010; // access
+      #16; // name_cpx
+      #17; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [6] { // methods
+    { // Member at 0x0316
+      0x0008; // access
+      #18; // name_cpx
+      #19; // sig_cpx
+      [1] { // Attributes
+        Attr(#20, 45) { // Code at 0x031E
+          2; // max_stack
+          2; // max_locals
+          Bytes[13]{
+            0xCB00014C1A2B5FCC;
+            0x00024C2BB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#21, 14) { // LineNumberTable at 0x033D
+              [3] { // LineNumberTable
+                0  12; //  at 0x0349
+                4  13; //  at 0x034D
+                11  14; //  at 0x0351
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0351
+      0x0011; // access
+      #22; // name_cpx
+      #23; // sig_cpx
+      [1] { // Attributes
+        Attr(#20, 31) { // Code at 0x0359
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00050000AC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#21, 6) { // LineNumberTable at 0x0372
+              [1] { // LineNumberTable
+                0  1; //  at 0x037E
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x037E
+      0x0011; // access
+      #24; // name_cpx
+      #25; // sig_cpx
+      [1] { // Attributes
+        Attr(#20, 32) { // Code at 0x0386
+          2; // max_stack
+          2; // max_locals
+          Bytes[8]{
+            0x2A2BBA00060000AC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#21, 6) { // LineNumberTable at 0x03A0
+              [1] { // LineNumberTable
+                0  1; //  at 0x03AC
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03AC
+      0x0011; // access
+      #26; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#20, 31) { // Code at 0x03B4
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00070000B0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#21, 6) { // LineNumberTable at 0x03CD
+              [1] { // LineNumberTable
+                0  1; //  at 0x03D9
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03D9
+      0x0008; // access
+      #28; // name_cpx
+      #29; // sig_cpx
+      [1] { // Attributes
+        Attr(#20, 33) { // Code at 0x03E1
+          1; // max_stack
+          0; // max_locals
+          Bytes[9]{
+            0x1208B80009B3000A;
+            0xB1;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#21, 6) { // LineNumberTable at 0x03FC
+              [1] { // LineNumberTable
+                0  2; //  at 0x0408
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0408
+      0x0008; // access
+      #30; // name_cpx
+      #31; // sig_cpx
+      [1] { // Attributes
+        Attr(#20, 58) { // Code at 0x0410
+          2; // max_stack
+          1; // max_locals
+          Bytes[22]{
+            0xCB00014B042A5FCC;
+            0x00024BCB00032A5F;
+            0xCC00044B2AB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#21, 18) { // LineNumberTable at 0x0438
+              [4] { // LineNumberTable
+                0  6; //  at 0x0444
+                4  7; //  at 0x0448
+                11  8; //  at 0x044C
+                20  9; //  at 0x0450
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [3] { // Attributes
+    Attr(#32, 2) { // SourceFile at 0x0452
+      #33;
+    } // end SourceFile
+    ;
+    Attr(#56, 10) { // InnerClasses at 0x045A
+      [1] { // InnerClasses
+        #54 #58 #55 25; //  at 0x046A
+      }
+    } // end InnerClasses
+    ;
+    Attr(#38, 6) { // BootstrapMethods at 0x046A
+      [1] { // bootstrap_methods
+        {  //  bootstrap_method
+          #39; // bootstrap_method_ref
+          [0] { // bootstrap_arguments
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+      }
+    } // end BootstrapMethods
+  } // Attributes
+} // end class Circ
+
+class Circ2 {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [60] { // Constant Pool
+    ; // first element is empty
+    class #33; // #1     at 0x0A
+    Field #1 #34; // #2     at 0x0D
+    class #35; // #3     at 0x12
+    Field #1 #36; // #4     at 0x15
+    InvokeDynamic 0s #39; // #5     at 0x1A
+    InvokeDynamic 0s #40; // #6     at 0x1F
+    InvokeDynamic 0s #41; // #7     at 0x24
+    Method #1 #42; // #8     at 0x29
+    Field #1 #43; // #9     at 0x2E
+    class #44; // #10     at 0x33
+    Utf8 "VT"; // #11     at 0x36
+    Utf8 "QCirc2;"; // #12     at 0x3B
+    Utf8 "char_v"; // #13     at 0x45
+    Utf8 "C"; // #14     at 0x4E
+    Utf8 "vv"; // #15     at 0x52
+    Utf8 "QCirc;"; // #16     at 0x57
+    Utf8 "makeCirc2"; // #17     at 0x60
+    Utf8 "(C)QCirc2;"; // #18     at 0x6C
+    Utf8 "Code"; // #19     at 0x79
+    Utf8 "LineNumberTable"; // #20     at 0x80
+    Utf8 "hashCode"; // #21     at 0x92
+    Utf8 "()I"; // #22     at 0x9D
+    Utf8 "equals"; // #23     at 0xA3
+    Utf8 "(Ljava/lang/Object;)Z"; // #24     at 0xAC
+    Utf8 "toString"; // #25     at 0xC4
+    Utf8 "()Ljava/lang/String;"; // #26     at 0xCF
+    Utf8 "<clinit>"; // #27     at 0xE6
+    Utf8 "()V"; // #28     at 0xF1
+    Utf8 "<vnew>"; // #29     at 0xF7
+    Utf8 "()QCirc2;"; // #30     at 0x0100
+    Utf8 "SourceFile"; // #31     at 0x010C
+    Utf8 "Circ2.java"; // #32     at 0x0119
+    Utf8 "Circ2"; // #33     at 0x0126
+    NameAndType #13 #14; // #34     at 0x012E
+    Utf8 "Circ"; // #35     at 0x0133
+    NameAndType #15 #16; // #36     at 0x013A
+    Utf8 "BootstrapMethods"; // #37     at 0x013F
+    MethodHandle 6b #45; // #38     at 0x0152
+    NameAndType #21 #46; // #39     at 0x0156
+    NameAndType #23 #47; // #40     at 0x015B
+    NameAndType #25 #48; // #41     at 0x0160
+    NameAndType #17 #18; // #42     at 0x0165
+    NameAndType #11 #12; // #43     at 0x016A
+    Utf8 "java/lang/Object"; // #44     at 0x016F
+    Method #49 #50; // #45     at 0x0182
+    Utf8 "(QCirc2;)I"; // #46     at 0x0187
+    Utf8 "(QCirc2;Ljava/lang/Object;)Z"; // #47     at 0x0194
+    Utf8 "(QCirc2;)Ljava/lang/String;"; // #48     at 0x01B3
+    class #51; // #49     at 0x01D1
+    NameAndType #52 #56; // #50     at 0x01D4
+    Utf8 "java/lang/invoke/ValueBootstrapMethods"; // #51     at 0x01D9
+    Utf8 "makeBootstrapMethod"; // #52     at 0x0202
+    class #58; // #53     at 0x0218
+    Utf8 "Lookup"; // #54     at 0x021B
+    Utf8 "InnerClasses"; // #55     at 0x0224
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; // #56     at 0x0233
+    class #59; // #57     at 0x02A9
+    Utf8 "java/lang/invoke/MethodHandles$Lookup"; // #58     at 0x02AC
+    Utf8 "java/lang/invoke/MethodHandles"; // #59     at 0x02D4
+  } // Constant Pool
+
+  0x0850; // access [ ACC_VALUE ACC_PRIMITIVE ACC_FINAL ]
+  #1;// this_cpx
+  #10;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [3] { // fields
+    { // Member at 0x02FF
+      0x0018; // access
+      #11; // name_cpx
+      #12; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0307
+      0x0010; // access
+      #13; // name_cpx
+      #14; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x030F
+      0x0010; // access
+      #15; // name_cpx
+      #16; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [6] { // methods
+    { // Member at 0x0319
+      0x0008; // access
+      #17; // name_cpx
+      #18; // sig_cpx
+      [1] { // Attributes
+        Attr(#19, 45) { // Code at 0x0321
+          2; // max_stack
+          2; // max_locals
+          Bytes[13]{
+            0xCB00014C1A2B5FCC;
+            0x00024C2BB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#20, 14) { // LineNumberTable at 0x0340
+              [3] { // LineNumberTable
+                0  12; //  at 0x034C
+                4  13; //  at 0x0350
+                11  14; //  at 0x0354
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0354
+      0x0011; // access
+      #21; // name_cpx
+      #22; // sig_cpx
+      [1] { // Attributes
+        Attr(#19, 31) { // Code at 0x035C
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00050000AC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#20, 6) { // LineNumberTable at 0x0375
+              [1] { // LineNumberTable
+                0  1; //  at 0x0381
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0381
+      0x0011; // access
+      #23; // name_cpx
+      #24; // sig_cpx
+      [1] { // Attributes
+        Attr(#19, 32) { // Code at 0x0389
+          2; // max_stack
+          2; // max_locals
+          Bytes[8]{
+            0x2A2BBA00060000AC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#20, 6) { // LineNumberTable at 0x03A3
+              [1] { // LineNumberTable
+                0  1; //  at 0x03AF
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03AF
+      0x0011; // access
+      #25; // name_cpx
+      #26; // sig_cpx
+      [1] { // Attributes
+        Attr(#19, 31) { // Code at 0x03B7
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00070000B0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#20, 6) { // LineNumberTable at 0x03D0
+              [1] { // LineNumberTable
+                0  1; //  at 0x03DC
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03DC
+      0x0008; // access
+      #27; // name_cpx
+      #28; // sig_cpx
+      [1] { // Attributes
+        Attr(#19, 34) { // Code at 0x03E4
+          1; // max_stack
+          0; // max_locals
+          Bytes[10]{
+            0x110123B80008B300;
+            0x09B1;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#20, 6) { // LineNumberTable at 0x0400
+              [1] { // LineNumberTable
+                0  2; //  at 0x040C
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x040C
+      0x0008; // access
+      #29; // name_cpx
+      #30; // sig_cpx
+      [1] { // Attributes
+        Attr(#19, 59) { // Code at 0x0414
+          2; // max_stack
+          1; // max_locals
+          Bytes[23]{
+            0xCB00014B107A2A5F;
+            0xCC00024BCB00032A;
+            0x5FCC00044B2AB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#20, 18) { // LineNumberTable at 0x043D
+              [4] { // LineNumberTable
+                0  6; //  at 0x0449
+                4  7; //  at 0x044D
+                12  8; //  at 0x0451
+                21  9; //  at 0x0455
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [3] { // Attributes
+    Attr(#31, 2) { // SourceFile at 0x0457
+      #32;
+    } // end SourceFile
+    ;
+    Attr(#55, 10) { // InnerClasses at 0x045F
+      [1] { // InnerClasses
+        #53 #57 #54 25; //  at 0x046F
+      }
+    } // end InnerClasses
+    ;
+    Attr(#37, 6) { // BootstrapMethods at 0x046F
+      [1] { // bootstrap_methods
+        {  //  bootstrap_method
+          #38; // bootstrap_method_ref
+          [0] { // bootstrap_arguments
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+      }
+    } // end BootstrapMethods
+  } // Attributes
+} // end class Circ2
+
+//////////////////////////////////////////////////////////////////////
+
+// Inline types CircStaticA and CircStaticB have static fields of each other's
+// type.  This should cause a ClassCircularityError exception when one of them
+// is being loaded.
+//
+// The inline types are based on these two Java classes:
+//
+// final inline class CircStaticA {
+//     static final CircStaticA VT = makeCircStaticA(0x01234567);
+//     final int int_v;
+//     static final CircStaticB v2 = CircStaticB.default;
+//     CircStaticA() {
+//         int_v = 1;
+//     }
+//     static CircStaticA makeCircStaticA(int x) {
+//         CircStaticA v = CircStaticA.default;
+//         v = __WithField(v.int_v, x);
+//         return v;
+//     }
+// }
+//----------------------------------------------------------------
+// final inline class CircStaticB {
+//     static final CircStaticB VT = makeCircStaticB(0x01234567);
+//     final int int_v;
+//     static final CircStaticA v2 = CircStaticA.default;
+//     CircStaticB() {
+//         int_v = 1;
+//     }
+//     static CircStaticB makeCircStaticB(int x) {
+//         CircStaticB v = CircStaticB.default;
+//         v = __WithField(v.int_v, x);
+//         return v;
+//     }
+// }
+
+class CircStaticA {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [61] { // Constant Pool
+    ; // first element is empty
+    class #34; // #1     at 0x0A
+    Field #1 #35; // #2     at 0x0D
+    InvokeDynamic 0s #38; // #3     at 0x12
+    InvokeDynamic 0s #39; // #4     at 0x17
+    InvokeDynamic 0s #40; // #5     at 0x1C
+    int 0x01234567; // #6     at 0x21
+    Method #1 #41; // #7     at 0x26
+    Field #1 #42; // #8     at 0x2B
+    class #43; // #9     at 0x30
+    Field #1 #44; // #10     at 0x33
+    class #45; // #11     at 0x38
+    Utf8 "VT"; // #12     at 0x3B
+    Utf8 "QCircStaticA;"; // #13     at 0x40
+    Utf8 "int_v"; // #14     at 0x50
+    Utf8 "I"; // #15     at 0x58
+    Utf8 "v2"; // #16     at 0x5C
+    Utf8 "QCircStaticB;"; // #17     at 0x61
+    Utf8 "makeCircStaticA"; // #18     at 0x71
+    Utf8 "(I)QCircStaticA;"; // #19     at 0x83
+    Utf8 "Code"; // #20     at 0x96
+    Utf8 "LineNumberTable"; // #21     at 0x9D
+    Utf8 "hashCode"; // #22     at 0xAF
+    Utf8 "()I"; // #23     at 0xBA
+    Utf8 "equals"; // #24     at 0xC0
+    Utf8 "(Ljava/lang/Object;)Z"; // #25     at 0xC9
+    Utf8 "toString"; // #26     at 0xE1
+    Utf8 "()Ljava/lang/String;"; // #27     at 0xEC
+    Utf8 "<clinit>"; // #28     at 0x0103
+    Utf8 "()V"; // #29     at 0x010E
+    Utf8 "<vnew>"; // #30     at 0x0114
+    Utf8 "()QCircStaticA;"; // #31     at 0x011D
+    Utf8 "SourceFile"; // #32     at 0x012F
+    Utf8 "CircStaticA.java"; // #33     at 0x013C
+    Utf8 "CircStaticA"; // #34     at 0x014F
+    NameAndType #14 #15; // #35     at 0x015D
+    Utf8 "BootstrapMethods"; // #36     at 0x0162
+    MethodHandle 6b #46; // #37     at 0x0175
+    NameAndType #22 #47; // #38     at 0x0179
+    NameAndType #24 #48; // #39     at 0x017E
+    NameAndType #26 #49; // #40     at 0x0183
+    NameAndType #18 #19; // #41     at 0x0188
+    NameAndType #12 #13; // #42     at 0x018D
+    Utf8 "CircStaticB"; // #43     at 0x0192
+    NameAndType #16 #17; // #44     at 0x01A0
+    Utf8 "java/lang/Object"; // #45     at 0x01A5
+    Method #50 #51; // #46     at 0x01B8
+    Utf8 "(QCircStaticA;)I"; // #47     at 0x01BD
+    Utf8 "(QCircStaticA;Ljava/lang/Object;)Z"; // #48     at 0x01D0
+    Utf8 "(QCircStaticA;)Ljava/lang/String;"; // #49     at 0x01F5
+    class #52; // #50     at 0x0219
+    NameAndType #53 #57; // #51     at 0x021C
+    Utf8 "java/lang/invoke/ValueBootstrapMethods"; // #52     at 0x0221
+    Utf8 "makeBootstrapMethod"; // #53     at 0x024A
+    class #59; // #54     at 0x0260
+    Utf8 "Lookup"; // #55     at 0x0263
+    Utf8 "InnerClasses"; // #56     at 0x026C
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; // #57     at 0x027B
+    class #60; // #58     at 0x02F1
+    Utf8 "java/lang/invoke/MethodHandles$Lookup"; // #59     at 0x02F4
+    Utf8 "java/lang/invoke/MethodHandles"; // #60     at 0x031C
+  } // Constant Pool
+
+  0x0850; // access [ ACC_VALUE ACC_PRIMITIVE ACC_FINAL ]
+  #1;// this_cpx
+  #11;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [3] { // fields
+    { // Member at 0x0347
+      0x0018; // access
+      #12; // name_cpx
+      #13; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x034F
+      0x0010; // access
+      #14; // name_cpx
+      #15; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0357
+      0x0018; // access
+      #16; // name_cpx
+      #17; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [6] { // methods
+    { // Member at 0x0361
+      0x0008; // access
+      #18; // name_cpx
+      #19; // sig_cpx
+      [1] { // Attributes
+        Attr(#20, 45) { // Code at 0x0369
+          2; // max_stack
+          2; // max_locals
+          Bytes[13]{
+            0xCB00014C1A2B5FCC;
+            0x00024C2BB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#21, 14) { // LineNumberTable at 0x0388
+              [3] { // LineNumberTable
+                0  11; //  at 0x0394
+                4  12; //  at 0x0398
+                11  13; //  at 0x039C
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x039C
+      0x0011; // access
+      #22; // name_cpx
+      #23; // sig_cpx
+      [1] { // Attributes
+        Attr(#20, 31) { // Code at 0x03A4
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00030000AC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#21, 6) { // LineNumberTable at 0x03BD
+              [1] { // LineNumberTable
+                0  1; //  at 0x03C9
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03C9
+      0x0011; // access
+      #24; // name_cpx
+      #25; // sig_cpx
+      [1] { // Attributes
+        Attr(#20, 32) { // Code at 0x03D1
+          2; // max_stack
+          2; // max_locals
+          Bytes[8]{
+            0x2A2BBA00040000AC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#21, 6) { // LineNumberTable at 0x03EB
+              [1] { // LineNumberTable
+                0  1; //  at 0x03F7
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03F7
+      0x0011; // access
+      #26; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#20, 31) { // Code at 0x03FF
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00050000B0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#21, 6) { // LineNumberTable at 0x0418
+              [1] { // LineNumberTable
+                0  1; //  at 0x0424
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0424
+      0x0008; // access
+      #28; // name_cpx
+      #29; // sig_cpx
+      [1] { // Attributes
+        Attr(#20, 43) { // Code at 0x042C
+          1; // max_stack
+          0; // max_locals
+          Bytes[15]{
+            0x1206B80007B30008;
+            0xCB0009B3000AB1;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#21, 10) { // LineNumberTable at 0x044D
+              [2] { // LineNumberTable
+                0  2; //  at 0x0459
+                8  4; //  at 0x045D
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x045D
+      0x0008; // access
+      #30; // name_cpx
+      #31; // sig_cpx
+      [1] { // Attributes
+        Attr(#20, 45) { // Code at 0x0465
+          2; // max_stack
+          1; // max_locals
+          Bytes[13]{
+            0xCB00014B042A5FCC;
+            0x00024B2AB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#21, 14) { // LineNumberTable at 0x0484
+              [3] { // LineNumberTable
+                0  6; //  at 0x0490
+                4  7; //  at 0x0494
+                11  8; //  at 0x0498
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [3] { // Attributes
+    Attr(#32, 2) { // SourceFile at 0x049A
+      #33;
+    } // end SourceFile
+    ;
+    Attr(#56, 10) { // InnerClasses at 0x04A2
+      [1] { // InnerClasses
+        #54 #58 #55 25; //  at 0x04B2
+      }
+    } // end InnerClasses
+    ;
+    Attr(#36, 6) { // BootstrapMethods at 0x04B2
+      [1] { // bootstrap_methods
+        {  //  bootstrap_method
+          #37; // bootstrap_method_ref
+          [0] { // bootstrap_arguments
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+      }
+    } // end BootstrapMethods
+  } // Attributes
+} // end class CircStaticA
+
+class CircStaticB {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [61] { // Constant Pool
+    ; // first element is empty
+    class #34; // #1     at 0x0A
+    Field #1 #35; // #2     at 0x0D
+    InvokeDynamic 0s #38; // #3     at 0x12
+    InvokeDynamic 0s #39; // #4     at 0x17
+    InvokeDynamic 0s #40; // #5     at 0x1C
+    int 0x01234567; // #6     at 0x21
+    Method #1 #41; // #7     at 0x26
+    Field #1 #42; // #8     at 0x2B
+    class #43; // #9     at 0x30
+    Field #1 #44; // #10     at 0x33
+    class #45; // #11     at 0x38
+    Utf8 "VT"; // #12     at 0x3B
+    Utf8 "QCircStaticB;"; // #13     at 0x40
+    Utf8 "int_v"; // #14     at 0x50
+    Utf8 "I"; // #15     at 0x58
+    Utf8 "v2"; // #16     at 0x5C
+    Utf8 "QCircStaticA;"; // #17     at 0x61
+    Utf8 "makeCircStaticB"; // #18     at 0x71
+    Utf8 "(I)QCircStaticB;"; // #19     at 0x83
+    Utf8 "Code"; // #20     at 0x96
+    Utf8 "LineNumberTable"; // #21     at 0x9D
+    Utf8 "hashCode"; // #22     at 0xAF
+    Utf8 "()I"; // #23     at 0xBA
+    Utf8 "equals"; // #24     at 0xC0
+    Utf8 "(Ljava/lang/Object;)Z"; // #25     at 0xC9
+    Utf8 "toString"; // #26     at 0xE1
+    Utf8 "()Ljava/lang/String;"; // #27     at 0xEC
+    Utf8 "<clinit>"; // #28     at 0x0103
+    Utf8 "()V"; // #29     at 0x010E
+    Utf8 "<vnew>"; // #30     at 0x0114
+    Utf8 "()QCircStaticB;"; // #31     at 0x011D
+    Utf8 "SourceFile"; // #32     at 0x012F
+    Utf8 "CircStaticB.java"; // #33     at 0x013C
+    Utf8 "CircStaticB"; // #34     at 0x014F
+    NameAndType #14 #15; // #35     at 0x015D
+    Utf8 "BootstrapMethods"; // #36     at 0x0162
+    MethodHandle 6b #46; // #37     at 0x0175
+    NameAndType #22 #47; // #38     at 0x0179
+    NameAndType #24 #48; // #39     at 0x017E
+    NameAndType #26 #49; // #40     at 0x0183
+    NameAndType #18 #19; // #41     at 0x0188
+    NameAndType #12 #13; // #42     at 0x018D
+    Utf8 "CircStaticA"; // #43     at 0x0192
+    NameAndType #16 #17; // #44     at 0x01A0
+    Utf8 "java/lang/Object"; // #45     at 0x01A5
+    Method #50 #51; // #46     at 0x01B8
+    Utf8 "(QCircStaticB;)I"; // #47     at 0x01BD
+    Utf8 "(QCircStaticB;Ljava/lang/Object;)Z"; // #48     at 0x01D0
+    Utf8 "(QCircStaticB;)Ljava/lang/String;"; // #49     at 0x01F5
+    class #52; // #50     at 0x0219
+    NameAndType #53 #57; // #51     at 0x021C
+    Utf8 "java/lang/invoke/ValueBootstrapMethods"; // #52     at 0x0221
+    Utf8 "makeBootstrapMethod"; // #53     at 0x024A
+    class #59; // #54     at 0x0260
+    Utf8 "Lookup"; // #55     at 0x0263
+    Utf8 "InnerClasses"; // #56     at 0x026C
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; // #57     at 0x027B
+    class #60; // #58     at 0x02F1
+    Utf8 "java/lang/invoke/MethodHandles$Lookup"; // #59     at 0x02F4
+    Utf8 "java/lang/invoke/MethodHandles"; // #60     at 0x031C
+  } // Constant Pool
+
+  0x0850; // access [ ACC_VALUE ACC_PRIMITIVE ACC_FINAL ]
+  #1;// this_cpx
+  #11;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [3] { // fields
+    { // Member at 0x0347
+      0x0018; // access
+      #12; // name_cpx
+      #13; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x034F
+      0x0010; // access
+      #14; // name_cpx
+      #15; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0357
+      0x0018; // access
+      #16; // name_cpx
+      #17; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [6] { // methods
+    { // Member at 0x0361
+      0x0008; // access
+      #18; // name_cpx
+      #19; // sig_cpx
+      [1] { // Attributes
+        Attr(#20, 45) { // Code at 0x0369
+          2; // max_stack
+          2; // max_locals
+          Bytes[13]{
+            0xCB00014C1A2B5FCC;
+            0x00024C2BB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#21, 14) { // LineNumberTable at 0x0388
+              [3] { // LineNumberTable
+                0  11; //  at 0x0394
+                4  12; //  at 0x0398
+                11  13; //  at 0x039C
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x039C
+      0x0011; // access
+      #22; // name_cpx
+      #23; // sig_cpx
+      [1] { // Attributes
+        Attr(#20, 31) { // Code at 0x03A4
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00030000AC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#21, 6) { // LineNumberTable at 0x03BD
+              [1] { // LineNumberTable
+                0  1; //  at 0x03C9
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03C9
+      0x0011; // access
+      #24; // name_cpx
+      #25; // sig_cpx
+      [1] { // Attributes
+        Attr(#20, 32) { // Code at 0x03D1
+          2; // max_stack
+          2; // max_locals
+          Bytes[8]{
+            0x2A2BBA00040000AC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#21, 6) { // LineNumberTable at 0x03EB
+              [1] { // LineNumberTable
+                0  1; //  at 0x03F7
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03F7
+      0x0011; // access
+      #26; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#20, 31) { // Code at 0x03FF
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00050000B0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#21, 6) { // LineNumberTable at 0x0418
+              [1] { // LineNumberTable
+                0  1; //  at 0x0424
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0424
+      0x0008; // access
+      #28; // name_cpx
+      #29; // sig_cpx
+      [1] { // Attributes
+        Attr(#20, 43) { // Code at 0x042C
+          1; // max_stack
+          0; // max_locals
+          Bytes[15]{
+            0x1206B80007B30008;
+            0xCB0009B3000AB1;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#21, 10) { // LineNumberTable at 0x044D
+              [2] { // LineNumberTable
+                0  2; //  at 0x0459
+                8  4; //  at 0x045D
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x045D
+      0x0008; // access
+      #30; // name_cpx
+      #31; // sig_cpx
+      [1] { // Attributes
+        Attr(#20, 45) { // Code at 0x0465
+          2; // max_stack
+          1; // max_locals
+          Bytes[13]{
+            0xCB00014B042A5FCC;
+            0x00024B2AB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#21, 14) { // LineNumberTable at 0x0484
+              [3] { // LineNumberTable
+                0  6; //  at 0x0490
+                4  7; //  at 0x0494
+                11  8; //  at 0x0498
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [3] { // Attributes
+    Attr(#32, 2) { // SourceFile at 0x049A
+      #33;
+    } // end SourceFile
+    ;
+    Attr(#56, 10) { // InnerClasses at 0x04A2
+      [1] { // InnerClasses
+        #54 #58 #55 25; //  at 0x04B2
+      }
+    } // end InnerClasses
+    ;
+    Attr(#36, 6) { // BootstrapMethods at 0x04B2
+      [1] { // bootstrap_methods
+        {  //  bootstrap_method
+          #37; // bootstrap_method_ref
+          [0] { // bootstrap_arguments
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+      }
+    } // end BootstrapMethods
+  } // Attributes
+} // end class CircStaticB
+
+//////////////////////////////////////////////////////////////////////
+
+// Test that an inline type cannot be Cloneable.
+//
+// final inline class ValueCloneable implements Cloneable {
+//    final int field;
+//    private ValueCloneable() { field = 0; }
+//}
+
+class ValueCloneable {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [46] { // Constant Pool
+    ; // first element is empty
+    class #22; // #1     at 0x0A
+    Field #1 #23; // #2     at 0x0D
+    InvokeDynamic 0s #26; // #3     at 0x12
+    InvokeDynamic 0s #27; // #4     at 0x17
+    InvokeDynamic 0s #28; // #5     at 0x1C
+    class #29; // #6     at 0x21
+    class #30; // #7     at 0x24
+    Utf8 "field"; // #8     at 0x27
+    Utf8 "I"; // #9     at 0x2F
+    Utf8 "hashCode"; // #10     at 0x33
+    Utf8 "()I"; // #11     at 0x3E
+    Utf8 "Code"; // #12     at 0x44
+    Utf8 "LineNumberTable"; // #13     at 0x4B
+    Utf8 "equals"; // #14     at 0x5D
+    Utf8 "(Ljava/lang/Object;)Z"; // #15     at 0x66
+    Utf8 "toString"; // #16     at 0x7E
+    Utf8 "()Ljava/lang/String;"; // #17     at 0x89
+    Utf8 "<vnew>"; // #18     at 0xA0
+    Utf8 "()QValueCloneable;"; // #19     at 0xA9
+    Utf8 "SourceFile"; // #20     at 0xBE
+    Utf8 "ValueCloneable.java"; // #21     at 0xCB
+    Utf8 "ValueCloneable"; // #22     at 0xE1
+    NameAndType #8 #9; // #23     at 0xF2
+    Utf8 "BootstrapMethods"; // #24     at 0xF7
+    MethodHandle 6b #31; // #25     at 0x010A
+    NameAndType #10 #32; // #26     at 0x010E
+    NameAndType #14 #33; // #27     at 0x0113
+    NameAndType #16 #34; // #28     at 0x0118
+    Utf8 "java/lang/Object"; // #29     at 0x011D
+    Utf8 "java/lang/Cloneable"; // #30     at 0x0130
+    Method #35 #36; // #31     at 0x0146
+    Utf8 "(QValueCloneable;)I"; // #32     at 0x014B
+    Utf8 "(QValueCloneable;Ljava/lang/Object;)Z"; // #33     at 0x0161
+    Utf8 "(QValueCloneable;)Ljava/lang/String;"; // #34     at 0x0189
+    class #37; // #35     at 0x01B0
+    NameAndType #38 #42; // #36     at 0x01B3
+    Utf8 "java/lang/invoke/ValueBootstrapMethods"; // #37     at 0x01B8
+    Utf8 "makeBootstrapMethod"; // #38     at 0x01E1
+    class #44; // #39     at 0x01F7
+    Utf8 "Lookup"; // #40     at 0x01FA
+    Utf8 "InnerClasses"; // #41     at 0x0203
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; // #42     at 0x0212
+    class #45; // #43     at 0x0288
+    Utf8 "java/lang/invoke/MethodHandles$Lookup"; // #44     at 0x028B
+    Utf8 "java/lang/invoke/MethodHandles"; // #45     at 0x02B3
+  } // Constant Pool
+
+  0x0850; // access [ ACC_VALUE ACC_PRIMITIVE ACC_FINAL ]
+  #1;// this_cpx
+  #6;// super_cpx
+
+  [1] { // Interfaces
+    #7;
+  } // Interfaces
+
+  [1] { // fields
+    { // Member at 0x02E0
+      0x0010; // access
+      #8; // name_cpx
+      #9; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [4] { // methods
+    { // Member at 0x02EA
+      0x0011; // access
+      #10; // name_cpx
+      #11; // sig_cpx
+      [1] { // Attributes
+        Attr(#12, 31) { // Code at 0x02F2
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00030000AC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#13, 6) { // LineNumberTable at 0x030B
+              [1] { // LineNumberTable
+                0  1; //  at 0x0317
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0317
+      0x0011; // access
+      #14; // name_cpx
+      #15; // sig_cpx
+      [1] { // Attributes
+        Attr(#12, 32) { // Code at 0x031F
+          2; // max_stack
+          2; // max_locals
+          Bytes[8]{
+            0x2A2BBA00040000AC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#13, 6) { // LineNumberTable at 0x0339
+              [1] { // LineNumberTable
+                0  1; //  at 0x0345
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0345
+      0x0011; // access
+      #16; // name_cpx
+      #17; // sig_cpx
+      [1] { // Attributes
+        Attr(#12, 31) { // Code at 0x034D
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00050000B0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#13, 6) { // LineNumberTable at 0x0366
+              [1] { // LineNumberTable
+                0  1; //  at 0x0372
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0372
+      0x0008; // access
+      #18; // name_cpx
+      #19; // sig_cpx
+      [1] { // Attributes
+        Attr(#12, 37) { // Code at 0x037A
+          2; // max_stack
+          1; // max_locals
+          Bytes[13]{
+            0xCB00014B032A5FCC;
+            0x00024B2AB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#13, 6) { // LineNumberTable at 0x0399
+              [1] { // LineNumberTable
+                0  3; //  at 0x03A5
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [3] { // Attributes
+    Attr(#20, 2) { // SourceFile at 0x03A7
+      #21;
+    } // end SourceFile
+    ;
+    Attr(#41, 10) { // InnerClasses at 0x03AF
+      [1] { // InnerClasses
+        #39 #43 #40 25; //  at 0x03BF
+      }
+    } // end InnerClasses
+    ;
+    Attr(#24, 6) { // BootstrapMethods at 0x03BF
+      [1] { // bootstrap_methods
+        {  //  bootstrap_method
+          #25; // bootstrap_method_ref
+          [0] { // bootstrap_arguments
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+      }
+    } // end BootstrapMethods
+  } // Attributes
+} // end class ValueCloneable
+
+
+// Test that a class with an old class file version cannot contain a Q signature.
+class OldClassWithQSig {
+  0xCAFEBABE;
+  0; // minor version
+  52; // version
+  [29] { // Constant Pool
+    ; // first element is empty
+    Utf8 "OldClassWithQSig"; // #1     at 0x0A
+    class #1; // #2     at 0x1A
+    Utf8 "java/lang/Object"; // #3     at 0x1D
+    class #3; // #4     at 0x30
+    Utf8 "inlineclasses.scala"; // #5     at 0x33
+    Utf8 "Lscala/reflect/ScalaSignature;"; // #6     at 0x49
+    Utf8 "bytes"; // #7     at 0x6A
+    Utf8 "i1QB\t!AAC\bI\t\tQ!!Q\nEAQ!YQ\"T=J]2Lg.Z\"mCN(\"q*W;z}\r1C\n!\tQQ\"D\fa!B:dC2\fB\b\f\te.*fM\t.F!\tQ!#\tJ;%Aj]&$h\b3AA!)qba#"; // #8     at 0x72
+    Utf8 "i"; // #9     at 0x0128
+    Utf8 "I"; // #10     at 0x012C
+    Utf8 "()I"; // #11     at 0x0130
+    NameAndType #9 #10; // #12     at 0x0136
+    Field #2 #12; // #13     at 0x013B
+    Utf8 "this"; // #14     at 0x0140
+    Utf8 "QOldClassWithQSig;"; // #15     at 0x0147
+    Utf8 "<init>"; // #16     at 0x0159
+    Utf8 "(I)V"; // #17     at 0x0162
+    Utf8 "()V"; // #18     at 0x0169
+    NameAndType #16 #18; // #19     at 0x016F
+    Method #4 #19; // #20     at 0x0174
+    Utf8 "Code"; // #21     at 0x0179
+    Utf8 "LineNumberTable"; // #22     at 0x0180
+    Utf8 "LocalVariableTable"; // #23     at 0x0192
+    Utf8 "MethodParameters"; // #24     at 0x01A7
+    Utf8 "SourceFile"; // #25     at 0x01BA
+    Utf8 "RuntimeVisibleAnnotations"; // #26     at 0x01C7
+    Utf8 "ScalaInlineInfo"; // #27     at 0x01E3
+    Utf8 "ScalaSig"; // #28     at 0x01F5
+  } // Constant Pool
+
+  0x0121; // access [ ACC_PUBLIC ACC_SUPER ]
+  #2;// this_cpx
+  #4;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [1] { // Fields
+    {  // field at 0x020A
+      0x0012; // access
+      #9; // name_index       : i
+      #10; // descriptor_index : I
+      [0] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [2] { // Methods
+    {  // method at 0x0214
+      0x0001; // access
+      #9; // name_index       : i
+      #11; // descriptor_index : ()I
+      [1] { // Attributes
+        Attr(#21, 47) { // Code at 0x021C
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB4000DAC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [2] { // Attributes
+            Attr(#22, 6) { // LineNumberTable at 0x0233
+              [1] { // line_number_table
+                0  1; //  at 0x023F
+              }
+            } // end LineNumberTable
+            ;
+            Attr(#23, 12) { // LocalVariableTable at 0x023F
+              [1] { // LocalVariableTable
+                0 5 14 15 0; //  at 0x0251
+              }
+            } // end LocalVariableTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method at 0x0251
+      0x0001; // access
+      #16; // name_index       : <init>
+      #17; // descriptor_index : (I)V
+      [2] { // Attributes
+        Attr(#21, 70) { // Code at 0x0259
+          2; // max_stack
+          2; // max_locals
+          Bytes[10]{
+            0x2A1BB5000D2AB700;
+            0x14B1;
+          }
+          [0] { // Traps
+          } // end Traps
+          [2] { // Attributes
+            Attr(#22, 14) { // LineNumberTable at 0x0275
+              [3] { // line_number_table
+                0  1; //  at 0x0281
+                5  3; //  at 0x0285
+                9  1; //  at 0x0289
+              }
+            } // end LineNumberTable
+            ;
+            Attr(#23, 22) { // LocalVariableTable at 0x0289
+              [2] { // LocalVariableTable
+                0 10 14 15 0; //  at 0x029B
+                0 10 9 10 1; //  at 0x02A5
+              }
+            } // end LocalVariableTable
+          } // Attributes
+        } // end Code
+        ;
+        Attr(#24, 5) { // MethodParameters at 0x02A5
+          [1]b { // MethodParameters
+            #9  0x0010; //  at 0x02B0
+          }
+        } // end MethodParameters
+      } // Attributes
+    }
+  } // Methods
+
+  [4] { // Attributes
+    Attr(#25, 2) { // SourceFile at 0x02B2
+      #5;
+    } // end SourceFile
+    ;
+    Attr(#26, 11) { // RuntimeVisibleAnnotations at 0x02BA
+      [1] { // annotations
+        {  //  annotation
+          #6;
+          [1] { // element_value_pairs
+            {  //  element value pair
+              #7;
+              {  //  element_value
+                's';
+                #8;
+              }  //  element_value
+            }  //  element value pair
+          }  //  element_value_pairs
+        }  //  annotation
+      }
+    } // end RuntimeVisibleAnnotations
+    ;
+    Attr(#27, 14) { // ScalaInlineInfo at 0x02CB
+      0x0100000200100011;
+      0x000009000B00;
+    } // end ScalaInlineInfo
+    ;
+    Attr(#28, 3) { // ScalaSig at 0x02DF
+      0x050200;
+    } // end ScalaSig
+  } // Attributes
+} // end class OldClassWithQSig
+
+
+// Test that a class with an old class file version cannot contain an array Q signature.
+class OldClassWithQArraySig {
+  0xCAFEBABE;
+  0; // minor version
+  52; // version
+  [20] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1     at 0x0A
+    class #4; // #2     at 0x0F
+    NameAndType #5 #6; // #3     at 0x12
+    Utf8 "java/lang/Object"; // #4     at 0x17
+    Utf8 "<init>"; // #5     at 0x2A
+    Utf8 "()V"; // #6     at 0x33
+    class #8; // #7     at 0x39
+    Utf8 "java/lang/Integer"; // #8     at 0x3C
+    Field #10 #11; // #9     at 0x50
+    class #12; // #10     at 0x55
+    NameAndType #13 #14; // #11     at 0x58
+    Utf8 "OldClassWithQArraySig"; // #12     at 0x5D
+    Utf8 "ia"; // #13     at 0x65
+    Utf8 "[Qjava/lang/Integer;"; // #14     at 0x6A
+    Utf8 "Code"; // #15     at 0x81
+    Utf8 "LineNumberTable"; // #16     at 0x88
+    Utf8 "runIt"; // #17     at 0x9A
+    Utf8 "SourceFile"; // #18     at 0xA2
+    Utf8 "OldClassWithQArraySig.java"; // #19     at 0xAF
+  } // Constant Pool
+
+  0x0021; // access [ ACC_PUBLIC ACC_SUPER ]
+  #10;// this_cpx
+  #2;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [1] { // Fields
+    {  // field at 0xC6
+      0x0000; // access
+      #13; // name_index       : ia
+      #14; // descriptor_index : [Qjava/lang/Integer;
+      [0] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [2] { // Methods
+    {  // method at 0xD0
+      0x0001; // access
+      #5; // name_index       : <init>
+      #6; // descriptor_index : ()V
+      [1] { // Attributes
+        Attr(#15, 29) { // Code at 0xD8
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB70001B1;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#16, 6) { // LineNumberTable at 0xEF
+              [1] { // line_number_table
+                0  1; //  at 0xFB
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method at 0xFB
+      0x0001; // access
+      #17; // name_index       : runIt
+      #6; // descriptor_index : ()V
+      [1] { // Attributes
+        Attr(#15, 37) { // Code at 0x0103
+          2; // max_stack
+          1; // max_locals
+          Bytes[9]{
+            0x2A05BD0007B50009;
+            0xB1;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#16, 10) { // LineNumberTable at 0x011E
+              [2] { // line_number_table
+                0  6; //  at 0x012A
+                8  7; //  at 0x012E
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [1] { // Attributes
+    Attr(#18, 2) { // SourceFile at 0x0130
+      #19;
+    } // end SourceFile
+  } // Attributes
+} // end class OldClassWithQArraySig
+
+
+// This class has a super_cpx of zero.  This should cause a ClassFormatError
+// exception when this class is loaded.
+class SuperIsZero {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [15] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1     at 0x0A
+    Utf8 "SuperIsZero"; // #2     at 0x0D
+    Field #1 #4; // #3     at 0x1A
+    NameAndType #5 #6; // #4     at 0x1F
+    Utf8 "x"; // #5     at 0x24
+    Utf8 "I"; // #6     at 0x28
+    class #8; // #7     at 0x2C
+    Utf8 "java/lang/Object"; // #8     at 0x2F
+    Utf8 "<init>"; // #9     at 0x42
+    Utf8 "()QSuperIsZero;"; // #10     at 0x4B
+    Utf8 "Code"; // #11     at 0x5C
+    Utf8 "LineNumberTable"; // #12     at 0x63
+    Utf8 "SourceFile"; // #13     at 0x75
+    Utf8 "SuperIsZero.java"; // #14     at 0x82
+  } // Constant Pool
+
+  0x0051; // access [ ACC_VALUE ACC_PUBLIC ACC_FINAL ]
+  #1;// this_cpx
+  #0;// super_cpx // !!! changed 7 to 0
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [1] { // Fields
+    {  // field at 0x9E
+      0x0010; // access
+      #5; // name_index       : x
+      #6; // descriptor_index : I
+      [0] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [1] { // Methods
+    {  // method at 0xA8
+      0x0009; // access
+      #9; // name_index       : <init>
+      #10; // descriptor_index : ()QSuperIsZero;
+      [1] { // Attributes
+        Attr(#11, 45) { // Code at 0xB0
+          2; // max_stack
+          1; // max_locals
+          Bytes[13]{
+            0xCB00014B082A5FCC;
+            0x00034B2AB0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#12, 14) { // LineNumberTable at 0xCF
+              [3] { // line_number_table
+                0  4; //  at 0xDB
+                4  5; //  at 0xDF
+                11  6; //  at 0xE3
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [1] { // Attributes
+    Attr(#13, 2) { // SourceFile at 0xE5
+      #14;
+    } // end SourceFile
+  } // Attributes
+} // end class SuperIsZero
+
+
+// This class file tests that a ClassFormatError exception is thrown for an old
+// class file (version 49) containing a Q signature.  This file is based on the
+// following Java code, except the signature for method callDot() was changed
+// to "(QDot;)V";.
+/*
+public class QInOldClass {
+
+    public static void callDot(Dot d) {
+        System.out.println("Hi Dot");
+    }
+
+}
+*/
+class QInOldClass {
+  0xCAFEBABE;
+  0; // minor version
+  49; // version
+  [32] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1     at 0x0A
+    class #4; // #2     at 0x0F
+    NameAndType #5 #6; // #3     at 0x12
+    Utf8 "java/lang/Object"; // #4     at 0x17
+    Utf8 "<init>"; // #5     at 0x2A
+    Utf8 "()V"; // #6     at 0x33
+    Field #8 #9; // #7     at 0x39
+    class #10; // #8     at 0x3E
+    NameAndType #11 #12; // #9     at 0x41
+    Utf8 "java/lang/System"; // #10     at 0x46
+    Utf8 "out"; // #11     at 0x59
+    Utf8 "Ljava/io/PrintStream;"; // #12     at 0x5F
+    String #14; // #13     at 0x77
+    Utf8 "Hi Dot"; // #14     at 0x7A
+    Method #16 #17; // #15     at 0x83
+    class #18; // #16     at 0x88
+    NameAndType #19 #20; // #17     at 0x8B
+    Utf8 "java/io/PrintStream"; // #18     at 0x90
+    Utf8 "println"; // #19     at 0xA6
+    Utf8 "(Ljava/lang/String;)V"; // #20     at 0xB0
+    class #22; // #21     at 0xC8
+    Utf8 "QInOldClass"; // #22     at 0xCB
+    Utf8 "Code"; // #23     at 0xD6
+    Utf8 "LineNumberTable"; // #24     at 0xDD
+    Utf8 "callDot"; // #25     at 0xEF
+    Utf8 "(QDot;)V"; // #26     at 0xF9
+    Utf8 "SourceFile"; // #27     at 0x0104
+    Utf8 "QInOldClass.java"; // #28     at 0x0111
+    Utf8 "Preload"; // #29     at 0x0121
+    class #31; // #30     at 0x012B
+    Utf8 "Dot"; // #31     at 0x012E
+  } // Constant Pool
+
+  0x0021; // access [ ACC_PUBLIC ACC_SUPER ]
+  #21;// this_cpx
+  #2;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [0] { // Fields
+  } // Fields
+
+  [2] { // Methods
+    {  // method at 0x0140
+      0x0001; // access
+      #5; // name_index       : <init>
+      #6; // descriptor_index : ()V
+      [1] { // Attributes
+        Attr(#23, 29) { // Code at 0x0148
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB70001B1;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#24, 6) { // LineNumberTable at 0x015F
+              [1] { // line_number_table
+                0  2; //  at 0x016B
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method at 0x016B
+      0x0009; // access
+      #25; // name_index       : callDot
+      #26; // descriptor_index : (LDot;)V
+      [1] { // Attributes
+        Attr(#23, 37) { // Code at 0x0173
+          2; // max_stack
+          1; // max_locals
+          Bytes[9]{
+            0xB20007120DB6000F;
+            0xB1;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#24, 10) { // LineNumberTable at 0x018E
+              [2] { // line_number_table
+                0  5; //  at 0x019A
+                8  6; //  at 0x019E
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [2] { // Attributes
+    Attr(#27, 2) { // SourceFile at 0x01A0
+      #28;
+    } // end SourceFile
+    ;
+    Attr(#29, 4) { // Preload at 0x01A8
+      0x0001001E;
+    } // end Preload
+  } // Attributes
+} // end class QInOldClass
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/classfileparser/fieldModifiersTest.jcod b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/classfileparser/fieldModifiersTest.jcod
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/classfileparser/fieldModifiersTest.jcod
@@ -0,0 +1,517 @@
+/*
+ * Copyright (c) 2024, 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 file contains multiple illegal inline type classes that should cause
+// ClassFormatError exceptions when attempted to be loaded.
+//
+// Many of these test were originally generated from this Java file and then
+// field modifiers are changed to cause a ClassFormatError exceptions.
+//
+// public value class Value {
+//    static int si = 0;
+//    int i = 0;
+//}
+
+
+// A field has both ACC_FINAL and ACC_VOLATILE set
+//
+class FinalAndVolatile {
+  0xCAFEBABE;
+  65535; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Field #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "FinalAndVolatile"; // #4
+    Utf8 "i"; // #5
+    Utf8 "I"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #12; // #9
+    Utf8 "java/lang/Object"; // #10
+    Utf8 "<init>"; // #11
+    Utf8 "()V"; // #12
+    Field #2 #14; // #13
+    NameAndType #15 #6; // #14
+    Utf8 "si"; // #15
+    Utf8 "ConstantValue"; // #16
+    int 0x00000000; // #17
+    Utf8 "Code"; // #18
+    Utf8 "LineNumberTable"; // #19
+    Utf8 "<clinit>"; // #20
+    Utf8 "SourceFile"; // #21
+    Utf8 "FinalAndVolatile.java"; // #22
+  } // Constant Pool
+
+  0x0011; // access
+  #2;// this_cpx
+  #8;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0008; // access
+      #15; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+    ;
+    {  // field
+      0x0850; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#16) { // ConstantValue
+          #17;
+        } // end ConstantValue
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0001; // access
+      #11; // name_index
+      #12; // descriptor_index
+      [] { // Attributes
+        Attr(#18) { // Code
+          2; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2A03B500012AB700;
+            0x07B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#19) { // LineNumberTable
+              [] { // line_number_table
+                0  3;
+                5  1;
+                9  3;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0008; // access
+      #20; // name_index
+      #12; // descriptor_index
+      [] { // Attributes
+        Attr(#18) { // Code
+          1; // max_stack
+          0; // max_locals
+          Bytes[]{
+            0x03B3000DB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#19) { // LineNumberTable
+              [] { // line_number_table
+                0  2;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#21) { // SourceFile
+      #22;
+    } // end SourceFile
+  } // Attributes
+} // end class FinalAndVolatile
+
+
+// A field has both ACC_STRICT and ACC_STATIC set
+//
+class StrictAndStatic {
+  0xCAFEBABE;
+  65535; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Field #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "StrictAndStatic"; // #4
+    Utf8 "i"; // #5
+    Utf8 "I"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #12; // #9
+    Utf8 "java/lang/Object"; // #10
+    Utf8 "<init>"; // #11
+    Utf8 "()V"; // #12
+    Field #2 #14; // #13
+    NameAndType #15 #6; // #14
+    Utf8 "si"; // #15
+    Utf8 "ConstantValue"; // #16
+    int 0x00000000; // #17
+    Utf8 "Code"; // #18
+    Utf8 "LineNumberTable"; // #19
+    Utf8 "<clinit>"; // #20
+    Utf8 "SourceFile"; // #21
+    Utf8 "StrictAndStatic.java"; // #22
+  } // Constant Pool
+
+  0x0011; // access
+  #2;// this_cpx
+  #8;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0808; // access
+      #15; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+    ;
+    {  // field
+      0x0810; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#16) { // ConstantValue
+          #17;
+        } // end ConstantValue
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0001; // access
+      #11; // name_index
+      #12; // descriptor_index
+      [] { // Attributes
+        Attr(#18) { // Code
+          2; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2A03B500012AB700;
+            0x07B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#19) { // LineNumberTable
+              [] { // line_number_table
+                0  3;
+                5  1;
+                9  3;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0008; // access
+      #20; // name_index
+      #12; // descriptor_index
+      [] { // Attributes
+        Attr(#18) { // Code
+          1; // max_stack
+          0; // max_locals
+          Bytes[]{
+            0x03B3000DB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#19) { // LineNumberTable
+              [] { // line_number_table
+                0  2;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#21) { // SourceFile
+      #22;
+    } // end SourceFile
+  } // Attributes
+} // end class StrictAndStatic
+
+
+// A field has ACC_STRICT set without ACC_FINAL being set
+//
+class StrictNotFinal {
+  0xCAFEBABE;
+  65535; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Field #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "StrictNotFinal"; // #4
+    Utf8 "i"; // #5
+    Utf8 "I"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #12; // #9
+    Utf8 "java/lang/Object"; // #10
+    Utf8 "<init>"; // #11
+    Utf8 "()V"; // #12
+    Field #2 #14; // #13
+    NameAndType #15 #6; // #14
+    Utf8 "si"; // #15
+    Utf8 "ConstantValue"; // #16
+    int 0x00000000; // #17
+    Utf8 "Code"; // #18
+    Utf8 "LineNumberTable"; // #19
+    Utf8 "<clinit>"; // #20
+    Utf8 "SourceFile"; // #21
+    Utf8 "StrictNotFinal.java"; // #22
+  } // Constant Pool
+
+  0x0011; // access
+  #2;// this_cpx
+  #8;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0008; // access
+      #15; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+    ;
+    {  // field
+      0x0810; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#16) { // ConstantValue
+          #17;
+        } // end ConstantValue
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0001; // access
+      #11; // name_index
+      #12; // descriptor_index
+      [] { // Attributes
+        Attr(#18) { // Code
+          2; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2A03B500012AB700;
+            0x07B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#19) { // LineNumberTable
+              [] { // line_number_table
+                0  3;
+                5  1;
+                9  3;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0008; // access
+      #20; // name_index
+      #12; // descriptor_index
+      [] { // Attributes
+        Attr(#18) { // Code
+          1; // max_stack
+          0; // max_locals
+          Bytes[]{
+            0x03B3000DB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#19) { // LineNumberTable
+              [] { // line_number_table
+                0  2;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#21) { // SourceFile
+      #22;
+    } // end SourceFile
+  } // Attributes
+} // end class StrictNotFinal
+
+// A value class declaring a field without ACC_STATIC nor ACC_STRICT
+//
+class NotStaticNotStrict {
+  0xCAFEBABE;
+  65535; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Field #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "NotStaticNotStrict"; // #4
+    Utf8 "i"; // #5
+    Utf8 "I"; // #6
+    Method #8 #9; // #7
+    class #10; // #8
+    NameAndType #11 #12; // #9
+    Utf8 "java/lang/Object"; // #10
+    Utf8 "<init>"; // #11
+    Utf8 "()V"; // #12
+    Field #2 #14; // #13
+    NameAndType #15 #6; // #14
+    Utf8 "si"; // #15
+    Utf8 "ConstantValue"; // #16
+    int 0x00000000; // #17
+    Utf8 "Code"; // #18
+    Utf8 "LineNumberTable"; // #19
+    Utf8 "<clinit>"; // #20
+    Utf8 "SourceFile"; // #21
+    Utf8 "NotStaticNotStrict.java"; // #22
+  } // Constant Pool
+
+  0x0011; // access
+  #2;// this_cpx
+  #8;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0008; // access
+      #15; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+    ;
+    {  // field
+      0x0010; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#16) { // ConstantValue
+          #17;
+        } // end ConstantValue
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0001; // access
+      #11; // name_index
+      #12; // descriptor_index
+      [] { // Attributes
+        Attr(#18) { // Code
+          2; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2A03B500012AB700;
+            0x07B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#19) { // LineNumberTable
+              [] { // line_number_table
+                0  3;
+                5  1;
+                9  3;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0008; // access
+      #20; // name_index
+      #12; // descriptor_index
+      [] { // Attributes
+        Attr(#18) { // Code
+          1; // max_stack
+          0; // max_locals
+          Bytes[]{
+            0x03B3000DB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#19) { // LineNumberTable
+              [] { // line_number_table
+                0  2;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#21) { // SourceFile
+      #22;
+    } // end SourceFile
+  } // Attributes
+} // end class NotStaticNotStrict
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/FieldAlignmentTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/FieldAlignmentTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/FieldAlignmentTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2024, 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=Oops32
+ * @requires vm.bits == 32
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @compile FieldLayoutAnalyzer.java FieldAlignmentTest.java
+ * @run main/othervm FieldAlignmentTest 0
+ */
+
+  /*
+ * @test id=CompressedOops
+ * @requires vm.bits == 64
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @compile FieldLayoutAnalyzer.java FieldAlignmentTest.java
+ * @run main/othervm FieldAlignmentTest 1
+ */
+
+  /*
+ * @test id=NoCompressedOops
+ * @requires vm.bits == 64
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @compile FieldLayoutAnalyzer.java FieldAlignmentTest.java
+ * @run main/othervm FieldAlignmentTest 2
+ */
+
+ import java.util.ArrayList;
+ import java.util.Collections;
+ import java.util.List;
+
+ import jdk.internal.vm.annotation.ImplicitlyConstructible;
+
+ import jdk.test.lib.Asserts;
+ import jdk.test.lib.ByteCodeLoader;
+ import jdk.test.lib.helpers.ClassFileInstaller;
+ import jdk.test.lib.compiler.InMemoryJavaCompiler;
+ import jdk.test.lib.process.OutputAnalyzer;
+ import jdk.test.lib.process.ProcessTools;
+
+ public class FieldAlignmentTest {
+  public static class ZeroByte { }
+  public static class OneByte { byte b; }
+  public static class TwoByte { byte b0; byte b1; }
+  public static class ThreeByte { byte b0; byte b1; byte b2; }
+  public static class FourByte { byte b0; byte b1; byte b2; byte b3; }
+  public static class FiveByte { byte b0; byte b1; byte b2; byte b3; byte b4; }
+  public static class SixByte { byte b0; byte b1; byte b2; byte b3; byte b4; byte b5; }
+  public static class SevenByte { byte b0; byte b1; byte b2; byte b3; byte b4; byte b5; byte b6; }
+  public static final String[] superNames = { ZeroByte.class.getCanonicalName(),
+                                              OneByte.class.getCanonicalName(),
+                                              TwoByte.class.getCanonicalName(),
+                                              ThreeByte.class.getCanonicalName(),
+                                              FourByte.class.getCanonicalName(),
+                                              FiveByte.class.getCanonicalName(),
+                                              SixByte.class.getCanonicalName(),
+                                              SevenByte.class.getCanonicalName() };
+  public static final String[] valueNames = { ValueOneByte.class.getCanonicalName(),
+                                              ValueOneChar.class.getCanonicalName(),
+                                              ValueOneShort.class.getCanonicalName(),
+                                              ValueOneInt.class.getCanonicalName(),
+                                              ValueOneLong.class.getCanonicalName(),
+                                              ValueOneFloat.class.getCanonicalName(),
+                                              ValueOneDouble.class.getCanonicalName(),
+                                              ValueByteLong.class.getCanonicalName(),
+                                              ValueByteInt.class.getCanonicalName() };
+
+  List<String> testNames = new ArrayList<String>();
+
+  @ImplicitlyConstructible static value class ValueOneByte { byte val = 0; }
+  @ImplicitlyConstructible static value class ValueOneChar { char val = 0; }
+  @ImplicitlyConstructible static value class ValueOneShort { short val = 0; }
+  @ImplicitlyConstructible static value class ValueOneInt { int val = 0; }
+  @ImplicitlyConstructible static value class ValueOneLong { long val = 0; }
+  @ImplicitlyConstructible static value class ValueOneFloat { float val = 0f; }
+  @ImplicitlyConstructible static value class ValueOneDouble { double val = 0d; }
+
+  @ImplicitlyConstructible static value class ValueByteLong { byte b = 0; long l = 0; }
+  @ImplicitlyConstructible static value class ValueByteInt { byte b = 0; int i = 0; }
+
+  void generateTests() throws Exception {
+    for (String vName : valueNames) {
+      for (String sName : superNames) {
+        String vNameShort = vName.substring(vName.lastIndexOf('.') + 1);
+        String sNameShort = sName.substring(sName.lastIndexOf('.') + 1);
+        String className = "Test" + vNameShort + "With" + sNameShort;
+        String sourceCode = "import jdk.internal.vm.annotation.NullRestricted; " +
+                            "public class " + className + " extends " + sName + " { " +
+                            "    @NullRestricted" +
+                            "    " + vName + " v1;" +
+                            "}";
+        String java_version = System.getProperty("java.specification.version");
+        byte[] byteCode = InMemoryJavaCompiler.compile(className, sourceCode,
+                                                      "-source", java_version, "--enable-preview",
+                                                      "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED");
+        jdk.test.lib.helpers.ClassFileInstaller.writeClassToDisk(className, byteCode);
+        testNames.add(className);
+      }
+    }
+  }
+
+  void generateTestRunner() throws Exception {
+    String className = "TestRunner";
+    StringBuilder sb = new StringBuilder();
+    sb.append("public class ").append(className).append(" {");
+    sb.append("    public void main(String[] args) {");
+    for (String name : testNames) {
+      sb.append("        ").append(name).append(" var").append(name).append(" = new ").append(name).append("();");
+    }
+    sb.append("    }");
+    sb.append("}");
+    String java_version = System.getProperty("java.specification.version");
+    byte[] byteCode = InMemoryJavaCompiler.compile(className, sb.toString(),
+                                                   "-source", java_version, "--enable-preview",
+                                                   "-cp", ".");
+    jdk.test.lib.helpers.ClassFileInstaller.writeClassToDisk(className, byteCode);
+  }
+
+  static ProcessBuilder exec(String compressedOopsArg, String... args) throws Exception {
+    List<String> argsList = new ArrayList<>();
+    Collections.addAll(argsList, "--enable-preview");
+    Collections.addAll(argsList, "-XX:+UnlockDiagnosticVMOptions");
+    Collections.addAll(argsList, "-XX:+PrintFieldLayout");
+    if (compressedOopsArg != null) {
+      Collections.addAll(argsList, compressedOopsArg);
+    }
+    Collections.addAll(argsList, "-Xmx256m");
+    Collections.addAll(argsList, "-cp", System.getProperty("java.class.path") + ":.");
+    Collections.addAll(argsList, args);
+    return ProcessTools.createTestJavaProcessBuilder(argsList);
+  }
+
+  public static void main(String[] args) throws Exception {
+    boolean useCompressedOops;
+    String compressedOopsArg;
+
+    switch(args[0]) {
+      case "0": useCompressedOops = false;
+                compressedOopsArg = null;
+                break;
+      case "1": useCompressedOops = true;
+                compressedOopsArg = "-XX:+UseCompressedOops";
+                break;
+      case "2": useCompressedOops = false;
+                compressedOopsArg = "-XX:-UseCompressedOops";
+                break;
+      default: throw new RuntimeException("Unrecognized configuration");
+    }
+
+    // Generate test classes
+    FieldAlignmentTest fat = new FieldAlignmentTest();
+    fat.generateTests();
+    fat.generateTestRunner();
+
+    // Execute the test runner in charge of loading all test classes
+    ProcessBuilder pb = exec(compressedOopsArg, "TestRunner");
+    OutputAnalyzer out = new OutputAnalyzer(pb.start());
+
+    // Analyze the test runner output
+    System.out.print(out.getOutput());
+    FieldLayoutAnalyzer.LogOutput lo = new FieldLayoutAnalyzer.LogOutput(out.asLines());
+
+    FieldLayoutAnalyzer fla =  FieldLayoutAnalyzer.createFieldLayoutAnalyzer(lo, useCompressedOops);
+    fla.check();
+  }
+ }
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/FieldLayoutAnalyzer.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/FieldLayoutAnalyzer.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/FieldLayoutAnalyzer.java
@@ -0,0 +1,554 @@
+/*
+ * Copyright (c) 2024, 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.PrintStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class FieldLayoutAnalyzer {
+
+  // Mutable wrapper around log output to manage the cursor while parsing
+  static class LogOutput {
+    List<String> lines;
+    int cursor;
+
+    public LogOutput(List<String> lines) {
+      this.lines = lines;
+      cursor = 0;
+    }
+
+    String getCurrentLine() { return lines.get(cursor); }
+    String get(int idx) { return lines.get(idx); }
+    int size() { return lines.size(); }
+    void moveToNextLine() { cursor = cursor + 1; }
+    boolean hasMoreLines() { return cursor < lines.size(); }
+  }
+
+  static enum BlockType {
+    RESERVED,
+    INHERITED,
+    EMPTY,
+    REGULAR,
+    PADDING,
+    FLAT,
+    NULL_MARKER,
+    INHERITED_NULL_MARKER;
+
+    static BlockType parseType(String s) {
+      switch(s) {
+        case "RESERVED"    : return RESERVED;
+        case "INHERITED"   : return INHERITED;
+        case "EMPTY"       : return EMPTY;
+        case "REGULAR"     : return REGULAR;
+        case "PADDING"     : return PADDING;
+        case "FLAT"        : return FLAT;
+        case "NULL_MARKER" : return NULL_MARKER;
+        case "INHERITED_NULL_MARKER" : return INHERITED_NULL_MARKER;
+        default:
+          throw new RuntimeException("Unknown block type: " + s);
+      }
+    }
+  }
+
+  static public record FieldBlock (int offset,
+                            BlockType type,
+                            int size,
+                            int alignment,
+                            String name,
+                            String signature,
+                            String fieldClass,
+                            int nullMarkerOffset,
+                            boolean hasInternalNullMarker,
+                            int referenceFieldOffset) { // for null marker blocks, gives the offset of the field they refer to
+
+
+    static FieldBlock createSpecialBlock(int offset, BlockType type, int size, int alignment, int referenceFieldOffset) {
+      return new FieldBlock(offset, type, size, alignment, null, null, null, -1, false, referenceFieldOffset);
+    }
+
+    static FieldBlock createJavaFieldBlock(int offset, BlockType type, int size, int alignment, String name, String signature, String fieldClass, int nullMarkerOffset, boolean hasInternalNullMarker) {
+      return new FieldBlock(offset, type, size, alignment, name, signature, fieldClass, nullMarkerOffset, hasInternalNullMarker, -1);
+    }
+
+    void print(PrintStream out) {
+      out.println("Offset=" + offset+
+                " type=" + type +
+                " size=" + size +
+                " alignment=" + alignment +
+                " name=" + name +
+                " signature=" + signature +
+                " fieldClass=" + fieldClass);
+    }
+
+    boolean isFlat() { return type == BlockType.FLAT; } // Warning: always return false for inherited fields, even flat ones
+    boolean hasNullMarker() {return nullMarkerOffset != -1; }
+
+    static FieldBlock parseField(String line) {
+      String[] fieldLine = line.split("\\s+");
+      // for(String  s : fieldLine) {
+      //   System.out.print("["+s+"]");  // debugging statement to be removed
+      // }
+      // System.out.println();
+      int offset = Integer.parseInt(fieldLine[1].substring(1, fieldLine[1].length()));
+      BlockType type = BlockType.parseType(fieldLine[2]);
+      String[] size_align = fieldLine[3].split("/");
+      int size = Integer.parseInt(size_align[0]);
+      int alignment = -1;
+      if (type != BlockType.RESERVED) {
+        alignment = Integer.parseInt(size_align[1]);
+      } else {
+        Asserts.assertTrue(size_align[1].equals("-"));
+      }
+      FieldBlock block = null;
+      switch(type) {
+        case BlockType.RESERVED:
+        case BlockType.EMPTY:
+        case BlockType.PADDING:
+        case BlockType.INHERITED_NULL_MARKER: {
+            block = FieldBlock.createSpecialBlock(offset, type, size, alignment, 0);
+            break;
+        }
+        case BlockType.REGULAR:
+        case BlockType.INHERITED:
+        case BlockType.FLAT: {
+            String name = fieldLine[4];
+            String signature = fieldLine[5];
+            String fieldClass = "";
+            int nullMarkerOffset = -1;
+            boolean hasInternalNullMarker = false;
+            if (type == BlockType.FLAT) {
+              fieldClass = fieldLine[6];
+              if (fieldLine.length >= 11) {
+                nullMarkerOffset = Integer.parseInt(fieldLine[10]);
+              }
+              if (fieldLine.length >= 12 ) {
+                Asserts.assertEquals(fieldLine[11], "(internal)");
+                hasInternalNullMarker = true;
+              }
+            }
+            block = FieldBlock.createJavaFieldBlock(offset, type, size, alignment, name, signature, fieldClass, nullMarkerOffset, hasInternalNullMarker);
+            break;
+          }
+        case BlockType.NULL_MARKER: {
+          int referenceFieldOffset = Integer.parseInt(fieldLine[10]);
+          block = FieldBlock.createSpecialBlock(offset, type, size, alignment, referenceFieldOffset);
+          break;
+        }
+      }
+      Asserts.assertNotNull(block);
+      return block;
+    }
+  }
+
+  static class ClassLayout {
+    String name;
+    String superName;
+    boolean isValue;
+    int size;
+    int firstFieldOffset;
+    int alignment;
+    int exactSize;
+    String[] lines;
+    ArrayList<FieldBlock> staticFields;
+    ArrayList<FieldBlock> nonStaticFields;
+    int internalNullMarkerOffset; // -1 if no internal null marker
+
+    private ClassLayout() {
+      staticFields = new ArrayList<FieldBlock>();
+      nonStaticFields = new ArrayList<FieldBlock>();
+    }
+
+    void processField(String line, boolean isStatic) {
+      FieldBlock block = FieldBlock.parseField(line);
+      if (isStatic) {
+        Asserts.assertTrue(block.type != BlockType.INHERITED); // static fields cannotbe inherited
+        staticFields.add(block);
+      } else {
+        nonStaticFields.add(block);
+      }
+    }
+
+    static ClassLayout parseClassLayout(LogOutput lo) {
+      ClassLayout cl = new ClassLayout();
+      // Parsing class name
+      Asserts.assertTrue(lo.getCurrentLine().startsWith("Layout of class"), lo.getCurrentLine());
+      String[] first = lo.getCurrentLine().split("\\s+");
+      cl.name = first[3];
+      if (first.length == 6) {
+        Asserts.assertEquals(first[4], "extends");
+        cl.superName = first[5];
+      } else {
+        cl.superName = null;
+      }
+      // System.out.println("Class name: " + cl.name);
+      lo.moveToNextLine();
+      Asserts.assertTrue(lo.getCurrentLine().startsWith("Instance fields:"), lo.getCurrentLine());
+      lo.moveToNextLine();
+      // Parsing instance fields
+      while (lo.getCurrentLine().startsWith(" @")) {
+        cl.processField(lo.getCurrentLine(), false);
+        lo.moveToNextLine();
+      }
+      Asserts.assertTrue(lo.getCurrentLine().startsWith("Static fields:"), lo.getCurrentLine());
+      lo.moveToNextLine();
+      // Parsing static fields
+      while (lo.getCurrentLine().startsWith(" @")) {
+        cl.processField(lo.getCurrentLine(), true);
+        lo.moveToNextLine();
+      }
+      Asserts.assertTrue(lo.getCurrentLine().startsWith("Instance size ="), lo.getCurrentLine());
+      String[] sizeLine = lo.getCurrentLine().split("\\s+");
+      cl.size = Integer.parseInt(sizeLine[3]);
+      lo.moveToNextLine();
+      if (lo.getCurrentLine().startsWith("First field offset =")) {
+        // The class is a value class, more lines to parse
+        cl.isValue = true;
+        String[] firstOffsetLine = lo.getCurrentLine().split("\\s+");
+        cl.firstFieldOffset = Integer.parseInt(firstOffsetLine[4]);
+        lo.moveToNextLine();
+        String[] alignmentLine = lo.getCurrentLine().split("\\s+");
+        cl.alignment = Integer.parseInt(alignmentLine[2]);
+        lo.moveToNextLine();
+        String[] exactSizeLine = lo.getCurrentLine().split("\\s+");
+        cl.exactSize = Integer.parseInt(exactSizeLine[3]);
+        lo.moveToNextLine();
+        if (lo.getCurrentLine().startsWith("Null marker offset")) {
+          String[] nullMarkerLine = lo.getCurrentLine().split("\\s+");
+          cl.internalNullMarkerOffset = Integer.parseInt(nullMarkerLine[4]);
+          lo.moveToNextLine();
+        } else {
+          cl.internalNullMarkerOffset = -1;
+        }
+      } else {
+        cl.isValue = false;
+      }
+
+      Asserts.assertTrue(lo.getCurrentLine().startsWith("---"), lo.getCurrentLine());
+      lo.moveToNextLine();
+      return cl;
+    }
+
+    FieldBlock getFieldAtOffset(int offset, boolean isStatic) {
+      ArrayList<FieldBlock> fields = isStatic ? staticFields : nonStaticFields;
+      for (FieldBlock block : fields) {
+        if (block.offset == offset) return block;
+      }
+      throw new RuntimeException("No " + (isStatic ? "static" : "nonstatic") + " field found at offset "+ offset);
+    }
+
+    FieldBlock getFieldFromName(String fieldName, boolean isStatic) {
+      Asserts.assertTrue(fieldName != null);
+      ArrayList<FieldBlock> fields = isStatic ? staticFields : nonStaticFields;
+      for (FieldBlock block : fields) {
+        if (block.name() == null) continue;
+        String n = block.name().substring(1, block.name().length() - 1); // in the log, name is surrounded by double quotes
+        if (fieldName.equals(n)) return block;
+      }
+      throw new RuntimeException("No " + (isStatic ? "static" : "nonstatic") + " field found with name "+ fieldName);
+    }
+
+  }
+
+  ArrayList<ClassLayout> layouts;
+  boolean compressOops;
+
+  static String signatureToName(String sig) {
+    Asserts.assertTrue((sig.charAt(0) == 'L'));
+    Asserts.assertTrue((sig.charAt(sig.length() - 1) == ';'));
+    return sig.substring(1, sig.length() - 1);
+  }
+
+  private FieldLayoutAnalyzer(boolean coop) {
+    compressOops = coop;
+    layouts = new ArrayList<ClassLayout>();
+  }
+
+  public static FieldLayoutAnalyzer createFieldLayoutAnalyzer(LogOutput lo, boolean compressOops) {
+    FieldLayoutAnalyzer fla = new FieldLayoutAnalyzer(compressOops);
+    fla.generate(lo);
+    return fla;
+  }
+
+  ClassLayout getClassLayout(String name) {
+    for(ClassLayout layout : layouts) {
+      if (layout.name.equals(name)) return layout;
+    }
+    return null;
+  }
+
+  ClassLayout getClassLayoutFromName(String name) {
+    for(ClassLayout layout : layouts) {
+      String sub = layout.name.substring(0, layout.name.indexOf('@'));
+      if (name.equals(sub)) return layout;
+    }
+    return null;
+  }
+
+  void checkNoOverlapOnFields(ArrayList<FieldBlock> fields) {
+    for (int i = 0; i < fields.size() - 1; i++) {
+      FieldBlock f0 = fields.get(i);
+      FieldBlock f1 = fields.get(i + 1);
+      if (f0.offset + f0.size < f1.offset) {
+        throw new RuntimeException("Hole issue found at offset " + f1.offset);
+      } else if (f0.offset + f0.size > f1.offset) {
+        throw new RuntimeException("Overlap issue found at offset " + f1.offset);
+      }
+    }
+  }
+
+  void checkNoOverlap() {
+    System.out.println("Checking for overlap");
+    for (ClassLayout layout : layouts) {
+      System.out.println("\t" + layout.name);
+      try {
+        checkNoOverlapOnFields(layout.staticFields);
+        checkNoOverlapOnFields(layout.nonStaticFields);
+      } catch(Throwable t) {
+        throw new RuntimeException("Unexpection exception when checking for overlaps/holes in class " + layout.name);
+      }
+    }
+  }
+
+  void checkSizeAndAlignmentForField(FieldBlock block) {
+    if (block.type == BlockType.RESERVED) {
+      Asserts.assertTrue(block.alignment == -1);
+      return;
+    }
+    if (block.type == BlockType.EMPTY || block.type == BlockType.PADDING
+        || block.type == BlockType.NULL_MARKER || block.type == BlockType.INHERITED_NULL_MARKER) {
+      Asserts.assertTrue(block.alignment == 1, "alignment = " + block.alignment);
+      return;
+    }
+
+    switch(block.signature()) {
+      case "Z" :
+      case "B" : Asserts.assertTrue(block.size() == 1);
+                 Asserts.assertTrue(block.alignment() == 1);
+        break;
+      case "S" :
+      case "C" : Asserts.assertTrue(block.size() == 2);
+                 Asserts.assertTrue(block.alignment() == 2);
+        break;
+      case "F" :
+      case "I" : Asserts.assertTrue(block.size() == 4);
+                 Asserts.assertTrue(block.alignment() == 4);
+        break;
+      case "J" :
+      case "D" : Asserts.assertTrue(block.size() == 8);
+                 Asserts.assertTrue(block.alignment() == 8);
+        break;
+      default: {
+        if (block.signature().startsWith("[")) {
+          Asserts.assertEquals(compressOops ? 4 : 8, block.size());
+        } else if (block.signature().startsWith("L")) {
+          if (block.type == BlockType.INHERITED || block.type == BlockType.INHERITED_NULL_MARKER) {
+            // Skip for now, will be verified when checking the class declaring the field
+          } else if (block.type == BlockType.REGULAR) {
+            Asserts.assertEquals(compressOops ? 4 : 8, block.size());
+          } else {
+            Asserts.assertEquals(BlockType.FLAT, block.type);
+            ClassLayout fcl = getClassLayout(block.fieldClass);
+            Asserts.assertNotNull(fcl);
+            Asserts.assertEquals(block.size(), fcl.exactSize);
+            Asserts.assertEquals(block.alignment(), fcl.alignment);
+          }
+        } else {
+          throw new RuntimeException("Unknown signature type: " + block.signature);
+        }
+      }
+      Asserts.assertTrue(block.offset % block.alignment == 0);
+    }
+  }
+
+  void checkSizeAndAlignment() {
+    System.out.println("Checking size and alignment");
+    for (ClassLayout layout : layouts) {
+      System.out.println("\t" + layout.name);
+      for (FieldBlock block : layout.staticFields) {
+        checkSizeAndAlignmentForField(block);
+      }
+      for (FieldBlock block : layout.nonStaticFields) {
+        checkSizeAndAlignmentForField(block);
+      }
+    }
+  }
+
+  // Verify that fields marked as INHERITED are declared in a super class
+  void checkInheritedFields() {
+    System.out.println("Checking Inherited Fields");
+    for (ClassLayout layout : layouts) {
+      System.out.println("\t" + layout.name);
+      // Preparing the list of ClassLayout of super classes
+      ArrayList<ClassLayout> supers = new ArrayList<ClassLayout>();
+      String className = layout.superName;
+      while (className != null) {
+        ClassLayout cl = getClassLayout(className);
+        supers.add(cl);
+        className = cl.superName;
+      }
+      for (FieldBlock field : layout.nonStaticFields) {
+        if (field.type == BlockType.INHERITED) {
+          int i = 0;
+          boolean found = false;
+          FieldBlock b = null;
+          while(i < supers.size() && !found) {
+            b = supers.get(i).getFieldAtOffset(field.offset, false);
+            if (b.type != BlockType.INHERITED) found = true;
+            i++;
+          }
+          String location = new String(" at " + layout.name + " offset " + field.offset());
+          Asserts.assertTrue(found, "No declaration found for an inherited field " + location);
+          Asserts.assertNotEquals(field.type, BlockType.EMPTY, location);
+          Asserts.assertEquals(field.size, b.size, location);
+          Asserts.assertEquals(field.alignment, b.alignment, location );
+          Asserts.assertEquals(field.name(), b.name(), location);
+          Asserts.assertEquals(field.signature(), b.signature(), location);
+        }
+      }
+    }
+  }
+
+  static class Node {
+    ClassLayout classLayout;
+    Node superClass;
+    ArrayList<Node> subClasses = new ArrayList<Node>();
+  }
+
+  // Verify that all fields declared in a class are present in all subclass
+  void checkSubClasses() {
+    System.out.println("Checking sub-classes");
+    // Generating the class inheritance graph
+    HashMap<String, Node> nodes = new HashMap<String, Node>();
+    for (ClassLayout layout : layouts) {
+      if (layout.name.contains("$$Lambda@0")) continue; // Skipping lambda classes
+      Node current = nodes.get(layout.name);
+      if (current == null) {
+        current = new Node();
+        nodes.put(layout.name, current);
+      }
+      if (current.classLayout == null) {
+        current.classLayout = layout;
+      } else {
+        System.out.println(current.classLayout.name + " vs " + layout.name);
+        Asserts.assertEQ(current.classLayout, layout);
+      }
+      if (layout.superName != null) {
+        Node superNode = nodes.get(layout.superName);
+        if (superNode == null) {
+          superNode = new Node();
+          superNode.subClasses.add(current);
+          nodes.put(layout.superName, superNode);
+        }
+        superNode.subClasses.add(current);
+      }
+    }
+    // Field verification
+    for (Node node : nodes.values()) {
+      ClassLayout layout = node.classLayout;
+      System.out.println("\t" + layout.name);
+      for (FieldBlock block : layout.nonStaticFields) {
+        if (block.offset() == 0) continue; // Skip object header
+        if (block.type() == BlockType.EMPTY) continue; // Empty spaces can be used by subclasses
+        // A special case for PADDING might be needed too => must NOT be used in subclasses
+        for (Node subnode : node.subClasses) {
+          checkFieldInClass(block, subnode);
+        }
+      }
+    }
+  }
+
+  void checkFieldInClass(FieldBlock block, Node node) {
+    FieldBlock b = node.classLayout.getFieldAtOffset(block.offset, false);
+    Asserts.assertTrue((block.type != BlockType.NULL_MARKER && block.type != BlockType.INHERITED_NULL_MARKER && b.type == BlockType.INHERITED)
+                       || ((block.type == BlockType.NULL_MARKER || block.type == BlockType.INHERITED_NULL_MARKER) && b.type == BlockType.INHERITED_NULL_MARKER));
+    Asserts.assertEquals(b.signature(), block.signature());
+    Asserts.assertEquals(b.name(), block.name());
+    Asserts.assertEquals(b.size(), block.size());
+    Asserts.assertTrue(b.alignment() == block.alignment());
+    for (Node subnode : node.subClasses) {
+      checkFieldInClass(block, subnode);
+    }
+  }
+
+  void checkNullMarkers() {
+    System.out.println("Checking null markers");
+    for (ClassLayout layout : layouts) {
+      System.out.println("\t" + layout.name);
+      BlockType last_type = BlockType.RESERVED;
+      boolean has_empty_slot = false;
+      for (FieldBlock block : layout.nonStaticFields) {
+        last_type = block.type;
+        if (block.type() == BlockType.FLAT && block.nullMarkerOffset() != -1) {
+          if (block.hasInternalNullMarker()) {
+            Asserts.assertTrue(block.nullMarkerOffset() > block.offset());
+            Asserts.assertTrue(block.nullMarkerOffset() < block.offset() + block.size());
+          } else {
+            FieldBlock marker = layout.getFieldAtOffset(block.nullMarkerOffset(), false);
+            Asserts.assertEquals(block.nullMarkerOffset(), marker.offset());
+          }
+        }
+        if (block.type() == BlockType.NULL_MARKER) {
+          FieldBlock flatField = layout.getFieldAtOffset(block.referenceFieldOffset(), false);
+          Asserts.assertEquals(flatField.type(), BlockType.FLAT);
+          Asserts.assertEquals(flatField.nullMarkerOffset(), block.offset());
+        }
+        if (block.type() == BlockType.EMPTY) has_empty_slot = true;
+      }
+      // null marker should not be added at the end of the layout if there's an empty slot
+      Asserts.assertTrue(last_type != BlockType.NULL_MARKER || has_empty_slot == false,
+                         "Problem detected in layout of class " + layout.name);
+      // static layout => must not have NULL_MARKERS because static fields are never flat
+      for (FieldBlock block : layout.staticFields) {
+        Asserts.assertNotEquals(block.type(), BlockType.NULL_MARKER);
+        if (block.type() == BlockType.FLAT) {
+          Asserts.assertEquals(block.nullMarkerOffset(), -1); // -1 means no null marker
+        }
+      }
+    }
+  }
+
+  void check() {
+    checkNoOverlap();
+    checkSizeAndAlignment();
+    checkInheritedFields();
+    checkSubClasses();
+    checkNullMarkers();
+  }
+
+  private void generate(LogOutput lo) {
+    while (lo.hasMoreLines()) {
+      if (lo.getCurrentLine().startsWith("Layout of class")) {
+        ClassLayout cl = ClassLayout.parseClassLayout(lo);
+        layouts.add(cl);
+      } else {
+        lo.moveToNextLine(); // skipping line
+      }
+    }
+  }
+ }
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/NullMarkersTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/NullMarkersTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/NullMarkersTest.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 2024, 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=NullMarker32
+ * @requires vm.bits == 32
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @compile FieldLayoutAnalyzer.java NullMarkersTest.java
+ * @run main/othervm NullMarkersTest 0
+ */
+
+/*
+ * @test id=NullMarker64CompressedOops
+ * @requires vm.bits == 64
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @compile FieldLayoutAnalyzer.java NullMarkersTest.java
+ * @run main/othervm NullMarkersTest 1
+ */
+
+/*
+ * @test id=NullMarker64NoCompressedOops
+ * @requires vm.bits == 64
+ * @library /test/lib
+ * @modules java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @compile FieldLayoutAnalyzer.java NullMarkersTest.java
+ * @run main/othervm NullMarkersTest 2
+ */
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.LooselyConsistentValue;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import jdk.test.lib.Asserts;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class NullMarkersTest {
+
+
+  static class TestRunner {
+    public static void main(String[] args) throws Exception {
+      Class testClass = Class.forName("NullMarkersTest");
+      Asserts.assertNotNull(testClass);
+      Method[] testMethods = testClass.getMethods();
+      for (Method test : testMethods) {
+        if (test.getName().startsWith("test_")) {
+          Asserts.assertTrue(Modifier.isStatic(test.getModifiers()));
+          Asserts.assertTrue(test.getReturnType().equals(Void.TYPE));
+          System.out.println("Running " + test.getName());
+          test.invoke(null);
+        }
+      }
+    }
+  }
+
+  static value class Value0 {
+    int i = 0;
+  }
+
+  static class Container0 {
+    Value0 val;
+  }
+
+  // Simple test with a single nullable flattenable field
+  static public void test_0() {
+    Container0 c = new Container0();
+  }
+
+  static public void check_0(FieldLayoutAnalyzer fla) {
+    FieldLayoutAnalyzer.ClassLayout cl = fla.getClassLayoutFromName("NullMarkersTest$Container0");
+    FieldLayoutAnalyzer.FieldBlock f = cl.getFieldFromName("val", false);
+    Asserts.assertTrue(f.isFlat());
+    Asserts.assertTrue(f.hasNullMarker());
+  }
+
+  @ImplicitlyConstructible
+  static value class Value1 {
+    short s = 0;
+  }
+
+  static class Container1 {
+    Value1 val0;
+    Value1 val1;
+    @NullRestricted
+    Value1 val2 = new Value1();
+  }
+
+  static public void test_1() {
+    Container1 c = new Container1();
+  }
+
+  static public void check_1(FieldLayoutAnalyzer fla) {
+    FieldLayoutAnalyzer.ClassLayout cl = fla.getClassLayoutFromName("NullMarkersTest$Container1");
+    FieldLayoutAnalyzer.FieldBlock f0 = cl.getFieldFromName("val0", false);
+    Asserts.assertTrue(f0.isFlat());
+    Asserts.assertTrue(f0.hasNullMarker());
+    FieldLayoutAnalyzer.FieldBlock f1 = cl.getFieldFromName("val1", false);
+    Asserts.assertTrue(f1.isFlat());
+    Asserts.assertTrue(f1.hasNullMarker());
+    FieldLayoutAnalyzer.FieldBlock f2 = cl.getFieldFromName("val2", false);
+    Asserts.assertTrue(f2.isFlat());
+    Asserts.assertFalse(f2.hasNullMarker());
+  }
+
+  static value class Value2 {
+    long l = 0;
+  }
+
+  static class Container2a {
+    Value2 vala;
+  }
+
+  static class Container2b extends Container2a {
+    Value2 valb;
+  }
+
+  static public void test_2() {
+    Container2b c = new Container2b();
+  }
+
+  static public void check_2(FieldLayoutAnalyzer fla) {
+    FieldLayoutAnalyzer.ClassLayout cla = fla.getClassLayoutFromName("NullMarkersTest$Container2a");
+    FieldLayoutAnalyzer.FieldBlock fa = cla.getFieldFromName("vala", false);
+    Asserts.assertTrue(fa.isFlat());
+    Asserts.assertTrue(fa.hasNullMarker());
+    FieldLayoutAnalyzer.ClassLayout clb = fla.getClassLayoutFromName("NullMarkersTest$Container2b");
+    FieldLayoutAnalyzer.FieldBlock fb = clb.getFieldFromName("valb", false);
+    Asserts.assertTrue(fb.isFlat());
+    Asserts.assertTrue(fb.hasNullMarker());
+  }
+
+  @ImplicitlyConstructible
+  static value class Value3 {
+    double d = 0.0d;
+  }
+
+  static class Container3a {
+    @NullRestricted
+    Value3 val0;
+    Value3 val1;
+  }
+
+  static class Container3b extends Container3a {
+    Value3 val2;
+    @NullRestricted
+    Value3 val3;
+  }
+
+  static class Container3c extends Container3b {
+    Value3 val4;
+    Value3 val5;
+  }
+
+  static public void test_3() {
+    Container3c c = new Container3c();
+  }
+
+  static public void check_3(FieldLayoutAnalyzer fla) {
+    FieldLayoutAnalyzer.ClassLayout cla = fla.getClassLayoutFromName("NullMarkersTest$Container3a");
+    FieldLayoutAnalyzer.FieldBlock f0 = cla.getFieldFromName("val0", false);
+    Asserts.assertTrue(f0.isFlat());
+    Asserts.assertFalse(f0.hasNullMarker());
+    FieldLayoutAnalyzer.FieldBlock f1 = cla.getFieldFromName("val1", false);
+    Asserts.assertTrue(f1.isFlat());
+    Asserts.assertTrue(f1.hasNullMarker());
+    FieldLayoutAnalyzer.ClassLayout clb = fla.getClassLayoutFromName("NullMarkersTest$Container3b");
+    FieldLayoutAnalyzer.FieldBlock f2 = clb.getFieldFromName("val2", false);
+    Asserts.assertTrue(f2.isFlat());
+    Asserts.assertTrue(f2.hasNullMarker());
+    FieldLayoutAnalyzer.FieldBlock f3 = clb.getFieldFromName("val3", false);
+    Asserts.assertTrue(f3.isFlat());
+    Asserts.assertFalse(f3.hasNullMarker());
+    FieldLayoutAnalyzer.ClassLayout clc = fla.getClassLayoutFromName("NullMarkersTest$Container3c");
+    FieldLayoutAnalyzer.FieldBlock f4 = clc.getFieldFromName("val4", false);
+    Asserts.assertTrue(f4.isFlat());
+    Asserts.assertTrue(f4.hasNullMarker());
+    FieldLayoutAnalyzer.FieldBlock f5 = clc.getFieldFromName("val5", false);
+    Asserts.assertTrue(f5.isFlat());
+    Asserts.assertTrue(f5.hasNullMarker());
+  }
+
+  @ImplicitlyConstructible
+  @LooselyConsistentValue
+  static value class Value4 {
+    int i = 0;
+    byte b = 0;
+  }
+
+  static class Container4 {
+    Value4 val0;
+    Value4 val1;
+  }
+
+  static public void test_4() {
+    Container4 c = new Container4();
+  }
+
+  static void check_4(FieldLayoutAnalyzer fla) {
+    FieldLayoutAnalyzer.ClassLayout cl = fla.getClassLayoutFromName("NullMarkersTest$Container4");
+    FieldLayoutAnalyzer.FieldBlock f0 = cl.getFieldFromName("val0", false);
+    Asserts.assertTrue(f0.isFlat());
+    Asserts.assertTrue(f0.hasNullMarker());
+    FieldLayoutAnalyzer.FieldBlock f1 = cl.getFieldFromName("val1", false);
+    Asserts.assertTrue(f1.isFlat());
+    Asserts.assertTrue(f1.hasNullMarker());
+  }
+
+  @ImplicitlyConstructible
+  @LooselyConsistentValue
+  static value class Value5a {
+    short s = 0;
+    byte b = 0;
+  }
+
+  @ImplicitlyConstructible
+  @LooselyConsistentValue
+  static value class Value5b {
+    @NullRestricted
+    Value5a val0 = new Value5a();
+    @NullRestricted
+    Value5a val1 = new Value5a();
+  }
+
+  static class Container5 {
+    Value5a vala;
+    Value5b valb0;
+    Value5b valb1;
+  }
+
+  static public void test_5() {
+    Container5 c = new Container5();
+  }
+
+  static void check_5(FieldLayoutAnalyzer fla) {
+    FieldLayoutAnalyzer.ClassLayout cl = fla.getClassLayoutFromName("NullMarkersTest$Container5");
+    FieldLayoutAnalyzer.FieldBlock fa = cl.getFieldFromName("vala", false);
+    Asserts.assertTrue(fa.isFlat());
+    Asserts.assertTrue(fa.hasNullMarker());
+    FieldLayoutAnalyzer.FieldBlock fb0 = cl.getFieldFromName("valb0", false);
+    Asserts.assertTrue(fb0.isFlat());
+    Asserts.assertTrue(fb0.hasNullMarker());
+    FieldLayoutAnalyzer.FieldBlock fb1 = cl.getFieldFromName("valb1", false);
+    Asserts.assertTrue(fb1.isFlat());
+    Asserts.assertTrue(fb1.hasNullMarker());
+  }
+
+  static ProcessBuilder exec(String compressedOopsArg, String... args) throws Exception {
+    List<String> argsList = new ArrayList<>();
+    Collections.addAll(argsList, "--enable-preview");
+    Collections.addAll(argsList, "-Xint");
+    Collections.addAll(argsList, "-XX:+UnlockDiagnosticVMOptions");
+    Collections.addAll(argsList, "-XX:+PrintFieldLayout");
+    if (compressedOopsArg != null) {
+      Collections.addAll(argsList, compressedOopsArg);
+    }
+    Collections.addAll(argsList, "-Xmx256m");
+    Collections.addAll(argsList, "-XX:+EnableNullableFieldFlattening");
+    Collections.addAll(argsList, "-cp", System.getProperty("java.class.path") + ":.");
+    Collections.addAll(argsList, args);
+    return ProcessTools.createTestJavaProcessBuilder(argsList);
+  }
+
+  public static void main(String[] args) throws Exception {
+    boolean useCompressedOops;
+    String compressedOopsArg;
+
+    switch(args[0]) {
+      case "0": useCompressedOops = false;
+                compressedOopsArg = null;
+                break;
+      case "1": useCompressedOops = true;
+                compressedOopsArg = "-XX:+UseCompressedOops";
+                break;
+      case "2": useCompressedOops = false;
+                compressedOopsArg = "-XX:-UseCompressedOops";
+                break;
+      default: throw new RuntimeException("Unrecognized configuration");
+    }
+
+    // Generate test classes
+    NullMarkersTest fat = new NullMarkersTest();
+
+    // Execute the test runner in charge of loading all test classes
+    ProcessBuilder pb = exec(compressedOopsArg, "NullMarkersTest$TestRunner");
+    OutputAnalyzer out = new OutputAnalyzer(pb.start());
+
+    Asserts.assertEquals(out.getExitValue(), 0, "Something went wrong while running the tests");
+
+    // Get and parse the test output
+    System.out.print(out.getOutput());
+    FieldLayoutAnalyzer.LogOutput lo = new FieldLayoutAnalyzer.LogOutput(out.asLines());
+    FieldLayoutAnalyzer fla =  FieldLayoutAnalyzer.createFieldLayoutAnalyzer(lo, useCompressedOops);
+
+    // Running tests verification method (check that tests produced the right configuration)
+    Class testClass = NullMarkersTest.class;
+      Method[] testMethods = testClass.getMethods();
+      for (Method test : testMethods) {
+        if (test.getName().startsWith("check_")) {
+          Asserts.assertTrue(Modifier.isStatic(test.getModifiers()));
+          Asserts.assertTrue(test.getReturnType().equals(Void.TYPE));
+          test.invoke(null, fla);
+        }
+      }
+
+    // Verify that all layouts are correct
+    fla.check();
+  }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/libInlineWithJni.c b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/libInlineWithJni.c
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/libInlineWithJni.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018, 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 <jni.h>
+
+JNIEXPORT void JNICALL
+Java_runtime_valhalla_inlinetypes_InlineWithJni_doJniMonitorEnter(JNIEnv *env, jobject obj) {
+    (*env)->MonitorEnter(env, obj);
+}
+
+JNIEXPORT void JNICALL
+Java_runtime_valhalla_inlinetypes_InlineWithJni_doJniMonitorExit(JNIEnv *env, jobject obj) {
+    (*env)->MonitorExit(env, obj);
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/libJNIIsSameObject.c b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/libJNIIsSameObject.c
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/libJNIIsSameObject.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020, 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <jni.h>
+
+#if !defined(_WIN32) && !defined(_WIN64)
+
+JNIEXPORT jboolean JNICALL
+Java_TestJNIIsSameObject_isSameObject(JNIEnv* env, jclass klass, jobject obj0, jobject obj1) {
+  return (*env)->IsSameObject(env, obj0, obj1);
+}
+
+#endif
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractClassWithBothModifiers.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractClassWithBothModifiers.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractClassWithBothModifiers.java
@@ -0,0 +1,4 @@
+abstract class AbstractClassWithBothModifiers {
+
+}
+
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractClassWithFieldExtendingValueClass.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractClassWithFieldExtendingValueClass.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractClassWithFieldExtendingValueClass.java
@@ -0,0 +1,4 @@
+abstract class AbstractClassWithFieldExtendingValueClass extends IdentityAbstract {
+    int i;
+}
+
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractClassWithFieldImplementingValueInterface.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractClassWithFieldImplementingValueInterface.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractClassWithFieldImplementingValueInterface.java
@@ -0,0 +1,4 @@
+abstract class AbstractClassWithFieldWithNoIdentityModifier {
+    int i;
+}
+
diff a/make/langtools/test/TEST.ROOT b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractClassWithFieldWithNoIdentityModifier.java
--- a/make/langtools/test/TEST.ROOT
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractClassWithFieldWithNoIdentityModifier.java
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractClassWithSynchMethodWithNoIdentityModifier.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractClassWithSynchMethodWithNoIdentityModifier.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractClassWithSynchMethodWithNoIdentityModifier.java
@@ -0,0 +1,4 @@
+abstract class AbstractClassWithBothModifiers {
+
+}
+
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractWithField.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractWithField.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractWithField.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022, 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.
+ *
+ */
+
+// Abstract class with an instance field, this must generate an identity class
+abstract class AbstractWithField {
+    int i;
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractWithSynchMethod.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractWithSynchMethod.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractWithSynchMethod.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022, 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.
+ *
+ */
+
+// abstract class with a synchronized method, must generate an identity class
+abstract class AbstractWithSynchMethod {
+    synchronized void synchMethod() { }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractWithSynchMethodWithNoIdentityModifier.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractWithSynchMethodWithNoIdentityModifier.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/AbstractWithSynchMethodWithNoIdentityModifier.java
@@ -0,0 +1,4 @@
+abstract class AbstractClassWithSynchMethodWithNoIdentityModifier {
+    synhronized void foo() { }
+}
+
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ClassesWithInvalidModifiers.jcod b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ClassesWithInvalidModifiers.jcod
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ClassesWithInvalidModifiers.jcod
@@ -0,0 +1,832 @@
+/*
+ * Copyright (c) 2022, 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.
+ *
+ */
+
+ class ValueClassExtendingIdentityClass {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1
+    Utf8 "ValueClassExtendingIdentityClass"; // #2
+    class #4; // #3
+    Utf8 "IdentityAbstract"; // #4
+    Utf8 "<vnew>"; // #5
+    Utf8 "()LValueClassExtendingIdentityClass;"; // #6
+    Utf8 "Code"; // #7
+    Utf8 "LineNumberTable"; // #8
+    Utf8 "SourceFile"; // #9
+    Utf8 "ValueClassExtendingIdentityClass.java"; // #10
+  } // Constant Pool
+
+  0x0050; // access
+  #1;// this_cpx
+  #3;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0008; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#7) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0xCB00014B2AB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#8) { // LineNumberTable
+              [] { // line_number_table
+                0  1;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#9) { // SourceFile
+      #10;
+    } // end SourceFile
+  } // Attributes
+} // end class ValueClassExtendingIdentityClass
+
+class ValueClassExtendingAbstractClassWithField {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1
+    Utf8 "ValueClassExtendingAbstractClassWithField"; // #2
+    class #4; // #3
+    Utf8 "AbstractWithField"; // #4
+    Utf8 "<vnew>"; // #5
+    Utf8 "()LValueClassExtendingAbstractClassWithField;"; // #6
+    Utf8 "Code"; // #7
+    Utf8 "LineNumberTable"; // #8
+    Utf8 "SourceFile"; // #9
+    Utf8 "ValueClassExtendingAbstractClassWithField.java"; // #10
+  } // Constant Pool
+
+  0x0050; // access
+  #1;// this_cpx
+  #3;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0008; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#7) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0xCB00014B2AB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#8) { // LineNumberTable
+              [] { // line_number_table
+                0  1;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#9) { // SourceFile
+      #10;
+    } // end SourceFile
+  } // Attributes
+} // end class ValueClassExtendingAbstractClassWithField
+
+class ValueClassExtendingAbstractClassWithSynchMethod {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1
+    Utf8 "ValueClassExtendingAbstractClassWithSynchMethod"; // #2
+    class #4; // #3
+    Utf8 "AbstractWithSynchMethod"; // #4
+    Utf8 "<vnew>"; // #5
+    Utf8 "()LValueClassExtendingAbstractClassWithSynchMethod;"; // #6
+    Utf8 "Code"; // #7
+    Utf8 "LineNumberTable"; // #8
+    Utf8 "SourceFile"; // #9
+    Utf8 "ValueClassExtendingAbstractClassWithSynchMethod.java"; // #10
+  } // Constant Pool
+
+  0x0050; // access
+  #1;// this_cpx
+  #3;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0008; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#7) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0xCB00014B2AB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#8) { // LineNumberTable
+              [] { // line_number_table
+                0  1;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#9) { // SourceFile
+      #10;
+    } // end SourceFile
+  } // Attributes
+} // end class ValueClassExtendingAbstractClassWithSynchMethod
+
+class ValueClassImplementingIdentityInterface {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1
+    Utf8 "ValueClassImplementingIdentityInterface"; // #2
+    class #4; // #3
+    Utf8 "java/lang/Object"; // #4
+    class #6; // #5
+    Utf8 "IdentityInterface"; // #6
+    Utf8 "<vnew>"; // #7
+    Utf8 "()LValueClassImplementingIdentityInterface;"; // #8
+    Utf8 "Code"; // #9
+    Utf8 "LineNumberTable"; // #10
+    Utf8 "SourceFile"; // #11
+    Utf8 "ValueClassImplementingIdentityInterface.java"; // #12
+  } // Constant Pool
+
+  0x0050; // access
+  #1;// this_cpx
+  #3;// super_cpx
+
+  [] { // Interfaces
+    #5;
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0008; // access
+      #7; // name_index
+      #8; // descriptor_index
+      [] { // Attributes
+        Attr(#9) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0xCB00014B2AB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#10) { // LineNumberTable
+              [] { // line_number_table
+                0  1;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#11) { // SourceFile
+      #12;
+    } // end SourceFile
+  } // Attributes
+} // end class ValueClassImplementingIdentityInterface
+
+class IdentityClassExtendingValueClass {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "ValueAbstract"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    class #8; // #7
+    Utf8 "IdentityClassExtendingValueClass"; // #8
+    Utf8 "Code"; // #9
+    Utf8 "LineNumberTable"; // #10
+    Utf8 "SourceFile"; // #11
+    Utf8 "IdentityClassExtendingValueClass.java"; // #12
+  } // Constant Pool
+
+  0x0020; // access
+  #7;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#9) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#10) { // LineNumberTable
+              [] { // line_number_table
+                0  1;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#11) { // SourceFile
+      #12;
+    } // end SourceFile
+  } // Attributes
+} // end class IdentityClassExtendingValueClass
+
+class IdentityClassImplementingValueInterface {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    class #8; // #7
+    Utf8 "IdentityClassImplementingValueInterface"; // #8
+    class #10; // #9
+    Utf8 "ValueInterface"; // #10
+    Utf8 "Code"; // #11
+    Utf8 "LineNumberTable"; // #12
+    Utf8 "SourceFile"; // #13
+    Utf8 "IdentityClassImplementingValueInterface.java"; // #14
+  } // Constant Pool
+
+  0x0020; // access
+  #7;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+    #9;
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#11) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#12) { // LineNumberTable
+              [] { // line_number_table
+                0  1;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#13) { // SourceFile
+      #14;
+    } // end SourceFile
+  } // Attributes
+} // end class IdentityClassImplementingValueInterface
+
+class AbstractClassWithFieldExtendingValueClass {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "ValueAbstract"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    class #8; // #7
+    Utf8 "AbstractClassWithFieldExtendingValueClass"; // #8
+    Utf8 "i"; // #9
+    Utf8 "I"; // #10
+    Utf8 "Code"; // #11
+    Utf8 "LineNumberTable"; // #12
+    Utf8 "SourceFile"; // #13
+    Utf8 "AbstractClassWithFieldExtendingValueClass.java"; // #14
+  } // Constant Pool
+
+  0x0420; // access
+  #7;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0000; // access
+      #9; // name_index
+      #10; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#11) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#12) { // LineNumberTable
+              [] { // line_number_table
+                0  1;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#13) { // SourceFile
+      #14;
+    } // end SourceFile
+  } // Attributes
+} // end class AbstractClassWithFieldExtendingValueClass
+
+class AbstractClassWithFieldImplementingValueInterface {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    class #8; // #7
+    Utf8 "AbstractClassWithFieldImplementingValueInterface"; // #8
+    class #10; // #9
+    Utf8 "ValueInterface"; // #10
+    Utf8 "i"; // #11
+    Utf8 "I"; // #12
+    Utf8 "Code"; // #13
+    Utf8 "LineNumberTable"; // #14
+    Utf8 "SourceFile"; // #15
+    Utf8 "AbstractClassWithFieldImplementingValueInterface.java"; // #16
+  } // Constant Pool
+
+  0x0420; // access
+  #7;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+    #9;
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0000; // access
+      #11; // name_index
+      #12; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#13) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#14) { // LineNumberTable
+              [] { // line_number_table
+                0  1;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#15) { // SourceFile
+      #16;
+    } // end SourceFile
+  } // Attributes
+} // end class AbstractClassWithFieldImplementingValueInterface
+
+class AbstractClassWithFieldWithNoIdentityModifier {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    class #8; // #7
+    Utf8 "AbstractClassWithFieldWithNoIdentityModifier"; // #8
+    Utf8 "i"; // #9
+    Utf8 "I"; // #10
+    Utf8 "Code"; // #11
+    Utf8 "LineNumberTable"; // #12
+    Utf8 "SourceFile"; // #13
+    Utf8 "AbstractClassWithFieldWithNoIdentityModifier.java"; // #14
+  } // Constant Pool
+
+  0x0400; // access
+  #7;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0000; // access
+      #9; // name_index
+      #10; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#11) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#12) { // LineNumberTable
+              [] { // line_number_table
+                0  1;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#13) { // SourceFile
+      #14;
+    } // end SourceFile
+  } // Attributes
+} // end class AbstractClassWithFieldWithNoIdentityModifier
+
+class AbstractClassWithSynchMethodWithNoIdentityModifier {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    class #8; // #7
+    Utf8 "AbstractClassWithSynchMethodWithNoIdentityModifier"; // #8
+    Utf8 "Code"; // #9
+    Utf8 "LineNumberTable"; // #10
+    Utf8 "foo"; // #11
+    Utf8 "SourceFile"; // #12
+    Utf8 "AbstractClassWithSynchMethodWithNoIdentityModifier.java"; // #13
+  } // Constant Pool
+
+  0x0400; // access
+  #7;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#9) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#10) { // LineNumberTable
+              [] { // line_number_table
+                0  1;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method
+      0x0020; // access
+      #11; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#9) { // Code
+          0; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0xB1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#10) { // LineNumberTable
+              [] { // line_number_table
+                0  2;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#12) { // SourceFile
+      #13;
+    } // end SourceFile
+  } // Attributes
+} // end class AbstractClassWithSynchMethodWithNoIdentityModifier
+
+class AbstractClassWithBothModifiers {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    class #8; // #7
+    Utf8 "AbstractClassWithBothModifiers"; // #8
+    Utf8 "Code"; // #9
+    Utf8 "LineNumberTable"; // #10
+    Utf8 "SourceFile"; // #11
+    Utf8 "AbstractClassWithBothModifiers.java"; // #12
+  } // Constant Pool
+
+  0x0460; // access
+  #7;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#9) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#10) { // LineNumberTable
+              [] { // line_number_table
+                0  1;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#11) { // SourceFile
+      #12;
+    } // end SourceFile
+  } // Attributes
+} // end class AbstractClassWithBothModifiers
+
+class ConcreteClassWithNoModifiers {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1
+    class #4; // #2
+    NameAndType #5 #6; // #3
+    Utf8 "java/lang/Object"; // #4
+    Utf8 "<init>"; // #5
+    Utf8 "()V"; // #6
+    class #8; // #7
+    Utf8 "ConcreteClassWithNoModifiers"; // #8
+    Utf8 "Code"; // #9
+    Utf8 "LineNumberTable"; // #10
+    Utf8 "SourceFile"; // #11
+    Utf8 "ConcreteClassWithNoModifiers.java"; // #12
+  } // Constant Pool
+
+  0x0000; // access
+  #7;// this_cpx
+  #2;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0000; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+        Attr(#9) { // Code
+          1; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0x2AB70001B1;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#10) { // LineNumberTable
+              [] { // line_number_table
+                0  1;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#11) { // SourceFile
+      #12;
+    } // end SourceFile
+  } // Attributes
+} // end class ConcreteClassWithNoModifiers
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ConcreteClassWithNoModifiers.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ConcreteClassWithNoModifiers.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ConcreteClassWithNoModifiers.java
@@ -0,0 +1,4 @@
+class ConcreteClassWithNoModifiers {
+
+}
+
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/IdentityAbstract.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/IdentityAbstract.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/IdentityAbstract.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2022, 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.
+ *
+ */
+
+abstract identity class IdentityAbstract { }
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/IdentityClassExtendingValueClass.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/IdentityClassExtendingValueClass.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/IdentityClassExtendingValueClass.java
@@ -0,0 +1,2 @@
+identity class IdentityClassExtendingValueClass extends IdentityAbstract { }
+
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/IdentityClassImplementingValueInterface.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/IdentityClassImplementingValueInterface.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/IdentityClassImplementingValueInterface.java
@@ -0,0 +1,4 @@
+abstract identity class IdentityClassImplementingValueInterface implements IdentityInterface {
+    int i;
+}
+
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/IdentityInterface.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/IdentityInterface.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/IdentityInterface.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2022, 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.
+ *
+ */
+
+identity interface IdentityInterface { }
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/NeutralAbstract.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/NeutralAbstract.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/NeutralAbstract.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2022, 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.
+ *
+ */
+
+abstract class NeutralAbstract { }
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/NeutralInterface.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/NeutralInterface.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/NeutralInterface.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2022, 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.
+ *
+ */
+
+interface NeutralInterface { }
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/TestClassModifiers.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/TestClassModifiers.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/TestClassModifiers.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2022, 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.
+ *
+ */
+
+/*
+ * @ignore Fix JDK-8328417
+ * @test
+ * @summary test that the JVM correctly accepts or rejects classes based on their
+ *          ACC_VALUE/ACC_IDENTITY modifiers
+ * @library /test/lib
+ * @enablePreview
+ * @compile NeutralInterface.java ValueInterface.java IdentityInterface.java
+ * @compile NeutralAbstract.java ValueAbstract.java IdentityAbstract.java AbstractWithField.java AbstractWithSynchMethod.java
+ * @compile ClassesWithInvalidModifiers.jcod
+ * @run main/othervm -verify TestClassModifiers
+ */
+
+import jdk.test.lib.Asserts;
+
+ public class TestClassModifiers {
+  static value class VC0 {}
+  static value class VC1 implements NeutralInterface { }
+  static value class VC2 implements ValueInterface { }
+  static value class VC3 extends NeutralAbstract { }
+  static value class VC4 extends ValueAbstract { }
+  static value class VC5 extends NeutralAbstract implements NeutralInterface { }
+  static value class VC6 extends NeutralAbstract implements ValueInterface { }
+  static value class VC7 extends ValueAbstract implements NeutralInterface { }
+  static value class VC8 extends ValueAbstract implements ValueInterface { }
+
+  static identity class IC0 { }
+  static identity class IC1 implements NeutralInterface { }
+  static identity class IC2 implements IdentityInterface { }
+  static identity class IC3 extends NeutralAbstract { }
+  static identity class IC4 extends IdentityAbstract { }
+  static identity class IC5 extends AbstractWithField { }
+  static identity class IC6 extends AbstractWithSynchMethod { }
+  static identity class IC7 extends NeutralAbstract implements NeutralInterface { }
+  static identity class IC8 extends NeutralAbstract implements IdentityInterface { }
+  static identity class IC9 extends IdentityAbstract implements NeutralInterface { }
+  static identity class IC10 extends IdentityAbstract implements IdentityInterface { }
+
+
+  static abstract class AC0 extends AbstractWithField implements NeutralInterface { }
+  static abstract class AC1 extends AbstractWithField implements IdentityInterface { }
+  static abstract class AC2 extends AbstractWithSynchMethod implements NeutralInterface { }
+  static abstract class AC3 extends AbstractWithSynchMethod implements IdentityInterface { }
+
+  static String[] validClasses = {"VC0", "VC1", "VC2", "VC3", "VC4", "VC5", "VC6", "VC7", "VC8",
+                                  "IC0", "IC1", "IC2", "IC3", "IC4", "IC5", "IC6", "IC7", "IC8", "IC9", "IC10",
+                                  "AC0", "AC1", "AC2", "AC3"};
+
+  static String[] invalidClassesWithICCE = {"ValueClassExtendingIdentityClass", "ValueClassExtendingAbstractClassWithField",
+                                    "ValueClassExtendingAbstractClassWithSynchMethod", "ValueClassImplementingIdentityInterface",
+                                    "IdentityClassExtendingValueClass", "IdentityClassImplementingValueInterface",
+                                    "AbstractClassWithFieldExtendingValueClass", "AbstractClassWithFieldImplementingValueInterface"};
+  static String[] invalidClassesWithCFE = {"AbstractClassWithFieldWithNoIdentityModifier", "AbstractClassWithSynchMethodWithNoIdentityModifier",
+                                           "AbstractClassWithBothModifiers", "ConcreteClassWithNoModifiers"};
+
+  public static void main(String[] args) throws Exception {
+    // testing valid cases first
+    try {
+      for (String name : validClasses) {
+        System.out.println("Trying to load "+name);
+        Class c = Class.forName("TestClassModifiers$"+name);
+      }
+    } catch(Throwable t) {
+      t.printStackTrace();
+      throw t;
+    }
+
+    // Testing invalid cases
+    for (String name: invalidClassesWithICCE) {
+      boolean icce = false;
+      boolean otherException = false;
+      Throwable exception = null;
+      try {
+        System.out.println("Trying to load "+name);
+        Class c = Class.forName(name);
+      } catch (IncompatibleClassChangeError e) {
+        icce = true;
+      } catch (Throwable t) {
+        otherException = true;
+        exception = t;
+      }
+      Asserts.assertFalse(otherException, "Unexpected exception "+exception);
+      Asserts.assertTrue(icce, "IncompatibleClassChangeError not thrown");
+    }
+    for (String name: invalidClassesWithCFE) {
+      boolean cfe = false;
+      boolean otherException = false;
+      Throwable exception = null;
+      try {
+        System.out.println("Trying to load "+name);
+        Class c = Class.forName(name);
+      } catch (ClassFormatError e) {
+        cfe = true;
+      } catch (Throwable t) {
+        otherException = true;
+        exception = t;
+      }
+      Asserts.assertFalse(otherException, "Unexpected exception "+exception);
+      Asserts.assertTrue(cfe, "ClassFormatError not thrown");
+    }
+  }
+ }
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ValueAbstract.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ValueAbstract.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ValueAbstract.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2022, 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.
+ *
+ */
+
+public abstract value class ValueAbstract { }
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ValueClassExtendingIdentityClass.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ValueClassExtendingIdentityClass.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ValueClassExtendingIdentityClass.java
@@ -0,0 +1,1 @@
+value class ValueClassExtendingIdentityClass extends ValueAbstract { }
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ValueClassImplementingIdentityInterface b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ValueClassImplementingIdentityInterface
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ValueClassImplementingIdentityInterface
@@ -0,0 +1,1 @@
+value class ValueClassExtendingIdentityClass implements ValueAbstract { }
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ValueClassImplementingIdentityInterface.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ValueClassImplementingIdentityInterface.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ValueClassImplementingIdentityInterface.java
@@ -0,0 +1,1 @@
+value class ValueClassImplementingIdentityInterface implements ValueInterface { }
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ValueInterface.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ValueInterface.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testClassModifiers/ValueInterface.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2022, 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.
+ *
+ */
+
+value interface ValueInterface { }
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/CtorHasArgs.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/CtorHasArgs.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/CtorHasArgs.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2020, 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.
+ *
+ */
+
+public abstract class CtorHasArgs {
+    CtorHasArgs(String x) { }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/CtorIsNotEmpty.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/CtorIsNotEmpty.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/CtorIsNotEmpty.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2020, 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.
+ *
+ */
+
+public abstract class CtorIsNotEmpty {
+    CtorIsNotEmpty() {
+        System.out.println("CtorIsNotEmpty");
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/HasNonStaticFields.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/HasNonStaticFields.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/HasNonStaticFields.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2020, 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.
+ *
+ */
+
+public abstract class HasNonStaticFields {
+    String hi;
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/HasSynchMethod.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/HasSynchMethod.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/HasSynchMethod.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2020, 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.
+ *
+ */
+
+public abstract class HasSynchMethod {
+    public synchronized void helloSynch() { }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/InlineClassWithBadSupers.jcod b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/InlineClassWithBadSupers.jcod
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/InlineClassWithBadSupers.jcod
@@ -0,0 +1,2637 @@
+/*
+ * Copyright (c) 2020, 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.
+ *
+ */
+// The test cases in this .jcod file are based on this .java file:
+// public primitive final class Point extends AbstractClass {
+//     final int x;
+//     final int y;
+//
+//     private Point() {
+//         x = 0;
+//         y = 0;
+//     }
+//
+//     public int getX() { return x; }
+//     public int getY() { return y; }
+// }
+
+// Primitive class with a super class that is not abstract.
+class PrimitiveSuperNotAbstract {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [51] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1     at 0x0A
+    Utf8 "PrimitiveSuperNotAbstract"; // #2     at 0x0D
+    Field #1 #4; // #3     at 0x20
+    NameAndType #5 #6; // #4     at 0x25
+    Utf8 "x"; // #5     at 0x2A
+    Utf8 "I"; // #6     at 0x2E
+    Field #1 #8; // #7     at 0x32
+    NameAndType #9 #6; // #8     at 0x37
+    Utf8 "y"; // #9     at 0x3C
+    InvokeDynamic 0s #11; // #10     at 0x40
+    NameAndType #12 #13; // #11     at 0x45
+    Utf8 "hashCode"; // #12     at 0x4A
+    Utf8 "(QPrimitiveSuperNotAbstract;)I"; // #13     at 0x55
+    InvokeDynamic 0s #15; // #14     at 0x6D
+    NameAndType #16 #17; // #15     at 0x72
+    Utf8 "equals"; // #16     at 0x77
+    Utf8 "(QPrimitiveSuperNotAbstract;Ljava/lang/Object;)Z"; // #17     at 0x80
+    InvokeDynamic 0s #19; // #18     at 0xAA
+    NameAndType #20 #21; // #19     at 0xAF
+    Utf8 "toString"; // #20     at 0xB4
+    Utf8 "(QPrimitiveSuperNotAbstract;)Ljava/lang/String;"; // #21     at 0xBF
+    class #23; // #22     at 0xE8
+    Utf8 "NotAbstract"; // #23     at 0xEB
+    class #25; // #24     at 0xF9
+    Utf8 "Unused"; // #25     at 0xFC
+    Utf8 "getX"; // #26     at 0x0115
+    Utf8 "()I"; // #27     at 0x011C
+    Utf8 "Code"; // #28     at 0x0122
+    Utf8 "LineNumberTable"; // #29     at 0x0129
+    Utf8 "getY"; // #30     at 0x013B
+    Utf8 "(Ljava/lang/Object;)Z"; // #31     at 0x0142
+    Utf8 "()Ljava/lang/String;"; // #32     at 0x015A
+    Utf8 "<vnew>"; // #33     at 0x0171
+    Utf8 "()QPrimitiveSuperNotAbstract;"; // #34     at 0x017A
+    Utf8 "SourceFile"; // #35     at 0x0191
+    Utf8 "PrimitiveSuperNotAbstract.java"; // #36     at 0x019E
+    Utf8 "BootstrapMethods"; // #37     at 0x01B6
+    MethodHandle 6b #39; // #38     at 0x01C9
+    Method #40 #41; // #39     at 0x01CD
+    class #42; // #40     at 0x01D2
+    NameAndType #43 #44; // #41     at 0x01D5
+    Utf8 "java/lang/invoke/ValueBootstrapMethods"; // #42     at 0x01DA
+    Utf8 "makeBootstrapMethod"; // #43     at 0x0203
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; // #44     at 0x0219
+    Utf8 "InnerClasses"; // #45     at 0x028F
+    class #47; // #46     at 0x029E
+    Utf8 "java/lang/invoke/MethodHandles$Lookup"; // #47     at 0x02A1
+    class #49; // #48     at 0x02C9
+    Utf8 "java/lang/invoke/MethodHandles"; // #49     at 0x02CC
+    Utf8 "Lookup"; // #50     at 0x02ED
+  } // Constant Pool
+
+  0x0851; // access [ ACC_VALUE ACC_PRIMITIVE ACC_PUBLIC ACC_FINAL ]
+  #1;// this_cpx
+  #22;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // fields
+    { // Member at 0x0302
+      0x0010; // access
+      #5; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x030A
+      0x0010; // access
+      #9; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [6] { // methods
+    { // Member at 0x0314
+      0x0001; // access
+      #26; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x031C
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40003AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x0333
+              [1] { // LineNumberTable
+                0  10; //  at 0x033F
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x033F
+      0x0001; // access
+      #30; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x0347
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40007AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x035E
+              [1] { // LineNumberTable
+                0  11; //  at 0x036A
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x036A
+      0x0011; // access
+      #12; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x0372
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA000A0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x038B
+              [1] { // LineNumberTable
+                0  1; //  at 0x0397
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0397
+      0x0011; // access
+      #16; // name_cpx
+      #31; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 32) { // Code at 0x039F
+          2; // max_stack
+          2; // max_locals
+          Bytes[8]{
+            0x2A2BBA000E0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03B9
+              [1] { // LineNumberTable
+                0  1; //  at 0x03C5
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03C5
+      0x0011; // access
+      #20; // name_cpx
+      #32; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x03CD
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00120000B0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03E6
+              [1] { // LineNumberTable
+                0  1; //  at 0x03F2
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03F2
+      0x000A; // access
+      #33; // name_cpx
+      #34; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 56) { // Code at 0x03FA
+          2; // max_stack
+          1; // max_locals
+          Bytes[20]{
+            0xCB00014B032A5FCC;
+            0x00034B032A5FCC00;
+            0x074B2AB0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 18) { // LineNumberTable at 0x0420
+              [4] { // LineNumberTable
+                0  5; //  at 0x042C
+                4  6; //  at 0x0430
+                11  7; //  at 0x0434
+                18  8; //  at 0x0438
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [3] { // Attributes
+    Attr(#35, 2) { // SourceFile at 0x043A
+      #36;
+    } // end SourceFile
+    ;
+    Attr(#37, 6) { // BootstrapMethods at 0x0442
+      [1] { // bootstrap_methods
+        {  //  bootstrap_method
+          #38; // bootstrap_method_ref
+          [0] { // bootstrap_arguments
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+      }
+    } // end BootstrapMethods
+    ;
+    Attr(#45, 10) { // InnerClasses at 0x044E
+      [1] { // InnerClasses
+        #46 #48 #50 25; //  at 0x045E
+      }
+    } // end InnerClasses
+  } // Attributes
+} // end class PrimitiveSuperNotAbstract
+
+
+
+// Primitive class with a super class that has instance fields.
+class PrimitiveSuperHasNonStaticFields {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [51] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1     at 0x0A
+    Utf8 "PrimitiveSuperHasNonStaticFields"; // #2     at 0x0D
+    Field #1 #4; // #3     at 0x20
+    NameAndType #5 #6; // #4     at 0x25
+    Utf8 "x"; // #5     at 0x2A
+    Utf8 "I"; // #6     at 0x2E
+    Field #1 #8; // #7     at 0x32
+    NameAndType #9 #6; // #8     at 0x37
+    Utf8 "y"; // #9     at 0x3C
+    InvokeDynamic 0s #11; // #10     at 0x40
+    NameAndType #12 #13; // #11     at 0x45
+    Utf8 "hashCode"; // #12     at 0x4A
+    Utf8 "(QPrimitiveSuperHasNonStaticFields;)I"; // #13     at 0x55
+    InvokeDynamic 0s #15; // #14     at 0x6D
+    NameAndType #16 #17; // #15     at 0x72
+    Utf8 "equals"; // #16     at 0x77
+    Utf8 "(QPrimitiveSuperHasNonStaticFields;Ljava/lang/Object;)Z"; // #17     at 0x80
+    InvokeDynamic 0s #19; // #18     at 0xAA
+    NameAndType #20 #21; // #19     at 0xAF
+    Utf8 "toString"; // #20     at 0xB4
+    Utf8 "(QPrimitiveSuperHasNonStaticFields;)Ljava/lang/String;"; // #21     at 0xBF
+    class #23; // #22     at 0xE8
+    Utf8 "HasNonStaticFields"; // #23     at 0xEB
+    class #25; // #24     at 0xF9
+    Utf8 "Unused"; // #25     at 0xFC
+    Utf8 "getX"; // #26     at 0x0115
+    Utf8 "()I"; // #27     at 0x011C
+    Utf8 "Code"; // #28     at 0x0122
+    Utf8 "LineNumberTable"; // #29     at 0x0129
+    Utf8 "getY"; // #30     at 0x013B
+    Utf8 "(Ljava/lang/Object;)Z"; // #31     at 0x0142
+    Utf8 "()Ljava/lang/String;"; // #32     at 0x015A
+    Utf8 "<vnew>"; // #33     at 0x0171
+    Utf8 "()QPrimitiveSuperHasNonStaticFields;"; // #34     at 0x017A
+    Utf8 "SourceFile"; // #35     at 0x0191
+    Utf8 "PrimitiveSuperHasNonStaticFields.java"; // #36     at 0x019E
+    Utf8 "BootstrapMethods"; // #37     at 0x01B6
+    MethodHandle 6b #39; // #38     at 0x01C9
+    Method #40 #41; // #39     at 0x01CD
+    class #42; // #40     at 0x01D2
+    NameAndType #43 #44; // #41     at 0x01D5
+    Utf8 "java/lang/invoke/ValueBootstrapMethods"; // #42     at 0x01DA
+    Utf8 "makeBootstrapMethod"; // #43     at 0x0203
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; // #44     at 0x0219
+    Utf8 "InnerClasses"; // #45     at 0x028F
+    class #47; // #46     at 0x029E
+    Utf8 "java/lang/invoke/MethodHandles$Lookup"; // #47     at 0x02A1
+    class #49; // #48     at 0x02C9
+    Utf8 "java/lang/invoke/MethodHandles"; // #49     at 0x02CC
+    Utf8 "Lookup"; // #50     at 0x02ED
+  } // Constant Pool
+
+  0x0851; // access [ ACC_VALUE ACC_PRIMITIVE ACC_PUBLIC ACC_FINAL ]
+  #1;// this_cpx
+  #22;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // fields
+    { // Member at 0x0302
+      0x0010; // access
+      #5; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x030A
+      0x0010; // access
+      #9; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [6] { // methods
+    { // Member at 0x0314
+      0x0001; // access
+      #26; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x031C
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40003AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x0333
+              [1] { // LineNumberTable
+                0  10; //  at 0x033F
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x033F
+      0x0001; // access
+      #30; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x0347
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40007AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x035E
+              [1] { // LineNumberTable
+                0  11; //  at 0x036A
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x036A
+      0x0011; // access
+      #12; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x0372
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA000A0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x038B
+              [1] { // LineNumberTable
+                0  1; //  at 0x0397
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0397
+      0x0011; // access
+      #16; // name_cpx
+      #31; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 32) { // Code at 0x039F
+          2; // max_stack
+          2; // max_locals
+          Bytes[8]{
+            0x2A2BBA000E0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03B9
+              [1] { // LineNumberTable
+                0  1; //  at 0x03C5
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03C5
+      0x0011; // access
+      #20; // name_cpx
+      #32; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x03CD
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00120000B0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03E6
+              [1] { // LineNumberTable
+                0  1; //  at 0x03F2
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03F2
+      0x000A; // access
+      #33; // name_cpx
+      #34; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 56) { // Code at 0x03FA
+          2; // max_stack
+          1; // max_locals
+          Bytes[20]{
+            0xCB00014B032A5FCC;
+            0x00034B032A5FCC00;
+            0x074B2AB0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 18) { // LineNumberTable at 0x0420
+              [4] { // LineNumberTable
+                0  5; //  at 0x042C
+                4  6; //  at 0x0430
+                11  7; //  at 0x0434
+                18  8; //  at 0x0438
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [3] { // Attributes
+    Attr(#35, 2) { // SourceFile at 0x043A
+      #36;
+    } // end SourceFile
+    ;
+    Attr(#37, 6) { // BootstrapMethods at 0x0442
+      [1] { // bootstrap_methods
+        {  //  bootstrap_method
+          #38; // bootstrap_method_ref
+          [0] { // bootstrap_arguments
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+      }
+    } // end BootstrapMethods
+    ;
+    Attr(#45, 10) { // InnerClasses at 0x044E
+      [1] { // InnerClasses
+        #46 #48 #50 25; //  at 0x045E
+      }
+    } // end InnerClasses
+  } // Attributes
+} // end class PrimitiveSuperHasNonStaticFields
+
+
+
+// primitive value class with a super class whose super class has a syncronized instance method.
+class PrimitiveSuperHasSynchMethod {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [51] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1     at 0x0A
+    Utf8 "PrimitiveSuperHasSynchMethod"; // #2     at 0x0D
+    Field #1 #4; // #3     at 0x20
+    NameAndType #5 #6; // #4     at 0x25
+    Utf8 "x"; // #5     at 0x2A
+    Utf8 "I"; // #6     at 0x2E
+    Field #1 #8; // #7     at 0x32
+    NameAndType #9 #6; // #8     at 0x37
+    Utf8 "y"; // #9     at 0x3C
+    InvokeDynamic 0s #11; // #10     at 0x40
+    NameAndType #12 #13; // #11     at 0x45
+    Utf8 "hashCode"; // #12     at 0x4A
+    Utf8 "(QPrimitiveSuperHasSynchMethod;)I"; // #13     at 0x55
+    InvokeDynamic 0s #15; // #14     at 0x6D
+    NameAndType #16 #17; // #15     at 0x72
+    Utf8 "equals"; // #16     at 0x77
+    Utf8 "(QPrimitiveSuperHasSynchMethod;Ljava/lang/Object;)Z"; // #17     at 0x80
+    InvokeDynamic 0s #19; // #18     at 0xAA
+    NameAndType #20 #21; // #19     at 0xAF
+    Utf8 "toString"; // #20     at 0xB4
+    Utf8 "(QPrimitiveSuperHasSynchMethod;)Ljava/lang/String;"; // #21     at 0xBF
+    class #23; // #22     at 0xE8
+    Utf8 "ValidSuper"; // #23     at 0xEB
+    class #25; // #24     at 0xF9
+    Utf8 "Unused"; // #25     at 0xFC
+    Utf8 "getX"; // #26     at 0x0115
+    Utf8 "()I"; // #27     at 0x011C
+    Utf8 "Code"; // #28     at 0x0122
+    Utf8 "LineNumberTable"; // #29     at 0x0129
+    Utf8 "getY"; // #30     at 0x013B
+    Utf8 "(Ljava/lang/Object;)Z"; // #31     at 0x0142
+    Utf8 "()Ljava/lang/String;"; // #32     at 0x015A
+    Utf8 "<vnew>"; // #33     at 0x0171
+    Utf8 "()QPrimitiveSuperHasSynchMethod;"; // #34     at 0x017A
+    Utf8 "SourceFile"; // #35     at 0x0191
+    Utf8 "PrimitiveSuperHasSynchMethod.java"; // #36     at 0x019E
+    Utf8 "BootstrapMethods"; // #37     at 0x01B6
+    MethodHandle 6b #39; // #38     at 0x01C9
+    Method #40 #41; // #39     at 0x01CD
+    class #42; // #40     at 0x01D2
+    NameAndType #43 #44; // #41     at 0x01D5
+    Utf8 "java/lang/invoke/ValueBootstrapMethods"; // #42     at 0x01DA
+    Utf8 "makeBootstrapMethod"; // #43     at 0x0203
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; // #44     at 0x0219
+    Utf8 "InnerClasses"; // #45     at 0x028F
+    class #47; // #46     at 0x029E
+    Utf8 "java/lang/invoke/MethodHandles$Lookup"; // #47     at 0x02A1
+    class #49; // #48     at 0x02C9
+    Utf8 "java/lang/invoke/MethodHandles"; // #49     at 0x02CC
+    Utf8 "Lookup"; // #50     at 0x02ED
+  } // Constant Pool
+
+  0x0851; // access [ ACC_VALUE ACC_PRIMITIVE ACC_PUBLIC ACC_FINAL ]
+  #1;// this_cpx
+  #22;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // fields
+    { // Member at 0x0302
+      0x0010; // access
+      #5; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x030A
+      0x0010; // access
+      #9; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [6] { // methods
+    { // Member at 0x0314
+      0x0001; // access
+      #26; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x031C
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40003AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x0333
+              [1] { // LineNumberTable
+                0  10; //  at 0x033F
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x033F
+      0x0001; // access
+      #30; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x0347
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40007AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x035E
+              [1] { // LineNumberTable
+                0  11; //  at 0x036A
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x036A
+      0x0011; // access
+      #12; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x0372
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA000A0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x038B
+              [1] { // LineNumberTable
+                0  1; //  at 0x0397
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0397
+      0x0011; // access
+      #16; // name_cpx
+      #31; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 32) { // Code at 0x039F
+          2; // max_stack
+          2; // max_locals
+          Bytes[8]{
+            0x2A2BBA000E0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03B9
+              [1] { // LineNumberTable
+                0  1; //  at 0x03C5
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03C5
+      0x0011; // access
+      #20; // name_cpx
+      #32; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x03CD
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00120000B0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03E6
+              [1] { // LineNumberTable
+                0  1; //  at 0x03F2
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03F2
+      0x000A; // access
+      #33; // name_cpx
+      #34; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 56) { // Code at 0x03FA
+          2; // max_stack
+          1; // max_locals
+          Bytes[20]{
+            0xCB00014B032A5FCC;
+            0x00034B032A5FCC00;
+            0x074B2AB0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 18) { // LineNumberTable at 0x0420
+              [4] { // LineNumberTable
+                0  5; //  at 0x042C
+                4  6; //  at 0x0430
+                11  7; //  at 0x0434
+                18  8; //  at 0x0438
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [3] { // Attributes
+    Attr(#35, 2) { // SourceFile at 0x043A
+      #36;
+    } // end SourceFile
+    ;
+    Attr(#37, 6) { // BootstrapMethods at 0x0442
+      [1] { // bootstrap_methods
+        {  //  bootstrap_method
+          #38; // bootstrap_method_ref
+          [0] { // bootstrap_arguments
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+      }
+    } // end BootstrapMethods
+    ;
+    Attr(#45, 10) { // InnerClasses at 0x044E
+      [1] { // InnerClasses
+        #46 #48 #50 25; //  at 0x045E
+      }
+    } // end InnerClasses
+  } // Attributes
+} // end class PrimitiveSuperHasSynchMethod
+
+
+// primitive value class with a super class containing a constructor that has arguments.
+class PrimitiveSuperCtorHasArgs {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [51] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1     at 0x0A
+    Utf8 "PrimitiveSuperCtorHasArgs"; // #2     at 0x0D
+    Field #1 #4; // #3     at 0x20
+    NameAndType #5 #6; // #4     at 0x25
+    Utf8 "x"; // #5     at 0x2A
+    Utf8 "I"; // #6     at 0x2E
+    Field #1 #8; // #7     at 0x32
+    NameAndType #9 #6; // #8     at 0x37
+    Utf8 "y"; // #9     at 0x3C
+    InvokeDynamic 0s #11; // #10     at 0x40
+    NameAndType #12 #13; // #11     at 0x45
+    Utf8 "hashCode"; // #12     at 0x4A
+    Utf8 "(QPrimitiveSuperCtorHasArgs;)I"; // #13     at 0x55
+    InvokeDynamic 0s #15; // #14     at 0x6D
+    NameAndType #16 #17; // #15     at 0x72
+    Utf8 "equals"; // #16     at 0x77
+    Utf8 "(QPrimitiveSuperCtorHasArgs;Ljava/lang/Object;)Z"; // #17     at 0x80
+    InvokeDynamic 0s #19; // #18     at 0xAA
+    NameAndType #20 #21; // #19     at 0xAF
+    Utf8 "toString"; // #20     at 0xB4
+    Utf8 "(QPrimitiveSuperCtorHasArgs;)Ljava/lang/String;"; // #21     at 0xBF
+    class #23; // #22     at 0xE8
+    Utf8 "CtorHasArgs"; // #23     at 0xEB
+    class #25; // #24     at 0xF9
+    Utf8 "Unused"; // #25     at 0xFC
+    Utf8 "getX"; // #26     at 0x0115
+    Utf8 "()I"; // #27     at 0x011C
+    Utf8 "Code"; // #28     at 0x0122
+    Utf8 "LineNumberTable"; // #29     at 0x0129
+    Utf8 "getY"; // #30     at 0x013B
+    Utf8 "(Ljava/lang/Object;)Z"; // #31     at 0x0142
+    Utf8 "()Ljava/lang/String;"; // #32     at 0x015A
+    Utf8 "<vnew>"; // #33     at 0x0171
+    Utf8 "()QPrimitiveSuperCtorHasArgs;"; // #34     at 0x017A
+    Utf8 "SourceFile"; // #35     at 0x0191
+    Utf8 "PrimitiveSuperCtorHasArgs.java"; // #36     at 0x019E
+    Utf8 "BootstrapMethods"; // #37     at 0x01B6
+    MethodHandle 6b #39; // #38     at 0x01C9
+    Method #40 #41; // #39     at 0x01CD
+    class #42; // #40     at 0x01D2
+    NameAndType #43 #44; // #41     at 0x01D5
+    Utf8 "java/lang/invoke/ValueBootstrapMethods"; // #42     at 0x01DA
+    Utf8 "makeBootstrapMethod"; // #43     at 0x0203
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; // #44     at 0x0219
+    Utf8 "InnerClasses"; // #45     at 0x028F
+    class #47; // #46     at 0x029E
+    Utf8 "java/lang/invoke/MethodHandles$Lookup"; // #47     at 0x02A1
+    class #49; // #48     at 0x02C9
+    Utf8 "java/lang/invoke/MethodHandles"; // #49     at 0x02CC
+    Utf8 "Lookup"; // #50     at 0x02ED
+  } // Constant Pool
+
+  0x0851; // access [ ACC_VALUE ACC_PRIMITIVE ACC_PUBLIC ACC_FINAL ]
+  #1;// this_cpx
+  #22;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // fields
+    { // Member at 0x0302
+      0x0010; // access
+      #5; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x030A
+      0x0010; // access
+      #9; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [6] { // methods
+    { // Member at 0x0314
+      0x0001; // access
+      #26; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x031C
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40003AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x0333
+              [1] { // LineNumberTable
+                0  10; //  at 0x033F
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x033F
+      0x0001; // access
+      #30; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x0347
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40007AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x035E
+              [1] { // LineNumberTable
+                0  11; //  at 0x036A
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x036A
+      0x0011; // access
+      #12; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x0372
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA000A0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x038B
+              [1] { // LineNumberTable
+                0  1; //  at 0x0397
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0397
+      0x0011; // access
+      #16; // name_cpx
+      #31; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 32) { // Code at 0x039F
+          2; // max_stack
+          2; // max_locals
+          Bytes[8]{
+            0x2A2BBA000E0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03B9
+              [1] { // LineNumberTable
+                0  1; //  at 0x03C5
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03C5
+      0x0011; // access
+      #20; // name_cpx
+      #32; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x03CD
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00120000B0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03E6
+              [1] { // LineNumberTable
+                0  1; //  at 0x03F2
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03F2
+      0x000A; // access
+      #33; // name_cpx
+      #34; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 56) { // Code at 0x03FA
+          2; // max_stack
+          1; // max_locals
+          Bytes[20]{
+            0xCB00014B032A5FCC;
+            0x00034B032A5FCC00;
+            0x074B2AB0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 18) { // LineNumberTable at 0x0420
+              [4] { // LineNumberTable
+                0  5; //  at 0x042C
+                4  6; //  at 0x0430
+                11  7; //  at 0x0434
+                18  8; //  at 0x0438
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [3] { // Attributes
+    Attr(#35, 2) { // SourceFile at 0x043A
+      #36;
+    } // end SourceFile
+    ;
+    Attr(#37, 6) { // BootstrapMethods at 0x0442
+      [1] { // bootstrap_methods
+        {  //  bootstrap_method
+          #38; // bootstrap_method_ref
+          [0] { // bootstrap_arguments
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+      }
+    } // end BootstrapMethods
+    ;
+    Attr(#45, 10) { // InnerClasses at 0x044E
+      [1] { // InnerClasses
+        #46 #48 #50 25; //  at 0x045E
+      }
+    } // end InnerClasses
+  } // Attributes
+} // end class PrimitiveSuperCtorHasArgs
+
+
+
+// primitive value class with a super class containing a constructor that has a non-empty
+// method body.
+class PrimitiveSuperCtorIsNotEmpty {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [51] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1     at 0x0A
+    Utf8 "PrimitiveSuperCtorIsNotEmpty"; // #2     at 0x0D
+    Field #1 #4; // #3     at 0x20
+    NameAndType #5 #6; // #4     at 0x25
+    Utf8 "x"; // #5     at 0x2A
+    Utf8 "I"; // #6     at 0x2E
+    Field #1 #8; // #7     at 0x32
+    NameAndType #9 #6; // #8     at 0x37
+    Utf8 "y"; // #9     at 0x3C
+    InvokeDynamic 0s #11; // #10     at 0x40
+    NameAndType #12 #13; // #11     at 0x45
+    Utf8 "hashCode"; // #12     at 0x4A
+    Utf8 "(QPrimitiveSuperCtorIsNotEmpty;)I"; // #13     at 0x55
+    InvokeDynamic 0s #15; // #14     at 0x6D
+    NameAndType #16 #17; // #15     at 0x72
+    Utf8 "equals"; // #16     at 0x77
+    Utf8 "(QPrimitiveSuperCtorIsNotEmpty;Ljava/lang/Object;)Z"; // #17     at 0x80
+    InvokeDynamic 0s #19; // #18     at 0xAA
+    NameAndType #20 #21; // #19     at 0xAF
+    Utf8 "toString"; // #20     at 0xB4
+    Utf8 "(QPrimitiveSuperCtorIsNotEmpty;)Ljava/lang/String;"; // #21     at 0xBF
+    class #23; // #22     at 0xE8
+    Utf8 "CtorIsNotEmpty"; // #23     at 0xEB
+    class #25; // #24     at 0xF9
+    Utf8 "Unused"; // #25     at 0xFC
+    Utf8 "getX"; // #26     at 0x0115
+    Utf8 "()I"; // #27     at 0x011C
+    Utf8 "Code"; // #28     at 0x0122
+    Utf8 "LineNumberTable"; // #29     at 0x0129
+    Utf8 "getY"; // #30     at 0x013B
+    Utf8 "(Ljava/lang/Object;)Z"; // #31     at 0x0142
+    Utf8 "()Ljava/lang/String;"; // #32     at 0x015A
+    Utf8 "<vnew>"; // #33     at 0x0171
+    Utf8 "()QPrimitiveSuperCtorIsNotEmpty;"; // #34     at 0x017A
+    Utf8 "SourceFile"; // #35     at 0x0191
+    Utf8 "PrimitiveSuperCtorIsNotEmpty.java"; // #36     at 0x019E
+    Utf8 "BootstrapMethods"; // #37     at 0x01B6
+    MethodHandle 6b #39; // #38     at 0x01C9
+    Method #40 #41; // #39     at 0x01CD
+    class #42; // #40     at 0x01D2
+    NameAndType #43 #44; // #41     at 0x01D5
+    Utf8 "java/lang/invoke/ValueBootstrapMethods"; // #42     at 0x01DA
+    Utf8 "makeBootstrapMethod"; // #43     at 0x0203
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; // #44     at 0x0219
+    Utf8 "InnerClasses"; // #45     at 0x028F
+    class #47; // #46     at 0x029E
+    Utf8 "java/lang/invoke/MethodHandles$Lookup"; // #47     at 0x02A1
+    class #49; // #48     at 0x02C9
+    Utf8 "java/lang/invoke/MethodHandles"; // #49     at 0x02CC
+    Utf8 "Lookup"; // #50     at 0x02ED
+  } // Constant Pool
+
+  0x0851; // access [ ACC_VALUE ACC_PRIMITIVE ACC_PUBLIC ACC_FINAL ]
+  #1;// this_cpx
+  #22;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // fields
+    { // Member at 0x0302
+      0x0010; // access
+      #5; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x030A
+      0x0010; // access
+      #9; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [6] { // methods
+    { // Member at 0x0314
+      0x0001; // access
+      #26; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x031C
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40003AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x0333
+              [1] { // LineNumberTable
+                0  10; //  at 0x033F
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x033F
+      0x0001; // access
+      #30; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x0347
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40007AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x035E
+              [1] { // LineNumberTable
+                0  11; //  at 0x036A
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x036A
+      0x0011; // access
+      #12; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x0372
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA000A0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x038B
+              [1] { // LineNumberTable
+                0  1; //  at 0x0397
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0397
+      0x0011; // access
+      #16; // name_cpx
+      #31; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 32) { // Code at 0x039F
+          2; // max_stack
+          2; // max_locals
+          Bytes[8]{
+            0x2A2BBA000E0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03B9
+              [1] { // LineNumberTable
+                0  1; //  at 0x03C5
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03C5
+      0x0011; // access
+      #20; // name_cpx
+      #32; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x03CD
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00120000B0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03E6
+              [1] { // LineNumberTable
+                0  1; //  at 0x03F2
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03F2
+      0x000A; // access
+      #33; // name_cpx
+      #34; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 56) { // Code at 0x03FA
+          2; // max_stack
+          1; // max_locals
+          Bytes[20]{
+            0xCB00014B032A5FCC;
+            0x00034B032A5FCC00;
+            0x074B2AB0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 18) { // LineNumberTable at 0x0420
+              [4] { // LineNumberTable
+                0  5; //  at 0x042C
+                4  6; //  at 0x0430
+                11  7; //  at 0x0434
+                18  8; //  at 0x0438
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [3] { // Attributes
+    Attr(#35, 2) { // SourceFile at 0x043A
+      #36;
+    } // end SourceFile
+    ;
+    Attr(#37, 6) { // BootstrapMethods at 0x0442
+      [1] { // bootstrap_methods
+        {  //  bootstrap_method
+          #38; // bootstrap_method_ref
+          [0] { // bootstrap_arguments
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+      }
+    } // end BootstrapMethods
+    ;
+    Attr(#45, 10) { // InnerClasses at 0x044E
+      [1] { // InnerClasses
+        #46 #48 #50 25; //  at 0x045E
+      }
+    } // end InnerClasses
+  } // Attributes
+} // end class PrimitiveSuperCtorIsNotEmpty
+
+// Again for value objects...
+
+// Value class with a super class that is not abstract.
+class ValueSuperNotAbstract {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [51] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1     at 0x0A
+    Utf8 "ValueSuperNotAbstract"; // #2     at 0x0D
+    Field #1 #4; // #3     at 0x20
+    NameAndType #5 #6; // #4     at 0x25
+    Utf8 "x"; // #5     at 0x2A
+    Utf8 "I"; // #6     at 0x2E
+    Field #1 #8; // #7     at 0x32
+    NameAndType #9 #6; // #8     at 0x37
+    Utf8 "y"; // #9     at 0x3C
+    InvokeDynamic 0s #11; // #10     at 0x40
+    NameAndType #12 #13; // #11     at 0x45
+    Utf8 "hashCode"; // #12     at 0x4A
+    Utf8 "(LValueSuperNotAbstract;)I"; // #13     at 0x55
+    InvokeDynamic 0s #15; // #14     at 0x6D
+    NameAndType #16 #17; // #15     at 0x72
+    Utf8 "equals"; // #16     at 0x77
+    Utf8 "(LValueSuperNotAbstract;Ljava/lang/Object;)Z"; // #17     at 0x80
+    InvokeDynamic 0s #19; // #18     at 0xAA
+    NameAndType #20 #21; // #19     at 0xAF
+    Utf8 "toString"; // #20     at 0xB4
+    Utf8 "(LValueSuperNotAbstract;)Ljava/lang/String;"; // #21     at 0xBF
+    class #23; // #22     at 0xE8
+    Utf8 "NotAbstract"; // #23     at 0xEB
+    class #25; // #24     at 0xF9
+    Utf8 "Unused"; // #25     at 0xFC
+    Utf8 "getX"; // #26     at 0x0115
+    Utf8 "()I"; // #27     at 0x011C
+    Utf8 "Code"; // #28     at 0x0122
+    Utf8 "LineNumberTable"; // #29     at 0x0129
+    Utf8 "getY"; // #30     at 0x013B
+    Utf8 "(Ljava/lang/Object;)Z"; // #31     at 0x0142
+    Utf8 "()Ljava/lang/String;"; // #32     at 0x015A
+    Utf8 "<vnew>"; // #33     at 0x0171
+    Utf8 "()LValueSuperNotAbstract;"; // #34     at 0x017A
+    Utf8 "SourceFile"; // #35     at 0x0191
+    Utf8 "ValueSuperNotAbstract.java"; // #36     at 0x019E
+    Utf8 "BootstrapMethods"; // #37     at 0x01B6
+    MethodHandle 6b #39; // #38     at 0x01C9
+    Method #40 #41; // #39     at 0x01CD
+    class #42; // #40     at 0x01D2
+    NameAndType #43 #44; // #41     at 0x01D5
+    Utf8 "java/lang/invoke/ValueBootstrapMethods"; // #42     at 0x01DA
+    Utf8 "makeBootstrapMethod"; // #43     at 0x0203
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; // #44     at 0x0219
+    Utf8 "InnerClasses"; // #45     at 0x028F
+    class #47; // #46     at 0x029E
+    Utf8 "java/lang/invoke/MethodHandles$Lookup"; // #47     at 0x02A1
+    class #49; // #48     at 0x02C9
+    Utf8 "java/lang/invoke/MethodHandles"; // #49     at 0x02CC
+    Utf8 "Lookup"; // #50     at 0x02ED
+  } // Constant Pool
+
+  0x0051; // access [ ACC_VALUE ACC_PUBLIC ACC_FINAL ]
+  #1;// this_cpx
+  #22;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // fields
+    { // Member at 0x0302
+      0x0010; // access
+      #5; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x030A
+      0x0010; // access
+      #9; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [6] { // methods
+    { // Member at 0x0314
+      0x0001; // access
+      #26; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x031C
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40003AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x0333
+              [1] { // LineNumberTable
+                0  10; //  at 0x033F
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x033F
+      0x0001; // access
+      #30; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x0347
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40007AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x035E
+              [1] { // LineNumberTable
+                0  11; //  at 0x036A
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x036A
+      0x0011; // access
+      #12; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x0372
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA000A0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x038B
+              [1] { // LineNumberTable
+                0  1; //  at 0x0397
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0397
+      0x0011; // access
+      #16; // name_cpx
+      #31; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 32) { // Code at 0x039F
+          2; // max_stack
+          2; // max_locals
+          Bytes[8]{
+            0x2A2BBA000E0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03B9
+              [1] { // LineNumberTable
+                0  1; //  at 0x03C5
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03C5
+      0x0011; // access
+      #20; // name_cpx
+      #32; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x03CD
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00120000B0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03E6
+              [1] { // LineNumberTable
+                0  1; //  at 0x03F2
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03F2
+      0x000A; // access
+      #33; // name_cpx
+      #34; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 56) { // Code at 0x03FA
+          2; // max_stack
+          1; // max_locals
+          Bytes[20]{
+            0xCB00014B032A5FCC;
+            0x00034B032A5FCC00;
+            0x074B2AB0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 18) { // LineNumberTable at 0x0420
+              [4] { // LineNumberTable
+                0  5; //  at 0x042C
+                4  6; //  at 0x0430
+                11  7; //  at 0x0434
+                18  8; //  at 0x0438
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [3] { // Attributes
+    Attr(#35, 2) { // SourceFile at 0x043A
+      #36;
+    } // end SourceFile
+    ;
+    Attr(#37, 6) { // BootstrapMethods at 0x0442
+      [1] { // bootstrap_methods
+        {  //  bootstrap_method
+          #38; // bootstrap_method_ref
+          [0] { // bootstrap_arguments
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+      }
+    } // end BootstrapMethods
+    ;
+    Attr(#45, 10) { // InnerClasses at 0x044E
+      [1] { // InnerClasses
+        #46 #48 #50 25; //  at 0x045E
+      }
+    } // end InnerClasses
+  } // Attributes
+} // end class ValueSuperNotAbstract
+
+
+
+// Value class with a super class that has instance fields.
+class ValueSuperHasNonStaticFields {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [51] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1     at 0x0A
+    Utf8 "ValueSuperHasNonStaticFields"; // #2     at 0x0D
+    Field #1 #4; // #3     at 0x20
+    NameAndType #5 #6; // #4     at 0x25
+    Utf8 "x"; // #5     at 0x2A
+    Utf8 "I"; // #6     at 0x2E
+    Field #1 #8; // #7     at 0x32
+    NameAndType #9 #6; // #8     at 0x37
+    Utf8 "y"; // #9     at 0x3C
+    InvokeDynamic 0s #11; // #10     at 0x40
+    NameAndType #12 #13; // #11     at 0x45
+    Utf8 "hashCode"; // #12     at 0x4A
+    Utf8 "(LValueSuperHasNonStaticFields;)I"; // #13     at 0x55
+    InvokeDynamic 0s #15; // #14     at 0x6D
+    NameAndType #16 #17; // #15     at 0x72
+    Utf8 "equals"; // #16     at 0x77
+    Utf8 "(LValueSuperHasNonStaticFields;Ljava/lang/Object;)Z"; // #17     at 0x80
+    InvokeDynamic 0s #19; // #18     at 0xAA
+    NameAndType #20 #21; // #19     at 0xAF
+    Utf8 "toString"; // #20     at 0xB4
+    Utf8 "(LValueSuperHasNonStaticFields;)Ljava/lang/String;"; // #21     at 0xBF
+    class #23; // #22     at 0xE8
+    Utf8 "HasNonStaticFields"; // #23     at 0xEB
+    class #25; // #24     at 0xF9
+    Utf8 "Unused"; // #25     at 0xFC
+    Utf8 "getX"; // #26     at 0x0115
+    Utf8 "()I"; // #27     at 0x011C
+    Utf8 "Code"; // #28     at 0x0122
+    Utf8 "LineNumberTable"; // #29     at 0x0129
+    Utf8 "getY"; // #30     at 0x013B
+    Utf8 "(Ljava/lang/Object;)Z"; // #31     at 0x0142
+    Utf8 "()Ljava/lang/String;"; // #32     at 0x015A
+    Utf8 "<vnew>"; // #33     at 0x0171
+    Utf8 "()LValueSuperHasNonStaticFields;"; // #34     at 0x017A
+    Utf8 "SourceFile"; // #35     at 0x0191
+    Utf8 "ValueSuperHasNonStaticFields.java"; // #36     at 0x019E
+    Utf8 "BootstrapMethods"; // #37     at 0x01B6
+    MethodHandle 6b #39; // #38     at 0x01C9
+    Method #40 #41; // #39     at 0x01CD
+    class #42; // #40     at 0x01D2
+    NameAndType #43 #44; // #41     at 0x01D5
+    Utf8 "java/lang/invoke/ValueBootstrapMethods"; // #42     at 0x01DA
+    Utf8 "makeBootstrapMethod"; // #43     at 0x0203
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; // #44     at 0x0219
+    Utf8 "InnerClasses"; // #45     at 0x028F
+    class #47; // #46     at 0x029E
+    Utf8 "java/lang/invoke/MethodHandles$Lookup"; // #47     at 0x02A1
+    class #49; // #48     at 0x02C9
+    Utf8 "java/lang/invoke/MethodHandles"; // #49     at 0x02CC
+    Utf8 "Lookup"; // #50     at 0x02ED
+  } // Constant Pool
+
+  0x0051; // access [ ACC_VALUE ACC_PUBLIC ACC_FINAL ]
+  #1;// this_cpx
+  #22;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // fields
+    { // Member at 0x0302
+      0x0010; // access
+      #5; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x030A
+      0x0010; // access
+      #9; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [6] { // methods
+    { // Member at 0x0314
+      0x0001; // access
+      #26; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x031C
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40003AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x0333
+              [1] { // LineNumberTable
+                0  10; //  at 0x033F
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x033F
+      0x0001; // access
+      #30; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x0347
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40007AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x035E
+              [1] { // LineNumberTable
+                0  11; //  at 0x036A
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x036A
+      0x0011; // access
+      #12; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x0372
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA000A0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x038B
+              [1] { // LineNumberTable
+                0  1; //  at 0x0397
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0397
+      0x0011; // access
+      #16; // name_cpx
+      #31; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 32) { // Code at 0x039F
+          2; // max_stack
+          2; // max_locals
+          Bytes[8]{
+            0x2A2BBA000E0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03B9
+              [1] { // LineNumberTable
+                0  1; //  at 0x03C5
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03C5
+      0x0011; // access
+      #20; // name_cpx
+      #32; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x03CD
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00120000B0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03E6
+              [1] { // LineNumberTable
+                0  1; //  at 0x03F2
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03F2
+      0x000A; // access
+      #33; // name_cpx
+      #34; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 56) { // Code at 0x03FA
+          2; // max_stack
+          1; // max_locals
+          Bytes[20]{
+            0xCB00014B032A5FCC;
+            0x00034B032A5FCC00;
+            0x074B2AB0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 18) { // LineNumberTable at 0x0420
+              [4] { // LineNumberTable
+                0  5; //  at 0x042C
+                4  6; //  at 0x0430
+                11  7; //  at 0x0434
+                18  8; //  at 0x0438
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [3] { // Attributes
+    Attr(#35, 2) { // SourceFile at 0x043A
+      #36;
+    } // end SourceFile
+    ;
+    Attr(#37, 6) { // BootstrapMethods at 0x0442
+      [1] { // bootstrap_methods
+        {  //  bootstrap_method
+          #38; // bootstrap_method_ref
+          [0] { // bootstrap_arguments
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+      }
+    } // end BootstrapMethods
+    ;
+    Attr(#45, 10) { // InnerClasses at 0x044E
+      [1] { // InnerClasses
+        #46 #48 #50 25; //  at 0x045E
+      }
+    } // end InnerClasses
+  } // Attributes
+} // end class ValueSuperHasNonStaticFields
+
+
+
+// primitive value class with a super class whose super class has a syncronized instance method.
+class ValueSuperHasSynchMethod {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [51] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1     at 0x0A
+    Utf8 "ValueSuperHasSynchMethod"; // #2     at 0x0D
+    Field #1 #4; // #3     at 0x20
+    NameAndType #5 #6; // #4     at 0x25
+    Utf8 "x"; // #5     at 0x2A
+    Utf8 "I"; // #6     at 0x2E
+    Field #1 #8; // #7     at 0x32
+    NameAndType #9 #6; // #8     at 0x37
+    Utf8 "y"; // #9     at 0x3C
+    InvokeDynamic 0s #11; // #10     at 0x40
+    NameAndType #12 #13; // #11     at 0x45
+    Utf8 "hashCode"; // #12     at 0x4A
+    Utf8 "(LValueSuperHasSynchMethod;)I"; // #13     at 0x55
+    InvokeDynamic 0s #15; // #14     at 0x6D
+    NameAndType #16 #17; // #15     at 0x72
+    Utf8 "equals"; // #16     at 0x77
+    Utf8 "(LValueSuperHasSynchMethod;Ljava/lang/Object;)Z"; // #17     at 0x80
+    InvokeDynamic 0s #19; // #18     at 0xAA
+    NameAndType #20 #21; // #19     at 0xAF
+    Utf8 "toString"; // #20     at 0xB4
+    Utf8 "(LValueSuperHasSynchMethod;)Ljava/lang/String;"; // #21     at 0xBF
+    class #23; // #22     at 0xE8
+    Utf8 "ValidSuper"; // #23     at 0xEB
+    class #25; // #24     at 0xF9
+    Utf8 "Unused"; // #25     at 0xFC
+    Utf8 "getX"; // #26     at 0x0115
+    Utf8 "()I"; // #27     at 0x011C
+    Utf8 "Code"; // #28     at 0x0122
+    Utf8 "LineNumberTable"; // #29     at 0x0129
+    Utf8 "getY"; // #30     at 0x013B
+    Utf8 "(Ljava/lang/Object;)Z"; // #31     at 0x0142
+    Utf8 "()Ljava/lang/String;"; // #32     at 0x015A
+    Utf8 "<vnew>"; // #33     at 0x0171
+    Utf8 "()LValueSuperHasSynchMethod;"; // #34     at 0x017A
+    Utf8 "SourceFile"; // #35     at 0x0191
+    Utf8 "ValueSuperHasSynchMethod.java"; // #36     at 0x019E
+    Utf8 "BootstrapMethods"; // #37     at 0x01B6
+    MethodHandle 6b #39; // #38     at 0x01C9
+    Method #40 #41; // #39     at 0x01CD
+    class #42; // #40     at 0x01D2
+    NameAndType #43 #44; // #41     at 0x01D5
+    Utf8 "java/lang/invoke/ValueBootstrapMethods"; // #42     at 0x01DA
+    Utf8 "makeBootstrapMethod"; // #43     at 0x0203
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; // #44     at 0x0219
+    Utf8 "InnerClasses"; // #45     at 0x028F
+    class #47; // #46     at 0x029E
+    Utf8 "java/lang/invoke/MethodHandles$Lookup"; // #47     at 0x02A1
+    class #49; // #48     at 0x02C9
+    Utf8 "java/lang/invoke/MethodHandles"; // #49     at 0x02CC
+    Utf8 "Lookup"; // #50     at 0x02ED
+  } // Constant Pool
+
+  0x0051; // access [ ACC_VALUE ACC_PUBLIC ACC_FINAL ]
+  #1;// this_cpx
+  #22;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // fields
+    { // Member at 0x0302
+      0x0010; // access
+      #5; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x030A
+      0x0010; // access
+      #9; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [6] { // methods
+    { // Member at 0x0314
+      0x0001; // access
+      #26; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x031C
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40003AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x0333
+              [1] { // LineNumberTable
+                0  10; //  at 0x033F
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x033F
+      0x0001; // access
+      #30; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x0347
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40007AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x035E
+              [1] { // LineNumberTable
+                0  11; //  at 0x036A
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x036A
+      0x0011; // access
+      #12; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x0372
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA000A0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x038B
+              [1] { // LineNumberTable
+                0  1; //  at 0x0397
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0397
+      0x0011; // access
+      #16; // name_cpx
+      #31; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 32) { // Code at 0x039F
+          2; // max_stack
+          2; // max_locals
+          Bytes[8]{
+            0x2A2BBA000E0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03B9
+              [1] { // LineNumberTable
+                0  1; //  at 0x03C5
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03C5
+      0x0011; // access
+      #20; // name_cpx
+      #32; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x03CD
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00120000B0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03E6
+              [1] { // LineNumberTable
+                0  1; //  at 0x03F2
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03F2
+      0x000A; // access
+      #33; // name_cpx
+      #34; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 56) { // Code at 0x03FA
+          2; // max_stack
+          1; // max_locals
+          Bytes[20]{
+            0xCB00014B032A5FCC;
+            0x00034B032A5FCC00;
+            0x074B2AB0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 18) { // LineNumberTable at 0x0420
+              [4] { // LineNumberTable
+                0  5; //  at 0x042C
+                4  6; //  at 0x0430
+                11  7; //  at 0x0434
+                18  8; //  at 0x0438
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [3] { // Attributes
+    Attr(#35, 2) { // SourceFile at 0x043A
+      #36;
+    } // end SourceFile
+    ;
+    Attr(#37, 6) { // BootstrapMethods at 0x0442
+      [1] { // bootstrap_methods
+        {  //  bootstrap_method
+          #38; // bootstrap_method_ref
+          [0] { // bootstrap_arguments
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+      }
+    } // end BootstrapMethods
+    ;
+    Attr(#45, 10) { // InnerClasses at 0x044E
+      [1] { // InnerClasses
+        #46 #48 #50 25; //  at 0x045E
+      }
+    } // end InnerClasses
+  } // Attributes
+} // end class ValueSuperHasSynchMethod
+
+
+// primitive value class with a super class containing a constructor that has arguments.
+class ValueSuperCtorHasArgs {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [51] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1     at 0x0A
+    Utf8 "ValueSuperCtorHasArgs"; // #2     at 0x0D
+    Field #1 #4; // #3     at 0x20
+    NameAndType #5 #6; // #4     at 0x25
+    Utf8 "x"; // #5     at 0x2A
+    Utf8 "I"; // #6     at 0x2E
+    Field #1 #8; // #7     at 0x32
+    NameAndType #9 #6; // #8     at 0x37
+    Utf8 "y"; // #9     at 0x3C
+    InvokeDynamic 0s #11; // #10     at 0x40
+    NameAndType #12 #13; // #11     at 0x45
+    Utf8 "hashCode"; // #12     at 0x4A
+    Utf8 "(LValueSuperCtorHasArgs;)I"; // #13     at 0x55
+    InvokeDynamic 0s #15; // #14     at 0x6D
+    NameAndType #16 #17; // #15     at 0x72
+    Utf8 "equals"; // #16     at 0x77
+    Utf8 "(LValueSuperCtorHasArgs;Ljava/lang/Object;)Z"; // #17     at 0x80
+    InvokeDynamic 0s #19; // #18     at 0xAA
+    NameAndType #20 #21; // #19     at 0xAF
+    Utf8 "toString"; // #20     at 0xB4
+    Utf8 "(LValueSuperCtorHasArgs;)Ljava/lang/String;"; // #21     at 0xBF
+    class #23; // #22     at 0xE8
+    Utf8 "CtorHasArgs"; // #23     at 0xEB
+    class #25; // #24     at 0xF9
+    Utf8 "Unused"; // #25     at 0xFC
+    Utf8 "getX"; // #26     at 0x0115
+    Utf8 "()I"; // #27     at 0x011C
+    Utf8 "Code"; // #28     at 0x0122
+    Utf8 "LineNumberTable"; // #29     at 0x0129
+    Utf8 "getY"; // #30     at 0x013B
+    Utf8 "(Ljava/lang/Object;)Z"; // #31     at 0x0142
+    Utf8 "()Ljava/lang/String;"; // #32     at 0x015A
+    Utf8 "<vnew>"; // #33     at 0x0171
+    Utf8 "()LValueSuperCtorHasArgs;"; // #34     at 0x017A
+    Utf8 "SourceFile"; // #35     at 0x0191
+    Utf8 "ValueSuperCtorHasArgs.java"; // #36     at 0x019E
+    Utf8 "BootstrapMethods"; // #37     at 0x01B6
+    MethodHandle 6b #39; // #38     at 0x01C9
+    Method #40 #41; // #39     at 0x01CD
+    class #42; // #40     at 0x01D2
+    NameAndType #43 #44; // #41     at 0x01D5
+    Utf8 "java/lang/invoke/ValueBootstrapMethods"; // #42     at 0x01DA
+    Utf8 "makeBootstrapMethod"; // #43     at 0x0203
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; // #44     at 0x0219
+    Utf8 "InnerClasses"; // #45     at 0x028F
+    class #47; // #46     at 0x029E
+    Utf8 "java/lang/invoke/MethodHandles$Lookup"; // #47     at 0x02A1
+    class #49; // #48     at 0x02C9
+    Utf8 "java/lang/invoke/MethodHandles"; // #49     at 0x02CC
+    Utf8 "Lookup"; // #50     at 0x02ED
+  } // Constant Pool
+
+  0x0051; // access [ ACC_VALUE ACC_PUBLIC ACC_FINAL ]
+  #1;// this_cpx
+  #22;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // fields
+    { // Member at 0x0302
+      0x0010; // access
+      #5; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x030A
+      0x0010; // access
+      #9; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [6] { // methods
+    { // Member at 0x0314
+      0x0001; // access
+      #26; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x031C
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40003AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x0333
+              [1] { // LineNumberTable
+                0  10; //  at 0x033F
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x033F
+      0x0001; // access
+      #30; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x0347
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40007AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x035E
+              [1] { // LineNumberTable
+                0  11; //  at 0x036A
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x036A
+      0x0011; // access
+      #12; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x0372
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA000A0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x038B
+              [1] { // LineNumberTable
+                0  1; //  at 0x0397
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0397
+      0x0011; // access
+      #16; // name_cpx
+      #31; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 32) { // Code at 0x039F
+          2; // max_stack
+          2; // max_locals
+          Bytes[8]{
+            0x2A2BBA000E0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03B9
+              [1] { // LineNumberTable
+                0  1; //  at 0x03C5
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03C5
+      0x0011; // access
+      #20; // name_cpx
+      #32; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x03CD
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00120000B0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03E6
+              [1] { // LineNumberTable
+                0  1; //  at 0x03F2
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03F2
+      0x000A; // access
+      #33; // name_cpx
+      #34; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 56) { // Code at 0x03FA
+          2; // max_stack
+          1; // max_locals
+          Bytes[20]{
+            0xCB00014B032A5FCC;
+            0x00034B032A5FCC00;
+            0x074B2AB0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 18) { // LineNumberTable at 0x0420
+              [4] { // LineNumberTable
+                0  5; //  at 0x042C
+                4  6; //  at 0x0430
+                11  7; //  at 0x0434
+                18  8; //  at 0x0438
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [3] { // Attributes
+    Attr(#35, 2) { // SourceFile at 0x043A
+      #36;
+    } // end SourceFile
+    ;
+    Attr(#37, 6) { // BootstrapMethods at 0x0442
+      [1] { // bootstrap_methods
+        {  //  bootstrap_method
+          #38; // bootstrap_method_ref
+          [0] { // bootstrap_arguments
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+      }
+    } // end BootstrapMethods
+    ;
+    Attr(#45, 10) { // InnerClasses at 0x044E
+      [1] { // InnerClasses
+        #46 #48 #50 25; //  at 0x045E
+      }
+    } // end InnerClasses
+  } // Attributes
+} // end class ValueSuperCtorHasArgs
+
+
+
+// primitive value class with a super class containing a constructor that has a non-empty
+// method body.
+class ValueSuperCtorIsNotEmpty {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [51] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1     at 0x0A
+    Utf8 "ValueSuperCtorIsNotEmpty"; // #2     at 0x0D
+    Field #1 #4; // #3     at 0x20
+    NameAndType #5 #6; // #4     at 0x25
+    Utf8 "x"; // #5     at 0x2A
+    Utf8 "I"; // #6     at 0x2E
+    Field #1 #8; // #7     at 0x32
+    NameAndType #9 #6; // #8     at 0x37
+    Utf8 "y"; // #9     at 0x3C
+    InvokeDynamic 0s #11; // #10     at 0x40
+    NameAndType #12 #13; // #11     at 0x45
+    Utf8 "hashCode"; // #12     at 0x4A
+    Utf8 "(LValueSuperCtorIsNotEmpty;)I"; // #13     at 0x55
+    InvokeDynamic 0s #15; // #14     at 0x6D
+    NameAndType #16 #17; // #15     at 0x72
+    Utf8 "equals"; // #16     at 0x77
+    Utf8 "(LValueSuperCtorIsNotEmpty;Ljava/lang/Object;)Z"; // #17     at 0x80
+    InvokeDynamic 0s #19; // #18     at 0xAA
+    NameAndType #20 #21; // #19     at 0xAF
+    Utf8 "toString"; // #20     at 0xB4
+    Utf8 "(LValueSuperCtorIsNotEmpty;)Ljava/lang/String;"; // #21     at 0xBF
+    class #23; // #22     at 0xE8
+    Utf8 "CtorIsNotEmpty"; // #23     at 0xEB
+    class #25; // #24     at 0xF9
+    Utf8 "Unused"; // #25     at 0xFC
+    Utf8 "getX"; // #26     at 0x0115
+    Utf8 "()I"; // #27     at 0x011C
+    Utf8 "Code"; // #28     at 0x0122
+    Utf8 "LineNumberTable"; // #29     at 0x0129
+    Utf8 "getY"; // #30     at 0x013B
+    Utf8 "(Ljava/lang/Object;)Z"; // #31     at 0x0142
+    Utf8 "()Ljava/lang/String;"; // #32     at 0x015A
+    Utf8 "<vnew>"; // #33     at 0x0171
+    Utf8 "()LValueSuperCtorIsNotEmpty;"; // #34     at 0x017A
+    Utf8 "SourceFile"; // #35     at 0x0191
+    Utf8 "ValueSuperCtorIsNotEmpty.java"; // #36     at 0x019E
+    Utf8 "BootstrapMethods"; // #37     at 0x01B6
+    MethodHandle 6b #39; // #38     at 0x01C9
+    Method #40 #41; // #39     at 0x01CD
+    class #42; // #40     at 0x01D2
+    NameAndType #43 #44; // #41     at 0x01D5
+    Utf8 "java/lang/invoke/ValueBootstrapMethods"; // #42     at 0x01DA
+    Utf8 "makeBootstrapMethod"; // #43     at 0x0203
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; // #44     at 0x0219
+    Utf8 "InnerClasses"; // #45     at 0x028F
+    class #47; // #46     at 0x029E
+    Utf8 "java/lang/invoke/MethodHandles$Lookup"; // #47     at 0x02A1
+    class #49; // #48     at 0x02C9
+    Utf8 "java/lang/invoke/MethodHandles"; // #49     at 0x02CC
+    Utf8 "Lookup"; // #50     at 0x02ED
+  } // Constant Pool
+
+  0x0051; // access [ ACC_VALUE ACC_PUBLIC ACC_FINAL ]
+  #1;// this_cpx
+  #22;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // fields
+    { // Member at 0x0302
+      0x0010; // access
+      #5; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x030A
+      0x0010; // access
+      #9; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [6] { // methods
+    { // Member at 0x0314
+      0x0001; // access
+      #26; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x031C
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40003AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x0333
+              [1] { // LineNumberTable
+                0  10; //  at 0x033F
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x033F
+      0x0001; // access
+      #30; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 29) { // Code at 0x0347
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40007AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x035E
+              [1] { // LineNumberTable
+                0  11; //  at 0x036A
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x036A
+      0x0011; // access
+      #12; // name_cpx
+      #27; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x0372
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA000A0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x038B
+              [1] { // LineNumberTable
+                0  1; //  at 0x0397
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0397
+      0x0011; // access
+      #16; // name_cpx
+      #31; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 32) { // Code at 0x039F
+          2; // max_stack
+          2; // max_locals
+          Bytes[8]{
+            0x2A2BBA000E0000AC;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03B9
+              [1] { // LineNumberTable
+                0  1; //  at 0x03C5
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03C5
+      0x0011; // access
+      #20; // name_cpx
+      #32; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 31) { // Code at 0x03CD
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00120000B0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 6) { // LineNumberTable at 0x03E6
+              [1] { // LineNumberTable
+                0  1; //  at 0x03F2
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03F2
+      0x000A; // access
+      #33; // name_cpx
+      #34; // sig_cpx
+      [1] { // Attributes
+        Attr(#28, 56) { // Code at 0x03FA
+          2; // max_stack
+          1; // max_locals
+          Bytes[20]{
+            0xCB00014B032A5FCC;
+            0x00034B032A5FCC00;
+            0x074B2AB0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#29, 18) { // LineNumberTable at 0x0420
+              [4] { // LineNumberTable
+                0  5; //  at 0x042C
+                4  6; //  at 0x0430
+                11  7; //  at 0x0434
+                18  8; //  at 0x0438
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [3] { // Attributes
+    Attr(#35, 2) { // SourceFile at 0x043A
+      #36;
+    } // end SourceFile
+    ;
+    Attr(#37, 6) { // BootstrapMethods at 0x0442
+      [1] { // bootstrap_methods
+        {  //  bootstrap_method
+          #38; // bootstrap_method_ref
+          [0] { // bootstrap_arguments
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+      }
+    } // end BootstrapMethods
+    ;
+    Attr(#45, 10) { // InnerClasses at 0x044E
+      [1] { // InnerClasses
+        #46 #48 #50 25; //  at 0x045E
+      }
+    } // end InnerClasses
+  } // Attributes
+} // end class ValueSuperCtorIsNotEmpty
+
+
+
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/NotAbstract.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/NotAbstract.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/NotAbstract.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2020, 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.
+ *
+ */
+
+public class NotAbstract { }
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/TestSuperClasses.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/TestSuperClasses.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/TestSuperClasses.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2020, 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.
+ *
+ */
+/*
+ * @ignore Fix JDK-8328419
+ * @test
+ * @bug 8243204
+ * @summary test that the JVM detects illegal super classes for value object
+ *           and primitive value types.
+ * @enablePreview
+ * @compile NotAbstract.java HasNonStaticFields.java CtorHasArgs.java CtorIsNotEmpty.java
+ * @compile HasSynchMethod.java ValidSuper.java
+ * @compile InlineClassWithBadSupers.jcod
+ * @run main/othervm -verify TestSuperClasses
+ */
+
+public class TestSuperClasses {
+
+    public static void runTestIncompatibleClassChangeError(String test_name, String message) throws Exception {
+        System.out.println("Testing: " + test_name);
+        try {
+            Class newClass = Class.forName(test_name);
+            throw new RuntimeException("Expected IncompatibleClassChangeError exception not thrown");
+        } catch (java.lang.IncompatibleClassChangeError e) {
+            if (!e.getMessage().contains(message)) {
+                throw new RuntimeException("Wrong IncompatibleClassChangeError: " + e.getMessage());
+            }
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+
+        // Value Objects...
+
+        // Test that the super class of an value type must be java.lang.Object or an abstract class.
+        runTestIncompatibleClassChangeError("ValueSuperNotAbstract",
+            "Value type ValueSuperNotAbstract has an identity type as supertype");
+
+        // Test that the super class of an value type cannot have instance fields.
+        runTestIncompatibleClassChangeError("ValueSuperHasNonStaticFields",
+            "Value type ValueSuperHasNonStaticFields has an identity type as supertype");
+
+        // Test that the super class of an value type cannot contain a synchronized instance method.
+        runTestIncompatibleClassChangeError("ValueSuperHasSynchMethod",
+            "Value type ValueSuperHasSynchMethod has an identity type as supertype");
+
+        // Test that the constructor in a super class of an value type must have a signature of "()V".
+        runTestIncompatibleClassChangeError("ValueSuperCtorHasArgs",
+            "Value type ValueSuperCtorHasArgs has an identity type as supertype");
+
+        // Test that the constructor in a super class of an value type must be empty.
+        runTestIncompatibleClassChangeError("ValueSuperCtorIsNotEmpty",
+            "Value type ValueSuperCtorIsNotEmpty has an identity type as supertype");
+
+        // Primitive values...
+
+        // Test that the super class of an primitive value type must be java.lang.Object or an abstract class.
+        runTestIncompatibleClassChangeError("PrimitiveSuperNotAbstract",
+            "Value type PrimitiveSuperNotAbstract has an identity type as supertype");
+
+        // Test that the super class of an primitive value type cannot have instance fields.
+        runTestIncompatibleClassChangeError("PrimitiveSuperHasNonStaticFields",
+            "Value type PrimitiveSuperHasNonStaticFields has an identity type as supertype");
+
+        // Test that the super class of an primitive value type cannot contain a synchronized instance method.
+        runTestIncompatibleClassChangeError("PrimitiveSuperHasSynchMethod",
+            "Value type PrimitiveSuperHasSynchMethod has an identity type as supertype");
+
+        // Test that the constructor in a super class of an primitive value type must have a signature of "()V".
+        runTestIncompatibleClassChangeError("PrimitiveSuperCtorHasArgs",
+            "Value type PrimitiveSuperCtorHasArgs has an identity type as supertype");
+
+        // Test that the constructor in a super class of an primitive value type must be empty.
+        runTestIncompatibleClassChangeError("PrimitiveSuperCtorIsNotEmpty",
+            "Value type PrimitiveSuperCtorIsNotEmpty has an identity type as supertype");
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/ValidSuper.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/ValidSuper.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/testSupers/ValidSuper.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2020, 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 abstract class is a valid super class for an inline type, but its
+// super class, HasSynchMethod, is not because it contains a synchronized
+// instance method.
+public abstract class ValidSuper extends HasSynchMethod {
+    public static String hi;
+    public static synchronized void helloWorld() { }
+    public void hi(int a) {
+        System.out.println("hi");
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/verifier/VTAssignability.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/verifier/VTAssignability.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/verifier/VTAssignability.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2018, 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.
+ */
+
+/*
+ * @ignore Fix JDK-8328438
+ * @test
+ * @summary Test basic verifier assignability of inline types.
+ * @enablePreview
+ * @compile VTAssignability.java
+ * @run main/othervm -Xverify:remote VTAssignability
+ */
+
+// Test that an inline type is assignable to itself, to java.lang.Object,
+// and to an interface,
+//
+interface II { }
+
+public primitive final class VTAssignability implements II {
+    final int x;
+    final int y;
+
+    public VTAssignability(int x, int y) {
+        this.x = x;
+        this.y = y;
+    }
+
+    public int getX() { return x; }
+    public int getY() { return y; }
+
+    public boolean isSameVTAssignability(VTAssignability that) {
+        return this.getX() == that.getX() && this.getY() == that.getY();
+    }
+
+    public boolean equals(Object o) {
+        if(o instanceof VTAssignability) {
+            return ((VTAssignability)o).x == x &&  ((VTAssignability)o).y == y;
+        } else {
+            return false;
+        }
+    }
+
+    public void takesInterface(II i) {
+        System.out.println("Test passes!!");
+    }
+
+    public static void main(String[] args) {
+        VTAssignability a = new VTAssignability(3, 4);
+        VTAssignability b = new VTAssignability(2, 4);
+
+        // Test assignability of an inline type to itself.
+        boolean res = a.isSameVTAssignability(b);
+
+        // Test assignability of an inline type to java.lang.Object.
+        res = b.equals(a);
+
+        // Test assignability of an inline type to an interface.
+        a.takesInterface(b);
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/verifier/VTMonitor.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/verifier/VTMonitor.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/verifier/VTMonitor.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ *
+ * 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 8227373
+ * @summary Test that verifier allows monitor operations on inline types.
+ * @enablePreview
+ * @compile  VTMonitor.java
+ * @run main/othervm -Xverify:remote VTMonitor
+ */
+
+public value final class VTMonitor {
+    final int x;
+    final int y;
+
+    public VTMonitor(int x, int y) {
+        this.x = x;
+        this.y = y;
+    }
+
+    public static void main(String[] args) {
+        Object a = new VTMonitor(3, 4);
+        try {
+            synchronized(a) {
+                throw new RuntimeException("Synchronization on inline type should fail");
+            }
+        } catch (java.lang.IllegalMonitorStateException e) {
+            // Expected
+        }
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/verifier/VerifierInlineTypes.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/verifier/VerifierInlineTypes.java
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/verifier/VerifierInlineTypes.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ */
+/*
+ * @ignore Fix JDK-8328423
+ * @test
+ * @bug 8223028
+ * @summary test that the right exceptions get thrown for bad inline type
+ *          class files.
+ * @enablePreview
+ * @compile verifierTests.jcod
+ * @run main/othervm -verify VerifierInlineTypes
+ */
+
+public class VerifierInlineTypes {
+
+    public static void runTestVerifyError(String test_name, String message) throws Exception {
+        System.out.println("Testing: " + test_name);
+        try {
+            Class newClass = Class.forName(test_name);
+            throw new RuntimeException("Expected VerifyError exception not thrown");
+        } catch (java.lang.VerifyError e) {
+            if (!e.getMessage().contains(message)) {
+                throw new RuntimeException("Wrong VerifyError: " + e.getMessage());
+            }
+        }
+    }
+
+    public static void runTestFormatError(String test_name, String message) throws Exception {
+        System.out.println("Testing: " + test_name);
+        try {
+            Class newClass = Class.forName(test_name);
+            throw new RuntimeException("Expected ClassFormatError exception not thrown");
+        } catch (java.lang.ClassFormatError e) {
+            if (!e.getMessage().contains(message)) {
+                throw new RuntimeException("Wrong ClassFormatError: " + e.getMessage());
+            }
+        }
+    }
+
+    public static void runTestNoError(String test_name) throws Exception {
+        System.out.println("Testing: " + test_name);
+        Class newClass = Class.forName(test_name);
+    }
+
+    public static void main(String[] args) throws Exception {
+
+        // Test that a aconst_init opcode with an out of bounds cp index causes a VerifyError.
+        runTestVerifyError("defValBadCP", "Illegal constant pool index");
+
+        // Test that ClassFormatError is thrown for a class file, with major version 54, that
+        // contains a aconst_init opcode.
+        runTestFormatError("defValBadMajorVersion", "aconst_init not supported by this class file version");
+
+        // Test VerifyError is thrown if a aconst_init's cp entry is not a class.
+        runTestVerifyError("defValWrongCPType", "Illegal type at constant pool entry");
+
+        // Test that a withfield opcode with an out of bounds cp index causes a VerifyError.
+        runTestVerifyError("wthFldBadCP", "Illegal constant pool index");
+
+        // Test that VerifyError is thrown if the first operand on the stack is not assignable
+        // to withfield's field.
+        runTestVerifyError("wthFldBadFldVal", "Bad type on operand stack");
+
+        // Test that VerifyError is thrown if the second operand on the stack is a primitive.
+        runTestVerifyError("wthFldBadFldRef", "Bad type on operand stack");
+
+        // Test that ClassFormatError is thrown for a class file, with major version 54, that
+        // contains a withfield opcode.
+        runTestFormatError("wthFldBadMajorVersion", "withfield not supported by this class file version");
+
+        // Test VerifyError is thrown if a withfields's cp entry is not a field.
+        runTestVerifyError("wthFldWrongCPType", "Illegal type at constant pool entry");
+
+        // Test VerifyError is thrown if a aconst_init's cp entry is not an inline type.
+        runTestVerifyError("defValueObj", "Illegal type at constant pool entry 4");
+
+        // Test that the verifier doesn't require that a withfield bytecode has a Q type operand.
+        Class newClass = Class.forName("withfieldL");
+
+        // Test that null is not assignable to an inline type.
+        runTestVerifyError("NoNullVT",
+            "Type null (current frame, stack[1]) is not assignable to 'QNoNullVT;'");
+    }
+}
diff a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/verifier/verifierTests.jcod b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/verifier/verifierTests.jcod
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/verifier/verifierTests.jcod
@@ -0,0 +1,1608 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ */
+
+// Many of the jcod classes in this file were derived from this Java inline type:
+//
+// final inline class Value {
+//     static final Value VT = makeValue(0x01234567);
+//     final int int_v;
+//     Value() {
+//         int_v = 1;
+//     }
+//     static Value makeValue(int x) {
+//         Value v = Value.default;
+//         v = __WithField(v.int_v, x);
+//         return v;
+//     }
+// }
+//
+// The changes for each test were made to the bytecodes for method makeValue(int x).
+// Its bytecodes are:
+//
+//  static Value makeValue(int); descriptor: (I)LValue; flags: (0x0008) ACC_STATIC
+//    Code:
+//      stack=2, locals=2, args_size=1
+//         0: aconst_init   #3                  // class Value
+//         3: astore_1
+//         4: aload_1
+//         5: iload_0
+//         6: withfield     #2                  // Field int_v:I
+//         9: astore_1
+//        10: aload_1
+//        11: areturn
+
+
+// The constant pool index of the aconst_init opcode (0xCB) in the Code
+// attribute was changed to 0x93.  Since this index is outside the range of
+// the constant pool, a VerifyError exception should get thrown.
+//
+class defValBadCP {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [27] { // Constant Pool
+    ; // first element is empty
+    Method #7 #21; // #1     at 0x0A
+    Field #3 #22; // #2     at 0x0F
+    class #23; // #3     at 0x14
+    int 0x01234567; // #4     at 0x17
+    Method #3 #24; // #5     at 0x1C
+    Field #3 #25; // #6     at 0x21
+    class #26; // #7     at 0x26
+    Utf8 "VT"; // #8     at 0x29
+    Utf8 "LdefValBadCP;"; // #9     at 0x2E
+    Utf8 "int_v"; // #10     at 0x38
+    Utf8 "I"; // #11     at 0x40
+    Utf8 "<vnew>"; // #12     at 0x44
+    Utf8 "()V"; // #13     at 0x4D
+    Utf8 "Code"; // #14     at 0x53
+    Utf8 "LineNumberTable"; // #15     at 0x5A
+    Utf8 "makeValue"; // #16     at 0x6C
+    Utf8 "(I)LdefValBadCP;"; // #17     at 0x78
+    Utf8 "<clinit>"; // #18     at 0x85
+    Utf8 "SourceFile"; // #19     at 0x90
+    Utf8 "defValBadCP.java"; // #20     at 0x9D
+    NameAndType #12 #13; // #21     at 0xAA
+    NameAndType #10 #11; // #22     at 0xAF
+    Utf8 "defValBadCP"; // #23     at 0xB4
+    NameAndType #16 #17; // #24     at 0xBC
+    NameAndType #8 #9; // #25     at 0xC1
+    Utf8 "java/lang/Object"; // #26     at 0xC6
+  } // Constant Pool
+
+  0x0850; // access [ ACC_VALUE ACC_PRIMITIVE ACC_FINAL ]
+  #3;// this_cpx
+  #7;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // fields
+    { // Member at 0xE3
+      0x0018; // access
+      #8; // name_cpx
+      #9; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0xEB
+      0x0010; // access
+      #10; // name_cpx
+      #11; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [1] { // methods
+    { // Member at 0x012D
+      0x0008; // access
+      #16; // name_cpx
+      #17; // sig_cpx
+      [1] { // Attributes
+        Attr(#14, 44) { // Code at 0x0135
+          2; // max_stack
+          2; // max_locals
+          Bytes[12]{
+            0xCB00934C2B1ACC00; // Changed CP index from 3 to 0x93 for opcode 0xCB (aconst_init)
+            0x024C2BB0;         // so that the index is outside of the range of the constant pool.
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#15, 14) { // LineNumberTable at 0x0153
+              [3] { // LineNumberTable
+                0  8; //  at 0x015F
+                4  9; //  at 0x0163
+                10  10; //  at 0x0167
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [1] { // Attributes
+    Attr(#19, 2) { // SourceFile at 0x0198
+      #20;
+    } // end SourceFile
+  } // Attributes
+} // end class defValBadCP
+
+///////////////////////////////////////////////////////////
+
+// The class's major version was changed to 54.  Since this class has a
+// aconst_init opcode (0xCB), this should cause a ClassFormatError
+// exception to get thrown.
+//
+class defValBadMajorVersion {
+  0xCAFEBABE;
+  0; // minor version
+  54; // version
+  [27] { // Constant Pool
+    ; // first element is empty
+    Method #7 #21; // #1     at 0x0A
+    Field #3 #22; // #2     at 0x0F
+    class #23; // #3     at 0x14
+    int 0x01234567; // #4     at 0x17
+    Method #3 #24; // #5     at 0x1C
+    Field #3 #25; // #6     at 0x21
+    class #26; // #7     at 0x26
+    Utf8 "VT"; // #8     at 0x29
+    Utf8 "LdefValBadMajorVersion;"; // #9     at 0x2E
+    Utf8 "int_v"; // #10     at 0x38
+    Utf8 "I"; // #11     at 0x40
+    Utf8 "<init>"; // #12     at 0x44
+    Utf8 "()V"; // #13     at 0x4D
+    Utf8 "Code"; // #14     at 0x53
+    Utf8 "LineNumberTable"; // #15     at 0x5A
+    Utf8 "makeValue"; // #16     at 0x6C
+    Utf8 "(I)LdefValBadMajorVersion;"; // #17     at 0x78
+    Utf8 "<clinit>"; // #18     at 0x85
+    Utf8 "SourceFile"; // #19     at 0x90
+    Utf8 "defValBadMajorVersion.java"; // #20     at 0x9D
+    NameAndType #12 #13; // #21     at 0xAA
+    NameAndType #10 #11; // #22     at 0xAF
+    Utf8 "defValBadMajorVersion"; // #23     at 0xB4
+    NameAndType #16 #17; // #24     at 0xBC
+    NameAndType #8 #9; // #25     at 0xC1
+    Utf8 "java/lang/Object"; // #26     at 0xC6
+  } // Constant Pool
+
+  0x0850; // access [ ACC_VALUE ACC_PRIMITIVE ACC_FINAL ]
+  #3;// this_cpx
+  #7;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // fields
+    { // Member at 0xE3
+      0x0018; // access
+      #8; // name_cpx
+      #9; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0xEB
+      0x0010; // access
+      #10; // name_cpx
+      #11; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [1] { // methods
+    { // Member at 0x012D
+      0x0008; // access
+      #16; // name_cpx
+      #17; // sig_cpx
+      [1] { // Attributes
+        Attr(#14, 44) { // Code at 0x0135
+          2; // max_stack
+          2; // max_locals
+          Bytes[12]{
+            0xCB00034C2B1ACC00;
+            0x024C2BB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#15, 14) { // LineNumberTable at 0x0153
+              [3] { // LineNumberTable
+                0  8; //  at 0x015F
+                4  9; //  at 0x0163
+                10  10; //  at 0x0167
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [1] { // Attributes
+    Attr(#19, 2) { // SourceFile at 0x0198
+      #20;
+    } // end SourceFile
+  } // Attributes
+} // end class defValBadMajorVersion
+
+///////////////////////////////////////////////////////////
+
+// The constant pool index of a aconst_init opcode (0xCB) in the Code
+// attribute was changed to 2.  Since this index now points to a Field
+// entry instead of a Class entry, a VerifyError exception should get thrown.
+//
+class defValWrongCPType {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [27] { // Constant Pool
+    ; // first element is empty
+    Method #7 #21; // #1     at 0x0A
+    Field #3 #22; // #2     at 0x0F
+    class #23; // #3     at 0x14
+    int 0x01234567; // #4     at 0x17
+    Method #3 #24; // #5     at 0x1C
+    Field #3 #25; // #6     at 0x21
+    class #26; // #7     at 0x26
+    Utf8 "VT"; // #8     at 0x29
+    Utf8 "LdefValWrongCPType;"; // #9     at 0x2E
+    Utf8 "int_v"; // #10     at 0x38
+    Utf8 "I"; // #11     at 0x40
+    Utf8 "<vnew>"; // #12     at 0x44
+    Utf8 "()V"; // #13     at 0x4D
+    Utf8 "Code"; // #14     at 0x53
+    Utf8 "LineNumberTable"; // #15     at 0x5A
+    Utf8 "makeValue"; // #16     at 0x6C
+    Utf8 "(I)LdefValWrongCPType;"; // #17     at 0x78
+    Utf8 "<clinit>"; // #18     at 0x85
+    Utf8 "SourceFile"; // #19     at 0x90
+    Utf8 "defValWrongCPType.java"; // #20     at 0x9D
+    NameAndType #12 #13; // #21     at 0xAA
+    NameAndType #10 #11; // #22     at 0xAF
+    Utf8 "defValWrongCPType"; // #23     at 0xB4
+    NameAndType #16 #17; // #24     at 0xBC
+    NameAndType #8 #9; // #25     at 0xC1
+    Utf8 "java/lang/Object"; // #26     at 0xC6
+  } // Constant Pool
+
+  0x0850; // access [ ACC_VALUE ACC_PRIMITIVE ACC_FINAL ]
+  #3;// this_cpx
+  #7;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // fields
+    { // Member at 0xE3
+      0x0018; // access
+      #8; // name_cpx
+      #9; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0xEB
+      0x0010; // access
+      #10; // name_cpx
+      #11; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [1] { // methods
+    { // Member at 0x012D
+      0x0008; // access
+      #16; // name_cpx
+      #17; // sig_cpx
+      [1] { // Attributes
+        Attr(#14, 44) { // Code at 0x0135
+          2; // max_stack
+          2; // max_locals
+          Bytes[12]{
+            0xCB00024C2B1ACC00; // Changed CP index from 3 to 2 for opcode 0xCB (aconst_init)
+            0x024C2BB0;         // so that the cp index no longer points to a cp Class entry.
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#15, 14) { // LineNumberTable at 0x0153
+              [3] { // LineNumberTable
+                0  8; //  at 0x015F
+                4  9; //  at 0x0163
+                10  10; //  at 0x0167
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [1] { // Attributes
+    Attr(#19, 2) { // SourceFile at 0x0198
+      #20;
+    } // end SourceFile
+  } // Attributes
+} // end class defValWrongCPType
+
+///////////////////////////////////////////////////////////
+
+// The constant pool index of the withfield opcode (0xCC) in the Code
+// attribute was changed to 0x82.  Since this index is outside the range of
+// the constant pool, a VerifyError exception should get thrown.
+//
+class wthFldBadCP {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [20] { // Constant Pool
+    ; // first element is empty
+    Method #4 #17; // #1     at 0x0A
+    Field #3 #18; // #2     at 0x0F
+    class #12; // #3     at 0x14
+    class #19; // #4     at 0x17
+    Utf8 "int_v"; // #5     at 0x1A
+    Utf8 "I"; // #6     at 0x22
+    Utf8 "<vnew>"; // #7     at 0x26
+    Utf8 "()V"; // #8     at 0x2F
+    Utf8 "Code"; // #9     at 0x35
+    Utf8 "LineNumberTable"; // #10     at 0x3C
+    Utf8 "makewthFldBadCP"; // #11     at 0x4E
+    Utf8 "wthFldBadCP"; // #12     at 0x60
+    Utf8 "ValueTypes"; // #13     at 0x6E
+    Utf8 "(I)LwthFldBadCP;"; // #14     at 0x7B
+    Utf8 "SourceFile"; // #15     at 0x8E
+    Utf8 "wthFldBadCP.java"; // #16     at 0x9B
+    NameAndType #7 #8; // #17     at 0xAE
+    NameAndType #5 #6; // #18     at 0xB3
+    Utf8 "java/lang/Object"; // #19     at 0xB8
+  } // Constant Pool
+
+  0x0850; // access [ ACC_VALUE ACC_PRIMITIVE ACC_FINAL ]
+  #3;// this_cpx
+  #4;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [1] { // fields
+    { // Member at 0xD5
+      0x0010; // access
+      #5; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [1] { // methods
+    { // Member at 0x0117
+      0x0008; // access
+      #11; // name_cpx
+      #14; // sig_cpx
+      [1] { // Attributes
+        Attr(#9, 44) { // Code at 0x011F
+          2; // max_stack
+          2; // max_locals
+          Bytes[12]{
+            0xCB00034C2B1ACC00; // Changed CP index from 2 to 0x82 for opcode 0xCC (withfield)
+            0x824C2BB0;         // so that the index is outside of the range of the constant pool.
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#10, 14) { // LineNumberTable at 0x013D
+              [3] { // LineNumberTable
+                0  8; //  at 0x0149
+                4  9; //  at 0x014D
+                10  10; //  at 0x0151
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [2] { // Attributes
+    Attr(#15, 2) { // SourceFile at 0x0153
+      #16;
+    } // end SourceFile
+    ;
+    Attr(#13, 4) { // ValueTypes at 0x015B
+      0x00010003;
+    } // end ValueTypes
+  } // Attributes
+} // end class wthFldBadCP
+
+///////////////////////////////////////////////////////////
+
+// The opcode at bytecode position 5 in the Code array was changed to aload_1
+// (0x2B).  This should cause a VerifyError because now the first operand on the
+// stack for the withfield opcode (0xCC at bytecode position 6) does not match
+// the type (int) of the field being assigned to.
+//
+class wthFldBadFldVal {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [20] { // Constant Pool
+    ; // first element is empty
+    Method #4 #17; // #1     at 0x0A
+    Field #3 #18; // #2     at 0x0F
+    class #12; // #3     at 0x14
+    class #19; // #4     at 0x17
+    Utf8 "int_v"; // #5     at 0x1A
+    Utf8 "I"; // #6     at 0x22
+    Utf8 "<vnew>"; // #7     at 0x26
+    Utf8 "()V"; // #8     at 0x2F
+    Utf8 "Code"; // #9     at 0x35
+    Utf8 "LineNumberTable"; // #10     at 0x3C
+    Utf8 "makewthFldBadFldVal"; // #11     at 0x4E
+    Utf8 "wthFldBadFldVal"; // #12     at 0x60
+    Utf8 "ValueTypes"; // #13     at 0x6E
+    Utf8 "(I)LwthFldBadFldVal;"; // #14     at 0x7B
+    Utf8 "SourceFile"; // #15     at 0x8E
+    Utf8 "wthFldBadFldVal.java"; // #16     at 0x9B
+    NameAndType #7 #8; // #17     at 0xAE
+    NameAndType #5 #6; // #18     at 0xB3
+    Utf8 "java/lang/Object"; // #19     at 0xB8
+  } // Constant Pool
+
+  0x0850; // access [ ACC_VALUE ACC_PRIMITIVE ACC_FINAL ]
+  #3;// this_cpx
+  #4;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [1] { // fields
+    { // Member at 0xD5
+      0x0010; // access
+      #5; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [1] { // methods
+    { // Member at 0x0117
+      0x0008; // access
+      #11; // name_cpx
+      #14; // sig_cpx
+      [1] { // Attributes
+        Attr(#9, 44) { // Code at 0x011F
+          2; // max_stack
+          2; // max_locals
+          Bytes[12]{
+            0xCB00034C2B2BCC00; // Changed opcode at bytecode 5 from iload_0 to aload_1
+            0x024C2BB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#10, 14) { // LineNumberTable at 0x013D
+              [3] { // LineNumberTable
+                0  8; //  at 0x0149
+                4  9; //  at 0x014D
+                10  10; //  at 0x0151
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [2] { // Attributes
+    Attr(#15, 2) { // SourceFile at 0x0153
+      #16;
+    } // end SourceFile
+    ;
+    Attr(#13, 4) { // ValueTypes at 0x015B
+      0x00010003;
+    } // end ValueTypes
+  } // Attributes
+} // end class wthFldBadFldVal
+
+///////////////////////////////////////////////////////////
+
+// The opcode at bytecode position 4 in the Code array was changed to iload_1
+// (0x1A).  This should cause a VerifyError because the second operand on the stack
+// for the withfield opcode (0xCC at bytecode position 6) must be a reference.
+//
+class wthFldBadFldRef {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [20] { // Constant Pool
+    ; // first element is empty
+    Method #4 #17; // #1     at 0x0A
+    Field #3 #18; // #2     at 0x0F
+    class #12; // #3     at 0x14
+    class #19; // #4     at 0x17
+    Utf8 "int_v"; // #5     at 0x1A
+    Utf8 "I"; // #6     at 0x22
+    Utf8 "<vnew>"; // #7     at 0x26
+    Utf8 "()V"; // #8     at 0x2F
+    Utf8 "Code"; // #9     at 0x35
+    Utf8 "LineNumberTable"; // #10     at 0x3C
+    Utf8 "makewthFldBadFldRef"; // #11     at 0x4E
+    Utf8 "wthFldBadFldRef"; // #12     at 0x60
+    Utf8 "ValueTypes"; // #13     at 0x6E
+    Utf8 "(I)LwthFldBadFldRef;"; // #14     at 0x7B
+    Utf8 "SourceFile"; // #15     at 0x8E
+    Utf8 "wthFldBadFldRef.java"; // #16     at 0x9B
+    NameAndType #7 #8; // #17     at 0xAE
+    NameAndType #5 #6; // #18     at 0xB3
+    Utf8 "java/lang/Object"; // #19     at 0xB8
+  } // Constant Pool
+
+  0x0850; // access [ ACC_VALUE ACC_PRIMITIVE ACC_FINAL ]
+  #3;// this_cpx
+  #4;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [1] { // fields
+    { // Member at 0xD5
+      0x0010; // access
+      #5; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [1] { // methods
+    { // Member at 0x0117
+      0x0008; // access
+      #11; // name_cpx
+      #14; // sig_cpx
+      [1] { // Attributes
+        Attr(#9, 44) { // Code at 0x011F
+          2; // max_stack
+          2; // max_locals
+          Bytes[12]{
+            0xCB00034C1A1ACC00; // Changed opcode at bytecode 4 from aload_1 to iload_0
+            0x024C2BB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#10, 14) { // LineNumberTable at 0x013D
+              [3] { // LineNumberTable
+                0  8; //  at 0x0149
+                4  9; //  at 0x014D
+                10  10; //  at 0x0151
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [2] { // Attributes
+    Attr(#15, 2) { // SourceFile at 0x0153
+      #16;
+    } // end SourceFile
+    ;
+    Attr(#13, 4) { // ValueTypes at 0x015B
+      0x00010003;
+    } // end ValueTypes
+  } // Attributes
+} // end class wthFldBadFldRef
+
+///////////////////////////////////////////////////////////
+
+// The class's major version was changed to 54 and the first opcode in the Code
+// attribute was changed to a withfield (0xCC)..  Since withfield opcodes are not
+// allowed in classes with major version 54, this should cause a ClassFormatError
+// exception to get thrown.
+//
+class wthFldBadMajorVersion {
+  0xCAFEBABE;
+  0; // minor version
+  54; // version
+  [27] { // Constant Pool
+    ; // first element is empty
+    Method #7 #21; // #1     at 0x0A
+    Field #3 #22; // #2     at 0x0F
+    class #23; // #3     at 0x14
+    int 0x01234567; // #4     at 0x17
+    Method #3 #24; // #5     at 0x1C
+    Field #3 #25; // #6     at 0x21
+    class #26; // #7     at 0x26
+    Utf8 "VT"; // #8     at 0x29
+    Utf8 "LwthFldBadMajorVersion;"; // #9     at 0x2E
+    Utf8 "int_v"; // #10     at 0x38
+    Utf8 "I"; // #11     at 0x40
+    Utf8 "<init>"; // #12     at 0x44
+    Utf8 "()V"; // #13     at 0x4D
+    Utf8 "Code"; // #14     at 0x53
+    Utf8 "LineNumberTable"; // #15     at 0x5A
+    Utf8 "makeValue"; // #16     at 0x6C
+    Utf8 "(I)LwthFldBadMajorVersion;"; // #17     at 0x78
+    Utf8 "<clinit>"; // #18     at 0x85
+    Utf8 "SourceFile"; // #19     at 0x90
+    Utf8 "wthFldBadMajorVersion.java"; // #20     at 0x9D
+    NameAndType #12 #13; // #21     at 0xAA
+    NameAndType #10 #11; // #22     at 0xAF
+    Utf8 "wthFldBadMajorVersion"; // #23     at 0xB4
+    NameAndType #16 #17; // #24     at 0xBC
+    NameAndType #8 #9; // #25     at 0xC1
+    Utf8 "java/lang/Object"; // #26     at 0xC6
+  } // Constant Pool
+
+  0x0850; // access [ ACC_VALUE ACC_PRIMITIVE ACC_FINAL ]
+  #3;// this_cpx
+  #7;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // fields
+    { // Member at 0xE3
+      0x0018; // access
+      #8; // name_cpx
+      #9; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0xEB
+      0x0010; // access
+      #10; // name_cpx
+      #11; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [1] { // methods
+    { // Member at 0x012D
+      0x0008; // access
+      #16; // name_cpx
+      #17; // sig_cpx
+      [1] { // Attributes
+        Attr(#14, 44) { // Code at 0x0135
+          2; // max_stack
+          2; // max_locals
+          Bytes[12]{
+            0xCC00034C2B1ACC00; // Changed the first opcode to 0xCC (withfield) in order to
+            0x024C2BB0;         // test withfield opcode with an illegal major version.
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#15, 14) { // LineNumberTable at 0x0153
+              [3] { // LineNumberTable
+                0  8; //  at 0x015F
+                4  9; //  at 0x0163
+                10  10; //  at 0x0167
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [1] { // Attributes
+    Attr(#19, 2) { // SourceFile at 0x0198
+      #20;
+    } // end SourceFile
+  } // Attributes
+} // end class wthFldBadMajorVersion
+
+///////////////////////////////////////////////////////////
+
+// The constant pool index of a withfield opcode (0xCC) in the Code
+// attribute was changed to 1.  Since this index now points to a Method
+// entry instead of a Field entry, a VerifyError exception should get thrown.
+//
+class wthFldWrongCPType {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [20] { // Constant Pool
+    ; // first element is empty
+    Method #4 #17; // #1     at 0x0A
+    Field #3 #18; // #2     at 0x0F
+    class #12; // #3     at 0x14
+    class #19; // #4     at 0x17
+    Utf8 "int_v"; // #5     at 0x1A
+    Utf8 "I"; // #6     at 0x22
+    Utf8 "<vnew>"; // #7     at 0x26
+    Utf8 "()V"; // #8     at 0x2F
+    Utf8 "Code"; // #9     at 0x35
+    Utf8 "LineNumberTable"; // #10     at 0x3C
+    Utf8 "makewthFldWrongCPType"; // #11     at 0x4E
+    Utf8 "wthFldWrongCPType"; // #12     at 0x60
+    Utf8 "ValueTypes"; // #13     at 0x6E
+    Utf8 "(I)LwthFldWrongCPType;"; // #14     at 0x7B
+    Utf8 "SourceFile"; // #15     at 0x8E
+    Utf8 "wthFldWrongCPType.java"; // #16     at 0x9B
+    NameAndType #7 #8; // #17     at 0xAE
+    NameAndType #5 #6; // #18     at 0xB3
+    Utf8 "java/lang/Object"; // #19     at 0xB8
+  } // Constant Pool
+
+  0x0850; // access [ ACC_VALUE ACC_PRIMITIVE ACC_FINAL ]
+  #3;// this_cpx
+  #4;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [1] { // fields
+    { // Member at 0xD5
+      0x0010; // access
+      #5; // name_cpx
+      #6; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [1] { // methods
+    { // Member at 0x0117
+      0x0008; // access
+      #11; // name_cpx
+      #14; // sig_cpx
+      [1] { // Attributes
+        Attr(#9, 44) { // Code at 0x011F
+          2; // max_stack
+          2; // max_locals
+          Bytes[12]{
+            0xCB00034C2B1ACC00; // Changed CP index from 2 to 1 for opcode 0xCC (withfield)
+            0x014C2BB0;         // so that the cp index no longer points to a cp Field entry.
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#10, 14) { // LineNumberTable at 0x013D
+              [3] { // LineNumberTable
+                0  8; //  at 0x0149
+                4  9; //  at 0x014D
+                10  10; //  at 0x0151
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [2] { // Attributes
+    Attr(#15, 2) { // SourceFile at 0x0153
+      #16;
+    } // end SourceFile
+    ;
+    Attr(#13, 4) { // ValueTypes at 0x015B
+      0x00010003;
+    } // end ValueTypes
+  } // Attributes
+} // end class wthFldWrongCPType
+
+///////////////////////////////////////////////////////////
+
+// The cp entry for the aconst_init opcode was changed to a reference that
+// is not an inline type.
+// This should cause a VerifyError because the cp entry for opcode aconst_init
+// must be an inline type.
+//
+class defValueObj {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [46] { // Constant Pool
+    ; // first element is empty
+    class #23; // #1     at 0x0A
+    Field #1 #24; // #2     at 0x0D
+    InvokeDynamic 0s #27; // #3     at 0x12
+    InvokeDynamic 0s #28; // #4     at 0x17
+    InvokeDynamic 0s #29; // #5     at 0x1C
+    class #30; // #6     at 0x21
+    Utf8 "int_v"; // #7     at 0x24
+    Utf8 "I"; // #8     at 0x2C
+    Utf8 "makedefValueObj"; // #9     at 0x30
+    Utf8 "(I)QdefValueObj;"; // #10     at 0x41
+    Utf8 "Code"; // #11     at 0x53
+    Utf8 "LineNumberTable"; // #12     at 0x5A
+    Utf8 "hashCode"; // #13     at 0x6C
+    Utf8 "()I"; // #14     at 0x77
+    Utf8 "equals"; // #15     at 0x7D
+    Utf8 "(Ljava/lang/Object;)Z"; // #16     at 0x86
+    Utf8 "toString"; // #17     at 0x9E
+    Utf8 "()Ljava/lang/String;"; // #18     at 0xA9
+    Utf8 "<vnew>"; // #19     at 0xC0
+    Utf8 "()QdefValueObj;"; // #20     at 0xC9
+    Utf8 "SourceFile"; // #21     at 0xDA
+    Utf8 "defValueObj.java"; // #22     at 0xE7
+    Utf8 "defValueObj"; // #23     at 0xF9
+    NameAndType #7 #8; // #24     at 0x0106
+    Utf8 "BootstrapMethods"; // #25     at 0x010B
+    MethodHandle 6b #31; // #26     at 0x011E
+    NameAndType #13 #32; // #27     at 0x0122
+    NameAndType #15 #33; // #28     at 0x0127
+    NameAndType #17 #34; // #29     at 0x012C
+    Utf8 "java/lang/Object"; // #30     at 0x0131
+    Method #35 #36; // #31     at 0x0144
+    Utf8 "(QdefValueObj;)I"; // #32     at 0x0149
+    Utf8 "(QdefValueObj;Ljava/lang/Object;)Z"; // #33     at 0x015B
+    Utf8 "(QdefValueObj;)Ljava/lang/String;"; // #34     at 0x017F
+    class #37; // #35     at 0x01A2
+    NameAndType #38 #42; // #36     at 0x01A5
+    Utf8 "java/lang/invoke/ValueBootstrapMethods"; // #37     at 0x01AA
+    Utf8 "makeBootstrapMethod"; // #38     at 0x01D3
+    class #44; // #39     at 0x01E9
+    Utf8 "Lookup"; // #40     at 0x01EC
+    Utf8 "InnerClasses"; // #41     at 0x01F5
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; // #42     at 0x0204
+    class #45; // #43     at 0x027A
+    Utf8 "java/lang/invoke/MethodHandles$Lookup"; // #44     at 0x027D
+    Utf8 "java/lang/invoke/MethodHandles"; // #45     at 0x02A5
+  } // Constant Pool
+
+  0x0850; // access [ ACC_VALUE ACC_PRIMITIVE ACC_FINAL ]
+  #1;// this_cpx
+  #6;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [1] { // fields
+    { // Member at 0x02D0
+      0x0010; // access
+      #7; // name_cpx
+      #8; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [5] { // methods
+    { // Member at 0x02DA
+      0x0008; // access
+      #9; // name_cpx
+      #10; // sig_cpx
+      [1] { // Attributes
+        Attr(#11, 45) { // Code at 0x02E2
+          2; // max_stack
+          2; // max_locals
+          Bytes[13]{
+            0xCB00044C1A2B5FCC; // Changed aconst_init's cp index at byte 3 from 3 to 4.
+            0x00024C2BB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#12, 14) { // LineNumberTable at 0x0301
+              [3] { // LineNumberTable
+                0  8; //  at 0x030D
+                4  9; //  at 0x0311
+                11  10; //  at 0x0315
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0315
+      0x0011; // access
+      #13; // name_cpx
+      #14; // sig_cpx
+      [1] { // Attributes
+        Attr(#11, 31) { // Code at 0x031D
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00030000AC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#12, 6) { // LineNumberTable at 0x0336
+              [1] { // LineNumberTable
+                0  1; //  at 0x0342
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0342
+      0x0011; // access
+      #15; // name_cpx
+      #16; // sig_cpx
+      [1] { // Attributes
+        Attr(#11, 32) { // Code at 0x034A
+          2; // max_stack
+          2; // max_locals
+          Bytes[8]{
+            0x2A2BBA00040000AC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#12, 6) { // LineNumberTable at 0x0364
+              [1] { // LineNumberTable
+                0  1; //  at 0x0370
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0370
+      0x0011; // access
+      #17; // name_cpx
+      #18; // sig_cpx
+      [1] { // Attributes
+        Attr(#11, 31) { // Code at 0x0378
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00050000B0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#12, 6) { // LineNumberTable at 0x0391
+              [1] { // LineNumberTable
+                0  1; //  at 0x039D
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x039D
+      0x0008; // access
+      #19; // name_cpx
+      #20; // sig_cpx
+      [1] { // Attributes
+        Attr(#11, 45) { // Code at 0x03A5
+          2; // max_stack
+          1; // max_locals
+          Bytes[13]{
+            0xCB00014B042A5FCC;
+            0x00024B2AB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#12, 14) { // LineNumberTable at 0x03C4
+              [3] { // LineNumberTable
+                0  4; //  at 0x03D0
+                4  5; //  at 0x03D4
+                11  6; //  at 0x03D8
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [3] { // Attributes
+    Attr(#21, 2) { // SourceFile at 0x03DA
+      #22;
+    } // end SourceFile
+    ;
+    Attr(#41, 10) { // InnerClasses at 0x03E2
+      [1] { // InnerClasses
+        #39 #43 #40 25; //  at 0x03F2
+      }
+    } // end InnerClasses
+    ;
+    Attr(#25, 6) { // BootstrapMethods at 0x03F2
+      [1] { // bootstrap_methods
+        {  //  bootstrap_method
+          #26; // bootstrap_method_ref
+          [0] { // bootstrap_arguments
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+      }
+    } // end BootstrapMethods
+  } // Attributes
+} // end class defValueObj
+
+///////////////////////////////////////////////////////////
+
+// This class has a withfield opcode with a non-Q type operand.
+class withfieldL {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [28] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1     at 0x0A
+    Utf8 "withfieldL"; // #2     at 0x0D
+    class #2; // #3     at 0x17
+    Field #1 #5; // #4     at 0x1A
+    NameAndType #6 #7; // #5     at 0x1F
+    Utf8 "x"; // #6     at 0x24
+    Utf8 "I"; // #7     at 0x28
+    Field #1 #9; // #8     at 0x2C
+    NameAndType #10 #7; // #9     at 0x31
+    Utf8 "y"; // #10     at 0x36
+    class #12; // #11     at 0x3A
+    Utf8 "QwithfieldL;"; // #12     at 0x3D
+    class #14; // #13     at 0x49
+    Utf8 "java/lang/Object"; // #14     at 0x4C
+    Utf8 "makePoint"; // #15     at 0x5F
+    Utf8 "(II)QwithfieldL;"; // #16     at 0x6B
+    Utf8 "Code"; // #17     at 0x7B
+    Utf8 "LineNumberTable"; // #18     at 0x82
+    Utf8 "<vnew>"; // #19     at 0x94
+    Utf8 "()QwithfieldL;"; // #20     at 0x9D
+    Utf8 "SourceFile"; // #21     at 0xAB
+    Utf8 "X.java"; // #22     at 0xB8
+    Utf8 "NestHost"; // #23     at 0xC1
+    class #25; // #24     at 0xCC
+    Utf8 "X"; // #25     at 0xCF
+    Utf8 "InnerClasses"; // #26     at 0xD3
+    Utf8 "Point"; // #27     at 0xE2
+  } // Constant Pool
+
+  0x0850; // access [ ACC_VALUE ACC_PRIMITIVE ACC_FINAL ]
+  #1;// this_cpx
+  #13;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // Fields
+    {  // field at 0xF4
+      0x0010; // access
+      #6; // name_index       : x
+      #7; // descriptor_index : I
+      [0] { // Attributes
+      } // Attributes
+    }
+    ;
+    {  // field at 0xFC
+      0x0010; // access
+      #10; // name_index       : y
+      #7; // descriptor_index : I
+      [0] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [2] { // Methods
+    {  // method at 0x0106
+      0x0008; // access
+      #15; // name_index       : makePoint
+      #16; // descriptor_index : (II)QwithfieldL;
+      [1] { // Attributes
+        Attr(#17, 62) { // Code at 0x010E
+          2; // max_stack
+          3; // max_locals
+          Bytes[26]{
+            0xCB0001C000034D1A;
+            0x2C5FCC00044D1B2C;
+            0x5FCC00084D2CC000;
+            0x0BB0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#18, 18) { // LineNumberTable at 0x013A
+              [4] { // line_number_table
+                0  4; //  at 0x0146
+                7  5; //  at 0x014A
+                14  6; //  at 0x014E
+                21  7; //  at 0x0152
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+    ;
+    {  // method at 0x0152
+      0x000A; // access
+      #19; // name_index       : <vnew>
+      #20; // descriptor_index : ()QwithfieldL;
+      [1] { // Attributes
+        Attr(#17, 55) { // Code at 0x015A
+          2; // max_stack
+          1; // max_locals
+          Bytes[23]{
+            0xCB00014B032A5FCC;
+            0x0008594BB400082A;
+            0x5FCC00044B2AB0;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#18, 14) { // LineNumberTable at 0x0183
+              [3] { // line_number_table
+                0  9; //  at 0x018F
+                4  10; //  at 0x0193
+                21  11; //  at 0x0197
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [3] { // Attributes
+    Attr(#21, 2) { // SourceFile at 0x0199
+      #22;
+    } // end SourceFile
+    ;
+    Attr(#23, 2) { // NestHost at 0x01A1
+      #24; // X at 0x01A9
+    } // end NestHost
+    ;
+    Attr(#26, 10) { // InnerClasses at 0x01A9
+      [1] { // classes
+        #1 #24 #27 280; //  at 0x01B9
+      }
+    } // end InnerClasses
+  } // Attributes
+} // end class withfieldL
+
+///////////////////////////////////////////////////////////
+
+// Test that a VerifyError exception is thrown when trying to pass a null
+// when the formal parameter is an inline type.
+//
+// // Java program emulating the jcod contents.
+// public inline final class NoNullVT {
+//     final int x;
+//     final int y;
+//
+//     private NoNullVT() {
+//         x = 0;
+//         y = 0;
+//     }
+//
+//     public int getX() { return x; }
+//     public int getY() { return y; }
+//
+//     public boolean isSameNoNullVT(NoNullVT that) {
+//         return this.getX() == that.getX() && this.getY() == that.getY();
+//     }
+//
+//     public boolean equals(Object o) {
+//         if(o instanceof NoNullVT) {
+//             return ((NoNullVT)o).x == x &&  ((NoNullVT)o).y == y;
+//         } else {
+//             return false;
+//         }
+//     }
+//
+//     public static NoNullVT createNoNullVT(int x, int y) {
+//         NoNullVT p = NoNullVT.default;
+//         p = __WithField(p.x, x);
+//         p = __WithField(p.y, y);
+//         return p;
+//     }
+//
+//     public static void main(String[] args) {
+//         String str = null;
+//         NoNullVT a = createNoNullVT(3, 4);
+//         NoNullVT b = createNoNullVT(2, 4);
+//         boolean res = a.isSameNoNullVT(null); // Should throw VerifyError
+//     }
+// }
+
+class NoNullVT {
+  0xCAFEBABE;
+  0; // minor version
+  67; // version
+  [63] { // Constant Pool
+    ; // first element is empty
+    class #36; // #1     at 0x0A
+    Field #1 #37; // #2     at 0x0D
+    Field #1 #38; // #3     at 0x12
+    Method #1 #39; // #4     at 0x17
+    Method #1 #40; // #5     at 0x1C
+    class #41; // #6     at 0x21
+    Method #1 #42; // #7     at 0x24
+    Method #1 #43; // #8     at 0x29
+    InvokeDynamic 0s #46; // #9     at 0x2E
+    InvokeDynamic 0s #47; // #10     at 0x33
+    class #48; // #11     at 0x38
+    Utf8 "x"; // #12     at 0x3B
+    Utf8 "I"; // #13     at 0x3F
+    Utf8 "y"; // #14     at 0x43
+    Utf8 "getX"; // #15     at 0x47
+    Utf8 "()I"; // #16     at 0x4E
+    Utf8 "Code"; // #17     at 0x54
+    Utf8 "LineNumberTable"; // #18     at 0x5B
+    Utf8 "getY"; // #19     at 0x6D
+    Utf8 "isSameNoNullVT"; // #20     at 0x74
+    Utf8 "(QNoNullVT;)Z"; // #21     at 0x85
+    Utf8 "StackMapTable"; // #22     at 0x95
+    Utf8 "equals"; // #23     at 0xA5
+    Utf8 "(Ljava/lang/Object;)Z"; // #24     at 0xAE
+    Utf8 "createNoNullVT"; // #25     at 0xC6
+    Utf8 "(II)QNoNullVT;"; // #26     at 0xD7
+    Utf8 "main"; // #27     at 0xE8
+    Utf8 "([Ljava/lang/String;)V"; // #28     at 0xEF
+    Utf8 "hashCode"; // #29     at 0x0108
+    Utf8 "toString"; // #30     at 0x0113
+    Utf8 "()Ljava/lang/String;"; // #31     at 0x011E
+    Utf8 "<vnew>"; // #32     at 0x0135
+    Utf8 "()QNoNullVT;"; // #33     at 0x013E
+    Utf8 "SourceFile"; // #34     at 0x014D
+    Utf8 "NoNullVT.java"; // #35     at 0x015A
+    Utf8 "NoNullVT"; // #36     at 0x016A
+    NameAndType #12 #13; // #37     at 0x0175
+    NameAndType #14 #13; // #38     at 0x017A
+    NameAndType #15 #16; // #39     at 0x017F
+    NameAndType #19 #16; // #40     at 0x0184
+    Utf8 "QNoNullVT;"; // #41     at 0x0189
+    NameAndType #25 #26; // #42     at 0x0196
+    NameAndType #20 #21; // #43     at 0x019B
+    Utf8 "BootstrapMethods"; // #44     at 0x01A0
+    MethodHandle 6b #49; // #45     at 0x01B3
+    NameAndType #29 #50; // #46     at 0x01B7
+    NameAndType #30 #51; // #47     at 0x01BC
+    Utf8 "java/lang/Object"; // #48     at 0x01C1
+    Method #52 #53; // #49     at 0x01D4
+    Utf8 "(QNoNullVT;)I"; // #50     at 0x01D9
+    Utf8 "(QNoNullVT;)Ljava/lang/String;"; // #51     at 0x01E9
+    class #54; // #52     at 0x020A
+    NameAndType #55 #59; // #53     at 0x020D
+    Utf8 "java/lang/invoke/ValueBootstrapMethods"; // #54     at 0x0212
+    Utf8 "makeBootstrapMethod"; // #55     at 0x023B
+    class #61; // #56     at 0x0251
+    Utf8 "Lookup"; // #57     at 0x0254
+    Utf8 "InnerClasses"; // #58     at 0x025D
+    Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; // #59     at 0x026C
+    class #62; // #60     at 0x02E2
+    Utf8 "java/lang/invoke/MethodHandles$Lookup"; // #61     at 0x02E5
+    Utf8 "java/lang/invoke/MethodHandles"; // #62     at 0x030D
+  } // Constant Pool
+
+  0x0851; // access [ ACC_VALUE ACC_PRIMITIVE ACC_PUBLIC ACC_FINAL ]
+  #1;// this_cpx
+  #11;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [2] { // fields
+    { // Member at 0x0338
+      0x0010; // access
+      #12; // name_cpx
+      #13; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0340
+      0x0010; // access
+      #14; // name_cpx
+      #13; // sig_cpx
+      [0] { // Attributes
+      } // Attributes
+    } // Member
+  } // fields
+
+  [9] { // methods
+    { // Member at 0x034A
+      0x0001; // access
+      #15; // name_cpx
+      #16; // sig_cpx
+      [1] { // Attributes
+        Attr(#17, 29) { // Code at 0x0352
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40002AC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#18, 6) { // LineNumberTable at 0x0369
+              [1] { // LineNumberTable
+                0  10; //  at 0x0375
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0375
+      0x0001; // access
+      #19; // name_cpx
+      #16; // sig_cpx
+      [1] { // Attributes
+        Attr(#17, 29) { // Code at 0x037D
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB40003AC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#18, 6) { // LineNumberTable at 0x0394
+              [1] { // LineNumberTable
+                0  11; //  at 0x03A0
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03A0
+      0x0001; // access
+      #20; // name_cpx
+      #21; // sig_cpx
+      [1] { // Attributes
+        Attr(#17, 63) { // Code at 0x03A8
+          2; // max_stack
+          2; // max_locals
+          Bytes[28]{
+            0x2AB600042BB60004;
+            0xA000122AB600052B;
+            0xB60005A0000704A7;
+            0x000403AC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [2] { // Attributes
+            Attr(#18, 6) { // LineNumberTable at 0x03D6
+              [1] { // LineNumberTable
+                0  14; //  at 0x03E2
+              }
+            } // end LineNumberTable
+            ;
+            Attr(#22, 5) { // StackMapTable at 0x03E2
+              [2] { //
+                26b; // same_frame
+                64b, [1]z{1b}; // same_locals_1_stack_item_frame
+              }
+            } // end StackMapTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x03ED
+      0x0001; // access
+      #23; // name_cpx
+      #24; // sig_cpx
+      [1] { // Attributes
+        Attr(#17, 87) { // Code at 0x03F5
+          2; // max_stack
+          2; // max_locals
+          Bytes[43]{
+            0x2BC100019900252B;
+            0xC00006B400022AB4;
+            0x0002A000152BC000;
+            0x06B400032AB40003;
+            0xA0000704A7000403;
+            0xAC03AC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [2] { // Attributes
+            Attr(#18, 14) { // LineNumberTable at 0x0432
+              [3] { // LineNumberTable
+                0  18; //  at 0x043E
+                7  19; //  at 0x0442
+                41  21; //  at 0x0446
+              }
+            } // end LineNumberTable
+            ;
+            Attr(#22, 6) { // StackMapTable at 0x0446
+              [3] { //
+                39b; // same_frame
+                64b, [1]z{1b}; // same_locals_1_stack_item_frame
+                0b; // same_frame
+              }
+            } // end StackMapTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0452
+      0x0009; // access
+      #25; // name_cpx
+      #26; // sig_cpx
+      [1] { // Attributes
+        Attr(#17, 56) { // Code at 0x045A
+          2; // max_stack
+          3; // max_locals
+          Bytes[20]{
+            0xCB00014D1A2C5FCC;
+            0x00024D1B2C5FCC00;
+            0x034D2CB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#18, 18) { // LineNumberTable at 0x0480
+              [4] { // LineNumberTable
+                0  26; //  at 0x048C
+                4  27; //  at 0x0490
+                11  28; //  at 0x0494
+                18  29; //  at 0x0498
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0498
+      0x0009; // access
+      #27; // name_cpx
+      #28; // sig_cpx
+      [1] { // Attributes
+        Attr(#17, 62) { // Code at 0x04A0
+          2; // max_stack
+          5; // max_locals
+          Bytes[22]{
+            0x014C0607B800074D;
+            0x0507B800074E2C2B; // Change last nibble from C to B to load null
+            0xB600083604B1;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#18, 22) { // LineNumberTable at 0x04C8
+              [5] { // LineNumberTable
+                0  33; //  at 0x04D4
+                2  34; //  at 0x04D8
+                8  35; //  at 0x04DC
+                14  36; //  at 0x04E0
+                21  37; //  at 0x04E4
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x04E4
+      0x0011; // access
+      #29; // name_cpx
+      #16; // sig_cpx
+      [1] { // Attributes
+        Attr(#17, 31) { // Code at 0x04EC
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA00090000AC;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#18, 6) { // LineNumberTable at 0x0505
+              [1] { // LineNumberTable
+                0  1; //  at 0x0511
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x0511
+      0x0011; // access
+      #30; // name_cpx
+      #31; // sig_cpx
+      [1] { // Attributes
+        Attr(#17, 31) { // Code at 0x0519
+          1; // max_stack
+          1; // max_locals
+          Bytes[7]{
+            0x2ABA000A0000B0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#18, 6) { // LineNumberTable at 0x0532
+              [1] { // LineNumberTable
+                0  1; //  at 0x053E
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x053E
+      0x000A; // access
+      #32; // name_cpx
+      #33; // sig_cpx
+      [1] { // Attributes
+        Attr(#17, 56) { // Code at 0x0546
+          2; // max_stack
+          1; // max_locals
+          Bytes[20]{
+            0xCB00014B032A5FCC;
+            0x00024B032A5FCC00;
+            0x034B2AB0;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#18, 18) { // LineNumberTable at 0x056C
+              [4] { // LineNumberTable
+                0  5; //  at 0x0578
+                4  6; //  at 0x057C
+                11  7; //  at 0x0580
+                18  8; //  at 0x0584
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [3] { // Attributes
+    Attr(#34, 2) { // SourceFile at 0x0586
+      #35;
+    } // end SourceFile
+    ;
+    Attr(#58, 10) { // InnerClasses at 0x058E
+      [1] { // InnerClasses
+        #56 #60 #57 25; //  at 0x059E
+      }
+    } // end InnerClasses
+    ;
+    Attr(#44, 6) { // BootstrapMethods at 0x059E
+      [1] { // bootstrap_methods
+        {  //  bootstrap_method
+          #45; // bootstrap_method_ref
+          [0] { // bootstrap_arguments
+          }  //  bootstrap_arguments
+        }  //  bootstrap_method
+      }
+    } // end BootstrapMethods
+  } // Attributes
+} // end class NoNullVT
diff a/test/hotspot/jtreg/serviceability/jvmti/HiddenClass/libHiddenClassSigTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/HiddenClass/libHiddenClassSigTest.cpp
--- a/test/hotspot/jtreg/serviceability/jvmti/HiddenClass/libHiddenClassSigTest.cpp
+++ b/test/hotspot/jtreg/serviceability/jvmti/HiddenClass/libHiddenClassSigTest.cpp
@@ -26,10 +26,11 @@
 
 extern "C" {
 
 static const char* EXP_INTERF_SIG = "LP/Q/HCInterf;";
 static const char* SIG_START      = "LP/Q/HiddenClassSig";
+static const char* IDENTITYOBJECT_IF = "Ljava/lang/IdentityObject;";
 static const size_t SIG_START_LEN = strlen(SIG_START);
 static const int    ACC_INTERFACE = 0x0200; // Interface class modifiers bit
 
 static jvmtiEnv *jvmti = NULL;
 static jint class_load_count = 0;
@@ -190,24 +191,30 @@
   char* sig = NULL;
   jint count = 0;
   jclass* interfaces = NULL;
   jvmtiError err;
 
-  // check that hidden class implements just one interface
+  // check that hidden class implements just one interface (or two if IdentityObject has been injected)
   err = jvmti->GetImplementedInterfaces(klass, &count, &interfaces);
   CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_impl_interf: Error in JVMTI GetImplementedInterfaces");
-  if (count != 1) {
-    LOG1("check_hidden_class_impl_interf: FAIL: implemented interfaces count: %d, expected to be 1\n", count);
+  if (count != 1 && count != 2) {
+    LOG1("check_hidden_class_impl_interf: FAIL: implemented interfaces count: %d, expected to be in [1-2] range\n", count);
     failed = true;
     return;
   }
-  // get interface signature
-  err = jvmti->GetClassSignature(interfaces[0], &sig, NULL);
-  CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_impl_interf: Error in JVMTI GetClassSignature for implemented interface");
+  bool found = false;
+  for (int i = 0; i < count; i++) {
+    // get interface signature
+    err = jvmti->GetClassSignature(interfaces[i], &sig, NULL);
+    CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_impl_interf: Error in JVMTI GetClassSignature for implemented interface");
+    // check the interface signature is matching the expected
+    if (strcmp(sig, EXP_INTERF_SIG) == 0) {
+      found = true;
+    }
+  }
 
-  // check the interface signature is matching the expected
-  if (strcmp(sig, EXP_INTERF_SIG) != 0) {
+  if (!found) {
     LOG2("check_hidden_class_impl_interf: FAIL: implemented interface signature: %s, expected to be: %s\n",
            sig, EXP_INTERF_SIG);
     failed = true;
   }
 }
diff a/test/hotspot/jtreg/serviceability/jvmti/Valhalla/FieldAccessModify/FieldAccessModify.java b/test/hotspot/jtreg/serviceability/jvmti/Valhalla/FieldAccessModify/FieldAccessModify.java
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/Valhalla/FieldAccessModify/FieldAccessModify.java
@@ -0,0 +1,225 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/**
+ * @ignore Fix JDK-8328461
+ * @test
+ * @summary Tests that all FieldAccess and FieldModification notifications
+            are generated for value classes.
+ * @requires vm.jvmti
+ * @enablePreview
+ * @compile FieldAccessModify.java
+ * @run main/othervm/native -agentlib:FieldAccessModify FieldAccessModify
+ */
+
+import java.lang.reflect.Field;
+import java.util.Arrays;
+
+
+public class FieldAccessModify {
+
+    private static final String agentLib = "FieldAccessModify";
+
+    private static value class MyPrimitive {
+        public int MyPrimitive_fld1;
+        public int MyPrimitive_fld2;
+
+        public MyPrimitive(int v1, int v2) { MyPrimitive_fld1 = v1; MyPrimitive_fld2 = v2; }
+
+        public static MyPrimitive create(int v1, int v2) {
+            return new MyPrimitive(v1, v2);
+        }
+
+        public String toString() {
+            return "MyPrimitive { fld1=" + MyPrimitive_fld1 + ", fld2=" + MyPrimitive_fld2 + "}";
+        }
+
+    }
+
+    private static class InstanceHolder {
+        public final MyPrimitive InstanceHolder_fld1;
+
+        public InstanceHolder(int v) {
+            InstanceHolder_fld1 = MyPrimitive.create(v, v + 100);
+        }
+
+        public String toString() {
+            return "InstanceHolder { fld1 is " + InstanceHolder_fld1 + "}";
+        }
+    }
+
+    private static value class PrimitiveHolder {
+        public MyPrimitive PrimitiveHolder_fld1;
+
+        public PrimitiveHolder(int v) {
+            PrimitiveHolder_fld1 = MyPrimitive.create(v, v + 200);
+        }
+
+        public String toString() {
+            return "PrimitiveHolder { fld1 is " + PrimitiveHolder_fld1 + "}";
+        }
+    }
+
+    private static class TestHolder {
+        public MyPrimitive primitiveObj = MyPrimitive.create(1, 1);
+        public InstanceHolder instanceHolderObj = new InstanceHolder(1);
+        public PrimitiveHolder primitiveHolderObj = new PrimitiveHolder(1);
+    }
+
+    public static void main(String[] args) throws Exception {
+        try {
+            System.loadLibrary(agentLib);
+        } catch (UnsatisfiedLinkError ex) {
+            System.err.println("Failed to load " + agentLib + " lib");
+            System.err.println("java.library.path: " + System.getProperty("java.library.path"));
+            throw ex;
+        }
+
+        // create objects for access testing before setting watchers
+        TestHolder testHolder = new TestHolder();
+
+        if (!initWatchers(MyPrimitive.class, MyPrimitive.class.getDeclaredField("MyPrimitive_fld1"))) {
+            throw new RuntimeException("Watchers initializations error (MyPrimitive_fld1)");
+        }
+        if (!initWatchers(MyPrimitive.class, MyPrimitive.class.getDeclaredField("MyPrimitive_fld2"))) {
+            throw new RuntimeException("Watchers initializations error (MyPrimitive_fld2)");
+        }
+        if (!initWatchers(InstanceHolder.class, InstanceHolder.class.getDeclaredField("InstanceHolder_fld1"))) {
+            throw new RuntimeException("Watchers initializations error (InstanceHolder_fld1)");
+        }
+        if (!initWatchers(PrimitiveHolder.class, PrimitiveHolder.class.getDeclaredField("PrimitiveHolder_fld1"))) {
+            throw new RuntimeException("Watchers initializations error (PrimitiveHolder_fld1)");
+        }
+
+        test("MyPrimitive (access)", () -> {
+                testHolder.primitiveObj.toString();     // should access both MyPrimitive_fld1 and MyPrimitive_fld2
+            }, new TestResult() {
+                public boolean MyPrimitive_fld1_access;
+                public boolean MyPrimitive_fld2_access;
+            });
+
+        test("InstanceHolder (access)", () ->  {
+                testHolder.instanceHolderObj.toString();
+            }, new TestResult() {
+                public boolean InstanceHolder_fld1_access;
+                // MyPrimitive fields should be accessed too
+                public boolean MyPrimitive_fld1_access;
+                public boolean MyPrimitive_fld2_access;
+            });
+
+        test("PrimitiveHolder (access)", () ->  {
+                testHolder.primitiveHolderObj.toString();
+            }, new TestResult() {
+                public boolean PrimitiveHolder_fld1_access;
+                // MyPrimitive fields should be accessed too
+                public boolean MyPrimitive_fld1_access;
+                public boolean MyPrimitive_fld2_access;
+            });
+
+        test("MyPrimitive (modify)", () ->  {
+                MyPrimitive obj = MyPrimitive.create(1, 1);
+            }, new TestResult() {
+                // KNOWN_ISSUE public boolean MyPrimitive_fld1_modify;
+                // KNOWN_ISSUE public boolean MyPrimitive_fld2_modify;
+            });
+
+        test("InstanceHolder (modify)", () ->  {
+                InstanceHolder obj = new InstanceHolder(10);
+            }, new TestResult() {
+                public boolean InstanceHolder_fld1_modify;
+                // MyPrimitive fields should be modified too
+                // KNOWN_ISSUE public boolean MyPrimitive_fld1_modify;
+                // KNOWN_ISSUE public boolean MyPrimitive_fld2_modify;
+            });
+
+        test("PrimitiveHolder (modify)", () ->  {
+                PrimitiveHolder obj = new PrimitiveHolder(11);
+            }, new TestResult() {
+                // KNOWN_ISSUE public boolean PrimitiveHolder_fld1_modify;
+                // MyPrimitive fields should be modified too
+                // KNOWN_ISSUE public boolean MyPrimitive_fld1_modify;
+                // KNOWN_ISSUE public boolean MyPrimitive_fld2_modify;
+            });
+
+    }
+
+    private static void log(String msg) {
+        System.out.println(msg);
+        System.out.flush();
+    }
+
+    private static void assertTrue(boolean value) {
+        if (!value) {
+            throw new RuntimeException("assert");
+        }
+    }
+
+    // For every access/modify notification native part tries to locate
+    // boolean "<field_name>_access"/"<field_name>_modify" field and sets it to true
+    private static class TestResult {
+
+        // verify that all fields are set to true
+        public void verify() {
+            Arrays.stream(this.getClass().getDeclaredFields()).forEach(f -> verify(f));
+        }
+
+        private void verify(Field f) {
+            try {
+                if (!f.getBoolean(this)) {
+                    throw new RuntimeException(f.getName() + " notification is missed");
+                }
+                log("  - " + f.getName() + ": OK");
+            } catch (IllegalAccessException ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+    }
+
+    @FunctionalInterface
+    private interface TestAction {
+        void apply();
+    }
+
+    private static void test(String descr, TestAction action, TestResult result) throws Exception {
+        log(descr + ": starting");
+        if (!startTest(result)) {
+            throw new RuntimeException("startTest failed");
+        }
+        action.apply();
+        // wait some time to ensure all posted events are handled
+        Thread.sleep(500);
+
+        stopTest();
+
+        // check the results
+        result.verify();
+
+        log(descr + ": OK");
+        log("");
+    }
+
+    private static native boolean initWatchers(Class cls, Field field);
+    private static native boolean startTest(TestResult results);
+    private static native void stopTest();
+
+}
diff a/test/hotspot/jtreg/serviceability/jvmti/Valhalla/FieldAccessModify/libFieldAccessModify.cpp b/test/hotspot/jtreg/serviceability/jvmti/Valhalla/FieldAccessModify/libFieldAccessModify.cpp
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/Valhalla/FieldAccessModify/libFieldAccessModify.cpp
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 2021, 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "jvmti.h"
+#include "jni.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+static jvmtiEnv *jvmti = nullptr;
+
+// valid while a test is executed
+static jobject testResultObject = nullptr;
+static jclass testResultClass = nullptr;
+// we log object values handling FieldModification event and this cause FieldAccess events are triggered.
+// The flag to disable FieldAccess handling.
+static bool disableAccessEvent = false;
+
+static void reportError(const char *msg, int err) {
+    printf("%s, error: %d\n", msg, err);
+}
+
+static void printJValue(const char *prefix, JNIEnv *jni_env, char signature_type, jvalue value) {
+    // print new_value to ensure the value is valid
+    // use String.valueOf(...) to get string representation
+    /*
+    Z boolean
+    B byte
+    C char
+    S short
+    I int
+    J long
+    F float
+    D double
+    L fully-qualified-class ;   fully-qualified-class
+    [ type                      type[]
+    */
+    char signature[64] = {};
+    if (signature_type == 'Q' || signature_type == 'L') {
+        snprintf(signature, sizeof(signature), "(Ljava/lang/Object;)Ljava/lang/String;");
+    } else {
+        snprintf(signature, sizeof(signature), "(%c)Ljava/lang/String;", signature_type);
+    }
+
+    jclass clsString = jni_env->FindClass("java/lang/String");
+    jmethodID mid = jni_env->GetStaticMethodID(clsString, "valueOf", signature);
+    jstring objJStr = (jstring)jni_env->CallStaticObjectMethodA(clsString, mid, &value);
+
+    const char* objStr = "UNKNOWN";
+    if (objJStr != nullptr) {
+        objStr = jni_env->GetStringUTFChars(objJStr, nullptr);
+    }
+
+    printf("    %s is: '%s'\n", prefix, objStr);
+    fflush(0);
+
+    if (objJStr != nullptr) {
+        jni_env->ReleaseStringUTFChars(objJStr, objStr);
+    }
+}
+
+
+// logs the notification and updates currentTestResult
+static void handleNotification(jvmtiEnv *jvmti, JNIEnv *jni_env,
+    jmethodID method,
+    jobject object,
+    jfieldID field,
+    jclass field_klass,
+    bool modified,
+    jlocation location)
+{
+    jvmtiError err;
+    char *name = nullptr;
+    char *signature = nullptr;
+    char *mname = nullptr;
+    char *mgensig = nullptr;
+    jclass methodClass = nullptr;
+    char *csig = nullptr;
+
+    if (testResultObject == nullptr) {
+        // we are out of test
+        return;
+    }
+
+    err = jvmti->GetFieldName(field_klass, field, &name, &signature, nullptr);
+    if (err != JVMTI_ERROR_NONE) {
+        reportError("GetFieldName failed", err);
+        return;
+    }
+
+    err = jvmti->GetMethodName(method, &mname, nullptr, &mgensig);
+    if (err != JVMTI_ERROR_NONE) {
+        reportError("GetMethodName failed", err);
+        return;
+    }
+
+    err = jvmti->GetMethodDeclaringClass(method, &methodClass);
+    if (err != JVMTI_ERROR_NONE) {
+        reportError("GetMethodDeclaringClass failed", err);
+        return;
+    }
+
+    err = jvmti->GetClassSignature(methodClass, &csig, nullptr);
+    if (err != JVMTI_ERROR_NONE) {
+        reportError("GetClassSignature failed", err);
+        return;
+    }
+
+    printf("  \"class: %s method: %s%s\" %s field: \"%s\" (type '%s'), location: %d\n",
+        csig, mname, mgensig, modified ? "modified" : "accessed", name, signature, (int)location);
+
+    // For FieldModification event print current value.
+    // Note: this will cause FieldAccess event.
+    if (modified) {
+        jvalue curValue = {};
+        switch (signature[0]) {
+        case 'L':
+        case 'Q':
+            curValue.l = jni_env->GetObjectField(object, field); break;
+        case 'Z':   // boolean
+            curValue.z = jni_env->GetBooleanField(object, field); break;
+        case 'B':   // byte
+            curValue.b = jni_env->GetByteField(object, field); break;
+        case 'C':   // char
+            curValue.c = jni_env->GetCharField(object, field); break;
+        case 'S':   // short
+            curValue.s = jni_env->GetShortField(object, field); break;
+        case 'I':   // int
+            curValue.i = jni_env->GetIntField(object, field); break;
+        case 'J':   // long
+            curValue.j = jni_env->GetLongField(object, field); break;
+        case 'F':   // float
+            curValue.f = jni_env->GetFloatField(object, field); break;
+        case 'D':   // double
+            curValue.d = jni_env->GetDoubleField(object, field); break;
+        default:
+            printf("ERROR: unexpected signature: %s\n", signature);
+            return;
+        }
+        printJValue("current value: ", jni_env, signature[0], curValue);
+    }
+
+    // set TestResult
+    if (testResultObject != nullptr && testResultClass != nullptr) {
+        jfieldID fieldID;
+        // field names in TestResult are "<field_name>_access"/"<field_name>_modify"
+        char *fieldName = (char *)malloc(strlen(name) + 16);
+        strcpy(fieldName, name);
+        strcat(fieldName, modified ? "_modify" : "_access");
+
+        fieldID = jni_env->GetFieldID(testResultClass, fieldName, "Z");
+        if (fieldID != nullptr) {
+            jni_env->SetBooleanField(testResultObject, fieldID, JNI_TRUE);
+        } else {
+            // the field is not interesting for the test
+        }
+        // clear any possible exception
+        jni_env->ExceptionClear();
+
+        free(fieldName);
+    }
+
+    jvmti->Deallocate((unsigned char*)csig);
+    jvmti->Deallocate((unsigned char*)mname);
+    jvmti->Deallocate((unsigned char*)mgensig);
+    jvmti->Deallocate((unsigned char*)name);
+    jvmti->Deallocate((unsigned char*)signature);
+}
+
+static void JNICALL
+onFieldAccess(jvmtiEnv *jvmti_env,
+            JNIEnv* jni_env,
+            jthread thread,
+            jmethodID method,
+            jlocation location,
+            jclass field_klass,
+            jobject object,
+            jfieldID field)
+{
+    if (disableAccessEvent) {
+        return;
+    }
+    handleNotification(jvmti_env, jni_env, method, object, field, field_klass, false, location);
+}
+
+static void JNICALL
+onFieldModification(jvmtiEnv *jvmti_env,
+            JNIEnv* jni_env,
+            jthread thread,
+            jmethodID method,
+            jlocation location,
+            jclass field_klass,
+            jobject object,
+            jfieldID field,
+            char signature_type,
+            jvalue new_value)
+{
+    disableAccessEvent = true;
+
+    handleNotification(jvmti_env, jni_env, method, object, field, field_klass, true, location);
+
+    printJValue("new value", jni_env, signature_type, new_value);
+
+    disableAccessEvent = false;
+}
+
+
+JNIEXPORT jint JNICALL
+Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
+{
+    jvmtiError err;
+    jvmtiCapabilities caps = {};
+    jvmtiEventCallbacks callbacks = {};
+    jint res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);
+    if (res != JNI_OK || jvmti == nullptr) {
+        reportError("GetEnv failed", res);
+        return JNI_ERR;
+    }
+
+    caps.can_generate_field_modification_events = 1;
+    caps.can_generate_field_access_events = 1;
+    caps.can_tag_objects = 1;
+    err = jvmti->AddCapabilities(&caps);
+    if (err != JVMTI_ERROR_NONE) {
+        reportError("Failed to set capabilities", err);
+        return JNI_ERR;
+    }
+
+    callbacks.FieldModification = &onFieldModification;
+    callbacks.FieldAccess = &onFieldAccess;
+
+    err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
+    if (err != JVMTI_ERROR_NONE) {
+        reportError("Failed to set event callbacks", err);
+        return JNI_ERR;
+    }
+
+    err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_FIELD_ACCESS, nullptr);
+    if (err != JVMTI_ERROR_NONE) {
+        reportError("Failed to set access notifications", err);
+        return JNI_ERR;
+    }
+
+    err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_FIELD_MODIFICATION, nullptr);
+    if (err != JVMTI_ERROR_NONE) {
+        reportError("Failed to set modification notifications", err);
+        return JNI_ERR;
+    }
+    setbuf(stdout, nullptr);
+    return JNI_OK;
+}
+
+
+JNIEXPORT jboolean JNICALL
+Java_FieldAccessModify_initWatchers(JNIEnv *env, jclass thisClass, jclass cls, jobject field)
+{
+    jfieldID fieldId;
+    jvmtiError err;
+
+    if (jvmti == nullptr) {
+        reportError("jvmti is NULL", 0);
+        return JNI_FALSE;
+    }
+
+    fieldId = env->FromReflectedField(field);
+
+    err = jvmti->SetFieldModificationWatch(cls, fieldId);
+    if (err != JVMTI_ERROR_NONE) {
+        reportError("SetFieldModificationWatch failed", err);
+        return JNI_FALSE;
+    }
+
+    err = jvmti->SetFieldAccessWatch(cls, fieldId);
+    if (err != JVMTI_ERROR_NONE) {
+        reportError("SetFieldAccessWatch failed", err);
+        return JNI_FALSE;
+    }
+
+    return JNI_TRUE;
+}
+
+
+JNIEXPORT jboolean JNICALL
+Java_FieldAccessModify_startTest(JNIEnv *env, jclass thisClass, jobject testResults)
+{
+    testResultObject = env->NewGlobalRef(testResults);
+    testResultClass = (jclass)env->NewGlobalRef(env->GetObjectClass(testResultObject));
+
+    return JNI_TRUE;
+}
+
+JNIEXPORT void JNICALL
+Java_FieldAccessModify_stopTest(JNIEnv *env, jclass thisClass)
+{
+    if (testResultObject != nullptr) {
+        env->DeleteGlobalRef(testResultObject);
+        testResultObject = nullptr;
+    }
+    if (testResultClass != nullptr) {
+        env->DeleteGlobalRef(testResultClass);
+        testResultClass = nullptr;
+    }
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
diff a/test/hotspot/jtreg/serviceability/jvmti/Valhalla/HeapDump/HeapDump.java b/test/hotspot/jtreg/serviceability/jvmti/Valhalla/HeapDump/HeapDump.java
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/Valhalla/HeapDump/HeapDump.java
@@ -0,0 +1,509 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/**
+ * @ignore Fix JDK-8328468
+ * @test
+ * @modules java.base/jdk.internal.value
+ * @library /test/lib
+ * @requires vm.jvmti
+ * @enablePreview
+ * @compile HeapDump.java
+ * @run main/othervm HeapDump
+ */
+
+import java.io.File;
+import java.lang.ref.Reference;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.concurrent.TimeUnit;
+import jdk.internal.value.PrimitiveClass;
+import jdk.test.lib.apps.LingeredApp;
+import jdk.test.lib.JDKToolLauncher;
+import jdk.test.lib.hprof.model.HackJavaValue;
+import jdk.test.lib.hprof.model.InlinedJavaObject;
+import jdk.test.lib.hprof.model.JavaByte;
+import jdk.test.lib.hprof.model.JavaClass;
+import jdk.test.lib.hprof.model.JavaField;
+import jdk.test.lib.hprof.model.JavaHeapObject;
+import jdk.test.lib.hprof.model.JavaObject;
+import jdk.test.lib.hprof.model.JavaObjectArray;
+import jdk.test.lib.hprof.model.JavaStatic;
+import jdk.test.lib.hprof.model.JavaThing;
+import jdk.test.lib.hprof.model.JavaValueArray;
+import jdk.test.lib.hprof.model.Snapshot;
+import jdk.test.lib.process.ProcessTools;
+
+import java.util.Enumeration;
+
+import jdk.test.lib.hprof.parser.Reader;
+
+
+class TestClass {
+
+    public static primitive class MyPrimitive0 {
+        public byte prim0_fld1;
+        public MyPrimitive0(int fld1) { prim0_fld1 = (byte)fld1; }
+    }
+    public static primitive class MyPrimitive {
+        public MyPrimitive0 prim_prim0;
+        public byte prim_fld1;
+        public int prim_fld2;
+
+        public MyPrimitive(int p0, int fld1, int fld2) {
+            prim_fld1 = (byte)fld1; prim_fld2 = fld2; prim_prim0 = new MyPrimitive0(p0);
+        }
+    }
+
+    public static primitive class PrimitiveHolder {
+        // offset of the inlined hld_flatObj is the same as offset of inlined PrimitiveHolder
+        public MyPrimitive hld_flatObj;
+
+        public PrimitiveHolder(int n) {
+            hld_flatObj = new MyPrimitive((byte)n, n+1, n+2);
+        }
+    }
+
+    // primitive class with reference
+    public static primitive class MyPrimitiveRef {
+        public byte ref_fld1;
+        public int ref_fld2;
+        public String ref_strObj;
+
+        public MyPrimitiveRef(int v1, int v2) {
+            ref_fld1 = (byte)v1;
+            ref_fld2 = v2;
+            ref_strObj = "#" + String.valueOf(v2); }
+    }
+
+    public static primitive class PrimitiveHolderRef {
+        public MyPrimitiveRef[] flatArr = new MyPrimitiveRef[5];
+        public MyPrimitiveRef flatObj;
+        public String ref_str;
+        public PrimitiveHolderRef(int n) {
+            ref_str = String.valueOf(n);
+            flatObj = new MyPrimitiveRef(n, n + 10);
+            for (int i = 0; i < flatArr.length; i++) {
+                flatArr[i] = new MyPrimitiveRef(i + n + 1, i + n + 11);
+            }
+        }
+    }
+
+    public MyPrimitive[] main_flatArr = new MyPrimitive[3];
+    public MyPrimitive main_flatObj = new MyPrimitive(10, 15, 9);
+    public MyPrimitiveRef main_flatObjRef = new MyPrimitiveRef(11, 144);
+    public MyPrimitiveRef[] flatArrRef = new MyPrimitiveRef[4];
+    public String main_strObj = "targ.strObj";
+
+    public Object main_nullObj;
+
+    public final PrimitiveHolder main_primHolder = new PrimitiveHolder(16);
+    // array of compound primitive objects
+    public final PrimitiveHolderRef[] primHolderArr = new PrimitiveHolderRef[4];
+
+
+    // static inlined fields
+    public static MyPrimitive main_flatObjStatic = new MyPrimitive(13, 241, 24);
+    public static MyPrimitiveRef[] flatArrRefStatic = new MyPrimitiveRef[6];
+    static {
+        for (int i = 0; i < flatArrRefStatic.length; i++) {
+            flatArrRefStatic[i] = new MyPrimitiveRef(i + 200, i + 225);
+        }
+    }
+
+    public TestClass() {
+        for (int i = 0; i < main_flatArr.length; i++) {
+            main_flatArr[i] = new MyPrimitive(i + 10, i + 110, i + 35);
+        }
+        for (int i = 0; i < flatArrRef.length; i++) {
+            flatArrRef[i] = new MyPrimitiveRef(i + 100, i + 120);
+        }
+        for (int i = 0; i < primHolderArr.length; i++) {
+            primHolderArr[i] = new PrimitiveHolderRef(20 + i);
+        }
+    }
+}
+
+class HeapDumpTarg extends LingeredApp {
+
+    public static void main(String[] args) {
+        TestClass testObj = new TestClass();
+        LingeredApp.main(args);
+        Reference.reachabilityFence(testObj);
+    }
+
+}
+
+public class HeapDump {
+    public static void main(String[] args) throws Throwable {
+        LingeredApp theApp = null;
+        String hprogFile = new File(System.getProperty("test.classes") + "/Myheapdump.hprof").getAbsolutePath();
+        try {
+            theApp = new HeapDumpTarg();
+
+            // -XX:+PrintInlineLayout is debug-only arg
+            LingeredApp.startApp(theApp, "--enable-preview"/*, "-XX:+PrintInlineLayout"*/);
+
+            // jcmd <pid> GC.heap_dump
+            JDKToolLauncher launcher = JDKToolLauncher
+                                        .createUsingTestJDK("jcmd")
+                                        .addToolArg(Long.toString(theApp.getPid()))
+                                        .addToolArg("GC.heap_dump")
+                                        .addToolArg(hprogFile);
+            Process jcmd = ProcessTools.startProcess("jcmd", new ProcessBuilder(launcher.getCommand()));
+            // If something goes wrong with heap dumping most likely we'll get crash of the target VM
+            while (!jcmd.waitFor(5, TimeUnit.SECONDS)) {
+                if (!theApp.getProcess().isAlive()) {
+                    log("ERROR: target VM died, killing jcmd...");
+                    jcmd.destroyForcibly();
+                    throw new Exception("Target VM died");
+                }
+            }
+
+            if (jcmd.exitValue() != 0) {
+                throw new Exception("Jcmd exited with code " + jcmd.exitValue());
+            }
+        } finally {
+            LingeredApp.stopApp(theApp);
+        }
+
+        // test object to compare
+        TestClass testObj = new TestClass();
+
+        log("Reading " + hprogFile + "...");
+        try (Snapshot snapshot = Reader.readFile(hprogFile,true, 0)) {
+            log("Snapshot read, resolving...");
+            snapshot.resolve(true);
+            log("Snapshot resolved.");
+
+            JavaObject dumpObj = findObject(snapshot, testObj.getClass().getName());
+
+            log("");
+            print(dumpObj);
+
+            log("");
+            log("Verifying object " + testObj.getClass().getName() + " (dumped object " + dumpObj + ")");
+            compareObjectFields("  ", testObj, dumpObj);
+        }
+    }
+
+    private static JavaObject findObject(Snapshot snapshot, String className) throws Exception {
+        log("looking for " + className + "...");
+        JavaClass jClass = snapshot.findClass(className);
+        if (jClass == null) {
+            throw new Exception("'" + className + "' not found");
+        }
+        Enumeration<JavaHeapObject> objects = jClass.getInstances(false);
+        if (!objects.hasMoreElements()) {
+            throw new Exception("No '" + className + "' instances found");
+        }
+        JavaHeapObject heapObj = objects.nextElement();
+        if (objects.hasMoreElements()) {
+            throw new Exception("More than 1 instances of '" + className + "' found");
+        }
+        if (!(heapObj instanceof JavaObject)) {
+            throw new Exception("'" + className + "' instance is not JavaObject (" + heapObj.getClass() + ")");
+        }
+        return (JavaObject)heapObj;
+    }
+
+    // description of the current object for logging and error reporting
+    private static String objDescr;
+    private static boolean errorReported = false;
+
+    private static void compareObjects(String logPrefix, Object testObj, JavaThing dumpObj) throws Exception {
+        if (testObj == null) {
+            if (!isNullValue(dumpObj)) {
+                throw new Exception("null expected, but dumped object is " + dumpObj);
+            }
+            log(logPrefix + objDescr + ": null");
+        } else if (dumpObj instanceof JavaObject obj) {
+            // special handling for Strings
+            if (testObj instanceof String testStr) {
+                objDescr += " (String, " + obj.getIdString() + ")";
+                if (!obj.getClazz().isString()) {
+                    throw new Exception("value (" + obj + ")"
+                            + " is not String (" + obj.getClazz() + ")");
+                }
+                String dumpStr = getStringValue(obj);
+                if (!testStr.equals(dumpStr)) {
+                    throw new Exception("different values:"
+                            + " expected \"" + testStr + "\", actual \"" + dumpStr + "\"");
+                }
+                log(logPrefix + objDescr + ": \"" + testStr + "\" ( == \"" + dumpStr + "\")");
+            } else {
+                // other Object
+                log(logPrefix + objDescr + ": Object " + obj);
+                if (isTestClass(obj.getClazz().getName())) {
+                    compareObjectFields(logPrefix + "  ", testObj, obj);
+                }
+            }
+        } else {
+            throw new Exception("Object expected, but the value (" + dumpObj + ")"
+                    + " is not JavaObject (" + dumpObj.getClass() + ")");
+        }
+    }
+
+    private static void compareObjectFields(String logPrefix, Object testObj, JavaObject dumpObj) throws Exception {
+        Field[] fields = testObj.getClass().getDeclaredFields();
+        for (Field testField : fields) {
+            boolean isStatic = Modifier.isStatic(testField.getModifiers());
+            testField.setAccessible(true);
+            objDescr = "- " + (isStatic ? "(static) " : "")
+                    + (PrimitiveClass.isPrimitiveClass(testField.getType()) ? "(primitive) " : "")
+                    + testField.getName() + " ('" + testField.getType().descriptorString() + "')";
+            try {
+                Object testValue = testField.get(testObj);
+
+                JavaField dumpField = getField(dumpObj, testField.getName(), isStatic);
+                JavaThing dumpValue = isStatic
+                        ? dumpObj.getClazz().getStaticField(dumpField.getName())
+                        : dumpObj.getField(dumpField.getName());
+
+                objDescr += ", dump signature '" + dumpField.getSignature() + "'";
+
+                compareType(testField, dumpField);
+
+                if (testValue == null) {
+                    if (!isNullValue(dumpValue)) {
+                        throw new Exception("null expected, but dumped object is " + dumpValue);
+                    }
+                    log(logPrefix + objDescr + ": null");
+                } else {
+                    switch (testField.getType().descriptorString().charAt(0)) {
+                    case 'L':
+                    case 'Q':
+                        compareObjects(logPrefix, testValue, dumpValue);
+                        break;
+                    case '[':
+                        int testLength = Array.getLength(testValue);
+                        objDescr += " (Array of '" + testField.getType().getComponentType() + "'"
+                                + ", length = " + testLength + ", " + dumpValue + ")";
+                        if (dumpValue instanceof JavaValueArray arr) {
+                            // array of primitive type
+                            char testElementType = testField.getType().getComponentType().descriptorString().charAt(0);
+                            if ((char)arr.getElementType() != testElementType) {
+                                throw new Exception("wrong element type: '" + (char)arr.getElementType() + "'");
+                            }
+                            int dumpLength = arr.getLength();
+                            if (dumpLength != testLength) {
+                                throw new Exception("wrong array size: " + dumpLength);
+                            }
+                            JavaThing[] dumpElements = arr.getElements();
+                            log(logPrefix + objDescr);
+                            for (int j = 0; j < testLength; j++) {
+                                Object elementValue = Array.get(testValue, j);
+                                objDescr = "[" + j + "]";
+                                if (arr.isFlatArray()) {
+                                    compareObjects(logPrefix + "  ", elementValue, dumpElements[j]);
+                                } else {
+                                    comparePrimitiveValues(elementValue, dumpElements[j]);
+                                    log(logPrefix + "  [" + j + "]: " + elementValue + " ( == " + dumpElements[j] + ")");
+                                }
+                            }
+                        } else if (dumpValue instanceof JavaObjectArray arr) {
+                            int dumpLength = arr.getLength();
+                            if (dumpLength != testLength) {
+                                throw new Exception("wrong array size: " + dumpLength);
+                            }
+                            JavaThing[] dumpElements = arr.getElements();
+                            log(logPrefix + objDescr);
+                            for (int j = 0; j < testLength; j++) {
+                                Object elementValue = Array.get(testValue, j);
+                                objDescr = "[" + j + "]";
+                                compareObjects(logPrefix + "  ", elementValue, dumpElements[j]);
+                            }
+                        } else {
+                            throw new Exception("Array expected, but the value (" + dumpValue + ")"
+                                    + " is neither JavaValueArray nor JavaObjectArray"
+                                    + " (" + dumpValue.getClass() + ")");
+                        }
+                        break;
+                    default:
+                        comparePrimitiveValues(testValue, dumpValue);
+                        log(logPrefix + objDescr + ": " + testValue + " ( == " + dumpValue + ")");
+                        break;
+                    }
+                }
+            } catch (Exception ex) {
+                if (!errorReported) {
+                    log(logPrefix + objDescr + ": ERROR - " + ex.getMessage());
+                    errorReported = true;
+                }
+                throw ex;
+            }
+        }
+    }
+
+    private static JavaField getField(JavaObject obj, String fieldName, boolean isStatic) throws Exception {
+        if (isStatic) {
+            JavaStatic[] statics = obj.getClazz().getStatics();
+            for (JavaStatic st: statics) {
+                if (st.getField().getName().equals(fieldName)) {
+                    return st.getField();
+                }
+            }
+        } else {
+            JavaField[] fields = obj.getClazz().getFields();
+            for (JavaField field : fields) {
+                if (fieldName.equals(field.getName())) {
+                    return field;
+                }
+            }
+        }
+        throw new Exception("field '" + fieldName + "' not found");
+    }
+
+    private static void compareType(Field field, JavaField dumpField) throws Exception {
+        String sig = field.getType().descriptorString();
+        char type = sig.charAt(0);
+        if (type == '[' || type == 'Q') {
+            type = 'L';
+        }
+        char dumpType = dumpField.getSignature().charAt(0);
+        if (dumpType == 'Q') {
+            dumpType = 'L';
+        }
+        if (dumpType != type) {
+            throw new Exception("type mismatch:"
+                    + " expected '" + type + "' (" + sig + ")"
+                    + ", found '" + dumpField.getSignature().charAt(0) + "' (" + dumpField.getSignature() + ")");
+        }
+    }
+
+    private static void comparePrimitiveValues(Object testValue, JavaThing dumpValue) throws Exception {
+        // JavaByte.toString() returns hex
+        String testStr = testValue instanceof Byte byteValue
+                ? (new JavaByte(byteValue)).toString()
+                : String.valueOf(testValue);
+        String dumpStr = dumpValue.toString();
+        if (!testStr.equals(dumpStr)) {
+            throw new Exception("Wrong value: expected " + testStr + ", actual " + dumpStr);
+        }
+    }
+
+    private static boolean isNullValue(JavaThing value) {
+        return value == null
+                // dumped value is HackJavaValue with string representation "<null>"
+                || (value instanceof HackJavaValue &&  "<null>".equals(value.toString()));
+    }
+
+    private static String getStringValue(JavaObject value) {
+        JavaThing valueObj = value.getField("value");
+        if (valueObj instanceof JavaValueArray valueArr) {
+            try {
+                if (valueArr.getElementType() == 'B') {
+                    Field valueField = JavaByte.class.getDeclaredField("value");
+                    valueField.setAccessible(true);
+                    JavaThing[] things = valueArr.getElements();
+                    byte[] bytes = new byte[things.length];
+                    for (int i = 0; i < things.length; i++) {
+                        bytes[i] = valueField.getByte(things[i]);
+                    }
+                    return new String(bytes);
+                }
+            } catch (Exception ignored) {
+            }
+            return valueArr.valueString();
+        } else {
+            return null;
+        }
+    }
+
+    private static void print(JavaObject dumpObject) {
+        log("Dumped object " + dumpObject + ":");
+        print("", dumpObject);
+    }
+
+    private static void print(String prefix, JavaObject dumpObject) {
+        JavaClass clazz = dumpObject.getClazz();
+        // print only test classes
+        if (!isTestClass(clazz.getName())) {
+            return;
+        }
+
+        JavaField[] fields = clazz.getFields();
+        for (JavaField field : fields) {
+            printFieldValue(prefix, field, false, dumpObject.getField(field.getName()));
+        }
+
+        JavaStatic[] statics = clazz.getStatics();
+        for (JavaStatic st: statics) {
+            printFieldValue(prefix, st.getField(), true, st.getValue());
+        }
+    }
+
+    private static void printFieldValue(String prefix, JavaField field, boolean isStatic, JavaThing value) {
+        String logPrefix = prefix + "- " + (isStatic ? "(static) " : "")
+                + field.getName() + " ('" + field.getSignature() + "'): ";
+        if (isNullValue(value)) {
+            log(logPrefix + "null");
+        } else {
+            if (value instanceof JavaObject obj) {
+                logPrefix += "(class '" + obj.getClazz().getName() + "'): ";
+                if (obj.getClazz().isString()) {
+                    String dumpStr = getStringValue(obj);
+                    log(logPrefix + "\"" + dumpStr + "\"");
+                } else {
+                    log(logPrefix + (value instanceof InlinedJavaObject ? "inlined " : "") + "object " + obj);
+                    print(prefix + "  ", obj);
+                }
+            } else if (value instanceof JavaObjectArray arr) {
+                log(logPrefix + " array " + arr + " length: " + arr.getLength());
+                JavaThing[] values = arr.getValues();
+                for (int v = 0; v < values.length; v++) {
+                    log(prefix + "  [" + v + "]: " + values[v]);
+                    if (values[v] instanceof JavaObject obj) {
+                        print(prefix + "    ", obj);
+                    }
+                }
+            } else if (value instanceof JavaValueArray arr) { // array of primitive type or flat array
+                if (arr.isFlatArray()) {
+                    log(logPrefix + " flat array " + arr + " length: " + arr.getLength());
+                    JavaThing[] values = arr.getElements();
+                    for (int v = 0; v < values.length; v++) {
+                        log(prefix + "  [" + v + "]: " + values[v]);
+                        if (values[v] instanceof JavaObject obj) {
+                            print(prefix + "    ", obj);
+                        }
+                    }
+                } else {
+                    log(logPrefix + "(array of '" + (char)arr.getElementType() + "')" + ": " + arr.valueString());
+                }
+            } else {
+                log(logPrefix + "(" + value.getClass() +  ")" + value.toString());
+            }
+        }
+    }
+
+    private static boolean isTestClass(String className) {
+        return className.startsWith("TestClass");
+    }
+
+    private static void log(String msg) {
+        System.out.println(msg);
+        System.out.flush();
+    }
+
+}
diff a/test/hotspot/jtreg/serviceability/sa/ClhsdbDumpclass.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbDumpclass.java
--- a/test/hotspot/jtreg/serviceability/sa/ClhsdbDumpclass.java
+++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbDumpclass.java
@@ -85,11 +85,11 @@
             OutputAnalyzer out = new OutputAnalyzer(javap);
             javap.waitFor();
             System.out.println(out.getStdout());
             System.err.println(out.getStderr());
             out.shouldHaveExitValue(0);
-            out.shouldMatch("public class " + APP_DOT_CLASSNAME);
+            out.shouldMatch("public identity class " + APP_DOT_CLASSNAME);
             // StackMapTable might not be generated for a class
             // containing only methods with sequential control flows.
             // But the class used here (LingeredApp) is not such a case.
             out.shouldContain("StackMapTable:");
             out.shouldContain("BootstrapMethods:");
diff a/test/hotspot/jtreg/serviceability/tmtools/jstack/WaitNotifyThreadTest.java b/test/hotspot/jtreg/serviceability/tmtools/jstack/WaitNotifyThreadTest.java
--- a/test/hotspot/jtreg/serviceability/tmtools/jstack/WaitNotifyThreadTest.java
+++ b/test/hotspot/jtreg/serviceability/tmtools/jstack/WaitNotifyThreadTest.java
@@ -36,11 +36,11 @@
 import java.util.Iterator;
 import utils.*;
 
 public class WaitNotifyThreadTest {
 
-    private Object monitor = new Object();
+    private Object monitor = new Object();;
     private final String OBJECT = "a java.lang.Object";
     private final String OBJECT_WAIT = "java.lang.Object.wait0";
     private final String RUN_METHOD = "WaitNotifyThreadTest$WaitThread.run";
 
     interface Action {
diff a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/modifiers/modifiers001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/modifiers/modifiers001.java
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/modifiers/modifiers001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/modifiers/modifiers001.java
@@ -186,13 +186,10 @@
             }
             int expected_modifiers = class_obj.getModifiers();
             String s_type = classes_for_check[i][2];
             String s_modifiers = classes_for_check[i][1];
             int got_modifiers = refType.modifiers();
-            // Class.getModifiers() will never return ACC_SUPER
-            // but Accessible.modifers() can, so ignore this bit
-            got_modifiers &= ~0x20; // 0x20 == ACC_SUPER
             logHandler.display("");
             if ( got_modifiers != expected_modifiers ) {
                 logHandler.complain("##> modifiers001: UNEXPECTED modifiers() method result ("
                                     + "0x" + Integer.toHexString(got_modifiers) + ") for " + s_type + ": " + className
                                     + "(" +  s_modifiers + ")");
diff a/test/hotspot/jtreg/vmTestbase/nsk/jdwp/ReferenceType/Interfaces/interfaces001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdwp/ReferenceType/Interfaces/interfaces001.java
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdwp/ReferenceType/Interfaces/interfaces001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdwp/ReferenceType/Interfaces/interfaces001.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2020, 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.
@@ -137,11 +137,11 @@
                         long interfaceID = reply.getReferenceTypeID();
                         log.display("    interfaceID: " + interfaceID);
 
                         if (interfaceID != interfaceIDs[i]) {
                             log.complain("Unexpected interface ID for interface #" + i + " in the reply packet: " + interfaceID
-                                        + " (expected: " + interfaceIDs[i] + ")");
+                                         + " (expected: " + interfaceIDs[i] + ")");
                             success = false;
                         }
 
                     }
 
diff a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetClassModifiers/getclmdf006.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetClassModifiers/getclmdf006.java
--- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetClassModifiers/getclmdf006.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetClassModifiers/getclmdf006.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2022, 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.
diff a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetClassModifiers/getclmdf006/getclmdf006.cpp b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetClassModifiers/getclmdf006/getclmdf006.cpp
--- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetClassModifiers/getclmdf006/getclmdf006.cpp
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetClassModifiers/getclmdf006/getclmdf006.cpp
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2022, 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.
diff a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage007.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage007.java
--- /dev/null
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage007.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020, 2021, 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 nsk.jvmti.GetObjectMonitorUsage;
+
+import java.io.PrintStream;
+
+public class objmonusage007 {
+    final static int JCK_STATUS_BASE = 95;
+    final static int NUMBER_OF_THREADS = 32;
+
+    static {
+        try {
+            System.loadLibrary("objmonusage007");
+        } catch (UnsatisfiedLinkError err) {
+            System.err.println("Could not load objmonusage7 library");
+            System.err.println("java.library.path:"
+                + System.getProperty("java.library.path"));
+            throw err;
+        }
+    }
+
+    native static int getResult();
+    native static void check(Object o, Thread owner, int ec, int wc);
+
+    static value class LocalValue {
+        int i = 0;
+    }
+
+    public static void main(String argv[]) {
+        argv = nsk.share.jvmti.JVMTITest.commonInit(argv);
+
+        System.exit(run(argv, System.out) + JCK_STATUS_BASE);
+    }
+
+    public static int run(String argv[], PrintStream out) {
+        LocalValue lv = new LocalValue();
+        objmonusage007.check(lv, null, 0, 0);
+
+        return getResult();
+    }
+}
+
diff a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage007/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage007/TestDescription.java
--- /dev/null
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage007/TestDescription.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020, 2020, 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
+ * VM Testbase keywords: [quick, jpda, jvmti, noras]
+ * VM Testbase readme:
+ * DESCRIPTION
+ *     The test exercises JVMTI function GetObjectMonitorUsage.
+ * COMMENTS
+ *     This test checks that GetObjectMonitorUsage works with inline types and always
+ *     returns information consistent with a never locked monitor
+ *
+ * @library /vmTestbase
+ *          /test/lib
+ * @run driver jdk.test.lib.FileInstaller . .
+ * @run main/othervm/native -agentlib:objmonusage007 nsk.jvmti.GetObjectMonitorUsage.objmonusage007
+ */
+
diff a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage007/libobjmonusage007.cpp b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage007/libobjmonusage007.cpp
--- /dev/null
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage007/libobjmonusage007.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2020, 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 "native_thread.cpp"
+#include "nsk_tools.cpp"
+#include "jni_tools.cpp"
+#include "jvmti_tools.cpp"
+#include "agent_tools.cpp"
+#include "jvmti_FollowRefObjects.cpp"
+#include "Injector.cpp"
+#include "JVMTITools.cpp"
+#include "agent_common.cpp"
+#include "objmonusage007.cpp"
diff a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage007/objmonusage007.cpp b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage007/objmonusage007.cpp
--- /dev/null
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetObjectMonitorUsage/objmonusage007/objmonusage007.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2020, 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 <stdio.h>
+#include <string.h>
+#include "jvmti.h"
+#include "agent_common.h"
+#include "JVMTITools.h"
+
+extern "C" {
+
+
+#define PASSED 0
+#define STATUS_FAILED 2
+
+static jvmtiEnv *jvmti = NULL;
+static jvmtiCapabilities caps;
+static jint result = PASSED;
+static jboolean printdump = JNI_FALSE;
+
+#ifdef STATIC_BUILD
+JNIEXPORT jint JNICALL Agent_OnLoad_objmonusage007(JavaVM *jvm, char *options, void *reserved) {
+    return Agent_Initialize(jvm, options, reserved);
+}
+JNIEXPORT jint JNICALL Agent_OnAttach_objmonusage007(JavaVM *jvm, char *options, void *reserved) {
+    return Agent_Initialize(jvm, options, reserved);
+}
+JNIEXPORT jint JNI_OnLoad_objmonusage007(JavaVM *jvm, char *options, void *reserved) {
+    return JNI_VERSION_1_8;
+}
+#endif
+jint  Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
+    jint res;
+    jvmtiError err;
+
+    if (options != NULL && strcmp(options, "printdump") == 0) {
+        printdump = JNI_TRUE;
+    }
+
+    res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);
+    if (res != JNI_OK || jvmti == NULL) {
+        printf("Wrong result of a valid call to GetEnv !\n");
+        return JNI_ERR;
+    }
+
+    err = jvmti->GetPotentialCapabilities(&caps);
+    if (err != JVMTI_ERROR_NONE) {
+        printf("(GetPotentialCapabilities) unexpected error: %s (%d)\n",
+               TranslateError(err), err);
+        return JNI_ERR;
+    }
+
+    err = jvmti->AddCapabilities(&caps);
+    if (err != JVMTI_ERROR_NONE) {
+        printf("(AddCapabilities) unexpected error: %s (%d)\n",
+               TranslateError(err), err);
+        return JNI_ERR;
+    }
+
+    err = jvmti->GetCapabilities(&caps);
+    if (err != JVMTI_ERROR_NONE) {
+        printf("(GetCapabilities) unexpected error: %s (%d)\n",
+               TranslateError(err), err);
+        return JNI_ERR;
+    }
+
+    if (!caps.can_get_monitor_info) {
+        printf("Warning: GetObjectMonitorUsage is not implemented\n");
+    }
+
+    return JNI_OK;
+}
+
+JNIEXPORT void JNICALL
+Java_nsk_jvmti_GetObjectMonitorUsage_objmonusage007_check(JNIEnv *env,
+        jclass cls, jobject obj, jthread owner,
+        jint entryCount, jint waiterCount) {
+    jvmtiError err;
+    jvmtiMonitorUsage inf;
+    jvmtiThreadInfo tinf;
+    int j;
+
+    if (result == STATUS_FAILED) {
+        return;
+    }
+
+    err = jvmti->GetObjectMonitorUsage(obj, &inf);
+    if (err == JVMTI_ERROR_MUST_POSSESS_CAPABILITY &&
+            !caps.can_get_monitor_info) {
+        /* Ok, it's expected */
+        return;
+    } else if (err != JVMTI_ERROR_NONE) {
+        printf("(GetMonitorInfo) unexpected error: %s (%d)\n",
+               TranslateError(err), err);
+        result = STATUS_FAILED;
+        return;
+    }
+
+    if (printdump == JNI_TRUE) {
+        if (inf.owner == NULL) {
+            printf(">>>     owner: none (0x0)\n");
+        } else {
+            jvmti->GetThreadInfo(inf.owner, &tinf);
+            printf(">>>     owner: %s (0x%p)\n",
+                   tinf.name, inf.owner);
+        }
+        printf(">>>   entry_count: %d\n", inf.entry_count);
+        printf(">>>  waiter_count: %d\n", inf.waiter_count);
+        if (inf.waiter_count > 0) {
+            printf(">>>       waiters:\n");
+            for (j = 0; j < inf.waiter_count; j++) {
+                jvmti->GetThreadInfo(inf.waiters[j], &tinf);
+                printf(">>>                %2d: %s (0x%p)\n",
+                       j, tinf.name, inf.waiters[j]);
+            }
+        }
+        printf(">>>  notify_waiter_count: %d\n", inf.notify_waiter_count);
+        if (inf.notify_waiter_count > 0) {
+            printf(">>>       notify_waiters:\n");
+            for (j = 0; j < inf.notify_waiter_count; j++) {
+                jvmti->GetThreadInfo(inf.notify_waiters[j], &tinf);
+                printf(">>>                %2d: %s (0x%p)\n",
+                       j, tinf.name, inf.notify_waiters[j]);
+            }
+        }
+    }
+
+    if (!env->IsSameObject(owner, inf.owner)) {
+        jvmti->GetThreadInfo(inf.owner, &tinf);
+        printf(" unexpected owner: %s (0x%p)\n", tinf.name, inf.owner);
+        result = STATUS_FAILED;
+    }
+
+    if (inf.entry_count != entryCount) {
+        printf(" entry_count expected: %d, actually: %d\n",
+               entryCount, inf.entry_count);
+        result = STATUS_FAILED;
+    }
+
+    if (inf.waiter_count != waiterCount) {
+        printf(" waiter_count expected: %d, actually: %d\n",
+               waiterCount, inf.waiter_count);
+        result = STATUS_FAILED;
+    }
+}
+
+JNIEXPORT jint JNICALL
+Java_nsk_jvmti_GetObjectMonitorUsage_objmonusage007_getResult(JNIEnv *env, jclass cls) {
+    return result;
+}
+
+}
diff a/test/jdk/ProblemList-Virtual.txt b/test/jdk/ProblemList-Virtual.txt
--- a/test/jdk/ProblemList-Virtual.txt
+++ b/test/jdk/ProblemList-Virtual.txt
@@ -74,5 +74,8 @@
 java/lang/SecurityManager/modules/CustomSecurityManagerTest.java 0000000 generic-all
 java/util/PluggableLocale/PermissionTest.java 0000000 generic-all
 java/util/Properties/StoreReproducibilityTest.java 0000000 generic-all
 java/util/Properties/StoreReproducibilityTest.java 0000000 generic-all
 javax/management/remote/mandatory/subjectDelegation/SubjectDelegation1Test.java 0000000 generic-all
+
+# Valhalla
+java/lang/Thread/virtual/stress/PingPong.java 8314996 macosx-all
diff a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt
--- a/test/jdk/ProblemList.txt
+++ b/test/jdk/ProblemList.txt
@@ -708,10 +708,14 @@
 
 com/sun/jdi/RepStep.java                                        8043571 generic-all
 
 com/sun/jdi/InvokeHangTest.java                                 8218463 linux-all
 
+com/sun/jdi/cds/CDSBreakpointTest.java                          8304168 generic-all
+com/sun/jdi/cds/CDSDeleteAllBkptsTest.java                      8304168 generic-all
+com/sun/jdi/cds/CDSFieldWatchpoints.java                        8304168 generic-all
+
 ############################################################################
 
 # jdk_time
 
 ############################################################################
@@ -756,10 +760,16 @@
 
 ############################################################################
 
 # jdk_internal
 
+jdk/classfile/SwapTest.java                                     8308778 generic-all
+jdk/classfile/LowAdaptTest.java                                 8308778 generic-all
+jdk/classfile/BuilderBlockTest.java                             8308778 generic-all
+jdk/classfile/BuilderTryCatchTest.java                          8308778 generic-all
+jdk/classfile/PrimitiveClassConstantTest.java                   8310649 generic-all
+
 ############################################################################
 
 # jdk_jpackage
 
 ############################################################################
@@ -795,5 +805,8 @@
 java/awt/TextArea/TextAreaCursorTest/HoveringAndDraggingTest.java 8024986 macosx-all,linux-all
 java/awt/event/MouseEvent/SpuriousExitEnter/SpuriousExitEnter.java 8254841 macosx-all
 java/awt/Focus/AppletInitialFocusTest/AppletInitialFocusTest1.java 8256289 windows-x64
 java/awt/FullScreen/TranslucentWindow/TranslucentWindow.java 8258103 linux-all
 java/awt/Focus/FrameMinimizeTest/FrameMinimizeTest.java 8016266 linux-x64
+
+# valhalla
+jdk/jfr/event/runtime/TestSyncOnValueBasedClassEvent.java 8328777 generic-all
diff a/test/jdk/TEST.groups b/test/jdk/TEST.groups
--- a/test/jdk/TEST.groups
+++ b/test/jdk/TEST.groups
@@ -113,11 +113,18 @@
     jdk/internal/ref \
     jdk/internal/jimage \
     jdk/internal/math \
     jdk/internal/vm \
     jdk/modules \
-    jni/nullCaller
+    jni/nullCaller \
+    valhalla
+
+# valhalla lworld tests
+jdk_valhalla = \
+    java/lang/invoke \
+    valhalla
+
 
 # All of the java.util package
 jdk_util = \
     :jdk_util_other \
     :jdk_collections \
diff a/test/jdk/java/io/Serializable/records/BasicRecordSer.java b/test/jdk/java/io/Serializable/records/BasicRecordSer.java
--- a/test/jdk/java/io/Serializable/records/BasicRecordSer.java
+++ b/test/jdk/java/io/Serializable/records/BasicRecordSer.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2019, 2020, 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.
@@ -21,13 +21,14 @@
  * questions.
  */
 
 /*
  * @test
- * @bug 8246774
+ * @bug 8246774 8326879
  * @summary Basic test that serializes and deserializes a number of records
  * @run testng BasicRecordSer
+ * @run testng/othervm --enable-preview BasicRecordSer
  * @run testng/othervm/java.security.policy=empty_security.policy BasicRecordSer
  */
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
diff a/test/jdk/java/io/Serializable/records/RecordClassTest.java b/test/jdk/java/io/Serializable/records/RecordClassTest.java
--- a/test/jdk/java/io/Serializable/records/RecordClassTest.java
+++ b/test/jdk/java/io/Serializable/records/RecordClassTest.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2019, 2020, 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.
@@ -21,13 +21,14 @@
  * questions.
  */
 
 /*
  * @test
- * @bug 8246774
+ * @bug 8246774 8326879
  * @summary Basic tests for serializing and deserializing record classes
  * @run testng RecordClassTest
+ * @run testng/othervm --enable-preview RecordClassTest
  * @run testng/othervm/java.security.policy=empty_security.policy RecordClassTest
  */
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
diff a/test/jdk/java/io/Serializable/valueObjects/SimpleValueGraphs.java b/test/jdk/java/io/Serializable/valueObjects/SimpleValueGraphs.java
--- /dev/null
+++ b/test/jdk/java/io/Serializable/valueObjects/SimpleValueGraphs.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (c) 1999, 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
+ * @library /test/lib
+ * @summary Serialize and deserialize value objects
+ * @enablePreview
+ * @run testng/othervm SimpleValueGraphs
+ */
+
+import java.lang.StackOverflowError;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.io.InvalidClassException;
+import java.io.InvalidObjectException;
+import java.io.NotSerializableException;
+
+import java.util.Arrays;
+import java.util.function.BiFunction;
+import java.util.Objects;
+
+import java.nio.charset.StandardCharsets;
+
+import org.testng.Assert;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import jdk.test.lib.hexdump.HexPrinter;
+import jdk.test.lib.hexdump.ObjectStreamPrinter;
+
+@Test
+public class SimpleValueGraphs implements Serializable {
+
+    private static boolean DEBUG = false;
+
+    private static SimpleValue foo1 = new SimpleValue("One", 1);
+    private static SimpleValue foo2 = new SimpleValue("Two", 2);
+
+    @DataProvider(name = "ValueObjects")
+    public Object[][] valueObjects() {
+        return new Object[][] {
+                {new SimpleValue(new SimpleValue(1))},
+                {new SimpleValue(new SimpleValue(2), 3)},
+                {new SimpleValue[] {foo1, foo1, foo2, foo1, foo2 }},
+        };
+    }
+
+    @Test(enabled = true, dataProvider = "ValueObjects")
+    public void roundTrip(Object expected) throws Exception {
+        byte[] bytes = serialize(expected);
+
+        if (DEBUG)
+            HexPrinter.simple().dest(System.out).formatter(ObjectStreamPrinter.formatter()).format(bytes);
+
+        Object actual = deserialize(bytes);
+        System.out.println("actual: " + actual.toString());
+        Assert.assertEquals(actual, expected, "Mismatch" + expected.getClass());
+    }
+
+    private static Tree treeI = Tree.makeTree(3, (l, r) -> new TreeI((TreeI)l, (TreeI)l));
+    private static Tree treeV = Tree.makeTree(3, (l, r) -> new TreeV((TreeV)l, (TreeV)l));
+
+    // Create a tree of identity objects with a cycle; it will serialize ok, but when deserialized as
+    // a value class the cycle is broken by replacing the back ref with null.
+    private static Tree treeCycle(boolean cycle) {
+        TreeI tree = (TreeI)Tree.makeTree(3, (l, r) -> new TreeI((TreeI)l, (TreeI)l));
+        tree.setLeft(cycle ? tree : null);     // force a cycle or null
+        return tree;
+    }
+
+    @DataProvider(name = "CompatibleChanges")
+    public Object[][] migrationObjects() {
+        return new Object[][] {
+                {treeI, "TreeI", "TreeV", treeV},       // Serialize as an identity class, deserialize as Value class
+                {treeCycle(true), "TreeI", "TreeV", treeCycle(false)},
+                // TBD: add cases for serializing TreeV and converting to TreeI:
+                // Waiting for:
+                // JDK-8293321: [lworld] PrimitiveObjectMethods.substitutableInvoker StackOverFlowError
+        };
+    }
+
+    /**
+     * Test serializing a object graph, and deserialize with a modification of the serialized form.
+     * The modifications to the stream change the class name being deserialized.
+     * The cases include serializing an identity class and deserialize the corresponding
+     * value class.
+     *
+     * @param origObj an object to serialize
+     * @param origName a string in the serialized stream to replace
+     * @param replName a string to replace the original string
+     * @param expectedObject the expected object (graph) or an exception if it should fail
+     * @throws Exception some unexpected exception may be thrown and cause the test to fail
+     */
+    @Test(enabled = true, dataProvider = "CompatibleChanges")
+    public void treeVTest(Object origObj, String origName, String replName, Object expectedObject) throws Exception {
+        byte[] bytes = serialize(origObj);
+        if (DEBUG) {
+            System.out.println("Original serialized " + origObj.getClass().getName());
+            HexPrinter.simple().dest(System.out).formatter(ObjectStreamPrinter.formatter()).format(bytes);
+        }
+
+        // Modify the serialized bytes to change a class name from the serialized name
+        // to a different class. The replacement name must be the same length as thr original name.
+        byte[] replBytes = patchBytes(bytes, origName, replName);
+        if (DEBUG) {
+            System.out.println("Modified serialized " + origObj.getClass().getName());
+            HexPrinter.simple().dest(System.out).formatter(ObjectStreamPrinter.formatter()).format(replBytes);
+        }
+        try {
+            Object actual = deserialize(replBytes);
+            if (expectedObject instanceof Exception ex) {
+                Assert.fail("Unexpected: " + expectedObject);
+            }
+            // Compare the shape of the actual and expected trees
+            Assert.assertEquals(actual.toString(), expectedObject.toString(),
+                    "Resulting object not equals: " + actual.getClass().getName());
+
+        } catch (Exception ex) {
+            Assert.assertEquals(ex.getClass(), expectedObject.getClass(), "exception type");
+            Assert.assertEquals(ex.getMessage(), ((Exception)expectedObject).getMessage(), "exception message");
+        }
+    }
+
+    /**
+     * Serialize an object and return the serialized bytes.
+     *
+     * @param expected an object to serialize
+     * @return a byte array containing the serialized object
+     */
+    private static byte[] serialize(Object expected) throws IOException {
+        try (ByteArrayOutputStream bout = new ByteArrayOutputStream();
+             ObjectOutputStream oout = new ObjectOutputStream(bout)) {
+            oout.writeObject(expected);
+            oout.flush();
+            return bout.toByteArray();
+        }
+    }
+
+    /**
+     * Deserialize an object from the byte array.
+     * @param bytes a byte array
+     * @return an Object read from the byte array
+     */
+    private static Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
+        try (ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
+             ObjectInputStream oin = new ObjectInputStream(bin)) {
+            return oin.readObject();
+        }
+    }
+
+    /**
+     * Replace every occurence of the string in the byte array with the replacement.
+     * The strings are US_ASCII only.
+     * @param bytes a byte array
+     * @param orig a string, converted to bytes using US_ASCII, originally exists in the bytes
+     * @param repl a string, converted to byted using US_ASCII, to replace the original bytes
+     * @return a new byte array that has been patched
+     */
+    private byte[] patchBytes(byte[] bytes, String orig, String repl) {
+        byte[] alt = patchBytes(bytes,
+                orig.getBytes(StandardCharsets.US_ASCII),
+                repl.getBytes(StandardCharsets.US_ASCII));
+        return alt;
+    }
+
+    /**
+     * Replace every occurence of the original bytes in the byte array with the replacement bytes.
+     * @param bytes a byte array
+     * @param orig a byte array containing existing bytes in the byte array
+     * @param repl a byte array to replace the original bytes
+     * @return a copy of the bytes array with each occurence of the orig bytes with the replacement bytes
+     */
+    static byte[] patchBytes(byte[] bytes, byte[] orig, byte[] repl) {
+        if (orig.length != repl.length && orig.length > 0)
+            throw new IllegalArgumentException("orig bytes and replacement must be same length");
+        byte[] result = Arrays.copyOf(bytes, bytes.length);
+        for (int i = 0; i < result.length - orig.length; i++) {
+            if (Arrays.equals(result, i, i + orig.length, orig, 0, orig.length)) {
+                for (int j = 0; j < orig.length; j++) {
+                    result[i + j] = repl[j];
+                }
+                i = i + orig.length - 1;    // continue replacing after this occurrence
+            }
+        }
+        return result;
+    }
+
+    public static class SimpleValue implements Serializable {
+        private static final long serialVersionUID = 1L;
+
+        int i;
+        Serializable obj;
+
+        public SimpleValue(Serializable o) {
+            this.obj = o;
+            this.i = 0;
+        }
+        SimpleValue(int i) {
+            this.i = i;
+        }
+
+        public SimpleValue(Serializable o, int i) {
+            this.obj = o;
+            this.i = i;
+        }
+
+        public boolean equals(Object obj) {
+            if (obj instanceof SimpleValue simpleValue) {
+                return (i == simpleValue.i && Objects.equals(obj, simpleValue));
+            }
+            return false;
+        }
+
+        public int hashCode() {
+            return i;
+        }
+
+        public String toString() {
+            return "SimpleValue{" + "i=" + i + ", obj=" + obj + '}';
+        }
+    }
+
+    interface Tree {
+        static Tree makeTree(int depth, BiFunction<Tree, Tree, Tree> genNode) {
+            if (depth <= 0) return null;
+            Tree left = makeTree(depth - 1, genNode);
+            Tree right = makeTree(depth - 1, genNode);
+            Tree t = genNode.apply(left, right);
+            return t;
+        }
+
+        Tree left();
+        Tree right();
+    }
+    static class TreeI implements Tree, Serializable {
+
+        private static final long serialVersionUID = 2L;
+        private TreeI left;
+        private TreeI right;
+
+        TreeI(TreeI left, TreeI right) {
+            this.left = left;
+            this.right = right;
+        }
+
+        public TreeI left() {
+            return left;
+        }
+        public TreeI right() {
+            return right;
+        }
+
+        public void setLeft(TreeI left) {
+            this.left = left;
+        }
+        public void setRight(TreeI right) {
+            this.right = right;
+        }
+
+        public boolean equals(Object other) {
+            // avoid ==, is substutible check causes stack overflow.
+            if (other instanceof TreeV tree) {
+                boolean leftEq = (this.left == null && tree.left == null) ||
+                        left.equals(tree.left);
+                boolean rightEq = (this.right == null && tree.right == null) ||
+                        right.equals(tree.right);
+                return leftEq == rightEq;
+            }
+            return false;
+        }
+        public String toString() {
+            return toString(5);
+        }
+        public String toString(int depth) {
+            if (depth <= 0)
+                return "!";
+            String l = (left != null) ? left.toString(depth - 1) : Character.toString(126);
+            String r = (right != null) ? right.toString(depth - 1) : Character.toString(126);
+            return "(" + l + r + ")";
+        }
+    }
+
+    static value class TreeV implements Tree, Serializable {
+
+        private static final long serialVersionUID = 2L;
+        private TreeV left;
+        private TreeV right;
+
+        TreeV(TreeV left, TreeV right) {
+            this.left = left;
+            this.right = right;
+        }
+
+        public TreeV left() {
+            return left;
+        }
+        public TreeV right() {
+            return right;
+        }
+
+        public boolean equals(Object other) {
+            // avoid ==, is substutible check causes stack overflow.
+            if (other instanceof TreeV tree) {
+                return compRef(this.left, tree.left) && compRef(this.right, tree.right);
+            }
+            return false;
+        }
+
+        // Compare references but don't use ==; isSubstutitable may recurse
+        private static boolean compRef(Object o1, Object o2) {
+            if (o1 == null && o2 == null)
+                return true;
+            if (o1 != null && o2 != null)
+                return o1.equals(o2);
+            return false;
+
+        }
+        public String toString() {
+            return toString(10);
+        }
+        public String toString(int depth) {
+            if (depth <= 0)
+                return "!";
+            String l = (left != null) ? left.toString(depth - 1) : Character.toString(126);
+            String r = (right != null) ? right.toString(depth - 1) : Character.toString(126);
+            return "(" + l + r + ")";
+        }
+    }
+
+    @Test
+    void testExternalizableNotSer() {
+        var obj = new ValueExt();
+        var ex = Assert.expectThrows(NotSerializableException.class, () -> serialize(obj));
+        Assert.assertTrue(ex.getMessage().contains("Externalizable not valid for value class"));
+    }
+
+    @Test
+    void testExternalizableNotDeser() throws IOException {
+        var obj = new IdentExt();
+        byte[] bytes = serialize(obj);
+        byte[] newBytes = patchBytes(bytes, "IdentExt", "ValueExt");
+        var ex = Assert.expectThrows(NotSerializableException.class, () -> deserialize(newBytes));
+        Assert.assertTrue(ex.getMessage().contains("Externalizable not valid for value class"));
+    }
+
+    // Exception trying to serialize
+    // Exception trying to deserialize
+
+    static class IdentExt implements Externalizable {
+        public void writeExternal(ObjectOutput is) {
+
+        }
+        public void readExternal(ObjectInput is) {
+
+        }
+        private static final long serialVersionUID = 3L;
+    }
+
+    static value class ValueExt implements Externalizable {
+        public void writeExternal(ObjectOutput is) {
+
+        }
+        public void readExternal(ObjectInput is) {
+
+        }
+        private static final long serialVersionUID = 3L;
+
+    }
+}
diff a/test/jdk/java/io/Serializable/valueObjects/ValueSerialization.java b/test/jdk/java/io/Serializable/valueObjects/ValueSerialization.java
--- /dev/null
+++ b/test/jdk/java/io/Serializable/valueObjects/ValueSerialization.java
@@ -0,0 +1,292 @@
+/*
+ * 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.
+ *
+ * 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 ValueSerialization support of value classes
+ * @enablePreview
+ * @compile ValueSerialization.java
+ * @run testng/othervm ValueSerialization
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.InvalidClassException;
+import java.io.InvalidObjectException;
+import java.io.NotSerializableException;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static java.io.ObjectStreamConstants.*;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.expectThrows;
+
+public class ValueSerialization {
+
+    static final Class<NotSerializableException> NSE = NotSerializableException.class;
+
+    @DataProvider(name = "doesNotImplementSerializable")
+    public Object[][] doesNotImplementSerializable() {
+        return new Object[][] {
+            new Object[] { new NonSerializablePoint(10, 100) },
+            // an array of Points
+            new Object[] { new NonSerializablePoint[] {new NonSerializablePoint(1, 5)} },
+            new Object[] { new Object[] {new NonSerializablePoint(3, 7)} },
+            new Object[] { new ExternalizablePoint(12, 102) },
+            new Object[] { new ExternalizablePoint[] {
+                    new ExternalizablePoint(3, 7),
+                    new ExternalizablePoint(2, 8) } },
+            new Object[] { new Object[] {
+                    new ExternalizablePoint(13, 17),
+                    new ExternalizablePoint(14, 18) } },
+        };
+    }
+
+    // value class that DOES NOT implement Serializable should throw NSE
+    @Test(dataProvider = "doesNotImplementSerializable")
+    public void doesNotImplementSerializable(Object obj) {
+        assertThrows(NSE, () -> serialize(obj));
+    }
+
+    /** Non-Serializable point. */
+    public static value class NonSerializablePoint {
+        public int x;
+        public int y;
+
+        public NonSerializablePoint(int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+    }
+
+    /** A Serializable value class Point */
+    static value class SerializablePoint implements Serializable {
+        public int x;
+        public int y;
+        SerializablePoint(int x, int y) { this.x = x; this.y = y; }
+        @Override public String toString() {
+            return "[SerializablePoint x=" + x + " y=" + y + "]"; }
+    }
+
+    /** A Serializable value class Point */
+    static value class SerializablePrimitivePoint implements Serializable {
+        public int x;
+        public int y;
+        SerializablePrimitivePoint(int x, int y) { this.x = x; this.y = y; }
+        @Override public String toString() {
+            return "[SerializablePrimitivePoint x=" + x + " y=" + y + "]"; }
+    }
+
+    /** An Externalizable Point is not Serializable, readExternal cannot modify fields */
+    static value class ExternalizablePoint implements Externalizable {
+        public int x;
+        public int y;
+        ExternalizablePoint(int x, int y) { this.x = x; this.y = y; }
+        @Override public void readExternal(ObjectInput in) {  }
+        @Override public void writeExternal(ObjectOutput out) {  }
+        @Override public String toString() {
+            return "[ExternalizablePoint x=" + x + " y=" + y + "]"; }
+    }
+
+    @DataProvider(name = "doesImplementSerializable")
+    public Object[][] doesImplementSerializable() {
+        return new Object[][] {
+            new Object[] { new SerializablePoint(11, 101) },
+            new Object[] { new SerializablePoint[] {
+                    new SerializablePoint(1, 5),
+                    new SerializablePoint(2, 6) } },
+            new Object[] { new Object[] {
+                    new SerializablePoint(3, 7),
+                    new SerializablePoint(4, 8) } },
+            new Object[] { new SerializablePrimitivePoint(711, 7101) },
+            new Object[] { new SerializablePrimitivePoint[] {
+                    new SerializablePrimitivePoint(71, 75),
+                    new SerializablePrimitivePoint(72, 76) } },
+            new Object[] { new Object[] {
+                    new SerializablePrimitivePoint(73, 77),
+                    new SerializablePrimitivePoint(74, 78) } },
+        };
+    }
+
+    // value class that DOES implement Serializable all supported
+    @Test(dataProvider = "doesImplementSerializable")
+    public void doesImplementSerializable(Object obj) throws IOException {
+        serialize(obj);
+    }
+
+    /** A Serializable Foo, with a serial proxy */
+    static value class SerializableFoo implements Serializable {
+        public int x;
+        SerializableFoo(int x) { this.x = x; }
+
+        Object writeReplace() throws ObjectStreamException {
+            return new SerialFooProxy(x);
+        }
+        private void readObject(ObjectInputStream s) throws InvalidObjectException {
+            throw new InvalidObjectException("Proxy required");
+        }
+        private static class SerialFooProxy implements Serializable {
+            final int x;
+            SerialFooProxy(int x) { this.x = x; }
+            Object readResolve() throws ObjectStreamException {
+                return new SerializableFoo(this.x);
+            }
+        }
+    }
+
+    /** An Externalizable Foo, with a serial proxy */
+    static value class ExternalizableFoo implements Externalizable {
+        public String s;
+        ExternalizableFoo(String s) {  this.s = s; }
+        Object writeReplace() throws ObjectStreamException {
+            return new SerialFooProxy(s);
+        }
+        private void readObject(ObjectInputStream s) throws InvalidObjectException {
+            throw new InvalidObjectException("Proxy required");
+        }
+        private static class SerialFooProxy implements Serializable {
+            final String s;
+            SerialFooProxy(String s) { this.s = s; }
+            Object readResolve() throws ObjectStreamException {
+                return new ExternalizableFoo(this.s);
+            }
+        }
+        @Override public void readExternal(ObjectInput in) {  }
+        @Override public void writeExternal(ObjectOutput out) {  }
+    }
+
+    // value classes that DO implement Serializable, but have a serial proxy
+    @Test
+    public void serializableFooWithProxy() throws Exception {
+        SerializableFoo foo = new SerializableFoo(45);
+        SerializableFoo foo1 = serializeDeserialize(foo);
+        assertEquals(foo.x, foo1.x);
+    }
+    @Test
+    public void serializableFooArrayWithProxy() throws Exception {
+        SerializableFoo[] fooArray = new SerializableFoo[]{new SerializableFoo(46)};
+        SerializableFoo[] fooArray1 = serializeDeserialize(fooArray);
+        assertEquals(fooArray.length, fooArray1.length);
+        assertEquals(fooArray[0].x, fooArray1[0].x);
+    }
+    @Test
+    public void externalizableFooWithProxy() throws Exception {
+        ExternalizableFoo foo = new ExternalizableFoo("hello");
+        ExternalizableFoo foo1 = serializeDeserialize(foo);
+        assertEquals(foo.s, foo1.s);
+    }
+    @Test
+    public void externalizableFooArrayWithProxy() throws Exception {
+        ExternalizableFoo[] fooArray = new ExternalizableFoo[] { new ExternalizableFoo("there") };
+        ExternalizableFoo[] fooArray1 = serializeDeserialize(fooArray);
+        assertEquals(fooArray.length, fooArray1.length);
+        assertEquals(fooArray[0].s, fooArray1[0].s);
+    }
+
+    private static byte[] byteStreamFor(String className, long uid, byte flags)
+        throws Exception
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        DataOutputStream dos = new DataOutputStream(baos);
+        dos.writeShort(STREAM_MAGIC);
+        dos.writeShort(STREAM_VERSION);
+        dos.writeByte(TC_OBJECT);
+        dos.writeByte(TC_CLASSDESC);
+        dos.writeUTF(className);
+        dos.writeLong(uid);
+        dos.writeByte(flags);
+        dos.writeShort(0);             // number of fields
+        dos.writeByte(TC_ENDBLOCKDATA);   // no annotations
+        dos.writeByte(TC_NULL);           // no superclasses
+        dos.close();
+        return baos.toByteArray();
+    }
+
+    private static byte[] serializableByteStreamFor(String className, long uid)
+        throws Exception
+    {
+        return byteStreamFor(className, uid, SC_SERIALIZABLE);
+    }
+
+    private static byte[] externalizableByteStreamFor(String className, long uid)
+         throws Exception
+    {
+        return byteStreamFor(className, uid, SC_EXTERNALIZABLE);
+    }
+
+    @DataProvider(name = "classes")
+    public Object[][] classes() {
+        return new Object[][] {
+            new Object[] { NonSerializablePoint.class },
+            new Object[] { SerializablePoint.class },
+            new Object[] { SerializablePrimitivePoint.class }
+        };
+    }
+
+    static final Class<InvalidClassException> ICE = InvalidClassException.class;
+
+    // value class read directly from a byte stream, both serializable and externalizable are supported
+    @Test(dataProvider = "classes")
+    public void deserialize(Class<?> cls) throws Exception {
+        var clsDesc = ObjectStreamClass.lookup(cls);
+        long uid = clsDesc == null ? 0L : clsDesc.getSerialVersionUID();
+
+        byte[] serialBytes = serializableByteStreamFor(cls.getName(), uid);
+
+        byte[] extBytes = externalizableByteStreamFor(cls.getName(), uid);
+    }
+
+    static <T> byte[] serialize(T obj) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(baos);
+        oos.writeObject(obj);
+        oos.close();
+        return baos.toByteArray();
+    }
+
+    @SuppressWarnings("unchecked")
+    static <T> T deserialize(byte[] streamBytes)
+        throws IOException, ClassNotFoundException
+    {
+        ByteArrayInputStream bais = new ByteArrayInputStream(streamBytes);
+        ObjectInputStream ois  = new ObjectInputStream(bais);
+        return (T) ois.readObject();
+    }
+
+    @SuppressWarnings("unchecked")
+    static <T> T serializeDeserialize(T obj)
+        throws IOException, ClassNotFoundException
+    {
+        return (T) deserialize(serialize(obj));
+    }
+}
diff a/test/jdk/java/lang/Class/GenericStringTest.java b/test/jdk/java/lang/Class/GenericStringTest.java
--- a/test/jdk/java/lang/Class/GenericStringTest.java
+++ b/test/jdk/java/lang/Class/GenericStringTest.java
@@ -23,10 +23,13 @@
 
 /*
  * @test
  * @bug 6298888 6992705 8161500 6304578 8322878
  * @summary Check Class.toGenericString()
+ * @enablePreview
+ * @compile GenericStringTest.java
+ * @run main/othervm GenericStringTest
  */
 
 import java.lang.reflect.*;
 import java.lang.annotation.*;
 import java.util.*;
@@ -84,10 +87,11 @@
         for(Class<?> clazz : List.of(GenericStringTest.class,
                                      AnInterface.class,
                                      LocalMap.class,
                                      AnEnum.class,
                                      AnotherEnum.class,
+                                     AValueClass.class,
 
                                      SealedRootClass.class,
                                      SealedRootClass.ChildA.class,
                                      SealedRootClass.ChildB.class,
                                      SealedRootClass.ChildB.GrandChildAB.class,
@@ -154,10 +158,13 @@
 @ExpectedGenericString("sealed enum AnotherEnum")
 enum AnotherEnum {
     BAR{};
 }
 
+@ExpectedGenericString("final value class AValueClass<E>")
+value class AValueClass<E> {}
+
 // Test cases for sealed/non-sealed _class_ hierarchy.
 @ExpectedGenericString("sealed class SealedRootClass")
 sealed class SealedRootClass
     permits
     SealedRootClass.ChildA,
diff a/test/jdk/java/lang/Class/getModifiers/StripACC_SUPER.java b/test/jdk/java/lang/Class/getModifiers/StripACC_SUPER.java
--- a/test/jdk/java/lang/Class/getModifiers/StripACC_SUPER.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 1998, 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 4109635
-   @summary VM adds ACC_SUPER bit to access flags of a class. This must
-            be stripped by the Class.getModifiers method, or else this
-            shows up as though the class is synchronized and that doesn't
-            make any sense.
-   @author Anand Palaniswamy
- */
-public class StripACC_SUPER {
-    public static void main(String[] args) throws Exception {
-        int access = StripACC_SUPER.class.getModifiers();
-        if (java.lang.reflect.Modifier.isSynchronized(access))
-            throw new Exception("ACC_SUPER bit is not being stripped");
-    }
-}
diff a/test/jdk/java/lang/invoke/MethodHandleProxies/WrapperHiddenClassTest.java b/test/jdk/java/lang/invoke/MethodHandleProxies/WrapperHiddenClassTest.java
--- a/test/jdk/java/lang/invoke/MethodHandleProxies/WrapperHiddenClassTest.java
+++ b/test/jdk/java/lang/invoke/MethodHandleProxies/WrapperHiddenClassTest.java
@@ -38,16 +38,20 @@
 
 import static java.lang.constant.ConstantDescs.*;
 import static java.lang.invoke.MethodHandleProxies.*;
 import static java.lang.invoke.MethodType.methodType;
 import static java.lang.classfile.ClassFile.*;
+
+import jdk.internal.misc.PreviewFeatures;
+
 import static org.junit.jupiter.api.Assertions.*;
 
 /*
  * @test
  * @bug 6983726
  * @library /test/lib
+ * @modules java.base/jdk.internal.misc
  * @enablePreview
  * @summary Tests on implementation hidden classes spinned by MethodHandleProxies
  * @build WrapperHiddenClassTest Client jdk.test.lib.util.ForceGC
  * @run junit WrapperHiddenClassTest
  */
@@ -84,11 +88,11 @@
     @SuppressWarnings("unchecked")
     private Comparator<Integer> createHostileInstance() throws Throwable {
         var cf = ClassFile.of();
         var bytes = cf.build(CD_HostileWrapper, clb -> {
             clb.withSuperclass(CD_Object);
-            clb.withFlags(ACC_FINAL | ACC_SYNTHETIC);
+            clb.withFlags((PreviewFeatures.isEnabled() ? ACC_IDENTITY : 0) | ACC_FINAL | ACC_SYNTHETIC);
             clb.withInterfaceSymbols(CD_Comparator);
 
             // static and instance fields
             clb.withField(TYPE, CD_Class, ACC_PRIVATE | ACC_STATIC | ACC_FINAL);
             clb.withField(TARGET, CD_MethodHandle, ACC_PRIVATE | ACC_FINAL);
diff a/test/jdk/java/lang/invoke/MethodHandles/TestRecursive.java b/test/jdk/java/lang/invoke/MethodHandles/TestRecursive.java
--- /dev/null
+++ b/test/jdk/java/lang/invoke/MethodHandles/TestRecursive.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2022, 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
+ * @modules java.base/java.lang.runtime:open
+ * @run testng TestRecursive
+ */
+
+import org.testng.annotations.Test;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.reflect.Method;
+
+import static java.lang.invoke.MethodHandles.lookup;
+import static java.lang.invoke.MethodType.methodType;
+import static org.testng.Assert.assertEquals;
+
+public class TestRecursive {
+    static MethodHandle recursive(MethodHandle base, MethodHandle... moreBases) throws ReflectiveOperationException {
+        // temporarily place this API in an internal class
+        Class<?> c = Class.forName("java.lang.runtime.ValueObjectMethods");
+        Method m = c.getDeclaredMethod("recursive", MethodHandle.class, MethodHandle[].class);
+        m.setAccessible(true);
+        return (MethodHandle) m.invoke(null, base, moreBases);
+    }
+
+    static class Snippet1 {
+        // classic recursive implementation of the factorial function
+        static int base(MethodHandle recur, int k) throws Throwable {
+            if (k <= 1)  return 1;
+            return k * (int) recur.invokeExact(k - 1);
+        }
+        static void doTest() throws Throwable {
+            var MT_base = methodType(int.class, MethodHandle.class, int.class);
+            var MH_base = lookup().findStatic(Snippet1.class, "base", MT_base);
+            // assume MH_base is a handle to the above method
+            MethodHandle recur = recursive(MH_base);
+            assertEquals(120, (int) recur.invoke(5));
+        }
+    }
+
+    @Test
+    public void testSingleRecursion() throws Throwable {
+        Snippet1.doTest();
+    }
+
+    static class DoubleRecursion {
+        static long entryPoint(MethodHandle entryPoint,
+                               MethodHandle factorialOdd,
+                               MethodHandle factorialEven,
+                               long k) throws Throwable {
+            if ((k & 1) == 0)
+                return (long) factorialEven.invokeExact(k, "even0", 2.2f);
+            else
+                return (long) factorialOdd.invokeExact(k, "odd0");
+        }
+        static long factorialOdd(MethodHandle entryPoint,
+                                 MethodHandle factorialOdd,
+                                 MethodHandle factorialEven,
+                                 long k,
+                                 // change up the signature:
+                                 String ignore) throws Throwable {
+            assertEquals(k & 1, 1);
+            if (k < 3)  return 1;
+            return k * (long) factorialEven.invokeExact(k - 1, "even1", 3.3f);
+        }
+        static long factorialEven(MethodHandle entryPoint,
+                                  MethodHandle factorialOdd,
+                                  MethodHandle factorialEven,
+                                  long k,
+                                 // change up the signature again:
+                                  String ignore, float ig2) throws Throwable {
+            assertEquals(k & 1, 0);
+            if (k < 2)  return 1;
+            return k * (long) factorialOdd.invokeExact(k - 1, "odd1");
+        }
+        static void doTest() throws Throwable {
+            var mt = methodType(long.class,
+                                MethodHandle.class,
+                                MethodHandle.class,
+                                MethodHandle.class,
+                                long.class);
+            var MH_entryPoint = lookup().findStatic(DoubleRecursion.class,
+                                                    "entryPoint", mt);
+            mt = mt.appendParameterTypes(String.class);
+            var MH_factorialOdd = lookup().findStatic(DoubleRecursion.class,
+                                                      "factorialOdd", mt);
+            mt = mt.appendParameterTypes(float.class);
+            var MH_factorialEven = lookup().findStatic(DoubleRecursion.class,
+                                                       "factorialEven", mt);
+            MethodHandle recur = recursive(MH_entryPoint,
+                                           MH_factorialOdd,
+                                           MH_factorialEven);
+            long fact = 1;
+            for (long k = 0; k < 20; k++) {
+                assertEquals(fact, (long) recur.invoke(k));
+                fact *= k+1;
+            }
+        }
+    }
+
+    @Test
+    public void testDoubleRecursion() throws Throwable {
+        DoubleRecursion.doTest();
+    }
+}
+
diff a/test/jdk/java/lang/invoke/MethodHandles/classData/ClassDataTest.java b/test/jdk/java/lang/invoke/MethodHandles/classData/ClassDataTest.java
--- a/test/jdk/java/lang/invoke/MethodHandles/classData/ClassDataTest.java
+++ b/test/jdk/java/lang/invoke/MethodHandles/classData/ClassDataTest.java
@@ -350,11 +350,11 @@
          * @param classname
          */
         ClassByteBuilder(String classname) {
             this.classname = classname;
             this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
-            cw.visit(V14, ACC_FINAL, classname, null, OBJECT_CLS, null);
+            cw.visit(V14, ACC_FINAL | ACC_IDENTITY, classname, null, OBJECT_CLS, null);
             MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
             mv.visitCode();
             mv.visitVarInsn(ALOAD, 0);
             mv.visitMethodInsn(INVOKESPECIAL, OBJECT_CLS, "<init>", "()V", false);
             mv.visitInsn(RETURN);
@@ -504,7 +504,5 @@
         Method m = c.getMethod("classData");
         Object v = m.invoke(o);
         assertEquals(value, v);
     }
 }
-
-
diff a/test/jdk/java/lang/invoke/TEST.properties b/test/jdk/java/lang/invoke/TEST.properties
--- a/test/jdk/java/lang/invoke/TEST.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-# disabled till JDK-8219408 is fixed
-allowSmartActionArgs=false
diff a/test/jdk/java/lang/invoke/VarHandles/Value.java b/test/jdk/java/lang/invoke/VarHandles/Value.java
--- /dev/null
+++ b/test/jdk/java/lang/invoke/VarHandles/Value.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018, 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 jdk.internal.vm.annotation.ImplicitlyConstructible;
+
+@ImplicitlyConstructible
+value class Value {
+    int v;
+    public Value(int v) {
+        this.v = v;
+    }
+
+    public static Value getInstance(int v) {
+        return new Value(v);
+    }
+}
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleBaseByteArrayTest.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleBaseByteArrayTest.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleBaseByteArrayTest.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleBaseByteArrayTest.java
@@ -189,10 +189,14 @@
 
         @Override
         VarHandleSource get() {
             return vhs;
         }
+
+        public String toString() {
+            return super.toString() + ", bs: " + bs + ", vhs: " + vhs;
+        }
     }
 
 
     static double rotateLeft(double i, int distance) {
         return Double.longBitsToDouble(
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleBaseTest.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleBaseTest.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleBaseTest.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleBaseTest.java
@@ -442,10 +442,14 @@
 
         @Override
         VarHandle get() {
             return vh;
         }
+
+        public String toString() {
+            return super.toString() + ", vh:" + vh;
+        }
     }
 
     static class MethodHandleAccessTestCase extends AccessTestCase<Handles> {
         final VarHandle vh;
         final VarHandleToMethodHandle f;
@@ -462,10 +466,14 @@
 
         @Override
         Handles get() throws Exception {
             return new Handles(vh, f);
         }
+
+        public String toString() {
+            return super.toString() + ", vh:" + vh + ", f: " + f;
+        }
     }
 
     static void testTypes(VarHandle vh) {
         List<Class<?>> pts = vh.coordinateTypes();
 
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessBoolean.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessBoolean.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessBoolean.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessBoolean.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @run testng/othervm -Diters=10   -Xint                                                   VarHandleTestAccessBoolean
  *
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessByte.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessByte.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessByte.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessByte.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @run testng/othervm -Diters=10   -Xint                                                   VarHandleTestAccessByte
  *
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessChar.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessChar.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessChar.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessChar.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @run testng/othervm -Diters=10   -Xint                                                   VarHandleTestAccessChar
  *
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessDouble.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessDouble.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessDouble.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessDouble.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @run testng/othervm -Diters=10   -Xint                                                   VarHandleTestAccessDouble
  *
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessFloat.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessFloat.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessFloat.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessFloat.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @run testng/othervm -Diters=10   -Xint                                                   VarHandleTestAccessFloat
  *
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessInt.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessInt.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessInt.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessInt.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @run testng/othervm -Diters=10   -Xint                                                   VarHandleTestAccessInt
  *
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessLong.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessLong.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessLong.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessLong.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @run testng/othervm -Diters=10   -Xint                                                   VarHandleTestAccessLong
  *
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessShort.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessShort.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessShort.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessShort.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @run testng/othervm -Diters=10   -Xint                                                   VarHandleTestAccessShort
  *
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessString.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessString.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessString.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessString.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @run testng/othervm -Diters=10   -Xint                                                   VarHandleTestAccessString
  *
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessValue.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessValue.java
--- /dev/null
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestAccessValue.java
@@ -0,0 +1,1402 @@
+/*
+ * Copyright (c) 2015, 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 file was mechanically generated: Do not edit! -- //
+
+/*
+ * @test
+ * @enablePreview
+ * @modules java.base/jdk.internal.vm.annotation
+ * @run testng/othervm -Diters=10   -Xint                                                   VarHandleTestAccessValue
+ *
+ * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
+ *          to hit compilation thresholds
+ *
+ * @run testng/othervm -Diters=2000 -XX:CompileThresholdScaling=0.1 -XX:TieredStopAtLevel=1 VarHandleTestAccessValue
+ * @run testng/othervm -Diters=2000 -XX:CompileThresholdScaling=0.1                         VarHandleTestAccessValue
+ * @run testng/othervm -Diters=2000 -XX:CompileThresholdScaling=0.1 -XX:-TieredCompilation  VarHandleTestAccessValue
+ */
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.testng.Assert.*;
+
+public class VarHandleTestAccessValue extends VarHandleBaseTest {
+    static final Value static_final_v = Value.getInstance(10);
+
+    static Value static_v;
+
+    final Value final_v = Value.getInstance(10);
+
+    Value v;
+
+    static final Value static_final_v2 = Value.getInstance(10);
+
+    static Value static_v2;
+
+    final Value final_v2 = Value.getInstance(10);
+
+    Value v2;
+
+    VarHandle vhFinalField;
+
+    VarHandle vhField;
+
+    VarHandle vhStaticField;
+
+    VarHandle vhStaticFinalField;
+
+    VarHandle vhArray;
+
+    VarHandle vhArrayObject;
+
+    VarHandle[] allocate(boolean same) {
+        List<VarHandle> vhs = new ArrayList<>();
+
+        String postfix = same ? "" : "2";
+        VarHandle vh;
+        try {
+            vh = MethodHandles.lookup().findVarHandle(
+                    VarHandleTestAccessValue.class, "final_v" + postfix, Value.class);
+            vhs.add(vh);
+
+            vh = MethodHandles.lookup().findVarHandle(
+                    VarHandleTestAccessValue.class, "v" + postfix, Value.class);
+            vhs.add(vh);
+
+            vh = MethodHandles.lookup().findStaticVarHandle(
+                VarHandleTestAccessValue.class, "static_final_v" + postfix, Value.class);
+            vhs.add(vh);
+
+            vh = MethodHandles.lookup().findStaticVarHandle(
+                VarHandleTestAccessValue.class, "static_v" + postfix, Value.class);
+            vhs.add(vh);
+
+            if (same) {
+                vh = MethodHandles.arrayElementVarHandle(Value[].class);
+            }
+            else {
+                vh = MethodHandles.arrayElementVarHandle(String[].class);
+            }
+            vhs.add(vh);
+        } catch (Exception e) {
+            throw new InternalError(e);
+        }
+        return vhs.toArray(new VarHandle[0]);
+    }
+
+    @BeforeClass
+    public void setup() throws Exception {
+        vhFinalField = MethodHandles.lookup().findVarHandle(
+                VarHandleTestAccessValue.class, "final_v", Value.class);
+
+        vhField = MethodHandles.lookup().findVarHandle(
+                VarHandleTestAccessValue.class, "v", Value.class);
+
+        vhStaticFinalField = MethodHandles.lookup().findStaticVarHandle(
+            VarHandleTestAccessValue.class, "static_final_v", Value.class);
+
+        vhStaticField = MethodHandles.lookup().findStaticVarHandle(
+            VarHandleTestAccessValue.class, "static_v", Value.class);
+
+        vhArray = MethodHandles.arrayElementVarHandle(Value[].class);
+        vhArrayObject = MethodHandles.arrayElementVarHandle(Object[].class);
+    }
+
+
+    @DataProvider
+    public Object[][] varHandlesProvider() throws Exception {
+        List<VarHandle> vhs = new ArrayList<>();
+        vhs.add(vhField);
+        vhs.add(vhStaticField);
+        vhs.add(vhArray);
+
+        return vhs.stream().map(tc -> new Object[]{tc}).toArray(Object[][]::new);
+    }
+
+    @Test
+    public void testEquals() {
+        VarHandle[] vhs1 = allocate(true);
+        VarHandle[] vhs2 = allocate(true);
+
+        for (int i = 0; i < vhs1.length; i++) {
+            for (int j = 0; j < vhs1.length; j++) {
+                if (i != j) {
+                    assertNotEquals(vhs1[i], vhs1[j]);
+                    assertNotEquals(vhs1[i], vhs2[j]);
+                }
+            }
+        }
+
+        VarHandle[] vhs3 = allocate(false);
+        for (int i = 0; i < vhs1.length; i++) {
+            assertNotEquals(vhs1[i], vhs3[i]);
+        }
+    }
+
+    @Test(dataProvider = "varHandlesProvider")
+    public void testIsAccessModeSupported(VarHandle vh) {
+        assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.GET));
+        assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.SET));
+        assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.GET_VOLATILE));
+        assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.SET_VOLATILE));
+        assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.GET_ACQUIRE));
+        assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.SET_RELEASE));
+        assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.GET_OPAQUE));
+        assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.SET_OPAQUE));
+
+        assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_SET));
+        assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE));
+        assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_ACQUIRE));
+        assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.COMPARE_AND_EXCHANGE_RELEASE));
+        assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_PLAIN));
+        assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET));
+        assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_ACQUIRE));
+        assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.WEAK_COMPARE_AND_SET_RELEASE));
+        assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET));
+        assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET_ACQUIRE));
+        assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_SET_RELEASE));
+
+        assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_ADD));
+        assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_ADD_ACQUIRE));
+        assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_ADD_RELEASE));
+
+        assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_BITWISE_OR));
+        assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_BITWISE_OR_ACQUIRE));
+        assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_BITWISE_OR_RELEASE));
+        assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_BITWISE_AND));
+        assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_BITWISE_AND_ACQUIRE));
+        assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_BITWISE_AND_RELEASE));
+        assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_BITWISE_XOR));
+        assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_BITWISE_XOR_ACQUIRE));
+        assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.GET_AND_BITWISE_XOR_RELEASE));
+    }
+
+
+    @DataProvider
+    public Object[][] typesProvider() throws Exception {
+        List<Object[]> types = new ArrayList<>();
+        types.add(new Object[] {vhField, Arrays.asList(VarHandleTestAccessValue.class)});
+        types.add(new Object[] {vhStaticField, Arrays.asList()});
+        types.add(new Object[] {vhArray, Arrays.asList(Value[].class, int.class)});
+
+        return types.stream().toArray(Object[][]::new);
+    }
+
+    @Test(dataProvider = "typesProvider")
+    public void testTypes(VarHandle vh, List<Class<?>> pts) {
+        assertEquals(vh.varType(), Value.class);
+
+        assertEquals(vh.coordinateTypes(), pts);
+
+        testTypes(vh);
+    }
+
+
+    @Test
+    public void testLookupInstanceToStatic() {
+        checkIAE("Lookup of static final field to instance final field", () -> {
+            MethodHandles.lookup().findStaticVarHandle(
+                    VarHandleTestAccessValue.class, "final_v", Value.class);
+        });
+
+        checkIAE("Lookup of static field to instance field", () -> {
+            MethodHandles.lookup().findStaticVarHandle(
+                    VarHandleTestAccessValue.class, "v", Value.class);
+        });
+    }
+
+    @Test
+    public void testLookupStaticToInstance() {
+        checkIAE("Lookup of instance final field to static final field", () -> {
+            MethodHandles.lookup().findVarHandle(
+                VarHandleTestAccessValue.class, "static_final_v", Value.class);
+        });
+
+        checkIAE("Lookup of instance field to static field", () -> {
+            vhStaticField = MethodHandles.lookup().findVarHandle(
+                VarHandleTestAccessValue.class, "static_v", Value.class);
+        });
+    }
+
+
+    @DataProvider
+    public Object[][] accessTestCaseProvider() throws Exception {
+        List<AccessTestCase<?>> cases = new ArrayList<>();
+
+        cases.add(new VarHandleAccessTestCase("Instance final field",
+                                              vhFinalField, vh -> testInstanceFinalField(this, vh)));
+        cases.add(new VarHandleAccessTestCase("Instance final field unsupported",
+                                              vhFinalField, vh -> testInstanceFinalFieldUnsupported(this, vh),
+                                              false));
+
+        cases.add(new VarHandleAccessTestCase("Static final field",
+                                              vhStaticFinalField, VarHandleTestAccessValue::testStaticFinalField));
+        cases.add(new VarHandleAccessTestCase("Static final field unsupported",
+                                              vhStaticFinalField, VarHandleTestAccessValue::testStaticFinalFieldUnsupported,
+                                              false));
+
+        cases.add(new VarHandleAccessTestCase("Instance field",
+                                              vhField, vh -> testInstanceField(this, vh)));
+        cases.add(new VarHandleAccessTestCase("Instance field unsupported",
+                                              vhField, vh -> testInstanceFieldUnsupported(this, vh),
+                                              false));
+
+        cases.add(new VarHandleAccessTestCase("Static field",
+                                              vhStaticField, VarHandleTestAccessValue::testStaticField));
+        cases.add(new VarHandleAccessTestCase("Static field unsupported",
+                                              vhStaticField, VarHandleTestAccessValue::testStaticFieldUnsupported,
+                                              false));
+
+        cases.add(new VarHandleAccessTestCase("Array",
+                                              vhArray, VarHandleTestAccessValue::testArray));
+        cases.add(new VarHandleAccessTestCase("Array Object[]",
+                                              vhArrayObject, VarHandleTestAccessValue::testArray));
+        cases.add(new VarHandleAccessTestCase("Array unsupported",
+                                              vhArray, VarHandleTestAccessValue::testArrayUnsupported,
+                                              false));
+        cases.add(new VarHandleAccessTestCase("Array index out of bounds",
+                                              vhArray, VarHandleTestAccessValue::testArrayIndexOutOfBounds,
+                                              false));
+        cases.add(new VarHandleAccessTestCase("Array store exception",
+                                              vhArrayObject, VarHandleTestAccessValue::testArrayStoreException,
+                                              false));
+        // Work around issue with jtreg summary reporting which truncates
+        // the String result of Object.toString to 30 characters, hence
+        // the first dummy argument
+        return cases.stream().map(tc -> new Object[]{tc.toString(), tc}).toArray(Object[][]::new);
+    }
+
+    @Test(dataProvider = "accessTestCaseProvider")
+    public <T> void testAccess(String desc, AccessTestCase<T> atc) throws Throwable {
+        T t = atc.get();
+        int iters = atc.requiresLoop() ? ITERS : 1;
+        for (int c = 0; c < iters; c++) {
+            atc.testAccess(t);
+        }
+    }
+
+    static void testInstanceFinalField(VarHandleTestAccessValue recv, VarHandle vh) {
+        // Plain
+        {
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(10), "get Value value");
+        }
+
+
+        // Volatile
+        {
+            Value x = (Value) vh.getVolatile(recv);
+            assertEquals(x, Value.getInstance(10), "getVolatile Value value");
+        }
+
+        // Lazy
+        {
+            Value x = (Value) vh.getAcquire(recv);
+            assertEquals(x, Value.getInstance(10), "getRelease Value value");
+        }
+
+        // Opaque
+        {
+            Value x = (Value) vh.getOpaque(recv);
+            assertEquals(x, Value.getInstance(10), "getOpaque Value value");
+        }
+    }
+
+    static void testInstanceFinalFieldUnsupported(VarHandleTestAccessValue recv, VarHandle vh) {
+        checkUOE(() -> {
+            vh.set(recv, Value.getInstance(20));
+        });
+
+        checkUOE(() -> {
+            vh.setVolatile(recv, Value.getInstance(20));
+        });
+
+        checkUOE(() -> {
+            vh.setRelease(recv, Value.getInstance(20));
+        });
+
+        checkUOE(() -> {
+            vh.setOpaque(recv, Value.getInstance(20));
+        });
+
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndAdd(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndAddAcquire(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndAddRelease(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseOr(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseOrAcquire(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseOrRelease(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseAnd(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseAndAcquire(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseAndRelease(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseXor(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseXorAcquire(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseXorRelease(recv, Value.getInstance(10));
+        });
+    }
+
+
+    static void testStaticFinalField(VarHandle vh) {
+        // Plain
+        {
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(10), "get Value value");
+        }
+
+
+        // Volatile
+        {
+            Value x = (Value) vh.getVolatile();
+            assertEquals(x, Value.getInstance(10), "getVolatile Value value");
+        }
+
+        // Lazy
+        {
+            Value x = (Value) vh.getAcquire();
+            assertEquals(x, Value.getInstance(10), "getRelease Value value");
+        }
+
+        // Opaque
+        {
+            Value x = (Value) vh.getOpaque();
+            assertEquals(x, Value.getInstance(10), "getOpaque Value value");
+        }
+    }
+
+    static void testStaticFinalFieldUnsupported(VarHandle vh) {
+        checkUOE(() -> {
+            vh.set(Value.getInstance(20));
+        });
+
+        checkUOE(() -> {
+            vh.setVolatile(Value.getInstance(20));
+        });
+
+        checkUOE(() -> {
+            vh.setRelease(Value.getInstance(20));
+        });
+
+        checkUOE(() -> {
+            vh.setOpaque(Value.getInstance(20));
+        });
+
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndAdd(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndAddAcquire(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndAddRelease(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseOr(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseOrAcquire(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseOrRelease(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseAnd(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseAndAcquire(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseAndRelease(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseXor(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseXorAcquire(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseXorRelease(Value.getInstance(10));
+        });
+    }
+
+
+    static void testInstanceField(VarHandleTestAccessValue recv, VarHandle vh) {
+        // Plain
+        {
+            vh.set(recv, Value.getInstance(10));
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(10), "set Value value");
+        }
+
+
+        // Volatile
+        {
+            vh.setVolatile(recv, Value.getInstance(20));
+            Value x = (Value) vh.getVolatile(recv);
+            assertEquals(x, Value.getInstance(20), "setVolatile Value value");
+        }
+
+        // Lazy
+        {
+            vh.setRelease(recv, Value.getInstance(10));
+            Value x = (Value) vh.getAcquire(recv);
+            assertEquals(x, Value.getInstance(10), "setRelease Value value");
+        }
+
+        // Opaque
+        {
+            vh.setOpaque(recv, Value.getInstance(20));
+            Value x = (Value) vh.getOpaque(recv);
+            assertEquals(x, Value.getInstance(20), "setOpaque Value value");
+        }
+
+        vh.set(recv, Value.getInstance(10));
+
+        // Compare
+        {
+            boolean r = vh.compareAndSet(recv, Value.getInstance(10), Value.getInstance(20));
+            assertEquals(r, true, "success compareAndSet Value");
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(20), "success compareAndSet Value value");
+        }
+
+        {
+            boolean r = vh.compareAndSet(recv, Value.getInstance(10), Value.getInstance(30));
+            assertEquals(r, false, "failing compareAndSet Value");
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(20), "failing compareAndSet Value value");
+        }
+
+        {
+            Value r = (Value) vh.compareAndExchange(recv, Value.getInstance(20), Value.getInstance(10));
+            assertEquals(r, Value.getInstance(20), "success compareAndExchange Value");
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(10), "success compareAndExchange Value value");
+        }
+
+        {
+            Value r = (Value) vh.compareAndExchange(recv, Value.getInstance(20), Value.getInstance(30));
+            assertEquals(r, Value.getInstance(10), "failing compareAndExchange Value");
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(10), "failing compareAndExchange Value value");
+        }
+
+        {
+            Value r = (Value) vh.compareAndExchangeAcquire(recv, Value.getInstance(10), Value.getInstance(20));
+            assertEquals(r, Value.getInstance(10), "success compareAndExchangeAcquire Value");
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(20), "success compareAndExchangeAcquire Value value");
+        }
+
+        {
+            Value r = (Value) vh.compareAndExchangeAcquire(recv, Value.getInstance(10), Value.getInstance(30));
+            assertEquals(r, Value.getInstance(20), "failing compareAndExchangeAcquire Value");
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(20), "failing compareAndExchangeAcquire Value value");
+        }
+
+        {
+            Value r = (Value) vh.compareAndExchangeRelease(recv, Value.getInstance(20), Value.getInstance(10));
+            assertEquals(r, Value.getInstance(20), "success compareAndExchangeRelease Value");
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(10), "success compareAndExchangeRelease Value value");
+        }
+
+        {
+            Value r = (Value) vh.compareAndExchangeRelease(recv, Value.getInstance(20), Value.getInstance(30));
+            assertEquals(r, Value.getInstance(10), "failing compareAndExchangeRelease Value");
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(10), "failing compareAndExchangeRelease Value value");
+        }
+
+        {
+            boolean success = false;
+            for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                success = vh.weakCompareAndSetPlain(recv, Value.getInstance(10), Value.getInstance(20));
+                if (!success) weakDelay();
+            }
+            assertEquals(success, true, "success weakCompareAndSetPlain Value");
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(20), "success weakCompareAndSetPlain Value value");
+        }
+
+        {
+            boolean success = vh.weakCompareAndSetPlain(recv, Value.getInstance(10), Value.getInstance(30));
+            assertEquals(success, false, "failing weakCompareAndSetPlain Value");
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(20), "failing weakCompareAndSetPlain Value value");
+        }
+
+        {
+            boolean success = false;
+            for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                success = vh.weakCompareAndSetAcquire(recv, Value.getInstance(20), Value.getInstance(10));
+                if (!success) weakDelay();
+            }
+            assertEquals(success, true, "success weakCompareAndSetAcquire Value");
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(10), "success weakCompareAndSetAcquire Value");
+        }
+
+        {
+            boolean success = vh.weakCompareAndSetAcquire(recv, Value.getInstance(20), Value.getInstance(30));
+            assertEquals(success, false, "failing weakCompareAndSetAcquire Value");
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(10), "failing weakCompareAndSetAcquire Value value");
+        }
+
+        {
+            boolean success = false;
+            for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                success = vh.weakCompareAndSetRelease(recv, Value.getInstance(10), Value.getInstance(20));
+                if (!success) weakDelay();
+            }
+            assertEquals(success, true, "success weakCompareAndSetRelease Value");
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(20), "success weakCompareAndSetRelease Value");
+        }
+
+        {
+            boolean success = vh.weakCompareAndSetRelease(recv, Value.getInstance(10), Value.getInstance(30));
+            assertEquals(success, false, "failing weakCompareAndSetRelease Value");
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(20), "failing weakCompareAndSetRelease Value value");
+        }
+
+        {
+            boolean success = false;
+            for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                success = vh.weakCompareAndSet(recv, Value.getInstance(20), Value.getInstance(10));
+                if (!success) weakDelay();
+            }
+            assertEquals(success, true, "success weakCompareAndSet Value");
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(10), "success weakCompareAndSet Value value");
+        }
+
+        {
+            boolean success = vh.weakCompareAndSet(recv, Value.getInstance(20), Value.getInstance(30));
+            assertEquals(success, false, "failing weakCompareAndSet Value");
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(10), "failing weakCompareAndSet Value value");
+        }
+
+        // Compare set and get
+        {
+            vh.set(recv, Value.getInstance(10));
+
+            Value o = (Value) vh.getAndSet(recv, Value.getInstance(20));
+            assertEquals(o, Value.getInstance(10), "getAndSet Value");
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(20), "getAndSet Value value");
+        }
+
+        {
+            vh.set(recv, Value.getInstance(10));
+
+            Value o = (Value) vh.getAndSetAcquire(recv, Value.getInstance(20));
+            assertEquals(o, Value.getInstance(10), "getAndSetAcquire Value");
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(20), "getAndSetAcquire Value value");
+        }
+
+        {
+            vh.set(recv, Value.getInstance(10));
+
+            Value o = (Value) vh.getAndSetRelease(recv, Value.getInstance(20));
+            assertEquals(o, Value.getInstance(10), "getAndSetRelease Value");
+            Value x = (Value) vh.get(recv);
+            assertEquals(x, Value.getInstance(20), "getAndSetRelease Value value");
+        }
+
+
+    }
+
+    static void testInstanceFieldUnsupported(VarHandleTestAccessValue recv, VarHandle vh) {
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndAdd(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndAddAcquire(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndAddRelease(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseOr(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseOrAcquire(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseOrRelease(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseAnd(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseAndAcquire(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseAndRelease(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseXor(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseXorAcquire(recv, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseXorRelease(recv, Value.getInstance(10));
+        });
+    }
+
+
+    static void testStaticField(VarHandle vh) {
+        // Plain
+        {
+            vh.set(Value.getInstance(10));
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(10), "set Value value");
+        }
+
+
+        // Volatile
+        {
+            vh.setVolatile(Value.getInstance(20));
+            Value x = (Value) vh.getVolatile();
+            assertEquals(x, Value.getInstance(20), "setVolatile Value value");
+        }
+
+        // Lazy
+        {
+            vh.setRelease(Value.getInstance(10));
+            Value x = (Value) vh.getAcquire();
+            assertEquals(x, Value.getInstance(10), "setRelease Value value");
+        }
+
+        // Opaque
+        {
+            vh.setOpaque(Value.getInstance(20));
+            Value x = (Value) vh.getOpaque();
+            assertEquals(x, Value.getInstance(20), "setOpaque Value value");
+        }
+
+        vh.set(Value.getInstance(10));
+
+        // Compare
+        {
+            boolean r = vh.compareAndSet(Value.getInstance(10), Value.getInstance(20));
+            assertEquals(r, true, "success compareAndSet Value");
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(20), "success compareAndSet Value value");
+        }
+
+        {
+            boolean r = vh.compareAndSet(Value.getInstance(10), Value.getInstance(30));
+            assertEquals(r, false, "failing compareAndSet Value");
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(20), "failing compareAndSet Value value");
+        }
+
+        {
+            Value r = (Value) vh.compareAndExchange(Value.getInstance(20), Value.getInstance(10));
+            assertEquals(r, Value.getInstance(20), "success compareAndExchange Value");
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(10), "success compareAndExchange Value value");
+        }
+
+        {
+            Value r = (Value) vh.compareAndExchange(Value.getInstance(20), Value.getInstance(30));
+            assertEquals(r, Value.getInstance(10), "failing compareAndExchange Value");
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(10), "failing compareAndExchange Value value");
+        }
+
+        {
+            Value r = (Value) vh.compareAndExchangeAcquire(Value.getInstance(10), Value.getInstance(20));
+            assertEquals(r, Value.getInstance(10), "success compareAndExchangeAcquire Value");
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(20), "success compareAndExchangeAcquire Value value");
+        }
+
+        {
+            Value r = (Value) vh.compareAndExchangeAcquire(Value.getInstance(10), Value.getInstance(30));
+            assertEquals(r, Value.getInstance(20), "failing compareAndExchangeAcquire Value");
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(20), "failing compareAndExchangeAcquire Value value");
+        }
+
+        {
+            Value r = (Value) vh.compareAndExchangeRelease(Value.getInstance(20), Value.getInstance(10));
+            assertEquals(r, Value.getInstance(20), "success compareAndExchangeRelease Value");
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(10), "success compareAndExchangeRelease Value value");
+        }
+
+        {
+            Value r = (Value) vh.compareAndExchangeRelease(Value.getInstance(20), Value.getInstance(30));
+            assertEquals(r, Value.getInstance(10), "failing compareAndExchangeRelease Value");
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(10), "failing compareAndExchangeRelease Value value");
+        }
+
+        {
+            boolean success = false;
+            for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                success = vh.weakCompareAndSetPlain(Value.getInstance(10), Value.getInstance(20));
+                if (!success) weakDelay();
+            }
+            assertEquals(success, true, "success weakCompareAndSetPlain Value");
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(20), "success weakCompareAndSetPlain Value value");
+        }
+
+        {
+            boolean success = vh.weakCompareAndSetPlain(Value.getInstance(10), Value.getInstance(30));
+            assertEquals(success, false, "failing weakCompareAndSetPlain Value");
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(20), "failing weakCompareAndSetPlain Value value");
+        }
+
+        {
+            boolean success = false;
+            for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                success = vh.weakCompareAndSetAcquire(Value.getInstance(20), Value.getInstance(10));
+                if (!success) weakDelay();
+            }
+            assertEquals(success, true, "success weakCompareAndSetAcquire Value");
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(10), "success weakCompareAndSetAcquire Value");
+        }
+
+        {
+            boolean success = vh.weakCompareAndSetAcquire(Value.getInstance(20), Value.getInstance(30));
+            assertEquals(success, false, "failing weakCompareAndSetAcquire Value");
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(10), "failing weakCompareAndSetAcquire Value value");
+        }
+
+        {
+            boolean success = false;
+            for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                success = vh.weakCompareAndSetRelease(Value.getInstance(10), Value.getInstance(20));
+                if (!success) weakDelay();
+            }
+            assertEquals(success, true, "success weakCompareAndSetRelease Value");
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(20), "success weakCompareAndSetRelease Value");
+        }
+
+        {
+            boolean success = vh.weakCompareAndSetRelease(Value.getInstance(10), Value.getInstance(30));
+            assertEquals(success, false, "failing weakCompareAndSetRelease Value");
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(20), "failing weakCompareAndSetRelease Value value");
+        }
+
+        {
+            boolean success = false;
+            for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                success = vh.weakCompareAndSet(Value.getInstance(20), Value.getInstance(10));
+                if (!success) weakDelay();
+            }
+            assertEquals(success, true, "success weakCompareAndSet Value");
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(10), "success weakCompareAndSet Value");
+        }
+
+        {
+            boolean success = vh.weakCompareAndSet(Value.getInstance(20), Value.getInstance(30));
+            assertEquals(success, false, "failing weakCompareAndSet Value");
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(10), "failing weakCompareAndSet Value value");
+        }
+
+        // Compare set and get
+        {
+            vh.set(Value.getInstance(10));
+
+            Value o = (Value) vh.getAndSet(Value.getInstance(20));
+            assertEquals(o, Value.getInstance(10), "getAndSet Value");
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(20), "getAndSet Value value");
+        }
+
+        {
+            vh.set(Value.getInstance(10));
+
+            Value o = (Value) vh.getAndSetAcquire(Value.getInstance(20));
+            assertEquals(o, Value.getInstance(10), "getAndSetAcquire Value");
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(20), "getAndSetAcquire Value value");
+        }
+
+        {
+            vh.set(Value.getInstance(10));
+
+            Value o = (Value) vh.getAndSetRelease(Value.getInstance(20));
+            assertEquals(o, Value.getInstance(10), "getAndSetRelease Value");
+            Value x = (Value) vh.get();
+            assertEquals(x, Value.getInstance(20), "getAndSetRelease Value value");
+        }
+
+
+    }
+
+    static void testStaticFieldUnsupported(VarHandle vh) {
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndAdd(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndAddAcquire(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndAddRelease(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseOr(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseOrAcquire(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseOrRelease(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseAnd(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseAndAcquire(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseAndRelease(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseXor(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseXorAcquire(Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseXorRelease(Value.getInstance(10));
+        });
+    }
+
+
+    static void testArray(VarHandle vh) {
+        Value[] array = new Value[10];
+
+        for (int i = 0; i < array.length; i++) {
+            // Plain
+            {
+                vh.set(array, i, Value.getInstance(10));
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(10), "get Value value");
+            }
+
+
+            // Volatile
+            {
+                vh.setVolatile(array, i, Value.getInstance(20));
+                Value x = (Value) vh.getVolatile(array, i);
+                assertEquals(x, Value.getInstance(20), "setVolatile Value value");
+            }
+
+            // Lazy
+            {
+                vh.setRelease(array, i, Value.getInstance(10));
+                Value x = (Value) vh.getAcquire(array, i);
+                assertEquals(x, Value.getInstance(10), "setRelease Value value");
+            }
+
+            // Opaque
+            {
+                vh.setOpaque(array, i, Value.getInstance(20));
+                Value x = (Value) vh.getOpaque(array, i);
+                assertEquals(x, Value.getInstance(20), "setOpaque Value value");
+            }
+
+            vh.set(array, i, Value.getInstance(10));
+
+            // Compare
+            {
+                boolean r = vh.compareAndSet(array, i, Value.getInstance(10), Value.getInstance(20));
+                assertEquals(r, true, "success compareAndSet Value");
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(20), "success compareAndSet Value value");
+            }
+
+            {
+                boolean r = vh.compareAndSet(array, i, Value.getInstance(10), Value.getInstance(30));
+                assertEquals(r, false, "failing compareAndSet Value");
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(20), "failing compareAndSet Value value");
+            }
+
+            {
+                Value r = (Value) vh.compareAndExchange(array, i, Value.getInstance(20), Value.getInstance(10));
+                assertEquals(r, Value.getInstance(20), "success compareAndExchange Value");
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(10), "success compareAndExchange Value value");
+            }
+
+            {
+                Value r = (Value) vh.compareAndExchange(array, i, Value.getInstance(20), Value.getInstance(30));
+                assertEquals(r, Value.getInstance(10), "failing compareAndExchange Value");
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(10), "failing compareAndExchange Value value");
+            }
+
+            {
+                Value r = (Value) vh.compareAndExchangeAcquire(array, i, Value.getInstance(10), Value.getInstance(20));
+                assertEquals(r, Value.getInstance(10), "success compareAndExchangeAcquire Value");
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(20), "success compareAndExchangeAcquire Value value");
+            }
+
+            {
+                Value r = (Value) vh.compareAndExchangeAcquire(array, i, Value.getInstance(10), Value.getInstance(30));
+                assertEquals(r, Value.getInstance(20), "failing compareAndExchangeAcquire Value");
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(20), "failing compareAndExchangeAcquire Value value");
+            }
+
+            {
+                Value r = (Value) vh.compareAndExchangeRelease(array, i, Value.getInstance(20), Value.getInstance(10));
+                assertEquals(r, Value.getInstance(20), "success compareAndExchangeRelease Value");
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(10), "success compareAndExchangeRelease Value value");
+            }
+
+            {
+                Value r = (Value) vh.compareAndExchangeRelease(array, i, Value.getInstance(20), Value.getInstance(30));
+                assertEquals(r, Value.getInstance(10), "failing compareAndExchangeRelease Value");
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(10), "failing compareAndExchangeRelease Value value");
+            }
+
+            {
+                boolean success = false;
+                for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                    success = vh.weakCompareAndSetPlain(array, i, Value.getInstance(10), Value.getInstance(20));
+                    if (!success) weakDelay();
+                }
+                assertEquals(success, true, "success weakCompareAndSetPlain Value");
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(20), "success weakCompareAndSetPlain Value value");
+            }
+
+            {
+                boolean success = vh.weakCompareAndSetPlain(array, i, Value.getInstance(10), Value.getInstance(30));
+                assertEquals(success, false, "failing weakCompareAndSetPlain Value");
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(20), "failing weakCompareAndSetPlain Value value");
+            }
+
+            {
+                boolean success = false;
+                for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                    success = vh.weakCompareAndSetAcquire(array, i, Value.getInstance(20), Value.getInstance(10));
+                    if (!success) weakDelay();
+                }
+                assertEquals(success, true, "success weakCompareAndSetAcquire Value");
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(10), "success weakCompareAndSetAcquire Value");
+            }
+
+            {
+                boolean success = vh.weakCompareAndSetAcquire(array, i, Value.getInstance(20), Value.getInstance(30));
+                assertEquals(success, false, "failing weakCompareAndSetAcquire Value");
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(10), "failing weakCompareAndSetAcquire Value value");
+            }
+
+            {
+                boolean success = false;
+                for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                    success = vh.weakCompareAndSetRelease(array, i, Value.getInstance(10), Value.getInstance(20));
+                    if (!success) weakDelay();
+                }
+                assertEquals(success, true, "success weakCompareAndSetRelease Value");
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(20), "success weakCompareAndSetRelease Value");
+            }
+
+            {
+                boolean success = vh.weakCompareAndSetRelease(array, i, Value.getInstance(10), Value.getInstance(30));
+                assertEquals(success, false, "failing weakCompareAndSetRelease Value");
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(20), "failing weakCompareAndSetRelease Value value");
+            }
+
+            {
+                boolean success = false;
+                for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                    success = vh.weakCompareAndSet(array, i, Value.getInstance(20), Value.getInstance(10));
+                    if (!success) weakDelay();
+                }
+                assertEquals(success, true, "success weakCompareAndSet Value");
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(10), "success weakCompareAndSet Value");
+            }
+
+            {
+                boolean success = vh.weakCompareAndSet(array, i, Value.getInstance(20), Value.getInstance(30));
+                assertEquals(success, false, "failing weakCompareAndSet Value");
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(10), "failing weakCompareAndSet Value value");
+            }
+
+            // Compare set and get
+            {
+                vh.set(array, i, Value.getInstance(10));
+
+                Value o = (Value) vh.getAndSet(array, i, Value.getInstance(20));
+                assertEquals(o, Value.getInstance(10), "getAndSet Value");
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(20), "getAndSet Value value");
+            }
+
+            {
+                vh.set(array, i, Value.getInstance(10));
+
+                Value o = (Value) vh.getAndSetAcquire(array, i, Value.getInstance(20));
+                assertEquals(o, Value.getInstance(10), "getAndSetAcquire Value");
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(20), "getAndSetAcquire Value value");
+            }
+
+            {
+                vh.set(array, i, Value.getInstance(10));
+
+                Value o = (Value) vh.getAndSetRelease(array, i, Value.getInstance(20));
+                assertEquals(o, Value.getInstance(10), "getAndSetRelease Value");
+                Value x = (Value) vh.get(array, i);
+                assertEquals(x, Value.getInstance(20), "getAndSetRelease Value value");
+            }
+
+
+        }
+    }
+
+    static void testArrayUnsupported(VarHandle vh) {
+        Value[] array = new Value[10];
+
+        int i = 0;
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndAdd(array, i, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndAddAcquire(array, i, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndAddRelease(array, i, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseOr(array, i, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseOrAcquire(array, i, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseOrRelease(array, i, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseAnd(array, i, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseAndAcquire(array, i, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseAndRelease(array, i, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseXor(array, i, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseXorAcquire(array, i, Value.getInstance(10));
+        });
+
+        checkUOE(() -> {
+            Value o = (Value) vh.getAndBitwiseXorRelease(array, i, Value.getInstance(10));
+        });
+    }
+
+    static void testArrayIndexOutOfBounds(VarHandle vh) throws Throwable {
+        Value[] array = new Value[10];
+
+        for (int i : new int[]{-1, Integer.MIN_VALUE, 10, 11, Integer.MAX_VALUE}) {
+            final int ci = i;
+
+            checkAIOOBE(() -> {
+                Value x = (Value) vh.get(array, ci);
+            });
+
+            checkAIOOBE(() -> {
+                vh.set(array, ci, Value.getInstance(10));
+            });
+
+            checkAIOOBE(() -> {
+                Value x = (Value) vh.getVolatile(array, ci);
+            });
+
+            checkAIOOBE(() -> {
+                vh.setVolatile(array, ci, Value.getInstance(10));
+            });
+
+            checkAIOOBE(() -> {
+                Value x = (Value) vh.getAcquire(array, ci);
+            });
+
+            checkAIOOBE(() -> {
+                vh.setRelease(array, ci, Value.getInstance(10));
+            });
+
+            checkAIOOBE(() -> {
+                Value x = (Value) vh.getOpaque(array, ci);
+            });
+
+            checkAIOOBE(() -> {
+                vh.setOpaque(array, ci, Value.getInstance(10));
+            });
+
+            checkAIOOBE(() -> {
+                boolean r = vh.compareAndSet(array, ci, Value.getInstance(10), Value.getInstance(20));
+            });
+
+            checkAIOOBE(() -> {
+                Value r = (Value) vh.compareAndExchange(array, ci, Value.getInstance(20), Value.getInstance(10));
+            });
+
+            checkAIOOBE(() -> {
+                Value r = (Value) vh.compareAndExchangeAcquire(array, ci, Value.getInstance(20), Value.getInstance(10));
+            });
+
+            checkAIOOBE(() -> {
+                Value r = (Value) vh.compareAndExchangeRelease(array, ci, Value.getInstance(20), Value.getInstance(10));
+            });
+
+            checkAIOOBE(() -> {
+                boolean r = vh.weakCompareAndSetPlain(array, ci, Value.getInstance(10), Value.getInstance(20));
+            });
+
+            checkAIOOBE(() -> {
+                boolean r = vh.weakCompareAndSet(array, ci, Value.getInstance(10), Value.getInstance(20));
+            });
+
+            checkAIOOBE(() -> {
+                boolean r = vh.weakCompareAndSetAcquire(array, ci, Value.getInstance(10), Value.getInstance(20));
+            });
+
+            checkAIOOBE(() -> {
+                boolean r = vh.weakCompareAndSetRelease(array, ci, Value.getInstance(10), Value.getInstance(20));
+            });
+
+            checkAIOOBE(() -> {
+                Value o = (Value) vh.getAndSet(array, ci, Value.getInstance(10));
+            });
+
+            checkAIOOBE(() -> {
+                Value o = (Value) vh.getAndSetAcquire(array, ci, Value.getInstance(10));
+            });
+
+            checkAIOOBE(() -> {
+                Value o = (Value) vh.getAndSetRelease(array, ci, Value.getInstance(10));
+            });
+
+
+        }
+    }
+
+    static void testArrayStoreException(VarHandle vh) throws Throwable {
+        Object[] array = new Value[10];
+        Arrays.fill(array, Value.getInstance(10));
+        Object value = new Object();
+
+        // Set
+        checkASE(() -> {
+            vh.set(array, 0, value);
+        });
+
+        // SetVolatile
+        checkASE(() -> {
+            vh.setVolatile(array, 0, value);
+        });
+
+        // SetOpaque
+        checkASE(() -> {
+            vh.setOpaque(array, 0, value);
+        });
+
+        // SetRelease
+        checkASE(() -> {
+            vh.setRelease(array, 0, value);
+        });
+
+        // CompareAndSet
+        checkASE(() -> { // receiver reference class
+            boolean r = vh.compareAndSet(array, 0, Value.getInstance(10), value);
+        });
+
+        // WeakCompareAndSet
+        checkASE(() -> { // receiver reference class
+            boolean r = vh.weakCompareAndSetPlain(array, 0, Value.getInstance(10), value);
+        });
+
+        // WeakCompareAndSetVolatile
+        checkASE(() -> { // receiver reference class
+            boolean r = vh.weakCompareAndSet(array, 0, Value.getInstance(10), value);
+        });
+
+        // WeakCompareAndSetAcquire
+        checkASE(() -> { // receiver reference class
+            boolean r = vh.weakCompareAndSetAcquire(array, 0, Value.getInstance(10), value);
+        });
+
+        // WeakCompareAndSetRelease
+        checkASE(() -> { // receiver reference class
+            boolean r = vh.weakCompareAndSetRelease(array, 0, Value.getInstance(10), value);
+        });
+
+        // CompareAndExchange
+        checkASE(() -> { // receiver reference class
+            Value x = (Value) vh.compareAndExchange(array, 0, Value.getInstance(10), value);
+        });
+
+        // CompareAndExchangeAcquire
+        checkASE(() -> { // receiver reference class
+            Value x = (Value) vh.compareAndExchangeAcquire(array, 0, Value.getInstance(10), value);
+        });
+
+        // CompareAndExchangeRelease
+        checkASE(() -> { // receiver reference class
+            Value x = (Value) vh.compareAndExchangeRelease(array, 0, Value.getInstance(10), value);
+        });
+
+        // GetAndSet
+        checkASE(() -> { // receiver reference class
+            Value x = (Value) vh.getAndSet(array, 0, value);
+        });
+
+        // GetAndSetAcquire
+        checkASE(() -> { // receiver reference class
+            Value x = (Value) vh.getAndSetAcquire(array, 0, value);
+        });
+
+        // GetAndSetRelease
+        checkASE(() -> { // receiver reference class
+            Value x = (Value) vh.getAndSetRelease(array, 0, value);
+        });
+    }
+}
+
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessBoolean.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessBoolean.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessBoolean.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessBoolean.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
  *          to hit compilation thresholds
  * @run testng/othervm -Diters=2000 -XX:CompileThresholdScaling=0.1 VarHandleTestMethodHandleAccessBoolean
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessByte.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessByte.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessByte.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessByte.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
  *          to hit compilation thresholds
  * @run testng/othervm -Diters=2000 -XX:CompileThresholdScaling=0.1 VarHandleTestMethodHandleAccessByte
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessChar.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessChar.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessChar.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessChar.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
  *          to hit compilation thresholds
  * @run testng/othervm -Diters=2000 -XX:CompileThresholdScaling=0.1 VarHandleTestMethodHandleAccessChar
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessDouble.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessDouble.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessDouble.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessDouble.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
  *          to hit compilation thresholds
  * @run testng/othervm -Diters=2000 -XX:CompileThresholdScaling=0.1 VarHandleTestMethodHandleAccessDouble
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessFloat.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessFloat.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessFloat.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessFloat.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
  *          to hit compilation thresholds
  * @run testng/othervm -Diters=2000 -XX:CompileThresholdScaling=0.1 VarHandleTestMethodHandleAccessFloat
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessInt.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessInt.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessInt.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessInt.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
  *          to hit compilation thresholds
  * @run testng/othervm -Diters=2000 -XX:CompileThresholdScaling=0.1 VarHandleTestMethodHandleAccessInt
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessLong.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessLong.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessLong.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessLong.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
  *          to hit compilation thresholds
  * @run testng/othervm -Diters=2000 -XX:CompileThresholdScaling=0.1 VarHandleTestMethodHandleAccessLong
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessShort.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessShort.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessShort.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessShort.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
  *          to hit compilation thresholds
  * @run testng/othervm -Diters=2000 -XX:CompileThresholdScaling=0.1 VarHandleTestMethodHandleAccessShort
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessString.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessString.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessString.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessString.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
  *          to hit compilation thresholds
  * @run testng/othervm -Diters=2000 -XX:CompileThresholdScaling=0.1 VarHandleTestMethodHandleAccessString
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessValue.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessValue.java
--- /dev/null
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodHandleAccessValue.java
@@ -0,0 +1,793 @@
+/*
+ * Copyright (c) 2015, 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 file was mechanically generated: Do not edit! -- //
+
+/*
+ * @test
+ * @enablePreview
+ * @modules java.base/jdk.internal.vm.annotation
+ * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
+ *          to hit compilation thresholds
+ * @run testng/othervm -Diters=2000 -XX:CompileThresholdScaling=0.1 VarHandleTestMethodHandleAccessValue
+ */
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.testng.Assert.*;
+
+public class VarHandleTestMethodHandleAccessValue extends VarHandleBaseTest {
+    static final Value static_final_v = Value.getInstance(10);
+
+    static Value static_v;
+
+    final Value final_v = Value.getInstance(10);
+
+    Value v;
+
+    VarHandle vhFinalField;
+
+    VarHandle vhField;
+
+    VarHandle vhStaticField;
+
+    VarHandle vhStaticFinalField;
+
+    VarHandle vhArray;
+
+    @BeforeClass
+    public void setup() throws Exception {
+        vhFinalField = MethodHandles.lookup().findVarHandle(
+                VarHandleTestMethodHandleAccessValue.class, "final_v", Value.class);
+
+        vhField = MethodHandles.lookup().findVarHandle(
+                VarHandleTestMethodHandleAccessValue.class, "v", Value.class);
+
+        vhStaticFinalField = MethodHandles.lookup().findStaticVarHandle(
+            VarHandleTestMethodHandleAccessValue.class, "static_final_v", Value.class);
+
+        vhStaticField = MethodHandles.lookup().findStaticVarHandle(
+            VarHandleTestMethodHandleAccessValue.class, "static_v", Value.class);
+
+        vhArray = MethodHandles.arrayElementVarHandle(Value[].class);
+    }
+
+
+    @DataProvider
+    public Object[][] accessTestCaseProvider() throws Exception {
+        List<AccessTestCase<?>> cases = new ArrayList<>();
+
+        for (VarHandleToMethodHandle f : VarHandleToMethodHandle.values()) {
+            cases.add(new MethodHandleAccessTestCase("Instance field",
+                                                     vhField, f, hs -> testInstanceField(this, hs)));
+            cases.add(new MethodHandleAccessTestCase("Instance field unsupported",
+                                                     vhField, f, hs -> testInstanceFieldUnsupported(this, hs),
+                                                     false));
+
+            cases.add(new MethodHandleAccessTestCase("Static field",
+                                                     vhStaticField, f, VarHandleTestMethodHandleAccessValue::testStaticField));
+            cases.add(new MethodHandleAccessTestCase("Static field unsupported",
+                                                     vhStaticField, f, VarHandleTestMethodHandleAccessValue::testStaticFieldUnsupported,
+                                                     false));
+
+            cases.add(new MethodHandleAccessTestCase("Array",
+                                                     vhArray, f, VarHandleTestMethodHandleAccessValue::testArray));
+            cases.add(new MethodHandleAccessTestCase("Array unsupported",
+                                                     vhArray, f, VarHandleTestMethodHandleAccessValue::testArrayUnsupported,
+                                                     false));
+            cases.add(new MethodHandleAccessTestCase("Array index out of bounds",
+                                                     vhArray, f, VarHandleTestMethodHandleAccessValue::testArrayIndexOutOfBounds,
+                                                     false));
+        }
+
+        // Work around issue with jtreg summary reporting which truncates
+        // the String result of Object.toString to 30 characters, hence
+        // the first dummy argument
+        return cases.stream().map(tc -> new Object[]{tc.toString(), tc}).toArray(Object[][]::new);
+    }
+
+    @Test(dataProvider = "accessTestCaseProvider")
+    public <T> void testAccess(String desc, AccessTestCase<T> atc) throws Throwable {
+        T t = atc.get();
+        int iters = atc.requiresLoop() ? ITERS : 1;
+        for (int c = 0; c < iters; c++) {
+            atc.testAccess(t);
+        }
+    }
+
+
+    static void testInstanceField(VarHandleTestMethodHandleAccessValue recv, Handles hs) throws Throwable {
+        // Plain
+        {
+            hs.get(TestAccessMode.SET).invokeExact(recv, Value.getInstance(10));
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(recv);
+            assertEquals(x, Value.getInstance(10), "set Value value");
+        }
+
+
+        // Volatile
+        {
+            hs.get(TestAccessMode.SET_VOLATILE).invokeExact(recv, Value.getInstance(20));
+            Value x = (Value) hs.get(TestAccessMode.GET_VOLATILE).invokeExact(recv);
+            assertEquals(x, Value.getInstance(20), "setVolatile Value value");
+        }
+
+        // Lazy
+        {
+            hs.get(TestAccessMode.SET_RELEASE).invokeExact(recv, Value.getInstance(10));
+            Value x = (Value) hs.get(TestAccessMode.GET_ACQUIRE).invokeExact(recv);
+            assertEquals(x, Value.getInstance(10), "setRelease Value value");
+        }
+
+        // Opaque
+        {
+            hs.get(TestAccessMode.SET_OPAQUE).invokeExact(recv, Value.getInstance(20));
+            Value x = (Value) hs.get(TestAccessMode.GET_OPAQUE).invokeExact(recv);
+            assertEquals(x, Value.getInstance(20), "setOpaque Value value");
+        }
+
+        hs.get(TestAccessMode.SET).invokeExact(recv, Value.getInstance(10));
+
+        // Compare
+        {
+            boolean r = (boolean) hs.get(TestAccessMode.COMPARE_AND_SET).invokeExact(recv, Value.getInstance(10), Value.getInstance(20));
+            assertEquals(r, true, "success compareAndSet Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(recv);
+            assertEquals(x, Value.getInstance(20), "success compareAndSet Value value");
+        }
+
+        {
+            boolean r = (boolean) hs.get(TestAccessMode.COMPARE_AND_SET).invokeExact(recv, Value.getInstance(10), Value.getInstance(30));
+            assertEquals(r, false, "failing compareAndSet Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(recv);
+            assertEquals(x, Value.getInstance(20), "failing compareAndSet Value value");
+        }
+
+        {
+            Value r = (Value) hs.get(TestAccessMode.COMPARE_AND_EXCHANGE).invokeExact(recv, Value.getInstance(20), Value.getInstance(10));
+            assertEquals(r, Value.getInstance(20), "success compareAndExchange Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(recv);
+            assertEquals(x, Value.getInstance(10), "success compareAndExchange Value value");
+        }
+
+        {
+            Value r = (Value) hs.get(TestAccessMode.COMPARE_AND_EXCHANGE).invokeExact(recv, Value.getInstance(20), Value.getInstance(30));
+            assertEquals(r, Value.getInstance(10), "failing compareAndExchange Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(recv);
+            assertEquals(x, Value.getInstance(10), "failing compareAndExchange Value value");
+        }
+
+        {
+            Value r = (Value) hs.get(TestAccessMode.COMPARE_AND_EXCHANGE_ACQUIRE).invokeExact(recv, Value.getInstance(10), Value.getInstance(20));
+            assertEquals(r, Value.getInstance(10), "success compareAndExchangeAcquire Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(recv);
+            assertEquals(x, Value.getInstance(20), "success compareAndExchangeAcquire Value value");
+        }
+
+        {
+            Value r = (Value) hs.get(TestAccessMode.COMPARE_AND_EXCHANGE_ACQUIRE).invokeExact(recv, Value.getInstance(10), Value.getInstance(30));
+            assertEquals(r, Value.getInstance(20), "failing compareAndExchangeAcquire Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(recv);
+            assertEquals(x, Value.getInstance(20), "failing compareAndExchangeAcquire Value value");
+        }
+
+        {
+            Value r = (Value) hs.get(TestAccessMode.COMPARE_AND_EXCHANGE_RELEASE).invokeExact(recv, Value.getInstance(20), Value.getInstance(10));
+            assertEquals(r, Value.getInstance(20), "success compareAndExchangeRelease Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(recv);
+            assertEquals(x, Value.getInstance(10), "success compareAndExchangeRelease Value value");
+        }
+
+        {
+            Value r = (Value) hs.get(TestAccessMode.COMPARE_AND_EXCHANGE_RELEASE).invokeExact(recv, Value.getInstance(20), Value.getInstance(30));
+            assertEquals(r, Value.getInstance(10), "failing compareAndExchangeRelease Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(recv);
+            assertEquals(x, Value.getInstance(10), "failing compareAndExchangeRelease Value value");
+        }
+
+        {
+            MethodHandle mh = hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_PLAIN);
+            boolean success = false;
+            for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                success = (boolean) mh.invokeExact(recv, Value.getInstance(10), Value.getInstance(20));
+                if (!success) weakDelay();
+            }
+            assertEquals(success, true, "success weakCompareAndSetPlain Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(recv);
+            assertEquals(x, Value.getInstance(20), "success weakCompareAndSetPlain Value value");
+        }
+
+        {
+            boolean success = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_PLAIN).invokeExact(recv, Value.getInstance(10), Value.getInstance(30));
+            assertEquals(success, false, "failing weakCompareAndSetPlain Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(recv);
+            assertEquals(x, Value.getInstance(20), "failing weakCompareAndSetPlain Value value");
+        }
+
+        {
+            MethodHandle mh = hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_ACQUIRE);
+            boolean success = false;
+            for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                success = (boolean) mh.invokeExact(recv, Value.getInstance(20), Value.getInstance(10));
+                if (!success) weakDelay();
+            }
+            assertEquals(success, true, "success weakCompareAndSetAcquire Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(recv);
+            assertEquals(x, Value.getInstance(10), "success weakCompareAndSetAcquire Value");
+        }
+
+        {
+            boolean success = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_ACQUIRE).invokeExact(recv, Value.getInstance(20), Value.getInstance(30));
+            assertEquals(success, false, "failing weakCompareAndSetAcquire Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(recv);
+            assertEquals(x, Value.getInstance(10), "failing weakCompareAndSetAcquire Value value");
+        }
+
+        {
+            MethodHandle mh = hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_RELEASE);
+            boolean success = false;
+            for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                success = (boolean) mh.invokeExact(recv, Value.getInstance(10), Value.getInstance(20));
+                if (!success) weakDelay();
+            }
+            assertEquals(success, true, "success weakCompareAndSetRelease Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(recv);
+            assertEquals(x, Value.getInstance(20), "success weakCompareAndSetRelease Value");
+        }
+
+        {
+            boolean success = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_RELEASE).invokeExact(recv, Value.getInstance(10), Value.getInstance(30));
+            assertEquals(success, false, "failing weakCompareAndSetRelease Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(recv);
+            assertEquals(x, Value.getInstance(20), "failing weakCompareAndSetRelease Value value");
+        }
+
+        {
+            boolean success = false;
+            MethodHandle mh = hs.get(TestAccessMode.WEAK_COMPARE_AND_SET);
+            for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                success = (boolean) mh.invokeExact(recv, Value.getInstance(20), Value.getInstance(10));
+                if (!success) weakDelay();
+            }
+            assertEquals(success, true, "success weakCompareAndSet Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(recv);
+            assertEquals(x, Value.getInstance(10), "success weakCompareAndSet Value");
+        }
+
+        {
+            boolean success = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET).invokeExact(recv, Value.getInstance(20), Value.getInstance(30));
+            assertEquals(success, false, "failing weakCompareAndSet Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(recv);
+            assertEquals(x, Value.getInstance(10), "failing weakCompareAndSet Value value");
+        }
+
+        // Compare set and get
+        {
+            Value o = (Value) hs.get(TestAccessMode.GET_AND_SET).invokeExact(recv, Value.getInstance(20));
+            assertEquals(o, Value.getInstance(10), "getAndSet Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(recv);
+            assertEquals(x, Value.getInstance(20), "getAndSet Value value");
+        }
+
+
+    }
+
+    static void testInstanceFieldUnsupported(VarHandleTestMethodHandleAccessValue recv, Handles hs) throws Throwable {
+
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.GET_AND_ADD)) {
+            checkUOE(am, () -> {
+                Value r = (Value) hs.get(am).invokeExact(recv, Value.getInstance(10));
+            });
+        }
+
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.GET_AND_BITWISE)) {
+            checkUOE(am, () -> {
+                Value r = (Value) hs.get(am).invokeExact(recv, Value.getInstance(10));
+            });
+        }
+    }
+
+
+    static void testStaticField(Handles hs) throws Throwable {
+        // Plain
+        {
+            hs.get(TestAccessMode.SET).invokeExact(Value.getInstance(10));
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(10), "set Value value");
+        }
+
+
+        // Volatile
+        {
+            hs.get(TestAccessMode.SET_VOLATILE).invokeExact(Value.getInstance(20));
+            Value x = (Value) hs.get(TestAccessMode.GET_VOLATILE).invokeExact();
+            assertEquals(x, Value.getInstance(20), "setVolatile Value value");
+        }
+
+        // Lazy
+        {
+            hs.get(TestAccessMode.SET_RELEASE).invokeExact(Value.getInstance(10));
+            Value x = (Value) hs.get(TestAccessMode.GET_ACQUIRE).invokeExact();
+            assertEquals(x, Value.getInstance(10), "setRelease Value value");
+        }
+
+        // Opaque
+        {
+            hs.get(TestAccessMode.SET_OPAQUE).invokeExact(Value.getInstance(20));
+            Value x = (Value) hs.get(TestAccessMode.GET_OPAQUE).invokeExact();
+            assertEquals(x, Value.getInstance(20), "setOpaque Value value");
+        }
+
+        hs.get(TestAccessMode.SET).invokeExact(Value.getInstance(10));
+
+        // Compare
+        {
+            boolean r = (boolean) hs.get(TestAccessMode.COMPARE_AND_SET).invokeExact(Value.getInstance(10), Value.getInstance(20));
+            assertEquals(r, true, "success compareAndSet Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(20), "success compareAndSet Value value");
+        }
+
+        {
+            boolean r = (boolean) hs.get(TestAccessMode.COMPARE_AND_SET).invokeExact(Value.getInstance(10), Value.getInstance(30));
+            assertEquals(r, false, "failing compareAndSet Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(20), "failing compareAndSet Value value");
+        }
+
+        {
+            Value r = (Value) hs.get(TestAccessMode.COMPARE_AND_EXCHANGE).invokeExact(Value.getInstance(20), Value.getInstance(10));
+            assertEquals(r, Value.getInstance(20), "success compareAndExchange Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(10), "success compareAndExchange Value value");
+        }
+
+        {
+            Value r = (Value) hs.get(TestAccessMode.COMPARE_AND_EXCHANGE).invokeExact(Value.getInstance(20), Value.getInstance(30));
+            assertEquals(r, Value.getInstance(10), "failing compareAndExchange Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(10), "failing compareAndExchange Value value");
+        }
+
+        {
+            Value r = (Value) hs.get(TestAccessMode.COMPARE_AND_EXCHANGE_ACQUIRE).invokeExact(Value.getInstance(10), Value.getInstance(20));
+            assertEquals(r, Value.getInstance(10), "success compareAndExchangeAcquire Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(20), "success compareAndExchangeAcquire Value value");
+        }
+
+        {
+            Value r = (Value) hs.get(TestAccessMode.COMPARE_AND_EXCHANGE_ACQUIRE).invokeExact(Value.getInstance(10), Value.getInstance(30));
+            assertEquals(r, Value.getInstance(20), "failing compareAndExchangeAcquire Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(20), "failing compareAndExchangeAcquire Value value");
+        }
+
+        {
+            Value r = (Value) hs.get(TestAccessMode.COMPARE_AND_EXCHANGE_RELEASE).invokeExact(Value.getInstance(20), Value.getInstance(10));
+            assertEquals(r, Value.getInstance(20), "success compareAndExchangeRelease Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(10), "success compareAndExchangeRelease Value value");
+        }
+
+        {
+            Value r = (Value) hs.get(TestAccessMode.COMPARE_AND_EXCHANGE_RELEASE).invokeExact(Value.getInstance(20), Value.getInstance(30));
+            assertEquals(r, Value.getInstance(10), "failing compareAndExchangeRelease Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(10), "failing compareAndExchangeRelease Value value");
+        }
+
+        {
+            MethodHandle mh = hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_PLAIN);
+            boolean success = false;
+            for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                success = (boolean) mh.invokeExact(Value.getInstance(10), Value.getInstance(20));
+                if (!success) weakDelay();
+            }
+            assertEquals(success, true, "success weakCompareAndSetPlain Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(20), "success weakCompareAndSetPlain Value value");
+        }
+
+        {
+            boolean success = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_PLAIN).invokeExact(Value.getInstance(10), Value.getInstance(30));
+            assertEquals(success, false, "failing weakCompareAndSetPlain Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(20), "failing weakCompareAndSetPlain Value value");
+        }
+
+        {
+            MethodHandle mh = hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_ACQUIRE);
+            boolean success = false;
+            for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                success = (boolean) mh.invokeExact(Value.getInstance(20), Value.getInstance(10));
+                if (!success) weakDelay();
+            }
+            assertEquals(success, true, "success weakCompareAndSetAcquire Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(10), "success weakCompareAndSetAcquire Value");
+        }
+
+        {
+            MethodHandle mh = hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_ACQUIRE);
+            boolean success = (boolean) mh.invokeExact(Value.getInstance(20), Value.getInstance(30));
+            assertEquals(success, false, "failing weakCompareAndSetAcquire Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(10), "failing weakCompareAndSetAcquire Value value");
+        }
+
+        {
+            MethodHandle mh = hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_RELEASE);
+            boolean success = false;
+            for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                success = (boolean) mh.invokeExact(Value.getInstance(10), Value.getInstance(20));
+                if (!success) weakDelay();
+            }
+            assertEquals(success, true, "success weakCompareAndSetRelease Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(20), "success weakCompareAndSetRelease Value");
+        }
+
+        {
+            boolean success = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_RELEASE).invokeExact(Value.getInstance(10), Value.getInstance(30));
+            assertEquals(success, false, "failing weakCompareAndSetRelease Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(20), "failing weakCompareAndSetRelease Value value");
+        }
+
+        {
+            MethodHandle mh = hs.get(TestAccessMode.WEAK_COMPARE_AND_SET);
+            boolean success = false;
+            for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                success = (boolean) mh.invokeExact(Value.getInstance(20), Value.getInstance(10));
+                if (!success) weakDelay();
+            }
+            assertEquals(success, true, "success weakCompareAndSet Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(10), "success weakCompareAndSet Value");
+        }
+
+        {
+            boolean success = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET).invokeExact(Value.getInstance(20), Value.getInstance(30));
+            assertEquals(success, false, "failing weakCompareAndSet Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(10), "failing weakCompareAndSetRe Value value");
+        }
+
+        // Compare set and get
+        {
+            hs.get(TestAccessMode.SET).invokeExact(Value.getInstance(10));
+
+            Value o = (Value) hs.get(TestAccessMode.GET_AND_SET).invokeExact(Value.getInstance(20));
+            assertEquals(o, Value.getInstance(10), "getAndSet Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(20), "getAndSet Value value");
+        }
+
+        // Compare set and get
+        {
+            hs.get(TestAccessMode.SET).invokeExact(Value.getInstance(10));
+
+            Value o = (Value) hs.get(TestAccessMode.GET_AND_SET_ACQUIRE).invokeExact(Value.getInstance(20));
+            assertEquals(o, Value.getInstance(10), "getAndSetAcquire Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(20), "getAndSetAcquire Value value");
+        }
+
+        // Compare set and get
+        {
+            hs.get(TestAccessMode.SET).invokeExact(Value.getInstance(10));
+
+            Value o = (Value) hs.get(TestAccessMode.GET_AND_SET_RELEASE).invokeExact(Value.getInstance(20));
+            assertEquals(o, Value.getInstance(10), "getAndSetRelease Value");
+            Value x = (Value) hs.get(TestAccessMode.GET).invokeExact();
+            assertEquals(x, Value.getInstance(20), "getAndSetRelease Value value");
+        }
+
+
+    }
+
+    static void testStaticFieldUnsupported(Handles hs) throws Throwable {
+
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.GET_AND_ADD)) {
+            checkUOE(am, () -> {
+                Value r = (Value) hs.get(am).invokeExact(Value.getInstance(10));
+            });
+        }
+
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.GET_AND_BITWISE)) {
+            checkUOE(am, () -> {
+                Value r = (Value) hs.get(am).invokeExact(Value.getInstance(10));
+            });
+        }
+    }
+
+
+    static void testArray(Handles hs) throws Throwable {
+        Value[] array = new Value[10];
+
+        for (int i = 0; i < array.length; i++) {
+            // Plain
+            {
+                hs.get(TestAccessMode.SET).invokeExact(array, i, Value.getInstance(10));
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(10), "get Value value");
+            }
+
+
+            // Volatile
+            {
+                hs.get(TestAccessMode.SET_VOLATILE).invokeExact(array, i, Value.getInstance(20));
+                Value x = (Value) hs.get(TestAccessMode.GET_VOLATILE).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(20), "setVolatile Value value");
+            }
+
+            // Lazy
+            {
+                hs.get(TestAccessMode.SET_RELEASE).invokeExact(array, i, Value.getInstance(10));
+                Value x = (Value) hs.get(TestAccessMode.GET_ACQUIRE).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(10), "setRelease Value value");
+            }
+
+            // Opaque
+            {
+                hs.get(TestAccessMode.SET_OPAQUE).invokeExact(array, i, Value.getInstance(20));
+                Value x = (Value) hs.get(TestAccessMode.GET_OPAQUE).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(20), "setOpaque Value value");
+            }
+
+            hs.get(TestAccessMode.SET).invokeExact(array, i, Value.getInstance(10));
+
+            // Compare
+            {
+                boolean r = (boolean) hs.get(TestAccessMode.COMPARE_AND_SET).invokeExact(array, i, Value.getInstance(10), Value.getInstance(20));
+                assertEquals(r, true, "success compareAndSet Value");
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(20), "success compareAndSet Value value");
+            }
+
+            {
+                boolean r = (boolean) hs.get(TestAccessMode.COMPARE_AND_SET).invokeExact(array, i, Value.getInstance(10), Value.getInstance(30));
+                assertEquals(r, false, "failing compareAndSet Value");
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(20), "failing compareAndSet Value value");
+            }
+
+            {
+                Value r = (Value) hs.get(TestAccessMode.COMPARE_AND_EXCHANGE).invokeExact(array, i, Value.getInstance(20), Value.getInstance(10));
+                assertEquals(r, Value.getInstance(20), "success compareAndExchange Value");
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(10), "success compareAndExchange Value value");
+            }
+
+            {
+                Value r = (Value) hs.get(TestAccessMode.COMPARE_AND_EXCHANGE).invokeExact(array, i, Value.getInstance(20), Value.getInstance(30));
+                assertEquals(r, Value.getInstance(10), "failing compareAndExchange Value");
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(10), "failing compareAndExchange Value value");
+            }
+
+            {
+                Value r = (Value) hs.get(TestAccessMode.COMPARE_AND_EXCHANGE_ACQUIRE).invokeExact(array, i, Value.getInstance(10), Value.getInstance(20));
+                assertEquals(r, Value.getInstance(10), "success compareAndExchangeAcquire Value");
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(20), "success compareAndExchangeAcquire Value value");
+            }
+
+            {
+                Value r = (Value) hs.get(TestAccessMode.COMPARE_AND_EXCHANGE_ACQUIRE).invokeExact(array, i, Value.getInstance(10), Value.getInstance(30));
+                assertEquals(r, Value.getInstance(20), "failing compareAndExchangeAcquire Value");
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(20), "failing compareAndExchangeAcquire Value value");
+            }
+
+            {
+                Value r = (Value) hs.get(TestAccessMode.COMPARE_AND_EXCHANGE_RELEASE).invokeExact(array, i, Value.getInstance(20), Value.getInstance(10));
+                assertEquals(r, Value.getInstance(20), "success compareAndExchangeRelease Value");
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(10), "success compareAndExchangeRelease Value value");
+            }
+
+            {
+                Value r = (Value) hs.get(TestAccessMode.COMPARE_AND_EXCHANGE_RELEASE).invokeExact(array, i, Value.getInstance(20), Value.getInstance(30));
+                assertEquals(r, Value.getInstance(10), "failing compareAndExchangeRelease Value");
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(10), "failing compareAndExchangeRelease Value value");
+            }
+
+            {
+                MethodHandle mh = hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_PLAIN);
+                boolean success = false;
+                for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                    success = (boolean) mh.invokeExact(array, i, Value.getInstance(10), Value.getInstance(20));
+                    if (!success) weakDelay();
+                }
+                assertEquals(success, true, "success weakCompareAndSetPlain Value");
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(20), "success weakCompareAndSetPlain Value value");
+            }
+
+            {
+                boolean success = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_PLAIN).invokeExact(array, i, Value.getInstance(10), Value.getInstance(30));
+                assertEquals(success, false, "failing weakCompareAndSetPlain Value");
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(20), "failing weakCompareAndSetPlain Value value");
+            }
+
+            {
+                MethodHandle mh = hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_ACQUIRE);
+                boolean success = false;
+                for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                    success = (boolean) mh.invokeExact(array, i, Value.getInstance(20), Value.getInstance(10));
+                    if (!success) weakDelay();
+                }
+                assertEquals(success, true, "success weakCompareAndSetAcquire Value");
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(10), "success weakCompareAndSetAcquire Value");
+            }
+
+            {
+                boolean success = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_ACQUIRE).invokeExact(array, i, Value.getInstance(20), Value.getInstance(30));
+                assertEquals(success, false, "failing weakCompareAndSetAcquire Value");
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(10), "failing weakCompareAndSetAcquire Value value");
+            }
+
+            {
+                MethodHandle mh = hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_RELEASE);
+                boolean success = false;
+                for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                    success = (boolean) mh.invokeExact(array, i, Value.getInstance(10), Value.getInstance(20));
+                    if (!success) weakDelay();
+                }
+                assertEquals(success, true, "success weakCompareAndSetRelease Value");
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(20), "success weakCompareAndSetRelease Value");
+            }
+
+            {
+                boolean success = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET_ACQUIRE).invokeExact(array, i, Value.getInstance(10), Value.getInstance(30));
+                assertEquals(success, false, "failing weakCompareAndSetAcquire Value");
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(20), "failing weakCompareAndSetAcquire Value value");
+            }
+
+            {
+                MethodHandle mh = hs.get(TestAccessMode.WEAK_COMPARE_AND_SET);
+                boolean success = false;
+                for (int c = 0; c < WEAK_ATTEMPTS && !success; c++) {
+                    success = (boolean) mh.invokeExact(array, i, Value.getInstance(20), Value.getInstance(10));
+                    if (!success) weakDelay();
+                }
+                assertEquals(success, true, "success weakCompareAndSet Value");
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(10), "success weakCompareAndSet Value");
+            }
+
+            {
+                boolean success = (boolean) hs.get(TestAccessMode.WEAK_COMPARE_AND_SET).invokeExact(array, i, Value.getInstance(20), Value.getInstance(30));
+                assertEquals(success, false, "failing weakCompareAndSet Value");
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(10), "failing weakCompareAndSet Value value");
+            }
+
+            // Compare set and get
+            {
+                hs.get(TestAccessMode.SET).invokeExact(array, i, Value.getInstance(10));
+
+                Value o = (Value) hs.get(TestAccessMode.GET_AND_SET).invokeExact(array, i, Value.getInstance(20));
+                assertEquals(o, Value.getInstance(10), "getAndSet Value");
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(20), "getAndSet Value value");
+            }
+
+            {
+                hs.get(TestAccessMode.SET).invokeExact(array, i, Value.getInstance(10));
+
+                Value o = (Value) hs.get(TestAccessMode.GET_AND_SET_ACQUIRE).invokeExact(array, i, Value.getInstance(20));
+                assertEquals(o, Value.getInstance(10), "getAndSetAcquire Value");
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(20), "getAndSetAcquire Value value");
+            }
+
+            {
+                hs.get(TestAccessMode.SET).invokeExact(array, i, Value.getInstance(10));
+
+                Value o = (Value) hs.get(TestAccessMode.GET_AND_SET_RELEASE).invokeExact(array, i, Value.getInstance(20));
+                assertEquals(o, Value.getInstance(10), "getAndSetRelease Value");
+                Value x = (Value) hs.get(TestAccessMode.GET).invokeExact(array, i);
+                assertEquals(x, Value.getInstance(20), "getAndSetRelease Value value");
+            }
+
+
+        }
+    }
+
+    static void testArrayUnsupported(Handles hs) throws Throwable {
+        Value[] array = new Value[10];
+
+        final int i = 0;
+
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.GET_AND_ADD)) {
+            checkUOE(am, () -> {
+                Value o = (Value) hs.get(am).invokeExact(array, i, Value.getInstance(10));
+            });
+        }
+
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.GET_AND_BITWISE)) {
+            checkUOE(am, () -> {
+                Value o = (Value) hs.get(am).invokeExact(array, i, Value.getInstance(10));
+            });
+        }
+    }
+
+    static void testArrayIndexOutOfBounds(Handles hs) throws Throwable {
+        Value[] array = new Value[10];
+
+        for (int i : new int[]{-1, Integer.MIN_VALUE, 10, 11, Integer.MAX_VALUE}) {
+            final int ci = i;
+
+            for (TestAccessMode am : testAccessModesOfType(TestAccessType.GET)) {
+                checkAIOOBE(am, () -> {
+                    Value x = (Value) hs.get(am).invokeExact(array, ci);
+                });
+            }
+
+            for (TestAccessMode am : testAccessModesOfType(TestAccessType.SET)) {
+                checkAIOOBE(am, () -> {
+                    hs.get(am).invokeExact(array, ci, Value.getInstance(10));
+                });
+            }
+
+            for (TestAccessMode am : testAccessModesOfType(TestAccessType.COMPARE_AND_SET)) {
+                checkAIOOBE(am, () -> {
+                    boolean r = (boolean) hs.get(am).invokeExact(array, ci, Value.getInstance(10), Value.getInstance(20));
+                });
+            }
+
+            for (TestAccessMode am : testAccessModesOfType(TestAccessType.COMPARE_AND_EXCHANGE)) {
+                checkAIOOBE(am, () -> {
+                    Value r = (Value) hs.get(am).invokeExact(array, ci, Value.getInstance(20), Value.getInstance(10));
+                });
+            }
+
+            for (TestAccessMode am : testAccessModesOfType(TestAccessType.GET_AND_SET)) {
+                checkAIOOBE(am, () -> {
+                    Value o = (Value) hs.get(am).invokeExact(array, ci, Value.getInstance(10));
+                });
+            }
+
+
+        }
+    }
+}
+
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeBoolean.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeBoolean.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeBoolean.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeBoolean.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @bug 8156486
  * @run testng/othervm VarHandleTestMethodTypeBoolean
  * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeBoolean
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeByte.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeByte.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeByte.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeByte.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @bug 8156486
  * @run testng/othervm VarHandleTestMethodTypeByte
  * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeByte
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeChar.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeChar.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeChar.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeChar.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @bug 8156486
  * @run testng/othervm VarHandleTestMethodTypeChar
  * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeChar
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeDouble.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeDouble.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeDouble.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeDouble.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @bug 8156486
  * @run testng/othervm VarHandleTestMethodTypeDouble
  * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeDouble
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeFloat.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeFloat.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeFloat.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeFloat.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @bug 8156486
  * @run testng/othervm VarHandleTestMethodTypeFloat
  * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeFloat
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeInt.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeInt.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeInt.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeInt.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @bug 8156486
  * @run testng/othervm VarHandleTestMethodTypeInt
  * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeInt
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeLong.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeLong.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeLong.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeLong.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @bug 8156486
  * @run testng/othervm VarHandleTestMethodTypeLong
  * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeLong
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeShort.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeShort.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeShort.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeShort.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @bug 8156486
  * @run testng/othervm VarHandleTestMethodTypeShort
  * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeShort
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeString.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeString.java
--- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeString.java
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeString.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,10 +19,12 @@
  * 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 file was mechanically generated: Do not edit! -- //
+
 /*
  * @test
  * @bug 8156486
  * @run testng/othervm VarHandleTestMethodTypeString
  * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeString
diff a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeValue.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeValue.java
--- /dev/null
+++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestMethodTypeValue.java
@@ -0,0 +1,2062 @@
+/*
+ * Copyright (c) 2015, 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 file was mechanically generated: Do not edit! -- //
+
+/*
+ * @test
+ * @bug 8156486
+ * @enablePreview
+ * @modules java.base/jdk.internal.vm.annotation
+ * @run testng/othervm VarHandleTestMethodTypeValue
+ * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeValue
+ * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false VarHandleTestMethodTypeValue
+ * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodTypeValue
+ */
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.testng.Assert.*;
+
+import static java.lang.invoke.MethodType.*;
+
+public class VarHandleTestMethodTypeValue extends VarHandleBaseTest {
+    static final Value static_final_v = Value.getInstance(10);
+
+    static Value static_v = Value.getInstance(10);
+
+    final Value final_v = Value.getInstance(10);
+
+    Value v = Value.getInstance(10);
+
+    VarHandle vhFinalField;
+
+    VarHandle vhField;
+
+    VarHandle vhStaticField;
+
+    VarHandle vhStaticFinalField;
+
+    VarHandle vhArray;
+
+    @BeforeClass
+    public void setup() throws Exception {
+        vhFinalField = MethodHandles.lookup().findVarHandle(
+                VarHandleTestMethodTypeValue.class, "final_v", Value.class);
+
+        vhField = MethodHandles.lookup().findVarHandle(
+                VarHandleTestMethodTypeValue.class, "v", Value.class);
+
+        vhStaticFinalField = MethodHandles.lookup().findStaticVarHandle(
+            VarHandleTestMethodTypeValue.class, "static_final_v", Value.class);
+
+        vhStaticField = MethodHandles.lookup().findStaticVarHandle(
+            VarHandleTestMethodTypeValue.class, "static_v", Value.class);
+
+        vhArray = MethodHandles.arrayElementVarHandle(Value[].class);
+    }
+
+    @DataProvider
+    public Object[][] accessTestCaseProvider() throws Exception {
+        List<AccessTestCase<?>> cases = new ArrayList<>();
+
+        cases.add(new VarHandleAccessTestCase("Instance field",
+                                              vhField, vh -> testInstanceFieldWrongMethodType(this, vh),
+                                              false));
+
+        cases.add(new VarHandleAccessTestCase("Static field",
+                                              vhStaticField, VarHandleTestMethodTypeValue::testStaticFieldWrongMethodType,
+                                              false));
+
+        cases.add(new VarHandleAccessTestCase("Array",
+                                              vhArray, VarHandleTestMethodTypeValue::testArrayWrongMethodType,
+                                              false));
+
+        for (VarHandleToMethodHandle f : VarHandleToMethodHandle.values()) {
+            cases.add(new MethodHandleAccessTestCase("Instance field",
+                                                     vhField, f, hs -> testInstanceFieldWrongMethodType(this, hs),
+                                                     false));
+
+            cases.add(new MethodHandleAccessTestCase("Static field",
+                                                     vhStaticField, f, VarHandleTestMethodTypeValue::testStaticFieldWrongMethodType,
+                                                     false));
+
+            cases.add(new MethodHandleAccessTestCase("Array",
+                                                     vhArray, f, VarHandleTestMethodTypeValue::testArrayWrongMethodType,
+                                                     false));
+        }
+        // Work around issue with jtreg summary reporting which truncates
+        // the String result of Object.toString to 30 characters, hence
+        // the first dummy argument
+        return cases.stream().map(tc -> new Object[]{tc.toString(), tc}).toArray(Object[][]::new);
+    }
+
+    @Test(dataProvider = "accessTestCaseProvider")
+    public <T> void testAccess(String desc, AccessTestCase<T> atc) throws Throwable {
+        T t = atc.get();
+        int iters = atc.requiresLoop() ? ITERS : 1;
+        for (int c = 0; c < iters; c++) {
+            atc.testAccess(t);
+        }
+    }
+
+
+    static void testInstanceFieldWrongMethodType(VarHandleTestMethodTypeValue recv, VarHandle vh) throws Throwable {
+        // Get
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            Value x = (Value) vh.get(null);
+        });
+        checkCCE(() -> { // receiver reference class
+            Value x = (Value) vh.get(Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            Value x = (Value) vh.get(0);
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void x = (Void) vh.get(recv);
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.get(recv);
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.get();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.get(recv, Void.class);
+        });
+
+
+        // Set
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            vh.set(null, Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            vh.set(Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // value reference class
+            vh.set(recv, Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            vh.set(0, Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            vh.set();
+        });
+        checkWMTE(() -> { // >
+            vh.set(recv, Value.getInstance(10), Void.class);
+        });
+
+
+        // GetVolatile
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            Value x = (Value) vh.getVolatile(null);
+        });
+        checkCCE(() -> { // receiver reference class
+            Value x = (Value) vh.getVolatile(Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            Value x = (Value) vh.getVolatile(0);
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void x = (Void) vh.getVolatile(recv);
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.getVolatile(recv);
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.getVolatile();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.getVolatile(recv, Void.class);
+        });
+
+
+        // SetVolatile
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            vh.setVolatile(null, Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            vh.setVolatile(Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // value reference class
+            vh.setVolatile(recv, Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            vh.setVolatile(0, Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            vh.setVolatile();
+        });
+        checkWMTE(() -> { // >
+            vh.setVolatile(recv, Value.getInstance(10), Void.class);
+        });
+
+
+        // GetOpaque
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            Value x = (Value) vh.getOpaque(null);
+        });
+        checkCCE(() -> { // receiver reference class
+            Value x = (Value) vh.getOpaque(Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            Value x = (Value) vh.getOpaque(0);
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void x = (Void) vh.getOpaque(recv);
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.getOpaque(recv);
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.getOpaque();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.getOpaque(recv, Void.class);
+        });
+
+
+        // SetOpaque
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            vh.setOpaque(null, Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            vh.setOpaque(Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // value reference class
+            vh.setOpaque(recv, Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            vh.setOpaque(0, Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            vh.setOpaque();
+        });
+        checkWMTE(() -> { // >
+            vh.setOpaque(recv, Value.getInstance(10), Void.class);
+        });
+
+
+        // GetAcquire
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            Value x = (Value) vh.getAcquire(null);
+        });
+        checkCCE(() -> { // receiver reference class
+            Value x = (Value) vh.getAcquire(Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            Value x = (Value) vh.getAcquire(0);
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void x = (Void) vh.getAcquire(recv);
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.getAcquire(recv);
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.getAcquire();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.getAcquire(recv, Void.class);
+        });
+
+
+        // SetRelease
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            vh.setRelease(null, Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            vh.setRelease(Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // value reference class
+            vh.setRelease(recv, Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            vh.setRelease(0, Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            vh.setRelease();
+        });
+        checkWMTE(() -> { // >
+            vh.setRelease(recv, Value.getInstance(10), Void.class);
+        });
+
+
+        // CompareAndSet
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            boolean r = vh.compareAndSet(null, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            boolean r = vh.compareAndSet(Void.class, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // expected reference class
+            boolean r = vh.compareAndSet(recv, Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            boolean r = vh.compareAndSet(recv, Value.getInstance(10), Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            boolean r = vh.compareAndSet(0, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            boolean r = vh.compareAndSet();
+        });
+        checkWMTE(() -> { // >
+            boolean r = vh.compareAndSet(recv, Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // WeakCompareAndSet
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            boolean r = vh.weakCompareAndSetPlain(null, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            boolean r = vh.weakCompareAndSetPlain(Void.class, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // expected reference class
+            boolean r = vh.weakCompareAndSetPlain(recv, Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            boolean r = vh.weakCompareAndSetPlain(recv, Value.getInstance(10), Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            boolean r = vh.weakCompareAndSetPlain(0, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            boolean r = vh.weakCompareAndSetPlain();
+        });
+        checkWMTE(() -> { // >
+            boolean r = vh.weakCompareAndSetPlain(recv, Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // WeakCompareAndSetVolatile
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            boolean r = vh.weakCompareAndSet(null, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            boolean r = vh.weakCompareAndSet(Void.class, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // expected reference class
+            boolean r = vh.weakCompareAndSet(recv, Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            boolean r = vh.weakCompareAndSet(recv, Value.getInstance(10), Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            boolean r = vh.weakCompareAndSet(0, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            boolean r = vh.weakCompareAndSet();
+        });
+        checkWMTE(() -> { // >
+            boolean r = vh.weakCompareAndSet(recv, Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // WeakCompareAndSetAcquire
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            boolean r = vh.weakCompareAndSetAcquire(null, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            boolean r = vh.weakCompareAndSetAcquire(Void.class, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // expected reference class
+            boolean r = vh.weakCompareAndSetAcquire(recv, Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            boolean r = vh.weakCompareAndSetAcquire(recv, Value.getInstance(10), Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            boolean r = vh.weakCompareAndSetAcquire(0, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            boolean r = vh.weakCompareAndSetAcquire();
+        });
+        checkWMTE(() -> { // >
+            boolean r = vh.weakCompareAndSetAcquire(recv, Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // WeakCompareAndSetRelease
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            boolean r = vh.weakCompareAndSetRelease(null, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            boolean r = vh.weakCompareAndSetRelease(Void.class, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // expected reference class
+            boolean r = vh.weakCompareAndSetRelease(recv, Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            boolean r = vh.weakCompareAndSetRelease(recv, Value.getInstance(10), Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            boolean r = vh.weakCompareAndSetRelease(0, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            boolean r = vh.weakCompareAndSetRelease();
+        });
+        checkWMTE(() -> { // >
+            boolean r = vh.weakCompareAndSetRelease(recv, Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // CompareAndExchange
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            Value x = (Value) vh.compareAndExchange(null, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            Value x = (Value) vh.compareAndExchange(Void.class, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // expected reference class
+            Value x = (Value) vh.compareAndExchange(recv, Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            Value x = (Value) vh.compareAndExchange(recv, Value.getInstance(10), Void.class);
+        });
+        checkWMTE(() -> { // reciever primitive class
+            Value x = (Value) vh.compareAndExchange(0, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void r = (Void) vh.compareAndExchange(recv, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.compareAndExchange(recv, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.compareAndExchange();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.compareAndExchange(recv, Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // CompareAndExchangeAcquire
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            Value x = (Value) vh.compareAndExchangeAcquire(null, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            Value x = (Value) vh.compareAndExchangeAcquire(Void.class, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // expected reference class
+            Value x = (Value) vh.compareAndExchangeAcquire(recv, Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            Value x = (Value) vh.compareAndExchangeAcquire(recv, Value.getInstance(10), Void.class);
+        });
+        checkWMTE(() -> { // reciever primitive class
+            Value x = (Value) vh.compareAndExchangeAcquire(0, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void r = (Void) vh.compareAndExchangeAcquire(recv, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.compareAndExchangeAcquire(recv, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.compareAndExchangeAcquire();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.compareAndExchangeAcquire(recv, Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // CompareAndExchangeRelease
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            Value x = (Value) vh.compareAndExchangeRelease(null, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            Value x = (Value) vh.compareAndExchangeRelease(Void.class, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // expected reference class
+            Value x = (Value) vh.compareAndExchangeRelease(recv, Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            Value x = (Value) vh.compareAndExchangeRelease(recv, Value.getInstance(10), Void.class);
+        });
+        checkWMTE(() -> { // reciever primitive class
+            Value x = (Value) vh.compareAndExchangeRelease(0, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void r = (Void) vh.compareAndExchangeRelease(recv, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.compareAndExchangeRelease(recv, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.compareAndExchangeRelease();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.compareAndExchangeRelease(recv, Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // GetAndSet
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            Value x = (Value) vh.getAndSet(null, Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            Value x = (Value) vh.getAndSet(Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // value reference class
+            Value x = (Value) vh.getAndSet(recv, Void.class);
+        });
+        checkWMTE(() -> { // reciever primitive class
+            Value x = (Value) vh.getAndSet(0, Value.getInstance(10));
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void r = (Void) vh.getAndSet(recv, Value.getInstance(10));
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.getAndSet(recv, Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.getAndSet();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.getAndSet(recv, Value.getInstance(10), Void.class);
+        });
+
+        // GetAndSetAcquire
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            Value x = (Value) vh.getAndSetAcquire(null, Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            Value x = (Value) vh.getAndSetAcquire(Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // value reference class
+            Value x = (Value) vh.getAndSetAcquire(recv, Void.class);
+        });
+        checkWMTE(() -> { // reciever primitive class
+            Value x = (Value) vh.getAndSetAcquire(0, Value.getInstance(10));
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void r = (Void) vh.getAndSetAcquire(recv, Value.getInstance(10));
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.getAndSetAcquire(recv, Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.getAndSetAcquire();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.getAndSetAcquire(recv, Value.getInstance(10), Void.class);
+        });
+
+        // GetAndSetRelease
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            Value x = (Value) vh.getAndSetRelease(null, Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            Value x = (Value) vh.getAndSetRelease(Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // value reference class
+            Value x = (Value) vh.getAndSetRelease(recv, Void.class);
+        });
+        checkWMTE(() -> { // reciever primitive class
+            Value x = (Value) vh.getAndSetRelease(0, Value.getInstance(10));
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void r = (Void) vh.getAndSetRelease(recv, Value.getInstance(10));
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.getAndSetRelease(recv, Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.getAndSetRelease();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.getAndSetRelease(recv, Value.getInstance(10), Void.class);
+        });
+
+
+    }
+
+    static void testInstanceFieldWrongMethodType(VarHandleTestMethodTypeValue recv, Handles hs) throws Throwable {
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.GET)) {
+            // Incorrect argument types
+            checkNPE(() -> { // null receiver
+                Value x = (Value) hs.get(am, methodType(Value.class, VarHandleTestMethodTypeValue.class)).
+                    invokeExact((VarHandleTestMethodTypeValue) null);
+            });
+            hs.checkWMTEOrCCE(() -> { // receiver reference class
+                Value x = (Value) hs.get(am, methodType(Value.class, Class.class)).
+                    invokeExact(Void.class);
+            });
+            checkWMTE(() -> { // receiver primitive class
+                Value x = (Value) hs.get(am, methodType(Value.class, int.class)).
+                    invokeExact(0);
+            });
+            // Incorrect return type
+            hs.checkWMTEOrCCE(() -> { // reference class
+                Void x = (Void) hs.get(am, methodType(Void.class, VarHandleTestMethodTypeValue.class)).
+                    invokeExact(recv);
+            });
+            checkWMTE(() -> { // primitive class
+                boolean x = (boolean) hs.get(am, methodType(boolean.class, VarHandleTestMethodTypeValue.class)).
+                    invokeExact(recv);
+            });
+            // Incorrect arity
+            checkWMTE(() -> { // 0
+                Value x = (Value) hs.get(am, methodType(Value.class)).
+                    invokeExact();
+            });
+            checkWMTE(() -> { // >
+                Value x = (Value) hs.get(am, methodType(Value.class, VarHandleTestMethodTypeValue.class, Class.class)).
+                    invokeExact(recv, Void.class);
+            });
+        }
+
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.SET)) {
+            // Incorrect argument types
+            checkNPE(() -> { // null receiver
+                hs.get(am, methodType(void.class, VarHandleTestMethodTypeValue.class, Value.class)).
+                    invokeExact((VarHandleTestMethodTypeValue) null, Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // receiver reference class
+                hs.get(am, methodType(void.class, Class.class, Value.class)).
+                    invokeExact(Void.class, Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // value reference class
+                hs.get(am, methodType(void.class, VarHandleTestMethodTypeValue.class, Class.class)).
+                    invokeExact(recv, Void.class);
+            });
+            checkWMTE(() -> { // receiver primitive class
+                hs.get(am, methodType(void.class, int.class, Value.class)).
+                    invokeExact(0, Value.getInstance(10));
+            });
+            // Incorrect arity
+            checkWMTE(() -> { // 0
+                hs.get(am, methodType(void.class)).
+                    invokeExact();
+            });
+            checkWMTE(() -> { // >
+                hs.get(am, methodType(void.class, VarHandleTestMethodTypeValue.class, Value.class, Class.class)).
+                    invokeExact(recv, Value.getInstance(10), Void.class);
+            });
+        }
+
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.COMPARE_AND_SET)) {
+            // Incorrect argument types
+            checkNPE(() -> { // null receiver
+                boolean r = (boolean) hs.get(am, methodType(boolean.class, VarHandleTestMethodTypeValue.class, Value.class, Value.class)).
+                    invokeExact((VarHandleTestMethodTypeValue) null, Value.getInstance(10), Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // receiver reference class
+                boolean r = (boolean) hs.get(am, methodType(boolean.class, Class.class, Value.class, Value.class)).
+                    invokeExact(Void.class, Value.getInstance(10), Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // expected reference class
+                boolean r = (boolean) hs.get(am, methodType(boolean.class, VarHandleTestMethodTypeValue.class, Class.class, Value.class)).
+                    invokeExact(recv, Void.class, Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // actual reference class
+                boolean r = (boolean) hs.get(am, methodType(boolean.class, VarHandleTestMethodTypeValue.class, Value.class, Class.class)).
+                    invokeExact(recv, Value.getInstance(10), Void.class);
+            });
+            checkWMTE(() -> { // receiver primitive class
+                boolean r = (boolean) hs.get(am, methodType(boolean.class, int.class , Value.class, Value.class)).
+                    invokeExact(0, Value.getInstance(10), Value.getInstance(10));
+            });
+            // Incorrect arity
+            checkWMTE(() -> { // 0
+                boolean r = (boolean) hs.get(am, methodType(boolean.class)).
+                    invokeExact();
+            });
+            checkWMTE(() -> { // >
+                boolean r = (boolean) hs.get(am, methodType(boolean.class, VarHandleTestMethodTypeValue.class, Value.class, Value.class, Class.class)).
+                    invokeExact(recv, Value.getInstance(10), Value.getInstance(10), Void.class);
+            });
+        }
+
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.COMPARE_AND_EXCHANGE)) {
+            checkNPE(() -> { // null receiver
+                Value x = (Value) hs.get(am, methodType(Value.class, VarHandleTestMethodTypeValue.class, Value.class, Value.class)).
+                    invokeExact((VarHandleTestMethodTypeValue) null, Value.getInstance(10), Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // receiver reference class
+                Value x = (Value) hs.get(am, methodType(Value.class, Class.class, Value.class, Value.class)).
+                    invokeExact(Void.class, Value.getInstance(10), Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // expected reference class
+                Value x = (Value) hs.get(am, methodType(Value.class, VarHandleTestMethodTypeValue.class, Class.class, Value.class)).
+                    invokeExact(recv, Void.class, Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // actual reference class
+                Value x = (Value) hs.get(am, methodType(Value.class, VarHandleTestMethodTypeValue.class, Value.class, Class.class)).
+                    invokeExact(recv, Value.getInstance(10), Void.class);
+            });
+            checkWMTE(() -> { // reciever primitive class
+                Value x = (Value) hs.get(am, methodType(Value.class, int.class , Value.class, Value.class)).
+                    invokeExact(0, Value.getInstance(10), Value.getInstance(10));
+            });
+            // Incorrect return type
+            hs.checkWMTEOrCCE(() -> { // reference class
+                Void r = (Void) hs.get(am, methodType(Void.class, VarHandleTestMethodTypeValue.class , Value.class, Value.class)).
+                    invokeExact(recv, Value.getInstance(10), Value.getInstance(10));
+            });
+            checkWMTE(() -> { // primitive class
+                boolean x = (boolean) hs.get(am, methodType(boolean.class, VarHandleTestMethodTypeValue.class , Value.class, Value.class)).
+                    invokeExact(recv, Value.getInstance(10), Value.getInstance(10));
+            });
+            // Incorrect arity
+            checkWMTE(() -> { // 0
+                Value x = (Value) hs.get(am, methodType(Value.class)).
+                    invokeExact();
+            });
+            checkWMTE(() -> { // >
+                Value x = (Value) hs.get(am, methodType(Value.class, VarHandleTestMethodTypeValue.class, Value.class, Value.class, Class.class)).
+                    invokeExact(recv, Value.getInstance(10), Value.getInstance(10), Void.class);
+            });
+        }
+
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.GET_AND_SET)) {
+            checkNPE(() -> { // null receiver
+                Value x = (Value) hs.get(am, methodType(Value.class, VarHandleTestMethodTypeValue.class, Value.class)).
+                    invokeExact((VarHandleTestMethodTypeValue) null, Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // receiver reference class
+                Value x = (Value) hs.get(am, methodType(Value.class, Class.class, Value.class)).
+                    invokeExact(Void.class, Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // value reference class
+                Value x = (Value) hs.get(am, methodType(Value.class, VarHandleTestMethodTypeValue.class, Class.class)).
+                    invokeExact(recv, Void.class);
+            });
+            checkWMTE(() -> { // reciever primitive class
+                Value x = (Value) hs.get(am, methodType(Value.class, int.class, Value.class)).
+                    invokeExact(0, Value.getInstance(10));
+            });
+            // Incorrect return type
+            hs.checkWMTEOrCCE(() -> { // reference class
+                Void r = (Void) hs.get(am, methodType(Void.class, VarHandleTestMethodTypeValue.class, Value.class)).
+                    invokeExact(recv, Value.getInstance(10));
+            });
+            checkWMTE(() -> { // primitive class
+                boolean x = (boolean) hs.get(am, methodType(boolean.class, VarHandleTestMethodTypeValue.class, Value.class)).
+                    invokeExact(recv, Value.getInstance(10));
+            });
+            // Incorrect arity
+            checkWMTE(() -> { // 0
+                Value x = (Value) hs.get(am, methodType(Value.class)).
+                    invokeExact();
+            });
+            checkWMTE(() -> { // >
+                Value x = (Value) hs.get(am, methodType(Value.class, VarHandleTestMethodTypeValue.class, Value.class)).
+                    invokeExact(recv, Value.getInstance(10), Void.class);
+            });
+        }
+
+
+    }
+
+
+    static void testStaticFieldWrongMethodType(VarHandle vh) throws Throwable {
+        // Get
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void x = (Void) vh.get();
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.get();
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.get(Void.class);
+        });
+
+
+        // Set
+        // Incorrect argument types
+        checkCCE(() -> { // value reference class
+            vh.set(Void.class);
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            vh.set();
+        });
+        checkWMTE(() -> { // >
+            vh.set(Value.getInstance(10), Void.class);
+        });
+
+
+        // GetVolatile
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void x = (Void) vh.getVolatile();
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.getVolatile();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.getVolatile(Void.class);
+        });
+
+
+        // SetVolatile
+        // Incorrect argument types
+        checkCCE(() -> { // value reference class
+            vh.setVolatile(Void.class);
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            vh.setVolatile();
+        });
+        checkWMTE(() -> { // >
+            vh.setVolatile(Value.getInstance(10), Void.class);
+        });
+
+
+        // GetOpaque
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void x = (Void) vh.getOpaque();
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.getOpaque();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.getOpaque(Void.class);
+        });
+
+
+        // SetOpaque
+        // Incorrect argument types
+        checkCCE(() -> { // value reference class
+            vh.setOpaque(Void.class);
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            vh.setOpaque();
+        });
+        checkWMTE(() -> { // >
+            vh.setOpaque(Value.getInstance(10), Void.class);
+        });
+
+
+        // GetAcquire
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void x = (Void) vh.getAcquire();
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.getAcquire();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.getAcquire(Void.class);
+        });
+
+
+        // SetRelease
+        // Incorrect argument types
+        checkCCE(() -> { // value reference class
+            vh.setRelease(Void.class);
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            vh.setRelease();
+        });
+        checkWMTE(() -> { // >
+            vh.setRelease(Value.getInstance(10), Void.class);
+        });
+
+
+        // CompareAndSet
+        // Incorrect argument types
+        checkCCE(() -> { // expected reference class
+            boolean r = vh.compareAndSet(Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            boolean r = vh.compareAndSet(Value.getInstance(10), Void.class);
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            boolean r = vh.compareAndSet();
+        });
+        checkWMTE(() -> { // >
+            boolean r = vh.compareAndSet(Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // WeakCompareAndSet
+        // Incorrect argument types
+        checkCCE(() -> { // expected reference class
+            boolean r = vh.weakCompareAndSetPlain(Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            boolean r = vh.weakCompareAndSetPlain(Value.getInstance(10), Void.class);
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            boolean r = vh.weakCompareAndSetPlain();
+        });
+        checkWMTE(() -> { // >
+            boolean r = vh.weakCompareAndSetPlain(Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // WeakCompareAndSetVolatile
+        // Incorrect argument types
+        checkCCE(() -> { // expected reference class
+            boolean r = vh.weakCompareAndSet(Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            boolean r = vh.weakCompareAndSet(Value.getInstance(10), Void.class);
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            boolean r = vh.weakCompareAndSet();
+        });
+        checkWMTE(() -> { // >
+            boolean r = vh.weakCompareAndSet(Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // WeakCompareAndSetAcquire
+        // Incorrect argument types
+        checkCCE(() -> { // expected reference class
+            boolean r = vh.weakCompareAndSetAcquire(Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            boolean r = vh.weakCompareAndSetAcquire(Value.getInstance(10), Void.class);
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            boolean r = vh.weakCompareAndSetAcquire();
+        });
+        checkWMTE(() -> { // >
+            boolean r = vh.weakCompareAndSetAcquire(Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // WeakCompareAndSetRelease
+        // Incorrect argument types
+        checkCCE(() -> { // expected reference class
+            boolean r = vh.weakCompareAndSetRelease(Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            boolean r = vh.weakCompareAndSetRelease(Value.getInstance(10), Void.class);
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            boolean r = vh.weakCompareAndSetRelease();
+        });
+        checkWMTE(() -> { // >
+            boolean r = vh.weakCompareAndSetRelease(Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // CompareAndExchange
+        // Incorrect argument types
+        checkCCE(() -> { // expected reference class
+            Value x = (Value) vh.compareAndExchange(Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            Value x = (Value) vh.compareAndExchange(Value.getInstance(10), Void.class);
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void r = (Void) vh.compareAndExchange(Value.getInstance(10), Value.getInstance(10));
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.compareAndExchange(Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.compareAndExchange();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.compareAndExchange(Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // CompareAndExchangeAcquire
+        // Incorrect argument types
+        checkCCE(() -> { // expected reference class
+            Value x = (Value) vh.compareAndExchangeAcquire(Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            Value x = (Value) vh.compareAndExchangeAcquire(Value.getInstance(10), Void.class);
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void r = (Void) vh.compareAndExchangeAcquire(Value.getInstance(10), Value.getInstance(10));
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.compareAndExchangeAcquire(Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.compareAndExchangeAcquire();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.compareAndExchangeAcquire(Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // CompareAndExchangeRelease
+        // Incorrect argument types
+        checkCCE(() -> { // expected reference class
+            Value x = (Value) vh.compareAndExchangeRelease(Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            Value x = (Value) vh.compareAndExchangeRelease(Value.getInstance(10), Void.class);
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void r = (Void) vh.compareAndExchangeRelease(Value.getInstance(10), Value.getInstance(10));
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.compareAndExchangeRelease(Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.compareAndExchangeRelease();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.compareAndExchangeRelease(Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // GetAndSet
+        // Incorrect argument types
+        checkCCE(() -> { // value reference class
+            Value x = (Value) vh.getAndSet(Void.class);
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void r = (Void) vh.getAndSet(Value.getInstance(10));
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.getAndSet(Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.getAndSet();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.getAndSet(Value.getInstance(10), Void.class);
+        });
+
+
+        // GetAndSetAcquire
+        // Incorrect argument types
+        checkCCE(() -> { // value reference class
+            Value x = (Value) vh.getAndSetAcquire(Void.class);
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void r = (Void) vh.getAndSetAcquire(Value.getInstance(10));
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.getAndSetAcquire(Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.getAndSetAcquire();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.getAndSetAcquire(Value.getInstance(10), Void.class);
+        });
+
+
+        // GetAndSetRelease
+        // Incorrect argument types
+        checkCCE(() -> { // value reference class
+            Value x = (Value) vh.getAndSetRelease(Void.class);
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void r = (Void) vh.getAndSetRelease(Value.getInstance(10));
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.getAndSetRelease(Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.getAndSetRelease();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.getAndSetRelease(Value.getInstance(10), Void.class);
+        });
+
+
+    }
+
+    static void testStaticFieldWrongMethodType(Handles hs) throws Throwable {
+        int i = 0;
+
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.GET)) {
+            // Incorrect return type
+            hs.checkWMTEOrCCE(() -> { // reference class
+                Void x = (Void) hs.get(am, methodType(Void.class)).
+                    invokeExact();
+            });
+            checkWMTE(() -> { // primitive class
+                boolean x = (boolean) hs.get(am, methodType(boolean.class)).
+                    invokeExact();
+            });
+            // Incorrect arity
+            checkWMTE(() -> { // >
+                Value x = (Value) hs.get(am, methodType(Class.class)).
+                    invokeExact(Void.class);
+            });
+        }
+
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.SET)) {
+            hs.checkWMTEOrCCE(() -> { // value reference class
+                hs.get(am, methodType(void.class, Class.class)).
+                    invokeExact(Void.class);
+            });
+            // Incorrect arity
+            checkWMTE(() -> { // 0
+                hs.get(am, methodType(void.class)).
+                    invokeExact();
+            });
+            checkWMTE(() -> { // >
+                hs.get(am, methodType(void.class, Value.class, Class.class)).
+                    invokeExact(Value.getInstance(10), Void.class);
+            });
+        }
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.COMPARE_AND_SET)) {
+            // Incorrect argument types
+            hs.checkWMTEOrCCE(() -> { // expected reference class
+                boolean r = (boolean) hs.get(am, methodType(boolean.class, Class.class, Value.class)).
+                    invokeExact(Void.class, Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // actual reference class
+                boolean r = (boolean) hs.get(am, methodType(boolean.class, Value.class, Class.class)).
+                    invokeExact(Value.getInstance(10), Void.class);
+            });
+            // Incorrect arity
+            checkWMTE(() -> { // 0
+                boolean r = (boolean) hs.get(am, methodType(boolean.class)).
+                    invokeExact();
+            });
+            checkWMTE(() -> { // >
+                boolean r = (boolean) hs.get(am, methodType(boolean.class, Value.class, Value.class, Class.class)).
+                    invokeExact(Value.getInstance(10), Value.getInstance(10), Void.class);
+            });
+        }
+
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.COMPARE_AND_EXCHANGE)) {
+            // Incorrect argument types
+            hs.checkWMTEOrCCE(() -> { // expected reference class
+                Value x = (Value) hs.get(am, methodType(Value.class, Class.class, Value.class)).
+                    invokeExact(Void.class, Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // actual reference class
+                Value x = (Value) hs.get(am, methodType(Value.class, Value.class, Class.class)).
+                    invokeExact(Value.getInstance(10), Void.class);
+            });
+            // Incorrect return type
+            hs.checkWMTEOrCCE(() -> { // reference class
+                Void r = (Void) hs.get(am, methodType(Void.class, Value.class, Value.class)).
+                    invokeExact(Value.getInstance(10), Value.getInstance(10));
+            });
+            checkWMTE(() -> { // primitive class
+                boolean x = (boolean) hs.get(am, methodType(boolean.class, Value.class, Value.class)).
+                    invokeExact(Value.getInstance(10), Value.getInstance(10));
+            });
+            // Incorrect arity
+            checkWMTE(() -> { // 0
+                Value x = (Value) hs.get(am, methodType(Value.class)).
+                    invokeExact();
+            });
+            checkWMTE(() -> { // >
+                Value x = (Value) hs.get(am, methodType(Value.class, Value.class, Value.class, Class.class)).
+                    invokeExact(Value.getInstance(10), Value.getInstance(10), Void.class);
+            });
+        }
+
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.GET_AND_SET)) {
+            // Incorrect argument types
+            hs.checkWMTEOrCCE(() -> { // value reference class
+                Value x = (Value) hs.get(am, methodType(Value.class, Class.class)).
+                    invokeExact(Void.class);
+            });
+            // Incorrect return type
+            hs.checkWMTEOrCCE(() -> { // reference class
+                Void r = (Void) hs.get(am, methodType(Void.class, Value.class)).
+                    invokeExact(Value.getInstance(10));
+            });
+            checkWMTE(() -> { // primitive class
+                boolean x = (boolean) hs.get(am, methodType(boolean.class, Value.class)).
+                    invokeExact(Value.getInstance(10));
+            });
+            // Incorrect arity
+            checkWMTE(() -> { // 0
+                Value x = (Value) hs.get(am, methodType(Value.class)).
+                    invokeExact();
+            });
+            checkWMTE(() -> { // >
+                Value x = (Value) hs.get(am, methodType(Value.class, Value.class, Class.class)).
+                    invokeExact(Value.getInstance(10), Void.class);
+            });
+        }
+
+
+    }
+
+
+    static void testArrayWrongMethodType(VarHandle vh) throws Throwable {
+        Value[] array = new Value[10];
+        Arrays.fill(array, Value.getInstance(10));
+
+        // Get
+        // Incorrect argument types
+        checkNPE(() -> { // null array
+            Value x = (Value) vh.get(null, 0);
+        });
+        checkCCE(() -> { // array reference class
+            Value x = (Value) vh.get(Void.class, 0);
+        });
+        checkWMTE(() -> { // array primitive class
+            Value x = (Value) vh.get(0, 0);
+        });
+        checkWMTE(() -> { // index reference class
+            Value x = (Value) vh.get(array, Void.class);
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void x = (Void) vh.get(array, 0);
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.get(array, 0);
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.get();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.get(array, 0, Void.class);
+        });
+
+
+        // Set
+        // Incorrect argument types
+        checkNPE(() -> { // null array
+            vh.set(null, 0, Value.getInstance(10));
+        });
+        checkCCE(() -> { // array reference class
+            vh.set(Void.class, 0, Value.getInstance(10));
+        });
+        checkCCE(() -> { // value reference class
+            vh.set(array, 0, Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            vh.set(0, 0, Value.getInstance(10));
+        });
+        checkWMTE(() -> { // index reference class
+            vh.set(array, Void.class, Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            vh.set();
+        });
+        checkWMTE(() -> { // >
+            vh.set(array, 0, Value.getInstance(10), Void.class);
+        });
+
+
+        // GetVolatile
+        // Incorrect argument types
+        checkNPE(() -> { // null array
+            Value x = (Value) vh.getVolatile(null, 0);
+        });
+        checkCCE(() -> { // array reference class
+            Value x = (Value) vh.getVolatile(Void.class, 0);
+        });
+        checkWMTE(() -> { // array primitive class
+            Value x = (Value) vh.getVolatile(0, 0);
+        });
+        checkWMTE(() -> { // index reference class
+            Value x = (Value) vh.getVolatile(array, Void.class);
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void x = (Void) vh.getVolatile(array, 0);
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.getVolatile(array, 0);
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.getVolatile();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.getVolatile(array, 0, Void.class);
+        });
+
+
+        // SetVolatile
+        // Incorrect argument types
+        checkNPE(() -> { // null array
+            vh.setVolatile(null, 0, Value.getInstance(10));
+        });
+        checkCCE(() -> { // array reference class
+            vh.setVolatile(Void.class, 0, Value.getInstance(10));
+        });
+        checkCCE(() -> { // value reference class
+            vh.setVolatile(array, 0, Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            vh.setVolatile(0, 0, Value.getInstance(10));
+        });
+        checkWMTE(() -> { // index reference class
+            vh.setVolatile(array, Void.class, Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            vh.setVolatile();
+        });
+        checkWMTE(() -> { // >
+            vh.setVolatile(array, 0, Value.getInstance(10), Void.class);
+        });
+
+
+        // GetOpaque
+        // Incorrect argument types
+        checkNPE(() -> { // null array
+            Value x = (Value) vh.getOpaque(null, 0);
+        });
+        checkCCE(() -> { // array reference class
+            Value x = (Value) vh.getOpaque(Void.class, 0);
+        });
+        checkWMTE(() -> { // array primitive class
+            Value x = (Value) vh.getOpaque(0, 0);
+        });
+        checkWMTE(() -> { // index reference class
+            Value x = (Value) vh.getOpaque(array, Void.class);
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void x = (Void) vh.getOpaque(array, 0);
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.getOpaque(array, 0);
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.getOpaque();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.getOpaque(array, 0, Void.class);
+        });
+
+
+        // SetOpaque
+        // Incorrect argument types
+        checkNPE(() -> { // null array
+            vh.setOpaque(null, 0, Value.getInstance(10));
+        });
+        checkCCE(() -> { // array reference class
+            vh.setOpaque(Void.class, 0, Value.getInstance(10));
+        });
+        checkCCE(() -> { // value reference class
+            vh.setOpaque(array, 0, Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            vh.setOpaque(0, 0, Value.getInstance(10));
+        });
+        checkWMTE(() -> { // index reference class
+            vh.setOpaque(array, Void.class, Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            vh.setOpaque();
+        });
+        checkWMTE(() -> { // >
+            vh.setOpaque(array, 0, Value.getInstance(10), Void.class);
+        });
+
+
+        // GetAcquire
+        // Incorrect argument types
+        checkNPE(() -> { // null array
+            Value x = (Value) vh.getAcquire(null, 0);
+        });
+        checkCCE(() -> { // array reference class
+            Value x = (Value) vh.getAcquire(Void.class, 0);
+        });
+        checkWMTE(() -> { // array primitive class
+            Value x = (Value) vh.getAcquire(0, 0);
+        });
+        checkWMTE(() -> { // index reference class
+            Value x = (Value) vh.getAcquire(array, Void.class);
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void x = (Void) vh.getAcquire(array, 0);
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.getAcquire(array, 0);
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.getAcquire();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.getAcquire(array, 0, Void.class);
+        });
+
+
+        // SetRelease
+        // Incorrect argument types
+        checkNPE(() -> { // null array
+            vh.setRelease(null, 0, Value.getInstance(10));
+        });
+        checkCCE(() -> { // array reference class
+            vh.setRelease(Void.class, 0, Value.getInstance(10));
+        });
+        checkCCE(() -> { // value reference class
+            vh.setRelease(array, 0, Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            vh.setRelease(0, 0, Value.getInstance(10));
+        });
+        checkWMTE(() -> { // index reference class
+            vh.setRelease(array, Void.class, Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            vh.setRelease();
+        });
+        checkWMTE(() -> { // >
+            vh.setRelease(array, 0, Value.getInstance(10), Void.class);
+        });
+
+
+        // CompareAndSet
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            boolean r = vh.compareAndSet(null, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            boolean r = vh.compareAndSet(Void.class, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // expected reference class
+            boolean r = vh.compareAndSet(array, 0, Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            boolean r = vh.compareAndSet(array, 0, Value.getInstance(10), Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            boolean r = vh.compareAndSet(0, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkWMTE(() -> { // index reference class
+            boolean r = vh.compareAndSet(array, Void.class, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            boolean r = vh.compareAndSet();
+        });
+        checkWMTE(() -> { // >
+            boolean r = vh.compareAndSet(array, 0, Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // WeakCompareAndSet
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            boolean r = vh.weakCompareAndSetPlain(null, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            boolean r = vh.weakCompareAndSetPlain(Void.class, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // expected reference class
+            boolean r = vh.weakCompareAndSetPlain(array, 0, Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            boolean r = vh.weakCompareAndSetPlain(array, 0, Value.getInstance(10), Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            boolean r = vh.weakCompareAndSetPlain(0, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkWMTE(() -> { // index reference class
+            boolean r = vh.weakCompareAndSetPlain(array, Void.class, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            boolean r = vh.weakCompareAndSetPlain();
+        });
+        checkWMTE(() -> { // >
+            boolean r = vh.weakCompareAndSetPlain(array, 0, Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // WeakCompareAndSetVolatile
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            boolean r = vh.weakCompareAndSet(null, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            boolean r = vh.weakCompareAndSet(Void.class, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // expected reference class
+            boolean r = vh.weakCompareAndSet(array, 0, Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            boolean r = vh.weakCompareAndSet(array, 0, Value.getInstance(10), Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            boolean r = vh.weakCompareAndSet(0, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkWMTE(() -> { // index reference class
+            boolean r = vh.weakCompareAndSet(array, Void.class, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            boolean r = vh.weakCompareAndSet();
+        });
+        checkWMTE(() -> { // >
+            boolean r = vh.weakCompareAndSet(array, 0, Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // WeakCompareAndSetAcquire
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            boolean r = vh.weakCompareAndSetAcquire(null, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            boolean r = vh.weakCompareAndSetAcquire(Void.class, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // expected reference class
+            boolean r = vh.weakCompareAndSetAcquire(array, 0, Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            boolean r = vh.weakCompareAndSetAcquire(array, 0, Value.getInstance(10), Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            boolean r = vh.weakCompareAndSetAcquire(0, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkWMTE(() -> { // index reference class
+            boolean r = vh.weakCompareAndSetAcquire(array, Void.class, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            boolean r = vh.weakCompareAndSetAcquire();
+        });
+        checkWMTE(() -> { // >
+            boolean r = vh.weakCompareAndSetAcquire(array, 0, Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // WeakCompareAndSetRelease
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            boolean r = vh.weakCompareAndSetRelease(null, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // receiver reference class
+            boolean r = vh.weakCompareAndSetRelease(Void.class, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // expected reference class
+            boolean r = vh.weakCompareAndSetRelease(array, 0, Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            boolean r = vh.weakCompareAndSetRelease(array, 0, Value.getInstance(10), Void.class);
+        });
+        checkWMTE(() -> { // receiver primitive class
+            boolean r = vh.weakCompareAndSetRelease(0, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkWMTE(() -> { // index reference class
+            boolean r = vh.weakCompareAndSetRelease(array, Void.class, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            boolean r = vh.weakCompareAndSetRelease();
+        });
+        checkWMTE(() -> { // >
+            boolean r = vh.weakCompareAndSetRelease(array, 0, Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // CompareAndExchange
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            Value x = (Value) vh.compareAndExchange(null, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // array reference class
+            Value x = (Value) vh.compareAndExchange(Void.class, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // expected reference class
+            Value x = (Value) vh.compareAndExchange(array, 0, Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            Value x = (Value) vh.compareAndExchange(array, 0, Value.getInstance(10), Void.class);
+        });
+        checkWMTE(() -> { // array primitive class
+            Value x = (Value) vh.compareAndExchange(0, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkWMTE(() -> { // index reference class
+            Value x = (Value) vh.compareAndExchange(array, Void.class, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void r = (Void) vh.compareAndExchange(array, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.compareAndExchange(array, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.compareAndExchange();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.compareAndExchange(array, 0, Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // CompareAndExchangeAcquire
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            Value x = (Value) vh.compareAndExchangeAcquire(null, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // array reference class
+            Value x = (Value) vh.compareAndExchangeAcquire(Void.class, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // expected reference class
+            Value x = (Value) vh.compareAndExchangeAcquire(array, 0, Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            Value x = (Value) vh.compareAndExchangeAcquire(array, 0, Value.getInstance(10), Void.class);
+        });
+        checkWMTE(() -> { // array primitive class
+            Value x = (Value) vh.compareAndExchangeAcquire(0, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkWMTE(() -> { // index reference class
+            Value x = (Value) vh.compareAndExchangeAcquire(array, Void.class, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void r = (Void) vh.compareAndExchangeAcquire(array, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.compareAndExchangeAcquire(array, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.compareAndExchangeAcquire();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.compareAndExchangeAcquire(array, 0, Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // CompareAndExchangeRelease
+        // Incorrect argument types
+        checkNPE(() -> { // null receiver
+            Value x = (Value) vh.compareAndExchangeRelease(null, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // array reference class
+            Value x = (Value) vh.compareAndExchangeRelease(Void.class, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkCCE(() -> { // expected reference class
+            Value x = (Value) vh.compareAndExchangeRelease(array, 0, Void.class, Value.getInstance(10));
+        });
+        checkCCE(() -> { // actual reference class
+            Value x = (Value) vh.compareAndExchangeRelease(array, 0, Value.getInstance(10), Void.class);
+        });
+        checkWMTE(() -> { // array primitive class
+            Value x = (Value) vh.compareAndExchangeRelease(0, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkWMTE(() -> { // index reference class
+            Value x = (Value) vh.compareAndExchangeRelease(array, Void.class, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void r = (Void) vh.compareAndExchangeRelease(array, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.compareAndExchangeRelease(array, 0, Value.getInstance(10), Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.compareAndExchangeRelease();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.compareAndExchangeRelease(array, 0, Value.getInstance(10), Value.getInstance(10), Void.class);
+        });
+
+
+        // GetAndSet
+        // Incorrect argument types
+        checkNPE(() -> { // null array
+            Value x = (Value) vh.getAndSet(null, 0, Value.getInstance(10));
+        });
+        checkCCE(() -> { // array reference class
+            Value x = (Value) vh.getAndSet(Void.class, 0, Value.getInstance(10));
+        });
+        checkCCE(() -> { // value reference class
+            Value x = (Value) vh.getAndSet(array, 0, Void.class);
+        });
+        checkWMTE(() -> { // reciarrayever primitive class
+            Value x = (Value) vh.getAndSet(0, 0, Value.getInstance(10));
+        });
+        checkWMTE(() -> { // index reference class
+            Value x = (Value) vh.getAndSet(array, Void.class, Value.getInstance(10));
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void r = (Void) vh.getAndSet(array, 0, Value.getInstance(10));
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.getAndSet(array, 0, Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.getAndSet();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.getAndSet(array, 0, Value.getInstance(10), Void.class);
+        });
+
+
+        // GetAndSetAcquire
+        // Incorrect argument types
+        checkNPE(() -> { // null array
+            Value x = (Value) vh.getAndSetAcquire(null, 0, Value.getInstance(10));
+        });
+        checkCCE(() -> { // array reference class
+            Value x = (Value) vh.getAndSetAcquire(Void.class, 0, Value.getInstance(10));
+        });
+        checkCCE(() -> { // value reference class
+            Value x = (Value) vh.getAndSetAcquire(array, 0, Void.class);
+        });
+        checkWMTE(() -> { // reciarrayever primitive class
+            Value x = (Value) vh.getAndSetAcquire(0, 0, Value.getInstance(10));
+        });
+        checkWMTE(() -> { // index reference class
+            Value x = (Value) vh.getAndSetAcquire(array, Void.class, Value.getInstance(10));
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void r = (Void) vh.getAndSetAcquire(array, 0, Value.getInstance(10));
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.getAndSetAcquire(array, 0, Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.getAndSetAcquire();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.getAndSetAcquire(array, 0, Value.getInstance(10), Void.class);
+        });
+
+
+        // GetAndSetRelease
+        // Incorrect argument types
+        checkNPE(() -> { // null array
+            Value x = (Value) vh.getAndSetRelease(null, 0, Value.getInstance(10));
+        });
+        checkCCE(() -> { // array reference class
+            Value x = (Value) vh.getAndSetRelease(Void.class, 0, Value.getInstance(10));
+        });
+        checkCCE(() -> { // value reference class
+            Value x = (Value) vh.getAndSetRelease(array, 0, Void.class);
+        });
+        checkWMTE(() -> { // reciarrayever primitive class
+            Value x = (Value) vh.getAndSetRelease(0, 0, Value.getInstance(10));
+        });
+        checkWMTE(() -> { // index reference class
+            Value x = (Value) vh.getAndSetRelease(array, Void.class, Value.getInstance(10));
+        });
+        // Incorrect return type
+        checkCCE(() -> { // reference class
+            Void r = (Void) vh.getAndSetRelease(array, 0, Value.getInstance(10));
+        });
+        checkWMTE(() -> { // primitive class
+            boolean x = (boolean) vh.getAndSetRelease(array, 0, Value.getInstance(10));
+        });
+        // Incorrect arity
+        checkWMTE(() -> { // 0
+            Value x = (Value) vh.getAndSetRelease();
+        });
+        checkWMTE(() -> { // >
+            Value x = (Value) vh.getAndSetRelease(array, 0, Value.getInstance(10), Void.class);
+        });
+
+
+    }
+
+    static void testArrayWrongMethodType(Handles hs) throws Throwable {
+        Value[] array = new Value[10];
+        Arrays.fill(array, Value.getInstance(10));
+
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.GET)) {
+            // Incorrect argument types
+            checkNPE(() -> { // null array
+                Value x = (Value) hs.get(am, methodType(Value.class, Value[].class, int.class)).
+                    invokeExact((Value[]) null, 0);
+            });
+            hs.checkWMTEOrCCE(() -> { // array reference class
+                Value x = (Value) hs.get(am, methodType(Value.class, Class.class, int.class)).
+                    invokeExact(Void.class, 0);
+            });
+            checkWMTE(() -> { // array primitive class
+                Value x = (Value) hs.get(am, methodType(Value.class, int.class, int.class)).
+                    invokeExact(0, 0);
+            });
+            checkWMTE(() -> { // index reference class
+                Value x = (Value) hs.get(am, methodType(Value.class, Value[].class, Class.class)).
+                    invokeExact(array, Void.class);
+            });
+            // Incorrect return type
+            hs.checkWMTEOrCCE(() -> { // reference class
+                Void x = (Void) hs.get(am, methodType(Void.class, Value[].class, int.class)).
+                    invokeExact(array, 0);
+            });
+            checkWMTE(() -> { // primitive class
+                boolean x = (boolean) hs.get(am, methodType(boolean.class, Value[].class, int.class)).
+                    invokeExact(array, 0);
+            });
+            // Incorrect arity
+            checkWMTE(() -> { // 0
+                Value x = (Value) hs.get(am, methodType(Value.class)).
+                    invokeExact();
+            });
+            checkWMTE(() -> { // >
+                Value x = (Value) hs.get(am, methodType(Value.class, Value[].class, int.class, Class.class)).
+                    invokeExact(array, 0, Void.class);
+            });
+        }
+
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.SET)) {
+            // Incorrect argument types
+            checkNPE(() -> { // null array
+                hs.get(am, methodType(void.class, Value[].class, int.class, Value.class)).
+                    invokeExact((Value[]) null, 0, Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // array reference class
+                hs.get(am, methodType(void.class, Class.class, int.class, Value.class)).
+                    invokeExact(Void.class, 0, Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // value reference class
+                hs.get(am, methodType(void.class, Value[].class, int.class, Class.class)).
+                    invokeExact(array, 0, Void.class);
+            });
+            checkWMTE(() -> { // receiver primitive class
+                hs.get(am, methodType(void.class, int.class, int.class, Value.class)).
+                    invokeExact(0, 0, Value.getInstance(10));
+            });
+            checkWMTE(() -> { // index reference class
+                hs.get(am, methodType(void.class, Value[].class, Class.class, Value.class)).
+                    invokeExact(array, Void.class, Value.getInstance(10));
+            });
+            // Incorrect arity
+            checkWMTE(() -> { // 0
+                hs.get(am, methodType(void.class)).
+                    invokeExact();
+            });
+            checkWMTE(() -> { // >
+                hs.get(am, methodType(void.class, Value[].class, int.class, Class.class)).
+                    invokeExact(array, 0, Value.getInstance(10), Void.class);
+            });
+        }
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.COMPARE_AND_SET)) {
+            // Incorrect argument types
+            checkNPE(() -> { // null receiver
+                boolean r = (boolean) hs.get(am, methodType(boolean.class, Value[].class, int.class, Value.class, Value.class)).
+                    invokeExact((Value[]) null, 0, Value.getInstance(10), Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // receiver reference class
+                boolean r = (boolean) hs.get(am, methodType(boolean.class, Class.class, int.class, Value.class, Value.class)).
+                    invokeExact(Void.class, 0, Value.getInstance(10), Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // expected reference class
+                boolean r = (boolean) hs.get(am, methodType(boolean.class, Value[].class, int.class, Class.class, Value.class)).
+                    invokeExact(array, 0, Void.class, Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // actual reference class
+                boolean r = (boolean) hs.get(am, methodType(boolean.class, Value[].class, int.class, Value.class, Class.class)).
+                    invokeExact(array, 0, Value.getInstance(10), Void.class);
+            });
+            checkWMTE(() -> { // receiver primitive class
+                boolean r = (boolean) hs.get(am, methodType(boolean.class, int.class, int.class, Value.class, Value.class)).
+                    invokeExact(0, 0, Value.getInstance(10), Value.getInstance(10));
+            });
+            checkWMTE(() -> { // index reference class
+                boolean r = (boolean) hs.get(am, methodType(boolean.class, Value[].class, Class.class, Value.class, Value.class)).
+                    invokeExact(array, Void.class, Value.getInstance(10), Value.getInstance(10));
+            });
+            // Incorrect arity
+            checkWMTE(() -> { // 0
+                boolean r = (boolean) hs.get(am, methodType(boolean.class)).
+                    invokeExact();
+            });
+            checkWMTE(() -> { // >
+                boolean r = (boolean) hs.get(am, methodType(boolean.class, Value[].class, int.class, Value.class, Value.class, Class.class)).
+                    invokeExact(array, 0, Value.getInstance(10), Value.getInstance(10), Void.class);
+            });
+        }
+
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.COMPARE_AND_EXCHANGE)) {
+            // Incorrect argument types
+            checkNPE(() -> { // null receiver
+                Value x = (Value) hs.get(am, methodType(Value.class, Value[].class, int.class, Value.class, Value.class)).
+                    invokeExact((Value[]) null, 0, Value.getInstance(10), Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // array reference class
+                Value x = (Value) hs.get(am, methodType(Value.class, Class.class, int.class, Value.class, Value.class)).
+                    invokeExact(Void.class, 0, Value.getInstance(10), Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // expected reference class
+                Value x = (Value) hs.get(am, methodType(Value.class, Value[].class, int.class, Class.class, Value.class)).
+                    invokeExact(array, 0, Void.class, Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // actual reference class
+                Value x = (Value) hs.get(am, methodType(Value.class, Value[].class, int.class, Value.class, Class.class)).
+                    invokeExact(array, 0, Value.getInstance(10), Void.class);
+            });
+            checkWMTE(() -> { // array primitive class
+                Value x = (Value) hs.get(am, methodType(Value.class, int.class, int.class, Value.class, Value.class)).
+                    invokeExact(0, 0, Value.getInstance(10), Value.getInstance(10));
+            });
+            checkWMTE(() -> { // index reference class
+                Value x = (Value) hs.get(am, methodType(Value.class, Value[].class, Class.class, Value.class, Value.class)).
+                    invokeExact(array, Void.class, Value.getInstance(10), Value.getInstance(10));
+            });
+            // Incorrect return type
+            hs.checkWMTEOrCCE(() -> { // reference class
+                Void r = (Void) hs.get(am, methodType(Void.class, Value[].class, int.class, Value.class, Value.class)).
+                    invokeExact(array, 0, Value.getInstance(10), Value.getInstance(10));
+            });
+            checkWMTE(() -> { // primitive class
+                boolean x = (boolean) hs.get(am, methodType(boolean.class, Value[].class, int.class, Value.class, Value.class)).
+                    invokeExact(array, 0, Value.getInstance(10), Value.getInstance(10));
+            });
+            // Incorrect arity
+            checkWMTE(() -> { // 0
+                Value x = (Value) hs.get(am, methodType(Value.class)).
+                    invokeExact();
+            });
+            checkWMTE(() -> { // >
+                Value x = (Value) hs.get(am, methodType(Value.class, Value[].class, int.class, Value.class, Value.class, Class.class)).
+                    invokeExact(array, 0, Value.getInstance(10), Value.getInstance(10), Void.class);
+            });
+        }
+
+        for (TestAccessMode am : testAccessModesOfType(TestAccessType.GET_AND_SET)) {
+            // Incorrect argument types
+            checkNPE(() -> { // null array
+                Value x = (Value) hs.get(am, methodType(Value.class, Value[].class, int.class, Value.class)).
+                    invokeExact((Value[]) null, 0, Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // array reference class
+                Value x = (Value) hs.get(am, methodType(Value.class, Class.class, int.class, Value.class)).
+                    invokeExact(Void.class, 0, Value.getInstance(10));
+            });
+            hs.checkWMTEOrCCE(() -> { // value reference class
+                Value x = (Value) hs.get(am, methodType(Value.class, Value[].class, int.class, Class.class)).
+                    invokeExact(array, 0, Void.class);
+            });
+            checkWMTE(() -> { // array primitive class
+                Value x = (Value) hs.get(am, methodType(Value.class, int.class, int.class, Value.class)).
+                    invokeExact(0, 0, Value.getInstance(10));
+            });
+            checkWMTE(() -> { // index reference class
+                Value x = (Value) hs.get(am, methodType(Value.class, Value[].class, Class.class, Value.class)).
+                    invokeExact(array, Void.class, Value.getInstance(10));
+            });
+            // Incorrect return type
+            hs.checkWMTEOrCCE(() -> { // reference class
+                Void r = (Void) hs.get(am, methodType(Void.class, Value[].class, int.class, Value.class)).
+                    invokeExact(array, 0, Value.getInstance(10));
+            });
+            checkWMTE(() -> { // primitive class
+                boolean x = (boolean) hs.get(am, methodType(boolean.class, Value[].class, int.class, Value.class)).
+                    invokeExact(array, 0, Value.getInstance(10));
+            });
+            // Incorrect arity
+            checkWMTE(() -> { // 0
+                Value x = (Value) hs.get(am, methodType(Value.class)).
+                    invokeExact();
+            });
+            checkWMTE(() -> { // >
+                Value x = (Value) hs.get(am, methodType(Value.class, Value[].class, int.class, Value.class, Class.class)).
+                    invokeExact(array, 0, Value.getInstance(10), Void.class);
+            });
+        }
+
+
+    }
+}
diff a/test/jdk/java/lang/invoke/VarHandles/X-VarHandleTestAccess.java.template b/test/jdk/java/lang/invoke/VarHandles/X-VarHandleTestAccess.java.template
--- a/test/jdk/java/lang/invoke/VarHandles/X-VarHandleTestAccess.java.template
+++ b/test/jdk/java/lang/invoke/VarHandles/X-VarHandleTestAccess.java.template
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,12 +19,18 @@
  * 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.
  */
 
+#warn
+
 /*
  * @test
+#if[Value]
+ * @enablePreview
+ * @modules java.base/jdk.internal.vm.annotation
+#end[Value]
  * @run testng/othervm -Diters=10   -Xint                                                   VarHandleTestAccess$Type$
  *
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
  *          to hit compilation thresholds
  *
@@ -70,13 +76,13 @@
 
     VarHandle vhStaticFinalField;
 
     VarHandle vhArray;
 
-#if[String]
+#if[Object]
     VarHandle vhArrayObject;
-#end[String]
+#end[Object]
 
     VarHandle[] allocate(boolean same) {
         List<VarHandle> vhs = new ArrayList<>();
 
         String postfix = same ? "" : "2";
@@ -128,13 +134,13 @@
 
         vhStaticField = MethodHandles.lookup().findStaticVarHandle(
             VarHandleTestAccess$Type$.class, "static_v", $type$.class);
 
         vhArray = MethodHandles.arrayElementVarHandle($type$[].class);
-#if[String]
+#if[Object]
         vhArrayObject = MethodHandles.arrayElementVarHandle(Object[].class);
-#end[String]
+#end[Object]
     }
 
 
     @DataProvider
     public Object[][] varHandlesProvider() throws Exception {
@@ -312,25 +318,25 @@
                                               vhStaticField, VarHandleTestAccess$Type$::testStaticFieldUnsupported,
                                               false));
 
         cases.add(new VarHandleAccessTestCase("Array",
                                               vhArray, VarHandleTestAccess$Type$::testArray));
-#if[String]
+#if[Object]
         cases.add(new VarHandleAccessTestCase("Array Object[]",
                                               vhArrayObject, VarHandleTestAccess$Type$::testArray));
-#end[String]
+#end[Object]
         cases.add(new VarHandleAccessTestCase("Array unsupported",
                                               vhArray, VarHandleTestAccess$Type$::testArrayUnsupported,
                                               false));
         cases.add(new VarHandleAccessTestCase("Array index out of bounds",
                                               vhArray, VarHandleTestAccess$Type$::testArrayIndexOutOfBounds,
                                               false));
-#if[String]
+#if[Object]
         cases.add(new VarHandleAccessTestCase("Array store exception",
                                               vhArrayObject, VarHandleTestAccess$Type$::testArrayStoreException,
                                               false));
-#end[String]
+#end[Object]
         // Work around issue with jtreg summary reporting which truncates
         // the String result of Object.toString to 30 characters, hence
         // the first dummy argument
         return cases.stream().map(tc -> new Object[]{tc.toString(), tc}).toArray(Object[][]::new);
     }
@@ -2001,11 +2007,11 @@
             });
 #end[Bitwise]
         }
     }
 
-#if[String]
+#if[Object]
     static void testArrayStoreException(VarHandle vh) throws Throwable {
         Object[] array = new $type$[10];
         Arrays.fill(array, $value1$);
         Object value = new Object();
 
@@ -2082,8 +2088,8 @@
         // GetAndSetRelease
         checkASE(() -> { // receiver reference class
             $type$ x = ($type$) vh.getAndSetRelease(array, 0, value);
         });
     }
-#end[String]
+#end[Object]
 }
 
diff a/test/jdk/java/lang/invoke/VarHandles/X-VarHandleTestMethodHandleAccess.java.template b/test/jdk/java/lang/invoke/VarHandles/X-VarHandleTestMethodHandleAccess.java.template
--- a/test/jdk/java/lang/invoke/VarHandles/X-VarHandleTestMethodHandleAccess.java.template
+++ b/test/jdk/java/lang/invoke/VarHandles/X-VarHandleTestMethodHandleAccess.java.template
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,12 +19,18 @@
  * 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.
  */
 
+#warn
+
 /*
  * @test
+#if[Value]
+ * @enablePreview
+ * @modules java.base/jdk.internal.vm.annotation
+#end[Value]
  * @comment Set CompileThresholdScaling to 0.1 so that the warmup loop sets to 2000 iterations
  *          to hit compilation thresholds
  * @run testng/othervm -Diters=2000 -XX:CompileThresholdScaling=0.1 VarHandleTestMethodHandleAccess$Type$
  */
 
diff a/test/jdk/java/lang/invoke/VarHandles/X-VarHandleTestMethodType.java.template b/test/jdk/java/lang/invoke/VarHandles/X-VarHandleTestMethodType.java.template
--- a/test/jdk/java/lang/invoke/VarHandles/X-VarHandleTestMethodType.java.template
+++ b/test/jdk/java/lang/invoke/VarHandles/X-VarHandleTestMethodType.java.template
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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.
@@ -19,13 +19,19 @@
  * 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.
  */
 
+#warn
+
 /*
  * @test
  * @bug 8156486
+#if[Value]
+ * @enablePreview
+ * @modules java.base/jdk.internal.vm.annotation
+#end[Value]
  * @run testng/othervm VarHandleTestMethodType$Type$
  * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodType$Type$
  * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false VarHandleTestMethodType$Type$
  * @run testng/othervm -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestMethodType$Type$
  */
@@ -136,11 +142,11 @@
         });
         checkWMTE(() -> { // receiver primitive class
             $type$ x = ($type$) vh.get(0);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void x = (Void) vh.get(recv);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.get(recv);
         });
@@ -159,11 +165,11 @@
             vh.set(null, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             vh.set(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             vh.set(recv, Void.class);
         });
         checkWMTE(() -> { // receiver primitive class
             vh.set(0, $value1$);
         });
@@ -186,11 +192,11 @@
         });
         checkWMTE(() -> { // receiver primitive class
             $type$ x = ($type$) vh.getVolatile(0);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void x = (Void) vh.getVolatile(recv);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getVolatile(recv);
         });
@@ -209,11 +215,11 @@
             vh.setVolatile(null, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             vh.setVolatile(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             vh.setVolatile(recv, Void.class);
         });
         checkWMTE(() -> { // receiver primitive class
             vh.setVolatile(0, $value1$);
         });
@@ -236,11 +242,11 @@
         });
         checkWMTE(() -> { // receiver primitive class
             $type$ x = ($type$) vh.getOpaque(0);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void x = (Void) vh.getOpaque(recv);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getOpaque(recv);
         });
@@ -259,11 +265,11 @@
             vh.setOpaque(null, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             vh.setOpaque(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             vh.setOpaque(recv, Void.class);
         });
         checkWMTE(() -> { // receiver primitive class
             vh.setOpaque(0, $value1$);
         });
@@ -286,11 +292,11 @@
         });
         checkWMTE(() -> { // receiver primitive class
             $type$ x = ($type$) vh.getAcquire(0);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void x = (Void) vh.getAcquire(recv);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAcquire(recv);
         });
@@ -309,11 +315,11 @@
             vh.setRelease(null, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             vh.setRelease(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             vh.setRelease(recv, Void.class);
         });
         checkWMTE(() -> { // receiver primitive class
             vh.setRelease(0, $value1$);
         });
@@ -333,14 +339,14 @@
             boolean r = vh.compareAndSet(null, $value1$, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             boolean r = vh.compareAndSet(Void.class, $value1$, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             boolean r = vh.compareAndSet(recv, Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             boolean r = vh.compareAndSet(recv, $value1$, Void.class);
         });
         checkWMTE(() -> { // receiver primitive class
             boolean r = vh.compareAndSet(0, $value1$, $value1$);
         });
@@ -359,14 +365,14 @@
             boolean r = vh.weakCompareAndSetPlain(null, $value1$, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             boolean r = vh.weakCompareAndSetPlain(Void.class, $value1$, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             boolean r = vh.weakCompareAndSetPlain(recv, Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             boolean r = vh.weakCompareAndSetPlain(recv, $value1$, Void.class);
         });
         checkWMTE(() -> { // receiver primitive class
             boolean r = vh.weakCompareAndSetPlain(0, $value1$, $value1$);
         });
@@ -385,14 +391,14 @@
             boolean r = vh.weakCompareAndSet(null, $value1$, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             boolean r = vh.weakCompareAndSet(Void.class, $value1$, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             boolean r = vh.weakCompareAndSet(recv, Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             boolean r = vh.weakCompareAndSet(recv, $value1$, Void.class);
         });
         checkWMTE(() -> { // receiver primitive class
             boolean r = vh.weakCompareAndSet(0, $value1$, $value1$);
         });
@@ -411,14 +417,14 @@
             boolean r = vh.weakCompareAndSetAcquire(null, $value1$, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             boolean r = vh.weakCompareAndSetAcquire(Void.class, $value1$, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             boolean r = vh.weakCompareAndSetAcquire(recv, Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             boolean r = vh.weakCompareAndSetAcquire(recv, $value1$, Void.class);
         });
         checkWMTE(() -> { // receiver primitive class
             boolean r = vh.weakCompareAndSetAcquire(0, $value1$, $value1$);
         });
@@ -437,14 +443,14 @@
             boolean r = vh.weakCompareAndSetRelease(null, $value1$, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             boolean r = vh.weakCompareAndSetRelease(Void.class, $value1$, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             boolean r = vh.weakCompareAndSetRelease(recv, Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             boolean r = vh.weakCompareAndSetRelease(recv, $value1$, Void.class);
         });
         checkWMTE(() -> { // receiver primitive class
             boolean r = vh.weakCompareAndSetRelease(0, $value1$, $value1$);
         });
@@ -463,21 +469,21 @@
             $type$ x = ($type$) vh.compareAndExchange(null, $value1$, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             $type$ x = ($type$) vh.compareAndExchange(Void.class, $value1$, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             $type$ x = ($type$) vh.compareAndExchange(recv, Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             $type$ x = ($type$) vh.compareAndExchange(recv, $value1$, Void.class);
         });
         checkWMTE(() -> { // reciever primitive class
             $type$ x = ($type$) vh.compareAndExchange(0, $value1$, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.compareAndExchange(recv, $value1$, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.compareAndExchange(recv, $value1$, $value1$);
         });
@@ -496,21 +502,21 @@
             $type$ x = ($type$) vh.compareAndExchangeAcquire(null, $value1$, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             $type$ x = ($type$) vh.compareAndExchangeAcquire(Void.class, $value1$, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             $type$ x = ($type$) vh.compareAndExchangeAcquire(recv, Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             $type$ x = ($type$) vh.compareAndExchangeAcquire(recv, $value1$, Void.class);
         });
         checkWMTE(() -> { // reciever primitive class
             $type$ x = ($type$) vh.compareAndExchangeAcquire(0, $value1$, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.compareAndExchangeAcquire(recv, $value1$, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.compareAndExchangeAcquire(recv, $value1$, $value1$);
         });
@@ -529,21 +535,21 @@
             $type$ x = ($type$) vh.compareAndExchangeRelease(null, $value1$, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             $type$ x = ($type$) vh.compareAndExchangeRelease(Void.class, $value1$, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             $type$ x = ($type$) vh.compareAndExchangeRelease(recv, Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             $type$ x = ($type$) vh.compareAndExchangeRelease(recv, $value1$, Void.class);
         });
         checkWMTE(() -> { // reciever primitive class
             $type$ x = ($type$) vh.compareAndExchangeRelease(0, $value1$, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.compareAndExchangeRelease(recv, $value1$, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.compareAndExchangeRelease(recv, $value1$, $value1$);
         });
@@ -562,18 +568,18 @@
             $type$ x = ($type$) vh.getAndSet(null, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             $type$ x = ($type$) vh.getAndSet(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndSet(recv, Void.class);
         });
         checkWMTE(() -> { // reciever primitive class
             $type$ x = ($type$) vh.getAndSet(0, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndSet(recv, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndSet(recv, $value1$);
         });
@@ -591,18 +597,18 @@
             $type$ x = ($type$) vh.getAndSetAcquire(null, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             $type$ x = ($type$) vh.getAndSetAcquire(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndSetAcquire(recv, Void.class);
         });
         checkWMTE(() -> { // reciever primitive class
             $type$ x = ($type$) vh.getAndSetAcquire(0, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndSetAcquire(recv, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndSetAcquire(recv, $value1$);
         });
@@ -620,18 +626,18 @@
             $type$ x = ($type$) vh.getAndSetRelease(null, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             $type$ x = ($type$) vh.getAndSetRelease(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndSetRelease(recv, Void.class);
         });
         checkWMTE(() -> { // reciever primitive class
             $type$ x = ($type$) vh.getAndSetRelease(0, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndSetRelease(recv, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndSetRelease(recv, $value1$);
         });
@@ -651,18 +657,18 @@
             $type$ x = ($type$) vh.getAndAdd(null, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             $type$ x = ($type$) vh.getAndAdd(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndAdd(recv, Void.class);
         });
         checkWMTE(() -> { // reciever primitive class
             $type$ x = ($type$) vh.getAndAdd(0, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndAdd(recv, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndAdd(recv, $value1$);
         });
@@ -680,18 +686,18 @@
             $type$ x = ($type$) vh.getAndAddAcquire(null, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             $type$ x = ($type$) vh.getAndAddAcquire(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndAddAcquire(recv, Void.class);
         });
         checkWMTE(() -> { // reciever primitive class
             $type$ x = ($type$) vh.getAndAddAcquire(0, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndAddAcquire(recv, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndAddAcquire(recv, $value1$);
         });
@@ -709,18 +715,18 @@
             $type$ x = ($type$) vh.getAndAddRelease(null, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             $type$ x = ($type$) vh.getAndAddRelease(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndAddRelease(recv, Void.class);
         });
         checkWMTE(() -> { // reciever primitive class
             $type$ x = ($type$) vh.getAndAddRelease(0, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndAddRelease(recv, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndAddRelease(recv, $value1$);
         });
@@ -740,18 +746,18 @@
             $type$ x = ($type$) vh.getAndBitwiseOr(null, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             $type$ x = ($type$) vh.getAndBitwiseOr(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseOr(recv, Void.class);
         });
         checkWMTE(() -> { // reciever primitive class
             $type$ x = ($type$) vh.getAndBitwiseOr(0, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseOr(recv, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseOr(recv, $value1$);
         });
@@ -770,18 +776,18 @@
             $type$ x = ($type$) vh.getAndBitwiseOrAcquire(null, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             $type$ x = ($type$) vh.getAndBitwiseOrAcquire(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseOrAcquire(recv, Void.class);
         });
         checkWMTE(() -> { // reciever primitive class
             $type$ x = ($type$) vh.getAndBitwiseOrAcquire(0, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseOrAcquire(recv, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseOrAcquire(recv, $value1$);
         });
@@ -800,18 +806,18 @@
             $type$ x = ($type$) vh.getAndBitwiseOrRelease(null, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             $type$ x = ($type$) vh.getAndBitwiseOr(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseOr(recv, Void.class);
         });
         checkWMTE(() -> { // reciever primitive class
             $type$ x = ($type$) vh.getAndBitwiseOr(0, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseOr(recv, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseOr(recv, $value1$);
         });
@@ -830,18 +836,18 @@
             $type$ x = ($type$) vh.getAndBitwiseAnd(null, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             $type$ x = ($type$) vh.getAndBitwiseAnd(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseAnd(recv, Void.class);
         });
         checkWMTE(() -> { // reciever primitive class
             $type$ x = ($type$) vh.getAndBitwiseAnd(0, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseAnd(recv, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseAnd(recv, $value1$);
         });
@@ -860,18 +866,18 @@
             $type$ x = ($type$) vh.getAndBitwiseAndAcquire(null, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             $type$ x = ($type$) vh.getAndBitwiseAndAcquire(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseAndAcquire(recv, Void.class);
         });
         checkWMTE(() -> { // reciever primitive class
             $type$ x = ($type$) vh.getAndBitwiseAndAcquire(0, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseAndAcquire(recv, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseAndAcquire(recv, $value1$);
         });
@@ -890,18 +896,18 @@
             $type$ x = ($type$) vh.getAndBitwiseAndRelease(null, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             $type$ x = ($type$) vh.getAndBitwiseAnd(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseAnd(recv, Void.class);
         });
         checkWMTE(() -> { // reciever primitive class
             $type$ x = ($type$) vh.getAndBitwiseAnd(0, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseAnd(recv, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseAnd(recv, $value1$);
         });
@@ -920,18 +926,18 @@
             $type$ x = ($type$) vh.getAndBitwiseXor(null, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             $type$ x = ($type$) vh.getAndBitwiseXor(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseXor(recv, Void.class);
         });
         checkWMTE(() -> { // reciever primitive class
             $type$ x = ($type$) vh.getAndBitwiseXor(0, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseXor(recv, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseXor(recv, $value1$);
         });
@@ -950,18 +956,18 @@
             $type$ x = ($type$) vh.getAndBitwiseXorAcquire(null, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             $type$ x = ($type$) vh.getAndBitwiseXorAcquire(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseXorAcquire(recv, Void.class);
         });
         checkWMTE(() -> { // reciever primitive class
             $type$ x = ($type$) vh.getAndBitwiseXorAcquire(0, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseXorAcquire(recv, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseXorAcquire(recv, $value1$);
         });
@@ -980,18 +986,18 @@
             $type$ x = ($type$) vh.getAndBitwiseXorRelease(null, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             $type$ x = ($type$) vh.getAndBitwiseXor(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseXor(recv, Void.class);
         });
         checkWMTE(() -> { // reciever primitive class
             $type$ x = ($type$) vh.getAndBitwiseXor(0, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseXor(recv, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseXor(recv, $value1$);
         });
@@ -1019,11 +1025,11 @@
             checkWMTE(() -> { // receiver primitive class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, int.class)).
                     invokeExact(0);
             });
             // Incorrect return type
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
                 Void x = (Void) hs.get(am, methodType(Void.class, VarHandleTestMethodType$Type$.class)).
                     invokeExact(recv);
             });
             checkWMTE(() -> { // primitive class
                 $wrong_primitive_type$ x = ($wrong_primitive_type$) hs.get(am, methodType($wrong_primitive_type$.class, VarHandleTestMethodType$Type$.class)).
@@ -1048,11 +1054,11 @@
             });
             hs.checkWMTEOrCCE(() -> { // receiver reference class
                 hs.get(am, methodType(void.class, Class.class, $type$.class)).
                     invokeExact(Void.class, $value1$);
             });
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
                 hs.get(am, methodType(void.class, VarHandleTestMethodType$Type$.class, Class.class)).
                     invokeExact(recv, Void.class);
             });
             checkWMTE(() -> { // receiver primitive class
                 hs.get(am, methodType(void.class, int.class, $type$.class)).
@@ -1078,15 +1084,15 @@
             });
             hs.checkWMTEOrCCE(() -> { // receiver reference class
                 boolean r = (boolean) hs.get(am, methodType(boolean.class, Class.class, $type$.class, $type$.class)).
                     invokeExact(Void.class, $value1$, $value1$);
             });
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // expected reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // expected reference class
                 boolean r = (boolean) hs.get(am, methodType(boolean.class, VarHandleTestMethodType$Type$.class, Class.class, $type$.class)).
                     invokeExact(recv, Void.class, $value1$);
             });
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // actual reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // actual reference class
                 boolean r = (boolean) hs.get(am, methodType(boolean.class, VarHandleTestMethodType$Type$.class, $type$.class, Class.class)).
                     invokeExact(recv, $value1$, Void.class);
             });
             checkWMTE(() -> { // receiver primitive class
                 boolean r = (boolean) hs.get(am, methodType(boolean.class, int.class , $type$.class, $type$.class)).
@@ -1110,24 +1116,24 @@
             });
             hs.checkWMTEOrCCE(() -> { // receiver reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, Class.class, $type$.class, $type$.class)).
                     invokeExact(Void.class, $value1$, $value1$);
             });
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // expected reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // expected reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, VarHandleTestMethodType$Type$.class, Class.class, $type$.class)).
                     invokeExact(recv, Void.class, $value1$);
             });
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // actual reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // actual reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, VarHandleTestMethodType$Type$.class, $type$.class, Class.class)).
                     invokeExact(recv, $value1$, Void.class);
             });
             checkWMTE(() -> { // reciever primitive class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, int.class , $type$.class, $type$.class)).
                     invokeExact(0, $value1$, $value1$);
             });
             // Incorrect return type
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
                 Void r = (Void) hs.get(am, methodType(Void.class, VarHandleTestMethodType$Type$.class , $type$.class, $type$.class)).
                     invokeExact(recv, $value1$, $value1$);
             });
             checkWMTE(() -> { // primitive class
                 $wrong_primitive_type$ x = ($wrong_primitive_type$) hs.get(am, methodType($wrong_primitive_type$.class, VarHandleTestMethodType$Type$.class , $type$.class, $type$.class)).
@@ -1151,20 +1157,20 @@
             });
             hs.checkWMTEOrCCE(() -> { // receiver reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, Class.class, $type$.class)).
                     invokeExact(Void.class, $value1$);
             });
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, VarHandleTestMethodType$Type$.class, Class.class)).
                     invokeExact(recv, Void.class);
             });
             checkWMTE(() -> { // reciever primitive class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, int.class, $type$.class)).
                     invokeExact(0, $value1$);
             });
             // Incorrect return type
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
                 Void r = (Void) hs.get(am, methodType(Void.class, VarHandleTestMethodType$Type$.class, $type$.class)).
                     invokeExact(recv, $value1$);
             });
             checkWMTE(() -> { // primitive class
                 $wrong_primitive_type$ x = ($wrong_primitive_type$) hs.get(am, methodType($wrong_primitive_type$.class, VarHandleTestMethodType$Type$.class, $type$.class)).
@@ -1190,20 +1196,20 @@
             });
             hs.checkWMTEOrCCE(() -> { // receiver reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, Class.class, $type$.class)).
                     invokeExact(Void.class, $value1$);
             });
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, VarHandleTestMethodType$Type$.class, Class.class)).
                     invokeExact(recv, Void.class);
             });
             checkWMTE(() -> { // reciever primitive class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, int.class, $type$.class)).
                     invokeExact(0, $value1$);
             });
             // Incorrect return type
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
                 Void r = (Void) hs.get(am, methodType(Void.class, VarHandleTestMethodType$Type$.class, $type$.class)).
                     invokeExact(recv, $value1$);
             });
             checkWMTE(() -> { // primitive class
                 $wrong_primitive_type$ x = ($wrong_primitive_type$) hs.get(am, methodType($wrong_primitive_type$.class, VarHandleTestMethodType$Type$.class, $type$.class)).
@@ -1229,20 +1235,20 @@
             });
             hs.checkWMTEOrCCE(() -> { // receiver reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, Class.class, $type$.class)).
                     invokeExact(Void.class, $value1$);
             });
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, VarHandleTestMethodType$Type$.class, Class.class)).
                     invokeExact(recv, Void.class);
             });
             checkWMTE(() -> { // reciever primitive class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, int.class, $type$.class)).
                     invokeExact(0, $value1$);
             });
             // Incorrect return type
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
                 Void r = (Void) hs.get(am, methodType(Void.class, VarHandleTestMethodType$Type$.class, $type$.class)).
                     invokeExact(recv, $value1$);
             });
             checkWMTE(() -> { // primitive class
                 $wrong_primitive_type$ x = ($wrong_primitive_type$) hs.get(am, methodType($wrong_primitive_type$.class, VarHandleTestMethodType$Type$.class, $type$.class)).
@@ -1263,11 +1269,11 @@
 
 
     static void testStaticFieldWrongMethodType(VarHandle vh) throws Throwable {
         // Get
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void x = (Void) vh.get();
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.get();
         });
@@ -1277,11 +1283,11 @@
         });
 
 
         // Set
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             vh.set(Void.class);
         });
         // Incorrect arity
         checkWMTE(() -> { // 0
             vh.set();
@@ -1291,11 +1297,11 @@
         });
 
 
         // GetVolatile
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void x = (Void) vh.getVolatile();
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getVolatile();
         });
@@ -1304,11 +1310,11 @@
         });
 
 
         // SetVolatile
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             vh.setVolatile(Void.class);
         });
         // Incorrect arity
         checkWMTE(() -> { // 0
             vh.setVolatile();
@@ -1318,11 +1324,11 @@
         });
 
 
         // GetOpaque
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void x = (Void) vh.getOpaque();
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getOpaque();
         });
@@ -1331,11 +1337,11 @@
         });
 
 
         // SetOpaque
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             vh.setOpaque(Void.class);
         });
         // Incorrect arity
         checkWMTE(() -> { // 0
             vh.setOpaque();
@@ -1345,11 +1351,11 @@
         });
 
 
         // GetAcquire
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void x = (Void) vh.getAcquire();
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAcquire();
         });
@@ -1358,11 +1364,11 @@
         });
 
 
         // SetRelease
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             vh.setRelease(Void.class);
         });
         // Incorrect arity
         checkWMTE(() -> { // 0
             vh.setRelease();
@@ -1373,14 +1379,14 @@
 
 
 #if[CAS]
         // CompareAndSet
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             boolean r = vh.compareAndSet(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             boolean r = vh.compareAndSet($value1$, Void.class);
         });
         // Incorrect arity
         checkWMTE(() -> { // 0
             boolean r = vh.compareAndSet();
@@ -1390,14 +1396,14 @@
         });
 
 
         // WeakCompareAndSet
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             boolean r = vh.weakCompareAndSetPlain(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             boolean r = vh.weakCompareAndSetPlain($value1$, Void.class);
         });
         // Incorrect arity
         checkWMTE(() -> { // 0
             boolean r = vh.weakCompareAndSetPlain();
@@ -1407,14 +1413,14 @@
         });
 
 
         // WeakCompareAndSetVolatile
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             boolean r = vh.weakCompareAndSet(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             boolean r = vh.weakCompareAndSet($value1$, Void.class);
         });
         // Incorrect arity
         checkWMTE(() -> { // 0
             boolean r = vh.weakCompareAndSet();
@@ -1424,14 +1430,14 @@
         });
 
 
         // WeakCompareAndSetAcquire
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             boolean r = vh.weakCompareAndSetAcquire(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             boolean r = vh.weakCompareAndSetAcquire($value1$, Void.class);
         });
         // Incorrect arity
         checkWMTE(() -> { // 0
             boolean r = vh.weakCompareAndSetAcquire();
@@ -1441,14 +1447,14 @@
         });
 
 
         // WeakCompareAndSetRelease
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             boolean r = vh.weakCompareAndSetRelease(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             boolean r = vh.weakCompareAndSetRelease($value1$, Void.class);
         });
         // Incorrect arity
         checkWMTE(() -> { // 0
             boolean r = vh.weakCompareAndSetRelease();
@@ -1458,18 +1464,18 @@
         });
 
 
         // CompareAndExchange
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             $type$ x = ($type$) vh.compareAndExchange(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             $type$ x = ($type$) vh.compareAndExchange($value1$, Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.compareAndExchange($value1$, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.compareAndExchange($value1$, $value1$);
         });
@@ -1482,18 +1488,18 @@
         });
 
 
         // CompareAndExchangeAcquire
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             $type$ x = ($type$) vh.compareAndExchangeAcquire(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             $type$ x = ($type$) vh.compareAndExchangeAcquire($value1$, Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.compareAndExchangeAcquire($value1$, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.compareAndExchangeAcquire($value1$, $value1$);
         });
@@ -1506,18 +1512,18 @@
         });
 
 
         // CompareAndExchangeRelease
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             $type$ x = ($type$) vh.compareAndExchangeRelease(Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             $type$ x = ($type$) vh.compareAndExchangeRelease($value1$, Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.compareAndExchangeRelease($value1$, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.compareAndExchangeRelease($value1$, $value1$);
         });
@@ -1530,15 +1536,15 @@
         });
 
 
         // GetAndSet
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndSet(Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndSet($value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndSet($value1$);
         });
@@ -1551,15 +1557,15 @@
         });
 
 
         // GetAndSetAcquire
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndSetAcquire(Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndSetAcquire($value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndSetAcquire($value1$);
         });
@@ -1572,15 +1578,15 @@
         });
 
 
         // GetAndSetRelease
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndSetRelease(Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndSetRelease($value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndSetRelease($value1$);
         });
@@ -1594,15 +1600,15 @@
 #end[CAS]
 
 #if[AtomicAdd]
         // GetAndAdd
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndAdd(Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndAdd($value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndAdd($value1$);
         });
@@ -1615,15 +1621,15 @@
         });
 
 
         // GetAndAddAcquire
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndAddAcquire(Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndAddAcquire($value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndAddAcquire($value1$);
         });
@@ -1636,15 +1642,15 @@
         });
 
 
         // GetAndAddRelease
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndAddRelease(Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndAddRelease($value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndAddRelease($value1$);
         });
@@ -1658,15 +1664,15 @@
 #end[AtomicAdd]
 
 #if[Bitwise]
         // GetAndBitwiseOr
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseOr(Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseOr($value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseOr($value1$);
         });
@@ -1679,15 +1685,15 @@
         });
 
 
         // GetAndBitwiseOrAcquire
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseOrAcquire(Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseOrAcquire($value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseOrAcquire($value1$);
         });
@@ -1700,15 +1706,15 @@
         });
 
 
         // GetAndBitwiseOrReleaseRelease
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseOrRelease(Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseOrRelease($value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseOrRelease($value1$);
         });
@@ -1721,15 +1727,15 @@
         });
 
 
         // GetAndBitwiseAnd
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseAnd(Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseAnd($value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseAnd($value1$);
         });
@@ -1742,15 +1748,15 @@
         });
 
 
         // GetAndBitwiseAndAcquire
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseAndAcquire(Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseAndAcquire($value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseAndAcquire($value1$);
         });
@@ -1763,15 +1769,15 @@
         });
 
 
         // GetAndBitwiseAndReleaseRelease
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseAndRelease(Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseAndRelease($value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseAndRelease($value1$);
         });
@@ -1784,15 +1790,15 @@
         });
 
 
         // GetAndBitwiseXor
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseXor(Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseXor($value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseXor($value1$);
         });
@@ -1805,15 +1811,15 @@
         });
 
 
         // GetAndBitwiseXorAcquire
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseXorAcquire(Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseXorAcquire($value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseXorAcquire($value1$);
         });
@@ -1826,15 +1832,15 @@
         });
 
 
         // GetAndBitwiseXorReleaseRelease
         // Incorrect argument types
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseXorRelease(Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseXorRelease($value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseXorRelease($value1$);
         });
@@ -1851,11 +1857,11 @@
     static void testStaticFieldWrongMethodType(Handles hs) throws Throwable {
         int i = 0;
 
         for (TestAccessMode am : testAccessModesOfType(TestAccessType.GET)) {
             // Incorrect return type
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
                 Void x = (Void) hs.get(am, methodType(Void.class)).
                     invokeExact();
             });
             checkWMTE(() -> { // primitive class
                 $wrong_primitive_type$ x = ($wrong_primitive_type$) hs.get(am, methodType($wrong_primitive_type$.class)).
@@ -1867,11 +1873,11 @@
                     invokeExact(Void.class);
             });
         }
 
         for (TestAccessMode am : testAccessModesOfType(TestAccessType.SET)) {
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
                 hs.get(am, methodType(void.class, Class.class)).
                     invokeExact(Void.class);
             });
             // Incorrect arity
             checkWMTE(() -> { // 0
@@ -1884,15 +1890,15 @@
             });
         }
 #if[CAS]
         for (TestAccessMode am : testAccessModesOfType(TestAccessType.COMPARE_AND_SET)) {
             // Incorrect argument types
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // expected reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // expected reference class
                 boolean r = (boolean) hs.get(am, methodType(boolean.class, Class.class, $type$.class)).
                     invokeExact(Void.class, $value1$);
             });
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // actual reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // actual reference class
                 boolean r = (boolean) hs.get(am, methodType(boolean.class, $type$.class, Class.class)).
                     invokeExact($value1$, Void.class);
             });
             // Incorrect arity
             checkWMTE(() -> { // 0
@@ -1905,20 +1911,20 @@
             });
         }
 
         for (TestAccessMode am : testAccessModesOfType(TestAccessType.COMPARE_AND_EXCHANGE)) {
             // Incorrect argument types
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // expected reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // expected reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, Class.class, $type$.class)).
                     invokeExact(Void.class, $value1$);
             });
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // actual reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // actual reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, $type$.class, Class.class)).
                     invokeExact($value1$, Void.class);
             });
             // Incorrect return type
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
                 Void r = (Void) hs.get(am, methodType(Void.class, $type$.class, $type$.class)).
                     invokeExact($value1$, $value1$);
             });
             checkWMTE(() -> { // primitive class
                 $wrong_primitive_type$ x = ($wrong_primitive_type$) hs.get(am, methodType($wrong_primitive_type$.class, $type$.class, $type$.class)).
@@ -1935,16 +1941,16 @@
             });
         }
 
         for (TestAccessMode am : testAccessModesOfType(TestAccessType.GET_AND_SET)) {
             // Incorrect argument types
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, Class.class)).
                     invokeExact(Void.class);
             });
             // Incorrect return type
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
                 Void r = (Void) hs.get(am, methodType(Void.class, $type$.class)).
                     invokeExact($value1$);
             });
             checkWMTE(() -> { // primitive class
                 $wrong_primitive_type$ x = ($wrong_primitive_type$) hs.get(am, methodType($wrong_primitive_type$.class, $type$.class)).
@@ -1963,16 +1969,16 @@
 #end[CAS]
 
 #if[AtomicAdd]
         for (TestAccessMode am : testAccessModesOfType(TestAccessType.GET_AND_ADD)) {
             // Incorrect argument types
-            check{#if[String]?CCE:WMTE}(() -> { // value reference class
+            check{#if[Object]?CCE:WMTE}(() -> { // value reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, Class.class)).
                     invokeExact(Void.class);
             });
             // Incorrect return type
-            check{#if[String]?CCE:WMTE}(() -> { // reference class
+            check{#if[Object]?CCE:WMTE}(() -> { // reference class
                 Void r = (Void) hs.get(am, methodType(Void.class, $type$.class)).
                     invokeExact($value1$);
             });
             checkWMTE(() -> { // primitive class
                 $wrong_primitive_type$ x = ($wrong_primitive_type$) hs.get(am, methodType($wrong_primitive_type$.class, $type$.class)).
@@ -1991,16 +1997,16 @@
 #end[AtomicAdd]
 
 #if[Bitwise]
         for (TestAccessMode am : testAccessModesOfType(TestAccessType.GET_AND_BITWISE)) {
             // Incorrect argument types
-            check{#if[String]?CCE:WMTE}(() -> { // value reference class
+            check{#if[Object]?CCE:WMTE}(() -> { // value reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, Class.class)).
                     invokeExact(Void.class);
             });
             // Incorrect return type
-            check{#if[String]?CCE:WMTE}(() -> { // reference class
+            check{#if[Object]?CCE:WMTE}(() -> { // reference class
                 Void r = (Void) hs.get(am, methodType(Void.class, $type$.class)).
                     invokeExact($value1$);
             });
             checkWMTE(() -> { // primitive class
                 $wrong_primitive_type$ x = ($wrong_primitive_type$) hs.get(am, methodType($wrong_primitive_type$.class, $type$.class)).
@@ -2037,11 +2043,11 @@
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.get(array, Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void x = (Void) vh.get(array, 0);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.get(array, 0);
         });
@@ -2060,11 +2066,11 @@
             vh.set(null, 0, $value1$);
         });
         checkCCE(() -> { // array reference class
             vh.set(Void.class, 0, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             vh.set(array, 0, Void.class);
         });
         checkWMTE(() -> { // receiver primitive class
             vh.set(0, 0, $value1$);
         });
@@ -2093,11 +2099,11 @@
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.getVolatile(array, Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void x = (Void) vh.getVolatile(array, 0);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getVolatile(array, 0);
         });
@@ -2116,11 +2122,11 @@
             vh.setVolatile(null, 0, $value1$);
         });
         checkCCE(() -> { // array reference class
             vh.setVolatile(Void.class, 0, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             vh.setVolatile(array, 0, Void.class);
         });
         checkWMTE(() -> { // receiver primitive class
             vh.setVolatile(0, 0, $value1$);
         });
@@ -2149,11 +2155,11 @@
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.getOpaque(array, Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void x = (Void) vh.getOpaque(array, 0);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getOpaque(array, 0);
         });
@@ -2172,11 +2178,11 @@
             vh.setOpaque(null, 0, $value1$);
         });
         checkCCE(() -> { // array reference class
             vh.setOpaque(Void.class, 0, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             vh.setOpaque(array, 0, Void.class);
         });
         checkWMTE(() -> { // receiver primitive class
             vh.setOpaque(0, 0, $value1$);
         });
@@ -2205,11 +2211,11 @@
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.getAcquire(array, Void.class);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void x = (Void) vh.getAcquire(array, 0);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAcquire(array, 0);
         });
@@ -2228,11 +2234,11 @@
             vh.setRelease(null, 0, $value1$);
         });
         checkCCE(() -> { // array reference class
             vh.setRelease(Void.class, 0, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             vh.setRelease(array, 0, Void.class);
         });
         checkWMTE(() -> { // receiver primitive class
             vh.setRelease(0, 0, $value1$);
         });
@@ -2255,14 +2261,14 @@
             boolean r = vh.compareAndSet(null, 0, $value1$, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             boolean r = vh.compareAndSet(Void.class, 0, $value1$, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             boolean r = vh.compareAndSet(array, 0, Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             boolean r = vh.compareAndSet(array, 0, $value1$, Void.class);
         });
         checkWMTE(() -> { // receiver primitive class
             boolean r = vh.compareAndSet(0, 0, $value1$, $value1$);
         });
@@ -2284,14 +2290,14 @@
             boolean r = vh.weakCompareAndSetPlain(null, 0, $value1$, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             boolean r = vh.weakCompareAndSetPlain(Void.class, 0, $value1$, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             boolean r = vh.weakCompareAndSetPlain(array, 0, Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             boolean r = vh.weakCompareAndSetPlain(array, 0, $value1$, Void.class);
         });
         checkWMTE(() -> { // receiver primitive class
             boolean r = vh.weakCompareAndSetPlain(0, 0, $value1$, $value1$);
         });
@@ -2313,14 +2319,14 @@
             boolean r = vh.weakCompareAndSet(null, 0, $value1$, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             boolean r = vh.weakCompareAndSet(Void.class, 0, $value1$, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             boolean r = vh.weakCompareAndSet(array, 0, Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             boolean r = vh.weakCompareAndSet(array, 0, $value1$, Void.class);
         });
         checkWMTE(() -> { // receiver primitive class
             boolean r = vh.weakCompareAndSet(0, 0, $value1$, $value1$);
         });
@@ -2342,14 +2348,14 @@
             boolean r = vh.weakCompareAndSetAcquire(null, 0, $value1$, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             boolean r = vh.weakCompareAndSetAcquire(Void.class, 0, $value1$, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             boolean r = vh.weakCompareAndSetAcquire(array, 0, Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             boolean r = vh.weakCompareAndSetAcquire(array, 0, $value1$, Void.class);
         });
         checkWMTE(() -> { // receiver primitive class
             boolean r = vh.weakCompareAndSetAcquire(0, 0, $value1$, $value1$);
         });
@@ -2371,14 +2377,14 @@
             boolean r = vh.weakCompareAndSetRelease(null, 0, $value1$, $value1$);
         });
         checkCCE(() -> { // receiver reference class
             boolean r = vh.weakCompareAndSetRelease(Void.class, 0, $value1$, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             boolean r = vh.weakCompareAndSetRelease(array, 0, Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             boolean r = vh.weakCompareAndSetRelease(array, 0, $value1$, Void.class);
         });
         checkWMTE(() -> { // receiver primitive class
             boolean r = vh.weakCompareAndSetRelease(0, 0, $value1$, $value1$);
         });
@@ -2400,24 +2406,24 @@
             $type$ x = ($type$) vh.compareAndExchange(null, 0, $value1$, $value1$);
         });
         checkCCE(() -> { // array reference class
             $type$ x = ($type$) vh.compareAndExchange(Void.class, 0, $value1$, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             $type$ x = ($type$) vh.compareAndExchange(array, 0, Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             $type$ x = ($type$) vh.compareAndExchange(array, 0, $value1$, Void.class);
         });
         checkWMTE(() -> { // array primitive class
             $type$ x = ($type$) vh.compareAndExchange(0, 0, $value1$, $value1$);
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.compareAndExchange(array, Void.class, $value1$, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.compareAndExchange(array, 0, $value1$, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.compareAndExchange(array, 0, $value1$, $value1$);
         });
@@ -2436,24 +2442,24 @@
             $type$ x = ($type$) vh.compareAndExchangeAcquire(null, 0, $value1$, $value1$);
         });
         checkCCE(() -> { // array reference class
             $type$ x = ($type$) vh.compareAndExchangeAcquire(Void.class, 0, $value1$, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             $type$ x = ($type$) vh.compareAndExchangeAcquire(array, 0, Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             $type$ x = ($type$) vh.compareAndExchangeAcquire(array, 0, $value1$, Void.class);
         });
         checkWMTE(() -> { // array primitive class
             $type$ x = ($type$) vh.compareAndExchangeAcquire(0, 0, $value1$, $value1$);
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.compareAndExchangeAcquire(array, Void.class, $value1$, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.compareAndExchangeAcquire(array, 0, $value1$, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.compareAndExchangeAcquire(array, 0, $value1$, $value1$);
         });
@@ -2472,24 +2478,24 @@
             $type$ x = ($type$) vh.compareAndExchangeRelease(null, 0, $value1$, $value1$);
         });
         checkCCE(() -> { // array reference class
             $type$ x = ($type$) vh.compareAndExchangeRelease(Void.class, 0, $value1$, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // expected reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // expected reference class
             $type$ x = ($type$) vh.compareAndExchangeRelease(array, 0, Void.class, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // actual reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // actual reference class
             $type$ x = ($type$) vh.compareAndExchangeRelease(array, 0, $value1$, Void.class);
         });
         checkWMTE(() -> { // array primitive class
             $type$ x = ($type$) vh.compareAndExchangeRelease(0, 0, $value1$, $value1$);
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.compareAndExchangeRelease(array, Void.class, $value1$, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.compareAndExchangeRelease(array, 0, $value1$, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.compareAndExchangeRelease(array, 0, $value1$, $value1$);
         });
@@ -2508,21 +2514,21 @@
             $type$ x = ($type$) vh.getAndSet(null, 0, $value1$);
         });
         checkCCE(() -> { // array reference class
             $type$ x = ($type$) vh.getAndSet(Void.class, 0, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndSet(array, 0, Void.class);
         });
         checkWMTE(() -> { // reciarrayever primitive class
             $type$ x = ($type$) vh.getAndSet(0, 0, $value1$);
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.getAndSet(array, Void.class, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndSet(array, 0, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndSet(array, 0, $value1$);
         });
@@ -2541,21 +2547,21 @@
             $type$ x = ($type$) vh.getAndSetAcquire(null, 0, $value1$);
         });
         checkCCE(() -> { // array reference class
             $type$ x = ($type$) vh.getAndSetAcquire(Void.class, 0, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndSetAcquire(array, 0, Void.class);
         });
         checkWMTE(() -> { // reciarrayever primitive class
             $type$ x = ($type$) vh.getAndSetAcquire(0, 0, $value1$);
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.getAndSetAcquire(array, Void.class, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndSetAcquire(array, 0, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndSetAcquire(array, 0, $value1$);
         });
@@ -2574,21 +2580,21 @@
             $type$ x = ($type$) vh.getAndSetRelease(null, 0, $value1$);
         });
         checkCCE(() -> { // array reference class
             $type$ x = ($type$) vh.getAndSetRelease(Void.class, 0, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndSetRelease(array, 0, Void.class);
         });
         checkWMTE(() -> { // reciarrayever primitive class
             $type$ x = ($type$) vh.getAndSetRelease(0, 0, $value1$);
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.getAndSetRelease(array, Void.class, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndSetRelease(array, 0, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndSetRelease(array, 0, $value1$);
         });
@@ -2608,21 +2614,21 @@
             $type$ x = ($type$) vh.getAndAdd(null, 0, $value1$);
         });
         checkCCE(() -> { // array reference class
             $type$ x = ($type$) vh.getAndAdd(Void.class, 0, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndAdd(array, 0, Void.class);
         });
         checkWMTE(() -> { // array primitive class
             $type$ x = ($type$) vh.getAndAdd(0, 0, $value1$);
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.getAndAdd(array, Void.class, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndAdd(array, 0, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndAdd(array, 0, $value1$);
         });
@@ -2641,21 +2647,21 @@
             $type$ x = ($type$) vh.getAndAddAcquire(null, 0, $value1$);
         });
         checkCCE(() -> { // array reference class
             $type$ x = ($type$) vh.getAndAddAcquire(Void.class, 0, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndAddAcquire(array, 0, Void.class);
         });
         checkWMTE(() -> { // array primitive class
             $type$ x = ($type$) vh.getAndAddAcquire(0, 0, $value1$);
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.getAndAddAcquire(array, Void.class, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndAddAcquire(array, 0, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndAddAcquire(array, 0, $value1$);
         });
@@ -2674,21 +2680,21 @@
             $type$ x = ($type$) vh.getAndAddRelease(null, 0, $value1$);
         });
         checkCCE(() -> { // array reference class
             $type$ x = ($type$) vh.getAndAddRelease(Void.class, 0, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndAddRelease(array, 0, Void.class);
         });
         checkWMTE(() -> { // array primitive class
             $type$ x = ($type$) vh.getAndAddRelease(0, 0, $value1$);
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.getAndAddRelease(array, Void.class, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndAddRelease(array, 0, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndAddRelease(array, 0, $value1$);
         });
@@ -2708,21 +2714,21 @@
             $type$ x = ($type$) vh.getAndBitwiseOr(null, 0, $value1$);
         });
         checkCCE(() -> { // array reference class
             $type$ x = ($type$) vh.getAndBitwiseOr(Void.class, 0, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseOr(array, 0, Void.class);
         });
         checkWMTE(() -> { // array primitive class
             $type$ x = ($type$) vh.getAndBitwiseOr(0, 0, $value1$);
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.getAndBitwiseOr(array, Void.class, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseOr(array, 0, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseOr(array, 0, $value1$);
         });
@@ -2741,21 +2747,21 @@
             $type$ x = ($type$) vh.getAndBitwiseOrAcquire(null, 0, $value1$);
         });
         checkCCE(() -> { // array reference class
             $type$ x = ($type$) vh.getAndBitwiseOrAcquire(Void.class, 0, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseOrAcquire(array, 0, Void.class);
         });
         checkWMTE(() -> { // array primitive class
             $type$ x = ($type$) vh.getAndBitwiseOrAcquire(0, 0, $value1$);
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.getAndBitwiseOrAcquire(array, Void.class, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseOrAcquire(array, 0, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseOrAcquire(array, 0, $value1$);
         });
@@ -2774,21 +2780,21 @@
             $type$ x = ($type$) vh.getAndBitwiseOrRelease(null, 0, $value1$);
         });
         checkCCE(() -> { // array reference class
             $type$ x = ($type$) vh.getAndBitwiseOrRelease(Void.class, 0, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseOrRelease(array, 0, Void.class);
         });
         checkWMTE(() -> { // array primitive class
             $type$ x = ($type$) vh.getAndBitwiseOrRelease(0, 0, $value1$);
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.getAndBitwiseOrRelease(array, Void.class, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseOrRelease(array, 0, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseOrRelease(array, 0, $value1$);
         });
@@ -2807,21 +2813,21 @@
             $type$ x = ($type$) vh.getAndBitwiseAnd(null, 0, $value1$);
         });
         checkCCE(() -> { // array reference class
             $type$ x = ($type$) vh.getAndBitwiseAnd(Void.class, 0, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseAnd(array, 0, Void.class);
         });
         checkWMTE(() -> { // array primitive class
             $type$ x = ($type$) vh.getAndBitwiseAnd(0, 0, $value1$);
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.getAndBitwiseAnd(array, Void.class, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseAnd(array, 0, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseAnd(array, 0, $value1$);
         });
@@ -2840,21 +2846,21 @@
             $type$ x = ($type$) vh.getAndBitwiseAndAcquire(null, 0, $value1$);
         });
         checkCCE(() -> { // array reference class
             $type$ x = ($type$) vh.getAndBitwiseAndAcquire(Void.class, 0, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseAndAcquire(array, 0, Void.class);
         });
         checkWMTE(() -> { // array primitive class
             $type$ x = ($type$) vh.getAndBitwiseAndAcquire(0, 0, $value1$);
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.getAndBitwiseAndAcquire(array, Void.class, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseAndAcquire(array, 0, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseAndAcquire(array, 0, $value1$);
         });
@@ -2873,21 +2879,21 @@
             $type$ x = ($type$) vh.getAndBitwiseAndRelease(null, 0, $value1$);
         });
         checkCCE(() -> { // array reference class
             $type$ x = ($type$) vh.getAndBitwiseAndRelease(Void.class, 0, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseAndRelease(array, 0, Void.class);
         });
         checkWMTE(() -> { // array primitive class
             $type$ x = ($type$) vh.getAndBitwiseAndRelease(0, 0, $value1$);
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.getAndBitwiseAndRelease(array, Void.class, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseAndRelease(array, 0, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseAndRelease(array, 0, $value1$);
         });
@@ -2906,21 +2912,21 @@
             $type$ x = ($type$) vh.getAndBitwiseXor(null, 0, $value1$);
         });
         checkCCE(() -> { // array reference class
             $type$ x = ($type$) vh.getAndBitwiseXor(Void.class, 0, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseXor(array, 0, Void.class);
         });
         checkWMTE(() -> { // array primitive class
             $type$ x = ($type$) vh.getAndBitwiseXor(0, 0, $value1$);
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.getAndBitwiseXor(array, Void.class, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseXor(array, 0, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseXor(array, 0, $value1$);
         });
@@ -2939,21 +2945,21 @@
             $type$ x = ($type$) vh.getAndBitwiseXorAcquire(null, 0, $value1$);
         });
         checkCCE(() -> { // array reference class
             $type$ x = ($type$) vh.getAndBitwiseXorAcquire(Void.class, 0, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseXorAcquire(array, 0, Void.class);
         });
         checkWMTE(() -> { // array primitive class
             $type$ x = ($type$) vh.getAndBitwiseXorAcquire(0, 0, $value1$);
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.getAndBitwiseXorAcquire(array, Void.class, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseXorAcquire(array, 0, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseXorAcquire(array, 0, $value1$);
         });
@@ -2972,21 +2978,21 @@
             $type$ x = ($type$) vh.getAndBitwiseXorRelease(null, 0, $value1$);
         });
         checkCCE(() -> { // array reference class
             $type$ x = ($type$) vh.getAndBitwiseXorRelease(Void.class, 0, $value1$);
         });
-        check{#if[String]?CCE:WMTE}(() -> { // value reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // value reference class
             $type$ x = ($type$) vh.getAndBitwiseXorRelease(array, 0, Void.class);
         });
         checkWMTE(() -> { // array primitive class
             $type$ x = ($type$) vh.getAndBitwiseXorRelease(0, 0, $value1$);
         });
         checkWMTE(() -> { // index reference class
             $type$ x = ($type$) vh.getAndBitwiseXorRelease(array, Void.class, $value1$);
         });
         // Incorrect return type
-        check{#if[String]?CCE:WMTE}(() -> { // reference class
+        check{#if[Object]?CCE:WMTE}(() -> { // reference class
             Void r = (Void) vh.getAndBitwiseXorRelease(array, 0, $value1$);
         });
         checkWMTE(() -> { // primitive class
             $wrong_primitive_type$ x = ($wrong_primitive_type$) vh.getAndBitwiseXorRelease(array, 0, $value1$);
         });
@@ -3021,11 +3027,11 @@
             checkWMTE(() -> { // index reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, $type$[].class, Class.class)).
                     invokeExact(array, Void.class);
             });
             // Incorrect return type
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
                 Void x = (Void) hs.get(am, methodType(Void.class, $type$[].class, int.class)).
                     invokeExact(array, 0);
             });
             checkWMTE(() -> { // primitive class
                 $wrong_primitive_type$ x = ($wrong_primitive_type$) hs.get(am, methodType($wrong_primitive_type$.class, $type$[].class, int.class)).
@@ -3050,11 +3056,11 @@
             });
             hs.checkWMTEOrCCE(() -> { // array reference class
                 hs.get(am, methodType(void.class, Class.class, int.class, $type$.class)).
                     invokeExact(Void.class, 0, $value1$);
             });
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
                 hs.get(am, methodType(void.class, $type$[].class, int.class, Class.class)).
                     invokeExact(array, 0, Void.class);
             });
             checkWMTE(() -> { // receiver primitive class
                 hs.get(am, methodType(void.class, int.class, int.class, $type$.class)).
@@ -3083,15 +3089,15 @@
             });
             hs.checkWMTEOrCCE(() -> { // receiver reference class
                 boolean r = (boolean) hs.get(am, methodType(boolean.class, Class.class, int.class, $type$.class, $type$.class)).
                     invokeExact(Void.class, 0, $value1$, $value1$);
             });
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // expected reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // expected reference class
                 boolean r = (boolean) hs.get(am, methodType(boolean.class, $type$[].class, int.class, Class.class, $type$.class)).
                     invokeExact(array, 0, Void.class, $value1$);
             });
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // actual reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // actual reference class
                 boolean r = (boolean) hs.get(am, methodType(boolean.class, $type$[].class, int.class, $type$.class, Class.class)).
                     invokeExact(array, 0, $value1$, Void.class);
             });
             checkWMTE(() -> { // receiver primitive class
                 boolean r = (boolean) hs.get(am, methodType(boolean.class, int.class, int.class, $type$.class, $type$.class)).
@@ -3120,15 +3126,15 @@
             });
             hs.checkWMTEOrCCE(() -> { // array reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, Class.class, int.class, $type$.class, $type$.class)).
                     invokeExact(Void.class, 0, $value1$, $value1$);
             });
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // expected reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // expected reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, $type$[].class, int.class, Class.class, $type$.class)).
                     invokeExact(array, 0, Void.class, $value1$);
             });
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // actual reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // actual reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, $type$[].class, int.class, $type$.class, Class.class)).
                     invokeExact(array, 0, $value1$, Void.class);
             });
             checkWMTE(() -> { // array primitive class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, int.class, int.class, $type$.class, $type$.class)).
@@ -3137,11 +3143,11 @@
             checkWMTE(() -> { // index reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, $type$[].class, Class.class, $type$.class, $type$.class)).
                     invokeExact(array, Void.class, $value1$, $value1$);
             });
             // Incorrect return type
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
                 Void r = (Void) hs.get(am, methodType(Void.class, $type$[].class, int.class, $type$.class, $type$.class)).
                     invokeExact(array, 0, $value1$, $value1$);
             });
             checkWMTE(() -> { // primitive class
                 $wrong_primitive_type$ x = ($wrong_primitive_type$) hs.get(am, methodType($wrong_primitive_type$.class, $type$[].class, int.class, $type$.class, $type$.class)).
@@ -3166,11 +3172,11 @@
             });
             hs.checkWMTEOrCCE(() -> { // array reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, Class.class, int.class, $type$.class)).
                     invokeExact(Void.class, 0, $value1$);
             });
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, $type$[].class, int.class, Class.class)).
                     invokeExact(array, 0, Void.class);
             });
             checkWMTE(() -> { // array primitive class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, int.class, int.class, $type$.class)).
@@ -3179,11 +3185,11 @@
             checkWMTE(() -> { // index reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, $type$[].class, Class.class, $type$.class)).
                     invokeExact(array, Void.class, $value1$);
             });
             // Incorrect return type
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
                 Void r = (Void) hs.get(am, methodType(Void.class, $type$[].class, int.class, $type$.class)).
                     invokeExact(array, 0, $value1$);
             });
             checkWMTE(() -> { // primitive class
                 $wrong_primitive_type$ x = ($wrong_primitive_type$) hs.get(am, methodType($wrong_primitive_type$.class, $type$[].class, int.class, $type$.class)).
@@ -3210,11 +3216,11 @@
             });
             hs.checkWMTEOrCCE(() -> { // array reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, Class.class, int.class, $type$.class)).
                     invokeExact(Void.class, 0, $value1$);
             });
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, $type$[].class, int.class, Class.class)).
                     invokeExact(array, 0, Void.class);
             });
             checkWMTE(() -> { // array primitive class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, int.class, int.class, $type$.class)).
@@ -3223,11 +3229,11 @@
             checkWMTE(() -> { // index reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, $type$[].class, Class.class, $type$.class)).
                     invokeExact(array, Void.class, $value1$);
             });
             // Incorrect return type
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
                 Void r = (Void) hs.get(am, methodType(Void.class, $type$[].class, int.class, $type$.class)).
                     invokeExact(array, 0, $value1$);
             });
             checkWMTE(() -> { // primitive class
                 $wrong_primitive_type$ x = ($wrong_primitive_type$) hs.get(am, methodType($wrong_primitive_type$.class, $type$[].class, int.class, $type$.class)).
@@ -3254,11 +3260,11 @@
             });
             hs.checkWMTEOrCCE(() -> { // array reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, Class.class, int.class, $type$.class)).
                     invokeExact(Void.class, 0, $value1$);
             });
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // value reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, $type$[].class, int.class, Class.class)).
                     invokeExact(array, 0, Void.class);
             });
             checkWMTE(() -> { // array primitive class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, int.class, int.class, $type$.class)).
@@ -3267,11 +3273,11 @@
             checkWMTE(() -> { // index reference class
                 $type$ x = ($type$) hs.get(am, methodType($type$.class, $type$[].class, Class.class, $type$.class)).
                     invokeExact(array, Void.class, $value1$);
             });
             // Incorrect return type
-            {#if[String]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
+            {#if[Object]?hs.checkWMTEOrCCE:checkWMTE}(() -> { // reference class
                 Void r = (Void) hs.get(am, methodType(Void.class, $type$[].class, int.class, $type$.class)).
                     invokeExact(array, 0, $value1$);
             });
             checkWMTE(() -> { // primitive class
                 $wrong_primitive_type$ x = ($wrong_primitive_type$) hs.get(am, methodType($wrong_primitive_type$.class, $type$[].class, int.class, $type$.class)).
diff a/test/jdk/java/lang/invoke/VarHandles/generate-vh-tests.sh b/test/jdk/java/lang/invoke/VarHandles/generate-vh-tests.sh
--- a/test/jdk/java/lang/invoke/VarHandles/generate-vh-tests.sh
+++ b/test/jdk/java/lang/invoke/VarHandles/generate-vh-tests.sh
@@ -7,11 +7,11 @@
 # Generates variable handle tests for objects and all primitive types
 # This is likely to be a temporary testing approach as it may be more
 # desirable to generate code using ASM which will allow more flexibility
 # in the kinds of tests that are generated.
 
-for type in boolean byte short char int long float double String
+for type in boolean byte short char int long float double String Value
 do
   Type="$(tr '[:lower:]' '[:upper:]' <<< ${type:0:1})${type:1}"
   args="-K$type -Dtype=$type -DType=$Type"
 
   args="$args -KCAS"
@@ -26,10 +26,21 @@
     boolean|byte|short|char|int|long)
       args="$args -KBitwise"
       ;;
   esac
 
+ # Object = objects of identity or value class
+ # Value = value class 
+  case $type in
+    String)
+      args="$args -KObject"
+      ;;
+    Value)
+      args="$args -KObject -KValue"
+      ;;
+  esac
+
   wrong_primitive_type=boolean
 
   case $type in
     boolean)
       value1=true
@@ -75,10 +86,15 @@
     String)
       value1=\"foo\"
       value2=\"bar\"
       value3=\"baz\"
       ;;
+    Value)
+      value1="Value.getInstance(10)"
+      value2="Value.getInstance(20)"
+      value3="Value.getInstance(30)"
+      ;;
   esac
 
   args="$args -Dvalue1=$value1 -Dvalue2=$value2 -Dvalue3=$value3 -Dwrong_primitive_type=$wrong_primitive_type"
 
   echo $args
diff a/test/jdk/java/lang/invoke/callerSensitive/CallerSensitiveAccess.java b/test/jdk/java/lang/invoke/callerSensitive/CallerSensitiveAccess.java
--- a/test/jdk/java/lang/invoke/callerSensitive/CallerSensitiveAccess.java
+++ b/test/jdk/java/lang/invoke/callerSensitive/CallerSensitiveAccess.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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.
@@ -20,11 +20,11 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
 
 /* @test
- * @bug 8196830 8235351 8257874
+ * @bug 8196830 8235351 8257874 8327639
  * @modules java.base/jdk.internal.reflect
  * @run testng/othervm CallerSensitiveAccess
  * @summary Check Lookup findVirtual, findStatic and unreflect behavior with
  *          caller sensitive methods with focus on AccessibleObject.setAccessible
  */
@@ -48,26 +48,33 @@
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import jdk.internal.reflect.CallerSensitive;
 
+import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.NoInjection;
 import org.testng.annotations.Test;
 import static org.testng.Assert.*;
 
 public class CallerSensitiveAccess {
+    // Cache the list of Caller Sensitive Methods
+    private static List<Method> CALLER_SENSITIVE_METHODS;
+
+    @BeforeClass
+    private void setupCallerSensitiveMethods() {
+        CALLER_SENSITIVE_METHODS = callerSensitiveMethods(Object.class.getModule());
+    }
 
     /**
      * Caller sensitive methods in APIs exported by java.base.
      */
     @DataProvider(name = "callerSensitiveMethods")
     static Object[][] callerSensitiveMethods() {
-        try (Stream<Method> stream = callerSensitiveMethods(Object.class.getModule())) {
-            return stream.map(m -> new Object[]{m, shortDescription(m)})
-                    .toArray(Object[][]::new);
-        }
+        return CALLER_SENSITIVE_METHODS.stream()
+                .map(m -> new Object[]{m, shortDescription(m)})
+                .toArray(Object[][]::new);
     }
 
     /**
      * Using publicLookup, attempt to use findVirtual or findStatic to obtain a
      * method handle to a caller sensitive method.
@@ -99,17 +106,15 @@
     /**
      * public accessible caller sensitive methods in APIs exported by java.base.
      */
     @DataProvider(name = "accessibleCallerSensitiveMethods")
     static Object[][] accessibleCallerSensitiveMethods() {
-        try (Stream<Method> stream = callerSensitiveMethods(Object.class.getModule())) {
-            return stream
+        return CALLER_SENSITIVE_METHODS.stream()
                 .filter(m -> Modifier.isPublic(m.getModifiers()))
                 .map(m -> { m.setAccessible(true); return m; })
                 .map(m -> new Object[] { m, shortDescription(m) })
                 .toArray(Object[][]::new);
-        }
     }
 
     /**
      * Using publicLookup, attempt to use unreflect to obtain a method handle to a
      * caller sensitive method.
@@ -394,14 +399,15 @@
     }
 
     // -- supporting methods --
 
     /**
-     * Returns a stream of all caller sensitive methods on public classes in packages
+     * Returns a List of all caller sensitive methods on public classes in packages
      * exported by a named module.
+     * Returns a List instead of a stream so the ModuleReader can be closed before returning.
      */
-    static Stream<Method> callerSensitiveMethods(Module module) {
+    static List<Method> callerSensitiveMethods(Module module) {
         assert module.isNamed();
         ModuleReference mref = module.getLayer().configuration()
                 .findModule(module.getName())
                 .orElseThrow(() -> new RuntimeException())
                 .reference();
@@ -417,11 +423,11 @@
                     .filter(cn -> module.isExported(packageName(cn)))
                     .map(cn -> Class.forName(module, cn))
                     .filter(refc -> refc != null
                                     && Modifier.isPublic(refc.getModifiers()))
                     .map(refc -> callerSensitiveMethods(refc))
-                    .flatMap(List::stream);
+                    .flatMap(List::stream).toList();
         } catch (IOException ioe) {
             throw new UncheckedIOException(ioe);
         }
     }
 
diff a/test/jdk/java/lang/invoke/common/test/java/lang/invoke/lib/InstructionHelper.java b/test/jdk/java/lang/invoke/common/test/java/lang/invoke/lib/InstructionHelper.java
--- a/test/jdk/java/lang/invoke/common/test/java/lang/invoke/lib/InstructionHelper.java
+++ b/test/jdk/java/lang/invoke/common/test/java/lang/invoke/lib/InstructionHelper.java
@@ -23,19 +23,20 @@
 
 package test.java.lang.invoke.lib;
 
 import java.lang.classfile.ClassBuilder;
 import java.lang.classfile.ClassFile;
+import java.lang.classfile.CodeBuilder;
 import java.lang.classfile.TypeKind;
 
 import java.lang.constant.*;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
-import java.util.concurrent.atomic.AtomicInteger;
-
 import static java.lang.invoke.MethodType.fromMethodDescriptorString;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
 
 public class InstructionHelper {
 
     static final AtomicInteger COUNT = new AtomicInteger();
 
@@ -133,6 +134,29 @@
     public static ClassDesc classDesc(Class<?> c, String suffix) {
         StringBuilder sb = new StringBuilder(c.descriptorString());
         String classDescStr = sb.insert(sb.length() - 1, suffix).toString();
         return ClassDesc.ofDescriptor(classDescStr);
     }
+
+
+    public static MethodHandle buildMethodHandle(MethodHandles.Lookup l, String methodName, MethodType methodType, Consumer<? super CodeBuilder> builder) {
+        ClassDesc genClassDesc = classDesc(l.lookupClass(), "$Code_" + COUNT.getAndIncrement());
+        return buildMethodHandle(l, genClassDesc, methodName, methodType, builder);
+    }
+
+    public static MethodHandle buildMethodHandle(MethodHandles.Lookup l, ClassDesc classDesc, String methodName, MethodType methodType, Consumer<? super CodeBuilder> builder) {
+        try {
+            byte[] bytes = ClassFile.of().build(classDesc, classBuilder -> {
+                classBuilder.withMethod(methodName,
+                                        MethodTypeDesc.ofDescriptor(methodType.toMethodDescriptorString()),
+                                        ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC,
+                                        methodBuilder -> methodBuilder.withCode(builder));
+            });
+            Class<?> clazz = l.defineClass(bytes);
+            return l.findStatic(clazz, methodName, methodType);
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw new RuntimeException("Failed to buildMethodHandle: " + methodName + " type " + methodType);
+        }
+    }
+
 }
diff a/test/jdk/java/lang/invoke/common/test/java/lang/invoke/lib/OldInstructionHelper.java b/test/jdk/java/lang/invoke/common/test/java/lang/invoke/lib/OldInstructionHelper.java
--- /dev/null
+++ b/test/jdk/java/lang/invoke/common/test/java/lang/invoke/lib/OldInstructionHelper.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2010, 2022, 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 test.java.lang.invoke.lib;
+
+import jdk.experimental.bytecode.BasicClassBuilder;
+import jdk.experimental.bytecode.BasicTypeHelper;
+import jdk.experimental.bytecode.BytePoolHelper;
+import jdk.experimental.bytecode.ClassBuilder;
+import jdk.experimental.bytecode.CodeBuilder;
+import jdk.experimental.bytecode.Flag;
+import jdk.experimental.bytecode.MethodBuilder;
+import jdk.experimental.bytecode.PoolHelper;
+import jdk.experimental.bytecode.TypedCodeBuilder;
+import jdk.experimental.bytecode.TypeHelper;
+import jdk.experimental.bytecode.TypeTag;
+
+import java.io.FileOutputStream;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import static java.lang.invoke.MethodType.fromMethodDescriptorString;
+import static java.lang.invoke.MethodType.methodType;
+
+/**
+ * 8308778: Temporarily keep the old bytecode API, but needs removing when Valhalla support is added to classfile API
+ */
+public class OldInstructionHelper {
+
+    static final BasicTypeHelper BTH = new BasicTypeHelper();
+
+    static final AtomicInteger COUNT = new AtomicInteger();
+
+    static String generateClassNameFromLookupClass(MethodHandles.Lookup l) {
+        return l.lookupClass().getCanonicalName().replace('.', '/') + "$Code_" + COUNT.getAndIncrement();
+    }
+
+    static BasicClassBuilder classBuilder(MethodHandles.Lookup l) {
+        String className = generateClassNameFromLookupClass(l);
+        return new BasicClassBuilder(className, 55, 0)
+                .withSuperclass("java/lang/Object")
+                .withMethod("<init>", "()V", M ->
+                        M.withFlags(Flag.ACC_PUBLIC)
+                                .withCode(TypedCodeBuilder::new, C ->
+                                        C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_()
+                                ));
+    }
+
+    public static MethodHandle invokedynamic(MethodHandles.Lookup l,
+                                      String name, MethodType type,
+                                      String bsmMethodName, MethodType bsmType,
+                                      Consumer<PoolHelper.StaticArgListBuilder<String, String, byte[]>> staticArgs) throws Exception {
+        byte[] byteArray = classBuilder(l)
+                .withMethod("m", type.toMethodDescriptorString(), M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new,
+                                          C -> {
+                                              for (int i = 0; i < type.parameterCount(); i++) {
+                                                  C.load(BTH.tag(cref(type.parameterType(i))), i);
+                                              }
+                                              C.invokedynamic(name, type.toMethodDescriptorString(),
+                                                              csym(l.lookupClass()), bsmMethodName, bsmType.toMethodDescriptorString(),
+                                                              staticArgs);
+                                              C.return_(BTH.tag(cref(type.returnType())));
+                                          }
+                                ))
+                .build();
+        Class<?> gc = l.defineClass(byteArray);
+        return l.findStatic(gc, "m", type);
+    }
+
+    public static MethodHandle ldcMethodHandle(MethodHandles.Lookup l,
+                                        int refKind, Class<?> owner, String name, MethodType type) throws Exception {
+        return ldc(l, MethodHandle.class,
+                   P -> P.putHandle(refKind, csym(owner), name, type.toMethodDescriptorString()));
+    }
+
+    public static MethodHandle ldcDynamicConstant(MethodHandles.Lookup l,
+                                                  String name, Class<?> type,
+                                                  String bsmMethodName, MethodType bsmType,
+                                                  Consumer<PoolHelper.StaticArgListBuilder<String, String, byte[]>> staticArgs) throws Exception {
+        return ldcDynamicConstant(l, name, type, l.lookupClass(), bsmMethodName, bsmType, staticArgs);
+    }
+
+    public static MethodHandle ldcDynamicConstant(MethodHandles.Lookup l,
+                                                  String name, Class<?> type,
+                                                  Class<?> bsmClass, String bsmMethodName, MethodType bsmType,
+                                                  Consumer<PoolHelper.StaticArgListBuilder<String, String, byte[]>> staticArgs) throws Exception {
+        return ldcDynamicConstant(l, name, cref(type), csym(bsmClass), bsmMethodName, bsmType.toMethodDescriptorString(), staticArgs);
+    }
+
+    public static MethodHandle ldcDynamicConstant(MethodHandles.Lookup l,
+                                                  String name, String type,
+                                                  String bsmMethodName, String bsmType,
+                                                  Consumer<PoolHelper.StaticArgListBuilder<String, String, byte[]>> staticArgs) throws Exception {
+        return ldcDynamicConstant(l, name, type, csym(l.lookupClass()), bsmMethodName, bsmType, staticArgs);
+    }
+
+    public static MethodHandle ldcDynamicConstant(MethodHandles.Lookup l,
+                                                  String name, String type,
+                                                  String bsmClass, String bsmMethodName, String bsmType,
+                                                  Consumer<PoolHelper.StaticArgListBuilder<String, String, byte[]>> staticArgs) throws Exception {
+        return ldc(l, type,
+                   P -> P.putDynamicConstant(name, type,
+                                             bsmClass, bsmMethodName, bsmType,
+                                             staticArgs));
+    }
+
+    public static MethodHandle ldc(MethodHandles.Lookup l,
+                            Class<?> type,
+                            Function<PoolHelper<String, String, byte[]>, Integer> poolFunc) throws Exception {
+        return ldc(l, cref(type), poolFunc);
+    }
+
+    public static MethodHandle ldc(MethodHandles.Lookup l,
+                                   String type,
+                                   Function<PoolHelper<String, String, byte[]>, Integer> poolFunc) throws Exception {
+        String methodType = "()" + type;
+        byte[] byteArray = classBuilder(l)
+                .withMethod("m", "()" + type, M ->
+                        M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
+                                .withCode(TypedCodeBuilder::new,
+                                          C -> {
+                                              C.ldc(null, (P, v) -> poolFunc.apply(P));
+                                              C.return_(BTH.tag(type));
+                                          }
+                                ))
+                .build();
+        Class<?> gc = l.defineClass(byteArray);
+        return l.findStatic(gc, "m", fromMethodDescriptorString(methodType, l.lookupClass().getClassLoader()));
+    }
+
+    public static String csym(Class<?> c) {
+        return c.getCanonicalName().replace('.', '/');
+    }
+
+    public static String cref(Class<?> c) {
+        return methodType(c).toMethodDescriptorString().substring(2);
+    }
+
+
+    // loadCode(MethodHandles.Lookup, String, MethodType, Consumer<? super MethodHandleCodeBuilder<?>>) et al...
+
+    public static MethodHandle loadCode(MethodHandles.Lookup lookup, String methodName, MethodType type, Consumer<? super MethodHandleCodeBuilder<?>> builder) {
+        String className = generateClassNameFromLookupClass(lookup);
+        return loadCode(lookup, className, methodName, type, builder);
+    }
+
+    public static MethodHandle loadCode(MethodHandles.Lookup lookup, String className, String methodName, MethodType type, Consumer<? super MethodHandleCodeBuilder<?>> builder) {
+        String descriptor = type.toMethodDescriptorString();
+        return loadCode(lookup, className, methodName, descriptor, MethodHandleCodeBuilder::new,
+                    clazz -> {
+                        try {
+                            return lookup.findStatic(clazz, methodName, MethodType.fromMethodDescriptorString(descriptor, lookup.lookupClass().getClassLoader()));
+                        } catch (ReflectiveOperationException ex) {
+                            throw new IllegalStateException(ex);
+                        }
+                    },
+                    builder);
+    }
+
+    // Helper method to load code built with "buildCode()"
+    public static MethodHandle loadCodeBytes(MethodHandles.Lookup lookup, String methodName, MethodType type, byte[] byteCode) {
+        try {
+            Class<?> clazz = lookup.defineClass(byteCode);
+            return lookup.findStatic(clazz, methodName, type);
+        } catch (Throwable t) {
+            throw new RuntimeException("Failed to loadCodeBytes \"" + methodName + "\"", t);
+        }
+    }
+
+
+    private static <Z, C extends CodeBuilder<Class<?>, String, byte[], ?>> Z loadCode(
+            MethodHandles.Lookup lookup, String className, String methodName, String type,
+            Function<MethodBuilder<Class<?>, String, byte[]>, ? extends C> builderFunc,
+            Function<Class<?>, Z> resFunc, Consumer<? super C> builder) {
+        try {
+            byte[] byteArray = buildCode(lookup, className, methodName, type, builderFunc, builder);
+            Class<?> clazz = lookup.defineClass(byteArray);
+            return resFunc.apply(clazz);
+        } catch (Throwable e) {
+             throw new IllegalStateException(e);
+        }
+    }
+
+    public static byte[] buildCode(MethodHandles.Lookup lookup, String methodName, MethodType type, Consumer<? super MethodHandleCodeBuilder<?>> builder) {
+        String className = generateClassNameFromLookupClass(lookup);
+        return buildCode(lookup, className, methodName, type.toMethodDescriptorString(), MethodHandleCodeBuilder::new, builder);
+    }
+
+    public static <C extends CodeBuilder<Class<?>, String, byte[], ?>> byte[] buildCode(
+        MethodHandles.Lookup lookup, String className, String methodName, String type,
+            Function<MethodBuilder<Class<?>, String, byte[]>, ? extends C> builderFunc,
+            Consumer<? super C> builder) {
+
+                return new IsolatedMethodBuilder(className, lookup)
+                    .withSuperclass(Object.class)
+                    .withMajorVersion(66)
+                    .withMinorVersion(0)
+                    .withFlags(Flag.ACC_PUBLIC, Flag.ACC_IDENTITY)
+                    .withMethod(methodName, type, M ->
+                        M.withFlags(Flag.ACC_STATIC, Flag.ACC_PUBLIC)
+                            .withCode(builderFunc, builder)).build();
+
+    }
+
+    private static class IsolatedMethodBuilder extends ClassBuilder<Class<?>, String, IsolatedMethodBuilder> {
+
+        private static final Class<?> THIS_CLASS = new Object() { }.getClass();
+
+        private IsolatedMethodBuilder(String clazz, MethodHandles.Lookup lookup) {
+            super(new IsolatedMethodPoolHelper(clazz),
+                  new IsolatedMethodTypeHelper(lookup));
+            withThisClass(THIS_CLASS);
+        }
+
+        public Class<?> thisClass() {
+            return THIS_CLASS;
+        }
+
+        static String classToInternalName(Class<?> c) {
+            if (c.isArray()) {
+                return c.descriptorString();
+            }
+            return c.getName().replace('.', '/');
+        }
+
+        private static class IsolatedMethodTypeHelper implements TypeHelper<Class<?>, String> {
+
+            BasicTypeHelper basicTypeHelper = new BasicTypeHelper();
+            MethodHandles.Lookup lookup;
+
+            private IsolatedMethodTypeHelper(MethodHandles.Lookup lookup) {
+                this.lookup = lookup;
+            }
+
+            @Override
+            public String elemtype(String s) {
+                return basicTypeHelper.elemtype(s);
+            }
+
+            @Override
+            public String arrayOf(String s) {
+                return basicTypeHelper.arrayOf(s);
+            }
+
+            @Override
+            public Iterator<String> parameterTypes(String s) {
+                return basicTypeHelper.parameterTypes(s);
+            }
+
+            @Override
+            public String fromTag(TypeTag tag) {
+                return basicTypeHelper.fromTag(tag);
+            }
+
+            @Override
+            public String returnType(String s) {
+                return basicTypeHelper.returnType(s);
+            }
+
+            @Override
+            public String type(Class<?> aClass) {
+                return aClass.descriptorString();
+            }
+
+            @Override
+            public Class<?> symbol(String desc) {
+                try {
+                    if (desc.startsWith("[")) {
+                        return Class.forName(desc.replaceAll("/", "."), true, lookup.lookupClass().getClassLoader());
+                    } else {
+                        return Class.forName(basicTypeHelper.symbol(desc).replaceAll("/", "."), true, lookup.lookupClass().getClassLoader());
+                    }
+                } catch (ReflectiveOperationException ex) {
+                    throw new AssertionError(ex);
+                }
+            }
+
+            @Override
+            public TypeTag tag(String s) {
+                return basicTypeHelper.tag(s);
+            }
+
+            @Override
+            public Class<?> symbolFrom(String s) {
+                return symbol(s);
+            }
+
+            @Override
+            public String commonSupertype(String t1, String t2) {
+                return basicTypeHelper.commonSupertype(t1, t2);
+            }
+
+            @Override
+            public String nullType() {
+                return basicTypeHelper.nullType();
+            }
+        }
+
+        private static class IsolatedMethodPoolHelper extends BytePoolHelper<Class<?>, String> {
+            final String clazz;
+
+            private IsolatedMethodPoolHelper(String clazz) {
+                super(c -> from(c, clazz), s->s);
+                this.clazz = clazz;
+            }
+
+            static String from(Class<?> c, String clazz) {
+                return c == THIS_CLASS ? clazz.replace('.', '/')
+                                       : classToInternalName(c);
+            }
+        }
+
+        @Override
+        public byte[] build() {
+            return super.build();
+        }
+    }
+
+    public static class MethodHandleCodeBuilder<T extends MethodHandleCodeBuilder<T>> extends TypedCodeBuilder<Class<?>, String, byte[], T> {
+
+        BasicTypeHelper basicTypeHelper = new BasicTypeHelper();
+
+        public MethodHandleCodeBuilder(jdk.experimental.bytecode.MethodBuilder<Class<?>, String, byte[]> methodBuilder) {
+            super(methodBuilder);
+        }
+
+        TypeTag getTagType(String s) {
+            return basicTypeHelper.tag(s);
+        }
+
+        public T ifcmp(String s, CondKind cond, CharSequence label) {
+            return super.ifcmp(getTagType(s), cond, label);
+        }
+
+        public T return_(String s) {
+            return super.return_(getTagType(s));
+        }
+    }
+
+
+
+}
diff a/test/jdk/java/lang/invoke/condy/CondyNestedResolution.jcod b/test/jdk/java/lang/invoke/condy/CondyNestedResolution.jcod
--- a/test/jdk/java/lang/invoke/condy/CondyNestedResolution.jcod
+++ b/test/jdk/java/lang/invoke/condy/CondyNestedResolution.jcod
@@ -185,11 +185,11 @@
     Method #84 #60; // #82     at 0x0416
     Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"; // #83     at 0x041B
     class #72; // #84     at 0x04C5
   } // Constant Pool
 
-  0x0000; // access [ ]
+  0x0020; // access [ ]
   #84;// this_cpx
   #51;// super_cpx
 
   [0] { // Interfaces
   } // Interfaces
diff a/test/jdk/java/lang/invoke/condy/CondyNestedTest_Code.jcod b/test/jdk/java/lang/invoke/condy/CondyNestedTest_Code.jcod
--- a/test/jdk/java/lang/invoke/condy/CondyNestedTest_Code.jcod
+++ b/test/jdk/java/lang/invoke/condy/CondyNestedTest_Code.jcod
@@ -92,11 +92,11 @@
     InvokeDynamic 2s #62; // #63    
     InvokeDynamic 1s #62; // #64    
     Utf8 "BootstrapMethods"; // #65    
   } // Constant Pool
 
-  0x0000; // access
+  0x0020; // access
   #17;// this_cpx
   #2;// super_cpx
 
   [] { // Interfaces
   } // Interfaces
@@ -141,11 +141,11 @@
           };
           [] { // Traps
           } // end Traps
           [] { // Attributes
             Attr(#29) { // StackMapTable
-              [] { // 
+              [] { //
                 252b, 17, []z{O,9}; // append_frame 1
                 9b; // same_frame
                 9b; // same_frame
               }
             } // end StackMapTable
@@ -169,11 +169,11 @@
           };
           [] { // Traps
           } // end Traps
           [] { // Attributes
             Attr(#29) { // StackMapTable
-              [] { // 
+              [] { //
                 22b; // same_frame
               }
             } // end StackMapTable
           } // Attributes
         } // end Code
diff a/test/jdk/java/lang/invoke/defineHiddenClass/BasicTest.java b/test/jdk/java/lang/invoke/defineHiddenClass/BasicTest.java
--- a/test/jdk/java/lang/invoke/defineHiddenClass/BasicTest.java
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/BasicTest.java
@@ -39,10 +39,11 @@
 import java.lang.invoke.MethodHandles.Lookup;
 
 import static java.lang.invoke.MethodHandles.lookup;
 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
 
+import java.lang.reflect.AccessFlag;
 import java.lang.reflect.Array;
 import java.lang.reflect.Method;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -112,12 +113,12 @@
         // sanity check
         Class<?> c = t.getClass();
         Class<?>[] intfs = c.getInterfaces();
         assertTrue(c.isHidden());
         assertFalse(c.isPrimitive());
-        assertTrue(intfs.length == 1);
-        assertTrue(intfs[0] == HiddenTest.class);
+        assertTrue(intfs.length == 1 || intfs.length == 2);
+        assertTrue(intfs[0] == HiddenTest.class || (intfs.length == 2 && intfs[1] == HiddenTest.class));
         assertTrue(c.getCanonicalName() == null);
 
         String hcName = "HiddenClass";
         String hcSuffix = "0x[0-9a-f]+";
         assertTrue(c.getName().matches(hcName + "/" + hcSuffix));
@@ -244,13 +245,13 @@
     }
 
     @DataProvider(name = "emptyClasses")
     private Object[][] emptyClasses() {
         return new Object[][] {
-                new Object[] { "EmptyHiddenSynthetic", ACC_SYNTHETIC },
-                new Object[] { "EmptyHiddenEnum", ACC_ENUM },
-                new Object[] { "EmptyHiddenAbstractClass", ACC_ABSTRACT },
+                new Object[] { "EmptyHiddenSynthetic", ACC_SYNTHETIC | ACC_IDENTITY },
+                new Object[] { "EmptyHiddenEnum", ACC_ENUM | ACC_IDENTITY },
+                new Object[] { "EmptyHiddenAbstractClass", ACC_ABSTRACT | ACC_IDENTITY },
                 new Object[] { "EmptyHiddenInterface", ACC_ABSTRACT|ACC_INTERFACE },
                 new Object[] { "EmptyHiddenAnnotation", ACC_ANNOTATION|ACC_ABSTRACT|ACC_INTERFACE },
         };
     }
 
@@ -262,27 +263,27 @@
      * enum class containing constants of its type should not be a hidden
      * class.
      */
     @Test(dataProvider = "emptyClasses")
     public void emptyHiddenClass(String name, int accessFlags) throws Exception {
-        byte[] bytes = (accessFlags == ACC_ENUM) ? classBytes(name, Enum.class, accessFlags)
+        byte[] bytes = (accessFlags == (ACC_ENUM | ACC_IDENTITY)) ? classBytes(name, Enum.class, accessFlags)
                                                  : classBytes(name, accessFlags);
         Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
         switch (accessFlags) {
-            case ACC_SYNTHETIC:
+            case (ACC_SYNTHETIC | ACC_IDENTITY):
                 assertTrue(hc.isSynthetic());
                 assertFalse(hc.isEnum());
                 assertFalse(hc.isAnnotation());
                 assertFalse(hc.isInterface());
                 break;
-            case ACC_ENUM:
+            case (ACC_ENUM | ACC_IDENTITY):
                 assertFalse(hc.isSynthetic());
                 assertTrue(hc.isEnum());
                 assertFalse(hc.isAnnotation());
                 assertFalse(hc.isInterface());
                 break;
-            case ACC_ABSTRACT:
+            case ACC_ABSTRACT | ACC_IDENTITY:
                 assertFalse(hc.isSynthetic());
                 assertFalse(hc.isEnum());
                 assertFalse(hc.isAnnotation());
                 assertFalse(hc.isInterface());
                 break;
@@ -300,11 +301,11 @@
                 break;
             default:
                 throw new IllegalArgumentException("unexpected access flag: " + accessFlags);
         }
         assertTrue(hc.isHidden());
-        assertTrue(hc.getModifiers() == (ACC_PUBLIC|accessFlags));
+        assertEquals(hc.getModifiers(), (ACC_PUBLIC|accessFlags));
         assertFalse(hc.isLocalClass());
         assertFalse(hc.isMemberClass());
         assertFalse(hc.isAnonymousClass());
         assertFalse(hc.isArray());
     }
diff a/test/jdk/java/lang/invoke/defineHiddenClass/HiddenNestmateTest.java b/test/jdk/java/lang/invoke/defineHiddenClass/HiddenNestmateTest.java
--- a/test/jdk/java/lang/invoke/defineHiddenClass/HiddenNestmateTest.java
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/HiddenNestmateTest.java
@@ -166,11 +166,11 @@
 
     private static byte[] classBytes(String classname) {
         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
         MethodVisitor mv;
 
-        cw.visit(V12, ACC_FINAL, classname, null, "java/lang/Object", null);
+        cw.visit(V12, ACC_FINAL|ACC_IDENTITY, classname, null, "java/lang/Object", null);
 
         {
             mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
             mv.visitCode();
             mv.visitVarInsn(ALOAD, 0);
diff a/test/jdk/java/lang/invoke/defineHiddenClass/HiddenValueClass.java b/test/jdk/java/lang/invoke/defineHiddenClass/HiddenValueClass.java
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/HiddenValueClass.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @library /test/lib
+ * @enablePreview
+ * @run testng/othervm HiddenValueClass
+ * @summary Test that a value class can be defined as a hidden class
+ */
+
+import java.lang.invoke.MethodHandles;
+import static java.lang.invoke.MethodType.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import jdk.test.lib.Utils;
+
+import org.testng.annotations.Test;
+
+public class HiddenValueClass {
+    private static final Path CLASSES_DIR = Paths.get(Utils.TEST_CLASSES);
+
+    /*
+     * Defines ValueImpl as a hidden class.
+     */
+    @Test
+    public void hiddenValueClass() throws Throwable {
+        byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("ValueImpl.class"));
+        var lookup = MethodHandles.lookup().defineHiddenClass(bytes, false);
+        var mh = lookup.findConstructor(lookup.lookupClass(), methodType(void.class))
+                                    .asType(methodType(Runnable.class));
+        Runnable r = (Runnable)mh.invokeExact();
+        r.run();
+    }
+}
+
+value class ValueImpl implements Runnable {
+    public void run() {}
+}
diff a/test/jdk/java/lang/reflect/AccessFlag/BasicAccessFlagTest.java b/test/jdk/java/lang/reflect/AccessFlag/BasicAccessFlagTest.java
--- a/test/jdk/java/lang/reflect/AccessFlag/BasicAccessFlagTest.java
+++ b/test/jdk/java/lang/reflect/AccessFlag/BasicAccessFlagTest.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 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.
@@ -21,12 +21,15 @@
  * questions.
  */
 
 /*
  * @test
- * @bug 8266670 8293626
+ * @bug 8266670 8281463 8293626
  * @summary Basic tests of AccessFlag
+ * @modules java.base/jdk.internal.misc
+ * @run main/othervm --enable-preview BasicAccessFlagTest
+ * @run main BasicAccessFlagTest
  */
 
 import java.lang.reflect.AccessFlag;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
@@ -34,10 +37,13 @@
 import java.util.Map;
 import java.util.LinkedHashMap;
 import java.util.HashSet;
 import java.util.Set;
 
+import jdk.internal.misc.PreviewFeatures;
+
+
 public class BasicAccessFlagTest {
     public static void main(String... args) throws Exception {
         testSourceModifiers();
         testMaskOrdering();
         testDisjoint();
@@ -110,10 +116,12 @@
                 continue;
             }
 
             Set<AccessFlag.Location> locations = new HashSet<>();
             for (var accessFlag : value) {
+                if (accessFlag.equals(AccessFlag.SUPER))
+                    continue;       // SUPER is defined to overlap with IDENTITY
                 for (var location : accessFlag.locations()) {
                     boolean added = locations.add(location);
                     if (!added) {
                         reportError(location, accessFlag,
                                     entry.getKey(), value);
@@ -139,11 +147,13 @@
         for (var accessFlag : AccessFlag.values()) {
             Set<AccessFlag> expectedSet = EnumSet.of(accessFlag);
             for (var location : accessFlag.locations()) {
                 Set<AccessFlag> computedSet =
                     AccessFlag.maskToAccessFlags(accessFlag.mask(), location);
-                if (!expectedSet.equals(computedSet)) {
+                if (!computedSet.containsAll(expectedSet)) {
+                    System.out.println("expected: " + expectedSet);
+                    System.out.println("computed: " + computedSet);
                     throw new RuntimeException("Bad set computation on " +
                                                accessFlag + ", " + location);
                 }
             }
         }
diff a/test/jdk/java/lang/reflect/AccessFlag/ClassAccessFlagPreviewTest.java b/test/jdk/java/lang/reflect/AccessFlag/ClassAccessFlagPreviewTest.java
--- /dev/null
+++ b/test/jdk/java/lang/reflect/AccessFlag/ClassAccessFlagPreviewTest.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2022, 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
+ * @bug 8266670 8291734 8296743
+ * @summary Test expected AccessFlag's on classes.
+ * @modules java.base/jdk.internal.misc
+ * @enablePreview
+ * @run main ClassAccessFlagPreviewTest
+ */
+
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import jdk.internal.misc.PreviewFeatures;
+
+/*
+ * Class access flags that can directly or indirectly declared in
+ * source include:
+ * public, private, protected, static, final, interface, abstract,
+ * annotation, enum.
+ *
+ * Additionally, the access flags super and synthetic cannot be
+ * explicitly applied.
+ *
+ * This test is written on top of the facilities of core reflection.
+ *
+ * Note that core reflection does not offer a supported mechanism to
+ * return the Class object created from a module-info.class
+ * file. Therefore, this test does not attempt to probe the setting of
+ * that access flag.
+ */
+@ExpectedClassFlags(value = "[PUBLIC, FINAL, SUPER]",
+        preview = "[PUBLIC, FINAL, IDENTITY]")
+public final class ClassAccessFlagPreviewTest {
+    public static void main(String... args) {
+        // Top-level and auxiliary classes; i.e. non-inner classes
+        Class<?>[] testClasses = {
+            ClassAccessFlagPreviewTest.class,
+            TestInterface.class,
+            TestFinalClass.class,
+            TestAbstractClass.class,
+            TestAbstractValueClass.class,
+            TestPrivateAbstractClass.class,
+            TestPrivateAbstractValueClass.class,
+            StaticTestInterface.class,
+            TestMarkerAnnotation.class,
+            ExpectedClassFlags.class,
+            TestOuterEnum.class
+        };
+        checkClasses(testClasses);
+
+        // Nested classes of ClassAccessFlagPreviewTest
+        checkClasses(ClassAccessFlagPreviewTest.class.getDeclaredClasses());
+
+        checkPrimitives();
+        checkArrays();
+    }
+
+    private static void checkClasses(Class<?>[] classes) {
+        for (var clazz : classes) {
+            checkClass(clazz);
+        }
+    }
+
+    private static void checkClass(Class<?> clazz) {
+        ExpectedClassFlags expected =
+            clazz.getAnnotation(ExpectedClassFlags.class);
+        if (expected != null) {
+            String actual = clazz.accessFlags().toString();
+            String expectedFlags = (PreviewFeatures.isEnabled() && !expected.preview().isEmpty())
+                    ? expected.preview() : expected.value();
+            if (!expectedFlags.equals(actual)) {
+                throw new RuntimeException("On " + clazz +
+                                           " expected " + expected +
+                                           " got " + actual);
+            }
+        }
+    }
+
+    private static void checkPrimitives() {
+        final Class<?>[] primitives = {
+            byte.class,
+            int.class,
+            long.class,
+            short.class,
+            char.class,
+            float.class,
+            double.class,
+            boolean.class,
+            void.class // same access flag rules
+        };
+
+        var expected = Set.of(AccessFlag.PUBLIC,
+                              AccessFlag.FINAL,
+                              AccessFlag.ABSTRACT);
+
+        for(var primClass : primitives) {
+            var accessFlags = primClass.accessFlags();
+            if (!accessFlags.equals(expected)) {
+                throw new RuntimeException("Unexpected flags on " +
+                                           primClass);
+            }
+        }
+    }
+
+    private static boolean containsAny(Set<AccessFlag> input,
+                                       Set<AccessFlag> test) {
+        var copy = new HashSet<>(input);
+        return copy.removeAll(test);
+    }
+
+    private static void checkArrays() {
+        Class<?>[] accessClasses = {
+            PublicInterface.class,
+            ProtectedInterface.class,
+            PrivateInterface.class,
+        };
+
+        for (var accessClass : accessClasses) {
+            AccessFlag accessLevel;
+            var flags = accessClass.accessFlags();
+            if (flags.contains(AccessFlag.PUBLIC))
+                accessLevel = AccessFlag.PUBLIC;
+            else if (flags.contains(AccessFlag.PROTECTED))
+                accessLevel = AccessFlag.PROTECTED;
+            else if (flags.contains(AccessFlag.PRIVATE))
+                accessLevel = AccessFlag.PRIVATE;
+            else
+                accessLevel = null;
+
+            var arrayClass = accessClass.arrayType();
+            // Access modifier must match on the array type
+            if (accessLevel != null) {
+                if (!arrayClass.accessFlags().contains(accessLevel)) {
+                    throw new RuntimeException("Mismatched access flags on " +
+                                               arrayClass);
+                }
+            } else {
+                if (containsAny(arrayClass.accessFlags(),
+                                Set.of(AccessFlag.PUBLIC,
+                                       AccessFlag.PROTECTED,
+                                       AccessFlag.PRIVATE))) {
+                    throw new RuntimeException("Unexpected access flags on " +
+                                               arrayClass);
+                }
+            }
+            // Verify IDENTITY, ABSTRACT, FINAL, and access mode
+            Set<AccessFlag> expected = new HashSet<>(4);
+            expected.add(AccessFlag.ABSTRACT);
+            expected.add(AccessFlag.FINAL);
+//            expected.add(AccessFlag.IDENTITY);  // NYI Pending: JDK-8294866
+            if (accessLevel != null)
+                expected.add(accessLevel);
+            if (!expected.equals(arrayClass.accessFlags())) {
+                throw new RuntimeException("Unexpected access flags for array: " + accessClass +
+                        ": actual: " + arrayClass.accessFlags() +
+                        ", expected: " + expected);
+            }
+        }
+
+    }
+
+    // inner classes and interfaces; possible flags on INNER_CLASS
+    // locations:
+    // PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, INTERFACE, ABSTRACT,
+    // SYNTHETIC, ANNOTATION, ENUM.
+    // Include cases for classes with identity, value modifier, or no modifier.
+
+    @ExpectedClassFlags("[PUBLIC, STATIC, INTERFACE, ABSTRACT]")
+    public      interface PublicInterface {}
+    @ExpectedClassFlags("[PROTECTED, STATIC, INTERFACE, ABSTRACT]")
+    protected   interface ProtectedInterface {}
+    @ExpectedClassFlags("[PRIVATE, STATIC, INTERFACE, ABSTRACT]")
+    private     interface PrivateInterface {}
+    @ExpectedClassFlags("[STATIC, INTERFACE, ABSTRACT]")
+    /*package*/ interface PackageInterface {}
+
+    @ExpectedClassFlags(value = "[FINAL]",
+            preview = "[FINAL, IDENTITY]")
+    /*package*/ final class TestFinalClass {}
+
+    @ExpectedClassFlags(value = "[ABSTRACT]",
+            preview = "[IDENTITY, ABSTRACT]")
+    /*package*/ abstract class TestAbstractClass {}
+
+    @ExpectedClassFlags(value = "[ABSTRACT]",
+            preview = "[ABSTRACT]")
+    /*package*/ abstract value class TestAbstractValueClass {}
+
+    @ExpectedClassFlags("[STATIC, INTERFACE, ABSTRACT, ANNOTATION]")
+    /*package*/ @interface TestMarkerAnnotation {}
+
+    @ExpectedClassFlags(value = "[PUBLIC, STATIC, FINAL, ENUM]",
+            preview = "[PUBLIC, STATIC, FINAL, IDENTITY, ENUM]")
+    public enum MetaSynVar {
+        QUUX;
+    }
+
+    // Is there is at least one special enum constant, the enum class
+    // itself is implicitly abstract rather than final.
+    @ExpectedClassFlags(value = "[PROTECTED, STATIC, ABSTRACT, ENUM]",
+            preview = "[PROTECTED, STATIC, IDENTITY, ABSTRACT, ENUM]")
+    protected enum MetaSynVar2 {
+        WOMBAT{
+            @Override
+            public int foo() {return 42;}
+        };
+        public abstract int foo();
+    }
+
+    @ExpectedClassFlags(value = "[PRIVATE, ABSTRACT]",
+            preview = "[PRIVATE, IDENTITY, ABSTRACT]")
+    private abstract class TestPrivateAbstractClass {}
+
+    @ExpectedClassFlags(value = "[PRIVATE, ABSTRACT]",
+            preview = "[PRIVATE, ABSTRACT]")
+    private abstract value class TestPrivateAbstractValueClass {}
+
+    @ExpectedClassFlags("[STATIC, INTERFACE, ABSTRACT]")
+    interface StaticTestInterface {}
+}
+
+@Retention(RetentionPolicy.RUNTIME)
+@ExpectedClassFlags("[INTERFACE, ABSTRACT, ANNOTATION]")
+@interface ExpectedClassFlags {
+    String value();
+    String preview() default "";
+}
+
+@ExpectedClassFlags("[INTERFACE, ABSTRACT]")
+interface TestInterface {}
+
+
+@ExpectedClassFlags(value="[FINAL, SUPER, ENUM]",
+        preview="[FINAL, IDENTITY, ENUM]")
+enum TestOuterEnum {
+    INSTANCE;
+}
diff a/test/jdk/java/lang/reflect/AccessFlag/ClassAccessFlagTest.java b/test/jdk/java/lang/reflect/AccessFlag/ClassAccessFlagTest.java
--- a/test/jdk/java/lang/reflect/AccessFlag/ClassAccessFlagTest.java
+++ b/test/jdk/java/lang/reflect/AccessFlag/ClassAccessFlagTest.java
@@ -23,16 +23,25 @@
 
 /*
  * @test
  * @bug 8266670 8291734 8296743
  * @summary Test expected AccessFlag's on classes.
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib ..
+ * @enablePreview
  */
 
+
+import jdk.internal.misc.PreviewFeatures;
+
 import java.lang.annotation.*;
 import java.lang.reflect.*;
 import java.util.*;
 
+import jtreg.SkippedException;
+
+
 /*
  * Class access flags that can directly or indirectly declared in
  * source include:
  * public, private, protected, static, final, interface, abstract,
  * annotation, enum.
@@ -48,10 +57,13 @@
  * that access flag.
  */
 @ExpectedClassFlags("[PUBLIC, FINAL, SUPER]")
 public final class ClassAccessFlagTest {
     public static void main(String... args) {
+        if (PreviewFeatures.isEnabled()) {
+            throw new SkippedException("Preview mode not supported");
+        }
         // Top-level and auxiliary classes; i.e. non-inner classes
         Class<?>[] testClasses = {
             ClassAccessFlagTest.class,
             TestInterface.class,
             ExpectedClassFlags.class,
diff a/test/jdk/java/lang/reflect/AccessFlag/SuperAccessFlagTest.java b/test/jdk/java/lang/reflect/AccessFlag/SuperAccessFlagTest.java
--- /dev/null
+++ b/test/jdk/java/lang/reflect/AccessFlag/SuperAccessFlagTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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 8301720
+ * @summary Test expected value of SUPER AccessFlag for pre-ValueClass .class file
+ * @compile -source 20 -target 20 SuperAccessFlagTest.java
+ * @comment  Cannot use --release 20 because the accessFlags() method is
+ *           not found in release 20; therefore -source and -target are used instead.
+ * @run main SuperAccessFlagTest
+ */
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/*
+ * Test expected value of ACC_SUPER access flag on an earlier release.
+ */
+@ExpectedClassFlags("[PUBLIC, SUPER]")
+public class SuperAccessFlagTest {
+    public static void main(String... args) {
+        checkClass(SuperAccessFlagTest.class);
+    }
+
+    private static void checkClass(Class<?> clazz) {
+        ExpectedClassFlags expected =
+                clazz.getAnnotation(ExpectedClassFlags.class);
+        if (expected != null) {
+            String actual = clazz.accessFlags().toString();
+            if (!expected.value().equals(actual)) {
+                throw new RuntimeException("On " + clazz +
+                        " expected " + expected.value() +
+                        " got " + actual);
+            }
+        }
+    }
+}
+
+@Retention(RetentionPolicy.RUNTIME)
+@ExpectedClassFlags("[INTERFACE, ABSTRACT, ANNOTATION]")
+@interface ExpectedClassFlags {
+    String value();
+}
diff a/test/jdk/java/lang/reflect/AccessFlag/VersionedLocationsTest.java b/test/jdk/java/lang/reflect/AccessFlag/VersionedLocationsTest.java
--- a/test/jdk/java/lang/reflect/AccessFlag/VersionedLocationsTest.java
+++ b/test/jdk/java/lang/reflect/AccessFlag/VersionedLocationsTest.java
@@ -49,11 +49,11 @@
  * PUBLIC       step
  * PRIVATE      step
  * PROTECTED    step
  * STATIC       step
  * FINAL        two-step
- * SUPER        invariant
+ * SUPER        step
  * OPEN         step
  * TRANSITIVE   step
  * SYNCHRONIZED invariant
  * STATIC_PHASE step
  * VOLATILE     invariant
@@ -85,11 +85,11 @@
      * Invariant access flags have the same set of locations for each
      * class file format version.
      */
     private static void testInvariantAccessFlags() {
         Set<AccessFlag> invariantAccessFlags =
-            Set.of(SUPER, SYNCHRONIZED, VOLATILE, TRANSIENT, NATIVE);
+            Set.of(SYNCHRONIZED, VOLATILE, TRANSIENT, NATIVE);
         for(var accessFlag : invariantAccessFlags) {
             Set<AccessFlag.Location> expected = accessFlag.locations();
 
             for(var cffv : ClassFileFormatVersion.values()) {
                 compareLocations(accessFlag.locations(), accessFlag, cffv);
@@ -113,10 +113,14 @@
 
             new StepFunctionTC(STATIC,
                                removeInnerClass(STATIC.locations()),
                                ClassFileFormatVersion.RELEASE_1),
 
+            new StepFunctionTC(SUPER,
+                               Set.of(AccessFlag.Location.CLASS),
+                               ClassFileFormatVersion.RELEASE_22),
+
             new StepFunctionTC(OPEN,
                                Set.of(),
                                ClassFileFormatVersion.RELEASE_9),
 
             new StepFunctionTC(TRANSITIVE,
diff a/test/jdk/java/lang/reflect/records/IsRecordTest.java b/test/jdk/java/lang/reflect/records/IsRecordTest.java
--- a/test/jdk/java/lang/reflect/records/IsRecordTest.java
+++ b/test/jdk/java/lang/reflect/records/IsRecordTest.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 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.
@@ -21,15 +21,16 @@
  * questions.
  */
 
 /*
  * @test
- * @bug 8255560
+ * @bug 8255560 8326879
  * @summary Class::isRecord should check that the current class is final and not abstract
  * @modules java.base/jdk.internal.org.objectweb.asm
  * @library /test/lib
  * @run testng/othervm IsRecordTest
+ * @run testng/othervm --enable-preview IsRecordTest
  * @run testng/othervm/java.security.policy=allPermissions.policy IsRecordTest
  */
 
 import java.io.IOException;
 import java.io.UncheckedIOException;
@@ -169,11 +170,11 @@
             access = access | Opcodes.ACC_FINAL;
         if (isAbstract)
             access = access | Opcodes.ACC_ABSTRACT;
 
         cw.visit(Opcodes.V16,
-                 access,
+                 access | Opcodes.ACC_IDENTITY,
                  className,
                  null,
                  superName,
                  null);
 
diff a/test/jdk/java/lang/reflect/records/RecordReflectionTest.java b/test/jdk/java/lang/reflect/records/RecordReflectionTest.java
--- a/test/jdk/java/lang/reflect/records/RecordReflectionTest.java
+++ b/test/jdk/java/lang/reflect/records/RecordReflectionTest.java
@@ -21,14 +21,15 @@
  * questions.
  */
 
 /*
  * @test
- * @bug 8235369 8235550 8247444
+ * @bug 8235369 8235550 8247444 8326879
  * @summary reflection test for records
  * @compile RecordReflectionTest.java
  * @run testng/othervm RecordReflectionTest
+ * @run testng/othervm --enable-preview RecordReflectionTest
  * @run testng/othervm/java.security.policy=allPermissions.policy RecordReflectionTest
  */
 
 import java.lang.annotation.*;
 import java.lang.reflect.*;
diff a/test/jdk/java/lang/runtime/ObjectMethodsTest.java b/test/jdk/java/lang/runtime/ObjectMethodsTest.java
--- a/test/jdk/java/lang/runtime/ObjectMethodsTest.java
+++ b/test/jdk/java/lang/runtime/ObjectMethodsTest.java
@@ -150,10 +150,17 @@
 
         assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "toString", C.EQUALS_DESC,    C.class, "x;y", C.ACCESSORS));
         assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "hashCode", C.TO_STRING_DESC, C.class, "x;y", C.ACCESSORS));
         assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "equals",   C.HASHCODE_DESC,  C.class, "x;y", C.ACCESSORS));
 
+        assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "toString", methodType(String.class, this.getClass()), C.class, "x;y", C.ACCESSORS));
+        assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "toString", C.TO_STRING_DESC, C.class, "x;y",
+                     new MethodHandle[]{
+                            MethodHandles.lookup().findGetter(C.class, "x", int.class),
+                            MethodHandles.lookup().findGetter(this.getClass(), "y", int.class),
+                     }));
+
         record NamePlusType(String mn, MethodType mt) {}
         List<NamePlusType> namePlusTypeList = List.of(
                 new NamePlusType("toString", C.TO_STRING_DESC),
                 new NamePlusType("equals", C.EQUALS_DESC),
                 new NamePlusType("hashCode", C.HASHCODE_DESC)
@@ -168,10 +175,13 @@
             assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, null,     npt.mt(), C.class, "x;y", C.ACCESSORS));
             assertThrows(NPE, () -> ObjectMethods.bootstrap(null, npt.mn(),     npt.mt(), C.class, "x;y", C.ACCESSORS));
         }
     }
 
+    // same field name and type as C::y
+    private int y;
+
     // Based on the ObjectMethods internal implementation
     private static int hashCombiner(int x, int y) {
         return x*31 + y;
     }
 }
diff a/test/jdk/java/util/Map/EntryHashCode.java b/test/jdk/java/util/Map/EntryHashCode.java
--- a/test/jdk/java/util/Map/EntryHashCode.java
+++ b/test/jdk/java/util/Map/EntryHashCode.java
@@ -49,10 +49,12 @@
         new Hashtable<>(),
         new IdentityHashMap<>(),
         new LinkedHashMap<>(),
         new TreeMap<>(),
         new WeakHashMap<>(),
+        new WeakHashMap<>(16, 0.75f, WeakHashMap.ValuePolicy.SOFT),
+        new WeakHashMap<>(16, 0.75f, WeakHashMap.ValuePolicy.STRONG),
         new ConcurrentHashMap<>(),
         new ConcurrentSkipListMap<>()
     };
 
     static {
diff a/test/jdk/javax/naming/module/src/test/test/StoreObject.ldap b/test/jdk/javax/naming/module/src/test/test/StoreObject.ldap
--- a/test/jdk/javax/naming/module/src/test/test/StoreObject.ldap
+++ b/test/jdk/javax/naming/module/src/test/test/StoreObject.ldap
@@ -659,6 +659,5 @@
 #        :   }
 #
 0000: 30 22 02 01 08 42 00 A0   1B 30 19 04 17 32 2E 31  0"...B...0...2.1
 0010: 36 2E 38 34 30 2E 31 2E   31 31 33 37 33 30 2E 33  6.840.1.113730.3
 0020: 2E 34 2E 32                                        .4.2
-
diff a/test/jdk/jdk/classfile/helpers/RebuildingTransformation.java b/test/jdk/jdk/classfile/helpers/RebuildingTransformation.java
--- a/test/jdk/jdk/classfile/helpers/RebuildingTransformation.java
+++ b/test/jdk/jdk/classfile/helpers/RebuildingTransformation.java
@@ -500,10 +500,11 @@
                     case ModuleResolutionAttribute a -> clb.with(ModuleResolutionAttribute.of(a.resolutionFlags()));
                     case ModuleTargetAttribute a -> clb.with(ModuleTargetAttribute.of(a.targetPlatform().stringValue()));
                     case NestHostAttribute a -> clb.with(NestHostAttribute.of(a.nestHost().asSymbol()));
                     case NestMembersAttribute a -> clb.with(NestMembersAttribute.ofSymbols(a.nestMembers().stream().map(ClassEntry::asSymbol).toArray(ClassDesc[]::new)));
                     case PermittedSubclassesAttribute a -> clb.with(PermittedSubclassesAttribute.ofSymbols(a.permittedSubclasses().stream().map(ClassEntry::asSymbol).toArray(ClassDesc[]::new)));
+                    case PreloadAttribute a -> clb.with(PreloadAttribute.ofSymbols(a.preloads().stream().map(ClassEntry::asSymbol).toArray(ClassDesc[]::new)));
                     case RecordAttribute a -> clb.with(RecordAttribute.of(a.components().stream().map(rci ->
                             RecordComponentInfo.of(rci.name().stringValue(), rci.descriptorSymbol(), rci.attributes().stream().mapMulti((rca, rcac) -> {
                                     switch(rca) {
                                         case RuntimeInvisibleAnnotationsAttribute riaa -> rcac.accept(RuntimeInvisibleAnnotationsAttribute.of(transformAnnotations(riaa.annotations())));
                                         case RuntimeInvisibleTypeAnnotationsAttribute ritaa -> rcac.accept(RuntimeInvisibleTypeAnnotationsAttribute.of(transformTypeAnnotations(ritaa.annotations(), null, null)));
diff a/test/jdk/jdk/jfr/event/runtime/TestSyncOnValueBasedClassEvent.java b/test/jdk/jdk/jfr/event/runtime/TestSyncOnValueBasedClassEvent.java
--- a/test/jdk/jdk/jfr/event/runtime/TestSyncOnValueBasedClassEvent.java
+++ b/test/jdk/jdk/jfr/event/runtime/TestSyncOnValueBasedClassEvent.java
@@ -37,16 +37,17 @@
  * @bug 8242263
  * @requires vm.hasJFR
  * @key jfr
  * @library /test/lib
  * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:DiagnoseSyncOnValueBasedClasses=2 jdk.jfr.event.runtime.TestSyncOnValueBasedClassEvent
+ * @run main/othervm --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:DiagnoseSyncOnValueBasedClasses=2 jdk.jfr.event.runtime.TestSyncOnValueBasedClassEvent
  */
 public class TestSyncOnValueBasedClassEvent {
     static final String EVENT_NAME = EventNames.SyncOnValueBasedClass;
     static String[] classesWanted = {"java/lang/Character", "java/lang/Boolean", "java/lang/Byte", "java/lang/Short",
                                      "java/lang/Integer", "java/lang/Long", "java/lang/Float", "java/lang/Double",
-                                     "java/time/Duration", "java/util/OptionalInt", "java/lang/Runtime$Version"};
+                                     "java/lang/Runtime$Version"};
     static List<Object> testObjects = new ArrayList<Object>();
     static Integer counter = 0;
 
     private static void initTestObjects() {
         testObjects.add(Character.valueOf('H'));
@@ -55,12 +56,10 @@
         testObjects.add(Short.valueOf((short)0x4000));
         testObjects.add(Integer.valueOf(0x40000000));
         testObjects.add(Long.valueOf(0x4000000000000000L));
         testObjects.add(Float.valueOf(1.20f));
         testObjects.add(Double.valueOf(1.2345));
-        testObjects.add(Duration.ofMillis(5));
-        testObjects.add(OptionalInt.of(10));
         testObjects.add(Runtime.version());
     }
 
     public static void main(String[] args) throws Throwable {
         initTestObjects();
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AbstractBuilder.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AbstractBuilder.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AbstractBuilder.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+/**
+ * Base builder.
+ *
+ * @param <S> the type of the symbol representation
+ * @param <T> the type of type descriptors representation
+ * @param <E> the type of pool entries
+ * @param <D> the type of this builder
+ */
+public class AbstractBuilder<S, T, E, D extends AbstractBuilder<S, T, E, D>> {
+    /**
+     * The helper to build the constant pool.
+     */
+    protected final PoolHelper<S, T, E> poolHelper;
+
+    /**
+     * The helper to use to manipulate type descriptors.
+     */
+    protected final TypeHelper<S, T> typeHelper;
+
+    /**
+     * Create a builder.
+     *
+     * @param poolHelper the helper to build the constant pool
+     * @param typeHelper the helper to use to manipulate type descriptors
+     */
+    AbstractBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
+        this.poolHelper = poolHelper;
+        this.typeHelper = typeHelper;
+    }
+
+    @SuppressWarnings("unchecked")
+    D thisBuilder() {
+        return (D) this;
+    }
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AnnotationsBuilder.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AnnotationsBuilder.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AnnotationsBuilder.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+import java.util.function.Consumer;
+import java.util.function.ToIntBiFunction;
+
+public class AnnotationsBuilder<S, T, E> extends AbstractBuilder<S, T, E, AnnotationsBuilder<S, T, E>> {
+
+    GrowableByteBuffer annoAttribute;
+    int nannos;
+
+    AnnotationsBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
+        super(poolHelper, typeHelper);
+        this.annoAttribute = new GrowableByteBuffer();
+        annoAttribute.writeChar(0);
+    }
+
+    public enum Kind {
+        RUNTIME_VISIBLE,
+        RUNTIME_INVISIBLE;
+    }
+
+    enum Tag {
+        B('B'),
+        C('C'),
+        D('D'),
+        F('F'),
+        I('I'),
+        J('J'),
+        S('S'),
+        Z('Z'),
+        STRING('s'),
+        ENUM('e'),
+        CLASS('c'),
+        ANNO('@'),
+        ARRAY('[');
+
+        char tagChar;
+
+        Tag(char tagChar) {
+            this.tagChar = tagChar;
+        }
+    }
+
+    AnnotationsBuilder<S, T, E> withAnnotation(T annoType, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
+        annoAttribute.writeChar(poolHelper.putType(annoType));
+        int offset = annoAttribute.offset;
+        annoAttribute.writeChar(0);
+        if (annotationBuilder != null) {
+            AnnotationElementBuilder _builder = new AnnotationElementBuilder();
+            int nelems = _builder.withElements(annotationBuilder);
+            patchCharAt(offset, nelems);
+        }
+        nannos++;
+        return this;
+    }
+
+    byte[] build() {
+        patchCharAt(0, nannos);
+        return annoAttribute.bytes();
+    }
+
+    private void patchCharAt(int offset, int newChar) {
+        int prevOffset = annoAttribute.offset;
+        try {
+            annoAttribute.offset = offset;
+            annoAttribute.writeChar(newChar);
+        } finally {
+            annoAttribute.offset = prevOffset;
+        }
+    }
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    static Consumer NO_BUILDER =
+            new Consumer() {
+                @Override
+                public void accept(Object o) {
+                    //do nothing
+                }
+            };
+
+    public class AnnotationElementBuilder {
+
+        int nelems;
+
+        public AnnotationElementBuilder withString(String name, String s) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writeStringValue(s);
+            return this;
+        }
+
+        private void writeStringValue(String s) {
+            annoAttribute.writeByte(Tag.STRING.tagChar);
+            annoAttribute.writeChar(poolHelper.putUtf8(s));
+            nelems++;
+        }
+
+        public AnnotationElementBuilder withClass(String name, T s) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writeClassValue(s);
+            return this;
+        }
+
+        private void writeClassValue(T s) {
+            annoAttribute.writeByte(Tag.CLASS.tagChar);
+            annoAttribute.writeChar(poolHelper.putType(s));
+            nelems++;
+        }
+
+        public AnnotationElementBuilder withEnum(String name, T enumType, int constant) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writeEnumValue(enumType, constant);
+            return this;
+        }
+
+        private void writeEnumValue(T enumType, int constant) {
+            annoAttribute.writeByte(Tag.ENUM.tagChar);
+            annoAttribute.writeChar(poolHelper.putType(enumType));
+            annoAttribute.writeChar(constant);
+            nelems++;
+        }
+
+        public AnnotationElementBuilder withAnnotation(String name, T annoType, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writeAnnotationValue(annoType, annotationBuilder);
+            return this;
+        }
+
+        private void writeAnnotationValue(T annoType, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
+            annoAttribute.writeByte(Tag.ANNO.tagChar);
+            annoAttribute.writeChar(poolHelper.putType(annoType));
+            int offset = annoAttribute.offset;
+            annoAttribute.writeChar(0);
+            int nelems = withNestedElements(annotationBuilder);
+            patchCharAt(offset, nelems);
+            this.nelems++;
+        }
+
+        public AnnotationElementBuilder withPrimitive(String name, char c) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writePrimitiveValue(Tag.C, (int)c, PoolHelper::putInt);
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitive(String name, short s) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writePrimitiveValue(Tag.S, (int)s, PoolHelper::putInt);
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitive(String name, byte b) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writePrimitiveValue(Tag.B, (int)b, PoolHelper::putInt);
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitive(String name, int i) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writePrimitiveValue(Tag.I, i, PoolHelper::putInt);
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitive(String name, float f) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writePrimitiveValue(Tag.F, f, PoolHelper::putFloat);
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitive(String name, long l) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writePrimitiveValue(Tag.J, l, PoolHelper::putLong);
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitive(String name, double d) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writePrimitiveValue(Tag.D, d, PoolHelper::putDouble);
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitive(String name, boolean b) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            writePrimitiveValue(Tag.Z, b ? 1 : 0, PoolHelper::putInt);
+            return this;
+        }
+
+        private <Z> void writePrimitiveValue(Tag tag, Z value, ToIntBiFunction<PoolHelper<S, T, E>, Z> poolFunc) {
+            annoAttribute.writeByte(tag.tagChar);
+            annoAttribute.writeChar(poolFunc.applyAsInt(poolHelper, value));
+            nelems++;
+        }
+
+        AnnotationElementBuilder withStrings(String name, String... ss) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(ss.length);
+            for (String s : ss) {
+                writeStringValue(s);
+            }
+            return this;
+        }
+
+        @SuppressWarnings("unchecked")
+        AnnotationElementBuilder withClasses(String name, T... cc) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(cc.length);
+            for (T c : cc) {
+                writeClassValue(c);
+            }
+            return this;
+        }
+
+        AnnotationElementBuilder withEnums(String name, T enumType, int... constants) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(constants.length);
+            for (int c : constants) {
+                writeEnumValue(enumType, c);
+            }
+            return this;
+        }
+
+        @SuppressWarnings("unchecked")
+        public AnnotationElementBuilder withAnnotations(String name, T annoType, Consumer<? super AnnotationElementBuilder>... annotationBuilders) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(annotationBuilders.length);
+            for (Consumer<? super AnnotationElementBuilder> annotationBuilder : annotationBuilders) {
+                writeAnnotationValue(annoType, annotationBuilder);
+            }
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitives(String name, char... cc) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(cc.length);
+            for (char c : cc) {
+                writePrimitiveValue(Tag.C, (int)c, PoolHelper::putInt);
+            }
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitives(String name, short... ss) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(ss.length);
+            for (short s : ss) {
+                writePrimitiveValue(Tag.S, (int)s, PoolHelper::putInt);
+            }
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitives(String name, byte... bb) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(bb.length);
+            for (byte b : bb) {
+                writePrimitiveValue(Tag.B, (int)b, PoolHelper::putInt);
+            }
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitives(String name, int... ii) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(ii.length);
+            for (int i : ii) {
+                writePrimitiveValue(Tag.I, i,  PoolHelper::putInt);
+            }
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitives(String name, float... ff) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(ff.length);
+            for (float f : ff) {
+                writePrimitiveValue(Tag.F, f, PoolHelper::putFloat);
+            }
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitives(String name, long... ll) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(ll.length);
+            for (long l : ll) {
+                writePrimitiveValue(Tag.J, l, PoolHelper::putLong);
+            }
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitives(String name, double... dd) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(dd.length);
+            for (double d : dd) {
+                writePrimitiveValue(Tag.D, d, PoolHelper::putDouble);
+            }
+            return this;
+        }
+
+        public AnnotationElementBuilder withPrimitives(String name, boolean... bb) {
+            annoAttribute.writeChar(poolHelper.putUtf8(name));
+            annoAttribute.writeChar(bb.length);
+            for (boolean b : bb) {
+                writePrimitiveValue(Tag.Z, b ? 1 : 0, PoolHelper::putInt);
+            }
+            return this;
+        }
+
+        int withNestedElements(Consumer<? super AnnotationElementBuilder> annotationBuilder) {
+            return withElements(new AnnotationElementBuilder(), annotationBuilder);
+        }
+
+        int withElements(Consumer<? super AnnotationElementBuilder> annotationBuilder) {
+            return withElements(this, annotationBuilder);
+        }
+
+        private int withElements(AnnotationElementBuilder builder, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
+            annotationBuilder.accept(builder);
+            return builder.nelems;
+        }
+    }
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AttributeBuilder.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AttributeBuilder.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AttributeBuilder.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+/**
+ * Base builder for attribute containing class file entities.
+ *
+ * @param <S> the type of the symbol representation
+ * @param <T> the type of type descriptors representation
+ * @param <E> the type of pool entries
+ * @param <D> the type of this builder
+ */
+ public class AttributeBuilder<S, T, E, D extends AttributeBuilder<S, T, E, D>>
+        extends AbstractBuilder<S, T, E, D> {
+
+     /**
+      * The number of attributes.
+      */
+    protected int nattrs;
+
+    /**
+     * The attributes represented as bytes.
+     */
+    protected GrowableByteBuffer attributes = new GrowableByteBuffer();
+
+    /**
+     * Create an attribute builder.
+     *
+     * @param poolHelper the helper to build the constant pool
+     * @param typeHelper the helper to use to manipulate type descriptors
+     */
+    public AttributeBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
+        super(poolHelper, typeHelper);
+    }
+
+    /**
+     * Add a class file Attribute.  Defined as:
+     * <pre>
+     * {@code   attribute_info {
+     *     u2 attribute_name_index;
+     *     u4 attribute_length;
+     *     u1 info[attribute_length];
+     *   }}
+     * </pre>
+     *
+     * @param name the attribute name
+     * @param bytes the bytes of the attribute info
+     * @return this builder, for chained calls
+     */
+    public D withAttribute(CharSequence name, byte[] bytes) {
+        attributes.writeChar(poolHelper.putUtf8(name));
+        attributes.writeInt(bytes.length);
+        attributes.writeBytes(bytes);
+        nattrs++;
+        return thisBuilder();
+    }
+
+    /**
+     * Add a class file Attribute, using a writer.  Defined as:
+     * <pre>
+     * {@code   attribute_info {
+     *     u2 attribute_name_index;
+     *     u4 attribute_length;
+     *     u1 info[attribute_length];
+     *   }}
+     * </pre>
+     *
+     * @param <Z> the type of the object representing the attribute
+     * @param name the attribute name
+     * @param attr the representation of the attribute
+     * @param attrWriter the writer which transform the attribute representation into bytes
+     * @return this builder, for chained calls
+     */
+    public <Z> D withAttribute(CharSequence name, Z attr, AttributeWriter<S, T, E, Z> attrWriter) {
+        attributes.writeChar(poolHelper.putUtf8(name));
+        int offset = attributes.offset;
+        attributes.writeInt(0);
+        attrWriter.write(attr, poolHelper, attributes);
+        int len = attributes.offset - offset - 4;
+        attributes.withOffset(offset, buf -> buf.writeInt(len));
+        nattrs++;
+        return thisBuilder();
+    }
+
+     /**
+      * Writer for transforming attribute representations to bytes
+      *
+      * @param <S> the type of symbol representation
+      * @param <T> the type of type descriptors representation
+      * @param <E> the type of pool entries
+      * @param <A> the type of the object representing the attribute
+      */
+    public interface AttributeWriter<S, T, E, A> {
+
+        /**
+         * Write an attribute representation into a byte buffer.
+         *
+         * @param attr the representation of the attribute
+         * @param poolHelper the constant pool helper
+         * @param buf the buffer to collect the bytes
+         */
+        void write(A attr, PoolHelper<S, T, E> poolHelper, GrowableByteBuffer buf);
+    }
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BasicClassBuilder.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BasicClassBuilder.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BasicClassBuilder.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+public class BasicClassBuilder extends ClassBuilder<String, String, BasicClassBuilder> {
+
+    public BasicClassBuilder(String thisClass, int majorVersion, int minorVersion) {
+        this();
+        withMinorVersion(minorVersion);
+        withMajorVersion(majorVersion);
+        withThisClass(thisClass);
+    }
+
+    public BasicClassBuilder(BytePoolHelper<String, String> poolHelper, TypeHelper<String, String> typeHelper) {
+        super(poolHelper, typeHelper);
+    }
+
+    public BasicClassBuilder() {
+        super(new BytePoolHelper<>(s->s, s->s), new BasicTypeHelper());
+    }
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BasicTypeHelper.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BasicTypeHelper.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BasicTypeHelper.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+import java.util.Iterator;
+
+/**
+ * Helper to create and manipulate type descriptors, where type descriptors
+ * are represented as JVM type descriptor strings and symbols are represented
+ * as name strings
+ */
+public class BasicTypeHelper implements TypeHelper<String, String> {
+
+    @Override
+    public String elemtype(String s) {
+        if (!s.startsWith("[")) {
+            throw new IllegalStateException();
+        }
+        return s.substring(1);
+    }
+
+    @Override
+    public String arrayOf(String s) {
+        return "[" + s;
+    }
+
+    @Override
+    public String type(String s) {
+        return "L" + s + ";";
+    }
+
+
+    @Override
+    public TypeTag tag(String s) {
+        switch (s.charAt(0)) {
+            case '[':
+            case 'L':
+                return TypeTag.A;
+            case 'B':
+            case 'C':
+            case 'Z':
+            case 'S':
+            case 'I':
+                return TypeTag.I;
+            case 'F':
+                return TypeTag.F;
+            case 'J':
+                return TypeTag.J;
+            case 'D':
+                return TypeTag.D;
+            case 'V':
+                return TypeTag.V;
+            default:
+                if (s == nullType()) {
+                    return TypeTag.A;
+                }
+                throw new IllegalStateException("Bad type: " + s);
+        }
+    }
+
+    @Override
+    public String nullType() {
+        // Needed in TypedCodeBuilder; ACONST_NULL pushes a 'null' onto the stack,
+        // and stack maps handle null differently
+        return "<null>";
+    }
+
+    @Override
+    public String commonSupertype(String t1, String t2) {
+        if (t1.equals(t2)) {
+            return t1;
+        } else {
+            try {
+                Class<?> c1 = from(t1);
+                Class<?> c2 = from(t2);
+                if (c1.isAssignableFrom(c2)) {
+                    return t1;
+                } else if (c2.isAssignableFrom(c1)) {
+                    return t2;
+                } else {
+                    return "Ljava/lang/Object;";
+                }
+            } catch (Exception e) {
+                return null;
+            }
+        }
+    }
+
+    public Class<?> from(String desc) throws ReflectiveOperationException {
+        if (desc.startsWith("[")) {
+            return Class.forName(desc.replaceAll("/", "."));
+        } else {
+            return Class.forName(symbol(desc).replaceAll("/", "."));
+        }
+    }
+
+    @Override
+    public Iterator<String> parameterTypes(String s) {
+        //TODO: gracefully non-method types
+        return new Iterator<String>() {
+            int ch = 1;
+
+            @Override
+            public boolean hasNext() {
+                return s.charAt(ch) != ')';
+            }
+
+            @Override
+            public String next() {
+                char curr = s.charAt(ch);
+                switch (curr) {
+                    case 'C':
+                    case 'B':
+                    case 'S':
+                    case 'I':
+                    case 'J':
+                    case 'F':
+                    case 'D':
+                    case 'Z':
+                        ch++;
+                        return String.valueOf(curr);
+                    case '[':
+                        ch++;
+                        return "[" + next();
+                    case 'L':
+                        StringBuilder builder = new StringBuilder();
+                        while (curr != ';') {
+                            builder.append(curr);
+                            curr = s.charAt(++ch);
+                        }
+                        builder.append(';');
+                        ch++;
+                        return builder.toString();
+                    default:
+                        throw new AssertionError("cannot parse string: " + s);
+                }
+            }
+        };
+    }
+
+    @Override
+    public String symbolFrom(String s) {
+        return s;
+    }
+
+    @Override
+    public String fromTag(TypeTag tag) {
+        return tag.name();
+    }
+
+    @Override
+    public String symbol(String type) {
+        return (type.startsWith("L") || type.startsWith("Q")) ? type.substring(1, type.length() - 1) : type;
+    }
+
+    @Override
+    public String returnType(String s) {
+        return s.substring(s.indexOf(')') + 1, s.length());
+    }
+
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BytePoolHelper.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BytePoolHelper.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BytePoolHelper.java
@@ -0,0 +1,758 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+import java.lang.invoke.MethodHandleInfo;
+import java.lang.invoke.MethodType;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.ToIntBiFunction;
+
+/**
+ * A helper for building and tracking constant pools whose entries are
+ * represented as byte arrays.
+ *
+ * @param <S> the type of the symbol representation
+ * @param <T> the type of type descriptors representation
+ */
+public class BytePoolHelper<S, T> implements PoolHelper<S, T, byte[]> {
+
+    GrowableByteBuffer pool = new GrowableByteBuffer();
+    GrowableByteBuffer bsm_attr = new GrowableByteBuffer();
+    //Map<PoolKey, PoolKey> indicesMap = new HashMap<>();
+    int currentIndex = 1;
+    int currentBsmIndex = 0;
+
+    KeyMap<PoolKey> entries = new KeyMap<>();
+    KeyMap<BsmKey> bootstraps = new KeyMap<>();
+    PoolKey key = new PoolKey();
+    BsmKey bsmKey = new BsmKey();
+
+    Function<S, String> symbolToString;
+    Function<T, String> typeToString;
+
+    public BytePoolHelper(Function<S, String> symbolToString, Function<T, String> typeToString) {
+        this.symbolToString = symbolToString;
+        this.typeToString = typeToString;
+    }
+
+    static class KeyMap<K extends AbstractKey<K>> {
+
+        @SuppressWarnings("unchecked")
+        K[] table = (K[])new AbstractKey<?>[0x10];
+        int nelems;
+
+        public void enter(K e) {
+            if (nelems * 3 >= (table.length - 1) * 2)
+                dble();
+            int hash = getIndex(e);
+            K old = table[hash];
+            if (old == null) {
+                nelems++;
+            }
+            e.next = old;
+            table[hash] = e;
+        }
+
+        protected K lookup(K other) {
+            K e = table[getIndex(other)];
+            while (e != null && !e.equals(other))
+                e = e.next;
+            return e;
+        }
+
+        /**
+         * Look for slot in the table.
+         * We use open addressing with double hashing.
+         */
+        int getIndex(K e) {
+            int hashMask = table.length - 1;
+            int h = e.hashCode();
+            int i = h & hashMask;
+            // The expression below is always odd, so it is guaranteed
+            // to be mutually prime with table.length, a power of 2.
+            int x = hashMask - ((h + (h >> 16)) << 1);
+            for (; ; ) {
+                K e2 = table[i];
+                if (e2 == null)
+                    return i;
+                else if (e.hash == e2.hash)
+                    return i;
+                i = (i + x) & hashMask;
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        private void dble() {
+            K[] oldtable = table;
+            table = (K[])new AbstractKey<?>[oldtable.length * 2];
+            int n = 0;
+            for (int i = oldtable.length; --i >= 0; ) {
+                K e = oldtable[i];
+                if (e != null) {
+                    table[getIndex(e)] = e;
+                    n++;
+                }
+            }
+            // We don't need to update nelems for shared inherited scopes,
+            // since that gets handled by leave().
+            nelems = n;
+        }
+    }
+
+    public static abstract class AbstractKey<K extends AbstractKey<K>> {
+        int hash;
+        int index = -1;
+        K next;
+
+        abstract K dup();
+
+        public abstract boolean equals(Object o);
+
+        @Override
+        public int hashCode() {
+            return hash;
+        }
+
+        void at(int index) {
+            this.index = index;
+        }
+    }
+
+    public static class PoolKey extends AbstractKey<PoolKey> {
+        PoolTag tag;
+        Object o1;
+        Object o2;
+        Object o3;
+        Object o4;
+        int size = -1;
+
+        void setUtf8(CharSequence s) {
+            tag = PoolTag.CONSTANT_UTF8;
+            o1 = s;
+            size = 1;
+            hash = tag.tag | (s.hashCode() << 1);
+        }
+
+        void setClass(String clazz) {
+            tag = PoolTag.CONSTANT_CLASS;
+            o1 = clazz;
+            size = 1;
+            hash = tag.tag | (clazz.hashCode() << 1);
+        }
+
+        void setNameAndType(CharSequence name, String type) {
+            tag = PoolTag.CONSTANT_NAMEANDTYPE;
+            o1 = name;
+            o2 = type;
+            size = 2;
+            hash = tag.tag | ((name.hashCode() | type.hashCode()) << 1);
+        }
+
+        void setMemberRef(PoolTag poolTag, String owner, CharSequence name, String type) {
+            tag = poolTag;
+            o1 = owner;
+            o2 = name;
+            o3 = type;
+            size = 3;
+            hash = tag.tag | ((owner.hashCode() | name.hashCode() | type.hashCode()) << 1);
+        }
+
+        void setInvokeDynamic(int bsmIndex, CharSequence name, String type) {
+            tag = PoolTag.CONSTANT_INVOKEDYNAMIC;
+            o1 = bsmIndex;
+            o2 = name;
+            o3 = type;
+            size = 3;
+            hash = tag.tag | ((bsmIndex | name.hashCode() | type.hashCode()) << 1);
+        }
+
+        void setDynamicConstant(int bsmIndex, CharSequence name, String type) {
+            tag = PoolTag.CONSTANT_DYNAMIC;
+            o1 = bsmIndex;
+            o2 = name;
+            o3 = type;
+            size = 3;
+            hash = tag.tag | ((bsmIndex | name.hashCode() | type.hashCode()) << 1);
+        }
+
+        void setString(String s) {
+            tag = PoolTag.CONSTANT_STRING;
+            o1 = s;
+            size = 1;
+            hash = tag.tag | (s.hashCode() << 1);
+        }
+
+        void setInteger(Integer i) {
+            tag = PoolTag.CONSTANT_INTEGER;
+            o1 = i;
+            size = 1;
+            hash = tag.tag | (i.hashCode() << 1);
+        }
+
+        void setFloat(Float f) {
+            tag = PoolTag.CONSTANT_FLOAT;
+            o1 = f;
+            size = 1;
+            hash = tag.tag | (f.hashCode() << 1);
+        }
+
+        void setLong(Long l) {
+            tag = PoolTag.CONSTANT_LONG;
+            o1 = l;
+            size = 1;
+            hash = tag.tag | (l.hashCode() << 1);
+        }
+
+        void setDouble(Double d) {
+            tag = PoolTag.CONSTANT_DOUBLE;
+            o1 = d;
+            size = 1;
+            hash = tag.tag | (d.hashCode() << 1);
+        }
+
+        void setMethodType(String type) {
+            tag = PoolTag.CONSTANT_METHODTYPE;
+            o1 = type;
+            size = 1;
+            hash = tag.tag | (type.hashCode() << 1);
+        }
+
+        void setMethodHandle(int bsmKind, String owner, CharSequence name, String type) {
+            tag = PoolTag.CONSTANT_METHODHANDLE;
+            o1 = bsmKind;
+            o2 = owner;
+            o3 = name;
+            o4 = type;
+            size = 4;
+            hash = tag.tag | (bsmKind | owner.hashCode() | name.hashCode() | type.hashCode() << 1);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            PoolKey that = (PoolKey) obj;
+            if (tag != that.tag) return false;
+            switch (size) {
+                case 1:
+                    if (!o1.equals(that.o1)) {
+                        return false;
+                    }
+                    break;
+                case 2:
+                    if (!o2.equals(that.o2) || !o1.equals(that.o1)) {
+                        return false;
+                    }
+                    break;
+                case 3:
+                    if (!o3.equals(that.o3) || !o2.equals(that.o2) || !o1.equals(that.o1)) {
+                        return false;
+                    }
+                    break;
+                case 4:
+                    if (!o4.equals(that.o4) || !o3.equals(that.o3) || !o2.equals(that.o2) || !o1.equals(that.o1)) {
+                        return false;
+                    }
+                    break;
+            }
+            return true;
+        }
+
+        PoolKey dup() {
+            PoolKey poolKey = new PoolKey();
+            poolKey.tag = tag;
+            poolKey.size = size;
+            poolKey.hash = hash;
+            poolKey.o1 = o1;
+            poolKey.o2 = o2;
+            poolKey.o3 = o3;
+            poolKey.o4 = o4;
+            return poolKey;
+        }
+    }
+
+    static class BsmKey extends AbstractKey<BsmKey> {
+        String bsmClass;
+        CharSequence bsmName;
+        String bsmType;
+        List<Integer> bsmArgs;
+
+        void set(String bsmClass, CharSequence bsmName, String bsmType, List<Integer> bsmArgs) {
+            this.bsmClass = bsmClass;
+            this.bsmName = bsmName;
+            this.bsmType = bsmType;
+            this.bsmArgs = bsmArgs;
+            hash = bsmClass.hashCode() | bsmName.hashCode() | bsmType.hashCode() | Objects.hash(bsmArgs);
+        }
+
+        BsmKey dup() {
+            BsmKey bsmKey = new BsmKey();
+            bsmKey.bsmClass = bsmClass;
+            bsmKey.bsmName = bsmName;
+            bsmKey.bsmType = bsmType;
+            bsmKey.bsmArgs = bsmArgs;
+            bsmKey.hash = hash;
+            return bsmKey;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof BsmKey) {
+                BsmKey that = (BsmKey)obj;
+                return Objects.equals(bsmClass, that.bsmClass) &&
+                        Objects.equals(bsmName, that.bsmName) &&
+                        Objects.equals(bsmType, that.bsmType) &&
+                        Objects.deepEquals(bsmArgs, that.bsmArgs);
+            } else {
+                return false;
+            }
+        }
+    }
+
+    @Override
+    public int putClass(S symbol) {
+        return putClassInternal(symbolToString.apply(symbol));
+    }
+
+    @Override
+    public int putInlineClass(S symbol) {
+        return putClassInternal("Q" + symbolToString.apply(symbol) + ";");
+    }
+
+    private int putClassInternal(String symbol) {
+        key.setClass(symbol);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            int utf8_idx = putUtf8(symbol);
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_CLASS.tag);
+            pool.writeChar(utf8_idx);
+        }
+        return poolKey.index;
+    }
+
+    @Override
+    public int putFieldRef(S owner, CharSequence name, T type) {
+        return putMemberRef(PoolTag.CONSTANT_FIELDREF, owner, name, type);
+    }
+
+    @Override
+    public int putMethodRef(S owner, CharSequence name, T type, boolean isInterface) {
+        return putMemberRef(isInterface ? PoolTag.CONSTANT_INTERFACEMETHODREF : PoolTag.CONSTANT_METHODREF,
+                owner, name, type);
+    }
+
+    int putMemberRef(PoolTag poolTag, S owner, CharSequence name, T type) {
+        return putMemberRefInternal(poolTag, symbolToString.apply(owner), name, typeToString.apply(type));
+    }
+
+    int putMemberRefInternal(PoolTag poolTag, String owner, CharSequence name, String type) {
+        key.setMemberRef(poolTag, owner, name, type);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            int owner_idx = putClassInternal(owner);
+            int nameAndType_idx = putNameAndType(name, type);
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(poolTag.tag);
+            pool.writeChar(owner_idx);
+            pool.writeChar(nameAndType_idx);
+        }
+        return poolKey.index;
+    }
+
+    @Override
+    public int putInt(int i) {
+        key.setInteger(i);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_INTEGER.tag);
+            pool.writeInt(i);
+        }
+        return poolKey.index;
+    }
+
+    @Override
+    public int putFloat(float f) {
+        key.setFloat(f);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_FLOAT.tag);
+            pool.writeFloat(f);
+        }
+        return poolKey.index;
+    }
+
+    @Override
+    public int putLong(long l) {
+        key.setLong(l);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_LONG.tag);
+            pool.writeLong(l);
+            currentIndex++;
+        }
+        return poolKey.index;
+    }
+
+    @Override
+    public int putDouble(double d) {
+        key.setDouble(d);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_DOUBLE.tag);
+            pool.writeDouble(d);
+            currentIndex++;
+        }
+        return poolKey.index;
+    }
+
+
+    @Override
+    public int putInvokeDynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
+        return putInvokeDynamicInternal(invokedName, typeToString.apply(invokedType), symbolToString.apply(bsmClass), bsmName, typeToString.apply(bsmType), staticArgs);
+    }
+
+    @Override
+    public int putDynamicConstant(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
+        return putDynamicConstantInternal(constName, typeToString.apply(constType), symbolToString.apply(bsmClass), bsmName, typeToString.apply(bsmType), staticArgs);
+    }
+
+    private int putInvokeDynamicInternal(CharSequence invokedName, String invokedType, String bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
+        int bsmIndex = putBsmInternal(bsmClass, bsmName, bsmType, staticArgs);
+        key.setInvokeDynamic(bsmIndex, invokedName, invokedType);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            int nameAndType_idx = putNameAndType(invokedName, invokedType);
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_INVOKEDYNAMIC.tag);
+            pool.writeChar(bsmIndex);
+            pool.writeChar(nameAndType_idx);
+        }
+        return poolKey.index;
+    }
+
+    private int putDynamicConstantInternal(CharSequence constName, String constType, String bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
+        int bsmIndex = putBsmInternal(bsmClass, bsmName, bsmType, staticArgs);
+        key.setDynamicConstant(bsmIndex, constName, constType);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            int nameAndType_idx = putNameAndType(constName, constType);
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_DYNAMIC.tag);
+            pool.writeChar(bsmIndex);
+            pool.writeChar(nameAndType_idx);
+        }
+        return poolKey.index;
+    }
+
+    private int putBsmInternal(String bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
+        ByteStaticArgListBuilder staticArgsBuilder = new ByteStaticArgListBuilder();
+        staticArgs.accept(staticArgsBuilder);
+        List<Integer> static_idxs = staticArgsBuilder.indexes;
+        bsmKey.set(bsmClass, bsmName, bsmType, static_idxs);
+        BsmKey poolKey = bootstraps.lookup(bsmKey);
+        if (poolKey == null) {
+            poolKey = bsmKey.dup();
+            // TODO the BSM could be a static method on an interface
+            int bsm_ref = putHandleInternal(MethodHandleInfo.REF_invokeStatic, bsmClass, bsmName, bsmType, false);
+            poolKey.at(currentBsmIndex++);
+            bootstraps.enter(poolKey);
+            bsm_attr.writeChar(bsm_ref);
+            bsm_attr.writeChar(static_idxs.size());
+            for (int i : static_idxs) {
+                bsm_attr.writeChar(i);
+            }
+        }
+        return poolKey.index;
+    }
+    //where
+        class ByteStaticArgListBuilder implements StaticArgListBuilder<S, T, byte[]> {
+
+            List<Integer> indexes = new ArrayList<>();
+
+            public ByteStaticArgListBuilder add(int i) {
+                indexes.add(putInt(i));
+                return this;
+            }
+            public ByteStaticArgListBuilder add(float f) {
+                indexes.add(putFloat(f));
+                return this;
+            }
+            public ByteStaticArgListBuilder add(long l) {
+                indexes.add(putLong(l));
+                return this;
+            }
+            public ByteStaticArgListBuilder add(double d) {
+                indexes.add(putDouble(d));
+                return this;
+            }
+            public ByteStaticArgListBuilder add(String s) {
+                indexes.add(putString(s));
+                return this;
+            }
+            @Override
+            public StaticArgListBuilder<S, T, byte[]> add(int refKind, S owner, CharSequence name, T type) {
+                indexes.add(putHandle(refKind, owner, name, type));
+                return this;
+            }
+            public <Z> ByteStaticArgListBuilder add(Z z, ToIntBiFunction<PoolHelper<S, T, byte[]>, Z> poolFunc) {
+                indexes.add(poolFunc.applyAsInt(BytePoolHelper.this, z));
+                return this;
+            }
+            public ByteStaticArgListBuilder add(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
+                indexes.add(putDynamicConstant(constName, constType, bsmClass, bsmName, bsmType, staticArgs));
+                return this;
+            }
+        }
+
+    @Override
+    public int putMethodType(T s) {
+        return putMethodTypeInternal(typeToString.apply(s));
+    }
+
+    private int putMethodTypeInternal(String s) {
+        key.setMethodType(s);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            int desc_idx = putUtf8(s);
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_METHODTYPE.tag);
+            pool.writeChar(desc_idx);
+        }
+        return poolKey.index;
+    }
+
+    @Override
+    public int putHandle(int refKind, S owner, CharSequence name, T type) {
+        return putHandleInternal(refKind, symbolToString.apply(owner), name, typeToString.apply(type), false);
+    }
+
+    @Override
+    public int putHandle(int refKind, S owner, CharSequence name, T type, boolean isInterface) {
+        return putHandleInternal(refKind, symbolToString.apply(owner), name, typeToString.apply(type), isInterface);
+    }
+
+    private int putHandleInternal(int refKind, String owner, CharSequence name, String type, boolean isInterface) {
+        key.setMethodHandle(refKind, owner, name, type);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            int ref_idx = putMemberRefInternal(fromKind(refKind, isInterface), owner, name, type);
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_METHODHANDLE.tag);
+            pool.writeByte(refKind);
+            pool.writeChar(ref_idx);
+        }
+        return poolKey.index;
+    }
+
+    PoolTag fromKind(int bsmKind, boolean isInterface) {
+        switch (bsmKind) {
+            case 1: // REF_getField
+            case 2: // REF_getStatic
+            case 3: // REF_putField
+            case 4: // REF_putStatic
+                return PoolTag.CONSTANT_FIELDREF;
+            case 5: // REF_invokeVirtual
+            case 6: // REF_invokeStatic
+            case 7: // REF_invokeSpecial
+            case 8: // REF_newInvokeSpecial
+            case 9: // REF_invokeInterface
+                return isInterface ? PoolTag.CONSTANT_INTERFACEMETHODREF : PoolTag.CONSTANT_METHODREF;
+            default:
+                throw new IllegalStateException();
+        }
+    }
+
+    @Override
+    public int putType(T s) {
+        return putUtf8(typeToString.apply(s));
+    }
+
+    public int putUtf8(CharSequence s) {
+        key.setUtf8(s);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_UTF8.tag);
+            putUTF8Internal(s);
+        }
+        return poolKey.index;
+    }
+
+    /**
+     * Puts an UTF8 string into this byte vector. The byte vector is
+     * automatically enlarged if necessary.
+     *
+     * @param s a String whose UTF8 encoded length must be less than 65536.
+     * @return this byte vector.
+     */
+    void putUTF8Internal(final CharSequence s) {
+        int charLength = s.length();
+        if (charLength > 65535) {
+            throw new IllegalArgumentException();
+        }
+        // optimistic algorithm: instead of computing the byte length and then
+        // serializing the string (which requires two loops), we assume the byte
+        // length is equal to char length (which is the most frequent case), and
+        // we start serializing the string right away. During the serialization,
+        // if we find that this assumption is wrong, we continue with the
+        // general method.
+        pool.writeChar(charLength);
+        for (int i = 0; i < charLength; ++i) {
+            char c = s.charAt(i);
+            if (c >= '\001' && c <= '\177') {
+                pool.writeByte((byte) c);
+            } else {
+                encodeUTF8(s, i, 65535);
+                break;
+            }
+        }
+    }
+
+    /**
+     * Puts an UTF8 string into this byte vector. The byte vector is
+     * automatically enlarged if necessary. The string length is encoded in two
+     * bytes before the encoded characters, if there is space for that (i.e. if
+     * this.length - i - 2 >= 0).
+     *
+     * @param s             the String to encode.
+     * @param i             the index of the first character to encode. The previous
+     *                      characters are supposed to have already been encoded, using
+     *                      only one byte per character.
+     * @param maxByteLength the maximum byte length of the encoded string, including the
+     *                      already encoded characters.
+     * @return this byte vector.
+     */
+    void encodeUTF8(final CharSequence s, int i, int maxByteLength) {
+        int charLength = s.length();
+        int byteLength = i;
+        char c;
+        for (int j = i; j < charLength; ++j) {
+            c = s.charAt(j);
+            if (c >= '\001' && c <= '\177') {
+                byteLength++;
+            } else if (c > '\u07FF') {
+                byteLength += 3;
+            } else {
+                byteLength += 2;
+            }
+        }
+        if (byteLength > maxByteLength) {
+            throw new IllegalArgumentException();
+        }
+        int byteLengthFinal = byteLength;
+        pool.withOffset(pool.offset - i - 2, buf -> buf.writeChar(byteLengthFinal));
+        for (int j = i; j < charLength; ++j) {
+            c = s.charAt(j);
+            if (c >= '\001' && c <= '\177') {
+                pool.writeChar((byte) c);
+            } else if (c > '\u07FF') {
+                pool.writeChar((byte) (0xE0 | c >> 12 & 0xF));
+                pool.writeChar((byte) (0x80 | c >> 6 & 0x3F));
+                pool.writeChar((byte) (0x80 | c & 0x3F));
+            } else {
+                pool.writeChar((byte) (0xC0 | c >> 6 & 0x1F));
+                pool.writeChar((byte) (0x80 | c & 0x3F));
+            }
+        }
+    }
+
+    @Override
+    public int putString(String s) {
+        key.setString(s);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            int utf8_index = putUtf8(s);
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_STRING.tag);
+            pool.writeChar(utf8_index);
+        }
+        return poolKey.index;
+    }
+
+    int putNameAndType(CharSequence name, String type) {
+        key.setNameAndType(name, type);
+        PoolKey poolKey = entries.lookup(key);
+        if (poolKey == null) {
+            poolKey = key.dup();
+            int name_idx = putUtf8(name);
+            int type_idx = putUtf8(type);
+            poolKey.at(currentIndex++);
+            entries.enter(poolKey);
+            pool.writeByte(PoolTag.CONSTANT_NAMEANDTYPE.tag);
+            pool.writeChar(name_idx);
+            pool.writeChar(type_idx);
+        }
+        return poolKey.index;
+    }
+
+    @Override
+    public int size() {
+        return currentIndex - 1;
+    }
+
+    @Override
+    public byte[] entries() {
+        return pool.bytes();
+    }
+
+    <Z extends ClassBuilder<S, T, Z>> void addAttributes(ClassBuilder<S , T, Z> cb) {
+        if (currentBsmIndex > 0) {
+            GrowableByteBuffer bsmAttrBuf = new GrowableByteBuffer();
+            bsmAttrBuf.writeChar(currentBsmIndex);
+            bsmAttrBuf.writeBytes(bsm_attr);
+            cb.withAttribute("BootstrapMethods", bsmAttrBuf.bytes());
+        }
+    }
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/ClassBuilder.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/ClassBuilder.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/ClassBuilder.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+import java.util.function.Consumer;
+
+/**
+ * Base class builder. The base of higher level class builders.
+ *
+ * @param <S> the type of symbol representation
+ * @param <T> the type of type descriptors representation
+ * @param <C> the type of this builder
+ */
+public class ClassBuilder<S, T, C extends ClassBuilder<S, T, C>>
+        extends DeclBuilder<S, T, byte[], C> {
+
+    /**
+     * The helper to use to manipulate type descriptors.
+     */
+    protected TypeHelper<S, T> typeHelper;
+
+    /**
+     * The symbol for the class being built.
+     */
+    protected S thisClass;
+
+    /**
+     * The super-interfaces of the class being built..
+     */
+    protected GrowableByteBuffer interfaces = new GrowableByteBuffer();
+
+    /**
+     * The fields of the class being built.
+     */
+    protected GrowableByteBuffer fields = new GrowableByteBuffer();
+
+    /**
+     * The methods of the class being built.
+     */
+    protected GrowableByteBuffer methods = new GrowableByteBuffer();
+
+    int majorVersion;
+    int minorVersion;
+    int flags;
+    int superclass;
+    int nmethods, nfields, ninterfaces;
+
+    /**
+     * Create a class builder.
+     *
+     * @param poolHelper the helper to build the constant pool
+     * @param typeHelper the helper to use to manipulate type descriptors
+     */
+    public ClassBuilder(BytePoolHelper<S, T> poolHelper, TypeHelper<S, T> typeHelper) {
+        super(poolHelper, typeHelper);
+        this.typeHelper = typeHelper;
+    }
+
+    /**
+     * Set the minor class file version.
+     *
+     * @param minorVersion the minor version number
+     * @return this builder, for chained calls
+     */
+    public C withMinorVersion(int minorVersion) {
+        this.minorVersion = minorVersion;
+        return thisBuilder();
+    }
+
+    /**
+     * Set the major class file version.
+     *
+     * @param majorVersion the major version number
+     * @return this builder, for chained calls
+     */
+    public C withMajorVersion(int majorVersion) {
+        this.majorVersion = majorVersion;
+        return thisBuilder();
+    }
+
+    /**
+     * Set the class symbol
+     *
+     * @param thisClass the class symbol
+     * @return this builder, for chained calls
+     */
+    public C withThisClass(S thisClass) {
+        this.thisClass = thisClass;
+        return thisBuilder();
+    }
+
+    /**
+     * Set the class access flags
+     *
+     * @param flags an array of {@code Flag}
+     * @return this builder, for chained calls
+     */
+    @Override
+    public C withFlags(Flag... flags) {
+        for (Flag f : flags) {
+            this.flags |= f.flag;
+        }
+        return thisBuilder();
+    }
+
+    /**
+     * Set the superclass
+     *
+     * @param sup the superclass symbol
+     * @return this builder, for chained calls
+     */
+    public C withSuperclass(S sup) {
+        this.superclass = poolHelper.putClass(sup);
+        return thisBuilder();
+    }
+
+    /**
+     * Add a super interface.
+     *
+     * @param sup an interface symbol
+     * @return this builder, for chained calls
+     */
+    public C withSuperinterface(S sup) {
+        this.interfaces.writeChar(poolHelper.putClass(sup));
+        ninterfaces++;
+        return thisBuilder();
+    }
+
+    /**
+     * Add a field.
+     *
+     * @param name the name of the field
+     * @param type the type descriptor of the field
+     * @return this builder, for chained calls
+     */
+    public final C withField(CharSequence name, T type) {
+        return withField(name, type, FB -> {
+        });
+    }
+
+    /**
+     * Add a field.
+     *
+     * @param name the name of the field
+     * @param type the type descriptor of the field
+     * @param fieldConfig access to the {@code FieldBuilder} to allow clients to
+     * adjust flags, annotations and bytecode attributes on the field declaration
+     * @return this builder, for chained calls
+     */
+    public C withField(CharSequence name, T type, Consumer<? super FieldBuilder<S, T, byte[]>> fieldConfig) {
+        FieldBuilder<S, T, byte[]> F = new FieldBuilder<>(name, type, poolHelper, typeHelper);
+        fieldConfig.accept(F);
+        F.build(fields);
+        nfields++;
+        return thisBuilder();
+    }
+
+    /**
+     * Add a method
+     *
+     * @param name the name of the method
+     * @param type the type descriptor of the method
+     * @return this builder, for chained calls
+     */
+    public final C withMethod(CharSequence name, T type) {
+        return withMethod(name, type, MB -> {
+        });
+    }
+
+    /**
+     * Add a method
+     *
+     * @param name the name of the method
+     * @param type the type descriptor of the method
+     * @param methodConfig access to the {@code MethodBuilder} to allow clients to
+     * adjust flags, annotations and bytecode attributes on the method declaration
+     * @return this builder, for chained calls
+     */
+    public C withMethod(CharSequence name, T type, Consumer<? super MethodBuilder<S, T, byte[]>> methodConfig) {
+        MethodBuilder<S, T, byte[]> M = new MethodBuilder<>(thisClass, name, type, poolHelper, typeHelper);
+        methodConfig.accept(M);
+        M.build(methods);
+        nmethods++;
+        return thisBuilder();
+    }
+
+    /**
+     * Build the constant pool into a byte array.
+     *
+     * @return a representation of this constant pool as a byte array
+     */
+    @SuppressWarnings("unchecked")
+    public byte[] build() {
+        ((BytePoolHelper<S, T>)poolHelper).addAttributes(this);
+        addAnnotations();
+        int thisClassIdx = poolHelper.putClass(thisClass);
+        byte[] poolBytes = poolHelper.entries();
+        GrowableByteBuffer buf = new GrowableByteBuffer();
+        buf.writeInt(0xCAFEBABE);
+        buf.writeChar(minorVersion);
+        buf.writeChar(majorVersion);
+        buf.writeChar(poolHelper.size() + 1);
+        buf.writeBytes(poolBytes);
+        buf.writeChar(flags);
+        buf.writeChar(thisClassIdx);
+        buf.writeChar(superclass);
+        buf.writeChar(ninterfaces);
+        if (ninterfaces > 0) {
+            buf.writeBytes(interfaces);
+        }
+        buf.writeChar(nfields);
+        buf.writeBytes(fields);
+        buf.writeChar(nmethods);
+        buf.writeBytes(methods);
+        buf.writeChar(nattrs);
+        buf.writeBytes(attributes);
+        return buf.bytes();
+    }
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/CodeBuilder.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/CodeBuilder.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/CodeBuilder.java
@@ -0,0 +1,1218 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+import jdk.experimental.bytecode.PoolHelper.StaticArgListBuilder;
+
+import java.lang.invoke.MethodHandle;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.ToIntFunction;
+
+/**
+ * Builder for class file code attributes. A code attribute is defined:
+ * <pre>
+ * {@code
+ * Code_attribute {
+ *    u2 attribute_name_index;
+ *    u4 attribute_length;
+ *    u2 max_stack;
+ *    u2 max_locals;
+ *    u4 code_length;
+ *    u1 code[code_length];
+ *    u2 exception_table_length;
+ *    {   u2 start_pc;
+ *        u2 end_pc;
+ *        u2 handler_pc;
+ *        u2 catch_type;
+ *    } exception_table[exception_table_length];
+ *    u2 attributes_count;
+ *    attribute_info attributes[attributes_count];
+ * } }
+ * </pre>
+ *
+ * @param <S> the type of symbol representation
+ * @param <T> the type of type descriptors representation
+ * @param <E> the type of pool entries
+ * @param <C> the type of this code builder
+ */
+public class CodeBuilder<S, T, E, C extends CodeBuilder<S, T, E, C>> extends AttributeBuilder<S, T, E, C> {
+
+    protected GrowableByteBuffer code = new GrowableByteBuffer();
+    GrowableByteBuffer catchers = new GrowableByteBuffer();
+    GrowableByteBuffer stackmaps = new GrowableByteBuffer();
+    MethodBuilder<S, T, E> methodBuilder;
+    int ncatchers;
+    int stacksize = -1;
+    int localsize = -1;
+    int nstackmaps = 0;
+
+    public enum JumpMode {
+        NARROW,
+        WIDE;
+    }
+
+    CodeBuilder(MethodBuilder<S, T, E> methodBuilder) {
+        super(methodBuilder.poolHelper, methodBuilder.typeHelper);
+        this.methodBuilder = methodBuilder;
+    }
+
+    public C getstatic(S owner, CharSequence name, T type) {
+        emitOp(Opcode.GETSTATIC, type);
+        code.writeChar(poolHelper.putFieldRef(owner, name, type));
+        return thisBuilder();
+    }
+
+    public C putstatic(S owner, CharSequence name, T type) {
+        emitOp(Opcode.PUTSTATIC, type);
+        code.writeChar(poolHelper.putFieldRef(owner, name, type));
+        return thisBuilder();
+    }
+
+    public C getfield(S owner, CharSequence name, T type) {
+        emitOp(Opcode.GETFIELD, type);
+        code.writeChar(poolHelper.putFieldRef(owner, name, type));
+        return thisBuilder();
+    }
+
+    public C putfield(S owner, CharSequence name, T type) {
+        emitOp(Opcode.PUTFIELD, type);
+        code.writeChar(poolHelper.putFieldRef(owner, name, type));
+        return thisBuilder();
+    }
+
+    public C withfield(S owner, CharSequence name, T type) {
+        emitOp(Opcode.WITHFIELD, type);
+        code.writeChar(poolHelper.putFieldRef(owner, name, type));
+        return thisBuilder();
+    }
+
+    public C invokevirtual(S owner, CharSequence name, T type, boolean isInterface) {
+        emitOp(Opcode.INVOKEVIRTUAL, type);
+        code.writeChar(poolHelper.putMethodRef(owner, name, type, isInterface));
+        return thisBuilder();
+    }
+
+    public C invokespecial(S owner, CharSequence name, T type, boolean isInterface) {
+        emitOp(Opcode.INVOKESPECIAL, type);
+        code.writeChar(poolHelper.putMethodRef(owner, name, type, isInterface));
+        return thisBuilder();
+    }
+
+    public C invokestatic(S owner, CharSequence name, T type, boolean isInterface) {
+        emitOp(Opcode.INVOKESTATIC, type);
+        code.writeChar(poolHelper.putMethodRef(owner, name, type, isInterface));
+        return thisBuilder();
+    }
+
+    public C invokeinterface(S owner, CharSequence name, T type) {
+        emitOp(Opcode.INVOKEINTERFACE, type);
+        code.writeChar(poolHelper.putMethodRef(owner, name, type, true));
+        int nargs = 1;
+        Iterator<T> it = typeHelper.parameterTypes(type);
+        while (it.hasNext()) {
+            nargs += typeHelper.tag(it.next()).width;
+        }
+        code.writeByte(nargs);
+        code.writeByte(0);
+        return thisBuilder();
+    }
+
+    public C invokedynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs) {
+        emitOp(Opcode.INVOKEDYNAMIC, invokedType);
+        code.writeChar(poolHelper.putInvokeDynamic(invokedName, invokedType, bsmClass, bsmName, bsmType, staticArgs));
+        code.writeChar(0); //padding
+        return thisBuilder();
+    }
+
+    public C new_(S clazz) {
+        emitOp(Opcode.NEW, clazz);
+        code.writeChar(poolHelper.putClass(clazz));
+        return thisBuilder();
+    }
+
+    public C aconst_init(S clazz) {
+        emitOp(Opcode.ACONST_INIT, clazz);
+        code.writeChar(poolHelper.putClass(clazz));
+        return thisBuilder();
+    }
+
+    public C newarray(TypeTag tag) {
+        emitOp(Opcode.NEWARRAY, tag);
+        int newarraycode = tag.newarraycode;
+        if (newarraycode == -1) {
+            throw new IllegalStateException("Bad tag " + tag);
+        }
+        code.writeByte(newarraycode);
+        return thisBuilder();
+    }
+
+    public C anewarray(S array) {
+        emitOp(Opcode.ANEWARRAY, array);
+        int poolIdx = poolHelper.putClass(array);
+        code.writeChar(poolIdx);
+        return thisBuilder();
+    }
+
+    public C checkcast(S target) {
+        emitOp(Opcode.CHECKCAST);
+        int poolIdx = poolHelper.putClass(target);
+        code.writeChar(poolIdx);
+        return thisBuilder();
+    }
+
+    public C instanceof_(S target) {
+        emitOp(Opcode.INSTANCEOF);
+        int poolIdx = poolHelper.putClass(target);
+        code.writeChar(poolIdx);
+        return thisBuilder();
+    }
+
+    public C multianewarray(S array, byte dims) {
+        emitOp(Opcode.MULTIANEWARRAY, new Object[]{array, dims});
+        code.writeChar(poolHelper.putClass(array)).writeByte(dims);
+        return thisBuilder();
+    }
+
+    public C ldc(int i) {
+        return ldc(pool -> pool.putInt(i), false);
+    }
+
+    public C ldc(long l) {
+        return ldc(pool -> pool.putLong(l), true);
+    }
+
+    public C ldc(float f) {
+        return ldc(pool -> pool.putFloat(f), false);
+    }
+
+    public C ldc(double d) {
+        return ldc(pool -> pool.putDouble(d), true);
+    }
+
+    public C ldc(String s) {
+        return ldc(pool -> pool.putString(s), false);
+    }
+
+    public C ldc(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs) {
+        boolean fat = typeHelper.tag(constType).width() == 2;
+        return ldc(pool -> pool.putDynamicConstant(constName, constType, bsmClass, bsmName, bsmType, staticArgs), fat);
+    }
+
+    public <Z> C ldc(Z z, BiFunction<PoolHelper<S, T, E>, Z, Integer> poolFunc) {
+        return ldc(pool -> poolFunc.apply(pool, z), false);
+    }
+
+    protected C ldc(ToIntFunction<PoolHelper<S, T, E>> indexFunc, boolean fat) {
+        // @@@ This should probably be abstract
+        int index = indexFunc.applyAsInt(poolHelper);
+        return ldc(index, null, fat);
+    }
+
+    protected final C ldc(int index, T type, boolean fat) {
+        if (fat) {
+            emitOp(Opcode.LDC2_W, type);
+            code.writeChar(index);
+        } else if (index > 63) {
+            emitOp(Opcode.LDC_W, type);
+            code.writeChar(index);
+        } else {
+            emitOp(Opcode.LDC, type);
+            code.writeByte(index);
+        }
+        return thisBuilder();
+    }
+
+    //other non-CP dependent opcodes
+    public C areturn() {
+        return emitOp(Opcode.ARETURN);
+    }
+
+    public C ireturn() {
+        return emitOp(Opcode.IRETURN);
+    }
+
+    public C freturn() {
+        return emitOp(Opcode.FRETURN);
+    }
+
+    public C lreturn() {
+        return emitOp(Opcode.LRETURN);
+    }
+
+    public C dreturn() {
+        return emitOp(Opcode.DRETURN);
+    }
+
+    public C return_() {
+        return emitOp(Opcode.RETURN);
+    }
+
+    protected C emitWideIfNeeded(Opcode opcode, int n) {
+        boolean wide = n > Byte.MAX_VALUE;
+        if (wide) {
+            wide();
+        }
+        emitOp(opcode, n);
+        if (wide) {
+            code.writeChar(n);
+        } else {
+            code.writeByte(n);
+        }
+        return thisBuilder();
+    }
+
+    protected C emitWideIfNeeded(Opcode opcode, int n, int v) {
+        boolean wide = n > Byte.MAX_VALUE || v > Byte.MAX_VALUE;
+        if (wide) {
+            wide();
+        }
+        emitOp(opcode, n);
+        if (wide) {
+            code.writeChar(n).writeChar(v);
+        } else {
+            code.writeByte(n).writeByte(v);
+        }
+        return thisBuilder();
+    }
+
+    public C aload(int i) {
+        return emitWideIfNeeded(Opcode.ALOAD, i);
+    }
+
+    public C iload(int i) {
+        return emitWideIfNeeded(Opcode.ILOAD, i);
+    }
+
+    public C fload(int i) {
+        return emitWideIfNeeded(Opcode.FLOAD, i);
+    }
+
+    public C lload(int i) {
+        return emitWideIfNeeded(Opcode.LLOAD, i);
+    }
+
+    public C dload(int i) {
+        return emitWideIfNeeded(Opcode.DLOAD, i);
+    }
+
+    public C aload_0() {
+        return emitOp(Opcode.ALOAD_0);
+    }
+
+    public C iload_0() {
+        return emitOp(Opcode.ILOAD_0);
+    }
+
+    public C fload_0() {
+        return emitOp(Opcode.FLOAD_0);
+    }
+
+    public C lload_0() {
+        return emitOp(Opcode.LLOAD_0);
+    }
+
+    public C dload_0() {
+        return emitOp(Opcode.DLOAD_0);
+    }
+
+    public C aload_1() {
+        return emitOp(Opcode.ALOAD_1);
+    }
+
+    public C iload_1() {
+        return emitOp(Opcode.ILOAD_1);
+    }
+
+    public C fload_1() {
+        return emitOp(Opcode.FLOAD_1);
+    }
+
+    public C lload_1() {
+        return emitOp(Opcode.LLOAD_1);
+    }
+
+    public C dload_1() {
+        return emitOp(Opcode.DLOAD_1);
+    }
+
+    public C aload_2() {
+        return emitOp(Opcode.ALOAD_2);
+    }
+
+    public C iload_2() {
+        return emitOp(Opcode.ILOAD_2);
+    }
+
+    public C fload_2() {
+        return emitOp(Opcode.FLOAD_2);
+    }
+
+    public C lload_2() {
+        return emitOp(Opcode.LLOAD_2);
+    }
+
+    public C dload_2() {
+        return emitOp(Opcode.DLOAD_2);
+    }
+
+    public C aload_3() {
+        return emitOp(Opcode.ALOAD_3);
+    }
+
+    public C iload_3() {
+        return emitOp(Opcode.ILOAD_3);
+    }
+
+    public C fload_3() {
+        return emitOp(Opcode.FLOAD_3);
+    }
+
+    public C lload_3() {
+        return emitOp(Opcode.LLOAD_3);
+    }
+
+    public C dload_3() {
+        return emitOp(Opcode.DLOAD_3);
+    }
+
+    public C astore(int i) {
+        return emitWideIfNeeded(Opcode.ASTORE, i);
+    }
+
+    public C istore(int i) {
+        return emitWideIfNeeded(Opcode.ISTORE, i);
+    }
+
+    public C fstore(int i) {
+        return emitWideIfNeeded(Opcode.FSTORE, i);
+    }
+
+    public C lstore(int i) {
+        return emitWideIfNeeded(Opcode.LSTORE, i);
+    }
+
+    public C dstore(int i) {
+        return emitWideIfNeeded(Opcode.DSTORE, i);
+    }
+
+    public C astore_0() {
+        return emitOp(Opcode.ASTORE_0);
+    }
+
+    public C istore_0() {
+        return emitOp(Opcode.ISTORE_0);
+    }
+
+    public C fstore_0() {
+        return emitOp(Opcode.FSTORE_0);
+    }
+
+    public C lstore_0() {
+        return emitOp(Opcode.LSTORE_0);
+    }
+
+    public C dstore_0() {
+        return emitOp(Opcode.DSTORE_0);
+    }
+
+    public C astore_1() {
+        return emitOp(Opcode.ASTORE_1);
+    }
+
+    public C istore_1() {
+        return emitOp(Opcode.ISTORE_1);
+    }
+
+    public C fstore_1() {
+        return emitOp(Opcode.FSTORE_1);
+    }
+
+    public C lstore_1() {
+        return emitOp(Opcode.LSTORE_1);
+    }
+
+    public C dstore_1() {
+        return emitOp(Opcode.DSTORE_1);
+    }
+
+    public C astore_2() {
+        return emitOp(Opcode.ASTORE_2);
+    }
+
+    public C istore_2() {
+        return emitOp(Opcode.ISTORE_2);
+    }
+
+    public C fstore_2() {
+        return emitOp(Opcode.FSTORE_2);
+    }
+
+    public C lstore_2() {
+        return emitOp(Opcode.LSTORE_2);
+    }
+
+    public C dstore_2() {
+        return emitOp(Opcode.DSTORE_2);
+    }
+
+    public C astore_3() {
+        return emitOp(Opcode.ASTORE_3);
+    }
+
+    public C istore_3() {
+        return emitOp(Opcode.ISTORE_3);
+    }
+
+    public C fstore_3() {
+        return emitOp(Opcode.FSTORE_3);
+    }
+
+    public C lstore_3() {
+        return emitOp(Opcode.LSTORE_3);
+    }
+
+    public C dstore_3() {
+        return emitOp(Opcode.DSTORE_3);
+    }
+
+    //...
+
+    public C iaload() {
+        return emitOp(Opcode.IALOAD);
+    }
+
+    public C laload() {
+        return emitOp(Opcode.LALOAD);
+    }
+
+    public C faload() {
+        return emitOp(Opcode.FALOAD);
+    }
+
+    public C daload() {
+        return emitOp(Opcode.DALOAD);
+    }
+
+    public C aaload() {
+        return emitOp(Opcode.AALOAD);
+    }
+
+    public C baload() {
+        return emitOp(Opcode.BALOAD);
+    }
+
+    public C caload() {
+        return emitOp(Opcode.CALOAD);
+    }
+
+    public C saload() {
+        return emitOp(Opcode.SALOAD);
+    }
+
+    public C iastore() {
+        return emitOp(Opcode.IASTORE);
+    }
+
+    public C lastore() {
+        return emitOp(Opcode.LASTORE);
+    }
+
+    public C fastore() {
+        return emitOp(Opcode.FASTORE);
+    }
+
+    public C dastore() {
+        return emitOp(Opcode.DASTORE);
+    }
+
+    public C aastore() {
+        return emitOp(Opcode.AASTORE);
+    }
+
+    public C bastore() {
+        return emitOp(Opcode.BASTORE);
+    }
+
+    public C castore() {
+        return emitOp(Opcode.CASTORE);
+    }
+
+    public C sastore() {
+        return emitOp(Opcode.SASTORE);
+    }
+
+    public C nop() {
+        return emitOp(Opcode.NOP);
+    }
+
+    public C aconst_null() {
+        return emitOp(Opcode.ACONST_NULL);
+    }
+
+    public C iconst_0() {
+        return emitOp(Opcode.ICONST_0);
+    }
+
+    public C iconst_1() {
+        return emitOp(Opcode.ICONST_1);
+    }
+
+    public C iconst_2() {
+        return emitOp(Opcode.ICONST_2);
+    }
+
+    public C iconst_3() {
+        return emitOp(Opcode.ICONST_3);
+    }
+
+    public C iconst_4() {
+        return emitOp(Opcode.ICONST_4);
+    }
+
+    public C iconst_5() {
+        return emitOp(Opcode.ICONST_5);
+    }
+
+    public C iconst_m1() {
+        return emitOp(Opcode.ICONST_M1);
+    }
+
+    public C lconst_0() {
+        return emitOp(Opcode.LCONST_0);
+    }
+
+    public C lconst_1() {
+        return emitOp(Opcode.LCONST_1);
+    }
+
+    public C fconst_0() {
+        return emitOp(Opcode.FCONST_0);
+    }
+
+    public C fconst_1() {
+        return emitOp(Opcode.FCONST_1);
+    }
+
+    public C fconst_2() {
+        return emitOp(Opcode.FCONST_2);
+    }
+
+    public C dconst_0() {
+        return emitOp(Opcode.DCONST_0);
+    }
+
+    public C dconst_1() {
+        return emitOp(Opcode.DCONST_1);
+    }
+
+    public C sipush(int s) {
+        emitOp(Opcode.SIPUSH);
+        code.writeChar(s);
+        return thisBuilder();
+    }
+
+    public C bipush(int b) {
+        emitOp(Opcode.BIPUSH);
+        code.writeByte(b);
+        return thisBuilder();
+    }
+
+    public C pop() {
+        return emitOp(Opcode.POP);
+    }
+
+    public C pop2() {
+        return emitOp(Opcode.POP2);
+    }
+
+    public C dup() {
+        return emitOp(Opcode.DUP);
+    }
+
+    public C dup_x1() {
+        return emitOp(Opcode.DUP_X1);
+    }
+
+    public C dup_x2() {
+        return emitOp(Opcode.DUP_X2);
+    }
+
+    public C dup2() {
+        return emitOp(Opcode.DUP2);
+    }
+
+    public C dup2_x1() {
+        return emitOp(Opcode.DUP2_X1);
+    }
+
+    public C dup2_x2() {
+        return emitOp(Opcode.DUP2_X2);
+    }
+
+    public C swap() {
+        return emitOp(Opcode.SWAP);
+    }
+
+    public C iadd() {
+        return emitOp(Opcode.IADD);
+    }
+
+    public C ladd() {
+        return emitOp(Opcode.LADD);
+    }
+
+    public C fadd() {
+        return emitOp(Opcode.FADD);
+    }
+
+    public C dadd() {
+        return emitOp(Opcode.DADD);
+    }
+
+    public C isub() {
+        return emitOp(Opcode.ISUB);
+    }
+
+    public C lsub() {
+        return emitOp(Opcode.LSUB);
+    }
+
+    public C fsub() {
+        return emitOp(Opcode.FSUB);
+    }
+
+    public C dsub() {
+        return emitOp(Opcode.DSUB);
+    }
+
+    public C imul() {
+        return emitOp(Opcode.IMUL);
+    }
+
+    public C lmul() {
+        return emitOp(Opcode.LMUL);
+    }
+
+    public C fmul() {
+        return emitOp(Opcode.FMUL);
+    }
+
+    public C dmul() {
+        return emitOp(Opcode.DMUL);
+    }
+
+    public C idiv() {
+        return emitOp(Opcode.IDIV);
+    }
+
+    public C ldiv() {
+        return emitOp(Opcode.LDIV);
+    }
+
+    public C fdiv() {
+        return emitOp(Opcode.FDIV);
+    }
+
+    public C ddiv() {
+        return emitOp(Opcode.DDIV);
+    }
+
+    public C irem() {
+        return emitOp(Opcode.IREM);
+    }
+
+    public C lrem() {
+        return emitOp(Opcode.LREM);
+    }
+
+    public C frem() {
+        return emitOp(Opcode.FREM);
+    }
+
+    public C drem() {
+        return emitOp(Opcode.DREM);
+    }
+
+    public C ineg() {
+        return emitOp(Opcode.INEG);
+    }
+
+    public C lneg() {
+        return emitOp(Opcode.LNEG);
+    }
+
+    public C fneg() {
+        return emitOp(Opcode.FNEG);
+    }
+
+    public C dneg() {
+        return emitOp(Opcode.DNEG);
+    }
+
+    public C ishl() {
+        return emitOp(Opcode.ISHL);
+    }
+
+    public C lshl() {
+        return emitOp(Opcode.LSHL);
+    }
+
+    public C ishr() {
+        return emitOp(Opcode.ISHR);
+    }
+
+    public C lshr() {
+        return emitOp(Opcode.LSHR);
+    }
+
+    public C iushr() {
+        return emitOp(Opcode.IUSHR);
+    }
+
+    public C lushr() {
+        return emitOp(Opcode.LUSHR);
+    }
+
+    public C iand() {
+        return emitOp(Opcode.IAND);
+    }
+
+    public C land() {
+        return emitOp(Opcode.LAND);
+    }
+
+    public C ior() {
+        return emitOp(Opcode.IOR);
+    }
+
+    public C lor() {
+        return emitOp(Opcode.LOR);
+    }
+
+    public C ixor() {
+        return emitOp(Opcode.IXOR);
+    }
+
+    public C lxor() {
+        return emitOp(Opcode.LXOR);
+    }
+
+    public C iinc(int index, int val) {
+        return emitWideIfNeeded(Opcode.IINC, index, val);
+    }
+
+    public C i2l() {
+        return emitOp(Opcode.I2L);
+    }
+
+    public C i2f() {
+        return emitOp(Opcode.I2F);
+    }
+
+    public C i2d() {
+        return emitOp(Opcode.I2D);
+    }
+
+    public C l2i() {
+        return emitOp(Opcode.L2I);
+    }
+
+    public C l2f() {
+        return emitOp(Opcode.L2F);
+    }
+
+    public C l2d() {
+        return emitOp(Opcode.L2D);
+    }
+
+    public C f2i() {
+        return emitOp(Opcode.F2I);
+    }
+
+    public C f2l() {
+        return emitOp(Opcode.F2L);
+    }
+
+    public C f2d() {
+        return emitOp(Opcode.F2D);
+    }
+
+    public C d2i() {
+        return emitOp(Opcode.D2I);
+    }
+
+    public C d2l() {
+        return emitOp(Opcode.D2L);
+    }
+
+    public C d2f() {
+        return emitOp(Opcode.D2F);
+    }
+
+    public C i2b() {
+        return emitOp(Opcode.I2B);
+    }
+
+    public C i2c() {
+        return emitOp(Opcode.I2C);
+    }
+
+    public C i2s() {
+        return emitOp(Opcode.I2S);
+    }
+
+    public C lcmp() {
+        return emitOp(Opcode.LCMP);
+    }
+
+    public C fcmpl() {
+        return emitOp(Opcode.FCMPL);
+    }
+
+    public C fcmpg() {
+        return emitOp(Opcode.FCMPG);
+    }
+
+    public C dcmpl() {
+        return emitOp(Opcode.DCMPL);
+    }
+
+    public C dcmpg() {
+        return emitOp(Opcode.DCMPG);
+    }
+
+    public C ifeq(short target) {
+        return emitNarrowJumpOp(Opcode.IFEQ, target);
+    }
+
+    public C ifne(short target) {
+        return emitNarrowJumpOp(Opcode.IFNE, target);
+    }
+
+    public C iflt(short target) {
+        return emitNarrowJumpOp(Opcode.IFLT, target);
+    }
+
+    public C ifge(short target) {
+        return emitNarrowJumpOp(Opcode.IFGE, target);
+    }
+
+    public C ifgt(short target) {
+        return emitNarrowJumpOp(Opcode.IFGT, target);
+    }
+
+    public C ifle(short target) {
+        return emitNarrowJumpOp(Opcode.IFLE, target);
+    }
+
+    public C if_icmpeq(short target) {
+        return emitNarrowJumpOp(Opcode.IF_ICMPEQ, target);
+    }
+
+    public C if_icmpne(short target) {
+        return emitNarrowJumpOp(Opcode.IF_ICMPNE, target);
+    }
+
+    public C if_icmplt(short target) {
+        return emitNarrowJumpOp(Opcode.IF_ICMPLT, target);
+    }
+
+    public C if_icmpge(short target) {
+        return emitNarrowJumpOp(Opcode.IF_ICMPGE, target);
+    }
+
+    public C if_icmpgt(short target) {
+        return emitNarrowJumpOp(Opcode.IF_ICMPGT, target);
+    }
+
+    public C if_icmple(short target) {
+        return emitNarrowJumpOp(Opcode.IF_ICMPLE, target);
+    }
+
+    public C if_acmpeq(short target) {
+        return emitNarrowJumpOp(Opcode.IF_ACMPEQ, target);
+    }
+
+    public C if_acmpne(short target) {
+        return emitNarrowJumpOp(Opcode.IF_ACMPNE, target);
+    }
+
+    public C goto_(short target) {
+        return emitNarrowJumpOp(Opcode.GOTO_, target);
+    }
+
+    public C jsr(short target) {
+        return emitNarrowJumpOp(Opcode.JSR, target);
+    }
+
+    public C ret(int index) {
+        return emitWideIfNeeded(Opcode.RET, index);
+    }
+
+    public C tableswitch(int low, int high, int defaultTarget, int... targets) {
+        if (high - low + 1 != targets.length) throw new IllegalStateException("Bad targets length");
+        emitOp(Opcode.TABLESWITCH);
+        //padding
+        int start = code.offset;
+        if ((start % 4) != 0) {
+            //add padding
+            for (int i = 0; i < 4 - (start % 4); i++) {
+                code.writeByte(0);
+            }
+        }
+        code.writeInt(defaultTarget)
+                .writeInt(low)
+                .writeInt(high);
+        for (int target : targets) {
+            code.writeInt(target);
+        }
+        return thisBuilder();
+    }
+
+    public C lookupswitch(int defaultTarget, int... npairs) {
+        if (npairs.length % 2 != 0) throw new IllegalStateException("Bad npairs length");
+        emitOp(Opcode.LOOKUPSWITCH);
+        //padding
+        int start = code.offset;
+        for (int i = 0; i < (4 - (start % 4)); i++) {
+            code.writeByte(0);
+        }
+        code.writeInt(defaultTarget)
+                .writeInt(npairs.length / 2);
+        for (int i = 0; i < npairs.length; i += 2) {
+            code.writeInt(npairs[i]);
+            code.writeInt(npairs[i + 1]);
+        }
+        return thisBuilder();
+    }
+
+    public C arraylength() {
+        return emitOp(Opcode.ARRAYLENGTH);
+    }
+
+    public C athrow() {
+        return emitOp(Opcode.ATHROW);
+    }
+
+    public C monitorenter() {
+        return emitOp(Opcode.MONITORENTER);
+    }
+
+    public C monitorexit() {
+        return emitOp(Opcode.MONITOREXIT);
+    }
+
+    public C wide() {
+        return emitOp(Opcode.WIDE);
+    }
+
+    public C if_null(short offset) {
+        return emitNarrowJumpOp(Opcode.IF_NULL, offset);
+    }
+
+    public C if_nonnull(short offset) {
+        return emitNarrowJumpOp(Opcode.IF_NONNULL, offset);
+    }
+
+    public C goto_w(int target) {
+        return emitWideJumpOp(Opcode.GOTO_W, target);
+    }
+
+    public C jsr_w(int target) {
+        return emitWideJumpOp(Opcode.JSR_W, target);
+    }
+
+    public C withCatch(S type, int start, int end, int offset) {
+        catchers.writeChar(start);
+        catchers.writeChar(end);
+        catchers.writeChar(offset);
+        catchers.writeChar(type != null ? poolHelper.putClass(type) : 0);
+        ncatchers++;
+        return thisBuilder();
+    }
+
+    public C withLocalSize(int localsize) {
+        this.localsize = localsize;
+        return thisBuilder();
+    }
+
+    public C withStackSize(int stacksize) {
+        this.stacksize = stacksize;
+        return thisBuilder();
+    }
+
+    protected int localsize() {
+        return localsize;
+    }
+
+    void build(GrowableByteBuffer buf) {
+        buf.writeChar(stacksize); //max stack size
+        buf.writeChar(localsize()); //max locals
+        buf.writeInt(code.offset);
+        buf.writeBytes(code);
+        buf.writeChar(ncatchers);
+        buf.writeBytes(catchers);
+        buf.writeChar(nattrs); //attributes
+        buf.writeBytes(attributes);
+    }
+
+    byte[] build() {
+        GrowableByteBuffer buf = new GrowableByteBuffer();
+        build(buf);
+        return buf.bytes();
+    }
+
+    protected C emitNarrowJumpOp(Opcode opcode, short target) {
+        emitOp(opcode);
+        emitOffset(code, JumpMode.NARROW, target);
+        return thisBuilder();
+    }
+
+    protected C emitWideJumpOp(Opcode opcode, int target) {
+        emitOp(opcode);
+        emitOffset(code, JumpMode.WIDE, target);
+        return thisBuilder();
+    }
+
+    protected C emitOp(Opcode opcode) {
+        return emitOp(opcode, null);
+    }
+
+    protected C emitOp(Opcode opcode, Object optPoolValue) {
+        code.writeByte(opcode.code);
+        return thisBuilder();
+    }
+
+    protected void emitOffset(GrowableByteBuffer buf, JumpMode jumpMode, int offset) {
+        if (jumpMode == JumpMode.NARROW) {
+            buf.writeChar((short) offset);
+        } else {
+            buf.writeInt(offset);
+        }
+    }
+
+    int offset() {
+        return code.offset;
+    }
+
+    /*** stackmap support ***/
+
+    /**
+     * The tags and constants used in compressed stackmap.
+     */
+    static final int SAME_FRAME_SIZE = 64;
+    static final int SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247;
+    static final int SAME_FRAME_EXTENDED = 251;
+    static final int FULL_FRAME = 255;
+    static final int MAX_LOCAL_LENGTH_DIFF = 4;
+
+    @SuppressWarnings("unchecked")
+    private void writeStackMapType(T t) {
+        if (t == null) {
+            stackmaps.writeByte(0);
+        } else {
+            switch (typeHelper.tag(t)) {
+                case B:
+                case C:
+                case S:
+                case I:
+                case Z:
+                    stackmaps.writeByte(1);
+                    break;
+                case F:
+                    stackmaps.writeByte(2);
+                    break;
+                case D:
+                    stackmaps.writeByte(3);
+                    break;
+                case J:
+                    stackmaps.writeByte(4);
+                    break;
+                case A:
+                    if (t == typeHelper.nullType()) {
+                        stackmaps.writeByte(5); //null
+                    } else {
+                        //TODO: uninit this, top?
+                        stackmaps.writeByte(7);
+                        stackmaps.writeChar(poolHelper.putClass(typeHelper.symbol(t)));
+                    }
+                    break;
+                default:
+                    throw new IllegalStateException("Bad type");
+            }
+        }
+    }
+
+    public void sameFrame(int offsetDelta) {
+        int frameType = (offsetDelta < SAME_FRAME_SIZE) ?
+                offsetDelta : SAME_FRAME_EXTENDED;
+        stackmaps.writeByte(frameType);
+        if (frameType == SAME_FRAME_EXTENDED) {
+            stackmaps.writeChar(offsetDelta);
+        }
+    }
+
+    public void sameLocals1StackItemFrame(int offsetDelta, T stackItem) {
+        int frameType = (offsetDelta < SAME_FRAME_SIZE) ?
+                (SAME_FRAME_SIZE + offsetDelta) : SAME_LOCALS_1_STACK_ITEM_EXTENDED;
+        stackmaps.writeByte(frameType);
+        if (frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) {
+            stackmaps.writeChar(offsetDelta);
+        }
+        writeStackMapType(stackItem);
+    }
+
+    public void appendFrame(int offsetDelta, int prevLocalsSize, List<T> locals) {
+        int frameType = SAME_FRAME_EXTENDED + (locals.size() - prevLocalsSize);
+        stackmaps.writeByte(frameType);
+        stackmaps.writeChar(offsetDelta);
+        for (int i = prevLocalsSize; i < locals.size(); i++) {
+            writeStackMapType(locals.get(i));
+        }
+    }
+
+    public void chopFrame(int offsetDelta, int droppedVars) {
+        int frameType = SAME_FRAME_EXTENDED - droppedVars;
+        stackmaps.writeByte(frameType);
+        stackmaps.writeChar(offsetDelta);
+    }
+
+    public void fullFrame(int offsetDelta, List<T> locals, List<T> stackItems) {
+        stackmaps.writeByte(FULL_FRAME);
+        stackmaps.writeChar(offsetDelta);
+        stackmaps.writeChar(locals.size());
+        for (T local : locals) {
+            writeStackMapType(local);
+        }
+
+        stackmaps.writeChar(stackItems.size());
+        for (T stackType : stackItems) {
+            writeStackMapType(stackType);
+        }
+    }
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/DeclBuilder.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/DeclBuilder.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/DeclBuilder.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+import java.util.function.Consumer;
+
+/**
+ * Declaration (class, class member, ...) builder.
+ *
+ * @param <S> the type of symbol representation
+ * @param <T> the type of type descriptors representation
+ * @param <E> the type of pool entries
+ * @param <D> the type of this builder
+ */
+public class DeclBuilder<S, T, E, D extends DeclBuilder<S, T, E, D>>
+        extends AttributeBuilder<S, T, E, D> {
+
+    /**
+     * The access flags of the declaration, as bit flags.
+     */
+    protected int flags;
+
+    AnnotationsBuilder<S, T, E> runtimeInvisibleAnnotations;
+    AnnotationsBuilder<S, T, E> runtimeVisibleAnnotations;
+
+    /**
+     * Create a declaration builder,
+     *
+     * @param poolHelper the helper to build the constant pool
+     * @param typeHelper the helper to use to manipulate type descriptors
+     */
+    DeclBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
+        super(poolHelper, typeHelper);
+    }
+
+    /**
+     * Specify the class file flags for this declaration.
+     *
+     * @param flags the flags as {@code Flag} objects
+     * @return this builder, for chained calls
+     */
+    public D withFlags(Flag... flags) {
+        for (Flag f : flags) {
+            this.flags |= f.flag;
+        }
+        return thisBuilder();
+    }
+
+    /**
+     * Specify, via bits, the class file flags for this declaration.
+     *
+     * @param flags the flags as bit settings
+     * @return this builder, for chained calls
+     */
+    public D withFlags(int flags) {
+        withFlags(Flag.parse(flags));
+        return thisBuilder();
+    }
+
+    public D withAnnotation(AnnotationsBuilder.Kind kind, T annoType) {
+        getAnnotations(kind).withAnnotation(annoType, null);
+        return thisBuilder();
+    }
+
+    public D withAnnotation(AnnotationsBuilder.Kind kind, T annoType, Consumer<? super AnnotationsBuilder<S, T, E>.AnnotationElementBuilder> annotations) {
+        getAnnotations(kind).withAnnotation(annoType, annotations);
+        return thisBuilder();
+    }
+
+    private AnnotationsBuilder<S, T, E> getAnnotations(AnnotationsBuilder.Kind kind) {
+        switch (kind) {
+            case RUNTIME_INVISIBLE:
+                if (runtimeInvisibleAnnotations == null) {
+                    runtimeInvisibleAnnotations = new AnnotationsBuilder<>(poolHelper, typeHelper);
+                }
+                return runtimeInvisibleAnnotations;
+            case RUNTIME_VISIBLE:
+                if (runtimeVisibleAnnotations == null) {
+                    runtimeVisibleAnnotations = new AnnotationsBuilder<>(poolHelper, typeHelper);
+                }
+                return runtimeVisibleAnnotations;
+        }
+        throw new IllegalStateException();
+    }
+
+    void addAnnotations() {
+        if (runtimeVisibleAnnotations != null) {
+            withAttribute("RuntimeVisibleAnnotations", runtimeVisibleAnnotations.build());
+        }
+        if (runtimeInvisibleAnnotations != null) {
+            withAttribute("RuntimeInvisibleAnnotations", runtimeVisibleAnnotations.build());
+        }
+    }
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/FieldBuilder.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/FieldBuilder.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/FieldBuilder.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+/**
+ * Field builder.
+ *
+ * @param <S> the type of symbol representation
+ * @param <T> the type of type descriptor representation
+ * @param <E> the type of pool entries
+ */
+public class FieldBuilder<S, T, E> extends MemberBuilder<S, T, E, FieldBuilder<S, T, E>> {
+    public FieldBuilder(CharSequence name, T type, PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
+        super(name, type, poolHelper, typeHelper);
+    }
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Flag.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Flag.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Flag.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+import java.util.EnumSet;
+
+public enum Flag {
+    ACC_PUBLIC(0x0001),
+    ACC_PROTECTED(0x0004),
+    ACC_PRIVATE(0x0002),
+    ACC_IDENTITY(0x0020),
+    ACC_INTERFACE(0x0200),
+    ACC_ENUM(0x4000),
+    ACC_ANNOTATION(0x2000),
+    ACC_SUPER(0x0020),
+    ACC_ABSTRACT(0x0400),
+    ACC_VOLATILE(0x0040),
+    ACC_TRANSIENT(0x0080),
+    ACC_SYNTHETIC(0x1000),
+    ACC_STATIC(0x0008),
+    ACC_FINAL(0x0010),
+    ACC_SYNCHRONIZED(0x0020),
+    ACC_BRIDGE(0x0040),
+    ACC_VARARGS(0x0080),
+    ACC_NATIVE(0x0100),
+    ACC_INLINE(0x0100),
+    ACC_STRICT(0x0800);
+
+    public int flag;
+
+    Flag(int flag) {
+        this.flag = flag;
+    }
+
+    static Flag[] parse(int flagsMask) {
+        EnumSet<Flag> flags = EnumSet.noneOf(Flag.class);
+        for (Flag f : Flag.values()) {
+            if ((f.flag & flagsMask) != 0) {
+                flags.add(f);
+            }
+        }
+        return flags.stream().toArray(Flag[]::new);
+    }
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/GrowableByteBuffer.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/GrowableByteBuffer.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/GrowableByteBuffer.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+import java.util.function.Consumer;
+
+public class GrowableByteBuffer {
+
+    public GrowableByteBuffer() {
+    }
+
+    byte[] elems = new byte[64];
+    int offset = 0;
+
+    public GrowableByteBuffer writeByte(int b) {
+        return writeBytes(1, b);
+    }
+
+    public GrowableByteBuffer writeChar(int x) {
+        return writeBytes(2, x);
+    }
+
+    public GrowableByteBuffer writeInt(int x) {
+        return writeBytes(4, x);
+    }
+
+    public GrowableByteBuffer writeFloat(float x) {
+        return writeInt(Float.floatToIntBits(x));
+    }
+
+    public GrowableByteBuffer writeLong(long x) {
+        return writeBytes(8, x);
+    }
+
+    public GrowableByteBuffer writeDouble(double x) {
+        writeLong(Double.doubleToLongBits(x));
+        return this;
+    }
+
+    public GrowableByteBuffer writeBytes(byte[] barr) {
+        expandIfNeeded(barr.length);
+        System.arraycopy(barr, 0, elems, offset, barr.length);
+        offset += barr.length;
+        return this;
+    }
+
+    public GrowableByteBuffer writeBytes(GrowableByteBuffer bb) {
+        expandIfNeeded(bb.offset);
+        System.arraycopy(bb.elems, 0, elems, offset, bb.offset);
+        offset += bb.offset;
+        return this;
+    }
+
+    public GrowableByteBuffer withOffset(int offset, Consumer<GrowableByteBuffer> actions) {
+        int prevOffset = this.offset;
+        this.offset = offset;
+        actions.accept(this);
+        this.offset = prevOffset;
+        return this;
+    }
+
+    private GrowableByteBuffer writeBytes(int size, long x) {
+        expandIfNeeded(size);
+        for (int i = 0; i < size; i++) {
+            elems[offset++] = (byte) ((x >> 8 * (size - i - 1)) & 0xFF);
+        }
+        return this;
+    }
+
+    void expandIfNeeded(int increment) {
+        if (offset + increment > elems.length) {
+            int newsize = elems.length * 2;
+            while (offset + increment > newsize) {
+                newsize *= 2;
+            }
+            byte[] newelems = new byte[newsize];
+            System.arraycopy(elems, 0, newelems, 0, offset);
+            elems = newelems;
+        }
+    }
+
+    public byte[] bytes() {
+        byte[] bytes = new byte[offset];
+        System.arraycopy(elems, 0, bytes, 0, offset);
+        return bytes;
+    }
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MacroCodeBuilder.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MacroCodeBuilder.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MacroCodeBuilder.java
@@ -0,0 +1,688 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.function.Consumer;
+
+public class MacroCodeBuilder<S, T, E, C extends MacroCodeBuilder<S, T, E, C>> extends CodeBuilder<S, T, E, C> {
+
+    JumpMode jumpMode = JumpMode.NARROW;
+
+    Map<CharSequence, Integer> labels = new HashMap<>();
+    List<PendingJump> pendingJumps = new LinkedList<>();
+
+    class PendingJump {
+        CharSequence label;
+        int pc;
+
+        PendingJump(CharSequence label, int pc) {
+            this.label = label;
+            this.pc = pc;
+        }
+
+        boolean resolve(CharSequence label, int offset) {
+            if (this.label.equals(label)) {
+                //patch offset
+                code.withOffset(pc + 1, buf -> emitOffset(buf, jumpMode, offset - pc));
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    public enum InvocationKind {
+        INVOKESTATIC,
+        INVOKEVIRTUAL,
+        INVOKESPECIAL,
+        INVOKEINTERFACE;
+    }
+
+    public enum FieldAccessKind {
+        STATIC,
+        INSTANCE;
+    }
+
+    public enum CondKind {
+        EQ(0),
+        NE(1),
+        LT(2),
+        GE(3),
+        GT(4),
+        LE(5);
+
+        int offset;
+
+        CondKind(int offset) {
+            this.offset = offset;
+        }
+
+        public CondKind negate() {
+            switch (this) {
+                case EQ:
+                    return NE;
+                case NE:
+                    return EQ;
+                case LT:
+                    return GE;
+                case GE:
+                    return LT;
+                case GT:
+                    return LE;
+                case LE:
+                    return GT;
+                default:
+                    throw new IllegalStateException("Unknown cond");
+            }
+        }
+    }
+
+    static class WideJumpException extends RuntimeException {
+        static final long serialVersionUID = 42L;
+    }
+
+    public MacroCodeBuilder(MethodBuilder<S, T, E> methodBuilder) {
+        super(methodBuilder);
+    }
+
+    public C load(TypeTag type, int n) {
+        switch (n) {
+            case 0:
+                return emitOp(Opcode.ILOAD_0.at(type, 4));
+            case 1:
+                return emitOp(Opcode.ILOAD_1.at(type, 4));
+            case 2:
+                return emitOp(Opcode.ILOAD_2.at(type, 4));
+            case 3:
+                return emitOp(Opcode.ILOAD_3.at(type, 4));
+            default:
+                return emitWideIfNeeded(Opcode.ILOAD.at(type), n);
+        }
+    }
+
+    public C store(TypeTag type, int n) {
+        switch (n) {
+            case 0:
+                return emitOp(Opcode.ISTORE_0.at(type, 4));
+            case 1:
+                return emitOp(Opcode.ISTORE_1.at(type, 4));
+            case 2:
+                return emitOp(Opcode.ISTORE_2.at(type, 4));
+            case 3:
+                return emitOp(Opcode.ISTORE_3.at(type, 4));
+            default:
+                return emitWideIfNeeded(Opcode.ISTORE.at(type), n);
+        }
+    }
+
+    public C arrayload(TypeTag type) {
+        return emitOp(Opcode.IALOAD.at(type));
+    }
+
+    public C arraystore(TypeTag type, int n) {
+        return emitOp(Opcode.IASTORE.at(type));
+    }
+
+    public C const_(int i) {
+        switch (i) {
+            case -1:
+                return iconst_m1();
+            case 0:
+                return iconst_0();
+            case 1:
+                return iconst_1();
+            case 2:
+                return iconst_2();
+            case 3:
+                return iconst_3();
+            case 4:
+                return iconst_4();
+            case 5:
+                return iconst_5();
+            default:
+                if (i > 0 && i <= Byte.MAX_VALUE) {
+                    return bipush(i);
+                } else if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) {
+                    return sipush(i);
+                } else {
+                    return ldc(i);
+                }
+        }
+    }
+
+    public C const_(long l) {
+        if (l == 0) {
+            return lconst_0();
+        } else if (l == 1) {
+            return lconst_1();
+        } else {
+            return ldc(l);
+        }
+    }
+
+    public C const_(float f) {
+        if (f == 0) {
+            return fconst_0();
+        } else if (f == 1) {
+            return fconst_1();
+        } else if (f == 2) {
+            return fconst_2();
+        } else {
+            return ldc(f);
+        }
+    }
+
+    public C const_(double d) {
+        if (d == 0) {
+            return dconst_0();
+        } else if (d == 1) {
+            return dconst_1();
+        } else {
+            return ldc(d);
+        }
+    }
+
+    public C getfield(FieldAccessKind fak, S owner, CharSequence name, T type) {
+        switch (fak) {
+            case INSTANCE:
+                return getfield(owner, name, type);
+            case STATIC:
+                return getstatic(owner, name, type);
+            default:
+                throw new IllegalStateException();
+        }
+    }
+
+    public C putfield(FieldAccessKind fak, S owner, CharSequence name, T type) {
+        switch (fak) {
+            case INSTANCE:
+                return putfield(owner, name, type);
+            case STATIC:
+                return putstatic(owner, name, type);
+            default:
+                throw new IllegalStateException();
+        }
+    }
+
+    public C invoke(InvocationKind ik, S owner, CharSequence name, T type, boolean isInterface) {
+        switch (ik) {
+            case INVOKESTATIC:
+                return invokestatic(owner, name, type, isInterface);
+            case INVOKEVIRTUAL:
+                return invokevirtual(owner, name, type, isInterface);
+            case INVOKESPECIAL:
+                return invokespecial(owner, name, type, isInterface);
+            case INVOKEINTERFACE:
+                if (!isInterface) throw new AssertionError();
+                return invokeinterface(owner, name, type);
+            default:
+                throw new IllegalStateException();
+        }
+    }
+
+    public C add(TypeTag type) {
+        return emitOp(Opcode.IADD.at(type));
+    }
+
+    public C sub(TypeTag type) {
+        return emitOp(Opcode.ISUB.at(type));
+    }
+
+    public C mul(TypeTag type) {
+        return emitOp(Opcode.IMUL.at(type));
+    }
+
+    public C div(TypeTag type) {
+        return emitOp(Opcode.IDIV.at(type));
+    }
+
+    public C rem(TypeTag type) {
+        return emitOp(Opcode.IREM.at(type));
+    }
+
+    public C neg(TypeTag type) {
+        return emitOp(Opcode.INEG.at(type));
+    }
+
+    public C shl(TypeTag type) {
+        return emitOp(Opcode.ISHL.at(type));
+    }
+
+    public C shr(TypeTag type) {
+        return emitOp(Opcode.ISHR.at(type));
+    }
+
+    public C ushr(TypeTag type) {
+        return emitOp(Opcode.ISHR.at(type));
+    }
+
+    public C and(TypeTag type) {
+        return emitOp(Opcode.IAND.at(type));
+    }
+
+    public C or(TypeTag type) {
+        return emitOp(Opcode.IOR.at(type));
+    }
+
+    public C xor(TypeTag type) {
+        return emitOp(Opcode.IXOR.at(type));
+    }
+
+    public C return_(TypeTag type) {
+        switch (type) {
+            case V:
+            case Q:
+                return return_();
+            default:
+                return emitOp(Opcode.IRETURN.at(type));
+        }
+    }
+
+    public C conv(TypeTag from, TypeTag to) {
+        switch (from) {
+            case B:
+            case C:
+            case S:
+                switch (to) {
+                    case J:
+                        return i2l();
+                    case F:
+                        return i2f();
+                    case D:
+                        return i2d();
+                }
+                break;
+            case I:
+                switch (to) {
+                    case J:
+                        return i2l();
+                    case F:
+                        return i2f();
+                    case D:
+                        return i2d();
+                    case B:
+                        return i2b();
+                    case C:
+                        return i2c();
+                    case S:
+                        return i2s();
+                }
+                break;
+            case J:
+                switch (to) {
+                    case I:
+                        return l2i();
+                    case F:
+                        return l2f();
+                    case D:
+                        return l2d();
+                }
+                break;
+            case F:
+                switch (to) {
+                    case I:
+                        return f2i();
+                    case J:
+                        return f2l();
+                    case D:
+                        return f2d();
+                }
+                break;
+            case D:
+                switch (to) {
+                    case I:
+                        return d2i();
+                    case J:
+                        return d2l();
+                    case F:
+                        return d2f();
+                }
+                break;
+        }
+        //no conversion is necessary - do nothing!
+        return thisBuilder();
+    }
+
+    public C if_null(CharSequence label) {
+        return emitCondJump(Opcode.IF_NULL, Opcode.IF_NONNULL, label);
+    }
+
+    public C if_nonnull(CharSequence label) {
+        return emitCondJump(Opcode.IF_NONNULL, Opcode.IF_NULL, label);
+    }
+
+    public C ifcmp(TypeTag type, CondKind cond, CharSequence label) {
+        switch (type) {
+            case I:
+                return emitCondJump(Opcode.IF_ICMPEQ, cond, label);
+            case A:
+                return emitCondJump(Opcode.IF_ACMPEQ, cond, label);
+            case J:
+                return lcmp().emitCondJump(Opcode.IFEQ, cond, label);
+            case D:
+                return dcmpg().emitCondJump(Opcode.IFEQ, cond, label);
+            case F:
+                return fcmpg().emitCondJump(Opcode.IFEQ, cond, label);
+            default:
+                throw new IllegalArgumentException("Bad cmp type");
+        }
+    }
+
+    public C goto_(CharSequence label) {
+        emitOp(jumpMode == JumpMode.NARROW ? Opcode.GOTO_ : Opcode.GOTO_W);
+        emitOffset(code, jumpMode, labelOffset(label));
+        return thisBuilder();
+    }
+
+    protected int labelOffset(CharSequence label) {
+        int pc = code.offset - 1;
+        Integer labelPc = labels.get(label);
+        if (labelPc == null) {
+            addPendingJump(label, pc);
+        }
+        return labelPc == null ? 0 : (labelPc - pc);
+    }
+
+    public C label(CharSequence s) {
+        int pc = code.offset;
+        Object old = labels.put(s, pc);
+        if (old != null) {
+            throw new IllegalStateException("label already exists");
+        }
+        resolveJumps(s, pc);
+        return thisBuilder();
+    }
+
+    //FIXME: address this jumpy mess - i.e. offset and state update work against each other!
+    public C emitCondJump(Opcode opcode, CondKind ck, CharSequence label) {
+        return emitCondJump(opcode.at(ck), opcode.at(ck.negate()), label);
+    }
+
+    public C emitCondJump(Opcode pos, Opcode neg, CharSequence label) {
+        if (jumpMode == JumpMode.NARROW) {
+            emitOp(pos);
+            emitOffset(code, jumpMode, labelOffset(label));
+        } else {
+            emitOp(neg);
+            emitOffset(code, JumpMode.NARROW, 8);
+            goto_w(labelOffset(label));
+        }
+        return thisBuilder();
+    }
+
+    void addPendingJump(CharSequence label, int pc) {
+        pendingJumps.add(new PendingJump(label, pc));
+    }
+
+    void resolveJumps(CharSequence label, int pc) {
+        Iterator<PendingJump> jumpsIt = pendingJumps.iterator();
+        while (jumpsIt.hasNext()) {
+            PendingJump jump = jumpsIt.next();
+            if (jump.resolve(label, pc)) {
+                jumpsIt.remove();
+            }
+        }
+    }
+
+    @Override
+    protected void emitOffset(GrowableByteBuffer buf, JumpMode jumpMode, int offset) {
+        if (jumpMode == JumpMode.NARROW && (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE)) {
+            throw new WideJumpException();
+        }
+        super.emitOffset(buf, jumpMode, offset);
+    }
+
+    public C jsr(CharSequence label) {
+        emitOp(jumpMode == JumpMode.NARROW ? Opcode.JSR : Opcode.JSR_W);
+        emitOffset(code, jumpMode, labelOffset(label));
+        return thisBuilder();
+    }
+
+    @SuppressWarnings("unchecked")
+    public C withTry(Consumer<? super C> tryBlock, Consumer<? super CatchBuilder> catchBlocks) {
+        int start = code.offset;
+        tryBlock.accept((C) this);
+        int end = code.offset;
+        CatchBuilder catchBuilder = makeCatchBuilder(start, end);
+        catchBlocks.accept(catchBuilder);
+        catchBuilder.build();
+        return thisBuilder();
+    }
+
+    void clear() {
+        code.offset = 0;
+        catchers.offset = 0;
+        ncatchers = 0;
+        labels.clear();
+        pendingJumps = null;
+    }
+
+    protected CatchBuilder makeCatchBuilder(int start, int end) {
+        return new CatchBuilder(start, end);
+    }
+
+    public class CatchBuilder {
+        int start, end;
+
+        String endLabel = labelName();
+
+        Map<S, Consumer<? super C>> catchers = new LinkedHashMap<>();
+        public Consumer<? super C> finalizer;
+        List<Integer> pendingGaps = new ArrayList<>();
+
+        public CatchBuilder(int start, int end) {
+            this.start = start;
+            this.end = end;
+        }
+
+        public CatchBuilder withCatch(S exc, Consumer<? super C> catcher) {
+            catchers.put(exc, catcher);
+            return this;
+        }
+
+        public CatchBuilder withFinally(Consumer<? super C> finalizer) {
+            this.finalizer = finalizer;
+            return this;
+        }
+
+        @SuppressWarnings("unchecked")
+        void build() {
+            if (finalizer != null) {
+                finalizer.accept((C) MacroCodeBuilder.this);
+            }
+            goto_(endLabel);
+            for (Map.Entry<S, Consumer<? super C>> catcher_entry : catchers.entrySet()) {
+                emitCatch(catcher_entry.getKey(), catcher_entry.getValue());
+            }
+            if (finalizer != null) {
+                emitFinalizer();
+            }
+            resolveJumps(endLabel, code.offset);
+        }
+
+        @SuppressWarnings("unchecked")
+        protected void emitCatch(S exc, Consumer<? super C> catcher) {
+            int offset = code.offset;
+            MacroCodeBuilder.this.withCatch(exc, start, end, offset);
+            catcher.accept((C) MacroCodeBuilder.this);
+            if (finalizer != null) {
+                int startFinalizer = code.offset;
+                finalizer.accept((C) MacroCodeBuilder.this);
+                pendingGaps.add(startFinalizer);
+                pendingGaps.add(code.offset);
+            }
+            goto_(endLabel);
+        }
+
+        @SuppressWarnings("unchecked")
+        protected void emitFinalizer() {
+            int offset = code.offset;
+            pop();
+            for (int i = 0; i < pendingGaps.size(); i += 2) {
+                MacroCodeBuilder.this.withCatch(null, pendingGaps.get(i), pendingGaps.get(i + 1), offset);
+            }
+            MacroCodeBuilder.this.withCatch(null, start, end, offset);
+            finalizer.accept((C) MacroCodeBuilder.this);
+        }
+
+//        @SuppressWarnings("unchecked")
+//        CatchBuilder withCatch(S exc, Consumer<? super C> catcher) {
+//            int offset = code.offset;
+//            MacroCodeBuilder.this.withCatch(exc, start, end, offset);
+//            catcher.accept((C)MacroCodeBuilder.this);
+//            return this;
+//        }
+//
+//        @SuppressWarnings("unchecked")
+//        CatchBuilder withFinally(Consumer<? super C> catcher) {
+//            int offset = code.offset;
+//            MacroCodeBuilder.this.withCatch(null, start, end, offset);
+//            catcher.accept((C)MacroCodeBuilder.this);
+//            return this;
+//        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public C switch_(Consumer<? super SwitchBuilder> consumer) {
+        int start = code.offset;
+        SwitchBuilder sb = makeSwitchBuilder();
+        consumer.accept(sb);
+        int nlabels = sb.cases.size();
+        switch (sb.switchCode()) {
+            case LOOKUPSWITCH: {
+                int[] lookupOffsets = new int[nlabels * 2];
+                int i = 0;
+                for (Integer v : sb.cases.keySet()) {
+                    lookupOffsets[i] = v;
+                    i += 2;
+                }
+                lookupswitch(0, lookupOffsets);
+                //backpatch lookup
+                int curr = code.offset - (8 * nlabels) - 8;
+                int defaultOffset = code.offset - start;
+                code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, defaultOffset));
+                sb.defaultCase.accept((C) this);
+                curr += 12;
+                for (Consumer<? super C> case_ : sb.cases.values()) {
+                    int offset = code.offset;
+                    code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, offset - start));
+                    case_.accept((C) this);
+                    curr += 8;
+                }
+                break;
+            }
+            case TABLESWITCH: {
+                int[] tableOffsets = new int[sb.hi - sb.lo + 1];
+                tableswitch(sb.lo, sb.hi, 0, tableOffsets);
+                //backpatch table
+                int curr = code.offset - (4 * tableOffsets.length) - 12;
+                int defaultOffset = code.offset - start;
+                code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, defaultOffset));
+                sb.defaultCase.accept((C) this);
+                curr += 12;
+                int lastCasePc = -1;
+                for (int i = sb.lo; i <= sb.hi; i++) {
+                    Consumer<? super C> case_ = sb.cases.get(i);
+                    if (case_ != null) {
+                        lastCasePc = code.offset;
+                        case_.accept((C) this);
+                    }
+                    int offset = lastCasePc - start;
+                    code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, offset));
+                    curr += 4;
+                }
+            }
+        }
+        resolveJumps(sb.endLabel, code.offset);
+        return thisBuilder();
+    }
+
+    private static int labelCount = 0;
+
+    String labelName() {
+        return "label" + labelCount++;
+    }
+
+    protected SwitchBuilder makeSwitchBuilder() {
+        return new SwitchBuilder();
+    }
+
+    public class SwitchBuilder {
+        Map<Integer, Consumer<? super C>> cases = new TreeMap<>();
+        int lo = Integer.MAX_VALUE;
+        int hi = Integer.MIN_VALUE;
+        String endLabel = labelName();
+
+        public Consumer<? super C> defaultCase;
+
+        @SuppressWarnings("unchecked")
+        public SwitchBuilder withCase(int value, Consumer<? super C> case_, boolean fallthrough) {
+            if (value > hi) {
+                hi = value;
+            }
+            if (value < lo) {
+                lo = value;
+            }
+            if (!fallthrough) {
+                Consumer<? super C> prevCase = case_;
+                case_ = C -> {
+                    prevCase.accept(C);
+                    C.goto_(endLabel);
+                };
+            }
+            cases.put(value, case_);
+            return this;
+        }
+
+        @SuppressWarnings("unchecked")
+        public SwitchBuilder withDefault(Consumer<? super C> defaultCase) {
+            if (this.defaultCase != null) {
+                throw new IllegalStateException("default already set");
+            }
+            this.defaultCase = defaultCase;
+            return this;
+        }
+
+        Opcode switchCode() {
+            int nlabels = cases.size();
+            // Determine whether to issue a tableswitch or a lookupswitch
+            // instruction.
+            long table_space_cost = 4 + ((long) hi - lo + 1); // words
+            long lookup_space_cost = 3 + 2 * (long) nlabels;
+            return
+                    nlabels > 0 &&
+                            table_space_cost <= lookup_space_cost
+                            ?
+                            Opcode.TABLESWITCH : Opcode.LOOKUPSWITCH;
+        }
+    }
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MemberBuilder.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MemberBuilder.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MemberBuilder.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+/**
+ * Class member builder.
+ *
+ * @param <S> the type of symbol representation
+ * @param <T> the type of type descriptors representation
+ * @param <E> the type of pool entries
+ * @param <M> the type of this builder
+ */
+public class MemberBuilder<S, T, E, M extends MemberBuilder<S, T, E, M>> extends DeclBuilder<S, T, E, M> {
+
+    CharSequence name;
+    T desc;
+
+    /**
+     * Create a member builder.
+     *
+     * @param name the name of the class member
+     * @param type the type descriptor of the class member
+     * @param poolHelper the helper to build the constant pool
+     * @param typeHelper the helper to use to manipulate type descriptors
+     */
+    MemberBuilder(CharSequence name, T type, PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
+        super(poolHelper, typeHelper);
+        this.name = name;
+        this.desc = type;
+    }
+
+    /**
+     * Build the member.
+     *
+     * @param buf the {@code GrowableByteBuffer} to build the member into
+     */
+    protected void build(GrowableByteBuffer buf) {
+        addAnnotations();
+        buf.writeChar(flags);
+        buf.writeChar(poolHelper.putUtf8(name));
+        buf.writeChar(poolHelper.putType(desc));
+        buf.writeChar(nattrs);
+        buf.writeBytes(attributes);
+    }
+
+    /**
+     * Build the member.
+     *
+     * @return a byte array representation of the member
+     */
+    protected byte[] build() {
+        GrowableByteBuffer buf = new GrowableByteBuffer();
+        addAnnotations();
+        build(buf);
+        return buf.bytes();
+    }
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MethodBuilder.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MethodBuilder.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MethodBuilder.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+import jdk.experimental.bytecode.CodeBuilder.JumpMode;
+
+import java.util.Iterator;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class MethodBuilder<S, T, E> extends MemberBuilder<S, T, E, MethodBuilder<S, T, E>> {
+
+    S thisClass;
+    ParameterAnnotationsBuilder runtimeVisibleParameterAnnotations;
+    ParameterAnnotationsBuilder runtimeInvisibleParameterAnnotations;
+
+    public MethodBuilder(S thisClass, CharSequence name, T type, PoolHelper<S, T, E> pool, TypeHelper<S, T> typeHelper) {
+        super(name, type, pool, typeHelper);
+        this.thisClass = thisClass;
+    }
+
+    public <C extends CodeBuilder<S, T, E, ?>> MethodBuilder<S, T, E> withCode(Function<? super MethodBuilder<S, T, E>, ? extends C> func,
+                                                                               Consumer<? super C> code) {
+        C codeBuilder = func.apply(this);
+        int start = attributes.offset;
+        try {
+            code.accept(codeBuilder);
+        } catch (MacroCodeBuilder.WideJumpException ex) {
+            //wide jumps! Redo the code
+            ((MacroCodeBuilder<S, T, E, ?>) codeBuilder).jumpMode = JumpMode.WIDE;
+            ((MacroCodeBuilder<S, T, E, ?>) codeBuilder).clear();
+            code.accept(codeBuilder);
+        }
+
+        attributes.writeChar(poolHelper.putUtf8("Code"));
+        attributes.writeInt(0);
+        codeBuilder.build(attributes);
+        int length = attributes.offset - start;
+        //avoid using lambda here
+        int prevOffset = attributes.offset;
+        try {
+            attributes.offset = start + 2;
+            attributes.writeInt(length - 6);
+        } finally {
+            attributes.offset = prevOffset;
+        }
+        nattrs++;
+        return this;
+    }
+
+    public MethodBuilder<S, T, E> withCode(Consumer<? super CodeBuilder<S, T, E, ?>> code) {
+        return withCode(CodeBuilder::new, code);
+    }
+
+    @SuppressWarnings({"varargs", "unchecked"})
+    public MethodBuilder<S, T, E> withExceptions(S... exceptions) {
+        attributes.writeChar(poolHelper.putUtf8("Exceptions"));
+        attributes.writeInt(2 + (2 * exceptions.length));
+        attributes.writeChar(exceptions.length);
+        for (S exception : exceptions) {
+            attributes.writeChar(poolHelper.putClass(exception));
+        }
+        nattrs++;
+        return this;
+    }
+
+    public MethodBuilder<S, T, E> withParameterAnnotation(AnnotationsBuilder.Kind kind, int nparam, T annoType) {
+        getParameterAnnotations(kind).builders[nparam].withAnnotation(annoType, null);
+        return this;
+    }
+
+    public MethodBuilder<S, T, E> withParameterAnnotation(AnnotationsBuilder.Kind kind, int nparam, T annoType, Consumer<? super AnnotationsBuilder<S, T, E>.AnnotationElementBuilder> annotations) {
+        getParameterAnnotations(kind).builders[nparam].withAnnotation(annoType, annotations);
+        return this;
+    }
+
+    private ParameterAnnotationsBuilder getParameterAnnotations(AnnotationsBuilder.Kind kind) {
+        switch (kind) {
+            case RUNTIME_INVISIBLE:
+                if (runtimeInvisibleParameterAnnotations == null) {
+                    runtimeInvisibleParameterAnnotations = new ParameterAnnotationsBuilder();
+                }
+                return runtimeInvisibleParameterAnnotations;
+            case RUNTIME_VISIBLE:
+                if (runtimeVisibleParameterAnnotations == null) {
+                    runtimeVisibleParameterAnnotations = new ParameterAnnotationsBuilder();
+                }
+                return runtimeVisibleParameterAnnotations;
+        }
+        throw new IllegalStateException();
+    }
+
+    class ParameterAnnotationsBuilder {
+
+        GrowableByteBuffer parameterAnnos = new GrowableByteBuffer();
+
+        @SuppressWarnings({"unchecked", "rawtypes"})
+        AnnotationsBuilder<S, T, E>[] builders = new AnnotationsBuilder[nparams()];
+
+        ParameterAnnotationsBuilder() {
+            for (int i = 0; i < builders.length; i++) {
+                builders[i] = new AnnotationsBuilder<>(poolHelper, typeHelper);
+            }
+        }
+
+        byte[] build() {
+            parameterAnnos.writeByte(builders.length);
+            for (AnnotationsBuilder<S, T, E> builder : builders) {
+                parameterAnnos.writeBytes(builder.build());
+            }
+            return parameterAnnos.bytes();
+        }
+
+        int nparams() {
+            Iterator<T> paramsIt = typeHelper.parameterTypes(desc);
+            int nparams = 0;
+            while (paramsIt.hasNext()) {
+                paramsIt.next();
+                nparams++;
+            }
+            return nparams;
+        }
+    }
+
+    @Override
+    void addAnnotations() {
+        super.addAnnotations();
+        if (runtimeInvisibleParameterAnnotations != null) {
+            withAttribute("RuntimeInvisibleParameterAnnotations", runtimeInvisibleParameterAnnotations.build());
+        }
+        if (runtimeVisibleParameterAnnotations != null) {
+            withAttribute("RuntimeVisibleParameterAnnotations", runtimeVisibleParameterAnnotations.build());
+        }
+    }
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Opcode.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Opcode.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Opcode.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+import jdk.experimental.bytecode.MacroCodeBuilder.CondKind;
+
+public enum Opcode {
+
+    NOP(0),
+    ACONST_NULL(1),
+    ICONST_M1(2),
+    ICONST_0(3),
+    ICONST_1(4),
+    ICONST_2(5),
+    ICONST_3(6),
+    ICONST_4(7),
+    ICONST_5(8),
+    LCONST_0(9),
+    LCONST_1(10),
+    FCONST_0(11),
+    FCONST_1(12),
+    FCONST_2(13),
+    DCONST_0(14),
+    DCONST_1(15),
+    BIPUSH(16),
+    SIPUSH(17),
+    LDC(18),
+    LDC_W(19),
+    LDC2_W(20),
+    ILOAD(21),
+    LLOAD(22),
+    FLOAD(23),
+    DLOAD(24),
+    ALOAD(25),
+    ILOAD_0(26),
+    ILOAD_1(27),
+    ILOAD_2(28),
+    ILOAD_3(29),
+    LLOAD_0(30),
+    LLOAD_1(31),
+    LLOAD_2(32),
+    LLOAD_3(33),
+    FLOAD_0(34),
+    FLOAD_1(35),
+    FLOAD_2(36),
+    FLOAD_3(37),
+    DLOAD_0(38),
+    DLOAD_1(39),
+    DLOAD_2(40),
+    DLOAD_3(41),
+    ALOAD_0(42),
+    ALOAD_1(43),
+    ALOAD_2(44),
+    ALOAD_3(45),
+    IALOAD(46),
+    LALOAD(47),
+    FALOAD(48),
+    DALOAD(49),
+    AALOAD(50),
+    BALOAD(51),
+    CALOAD(52),
+    SALOAD(53),
+    ISTORE(54),
+    LSTORE(55),
+    FSTORE(56),
+    DSTORE(57),
+    ASTORE(58),
+    ISTORE_0(59),
+    ISTORE_1(60),
+    ISTORE_2(61),
+    ISTORE_3(62),
+    LSTORE_0(63),
+    LSTORE_1(64),
+    LSTORE_2(65),
+    LSTORE_3(66),
+    FSTORE_0(67),
+    FSTORE_1(68),
+    FSTORE_2(69),
+    FSTORE_3(70),
+    DSTORE_0(71),
+    DSTORE_1(72),
+    DSTORE_2(73),
+    DSTORE_3(74),
+    ASTORE_0(75),
+    ASTORE_1(76),
+    ASTORE_2(77),
+    ASTORE_3(78),
+    IASTORE(79),
+    LASTORE(80),
+    FASTORE(81),
+    DASTORE(82),
+    AASTORE(83),
+    BASTORE(84),
+    CASTORE(85),
+    SASTORE(86),
+    POP(87),
+    POP2(88),
+    DUP(89),
+    DUP_X1(90),
+    DUP_X2(91),
+    DUP2(92),
+    DUP2_X1(93),
+    DUP2_X2(94),
+    SWAP(95),
+    IADD(96),
+    LADD(97),
+    FADD(98),
+    DADD(99),
+    ISUB(100),
+    LSUB(101),
+    FSUB(102),
+    DSUB(103),
+    IMUL(104),
+    LMUL(105),
+    FMUL(106),
+    DMUL(107),
+    IDIV(108),
+    LDIV(109),
+    FDIV(110),
+    DDIV(111),
+    IREM(112),
+    LREM(113),
+    FREM(114),
+    DREM(115),
+    INEG(116),
+    LNEG(117),
+    FNEG(118),
+    DNEG(119),
+    ISHL(120),
+    LSHL(121),
+    ISHR(122),
+    LSHR(123),
+    IUSHR(124),
+    LUSHR(125),
+    IAND(126),
+    LAND(127),
+    IOR(128),
+    LOR(129),
+    IXOR(130),
+    LXOR(131),
+    IINC(132),
+    I2L(133),
+    I2F(134),
+    I2D(135),
+    L2I(136),
+    L2F(137),
+    L2D(138),
+    F2I(139),
+    F2L(140),
+    F2D(141),
+    D2I(142),
+    D2L(143),
+    D2F(144),
+    I2B(145),
+    I2C(146),
+    I2S(147),
+    LCMP(148),
+    FCMPL(149),
+    FCMPG(150),
+    DCMPL(151),
+    DCMPG(152),
+    IFEQ(153),
+    IFNE(154),
+    IFLT(155),
+    IFGE(156),
+    IFGT(157),
+    IFLE(158),
+    IF_ICMPEQ(159),
+    IF_ICMPNE(160),
+    IF_ICMPLT(161),
+    IF_ICMPGE(162),
+    IF_ICMPGT(163),
+    IF_ICMPLE(164),
+    IF_ACMPEQ(165),
+    IF_ACMPNE(166),
+    GOTO_(167),
+    JSR(168),
+    RET(169),
+    TABLESWITCH(170),
+    LOOKUPSWITCH(171),
+    IRETURN(172),
+    LRETURN(173),
+    FRETURN(174),
+    DRETURN(175),
+    ARETURN(176),
+    RETURN(177),
+    GETSTATIC(178),
+    PUTSTATIC(179),
+    GETFIELD(180),
+    PUTFIELD(181),
+    INVOKEVIRTUAL(182),
+    INVOKESPECIAL(183),
+    INVOKESTATIC(184),
+    INVOKEINTERFACE(185),
+    INVOKEDYNAMIC(186),
+    NEW(187),
+    NEWARRAY(188),
+    ANEWARRAY(189),
+    ARRAYLENGTH(190),
+    ATHROW(191),
+    CHECKCAST(192),
+    INSTANCEOF(193),
+    MONITORENTER(194),
+    MONITOREXIT(195),
+    WIDE(196),
+    MULTIANEWARRAY(197),
+    IF_NULL(198),
+    IF_NONNULL(199),
+    GOTO_W(200),
+    JSR_W(201),
+    ACONST_INIT(203),
+    WITHFIELD(204);
+
+
+    int code;
+
+    Opcode(int code) {
+        this.code = code;
+    }
+
+    protected Opcode at(TypeTag type) {
+        return at(type, 1);
+    }
+
+    protected Opcode at(CondKind cond) {
+        return at(cond.offset, 1);
+    }
+
+    protected Opcode at(TypeTag type, int multiplier) {
+        return at(type.offset, multiplier);
+    }
+
+    private Opcode at(int offset, int multiplier) {
+        if (offset < 0) throw new AssertionError();
+        return Opcode.values()[code + (multiplier * offset)];
+    }
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/PoolHelper.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/PoolHelper.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/PoolHelper.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+import java.util.function.Consumer;
+import java.util.function.ToIntBiFunction;
+
+/**
+ * An interface for building and tracking constant pools.
+ *
+ * @param <S> the type of the symbol representation
+ * @param <T> the type of type descriptors representation
+ * @param <E> the type of pool entries
+ */
+public interface PoolHelper<S, T, E> {
+    int putClass(S symbol);
+
+    int putInlineClass(S symbol);
+
+    int putFieldRef(S owner, CharSequence name, T type);
+
+    int putMethodRef(S owner, CharSequence name, T type, boolean isInterface);
+
+    int putUtf8(CharSequence s);
+
+    int putInt(int i);
+
+    int putFloat(float f);
+
+    int putLong(long l);
+
+    int putDouble(double d);
+
+    int putString(String s);
+
+    int putType(T t);
+
+    int putMethodType(T t);
+
+    int putHandle(int refKind, S owner, CharSequence name, T type);
+
+    int putHandle(int refKind, S owner, CharSequence name, T type, boolean isInterface);
+
+    int putInvokeDynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs);
+
+    int putDynamicConstant(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs);
+
+    int size();
+
+    E entries();
+
+    interface StaticArgListBuilder<S, T, E> {
+        StaticArgListBuilder<S, T, E> add(int i);
+        StaticArgListBuilder<S, T, E> add(float f);
+        StaticArgListBuilder<S, T, E> add(long l);
+        StaticArgListBuilder<S, T, E> add(double d);
+        StaticArgListBuilder<S, T, E> add(String s);
+        StaticArgListBuilder<S, T, E> add(int refKind, S owner, CharSequence name, T type);
+        <Z> StaticArgListBuilder<S, T, E> add(Z z, ToIntBiFunction<PoolHelper<S, T, E>, Z> poolFunc);
+        StaticArgListBuilder<S, T, E> add(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgList);
+    }
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/PoolTag.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/PoolTag.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/PoolTag.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+public enum PoolTag {
+    CONSTANT_UTF8(1),
+    CONSTANT_UNICODE(2),
+    CONSTANT_INTEGER(3),
+    CONSTANT_FLOAT(4),
+    CONSTANT_LONG(5),
+    CONSTANT_DOUBLE(6),
+    CONSTANT_CLASS(7),
+    CONSTANT_STRING(8),
+    CONSTANT_FIELDREF(9),
+    CONSTANT_METHODREF(10),
+    CONSTANT_INTERFACEMETHODREF(11),
+    CONSTANT_NAMEANDTYPE(12),
+    CONSTANT_METHODHANDLE(15),
+    CONSTANT_METHODTYPE(16),
+    CONSTANT_DYNAMIC(17),
+    CONSTANT_INVOKEDYNAMIC(18);
+
+    public final int tag;
+
+    PoolTag(int tag) {
+        this.tag = tag;
+    }
+
+    static PoolTag from(int tag) {
+        return values()[tag - 1];
+    }
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Type.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Type.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Type.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+public interface Type {
+    TypeTag getTag();
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypeHelper.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypeHelper.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypeHelper.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+import java.util.Iterator;
+
+/**
+ * Helper to create and manipulate type descriptors of T.
+ *
+ * @param <S> the type of symbols
+ * @param <T> the type of type descriptors
+ */
+public interface TypeHelper<S, T> {
+    /**
+     * Return the type descriptor of an element given the type
+     * descriptor of an array.
+     *
+     * @param t the type descriptor of the array
+     * @return the element type
+     */
+    T elemtype(T t);
+
+    /**
+     * Return the type descriptor of an array given the type descriptor
+     * of an element.
+     *
+     * @param t the type descriptor of the element
+     * @return the type descriptor of the array
+     */
+    T arrayOf(T t);
+
+    /**
+     * Return an iterator over the type descriptors of the parameters of a
+     * method.
+       *
+     * @param t the method type descriptor
+     * @return an iterator over the type descriptors of the parameters
+     */
+    Iterator<T> parameterTypes(T t);
+
+    /**
+     * Return the type descriptor of a {@code TypeTag}.
+     *
+     * @param tag the {@code TypeTag} of a primitive type
+     * @return the type descriptor of the primitive type
+     */
+    T fromTag(TypeTag tag);
+
+    /**
+     * Return the return type descriptor of a method.
+     *
+     * @param t the method type descriptor
+     * @return the return type descriptor
+     */
+    T returnType(T t);
+
+    /**
+     * Return the type descriptor for a symbol.
+     *
+     * @param s the symbol
+     * @return the type descriptor
+     */
+    T type(S s);
+
+    /**
+     * Return the symbol corresponding to a type descriptor.
+     *
+     * @param type the type descriptor
+     * @return the symbol
+     */
+    S symbol(T type);
+
+    /**
+     * Return the {@code TypeTag} corresponding to a type descriptor.  Reference
+     * types return {@code TypeTag.A}.
+     *
+     * @param t a type descriptor
+     * @return the corresponding {@code TypeTag}
+     */
+    TypeTag tag(T t);
+
+    /**
+     * Return the symbol corresponding to a JVM type descriptor string.
+     *
+     * @param s a JVM type descriptor string
+     * @return the corresponding symbol
+     */
+    S symbolFrom(String s);
+
+    /**
+     * Return the common supertype descriptor of two type descriptors.
+     *
+     * @param t1 a type descriptor
+     * @param t2 a type descriptor
+     * @return the common supertype descriptor
+     */
+    T commonSupertype(T t1, T t2);
+
+    /**
+     * Return the type descriptor for the null type.
+     *
+     * @return the type descriptor for the null type
+     */
+    T nullType();
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypeTag.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypeTag.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypeTag.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+public enum TypeTag implements Type {
+    /**
+     * byte
+     */
+    B("B", 0, 1, 8),
+    /**
+     * short
+     */
+    S("S", 0, 1, 9),
+    /**
+     * int
+     */
+    I("I", 0, 1, 10),
+    /**
+     * float
+     */
+    F("F", 2, 1, 6),
+    /**
+     * long
+     */
+    J("J", 1, 2, 11),
+    /**
+     * double
+     */
+    D("D", 3, 2, 7),
+    /**
+     * Reference type
+     */
+    A("A", 4, 1, -1),
+    /**
+     * char
+     */
+    C("C", 0, 1, 5),
+    /**
+     * boolean
+     */
+    Z("Z", 0, 1, 4),
+    /**
+     * void
+     */
+    V("V", -1, -1, -1),
+    /**
+     * Value type
+     */
+    Q("Q", -1, 1, -1);
+
+    String typeStr;
+    int offset;
+    int width;
+    int newarraycode;
+
+    TypeTag(String typeStr, int offset, int width, int newarraycode) {
+        this.typeStr = typeStr;
+        this.offset = offset;
+        this.width = width;
+        this.newarraycode = newarraycode;
+    }
+
+    static TypeTag commonSupertype(TypeTag t1, TypeTag t2) {
+        if (t1.isIntegral() && t2.isIntegral()) {
+            int p1 = t1.ordinal();
+            int p2 = t2.ordinal();
+            return (p1 <= p2) ? t2 : t1;
+        } else {
+            return null;
+        }
+    }
+
+    public int width() {
+        return width;
+    }
+
+    boolean isIntegral() {
+        switch (this) {
+            case B:
+            case S:
+            case I:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    @Override
+    public TypeTag getTag() {
+        return this;
+    }
+}
diff a/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypedCodeBuilder.java b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypedCodeBuilder.java
--- /dev/null
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypedCodeBuilder.java
@@ -0,0 +1,1133 @@
+/*
+ * Copyright (c) 2017, 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 jdk.experimental.bytecode;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Vector;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.function.ToIntFunction;
+
+public class TypedCodeBuilder<S, T, E, C extends TypedCodeBuilder<S, T, E, C>> extends MacroCodeBuilder<S, T, E, C> {
+
+    State lastStackMapState;
+    int lastStackMapPc = -1;
+    Map<CharSequence, LocalVarInfo> lvarOffsets = new HashMap<>();
+    protected State state;
+    int depth = 0;
+    int currLocalOffset = 0;
+
+    class StatefulPendingJump extends PendingJump {
+
+        State state;
+
+        StatefulPendingJump(CharSequence label, int pc, State state) {
+            super(label, pc);
+            this.state = state;
+        }
+
+        @Override
+        boolean resolve(CharSequence label, int pc) {
+            boolean b = super.resolve(label, pc);
+            if (b) {
+                TypedCodeBuilder.this.state = TypedCodeBuilder.this.state.merge(state);
+            }
+            return b;
+        }
+    }
+
+    class LocalVarInfo {
+        CharSequence name;
+        int offset;
+        int depth;
+        TypeTag type;
+
+        LocalVarInfo(CharSequence name, int offset, int depth, TypeTag type) {
+            this.name = name;
+            this.offset = offset;
+            this.depth = depth;
+            this.type = type;
+        }
+    }
+
+    public TypedCodeBuilder(MethodBuilder<S, T, E> methodBuilder) {
+        super(methodBuilder);
+        T t = methodBuilder.desc;
+        state = new State();
+        if ((methodBuilder.flags & Flag.ACC_STATIC.flag) == 0) {
+            T clazz = typeHelper.type(methodBuilder.thisClass);
+            state.load(clazz, currLocalOffset++); //TODO: uninit??
+        }
+        Iterator<T> paramsIt = typeHelper.parameterTypes(t);
+        while (paramsIt.hasNext()) {
+            T p = paramsIt.next();
+            state.load(p, currLocalOffset);
+            currLocalOffset += typeHelper.tag(p).width;
+        }
+        lastStackMapState = state.dup();
+        stacksize = state.stack.size();
+        localsize = state.locals.size();
+    }
+
+    @Override
+    protected C emitOp(Opcode opcode, Object optPoolValue) {
+        updateState(opcode, optPoolValue);
+        return super.emitOp(opcode, optPoolValue);
+    }
+
+    @Override
+    protected SwitchBuilder makeSwitchBuilder() {
+        return new TypedSwitchBuilder();
+    }
+
+    class TypedSwitchBuilder extends SwitchBuilder {
+
+        @Override
+        public SwitchBuilder withCase(int value, Consumer<? super C> case_, boolean fallthrough) {
+            super.withCase(value, c -> {
+                withLocalScope(() -> {
+                    State prevState = state;
+                    state = prevState.dup();
+                    emitStackMap(c.offset());
+                    case_.accept(c);
+                    state = prevState;
+                });
+            }, fallthrough);
+            return this;
+        }
+
+        @Override
+        public SwitchBuilder withDefault(Consumer<? super C> defaultCase) {
+            super.withDefault(c -> {
+                withLocalScope(() -> {
+                    State prevState = state;
+                    state = prevState.dup();
+                    emitStackMap(c.offset());
+                    defaultCase.accept(c);
+                    state = prevState;
+                });
+            });
+            return this;
+        }
+    }
+
+    public class State {
+        public final ArrayList<T> stack;
+        public final Vector<T> locals;
+        boolean alive;
+
+        State(ArrayList<T> stack, Vector<T> locals) {
+            this.stack = stack;
+            this.locals = locals;
+        }
+
+        State() {
+            this(new ArrayList<>(), new Vector<>());
+        }
+
+        void push(TypeTag tag) {
+            switch (tag) {
+                case A:
+                case V:
+                    throw new IllegalStateException("Bad type tag");
+                default:
+                    push(typeHelper.fromTag(tag));
+            }
+        }
+
+        void push(T t) {
+            stack.add(t);
+            if (width(t) == 2) {
+                stack.add(null);
+            }
+            if (stack.size() > stacksize) {
+                stacksize = stack.size();
+            }
+        }
+
+        T peek() {
+            return stack.get(stack.size() - 1);
+        }
+
+        T tosType() {
+            T tos = peek();
+            if (tos == null) {
+                //double slot
+                tos = stack.get(stack.size() - 2);
+            }
+            return tos;
+        }
+
+        T popInternal() {
+            return stack.remove(stack.size() - 1);
+        }
+
+        @SuppressWarnings("unchecked")
+        T pop() {
+            if (stack.size() == 0 || peek() == null) throw new IllegalStateException();
+            return popInternal();
+        }
+
+        T pop2() {
+            T o = stack.get(stack.size() - 2);
+            TypeTag t = typeHelper.tag(o);
+            if (t.width != 2) throw new IllegalStateException();
+            popInternal();
+            popInternal();
+            return o;
+        }
+
+        T pop(TypeTag t) {
+            return (t.width() == 2) ?
+                pop2() : pop();
+        }
+
+        void load(TypeTag tag, int index) {
+            if (tag == TypeTag.A) throw new IllegalStateException("Bad type tag");
+            load(typeHelper.fromTag(tag), index);
+        }
+
+        void load(T t, int index) {
+            ensureDefined(index);
+            locals.set(index, t);
+            if (width(t) == 2) {
+                locals.add(null);
+            }
+            if (locals.size() > localsize) {
+                localsize = locals.size();
+            }
+        }
+
+        void ensureDefined(int index) {
+            if (index >= locals.size()) {
+                locals.setSize(index + 1);
+            }
+        }
+
+        State dup() {
+            State newState = new State(new ArrayList<>(stack), new Vector<>(locals));
+            return newState;
+        }
+
+        State merge(State that) {
+            if (!alive) { return that; }
+            if (that.stack.size() != stack.size()) {
+                throw new IllegalStateException("Bad stack size at merge point");
+            }
+            for (int i = 0; i < stack.size(); i++) {
+                T t1 = stack.get(i);
+                T t2 = that.stack.get(i);
+                stack.set(i, merge(t1, t2, "Bad stack type at merge point"));
+            }
+            int nlocals = locals.size() > that.locals.size() ? that.locals.size() : locals.size();
+            for (int i = 0; i < nlocals; i++) {
+                T t1 = locals.get(i);
+                T t2 = that.locals.get(i);
+                locals.set(i, merge(t1, t2, "Bad local type at merge point"));
+            }
+            if (locals.size() > nlocals) {
+                for (int i = nlocals; i < locals.size(); i++) {
+                    locals.remove(i);
+                }
+            }
+            return this;
+        }
+
+        T merge(T t1, T t2, String msg) {
+            if (t1 == null && t2 == null) {
+                return t1;
+            }
+            T res;
+            TypeTag tag1 = typeHelper.tag(t1);
+            TypeTag tag2 = typeHelper.tag(t2);
+            if (tag1 != TypeTag.A && tag2 != TypeTag.A &&
+                    tag1 != TypeTag.Q && tag2 != TypeTag.Q) {
+                res = typeHelper.fromTag(TypeTag.commonSupertype(tag1, tag2));
+            } else if (t1 == typeHelper.nullType()) {
+                res = t2;
+            } else if (t2 == typeHelper.nullType()) {
+                res = t1;
+            } else {
+                res = typeHelper.commonSupertype(t1, t2);
+            }
+            if (res == null) {
+                throw new IllegalStateException(msg);
+            }
+            return res;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("[locals = %s, stack = %s]", locals, stack);
+        }
+    }
+
+    int width(T o) {
+        return o == typeHelper.nullType() ?
+                TypeTag.A.width() :
+                typeHelper.tag(o).width;
+    }
+
+    @SuppressWarnings("unchecked")
+    public void updateState(Opcode op, Object optValue) {
+        switch (op) {
+            case AALOAD:
+                state.pop();
+                state.push(typeHelper.elemtype(state.pop()));
+                break;
+            case GOTO_:
+                state.alive = false;
+                break;
+            case NOP:
+            case IINC:
+            case INEG:
+            case LNEG:
+            case FNEG:
+            case DNEG:
+                break;
+            case ACONST_NULL:
+                state.push(typeHelper.nullType());
+                break;
+            case ICONST_M1:
+            case ICONST_0:
+            case ICONST_1:
+            case ICONST_2:
+            case ICONST_3:
+            case ICONST_4:
+            case ICONST_5:
+                state.push(TypeTag.I);
+                break;
+            case LCONST_0:
+            case LCONST_1:
+                state.push(TypeTag.J);
+                break;
+            case FCONST_0:
+            case FCONST_1:
+            case FCONST_2:
+                state.push(TypeTag.F);
+                break;
+            case DCONST_0:
+            case DCONST_1:
+                state.push(TypeTag.D);
+                break;
+            case ILOAD_0:
+            case FLOAD_0:
+            case ALOAD_0:
+            case LLOAD_0:
+            case DLOAD_0:
+                state.push(state.locals.get(0));
+                break;
+            case ILOAD_1:
+            case FLOAD_1:
+            case ALOAD_1:
+            case LLOAD_1:
+            case DLOAD_1:
+                state.push(state.locals.get(1));
+                break;
+            case ILOAD_2:
+            case FLOAD_2:
+            case ALOAD_2:
+            case LLOAD_2:
+            case DLOAD_2:
+                state.push(state.locals.get(2));
+                break;
+            case ILOAD_3:
+            case FLOAD_3:
+            case ALOAD_3:
+            case LLOAD_3:
+            case DLOAD_3:
+                state.push(state.locals.get(3));
+                break;
+            case ILOAD:
+            case FLOAD:
+            case ALOAD:
+            case LLOAD:
+            case DLOAD:
+                state.push(state.locals.get((Integer) optValue));
+                break;
+            case IALOAD:
+            case BALOAD:
+            case CALOAD:
+            case SALOAD:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.I);
+                break;
+            case LALOAD:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.J);
+                break;
+            case FALOAD:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.F);
+                break;
+            case DALOAD:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.D);
+                break;
+            case ISTORE_0:
+            case FSTORE_0:
+            case ASTORE_0:
+                state.load(state.pop(), 0);
+                break;
+            case ISTORE_1:
+            case FSTORE_1:
+            case ASTORE_1:
+                state.load(state.pop(), 1);
+                break;
+            case ISTORE_2:
+            case FSTORE_2:
+            case ASTORE_2:
+                state.load(state.pop(), 2);
+                break;
+            case ISTORE_3:
+            case FSTORE_3:
+            case ASTORE_3:
+                state.load(state.pop(), 3);
+                break;
+            case ISTORE:
+            case FSTORE:
+            case ASTORE:
+                state.load(state.pop(), (int) optValue);
+                break;
+            case LSTORE_0:
+            case DSTORE_0:
+                state.load(state.pop2(), 0);
+                break;
+            case LSTORE_1:
+            case DSTORE_1:
+                state.load(state.pop2(), 1);
+                break;
+            case LSTORE_2:
+            case DSTORE_2:
+                state.load(state.pop2(), 2);
+                break;
+            case LSTORE_3:
+            case DSTORE_3:
+                state.load(state.pop2(), 3);
+                break;
+            case LSTORE:
+            case DSTORE:
+                state.load(state.pop2(), (int) optValue);
+                break;
+            case POP:
+            case LSHR:
+            case LSHL:
+            case LUSHR:
+                state.pop();
+                break;
+            case ARETURN:
+            case IRETURN:
+            case FRETURN:
+                state.pop();
+                break;
+            case ATHROW:
+                state.pop();
+                break;
+            case POP2:
+                state.pop2();
+                break;
+            case LRETURN:
+            case DRETURN:
+                state.pop2();
+                break;
+            case DUP:
+                state.push(state.peek());
+                break;
+            case RETURN:
+                break;
+            case ARRAYLENGTH:
+                state.pop();
+                state.push(TypeTag.I);
+                break;
+            case ISUB:
+            case IADD:
+            case IMUL:
+            case IDIV:
+            case IREM:
+            case ISHL:
+            case ISHR:
+            case IUSHR:
+            case IAND:
+            case IOR:
+            case IXOR:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.I);
+                break;
+            case AASTORE:
+                state.pop();
+                state.pop();
+                state.pop();
+                break;
+            case LAND:
+            case LOR:
+            case LXOR:
+            case LREM:
+            case LDIV:
+            case LMUL:
+            case LSUB:
+            case LADD:
+                state.pop2();
+                state.pop2();
+                state.push(TypeTag.J);
+                break;
+            case LCMP:
+                state.pop2();
+                state.pop2();
+                state.push(TypeTag.I);
+                break;
+            case L2I:
+                state.pop2();
+                state.push(TypeTag.I);
+                break;
+            case I2L:
+                state.pop();
+                state.push(TypeTag.J);
+                break;
+            case I2F:
+                state.pop();
+                state.push(TypeTag.F);
+                break;
+            case I2D:
+                state.pop();
+                state.push(TypeTag.D);
+                break;
+            case L2F:
+                state.pop2();
+                state.push(TypeTag.F);
+                break;
+            case L2D:
+                state.pop2();
+                state.push(TypeTag.D);
+                break;
+            case F2I:
+                state.pop();
+                state.push(TypeTag.I);
+                break;
+            case F2L:
+                state.pop();
+                state.push(TypeTag.J);
+                break;
+            case F2D:
+                state.pop();
+                state.push(TypeTag.D);
+                break;
+            case D2I:
+                state.pop2();
+                state.push(TypeTag.I);
+                break;
+            case D2L:
+                state.pop2();
+                state.push(TypeTag.J);
+                break;
+            case D2F:
+                state.pop2();
+                state.push(TypeTag.F);
+                break;
+            case TABLESWITCH:
+            case LOOKUPSWITCH:
+                state.pop();
+                break;
+            case DUP_X1: {
+                T val1 = state.pop();
+                T val2 = state.pop();
+                state.push(val1);
+                state.push(val2);
+                state.push(val1);
+                break;
+            }
+            case BASTORE:
+                state.pop();
+                state.pop();
+                state.pop();
+                break;
+            case I2B:
+            case I2C:
+            case I2S:
+                break;
+            case FMUL:
+            case FADD:
+            case FSUB:
+            case FDIV:
+            case FREM:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.F);
+                break;
+            case CASTORE:
+            case IASTORE:
+            case FASTORE:
+            case SASTORE:
+                state.pop();
+                state.pop();
+                state.pop();
+                break;
+            case LASTORE:
+            case DASTORE:
+                state.pop2();
+                state.pop();
+                state.pop();
+                break;
+            case DUP2:
+                if (state.peek() != null) {
+                    //form 1
+                    T value1 = state.pop();
+                    T value2 = state.pop();
+                    state.push(value2);
+                    state.push(value1);
+                    state.push(value2);
+                    state.push(value1);
+                } else {
+                    //form 2
+                    T value = state.pop2();
+                    state.push(value);
+                    state.push(value);
+                }
+                break;
+            case DUP2_X1:
+                if (state.peek() != null) {
+                    T value1 = state.pop();
+                    T value2 = state.pop();
+                    T value3 = state.pop();
+                    state.push(value2);
+                    state.push(value1);
+                    state.push(value3);
+                    state.push(value2);
+                    state.push(value1);
+                } else {
+                    T value1 = state.pop2();
+                    T value2 = state.pop();
+                    state.push(value1);
+                    state.push(value2);
+                    state.push(value1);
+                }
+                break;
+            case DUP2_X2:
+                if (state.peek() != null) {
+                    T value1 = state.pop();
+                    T value2 = state.pop();
+                    if (state.peek() != null) {
+                        // form 1
+                        T value3 = state.pop();
+                        T value4 = state.pop();
+                        state.push(value2);
+                        state.push(value1);
+                        state.push(value4);
+                        state.push(value3);
+                        state.push(value2);
+                        state.push(value1);
+                    } else {
+                        // form 3
+                        T value3 = state.pop2();
+                        state.push(value2);
+                        state.push(value1);
+                        state.push(value3);
+                        state.push(value2);
+                        state.push(value1);
+                    }
+                } else {
+                    T value1 = state.pop2();
+                    if (state.peek() != null) {
+                        // form 2
+                        T value2 = state.pop();
+                        T value3 = state.pop();
+                        state.push(value1);
+                        state.push(value3);
+                        state.push(value2);
+                        state.push(value1);
+                    } else {
+                        // form 4
+                        T value2 = state.pop2();
+                        state.push(value1);
+                        state.push(value2);
+                        state.push(value1);
+                    }
+                }
+                break;
+            case DUP_X2: {
+                T value1 = state.pop();
+                if (state.peek() != null) {
+                    // form 1
+                    T value2 = state.pop();
+                    T value3 = state.pop();
+                    state.push(value1);
+                    state.push(value3);
+                    state.push(value2);
+                    state.push(value1);
+                } else {
+                    // form 2
+                    T value2 = state.pop2();
+                    state.push(value1);
+                    state.push(value2);
+                    state.push(value1);
+                }
+            }
+            break;
+            case FCMPL:
+            case FCMPG:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.I);
+                break;
+            case DCMPL:
+            case DCMPG:
+                state.pop2();
+                state.pop2();
+                state.push(TypeTag.I);
+                break;
+            case SWAP: {
+                T value1 = state.pop();
+                T value2 = state.pop();
+                state.push(value1);
+                state.push(value2);
+                break;
+            }
+            case DADD:
+            case DSUB:
+            case DMUL:
+            case DDIV:
+            case DREM:
+                state.pop2();
+                state.pop2();
+                state.push(TypeTag.D);
+                break;
+            case RET:
+                break;
+            case WIDE:
+                // must be handled by the caller.
+                return;
+            case MONITORENTER:
+            case MONITOREXIT:
+                state.pop();
+                break;
+            case NEW:
+            case ACONST_INIT:
+                state.push(typeHelper.type((S) optValue));
+                break;
+            case NEWARRAY:
+                state.pop();
+                state.push(typeHelper.arrayOf(typeHelper.fromTag((TypeTag) optValue)));
+                break;
+            case ANEWARRAY:
+                state.pop();
+                state.push(typeHelper.arrayOf(typeHelper.arrayOf(typeHelper.type((S)optValue))));
+                break;
+            case MULTIANEWARRAY:
+                for (int i = 0; i < (byte) ((Object[]) optValue)[1]; i++) {
+                    state.pop();
+                }
+                state.push(typeHelper.type((S) ((Object[]) optValue)[0]));
+                break;
+            case INVOKEINTERFACE:
+            case INVOKEVIRTUAL:
+            case INVOKESPECIAL:
+            case INVOKESTATIC:
+            case INVOKEDYNAMIC:
+                processInvoke(op, (T) optValue);
+                break;
+            case GETSTATIC:
+                state.push((T) optValue);
+                break;
+            case GETFIELD:
+                state.pop();
+                state.push((T) optValue);
+                break;
+            case PUTSTATIC: {
+                TypeTag tag = typeHelper.tag((T) optValue);
+                if (tag.width == 1) {
+                    state.pop();
+                } else {
+                    state.pop2();
+                }
+                break;
+            }
+            case PUTFIELD: {
+                TypeTag tag = typeHelper.tag((T) optValue);
+                if (tag.width == 1) {
+                    state.pop();
+                } else {
+                    state.pop2();
+                }
+                state.pop();
+                break;
+            }
+            case WITHFIELD: {
+                TypeTag tag = typeHelper.tag((T) optValue);
+                if (tag.width == 1) {
+                    state.pop();
+                } else {
+                    state.pop2();
+                }
+                break;
+            }
+            case BIPUSH:
+            case SIPUSH:
+                state.push(TypeTag.I);
+                break;
+            case LDC:
+            case LDC_W:
+            case LDC2_W:
+                state.push((T)optValue);
+                break;
+            case IF_ACMPEQ:
+            case IF_ICMPEQ:
+            case IF_ACMPNE:
+            case IF_ICMPGE:
+            case IF_ICMPGT:
+            case IF_ICMPLE:
+            case IF_ICMPLT:
+            case IF_ICMPNE:
+                state.pop();
+                state.pop();
+                break;
+            case IF_NONNULL:
+            case IF_NULL:
+            case IFEQ:
+            case IFGE:
+            case IFGT:
+            case IFLE:
+            case IFLT:
+            case IFNE:
+                state.pop();
+                break;
+            case INSTANCEOF:
+                state.pop();
+                state.push(TypeTag.Z);
+                break;
+            case CHECKCAST:
+                break;
+
+            default:
+                throw new UnsupportedOperationException("Unsupported opcode: " + op);
+        }
+    }
+
+    void processInvoke(Opcode opcode, T invokedType) {
+        Iterator<T> paramsIt = typeHelper.parameterTypes(invokedType);
+        while (paramsIt.hasNext()) {
+            T t = paramsIt.next();
+            TypeTag tag = typeHelper.tag(t);
+            if (tag.width == 2) {
+                state.popInternal();
+                state.popInternal();
+            } else {
+                state.popInternal();
+            }
+        }
+        if (opcode != Opcode.INVOKESTATIC && opcode != Opcode.INVOKEDYNAMIC) {
+            state.pop(); //receiver
+        }
+        T retType = typeHelper.returnType(invokedType);
+        TypeTag retTag = typeHelper.tag(retType);
+        if (retTag != TypeTag.V)
+            state.push(retType);
+    }
+
+    @Override
+    protected C ldc(ToIntFunction<PoolHelper<S, T, E>> indexFunc, boolean fat) {
+        LdcPoolHelper ldcPoolHelper = new LdcPoolHelper();
+        int index = indexFunc.applyAsInt(ldcPoolHelper);
+        fat = typeHelper.tag(ldcPoolHelper.type).width() == 2;
+        return super.ldc(index, ldcPoolHelper.type, fat);
+    }
+    //where
+        class LdcPoolHelper implements PoolHelper<S, T, E> {
+
+            T type;
+
+            @Override
+            public int putClass(S symbol) {
+                type = typeHelper.type(symbol);
+                return poolHelper.putClass(symbol);
+            }
+
+            @Override
+            public int putInlineClass(S symbol) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public int putInt(int i) {
+                type = typeHelper.fromTag(TypeTag.I);
+                return poolHelper.putInt(i);
+            }
+
+            @Override
+            public int putFloat(float f) {
+                type = typeHelper.fromTag(TypeTag.F);
+                return poolHelper.putFloat(f);
+            }
+
+            @Override
+            public int putLong(long l) {
+                type = typeHelper.fromTag(TypeTag.J);
+                return poolHelper.putLong(l);
+            }
+
+            @Override
+            public int putDouble(double d) {
+                type = typeHelper.fromTag(TypeTag.D);
+                return poolHelper.putDouble(d);
+            }
+
+            @Override
+            public int putString(String s) {
+                type = typeHelper.type(typeHelper.symbolFrom("java/lang/String"));
+                return poolHelper.putString(s);
+            }
+
+            @Override
+            public int putDynamicConstant(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs) {
+                type = constType;
+                return poolHelper.putDynamicConstant(constName, constType, bsmClass, bsmName, bsmType, staticArgs);
+            }
+
+            @Override
+            public int putFieldRef(S owner, CharSequence name, T type) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public int putMethodRef(S owner, CharSequence name, T type, boolean isInterface) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public int putUtf8(CharSequence s) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public int putType(T t) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public int putMethodType(T t) {
+                type = typeHelper.type(typeHelper.symbolFrom("java/lang/invoke/MethodType"));
+                return poolHelper.putMethodType(t);
+            }
+
+            @Override
+            public int putHandle(int refKind, S owner, CharSequence name, T t) {
+                type = typeHelper.type(typeHelper.symbolFrom("java/lang/invoke/MethodHandle"));
+                return poolHelper.putHandle(refKind, owner, name, t);
+            }
+
+            @Override
+            public int putHandle(int refKind, S owner, CharSequence name, T t, boolean isInterface) {
+                type = typeHelper.type(typeHelper.symbolFrom("java/lang/invoke/MethodHandle"));
+                return poolHelper.putHandle(refKind, owner, name, t, isInterface);
+            }
+
+            @Override
+            public int putInvokeDynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public int size() {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public E entries() {
+                throw new IllegalStateException();
+            }
+    }
+
+    public C load(int index) {
+        return load(typeHelper.tag(state.locals.get(index)), index);
+    }
+
+    public C store(int index) {
+        return store(typeHelper.tag(state.tosType()), index);
+    }
+
+    @Override
+    public C withLocalSize(int localsize) {
+        throw new IllegalStateException("Local size automatically computed");
+    }
+
+    @Override
+    public C withStackSize(int stacksize) {
+        throw new IllegalStateException("Stack size automatically computed");
+    }
+
+    public C withLocal(CharSequence name, T type) {
+        int offset = currLocalOffset;
+        TypeTag tag = typeHelper.tag(type);
+        lvarOffsets.put(name, new LocalVarInfo(name, offset, depth, tag));
+        state.load(type, offset);
+        currLocalOffset += tag.width;
+        return thisBuilder();
+    }
+
+    public C load(CharSequence local) {
+        return load(lvarOffsets.get(local).offset);
+    }
+
+    public C store(CharSequence local) {
+        return store(lvarOffsets.get(local).offset);
+    }
+
+    @Override
+    public C withTry(Consumer<? super C> tryBlock, Consumer<? super CatchBuilder> catchBlocks) {
+        return super.withTry(c -> {
+            withLocalScope(() -> {
+                tryBlock.accept(c);
+            });
+        }, catchBlocks);
+    }
+
+    @Override
+    protected CatchBuilder makeCatchBuilder(int start, int end) {
+        return new TypedCatchBuilder(start, end);
+    }
+
+    class TypedCatchBuilder extends CatchBuilder {
+
+        State initialState = state.dup();
+
+        TypedCatchBuilder(int start, int end) {
+            super(start, end);
+        }
+
+        @Override
+        protected void emitCatch(S exc, Consumer<? super C> catcher) {
+            withLocalScope(() -> {
+                state.push(typeHelper.type(exc));
+                emitStackMap(code.offset);
+                super.emitCatch(exc, catcher);
+                state = initialState;
+            });
+        }
+
+        @Override
+        protected void emitFinalizer() {
+            withLocalScope(() -> {
+                state.push(typeHelper.type(typeHelper.symbolFrom("java/lang/Throwable")));
+                emitStackMap(code.offset);
+                super.emitFinalizer();
+            });
+        }
+    }
+
+    protected void withLocalScope(Runnable runnable) {
+        int prevDepth = depth;
+        try {
+            depth++;
+            runnable.run();
+        } finally {
+            Iterator<Entry<CharSequence, LocalVarInfo>> lvarIt = lvarOffsets.entrySet().iterator();
+            while (lvarIt.hasNext()) {
+                LocalVarInfo lvi = lvarIt.next().getValue();
+                if (lvi.depth == depth) {
+                    int width = lvi.type.width;
+                    currLocalOffset -= width;
+                    lvarIt.remove();
+                }
+            }
+            depth = prevDepth;
+        }
+    }
+
+    @Override
+    void addPendingJump(CharSequence label, int pc) {
+        pendingJumps.add(new StatefulPendingJump(label, pc, state.dup()));
+    }
+
+    @Override
+    void resolveJumps(CharSequence label, int pc) {
+        super.resolveJumps(label, pc);
+        emitStackMap(pc);
+    }
+
+    //TODO: optimize stackmap generation by avoiding intermediate classes
+    protected void emitStackMap(int pc) {
+        //stack map generation
+        if (pc > lastStackMapPc) {
+            writeStackMapFrame(pc);
+            lastStackMapState = state.dup();
+            lastStackMapPc = pc;
+            nstackmaps++;
+        }
+    }
+
+    @Override
+    void build(GrowableByteBuffer buf) {
+        if (stacksize == -1) {
+            throw new IllegalStateException("Bad stack size");
+        }
+        if (localsize == -1) {
+            throw new IllegalStateException("Bad locals size");
+        }
+        if (nstackmaps > 0) {
+            GrowableByteBuffer stackmapsAttr = new GrowableByteBuffer();
+            stackmapsAttr.writeChar(nstackmaps);
+            stackmapsAttr.writeBytes(stackmaps);
+            withAttribute("StackMapTable", stackmapsAttr.bytes());
+        }
+        super.build(buf);
+    }
+
+    /**
+     * Compare this frame with the previous frame and produce
+     * an entry of compressed stack map frame.
+     */
+    void writeStackMapFrame(int pc) {
+        List<T> locals = state.locals;
+        List<T> stack = state.stack;
+        List<T> prev_locals = lastStackMapState.locals;
+        int offset_delta = lastStackMapPc == -1 ? pc : pc - lastStackMapPc - 1;
+        if (stack.size() == 1) {
+            if (locals.size() == prev_locals.size() && prev_locals.equals(locals)) {
+                sameLocals1StackItemFrame(offset_delta, stack.get(stack.size() - 1));
+                return;
+            }
+        } else if (stack.size() == 0) {
+            int diff_length = prev_locals.size() - locals.size();
+            if (diff_length == 0) {
+                sameFrame(offset_delta);
+                return;
+            } else if (-MAX_LOCAL_LENGTH_DIFF < diff_length && diff_length < 0) {
+                appendFrame(offset_delta, prev_locals.size(), locals);
+                return;
+            } else if (0 < diff_length && diff_length < MAX_LOCAL_LENGTH_DIFF) {
+                chopFrame(offset_delta, diff_length);
+                return;
+            }
+        }
+        fullFrame(offset_delta, locals, stack);
+    }
+}
diff a/test/jdk/tools/launcher/modules/patch/basic/PatchTestWarningError.java b/test/jdk/tools/launcher/modules/patch/basic/PatchTestWarningError.java
--- a/test/jdk/tools/launcher/modules/patch/basic/PatchTestWarningError.java
+++ b/test/jdk/tools/launcher/modules/patch/basic/PatchTestWarningError.java
@@ -133,10 +133,29 @@
                 .getExitValue();
 
         assertTrue(exitValue != 0);
     }
 
+    /**
+     * Test with --patch-module options patching the same module (not java.base).
+     *
+     */
+    public void testDuplicateModuleLogging() throws Exception {
+        int exitValue =
+            executeTestJava("--patch-module", "java.logging=" + PATCHES1_DIR.resolve("java.base"),
+                            "--patch-module", "java.logging=" + PATCHES2_DIR.resolve("java.base"),
+                            "--module-path", MODS_DIR.toString(),
+                            "-m", "test/jdk.test.Main")
+                .outputTo(System.out)
+                .errorTo(System.out)
+                // error output by VM
+                .shouldContain("Cannot specify a module more than once to --patch-module: java.logging")
+                .getExitValue();
+
+        assertTrue(exitValue != 0);
+    }
+
     @DataProvider(name = "emptyItem")
     public Object[][] emptyItems() {
         String patch1 = PATCHES1_DIR.resolve("java.base").toString();
         String patch2 = PATCHES2_DIR.resolve("java.base").toString();
         String pathSep = File.pathSeparator;
diff a/test/jdk/valhalla/valuetypes/ArrayElementVarHandleTest.java b/test/jdk/valhalla/valuetypes/ArrayElementVarHandleTest.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/ArrayElementVarHandleTest.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2018, 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 VarHandle on value class array
+ * @enablePreview
+ * @run junit/othervm -XX:FlatArrayElementMaxSize=-1 ArrayElementVarHandleTest
+ * @run junit/othervm -XX:FlatArrayElementMaxSize=0  ArrayElementVarHandleTest
+ */
+
+import java.lang.invoke.*;
+import java.util.stream.Stream;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.NullRestricted;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class ArrayElementVarHandleTest {
+    @ImplicitlyConstructible
+    static value class Point {
+        public int x;
+        public int y;
+        Point(int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class Line {
+        @NullRestricted
+        Point p1;
+        @NullRestricted
+        Point p2;
+
+        Line(Point p1, Point p2) {
+            this.p1 = p1;
+            this.p2 = p2;
+        }
+        Line(int x1, int y1, int x2, int y2) {
+            this(new Point(x1, y1), new Point(x2, y2));
+        }
+    }
+
+    private static final Point[] POINTS = new Point[]{
+            new Point(1, 2),
+            new Point(10, 20),
+            new Point(100, 200),
+            null
+    };
+
+    private static final Line[] LINES = new Line[]{
+            new Line(1, 2, 3, 4),
+            new Line(10, 20, 30, 40),
+            null
+    };
+
+    static Stream<Arguments> testCases() throws Throwable {
+        int plen = POINTS.length;
+        int llen = LINES.length;
+        return Stream.of(
+                Arguments.of(newArray(Object[].class, plen),    POINTS),
+                Arguments.of(newArray(Object[].class, plen),    new Object[] { "abc", new Point(1, 2) }),
+                Arguments.of(newArray(Point[].class, plen),     POINTS),
+                Arguments.of(new Point[plen],                   POINTS),
+
+                Arguments.of(newArray(Object[].class, llen),    LINES),
+                Arguments.of(newArray(Line[].class, llen),      LINES),
+                Arguments.of(new Line[llen],                    LINES)
+        );
+    }
+
+    /*
+     * Constructs a new array of the specified type and size using
+     * MethodHandle.
+     */
+    private static Object[] newArray(Class<?> arrayType, int size) throws Throwable {
+        MethodHandle ctor = MethodHandles.arrayConstructor(arrayType);
+        return (Object[]) ctor.invoke(size);
+    }
+
+    /*
+     * Test VarHandle to set elements of the given array with
+     * various access mode.
+     */
+    @ParameterizedTest
+    @MethodSource("testCases")
+    public void testSetArrayElements(Object[] array, Object[] elements) {
+        Class<?> arrayType = array.getClass();
+        assertTrue(array.length >= elements.length);
+
+        VarHandle vh = MethodHandles.arrayElementVarHandle(arrayType);
+        set(vh, array.clone(), elements);
+        setVolatile(vh, array.clone(), elements);
+        setOpaque(vh, array.clone(), elements);
+        setRelease(vh, array.clone(), elements);
+        getAndSet(vh, array.clone(), elements);
+        compareAndSet(vh, array.clone(), elements);
+        compareAndExchange(vh, array.clone(), elements);
+    }
+
+    // VarHandle::set
+    void set(VarHandle vh, Object[] array, Object[] elements) {
+        for (int i = 0; i < elements.length; i++) {
+            vh.set(array, i, elements[i]);
+        }
+        for (int i = 0; i < elements.length; i++) {
+            Object v = (Object) vh.get(array, i);
+            assertEquals(elements[i], v);
+        }
+    }
+
+    // VarHandle::setVolatile
+    void setVolatile(VarHandle vh, Object[] array, Object[] elements) {
+        for (int i = 0; i < elements.length; i++) {
+            vh.setVolatile(array, i, elements[i]);
+        }
+        for (int i = 0; i < elements.length; i++) {
+            Object v = (Object) vh.getVolatile(array, i);
+            assertEquals(elements[i], v);
+        }
+    }
+
+    // VarHandle::setOpaque
+    void setOpaque(VarHandle vh, Object[] array, Object[] elements) {
+        for (int i = 0; i < elements.length; i++) {
+            vh.setOpaque(array, i, elements[i]);
+        }
+        for (int i = 0; i < elements.length; i++) {
+            Object v = (Object) vh.getOpaque(array, i);
+            assertEquals(elements[i], v);
+        }
+    }
+
+    // VarHandle::setRelease
+    void setRelease(VarHandle vh, Object[] array, Object[] elements) {
+        for (int i = 0; i < elements.length; i++) {
+            vh.setRelease(array, i, elements[i]);
+        }
+        for (int i = 0; i < elements.length; i++) {
+            Object v = (Object) vh.getAcquire(array, i);
+            assertEquals(elements[i], v);
+        }
+    }
+
+    void getAndSet(VarHandle vh, Object[] array, Object[] elements) {
+        for (int i = 0; i < elements.length; i++) {
+            Object o = vh.getAndSet(array, i, elements[i]);
+        }
+        for (int i = 0; i < elements.length; i++) {
+            Object v = (Object) vh.get(array, i);
+            assertEquals(elements[i], v);
+        }
+    }
+
+    // sanity CAS test
+    // see test/jdk/java/lang/invoke/VarHandles tests
+    void compareAndSet(VarHandle vh, Object[] array, Object[] elements) {
+        // initialize to some values
+        for (int i = 0; i < elements.length; i++) {
+            vh.set(array, i, elements[i]);
+        }
+        // shift to the right element
+        for (int i = 0; i < elements.length; i++) {
+            Object v = elements[i + 1 < elements.length ? i + 1 : 0];
+            boolean cas = vh.compareAndSet(array, i, elements[i], v);
+            if (!cas)
+                System.out.format("cas = %s array[%d] = %s vs old = %s new = %s%n", cas, i, array[i], elements[i], v);
+            assertTrue(cas);
+        }
+    }
+
+    void compareAndExchange(VarHandle vh, Object[] array, Object[] elements) {
+        // initialize to some values
+        for (int i = 0; i < elements.length; i++) {
+            vh.set(array, i, elements[i]);
+        }
+        // shift to the right element
+        for (int i = 0; i < elements.length; i++) {
+            Object v = elements[i + 1 < elements.length ? i + 1 : 0];
+            assertEquals(elements[i], vh.compareAndExchange(array, i, elements[i], v));
+        }
+    }
+}
diff a/test/jdk/valhalla/valuetypes/LambdaMetaFactory/LambdaConversion.java b/test/jdk/valhalla/valuetypes/LambdaMetaFactory/LambdaConversion.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/LambdaMetaFactory/LambdaConversion.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ *
+ * 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 lambda type conversion of value class
+ * @enablePreview
+ * @run junit/othervm LambdaConversion
+ */
+
+import java.util.function.ToIntFunction;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class LambdaConversion {
+
+    static class c_int { }
+    static value class Pointer<X> {
+        long addr;
+
+        public Pointer(long addr) {
+            this.addr = addr;
+        }
+
+        long address() { return addr; }
+    }
+
+    @Test
+    public static void test() {
+        Pointer<c_int> p_int = new Pointer<>(12);
+        assertTrue(doAction(p_int, LambdaConversion::one) == 1);
+        assertTrue(doAction(p_int, LambdaConversion::two) == 2);
+    }
+
+    static <Z> int doAction(Pointer<Z> pointer, ToIntFunction<Pointer<Z>> action) {
+        return action.applyAsInt(pointer);
+    }
+
+    static int one(Pointer<c_int> pointer) {
+        return 1;
+    }
+
+    static int two(Pointer<c_int> p_int) {
+        return 2;
+    }
+}
+
diff a/test/jdk/valhalla/valuetypes/LambdaMetaFactory/LambdaTest.java b/test/jdk/valhalla/valuetypes/LambdaMetaFactory/LambdaTest.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/LambdaMetaFactory/LambdaTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2022, 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 lambdas with parameter types or return type of value class
+ * @enablePreview
+ * @run junit/othervm LambdaTest
+ */
+
+import java.util.function.Function;
+import java.util.function.IntFunction;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.NullRestricted;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class LambdaTest {
+    @ImplicitlyConstructible
+    static value class V {
+        int x;
+        V(int x) {
+            this.x = x;
+        }
+
+        static V get(int x) {
+            return new V(x);
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class Value {
+        @NullRestricted
+        V v;
+        Value(V v) {
+            this.v = v;
+        }
+        static Value get(int x) {
+            return new Value(new V(x));
+        }
+    }
+
+    static int getV(V v) {
+        return v.x;
+    }
+
+    static int getValue(Value v) {
+        return v.v.x;
+    }
+
+    @Test
+    public void testValueParameterType() {
+        Function<Value, Integer> func1 = LambdaTest::getValue;
+        assertTrue(func1.apply(new Value(new V(100))) == 100);
+
+        Function<V, Integer> func2 = LambdaTest::getV;
+        assertTrue(func2.apply(new V(200)) == 200);
+    }
+
+    @Test
+    public void testValueReturnType() {
+        IntFunction<Value> func1 = Value::get;
+        assertEquals(func1.apply(10), new Value(new V(10)));
+
+        IntFunction<V> func2 = V::get;
+        assertEquals(func2.apply(20), new V(20));
+    }
+}
diff a/test/jdk/valhalla/valuetypes/LambdaMetaFactory/src/IdentityRunnable.java b/test/jdk/valhalla/valuetypes/LambdaMetaFactory/src/IdentityRunnable.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/LambdaMetaFactory/src/IdentityRunnable.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2022, 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 interface IdentityRunnable {
+    void run();
+}
diff a/test/jdk/valhalla/valuetypes/LambdaMetaFactory/src/Test.java b/test/jdk/valhalla/valuetypes/LambdaMetaFactory/src/Test.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/LambdaMetaFactory/src/Test.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2022, 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 Test {
+    public static void testValueRunnable() {
+        ValueRunnable r = () -> {
+            System.out.println("ValueRunnable::run");
+        };
+        r.run();
+    }
+
+    public static void testIdentityRunnable() {
+        IdentityRunnable r = () -> {
+            System.out.println("IdentityRunnable::run");
+        };
+        r.run();
+    }
+}
diff a/test/jdk/valhalla/valuetypes/LambdaMetaFactory/src/ValueRunnable.java b/test/jdk/valhalla/valuetypes/LambdaMetaFactory/src/ValueRunnable.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/LambdaMetaFactory/src/ValueRunnable.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2022, 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 interface ValueRunnable {
+     void run();
+}
diff a/test/jdk/valhalla/valuetypes/MHZeroValue.java b/test/jdk/valhalla/valuetypes/MHZeroValue.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/MHZeroValue.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2022, 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 MethodHandles::zero, MethodHandles::empty and MethodHandles::constant
+ *          on value classes.
+ * @enablePreview
+ * @run junit/othervm -XX:InlineFieldMaxFlatSize=128 MHZeroValue
+ * @run junit/othervm -XX:InlineFieldMaxFlatSize=0 MHZeroValue
+ */
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.stream.Stream;
+
+import static java.lang.invoke.MethodType.*;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.NullRestricted;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class MHZeroValue {
+    @ImplicitlyConstructible
+    static value class V {}
+
+    @ImplicitlyConstructible
+    static value class P {
+        @NullRestricted
+        V empty;
+        P() {
+            this.empty = new V();
+        }
+    }
+
+    static Stream<Arguments> defaultValue() {
+        return Stream.of(
+                // for any type T, default value is always the same as (new T[1])[0]
+                Arguments.of(int.class,         (new int[1])[0],      0 /* default value */),
+                Arguments.of(Integer.class,     (new Integer[1])[0],  null),
+                Arguments.of(P.class,           (new P[1])[0],        null),
+                Arguments.of(V.class,           (new V[1])[0],        null)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("defaultValue")
+    public void zero(Class<?> type, Object value, Object expected) throws Throwable {
+        var mh = MethodHandles.zero(type);
+        assertEquals(mh.invoke(), expected);
+        assertEquals(value, expected);
+    }
+
+    static Stream<Arguments> testCases() {
+        return Stream.of(
+                Arguments.of(methodType(int.class, int.class, Object.class),     new V(), 0),
+                Arguments.of(methodType(Integer.class, int.class, Object.class), new P(), null),
+                Arguments.of(methodType(P.class, int.class, P.class),            new P(), null),
+                Arguments.of(methodType(V.class, int.class, P.class),            new P(), null),
+                Arguments.of(methodType(V.class, int.class, V.class),            new V(), null)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("testCases")
+    public void empty(MethodType mtype, Object param, Object value) throws Throwable {
+        var mh = MethodHandles.empty(mtype);
+        assertEquals(mh.invoke(1, param), value);
+    }
+}
diff a/test/jdk/valhalla/valuetypes/MethodHandleTest.java b/test/jdk/valhalla/valuetypes/MethodHandleTest.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/MethodHandleTest.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2018, 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 MethodHandle and VarHandle of value classes
+ * @enablePreview
+ * @run junit/othervm MethodHandleTest
+ */
+
+import java.lang.invoke.*;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.NullRestricted;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class MethodHandleTest {
+    @ImplicitlyConstructible
+    static value class Point {
+        public int x;
+        public int y;
+        Point(int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class Line {
+        @NullRestricted
+        Point p1;
+        @NullRestricted
+        Point p2;
+
+        Line(int x1, int y1, int x2, int y2) {
+            this.p1 = new Point(x1, y1);
+            this.p2 = new Point(x2, y2);
+        }
+    }
+
+    static class Ref {
+        @NullRestricted
+        Point p;
+        Line l;
+        List<String> list;
+        ValueOptional vo;
+
+        Ref(Point p, Line l) {
+            this.p = p;
+            this.l = l;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class ValueOptional {
+        private Object o;
+        public ValueOptional(Object o) {
+            this.o = o;
+        }
+    }
+
+    private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+
+    static final Point P = new Point(1, 2);
+    static final Line L = new Line(1, 2, 3, 4);
+    static final Ref R = new Ref(P, null);
+
+    static Stream<Arguments> fields() {
+        return Stream.of(
+                // value class with int fields
+                Arguments.of("MethodHandleTest$Point", P, Set.of("x", "y")),
+                // value class whose fields are null-restricted and of value class
+                Arguments.of("MethodHandleTest$Line", L, Set.of("p1", "p2")),
+                // identity class whose non-final fields are of value type,
+                Arguments.of("MethodHandleTest$Ref", R, Set.of("p", "l", "list", "vo"))
+        );
+    }
+
+    /**
+     * Test MethodHandle invocation on the fields of a given class.
+     * MethodHandle produced by Lookup::unreflectGetter, Lookup::findGetter,
+     * Lookup::findVarHandle.
+     */
+    @ParameterizedTest
+    @MethodSource("fields")
+    public void testFieldGetter(String cn, Object o, Set<String> fields) throws Throwable  {
+        Class<?> c = Class.forName(cn);
+        for (String name : fields) {
+            Field f = c.getDeclaredField(name);
+            var mh = LOOKUP.findGetter(c, f.getName(), f.getType());
+            var v1 = mh.invoke(o);
+
+            var vh = LOOKUP.findVarHandle(c, f.getName(), f.getType());
+            var v2 = vh.get(o);
+
+            var mh3 = LOOKUP.unreflectGetter(f);
+            var v3 = mh3.invoke(o);
+
+            if (c.isValue())
+                ensureImmutable(f, o);
+        }
+    }
+
+    static Stream<Arguments> setters() {
+        return Stream.of(
+                Arguments.of(Ref.class, R, "p", true),
+                Arguments.of(Ref.class, R, "l", false),
+                Arguments.of(Ref.class, R, "list", false),
+                Arguments.of(Ref.class, R, "vo", false)
+        );
+    }
+    @ParameterizedTest
+    @MethodSource("setters")
+    public void testFieldSetter(Class<?> cls, Object o, String name, boolean nullRestricted) throws Throwable {
+        Field f = cls.getDeclaredField(name);
+        var mh = LOOKUP.findSetter(cls, f.getName(), f.getType());
+        var vh = LOOKUP.findVarHandle(cls, f.getName(), f.getType());
+        var mh3 = LOOKUP.unreflectSetter(f);
+
+        if (nullRestricted) {
+            assertThrows(NullPointerException.class, () -> mh.invoke(o, null));
+            assertThrows(NullPointerException.class, () -> vh.set(o, null));
+            assertThrows(NullPointerException.class, () -> mh3.invoke(o, null));
+        } else {
+            mh.invoke(o, null);
+            vh.set(o, null);
+            mh3.invoke(o, null);
+        }
+    }
+
+    static Stream<Arguments> arrays() throws Throwable {
+        return Stream.of(
+                Arguments.of(Point[].class, newArray(Point[].class), P, false),
+                Arguments.of(Point[].class, newNullRestrictedArray(Point.class), P, true),
+                Arguments.of(Line[].class, newArray(Line[].class), L, false),
+                Arguments.of(Line[].class, newNullRestrictedArray(Line.class), L, true),
+                Arguments.of(Ref[].class, newArray(Ref[].class), R, false)
+        );
+    }
+
+    private static final int ARRAY_SIZE = 5;
+    private static Object[] newArray(Class<?> arrayClass) throws Throwable {
+        MethodHandle ctor = MethodHandles.arrayConstructor(arrayClass);
+        return (Object[])ctor.invoke(ARRAY_SIZE);
+    }
+    private static Object[] newNullRestrictedArray(Class<?> componentClass) throws Throwable {
+        return ValueClass.newNullRestrictedArray(componentClass, ARRAY_SIZE);
+    }
+
+    @ParameterizedTest
+    @MethodSource("arrays")
+    public void testArrayElementSetterAndGetter(Class<?> arrayClass, Object[] array, Object element, boolean nullRestricted) throws Throwable {
+        MethodHandle setter = MethodHandles.arrayElementSetter(array.getClass());
+        MethodHandle getter = MethodHandles.arrayElementGetter(array.getClass());
+        VarHandle vh = MethodHandles.arrayElementVarHandle(arrayClass);
+        Class<?> componentType = arrayClass.getComponentType();
+
+        for (int i=0; i < ARRAY_SIZE; i++) {
+            var v = getter.invoke(array, i);
+            if (nullRestricted) {
+                assertTrue(v == ValueClass.zeroInstance(componentType));
+            } else {
+                assertTrue(v == null);
+            }
+        }
+        for (int i=0; i < ARRAY_SIZE; i++) {
+            setter.invoke(array, i, element);
+            assertTrue(getter.invoke(array, i) == element);
+        }
+        // set an array element to null
+        if (nullRestricted) {
+            assertTrue(vh.get(array, 1) != null);
+            assertThrows(NullPointerException.class, () -> setter.invoke(array, 1, null));
+            assertThrows(NullPointerException.class, () -> vh.set(array, 1, null));
+        } else {
+            setter.invoke(array, 1, null);
+            assertNull(getter.invoke(array, 1));
+            vh.set(array, 1, null);
+        }
+    }
+
+    static void ensureImmutable(Field f, Object o) throws Throwable {
+        Class<?> c = f.getDeclaringClass();
+        assertTrue(Modifier.isFinal(f.getModifiers()));
+        assertFalse(Modifier.isStatic(f.getModifiers()));
+        assertTrue(f.trySetAccessible());
+
+        Object v = f.get(o);
+
+        assertThrows(IllegalAccessException.class, () -> LOOKUP.findSetter(c, f.getName(), f.getType()));
+        assertThrows(IllegalAccessException.class, () -> LOOKUP.unreflectSetter(f));
+        VarHandle vh = LOOKUP.findVarHandle(c, f.getName(), f.getType());
+
+        // test var handle
+        assertThrows(UnsupportedOperationException.class, () -> vh.set(o, v));
+    }
+}
diff a/test/jdk/valhalla/valuetypes/MethodReference.java b/test/jdk/valhalla/valuetypes/MethodReference.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/MethodReference.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * 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 method reference of value class as the parameter type
+ * @enablePreview
+ * @run main/othervm MethodReference
+ */
+
+import java.util.function.Supplier;
+
+public value class MethodReference {
+    final int x;
+    final int y;
+    MethodReference() {
+        this.x = 1234;
+        this.y = 5678;
+    }
+
+    public static void main(String... args) {
+        Supplier<MethodReference> supplier = MethodReference::new;
+        MethodReference o = supplier.get();
+        if (o.x != 1234 || o.y != 5678)
+            throw new AssertionError(o);
+    }
+}
diff a/test/jdk/valhalla/valuetypes/Nest.java b/test/jdk/valhalla/valuetypes/Nest.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/Nest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ *
+ * 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 substitutability of inner class and anonymous class that
+ *          has the enclosing instance and possibly other captured outer locals
+ * @enablePreview
+ * @run junit/othervm Nest
+ */
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+public class Nest {
+    static interface I {
+        String toString();
+    }
+
+    static I of(int value, Object next) {
+        // anonymous class capturing outer locals
+        return new value I() {
+            public String toString() {
+                return value + " -> " + next;
+            }
+        };
+    }
+
+    @Test
+    public void test1() {
+        assertEquals(Nest.of(1, null), Nest.of(1, null));
+        assertNotEquals(Nest.of(1, null), Nest.of(2, null));
+    }
+
+    @Test
+    public void test2() {
+        Outer n = new Outer(1);
+        Outer.Inner inner = n.new Inner(10);
+        Outer n1 = new Outer(1);
+        Outer n2 = new Outer(2);
+        // o1.new Inner(1) == o2.new Inner(1) iff o1 == o2
+        assertEquals(n1.new Inner(10), inner);
+        assertEquals(n2.new Inner(10), new Outer(2).new Inner(10));
+    }
+
+    @ImplicitlyConstructible
+    value class Outer {
+        final int i;
+        Outer(int i) {
+            this.i = i;
+        }
+
+        @ImplicitlyConstructible
+        value class Inner {
+            final int ic;
+            Inner(int ic) {
+                this.ic = ic;
+            }
+        }
+    }
+}
diff a/test/jdk/valhalla/valuetypes/NullRestrictedArraysTest.java b/test/jdk/valhalla/valuetypes/NullRestrictedArraysTest.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/NullRestrictedArraysTest.java
@@ -0,0 +1,235 @@
+/*
+ * 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
+ * @enablePreview
+ * @run junit/othervm NullRestrictedArraysTest
+ * @run junit/othervm -XX:FlatArrayElementMaxSize=0 NullRestrictedArraysTest
+ */
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.stream.Stream;
+
+import jdk.internal.value.CheckedType;
+import jdk.internal.value.NullRestrictedCheckedType;
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class NullRestrictedArraysTest {
+    interface I {
+        int getValue();
+    }
+    @ImplicitlyConstructible
+    static value class Value implements I {
+        int v;
+        Value() {
+            this(0);
+        }
+        Value(int v) {
+            this.v = v;
+        }
+        public int getValue() {
+            return v;
+        }
+    }
+
+    static class T {
+        String s;
+        Value obj;  // can be null
+        @NullRestricted
+        Value value;
+    }
+
+    static Stream<Arguments> checkedTypes() throws ReflectiveOperationException {
+        return Stream.of(
+                Arguments.of(T.class.getDeclaredField("s"), String.class, false),
+                Arguments.of(T.class.getDeclaredField("obj"), Value.class, false),
+                Arguments.of(T.class.getDeclaredField("value"), Value.class, true)
+        );
+    }
+
+    /*
+     * Test creating null-restricted arrays with CheckedType
+     */
+    @ParameterizedTest
+    @MethodSource("checkedTypes")
+    public void testCheckedTypeArrays(Field field, Class<?> type, boolean nullRestricted) throws ReflectiveOperationException {
+        CheckedType checkedType = ValueClass.checkedType(field);
+        assertTrue(field.getType() == type);
+        assertTrue(checkedType.boundingClass() == type);
+        Object[] array = ValueClass.newArrayInstance(checkedType, 4);
+        assertTrue(ValueClass.isNullRestrictedArray(array) == nullRestricted);
+        assertTrue(checkedType instanceof NullRestrictedCheckedType == nullRestricted);
+        for (int i=0; i < array.length; i++) {
+            array[i] = type.newInstance();
+        }
+        if (nullRestricted) {
+            // NPE thrown if elements in a null-restricted array set to null
+            assertThrows(NullPointerException.class, () -> array[0] = null);
+        } else {
+            array[0] = null;
+        }
+    }
+
+    /*
+     * Test Arrays::copyOf and Arrays::copyOfRange to create null-restricted arrays.
+     */
+    @Test
+    public void testArraysCopyOf() {
+        int len = 4;
+        Object[] array = (Object[]) Array.newInstance(Value.class, len);
+        Object[] nullRestrictedArray = ValueClass.newNullRestrictedArray(Value.class, len);
+        for (int i=0; i < len; i++) {
+            array[i] = new Value(i);
+            nullRestrictedArray[i] = new Value(i);
+        }
+        testCopyOf(array, nullRestrictedArray);
+        testCopyOfRange(array, nullRestrictedArray, 1, len+2);
+    };
+
+    private void testCopyOf(Object[] array, Object[] nullRestrictedArray) {
+        Object[] newArray1 = Arrays.copyOf(array, array.length);
+        Object[] newArray2 = Arrays.copyOf(nullRestrictedArray, nullRestrictedArray.length);
+
+        assertFalse(ValueClass.isNullRestrictedArray(newArray1));
+        assertTrue(ValueClass.isNullRestrictedArray(newArray2));
+
+        // elements in a normal array can be null
+        for (int i=0; i < array.length; i++) {
+            newArray1[i] = null;
+        }
+        // NPE thrown if elements in a null-restricted array set to null
+        assertThrows(NullPointerException.class, () -> newArray2[0] = null);
+    }
+
+    private void testCopyOfRange(Object[] array, Object[] nullRestrictedArray, int from, int to) {
+        Object[] newArray1 = Arrays.copyOfRange(array, from, to);
+        Object[] newArray2 = Arrays.copyOfRange(nullRestrictedArray, from, to);
+        System.out.println("newArray2 " + newArray2.length + " " + Arrays.toString(newArray2));
+        // elements in a normal array can be null
+        for (int i=0; i < newArray1.length; i++) {
+            newArray1[i] = null;
+        }
+        // NPE thrown if elements in a null-restricted array set to null
+        assertThrows(NullPointerException.class, () -> newArray2[0] = null);
+
+        // check the new array padded with null if normal array and
+        // zero instance if null-restricted array
+        for (int i=0; i < newArray1.length; i++) {
+            if (from+1 >= array.length) {
+                // padded with null
+                assertTrue(newArray1[i] == null);
+            }
+        }
+        Class<?> componentType = nullRestrictedArray.getClass().getComponentType();
+        for (int i=0; i < newArray2.length; i++) {
+            if (from+1 >= nullRestrictedArray.length) {
+                // padded with zero instance
+                assertTrue(newArray2[i] == ValueClass.zeroInstance(componentType));
+            }
+        }
+    }
+
+    @Test
+    public void testVarHandle() {
+        int len = 4;
+        Object[] array = (Object[]) Array.newInstance(Value.class, len);
+        Object[] nullRestrictedArray = ValueClass.newNullRestrictedArray(Value.class, len);
+
+        // Test var handles
+        testVarHandleArray(array, Value[].class);
+        testVarHandleArray(array, I[].class);
+        testVarHandleNullRestrictedArray(nullRestrictedArray, Value[].class);
+        testVarHandleNullRestrictedArray(nullRestrictedArray, I[].class);
+    }
+
+    private void testVarHandleArray(Object[] array, Class<?> arrayClass) {
+        for (int i=0; i < array.length; i++) {
+            array[i] = new Value(i);
+        }
+
+        VarHandle vh = MethodHandles.arrayElementVarHandle(arrayClass);
+        Value value = new Value(0);
+        Value value1 =  new Value(1);
+
+        assertTrue(vh.get(array, 0) == value);
+        assertTrue(vh.getVolatile(array, 0) == value);
+        assertTrue(vh.getOpaque(array, 0) == value);
+        assertTrue(vh.getAcquire(array, 0) == value);
+        vh.set(array, 0, null);
+        vh.setVolatile(array, 0, null);
+        vh.setOpaque(array, 0, null);
+        vh.setRelease(array, 0, null);
+
+        vh.compareAndSet(array, 1, value1, null);             vh.set(array, 1, value1);
+        vh.compareAndExchange(array, 1, value1, null);        vh.set(array, 1, value1);
+        vh.compareAndExchangeAcquire(array, 1, value1, null); vh.set(array, 1, value1);
+        vh.compareAndExchangeRelease(array, 1, value1, null); vh.set(array, 1, value1);
+        vh.weakCompareAndSet(array, 1, value1, null);         vh.set(array, 1, value1);
+        vh.weakCompareAndSetAcquire(array, 1, value1, null);  vh.set(array, 1, value1);
+        vh.weakCompareAndSetPlain(array, 1, value1, null);    vh.set(array, 1, value1);
+        vh.weakCompareAndSetRelease(array, 1, value1, null);  vh.set(array, 1, value1);
+    }
+
+    private void testVarHandleNullRestrictedArray(Object[] array, Class<?> arrayClass) {
+        for (int i=0; i < array.length; i++) {
+            array[i] = new Value(i);
+        }
+
+        VarHandle vh = MethodHandles.arrayElementVarHandle(arrayClass);
+        Value value = new Value(0);
+        Value value1 =  new Value(1);
+        assertTrue(vh.get(array, 0) == value);
+        assertTrue(vh.getVolatile(array, 0) == value);
+        assertTrue(vh.getOpaque(array, 0) == value);
+        assertTrue(vh.getAcquire(array, 0) == value);
+        assertThrows(NullPointerException.class, () -> vh.set(array, 0, null));
+        assertThrows(NullPointerException.class, () -> vh.setVolatile(array, 0, null));
+        assertThrows(NullPointerException.class, () -> vh.setOpaque(array, 0, null));
+        assertThrows(NullPointerException.class, () -> vh.setRelease(array, 0, null));
+
+        assertThrows(NullPointerException.class, () -> vh.compareAndSet(array, 1, value1, null));
+        assertThrows(NullPointerException.class, () -> vh.compareAndExchange(array, 1, value1, null));
+        assertThrows(NullPointerException.class, () -> vh.compareAndExchangeAcquire(array, 1, value1, null));
+        assertThrows(NullPointerException.class, () -> vh.compareAndExchangeRelease(array, 1, value1, null));
+        assertThrows(NullPointerException.class, () -> vh.weakCompareAndSet(array, 1, value1, null));
+        assertThrows(NullPointerException.class, () -> vh.weakCompareAndSetAcquire(array, 1, value1, null));
+        assertThrows(NullPointerException.class, () -> vh.weakCompareAndSetPlain(array, 1, value1, null));
+        assertThrows(NullPointerException.class, () -> vh.weakCompareAndSetRelease(array, 1, value1, null));
+    }
+
+}
diff a/test/jdk/valhalla/valuetypes/NullRestrictedTest.java b/test/jdk/valhalla/valuetypes/NullRestrictedTest.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/NullRestrictedTest.java
@@ -0,0 +1,188 @@
+/*
+ * 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.
+ *
+ * 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 reflection and method handle on accessing a field of a null-restricted value class
+ *          that may be flattened or non-flattened
+ * @enablePreview
+ * @run junit/othervm NullRestrictedTest
+ * @run junit/othervm -XX:InlineFieldMaxFlatSize=0 NullRestrictedTest
+ */
+
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Field;
+import java.util.stream.Stream;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class NullRestrictedTest {
+    @ImplicitlyConstructible
+    static value class EmptyValue {
+        public boolean isEmpty() {
+            return true;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class Value {
+        Object o;
+        @NullRestricted
+        EmptyValue empty;
+        Value() {
+            this.o = null;
+            this.empty = new EmptyValue();
+        }
+    }
+
+    static class Mutable {
+        EmptyValue o;
+        @NullRestricted
+        EmptyValue empty;
+        @NullRestricted
+        volatile EmptyValue vempty;
+    }
+
+    @Test
+    public void emptyValueClass() {
+        EmptyValue e = new EmptyValue();
+        Field[] fields = e.getClass().getDeclaredFields();
+        assertTrue(fields.length == 0);
+    }
+
+    @Test
+    public void lazyInitializedDefaultValue() {
+        // VM lazily sets the null-restricted non-flat field to zero default
+        assertTrue(new Value() == ValueClass.zeroInstance(Value.class));
+        assertTrue(new Value().empty == ValueClass.zeroInstance(EmptyValue.class));
+    }
+
+    static Stream<Arguments> getterCases() {
+        Value v = new Value();
+        EmptyValue emptyValue = ValueClass.zeroInstance(EmptyValue.class);
+        Mutable m = new Mutable();
+
+        return Stream.of(
+                Arguments.of(Value.class, "o", Object.class, v, null),
+                Arguments.of(Value.class, "empty", EmptyValue.class, v, emptyValue),
+                Arguments.of(Mutable.class, "o", EmptyValue.class, m, null),
+                Arguments.of(Mutable.class, "empty", EmptyValue.class, m, emptyValue),
+                Arguments.of(Mutable.class, "vempty", EmptyValue.class, m, emptyValue)
+        );
+    };
+
+    @ParameterizedTest
+    @MethodSource("getterCases")
+    public void testGetter(Class<?> type, String name, Class<?> ftype, Object obj, Object expected) throws Throwable {
+        var f = type.getDeclaredField(name);
+        assertTrue(f.getType() == ftype);
+        var o1 = f.get(obj);
+        assertTrue(expected == o1);
+
+        var getter = MethodHandles.lookup().findGetter(type, name, ftype);
+        var o2 = getter.invoke(obj);
+        assertTrue(expected == o2);
+
+        var vh = MethodHandles.lookup().findVarHandle(type, name, ftype);
+        var o3 = vh.get(obj);
+        assertTrue(expected == o3);
+    }
+
+    static Stream<Arguments> setterCases() {
+        EmptyValue emptyValue = ValueClass.zeroInstance(EmptyValue.class);
+        Mutable m = new Mutable();
+        return Stream.of(
+                Arguments.of(Mutable.class, "o", EmptyValue.class, m, null),
+                Arguments.of(Mutable.class, "o", EmptyValue.class, m, emptyValue),
+                Arguments.of(Mutable.class, "empty", EmptyValue.class, m, emptyValue),
+                Arguments.of(Mutable.class, "vempty", EmptyValue.class, m, emptyValue)
+        );
+    };
+
+    @ParameterizedTest
+    @MethodSource("setterCases")
+    public void testSetter(Class<?> type, String name, Class<?> ftype, Object obj, Object expected) throws Throwable {
+        var f = type.getDeclaredField(name);
+        assertTrue(f.getType() == ftype);
+        f.set(obj, expected);
+        assertTrue(f.get(obj) == expected);
+
+        var setter = MethodHandles.lookup().findSetter(type, name, ftype);
+        setter.invoke(obj, expected);
+        assertTrue(f.get(obj) == expected);
+    }
+
+    @Test
+    public void noWriteAccess() throws ReflectiveOperationException {
+        Value v = new Value();
+        Field f = v.getClass().getDeclaredField("o");
+        assertThrows(IllegalAccessException.class, () -> f.set(v, null));
+    }
+
+    static Stream<Arguments> nullRestrictedFields() {
+        Mutable m = new Mutable();
+        return Stream.of(
+                Arguments.of(Mutable.class, "o", EmptyValue.class, m, false),
+                Arguments.of(Mutable.class, "empty", EmptyValue.class, m, true),
+                Arguments.of(Mutable.class, "vempty", EmptyValue.class, m, true)
+        );
+    };
+
+    @ParameterizedTest
+    @MethodSource("nullRestrictedFields")
+    public void testNullRestrictedField(Class<?> type, String name, Class<?> ftype, Object obj, boolean nullRestricted) throws Throwable {
+        var f = type.getDeclaredField(name);
+        assertTrue(f.getType() == ftype);
+        if (nullRestricted) {
+            assertThrows(NullPointerException.class, () -> f.set(obj, null));
+        } else {
+            f.set(obj, null);
+            assertTrue(f.get(obj) == null);
+        }
+
+        var mh = MethodHandles.lookup().findSetter(type, name, ftype);
+        if (nullRestricted) {
+            assertThrows(NullPointerException.class, () -> mh.invoke(obj, null));
+        } else {
+            mh.invoke(obj, null);
+            assertTrue(f.get(obj) == null);
+        }
+
+        var vh = MethodHandles.lookup().findVarHandle(type, name, ftype);
+        if (nullRestricted) {
+            assertThrows(NullPointerException.class, () -> vh.set(obj, null));
+        } else {
+            vh.set(obj, null);
+            assertTrue(f.get(obj) == null);
+        }
+    }
+}
diff a/test/jdk/valhalla/valuetypes/ObjectMethods.java b/test/jdk/valhalla/valuetypes/ObjectMethods.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/ObjectMethods.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (c) 2018, 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 Object methods on value classes
+ * @enablePreview
+ * @run junit/othervm -Dvalue.bsm.salt=1 ObjectMethods
+ * @run junit/othervm -Dvalue.bsm.salt=1 -XX:InlineFieldMaxFlatSize=0 ObjectMethods
+ */
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.NullRestricted;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class ObjectMethods {
+    @ImplicitlyConstructible
+    static value class Point {
+        public int x;
+        public int y;
+        Point(int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class Line {
+        @NullRestricted
+        Point p1;
+        @NullRestricted
+        Point p2;
+
+        Line(int x1, int y1, int x2, int y2) {
+            this.p1 = new Point(x1, y1);
+            this.p2 = new Point(x2, y2);
+        }
+    }
+
+    static class Ref {
+        @NullRestricted
+        Point p;
+        Line l;
+        Ref(Point p, Line l) {
+            this.p = p;
+            this.l = l;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class Value {
+        @NullRestricted
+        Point p;
+        @NullRestricted
+        Line l;
+        Ref r;
+        String s;
+        Value(Point p, Line l, Ref r, String s) {
+            this.p = p;
+            this.l = l;
+            this.r = r;
+            this.s = s;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class ValueOptional {
+        private Object o;
+        public ValueOptional(Object o) {
+            this.o = o;
+        }
+    }
+
+    static value record ValueRecord(int i, String name) {}
+
+    static final int SALT = 1;
+    static final Point P1 = new Point(1, 2);
+    static final Point P2 = new Point(30, 40);
+    static final Line L1 = new Line(1, 2, 3, 4);
+    static final Line L2 = new Line(10, 20, 3, 4);
+    static final Ref R1 = new Ref(P1, L1);
+    static final Ref R2 = new Ref(P2, null);
+    static final Value V = new Value(P1, L1, R1, "value");
+
+    static Stream<Arguments> identitiesData() {
+        return Stream.of(
+                Arguments.of(new Object(), true, false),
+                Arguments.of("String", true, false),
+                Arguments.of(String.class, true, false),
+                Arguments.of(Object.class, true, false),
+                Arguments.of(L1, false, true),
+                Arguments.of(V, false, true),
+                Arguments.of(new ValueRecord(1, "B"), false, true),
+                Arguments.of(new int[0], true, false),     // arrays of primitive type are identity objects
+                Arguments.of(new Object[0], true, false),  // arrays of identity classes are identity objects
+                Arguments.of(new String[0], true, false),  // arrays of identity classes are identity objects
+                Arguments.of(new Value[0], true, false)    // arrays of value classes are identity objects
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("identitiesData")
+    public void identityTests(Object obj, boolean identityClass, boolean valueClass) {
+        Class<?> clazz = obj.getClass();
+
+        if (clazz == Object.class) {
+            assertTrue(Objects.isIdentityObject(obj), "Objects.isIdentityObject()");
+        } else {
+            assertEquals(identityClass, Objects.isIdentityObject(obj), "Objects.isIdentityObject()");
+        }
+
+        assertEquals(valueClass, Objects.isValueObject(obj), "Objects.isValueObject()");
+
+        assertEquals(identityClass, clazz.isIdentity(), "Class.isIdentity()");
+
+        assertEquals(valueClass, clazz.isValue(), "Class.isValue()");
+
+        // JDK-8294866: Not yet implemented checks of AccessFlags for the array class
+//        assertEquals(clazz.accessFlags().contains(AccessFlag.IDENTITY),
+//                identityClass, "AccessFlag.IDENTITY");
+//
+//        assertEquals(clazz.accessFlags().contains(AccessFlag.VALUE),
+//                valueClass, "AccessFlag.VALUE");
+    }
+
+    static Stream<Arguments> equalsTests() {
+        return Stream.of(
+                Arguments.of(P1, P1, true),
+                Arguments.of(P1, new Point(1, 2), true),
+                Arguments.of(P1, P2, false),
+                Arguments.of(P1, L1, false),
+                Arguments.of(L1, new Line(1, 2, 3, 4), true),
+                Arguments.of(L1, L2, false),
+                Arguments.of(L1, L1, true),
+                Arguments.of(V, new Value(P1, L1, R1, "value"), true),
+                Arguments.of(V, new Value(new Point(1, 2), new Line(1, 2, 3, 4), R1, "value"), true),
+                Arguments.of(V, new Value(P1, L1, new Ref(P1, L1), "value"), false),
+                Arguments.of(new Value(P1, L1, R2, "value2"), new Value(P1, L1, new Ref(P2, null), "value2"), false),
+                Arguments.of(new ValueRecord(50, "fifty"), new ValueRecord(50, "fifty"), true),
+
+                // reference classes containing fields of value class
+                Arguments.of(R1, new Ref(P1, L1), false),   // identity object
+
+                // uninitialized default value
+                Arguments.of(ValueClass.zeroInstance(Line.class), new Line(0, 0, 0, 0), true),
+                Arguments.of(ValueClass.zeroInstance(Value.class), ValueClass.zeroInstance(Value.class), true),
+                Arguments.of(new ValueOptional(L1), new ValueOptional(L1), true),
+                Arguments.of(new ValueOptional(List.of(P1)), new ValueOptional(List.of(P1)), false)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("equalsTests")
+    public void testEquals(Object o1, Object o2, boolean expected) {
+        assertTrue(o1.equals(o2) == expected);
+    }
+
+    static Stream<Arguments> toStringTests() {
+        return Stream.of(
+                Arguments.of(new Point(100, 200)),
+                Arguments.of(new Line(1, 2, 3, 4)),
+                Arguments.of(V),
+                Arguments.of(R1),
+                // enclosing instance field `this$0` should be filtered
+                Arguments.of(ValueClass.zeroInstance(Value.class)),
+                Arguments.of(new Value(P1, L1, null, null)),
+                Arguments.of(new Value(P2, L2, new Ref(P1, null), "value")),
+                Arguments.of(ValueClass.zeroInstance(ValueOptional.class)),
+                Arguments.of(new ValueOptional(P1))
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("toStringTests")
+    public void testToString(Object o) {
+        String expected = String.format("%s@%s", o.getClass().getName(), Integer.toHexString(o.hashCode()));
+        assertEquals(o.toString(), expected);
+    }
+
+    @Test
+    public void testValueRecordToString() {
+        ValueRecord o = new ValueRecord(30, "thirty");
+        assertEquals(o.toString(), "ValueRecord[i=30, name=thirty]");
+    }
+
+
+    static Stream<Arguments> hashcodeTests() {
+        Point p = ValueClass.zeroInstance(Point.class);
+        Line l = ValueClass.zeroInstance(Line.class);
+        Value v = ValueClass.zeroInstance(Value.class);
+        // this is sensitive to the order of the returned fields from Class::getDeclaredFields
+        return Stream.of(
+                Arguments.of(P1, hash(Point.class, 1, 2)),
+                Arguments.of(L1, hash(Line.class, new Point(1, 2), new Point(3, 4))),
+                Arguments.of(V, hash(Value.class, P1, L1, V.r, V.s)),
+                Arguments.of(new Point(0, 0), hash(Point.class, 0, 0)),
+                Arguments.of(p, hash(Point.class, 0, 0)),
+                Arguments.of(v, hash(Value.class, p, l, null, null)),
+                Arguments.of(new ValueOptional(P1), hash(ValueOptional.class, P1))
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("hashcodeTests")
+    public void testHashCode(Object o, int hash) {
+        assertEquals(o.hashCode(), hash);
+        assertEquals(System.identityHashCode(o), hash);
+    }
+
+    private static int hash(Object... values) {
+        int hc = SALT;
+        for (Object o : values) {
+            hc = 31 * hc + (o != null ? o.hashCode() : 0);
+        }
+        return hc;
+    }
+
+    interface Number {
+        int value();
+    }
+
+    static class ReferenceType implements Number {
+        int i;
+        public ReferenceType(int i) {
+            this.i = i;
+        }
+        public int value() {
+            return i;
+        }
+        @Override
+        public boolean equals(Object o) {
+            if (o != null && o instanceof Number) {
+                return this.value() == ((Number)o).value();
+            }
+            return false;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class ValueType1 implements Number {
+        int i;
+        public ValueType1(int i) {
+            this.i = i;
+        }
+        public int value() {
+            return i;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class ValueType2 implements Number {
+        int i;
+        public ValueType2(int i) {
+            this.i = i;
+        }
+        public int value() {
+            return i;
+        }
+        @Override
+        public boolean equals(Object o) {
+            if (o != null && o instanceof Number) {
+                return this.value() == ((Number)o).value();
+            }
+            return false;
+        }
+    }
+
+    static Stream<Arguments> interfaceEqualsTests() {
+        return Stream.of(
+                Arguments.of(new ReferenceType(10), new ReferenceType(10), false, true),
+                Arguments.of(new ValueType1(10),    new ValueType1(10),    true,  true),
+                Arguments.of(new ValueType2(10),    new ValueType2(10),    true,  true),
+                Arguments.of(new ValueType1(20),    new ValueType2(20),    false, false),
+                Arguments.of(new ValueType2(20),    new ValueType1(20),    false, true),
+                Arguments.of(new ReferenceType(30), new ValueType1(30),    false, true),
+                Arguments.of(new ReferenceType(30), new ValueType2(30),    false, true)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("interfaceEqualsTests")
+    public void testNumber(Number n1, Number n2, boolean isSubstitutable, boolean isEquals) {
+        assertTrue((n1 == n2) == isSubstitutable);
+        assertTrue(n1.equals(n2) == isEquals);
+    }
+}
diff a/test/jdk/valhalla/valuetypes/ObjectMethodsViaCondy.java b/test/jdk/valhalla/valuetypes/ObjectMethodsViaCondy.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/ObjectMethodsViaCondy.java
@@ -0,0 +1,177 @@
+/*
+ * 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.
+ *
+ * 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 ObjectMethods::bootstrap call via condy
+ * @modules java.base/jdk.internal.value
+ *          java.base/jdk.internal.org.objectweb.asm
+ * @enablePreview
+ * @run testng/othervm ObjectMethodsViaCondy
+ */
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup.ClassOption;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.TypeDescriptor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.stream.Stream;
+
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.ConstantDynamic;
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Type;
+import org.testng.annotations.Test;
+import static java.lang.invoke.MethodType.methodType;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_IDENTITY;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
+import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
+import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
+import static jdk.internal.org.objectweb.asm.Opcodes.H_GETFIELD;
+import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
+import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
+import static jdk.internal.org.objectweb.asm.Opcodes.V19;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertFalse;
+
+public class ObjectMethodsViaCondy {
+    public static value record ValueRecord(int i, String name) {
+        static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+        static final MethodType TO_STRING_DESC = methodType(String.class, ValueRecord.class);
+        static final Handle[] ACCESSORS = accessors();
+        static final String NAME_LIST = "i;name";
+        private static Handle[] accessors() {
+            try {
+                return  new Handle[]{
+                        new Handle(H_GETFIELD, Type.getInternalName(ValueRecord.class), "i", "I", false),
+                        new Handle(H_GETFIELD, Type.getInternalName(ValueRecord.class), "name", String.class.descriptorString(), false)
+                };
+            } catch (Exception e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        /**
+         * Returns the method handle for the given method for this ValueRecord class.
+         * This method defines a hidden class to invoke the ObjectMethods::bootstrap method
+         * via condy.
+         *
+         * @param methodName   the name of the method to generate, which must be one of
+         *                     {@code "equals"}, {@code "hashCode"}, or {@code "toString"}
+         */
+        static MethodHandle makeBootstrapMethod(String methodName) throws Throwable {
+            ClassFileBuilder builder = new ClassFileBuilder("Test-" + methodName);
+            builder.bootstrapMethod(methodName, TO_STRING_DESC, ValueRecord.class, NAME_LIST, ACCESSORS);
+            byte[] bytes = builder.build();
+            MethodHandles.Lookup lookup = LOOKUP.defineHiddenClass(bytes, true, ClassOption.NESTMATE);
+            MethodType mtype = MethodType.methodType(Object.class);
+            MethodHandle mh = lookup.findStatic(lookup.lookupClass(), "bootstrap", mtype);
+            return (MethodHandle) mh.invoke();
+        }
+    }
+
+    @Test
+    public void testToString() throws Throwable {
+        MethodHandle handle = ValueRecord.makeBootstrapMethod("toString");
+        assertEquals((String)handle.invokeExact(new ValueRecord(10, "ten")), "ValueRecord[i=10, name=ten]");
+        assertEquals((String)handle.invokeExact(new ValueRecord(40, "forty")), "ValueRecord[i=40, name=forty]");
+    }
+
+    @Test
+    public void testToEquals() throws Throwable {
+        MethodHandle handle = ValueRecord.makeBootstrapMethod("equals");
+        assertTrue((boolean)handle.invoke(new ValueRecord(10, "ten"), new ValueRecord(10, "ten")));
+        assertFalse((boolean)handle.invoke(new ValueRecord(11, "eleven"), new ValueRecord(10, "ten")));
+    }
+
+    static class ClassFileBuilder {
+        private static final String OBJECT_CLS = "java/lang/Object";
+        private static final String OBJ_METHODS_CLS = "java/lang/runtime/ObjectMethods";
+        private static final String BSM_DESCR =
+                MethodType.methodType(Object.class, MethodHandles.Lookup.class, String.class,
+                                      TypeDescriptor.class, Class.class, String.class, MethodHandle[].class)
+                          .descriptorString();
+        private final ClassWriter cw;
+        private final String classname;
+
+        /**
+         * A builder to generate a class file to access class data
+         *
+         * @param classname
+         */
+        ClassFileBuilder(String classname) {
+            this.classname = classname;
+            this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+            cw.visit(V19, ACC_FINAL | ACC_IDENTITY, classname, null, OBJECT_CLS, null);
+            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+            mv.visitCode();
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitMethodInsn(INVOKESPECIAL, OBJECT_CLS, "<init>", "()V", false);
+            mv.visitInsn(RETURN);
+            mv.visitMaxs(0, 0);
+            mv.visitEnd();
+        }
+
+        byte[] build() {
+            cw.visitEnd();
+            byte[] bytes = cw.toByteArray();
+            Path p = Paths.get(classname + ".class");
+            try (OutputStream os = Files.newOutputStream(p)) {
+                os.write(bytes);
+            } catch (IOException e) {
+                throw new UncheckedIOException(e);
+            }
+            return bytes;
+        }
+
+        /*
+         * Generate the bootstrap method that invokes ObjectMethods::bootstrap via condy
+         */
+        void bootstrapMethod(String name, TypeDescriptor descriptor, Class<?> recordClass, String names, Handle[] getters) {
+            MethodType mtype = MethodType.methodType(Object.class);
+            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC|ACC_STATIC,
+                    "bootstrap", mtype.descriptorString(), null, null);
+            mv.visitCode();
+            Handle bsm = new Handle(H_INVOKESTATIC, OBJ_METHODS_CLS, "bootstrap",
+                                    BSM_DESCR, false);
+            Object[] args = Stream.concat(Stream.of(Type.getType(recordClass), names), Arrays.stream(getters)).toArray();
+            ConstantDynamic dynamic = new ConstantDynamic(name, MethodHandle.class.descriptorString(), bsm, args);
+            mv.visitLdcInsn(dynamic);
+            mv.visitInsn(ARETURN);
+            mv.visitMaxs(0, 0);
+            mv.visitEnd();
+        }
+    }
+}
diff a/test/jdk/valhalla/valuetypes/ObjectNewInstance.java b/test/jdk/valhalla/valuetypes/ObjectNewInstance.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/ObjectNewInstance.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2022, 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 Method handle and core reflection to invoke on the constructor of
+ *          java.lang.Object (abstract class) should return an Identity instance
+ * @enablePreview
+ * @run testng/othervm ObjectNewInstance
+ */
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Constructor;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+public class ObjectNewInstance {
+    @Test
+    public void classNewInstance() throws ReflectiveOperationException {
+        Object o = Object.class.newInstance();
+        assertTrue(o.getClass() == Object.class);
+    }
+
+    @Test
+    public void constructorNewInstance() throws ReflectiveOperationException {
+        Constructor<Object> ctor = Object.class.getDeclaredConstructor();
+        Object o = ctor.newInstance();
+        assertTrue(o.getClass() == Object.class);
+    }
+
+    @Test
+    public void methodHandle() throws Throwable {
+        MethodHandle mh = MethodHandles.publicLookup()
+                                       .findConstructor(Object.class, MethodType.methodType(void.class));
+        Object o = mh.invokeExact();
+        assertTrue(o.getClass() == Object.class);
+    }
+}
diff a/test/jdk/valhalla/valuetypes/ProxyTest.java b/test/jdk/valhalla/valuetypes/ProxyTest.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/ProxyTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2022, 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 dynamic proxies with parameter types or return type of value class
+ * @enablePreview
+ * @run testng/othervm ProxyTest
+ */
+
+import java.lang.reflect.*;
+import java.util.Arrays;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+public class ProxyTest {
+    static value class V {
+        int v;
+        V(int v) {
+            this.v = v;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class P {
+        int p;
+        P(int p) {
+            this.p = p;
+        }
+    }
+
+    interface I {
+        int getV(V v);
+        int getP(P p);
+    }
+
+    interface J {
+        int[] getV(V[] v);
+    }
+
+    @Test
+    public void testProxy() throws Exception {
+        InvocationHandler handler = new InvocationHandler() {
+            @Override
+            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                switch (method.getName()) {
+                    case "getV":
+                        V v = (V)args[0];
+                        return v.v;
+                    case "getP":
+                        P p = (P)args[0];
+                        return p.p;
+                    default:
+                        throw new UnsupportedOperationException(method.toString());
+                }
+            }
+        };
+
+        Class<?>[] intfs = new Class<?>[] { I.class };
+        I i = (I) Proxy.newProxyInstance(ProxyTest.class.getClassLoader(), intfs, handler);
+
+        assertTrue(i.getV(new V(100)) == 100);
+        assertTrue(i.getP(new P(200)) == 200);
+    }
+
+    @Test
+    public void testValueArrayType() {
+        InvocationHandler handler = new InvocationHandler() {
+            @Override
+            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                switch (method.getName()) {
+                    case "getV":
+                        V[] vs = (V[])args[0];
+                        return Arrays.stream(vs).mapToInt(v -> v.v).toArray();
+                    default:
+                        throw new UnsupportedOperationException(method.toString());
+                }
+            }
+        };
+
+        Class<?>[] intfs = new Class<?>[] { J.class };
+        J j = (J) Proxy.newProxyInstance(ProxyTest.class.getClassLoader(), intfs, handler);
+
+        V[] array = new V[] { new V(10), new V(20), new V(30)};
+        assertEquals(j.getV(array), new int[] { 10, 20, 30});
+    }
+}
diff a/test/jdk/valhalla/valuetypes/RecursiveValueClass.java b/test/jdk/valhalla/valuetypes/RecursiveValueClass.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/RecursiveValueClass.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2022, 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
+ * @run junit/othervm -Xint -Djdk.value.recursion.threshold=100000 RecursiveValueClass
+ */
+
+/*
+ * @ignore 8296056
+ * @test
+ * @run junit/othervm -XX:TieredStopAtLevel=1 -Djdk.value.recursion.threshold=100000 RecursiveValueClass
+ */
+
+/*
+ * @ignore 8296056
+ * @test
+ * @run junit/othervm -Xcomp -Djdk.value.recursion.threshold=100000 RecursiveValueClass
+ */
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.NullRestricted;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.Arguments;
+
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class RecursiveValueClass {
+    @ImplicitlyConstructible
+    static value class Node {
+        Node left;
+        Node right;
+
+        Node(Node l, Node r) {
+            this.left = l;
+            this.right = r;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class P {
+        Node node;
+        V v;
+        P(Node node, V v) {
+            this.node = node;
+            this.v = v;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class V {
+        P p;
+        V(P p) {
+            this.p = p;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class A {
+        B b;
+        E e;
+        A(B b, E e) {
+            this.b = b;
+            this.e = e;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class B {
+        C c;
+        D d;
+        B(C c, D d) {
+            this.c = c;
+            this.d = d;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class C {
+        A a;
+        C(A a) {
+            this.a = a;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class D {
+        int x;
+        D(int x) {
+            this.x = x;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class E {
+        F f;
+        E(F f) {
+            this.f = f;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class F {
+        E e;
+        F(E e) {
+            this.e = e;
+        }
+    }
+
+    static Stream<Arguments> objectsProvider() {
+        var n1 = ValueClass.zeroInstance(Node.class);
+        var n2 = new Node(n1, null);
+        var n3 = new Node(n2, n1);
+        var n4 = new Node(n2, n1);
+        var v1 = ValueClass.zeroInstance(V.class);
+        var p1 = new P(n3, v1);
+        var p2 = new P(n4, v1);
+        var v2 = new V(p1);
+        var v3 = new V(p2);
+        var p3 = new P(n3, v2);
+
+        var e1 = new E(ValueClass.zeroInstance(F.class));
+        var f1 = new F(e1);
+        var e2 = new E(f1);
+        var f2 = new F(e2);
+
+        var a = new A(ValueClass.zeroInstance(B.class), ValueClass.zeroInstance(E.class));
+
+        var d1 = new D(1);
+        var d2 = new D(2);
+        var c1 = new C(a);
+        var c2 = new C(a);
+
+        var b1 = new B(c1, d1);
+        var b2 = new B(c1, d2);
+        var b3 = new B(c2, d2);
+        var a1 = new A(b1, e1);
+        var a2 = new A(b2, e2);
+
+        return Stream.of(
+                // Node -> Node left & right
+                Arguments.of(n1, n1,     true),
+                Arguments.of(n1, n2,     false),
+                Arguments.of(n2, n3,     false),
+                Arguments.of(n3, n4,     true),
+                Arguments.of(null, n4,   false),
+                Arguments.of(null, null, true),
+                Arguments.of(n1, "foo",  false),
+
+                // value class P -> value class V -> P
+                Arguments.of(p1, p2,     true),
+                Arguments.of(p1, p3,     false),
+                Arguments.of(p3, p3,     true),
+                Arguments.of(v2, v3,     true),
+
+                // E -> F -> E
+                Arguments.of(e1, e2,     false),
+                Arguments.of(e1, e1,     true),
+                Arguments.of(f1, f2,     false),
+                Arguments.of(f2, f2,     true),
+
+                // two cyclic memberships from A
+                // A -> B -> C -> A and E -> F -> E
+                Arguments.of(a1, a2,     false),
+                Arguments.of(a2, a2,     true),
+                Arguments.of(b1, b2,     false),
+                Arguments.of(b2, b3,     true),
+                Arguments.of(c1, c2,     true)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("objectsProvider")
+    public void testAcmp(Object o1, Object o2, boolean expected) {
+        var value = o1 == o2;
+        assertEquals(expected, value, o1 + " == " + o2);
+    }
+
+    static Stream<Arguments> hashCodeProvider() {
+        var n1 = ValueClass.zeroInstance(Node.class);
+        var n2 = new Node(n1, null);
+        var n3 = new Node(n2, n1);
+        var v1 = new V(null);
+        var p1 = new P(n3, v1);
+        var v2 = new V(p1);
+
+        var e1 = new E(ValueClass.zeroInstance(F.class));
+        var f1 = new F(e1);
+        var e2 = new E(f1);
+        var f2 = new F(e2);
+
+        var a = new A(ValueClass.zeroInstance(B.class), ValueClass.zeroInstance(E.class));
+
+        var d1 = new D(1);
+        var d2 = new D(2);
+        var c1 = new C(a);
+
+        var b1 = new B(c1, d1);
+        var b2 = new B(c1, d2);
+        var a1 = new A(b1, e1);
+        var a2 = new A(b2, e2);
+
+        return Stream.of(
+                // Node -> Node left & right
+                Arguments.of(n1),
+                Arguments.of(n2),
+                Arguments.of(n3),
+
+                // value class P -> value class V -> P
+                Arguments.of(p1),
+                Arguments.of(v2),
+
+                // E -> F -> E
+                Arguments.of(e1),
+                Arguments.of(e2),
+                Arguments.of(f1),
+                Arguments.of(f2),
+
+                // two cyclic memberships from A
+                // A -> B -> C -> A and E -> F -> E
+                Arguments.of(a1),
+                Arguments.of(a2),
+                Arguments.of(b1),
+                Arguments.of(b2),
+                Arguments.of(c1)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("hashCodeProvider")
+    public void testHashCode(Object o) {
+        int hc = o.hashCode();
+        assertEquals(System.identityHashCode(o), hc, o.toString());
+    }
+
+    @ImplicitlyConstructible
+    static value class N {
+        N l;
+        N r;
+        int id;
+        N(N l, N r, int id) {
+            this.l = l;
+            this.r = r;
+            this.id = id;
+        }
+    }
+
+    private static N build() {
+        N n1 = new N(null, null, 0);
+        N n2 = new N(null, null, 0);
+        for (int id = 1; id < 100; ++id) {
+            N l = new N(n1, n2, id);
+            N r = new N(n1, n2, id);
+            n1 = l;
+            n2 = r;
+        }
+        return new N(n1, n2, 100);
+    }
+
+    /*
+     * Throw SOE for large graph
+     */
+    @Test
+    public void largeGraph() {
+        N node = build();
+        long start = System.nanoTime();
+        assertThrows(StackOverflowError.class, () -> { boolean v = node.l == node.r; });
+        assertThrows(StackOverflowError.class, () -> { int hc = node.hashCode(); });
+        System.out.format("testing large graph: %d ms%n", (System.nanoTime() - start) / 1000);
+    }
+}
diff a/test/jdk/valhalla/valuetypes/Reflection.java b/test/jdk/valhalla/valuetypes/Reflection.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/Reflection.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2017, 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 core reflection on value classes
+ * @enablePreview
+ * @run junit/othervm Reflection
+ */
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.stream.Stream;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.NullRestricted;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class Reflection {
+    @ImplicitlyConstructible
+    static value class V {
+        int x;
+        V(int x) {
+            this.x = x;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class Value {
+        @NullRestricted
+        V v1;
+        V v2;
+        Value(V v1, V v2) {
+            this.v1 = v1;
+            this.v2 = v2;
+        }
+
+        static Value newValue(V v1, V v2) {
+            return new Value(v1, v2);
+        }
+    }
+
+    @Test
+    void testNewInstance() throws Exception {
+        V v = new V(10);
+        Constructor<Value> ctor = Value.class.getDeclaredConstructor(V.class, V.class);
+        Value o = ctor.newInstance(v, v);
+        assertEquals(o.getClass(), Value.class);
+    }
+
+    @Test
+    void testAccess() throws Exception {
+        Field field = Value.class.getDeclaredField("v1");
+        V v = new V(10);
+        Value o = new Value(v, null);
+        assertEquals(v, field.get(o));
+
+        // accessible but no write access
+        assertTrue(field.trySetAccessible());
+        assertTrue(field.isAccessible());
+        assertThrows(IllegalAccessException.class, () -> field.set(o, v));
+    }
+
+    @Test
+    void testNullRestricted() throws Exception {
+        Method m = Value.class.getDeclaredMethod("newValue", V.class, V.class);
+        Throwable t = assertThrows(InvocationTargetException.class, () -> m.invoke(null, new Object[] {null, null}));
+        assertEquals(NullPointerException.class, t.getCause().getClass());
+    }
+
+    static Stream<Arguments> arrays() {
+        V v1 = new V(10);
+        V v2 = new V(20);
+        Value value = new Value(v1, v2);
+
+        V[] varray = (V[]) Array.newInstance(V.class, 2);
+        V[] varrayNR = (V[]) ValueClass.newNullRestrictedArray(V.class, 3);
+        Value[] valuearray = (Value[]) Array.newInstance(Value.class, 2);
+        Value[] valuearrayNR = (Value[]) ValueClass.newNullRestrictedArray(Value.class, 3);
+
+        return Stream.of(
+                Arguments.of(V[].class, varray, false, v1),
+                Arguments.of(V[].class, varrayNR, true, v2),
+                Arguments.of(Value[].class, valuearray, false, value),
+                Arguments.of(Value[].class, valuearrayNR, true, value)
+
+        );
+    }
+
+    /**
+     * Setting the elements of an array.
+     * NPE will be thrown if null is set on an element in a null-restricted value class array
+     */
+    @ParameterizedTest
+    @MethodSource("arrays")
+    public void testArrays(Class<?> arrayClass, Object[] array, boolean nullRestricted, Object element) {
+        Class<?> componentType = arrayClass.getComponentType();
+        assertTrue(arrayClass.isArray());
+        // TODO: check Array.getComponentType(array) instead
+        assertTrue(array.getClass() == arrayClass || nullRestricted);
+        assertTrue(array.getClass().getComponentType() == componentType || nullRestricted);
+
+        for (int i = 0; i < array.length; i++) {
+            Object o = Array.get(array, i);
+            if (nullRestricted) {
+                assertTrue(o == ValueClass.zeroInstance(componentType));
+            } else {
+                assertTrue(o == null);
+            }
+        }
+
+        // set elements
+        for (int i = 0; i < array.length; i++) {
+            Array.set(array, i, element);
+            assertTrue(Array.get(array, i) == element);
+        }
+
+        Arrays.setAll(array, i -> array[i]);
+
+        // test nullable
+        for (int i = 0; i < array.length; i++) {
+            if (nullRestricted) {
+                final int index = i;
+                assertThrows(NullPointerException.class, () -> Array.set(array, index, null));
+            } else {
+                Array.set(array, i, null);
+            }
+        }
+    }
+}
diff a/test/jdk/valhalla/valuetypes/StreamTest.java b/test/jdk/valhalla/valuetypes/StreamTest.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/StreamTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2020, 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 Basic stream tests to iterate on nullable and null-restricted values
+ * @enablePreview
+ * @run junit/othervm StreamTest
+ */
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class StreamTest {
+
+    static value class X {
+        int x;
+        X(int x) {
+            this.x = x;
+        }
+        int x() {
+            return x;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class Point {
+        public int x;
+        public int y;
+        Point(int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class Value {
+        int i;
+        @NullRestricted
+        Point p;
+        Point nullable;
+        List<X> list;
+        Value(int i, Point/* Point! */ p, Point np, List<X> list) {
+            this.i = i;
+            this.p = p;
+            this.nullable = np;
+            this.list = list;
+        }
+
+        Point point() {
+            return p;
+        }
+
+        Point nullablePoint() {
+            return nullable;
+        }
+
+        int getI() { return i; }
+
+        List<X> list() { return list; }
+    }
+
+    final Value[] values = init();
+    private Value[] init() {
+        Value[] values = new Value[10];
+        for (int i = 0; i < 10; i++) {
+            values[i] = new Value(i,
+                                  new Point(i,i*2),
+                                  (i%2) == 0 ? null : new Point(i*10, i*20),
+                                  List.of(new X(i), new X(i*10)));
+        }
+        return values;
+    }
+
+    @Test
+    public void testValues() {
+        Arrays.stream(values)
+              .filter(v -> (v.i % 2) == 0)
+              .forEach(System.out::println);
+    }
+
+    @Test
+    public void testNullRestrictedType() {
+        Arrays.stream(values)
+                .map(Value::point)
+                .filter(p -> p.x >= 5)
+                .forEach(System.out::println);
+
+    }
+
+    @Test
+    public void testNullableValueType() {
+        Arrays.stream(values)
+                .map(Value::nullablePoint)
+                .filter(p -> p != null)
+                .forEach(System.out::println);
+    }
+
+    @Test
+    public void mapToInt() {
+        Stream<Point> stream = Arrays.stream(values)
+                                     .filter(v -> (v.getI() % 2) == 0)
+                                     .map(Value::point);
+        stream.forEach(p -> assertTrue((p.x % 2) == 0));
+    }
+
+    @Test
+    public void testListOfValues() {
+        long count = Arrays.stream(values)
+                           .map(Value::list)
+                           .flatMap(List::stream)
+                           .map(X::x)
+                           .filter(x -> x >= 10)
+                           .count();
+        assertEquals(count, values.length-1);
+    }
+}
diff a/test/jdk/valhalla/valuetypes/SubstitutabilityTest.java b/test/jdk/valhalla/valuetypes/SubstitutabilityTest.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/SubstitutabilityTest.java
@@ -0,0 +1,205 @@
+/*
+ * 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
+ * @modules java.base/java.lang.runtime:open
+ *          java.base/jdk.internal.value
+ *          java.base/jdk.internal.vm.annotation
+ * @enablePreview
+ * @run junit/othervm SubstitutabilityTest
+ */
+
+import java.lang.reflect.Method;
+import java.util.stream.Stream;
+
+import jdk.internal.value.ValueClass;
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import jdk.internal.vm.annotation.NullRestricted;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class SubstitutabilityTest {
+    @ImplicitlyConstructible
+    static value class Point {
+        public int x;
+        public int y;
+        Point(int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class Line {
+        @NullRestricted
+        Point p1;
+        @NullRestricted
+        Point p2;
+
+        Line(Point p1, Point p2) {
+            this.p1 = p1;
+            this.p2 = p2;
+        }
+        Line(int x1, int y1, int x2, int y2) {
+            this(new Point(x1, y1), new Point(x2, y2));
+        }
+    }
+
+    // contains null-reference and null-restricted fields
+    @ImplicitlyConstructible
+    static value class MyValue {
+        MyValue2 v1;
+        @NullRestricted
+        MyValue2 v2;
+        public MyValue(MyValue2 v1, MyValue2 v2) {
+            this.v1 = v1;
+            this.v2 = v2;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class MyValue2 {
+        static int cnt = 0;
+        int x;
+        MyValue2(int x) {
+            this.x = x;
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class MyFloat {
+        public static float NaN1 = Float.intBitsToFloat(0x7ff00001);
+        public static float NaN2 = Float.intBitsToFloat(0x7ff00002);
+        float x;
+        MyFloat(float x) {
+            this.x = x;
+        }
+        public String toString() {
+            return Float.toString(x);
+        }
+    }
+
+    @ImplicitlyConstructible
+    static value class MyDouble {
+        public static double NaN1 = Double.longBitsToDouble(0x7ff0000000000001L);
+        public static double NaN2 = Double.longBitsToDouble(0x7ff0000000000002L);
+        double x;
+        MyDouble(double x) {
+            this.x = x;
+        }
+        public String toString() {
+            return Double.toString(x);
+        }
+    }
+
+    static Stream<Arguments> substitutableCases() {
+        Point p1 = new Point(10, 10);
+        Point p2 = new Point(20, 20);
+        Line l1 = new Line(p1, p2);
+        MyValue v1 = new MyValue(null, ValueClass.zeroInstance(MyValue2.class));
+        MyValue v2 = new MyValue(new MyValue2(2), new MyValue2(3));
+        MyValue2 value2 = new MyValue2(2);
+        MyValue2 value3 = new MyValue2(3);
+        MyValue[] va = new MyValue[1];
+        return Stream.of(
+                Arguments.of(new MyFloat(1.0f), new MyFloat(1.0f)),
+                Arguments.of(new MyDouble(1.0), new MyDouble(1.0)),
+                Arguments.of(new MyFloat(Float.NaN), new MyFloat(Float.NaN)),
+                Arguments.of(new MyDouble(Double.NaN), new MyDouble(Double.NaN)),
+                Arguments.of(p1, new Point(10, 10)),
+                Arguments.of(p2, new Point(20, 20)),
+                Arguments.of(l1, new Line(10,10, 20,20)),
+                Arguments.of(v1, ValueClass.zeroInstance(MyValue.class)),
+                Arguments.of(v2, new MyValue(value2, value3)),
+                Arguments.of(va[0], null)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("substitutableCases")
+    public void substitutableTest(Object a, Object b) {
+        assertTrue(isSubstitutable(a, b));
+    }
+
+    static Stream<Arguments> notSubstitutableCases() {
+        // MyValue![] va = new MyValue![1];
+        MyValue[] va = new MyValue[] { ValueClass.zeroInstance(MyValue.class) };
+        Object[] oa = new Object[] { va };
+        return Stream.of(
+                Arguments.of(new MyFloat(1.0f), new MyFloat(2.0f)),
+                Arguments.of(new MyDouble(1.0), new MyDouble(2.0)),
+                Arguments.of(new MyFloat(MyFloat.NaN1), new MyFloat(MyFloat.NaN2)),
+                Arguments.of(new MyDouble(MyDouble.NaN1), new MyDouble(MyDouble.NaN2)),
+                Arguments.of(new Point(10, 10), new Point(20, 20)),
+                /*
+                 * Verify ValueObjectMethods::isSubstitutable that does not
+                 * throw an exception if any one of parameter is null or if
+                 * the parameters are of different types.
+                 */
+                Arguments.of(va[0], null),
+                Arguments.of(null, va[0]),
+                Arguments.of(va[0], oa),
+                Arguments.of(va[0], oa[0]),
+                Arguments.of(va, oa),
+                Arguments.of(new Point(10, 10), Integer.valueOf(10)),
+                Arguments.of(Integer.valueOf(10), Integer.valueOf(20))
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("notSubstitutableCases")
+    public void notSubstitutableTest(Object a, Object b) {
+        assertFalse(isSubstitutable(a, b));
+    }
+
+    @Test
+    public void nullArguments() {
+        assertTrue(isSubstitutable(null, null));
+    }
+
+    private static final Method IS_SUBSTITUTABLE;
+    static {
+        Method m = null;
+        try {
+            Class<?> c = Class.forName("java.lang.runtime.ValueObjectMethods");
+            m = c.getDeclaredMethod("isSubstitutable", Object.class, Object.class);
+            m.setAccessible(true);
+        } catch (ReflectiveOperationException e) {
+            throw new RuntimeException(e);
+        }
+        IS_SUBSTITUTABLE = m;
+    }
+    private static boolean isSubstitutable(Object a, Object b) {
+        try {
+            return (boolean) IS_SUBSTITUTABLE.invoke(null, a, b);
+        } catch (ReflectiveOperationException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff a/test/jdk/valhalla/valuetypes/TEST.properties b/test/jdk/valhalla/valuetypes/TEST.properties
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/TEST.properties
@@ -0,0 +1,3 @@
+modules = java.base/jdk.internal.value \
+          java.base/jdk.internal.vm.annotation
+enablePreview = true
diff a/test/jdk/valhalla/valuetypes/UseValueClassTest.java b/test/jdk/valhalla/valuetypes/UseValueClassTest.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/UseValueClassTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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 static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.ParameterizedTest;
+
+import jdk.internal.misc.PreviewFeatures;
+
+import java.util.Arrays;
+import java.util.stream.Stream;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.MonthDay;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.util.Optional;
+import java.util.OptionalDouble;
+import java.util.OptionalInt;
+import java.util.OptionalLong;
+import java.time.Period;
+import java.time.Year;
+import java.time.YearMonth;
+import java.time.ZonedDateTime;
+
+/*
+ * @test
+ * @summary Test that classes are value classes or not depending on --enable-preview
+ * @modules java.base/jdk.internal.misc
+ * @run junit/othervm -Xlog --enable-preview UseValueClassTest
+ * @run junit/othervm -Xlog UseValueClassTest
+ */
+
+public class UseValueClassTest {
+
+    // Classes to be checked
+    private static Stream<Class<?>> classProvider() {
+        Class<?>[] classes = {
+                Byte.class,
+                Short.class,
+                Integer.class,
+                Long.class,
+                Float.class,
+                Double.class,
+                Boolean.class,
+                Character.class,
+                Number.class,
+                Record.class,
+                Duration.class,
+                Instant.class,
+                LocalDate.class,
+                LocalDateTime.class,
+                LocalTime.class,
+                MonthDay.class,
+                OffsetDateTime.class,
+                OffsetTime.class,
+                Optional.class,
+                OptionalDouble.class,
+                OptionalInt.class,
+                OptionalLong.class,
+                Period.class,
+                Year.class,
+                YearMonth.class,
+                ZonedDateTime.class,
+        };
+        return Arrays.stream(classes, 0, classes.length);
+    }
+
+    /**
+     * Verify that the class is a value class if --enable-preview is true
+     * @param clazz a class
+     */
+    @ParameterizedTest
+    @MethodSource("classProvider")
+    public void testValue(Class<?> clazz) {
+        System.out.printf("isPreview: %s%n", PreviewFeatures.isEnabled());
+        assertEquals(PreviewFeatures.isEnabled(), clazz.isValue(), clazz.getName());
+    }
+}
diff a/test/jdk/valhalla/valuetypes/ValueConstantDesc.java b/test/jdk/valhalla/valuetypes/ValueConstantDesc.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/ValueConstantDesc.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ *
+ * 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 ConstantDesc for value classes
+ * @enablePreview
+ * @run junit/othervm ValueConstantDesc
+ */
+
+import java.lang.constant.ClassDesc;
+import java.lang.invoke.MethodHandles;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class ValueConstantDesc {
+    static value class V { }
+
+    private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+
+    static Stream<Arguments> testCases() {
+        return Stream.of(
+                Arguments.of(V.class, "LValueConstantDesc$V;", "ValueConstantDesc$V"),
+                Arguments.of(V[].class, "[LValueConstantDesc$V;", "ValueConstantDesc$V[]"),
+                Arguments.of(V[][].class, "[[LValueConstantDesc$V;", "ValueConstantDesc$V[][]")
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("testCases")
+    public void classDesc(Class<?> type, String descriptor, String displayName) throws ReflectiveOperationException {
+        ClassDesc cd = type.describeConstable().orElseThrow();
+        ClassDesc desc = ClassDesc.ofDescriptor(descriptor);
+
+        if (type.isArray()) {
+            assertTrue(cd.isArray());
+            assertFalse(cd.isClassOrInterface());
+        } else {
+            assertFalse(cd.isArray());
+            assertTrue(cd.isClassOrInterface());
+        }
+        assertEquals(cd, desc);
+        assertEquals(cd.displayName(), displayName);
+        assertEquals(cd.descriptorString(), type.descriptorString());
+
+        Class<?> c = cd.resolveConstantDesc(LOOKUP);
+        assertTrue(c == type);
+    }
+}
diff a/test/jdk/valhalla/valuetypes/WeakReferenceTest.java b/test/jdk/valhalla/valuetypes/WeakReferenceTest.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/WeakReferenceTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ *
+ * 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.ref.WeakReference;
+import java.lang.ref.ReferenceQueue;
+
+import jdk.internal.vm.annotation.ImplicitlyConstructible;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+/*
+ * @test
+ * @summary Test value classes with Reference types
+ * @enablePreview
+ * @run testng/othervm WeakReferenceTest
+ */
+@Test
+public class WeakReferenceTest {
+    @ImplicitlyConstructible
+    static value class Value {
+    }
+
+    @Test(expectedExceptions = IdentityException.class)
+    static void test1() {
+        Value v = new Value();
+        WeakReference<Value> r = new WeakReference<>(v);
+    }
+
+    @Test(expectedExceptions = IdentityException.class)
+    static void test2() {
+        ReferenceQueue<Object> q = new ReferenceQueue<>();
+        Value v = new Value();
+        WeakReference<Value> r = new WeakReference<>(v, q);
+    }
+}
diff a/test/jdk/valhalla/valuetypes/WeakValuePolicyTest.java b/test/jdk/valhalla/valuetypes/WeakValuePolicyTest.java
--- /dev/null
+++ b/test/jdk/valhalla/valuetypes/WeakValuePolicyTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2022, 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
+ * @run testng/othervm WeakValuePolicyTest
+ * @summary Test WeakHashMap.ValuePolicy modes
+ */
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import org.testng.Assert;
+import static org.testng.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Objects;
+import java.util.WeakHashMap;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+
+@Test
+public class WeakValuePolicyTest {
+
+    record KeyValue(Object key, String value){};
+
+    @DataProvider(name="Keys")
+    KeyValue[] keys() {
+        return new KeyValue[] {
+                new KeyValue(new IntValue(1), "IntValue(1)"),
+                new KeyValue(new StringValue("xyz"), "StringValue(\"xyz\")"),
+                new KeyValue(Integer.valueOf(2), "Integer.valueOf(2)"),
+        };
+    }
+
+    @DataProvider(name="WeakHashMaps")
+    Object[][] weakValuePolicy() {
+        return new Object[][] {
+                {new WeakHashMap<Object, String>(0, 0.75f, WeakHashMap.ValuePolicy.SOFT)},
+                {new WeakHashMap<Object, String>(0, 0.75f, WeakHashMap.ValuePolicy.STRONG)},
+        };
+    }
+
+    @Test(dataProvider="WeakHashMaps")
+    public void putValueSoftStrong(WeakHashMap map) {
+        WeakHashMap.ValuePolicy policy = map.valuePolicy();
+        for (KeyValue kv : keys()) {
+            System.out.println("k: " + kv.key);
+            Assert.assertFalse(map.containsKey(kv.key), "map.contains on empty map: " + kv.key);
+            Assert.assertNull(map.get(kv.key), "map.get on empty map: " + kv.key);
+            var prev = map.put(kv.key, kv.value);
+            Assert.assertNull(prev, "map.put on empty map did not return null: " + kv.key);
+            Assert.assertEquals(map.get(kv.key), kv.value, "map.get after put: " + kv.key);
+
+            forceGC();
+
+            if (kv.key.getClass().isValue() &&
+                    policy.equals(WeakHashMap.ValuePolicy.SOFT)) {
+                Assert.assertFalse(map.containsKey(kv.key), "map.containsKey after GC: " + kv.key);
+                String value = (String)map.get(kv.key);
+                Assert.assertNull(value, "map.get after GC should return null: " + kv.key);
+            } else {
+                prev = map.remove(kv.key);
+                Assert.assertEquals(prev, kv.value, "map.remove: " + kv.key);
+                Assert.assertNull(map.get(kv.key), "map.get after remove: " + kv.key);
+            }
+            Assert.assertTrue(map.isEmpty(), "m.isEmpty()");
+        }
+    }
+
+    @Test
+    public void putValueDiscard() {
+        final WeakHashMap<Object, String> map = new WeakHashMap<>(0, 0.75f, WeakHashMap.ValuePolicy.DISCARD);
+        final IntValue intValue = new IntValue(1);
+        String old = map.put(intValue, "IntValue(1)");
+        Assert.assertNull(old, "old");
+        old = map.get(intValue);
+        Assert.assertNull(old, "get after put of discarded value");
+    }
+
+    @Test
+    public void putValueThrows() {
+        final WeakHashMap<Object, String> map = new WeakHashMap<>(0, 0.75f, WeakHashMap.ValuePolicy.THROW);
+        Assert.assertThrows(IdentityException.class, () -> map.put(new IntValue(1), "IntValue(1)"));
+    }
+
+    private void forceGC()  {
+        Object marker = new Object();
+        ReferenceQueue<Object> queue = new ReferenceQueue<>();
+        SoftReference expected = new SoftReference(marker, queue);
+        marker = null;
+        Reference<?> actual = waitForReference(queue);
+        assertEquals(actual, expected, "Unexpected Reference queued");
+    }
+
+    /**
+     * Wait for any Reference to be enqueued to a ReferenceQueue.
+     * The garbage collector is invoked to find unreferenced objects.
+     *
+     * @param queue a ReferenceQueue
+     * @return true if the reference was enqueued, false if not enqueued within
+     */
+    private static Reference<?> waitForReference(ReferenceQueue<Object> queue) {
+        Objects.requireNonNull(queue, "queue should not be null");
+        ArrayList<Object> chunks = new ArrayList<>(10000);
+        try {
+            for (int i = 0; i < 10_000; i++) {
+                chunks.add(new byte[100_000]);
+            }
+        } catch (OutOfMemoryError oome) {
+
+        } finally {
+            chunks = null;
+        }
+        for (int retries = 100; retries > 0; retries--) {
+            try {
+                var r = queue.remove(10L);
+                if (r != null) {
+                    return r;
+                }
+            } catch (InterruptedException ie) {
+                // ignore, the loop will try again
+            }
+        }
+        return null;
+    }
+
+    static value class IntValue {
+        int value;
+
+        IntValue(int value) {
+            this.value = value;
+        }
+
+        @java.lang.Override
+        public java.lang.String toString() {
+            return "IntValue{" + "value=" + value + '}';
+        }
+    }
+
+    static value class StringValue {
+        String value;
+
+        StringValue(String value) {
+            this.value = value;
+        }
+
+        @java.lang.Override
+        public java.lang.String toString() {
+            return "StringValue{" + "value='" + value + '\'' + '}';
+        }
+    }
+}
diff a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java
--- a/test/jtreg-ext/requires/VMProps.java
+++ b/test/jtreg-ext/requires/VMProps.java
@@ -387,10 +387,11 @@
         vmOptFinalFlag(map, "ClassUnloading");
         vmOptFinalFlag(map, "ClassUnloadingWithConcurrentMark");
         vmOptFinalFlag(map, "CriticalJNINatives");
         vmOptFinalFlag(map, "EnableJVMCI");
         vmOptFinalFlag(map, "EliminateAllocations");
+        vmOptFinalFlag(map, "TieredCompilation");
         vmOptFinalFlag(map, "UseCompressedOops");
         vmOptFinalFlag(map, "UseVectorizedMismatchIntrinsic");
         vmOptFinalFlag(map, "UseVtableBasedCHA");
         vmOptFinalFlag(map, "ZGenerational");
     }
diff a/test/langtools/ProblemList.txt b/test/langtools/ProblemList.txt
--- a/test/langtools/ProblemList.txt
+++ b/test/langtools/ProblemList.txt
@@ -51,10 +51,17 @@
 jdk/jshell/UserJdiUserRemoteTest.java                                           8173079    linux-all
 jdk/jshell/UserInputTest.java                                                   8169536    generic-all
 jdk/jshell/ToolBasicTest.java                                                   8265357    macosx-aarch64
 jdk/jshell/HighlightUITest.java                                                 8284144    generic-all
 
+jdk/jshell/UserExecutionControlTest.java                                        8308779    generic-all
+jdk/jshell/ToolLocalSimpleTest.java                                             8308779    generic-all
+jdk/jshell/SimpleRegressionTest.java                                            8308779    generic-all
+jdk/jshell/ExecutionControlSpecTest.java                                        8308779    generic-all
+jdk/jshell/ExceptionMessageTest.java                                            8308779    generic-all
+jdk/jshell/ClassMembersTest.java                                                8308779    generic-all
+
 ###########################################################################
 #
 # javac
 
 tools/javac/annotations/typeAnnotations/failures/CantAnnotatePackages.java      8057679    generic-all    clarify error messages trying to annotate scoping
@@ -70,9 +77,8 @@
 ###########################################################################
 #
 # javap
 
 tools/javap/output/RepeatingTypeAnnotations.java                                8057687    generic-all    emit correct byte code an attributes for type annotations
-
 ###########################################################################
 #
 # jdeps
diff a/test/langtools/jdk/javadoc/doclet/testValueClasses/TestValueClasses.java b/test/langtools/jdk/javadoc/doclet/testValueClasses/TestValueClasses.java
--- /dev/null
+++ b/test/langtools/jdk/javadoc/doclet/testValueClasses/TestValueClasses.java
@@ -0,0 +1,66 @@
+/*
+ * 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      8308590
+ * @summary  value classes
+ * @library  /tools/lib ../../lib
+ * @modules jdk.javadoc/jdk.javadoc.internal.tool
+ * @build    toolbox.ToolBox javadoc.tester.*
+ * @run main TestValueClasses
+ */
+
+import java.io.IOException;
+import java.nio.file.Path;
+
+import javadoc.tester.JavadocTester;
+import toolbox.ToolBox;
+
+public class TestValueClasses extends JavadocTester {
+
+    public static void main(String... args) throws Exception {
+        var tester = new TestValueClasses();
+        tester.runTests();
+    }
+
+    private final ToolBox tb = new ToolBox();
+
+    @Test
+    public void testValueClassModifiers(Path base) throws IOException {
+        Path src = base.resolve("src");
+        tb.writeJavaFiles(src,
+                "package p; public value class ValueClass {}");
+
+        javadoc("-d", base.resolve("out").toString(),
+                "--enable-preview", "-source", String.valueOf(Runtime.version().feature()),
+                "-sourcepath", src.toString(),
+                "p");
+        checkExit(Exit.OK);
+
+        checkOutput("p/ValueClass.html", true,
+                """
+                <div class="type-signature"><span class="modifiers">public value final class </span><span class="element-name type-name-label">ValueClass</span>
+                """);
+    }
+}
diff a/test/langtools/jdk/jshell/Test8294583.java b/test/langtools/jdk/jshell/Test8294583.java
--- a/test/langtools/jdk/jshell/Test8294583.java
+++ b/test/langtools/jdk/jshell/Test8294583.java
@@ -25,10 +25,11 @@
  * @test
  * @bug 8294583
  * @summary JShell: NPE in switch with non existing record pattern
  * @build KullaTesting TestingInputStream
  * @run testng Test8294583
+ * @ignore 8316628
  */
 
 import org.testng.annotations.Test;
 import static org.testng.Assert.assertEquals;
 
diff a/test/langtools/jdk/jshell/Test8296012.java b/test/langtools/jdk/jshell/Test8296012.java
--- a/test/langtools/jdk/jshell/Test8296012.java
+++ b/test/langtools/jdk/jshell/Test8296012.java
@@ -25,10 +25,11 @@
  * @test
  * @bug 8296012
  * @summary jshell crashes on mismatched record pattern
  * @build KullaTesting TestingInputStream
  * @run testng Test8296012
+ * @ignore 8316628
  */
 
 import org.testng.annotations.Test;
 import static org.testng.Assert.assertEquals;
 
diff a/test/langtools/jdk/jshell/ToolEnablePreviewTest.java b/test/langtools/jdk/jshell/ToolEnablePreviewTest.java
--- a/test/langtools/jdk/jshell/ToolEnablePreviewTest.java
+++ b/test/langtools/jdk/jshell/ToolEnablePreviewTest.java
@@ -24,10 +24,11 @@
 /*
  * @test
  * @bug 8199193
  * @summary Tests for the --enable-preview option
  * @run testng ToolEnablePreviewTest
+ * @ignore 8316628
  */
 
 import org.testng.annotations.Test;
 
 import static org.testng.Assert.assertTrue;
diff a/test/langtools/lib/combo/tools/javac/combo/CompilationTestCase.java b/test/langtools/lib/combo/tools/javac/combo/CompilationTestCase.java
--- a/test/langtools/lib/combo/tools/javac/combo/CompilationTestCase.java
+++ b/test/langtools/lib/combo/tools/javac/combo/CompilationTestCase.java
@@ -110,10 +110,14 @@
 
     protected void assertOKWithWarning(String warning, String... constructs) {
         assertCompile(expandMarkers(constructs), () -> assertCompileSucceededWithWarning(warning), false);
     }
 
+    protected void assertOKWithWarning(String warning, int numberOfTimes, String... constructs) {
+        assertCompile(expandMarkers(constructs), () -> assertCompileSucceededWithWarning(warning, numberOfTimes), false);
+    }
+
     protected void assertFail(String expectedDiag, String... constructs) {
         assertCompile(expandMarkers(constructs), () -> assertCompileFailed(expectedDiag), false);
     }
 
     protected void assertFail(String expectedDiag, Consumer<Diagnostic<?>> diagConsumer, String... constructs) {
diff a/test/langtools/lib/combo/tools/javac/combo/Diagnostics.java b/test/langtools/lib/combo/tools/javac/combo/Diagnostics.java
--- a/test/langtools/lib/combo/tools/javac/combo/Diagnostics.java
+++ b/test/langtools/lib/combo/tools/javac/combo/Diagnostics.java
@@ -82,10 +82,16 @@
         return diags.stream()
                     .filter(d -> d.getKind() == Diagnostic.Kind.WARNING || d.getKind() == Diagnostic.Kind.MANDATORY_WARNING)
                     .anyMatch(d -> d.getCode().equals(key));
     }
 
+    public boolean containsWarningKey(String key, int numberOfWarnings) {
+        return diags.stream()
+                .filter(d -> d.getKind() == Diagnostic.Kind.WARNING || d.getKind() == Diagnostic.Kind.MANDATORY_WARNING)
+                .filter(d -> d.getCode().equals(key)).count() == numberOfWarnings;
+    }
+
     /** Get the error keys */
     public List<String> errorKeys() {
         return diags.stream()
                     .filter(d -> d.getKind() == Diagnostic.Kind.ERROR)
                     .map(Diagnostic::getCode)
diff a/test/langtools/lib/combo/tools/javac/combo/JavacTemplateTestBase.java b/test/langtools/lib/combo/tools/javac/combo/JavacTemplateTestBase.java
--- a/test/langtools/lib/combo/tools/javac/combo/JavacTemplateTestBase.java
+++ b/test/langtools/lib/combo/tools/javac/combo/JavacTemplateTestBase.java
@@ -152,10 +152,18 @@
         if (!diags.containsWarningKey(warning)) {
             fail(String.format("Expected compilation warning with %s, found %s", warning, diags.keys()));
         }
     }
 
+    protected void assertCompileSucceededWithWarning(String warning, int numberOfWarnings) {
+        if (diags.errorsFound())
+            fail("Expected successful compilation");
+        if (!diags.containsWarningKey(warning, numberOfWarnings)) {
+            fail(String.format("Expected compilation warning with %s, found %s", warning, diags.keys()));
+        }
+    }
+
     /**
      * If the provided boolean is true, assert all previous compiles succeeded,
      * otherwise assert that a compile failed.
      * */
     protected void assertCompileSucceededIff(boolean b) {
diff a/test/langtools/tools/javac/AnonymousClass/AnonymousClassFlags.java b/test/langtools/tools/javac/AnonymousClass/AnonymousClassFlags.java
--- a/test/langtools/tools/javac/AnonymousClass/AnonymousClassFlags.java
+++ b/test/langtools/tools/javac/AnonymousClass/AnonymousClassFlags.java
@@ -47,42 +47,42 @@
     private static Map<String, Integer> anonClasses = new LinkedHashMap<>();
 
     // ******* TEST CASES ********
 
     static Object o1 = new Object() {
-        { anonClasses.put(getClass().getName(), 0); }
+        { anonClasses.put(getClass().getName(), ClassFile.ACC_IDENTITY); }
     };
 
     static void staticMethod() {
         Object o2 = new Object() {
-            { anonClasses.put(getClass().getName(), 0); }
+            { anonClasses.put(getClass().getName(), ClassFile.ACC_IDENTITY); }
         };
     }
 
     static {
         staticMethod();
 
         Object o3 = new Object() {
-            { anonClasses.put(getClass().getName(), 0); }
+            { anonClasses.put(getClass().getName(), ClassFile.ACC_IDENTITY); }
         };
     }
 
     Object o4 = new Object() {
-        { anonClasses.put(getClass().getName(), 0); }
+        { anonClasses.put(getClass().getName(), ClassFile.ACC_IDENTITY); }
     };
 
     void instanceMethod() {
         Object o5 = new Object() {
-            { anonClasses.put(getClass().getName(), 0); }
+            { anonClasses.put(getClass().getName(), ClassFile.ACC_IDENTITY); }
         };
     }
 
     {
         instanceMethod();
 
         Object o6 = new Object() {
-            { anonClasses.put(getClass().getName(), 0); }
+            { anonClasses.put(getClass().getName(), ClassFile.ACC_IDENTITY); }
         };
     }
 
     // ******* TEST IMPLEMENTATION ********
 
@@ -103,12 +103,12 @@
         }
     }
 
     static void assertClassFlags(ClassModel classFile, String name, int expected) {
         int mask = ClassFile.ACC_PUBLIC | ClassFile.ACC_FINAL | ClassFile.ACC_INTERFACE | ClassFile.ACC_ABSTRACT |
-                   ClassFile.ACC_SYNTHETIC | ClassFile.ACC_ANNOTATION | ClassFile.ACC_ENUM;
-        int classExpected = (expected & mask) | ClassFile.ACC_SUPER;
+                   ClassFile.ACC_SYNTHETIC | ClassFile.ACC_ANNOTATION | ClassFile.ACC_ENUM | ClassFile.ACC_IDENTITY;
+        int classExpected = (expected & mask);
         int classActual = classFile.flags().flagsMask();
         if (classActual != classExpected) {
             throw new AssertionError("Incorrect access_flags for class " + name +
                                      ": expected=" + classExpected + ", actual=" + classActual);
         }
diff a/test/langtools/tools/javac/ClassFileModifiers/ClassModifiers.out b/test/langtools/tools/javac/ClassFileModifiers/ClassModifiers.out
--- a/test/langtools/tools/javac/ClassFileModifiers/ClassModifiers.out
+++ b/test/langtools/tools/javac/ClassFileModifiers/ClassModifiers.out
@@ -1,123 +1,123 @@
 
 CLASSFILE  T.iC
---- SUPER
+---
 INNERCLASS  iC
 ---
 
 CLASSFILE  T.iSC
---- SUPER
+---
 INNERCLASS  iSC
 --- STATIC
 
 CLASSFILE  T.iVC
---- SUPER
+---
 INNERCLASS  iVC
 --- PRIVATE
 
 CLASSFILE  T.iSVC
---- SUPER
+---
 INNERCLASS  iSVC
 --- PRIVATE STATIC
 
 CLASSFILE  T.iFC
---- FINAL SUPER
+--- FINAL
 INNERCLASS  iFC
 --- FINAL
 
 CLASSFILE  T.iSFC
---- FINAL SUPER
+--- FINAL
 INNERCLASS  iSFC
 --- STATIC FINAL
 
 CLASSFILE  T.iFVC
---- FINAL SUPER
+--- FINAL
 INNERCLASS  iFVC
 --- PRIVATE FINAL
 
 CLASSFILE  T.iSFVC
---- FINAL SUPER
+--- FINAL
 INNERCLASS  iSFVC
 --- PRIVATE STATIC FINAL
 
 CLASSFILE  T.iAC
---- SUPER ABSTRACT
+--- ABSTRACT
 INNERCLASS  iAC
 --- ABSTRACT
 
 CLASSFILE  T.iSAC
---- SUPER ABSTRACT
+--- ABSTRACT
 INNERCLASS  iSAC
 --- STATIC ABSTRACT
 
 CLASSFILE  T.iAVC
---- SUPER ABSTRACT
+--- ABSTRACT
 INNERCLASS  iAVC
 --- PRIVATE ABSTRACT
 
 CLASSFILE  T.iSAVC
---- SUPER ABSTRACT
+--- ABSTRACT
 INNERCLASS  iSAVC
 --- PRIVATE STATIC ABSTRACT
 
 CLASSFILE  T.iRC
---- PUBLIC SUPER
+--- PUBLIC
 INNERCLASS  iRC
 --- PROTECTED
 
 CLASSFILE  T.iSRC
---- PUBLIC SUPER
+--- PUBLIC
 INNERCLASS  iSRC
 --- PROTECTED STATIC
 
 CLASSFILE  T.iUC
---- PUBLIC SUPER
+--- PUBLIC
 INNERCLASS  iUC
 --- PUBLIC
 
 CLASSFILE  T.iSUC
---- PUBLIC SUPER
+--- PUBLIC
 INNERCLASS  iSUC
 --- PUBLIC STATIC
 
 CLASSFILE  T.iFRC
---- PUBLIC FINAL SUPER
+--- PUBLIC FINAL
 INNERCLASS  iFRC
 --- PROTECTED FINAL
 
 CLASSFILE  T.iSFRC
---- PUBLIC FINAL SUPER
+--- PUBLIC FINAL
 INNERCLASS  iSFRC
 --- PROTECTED STATIC FINAL
 
 CLASSFILE  T.iFUC
---- PUBLIC FINAL SUPER
+--- PUBLIC FINAL
 INNERCLASS  iFUC
 --- PUBLIC FINAL
 
 CLASSFILE  T.iSFUC
---- PUBLIC FINAL SUPER
+--- PUBLIC FINAL
 INNERCLASS  iSFUC
 --- PUBLIC STATIC FINAL
 
 CLASSFILE  T.iARC
---- PUBLIC SUPER ABSTRACT
+--- PUBLIC ABSTRACT
 INNERCLASS  iARC
 --- PROTECTED ABSTRACT
 
 CLASSFILE  T.iSARC
---- PUBLIC SUPER ABSTRACT
+--- PUBLIC ABSTRACT
 INNERCLASS  iSARC
 --- PROTECTED STATIC ABSTRACT
 
 CLASSFILE  T.iAUC
---- PUBLIC SUPER ABSTRACT
+--- PUBLIC ABSTRACT
 INNERCLASS  iAUC
 --- PUBLIC ABSTRACT
 
 CLASSFILE  T.iSAUC
---- PUBLIC SUPER ABSTRACT
+--- PUBLIC ABSTRACT
 INNERCLASS  iSAUC
 --- PUBLIC STATIC ABSTRACT
 
 CLASSFILE  T.iI
 --- INTERFACE ABSTRACT
@@ -198,11 +198,11 @@
 --- PUBLIC INTERFACE ABSTRACT
 INNERCLASS  iSAUI
 --- PUBLIC STATIC INTERFACE ABSTRACT
 
 CLASSFILE  T
---- SUPER
+---
 INNERCLASS  iSAUI
 --- PUBLIC STATIC INTERFACE ABSTRACT
 INNERCLASS  iAUI
 --- PUBLIC STATIC INTERFACE ABSTRACT
 INNERCLASS  iSUI
@@ -281,66 +281,66 @@
 --- STATIC
 INNERCLASS  iC
 ---
 
 CLASSFILE  U.jC
---- PUBLIC SUPER
+--- PUBLIC
 INNERCLASS  jC
 --- PUBLIC STATIC
 
 CLASSFILE  U.jSC
---- PUBLIC SUPER
+--- PUBLIC
 INNERCLASS  jSC
 --- PUBLIC STATIC
 
 CLASSFILE  U.jUC
---- PUBLIC SUPER
+--- PUBLIC
 INNERCLASS  jUC
 --- PUBLIC STATIC
 
 CLASSFILE  U.jSUC
---- PUBLIC SUPER
+--- PUBLIC
 INNERCLASS  jSUC
 --- PUBLIC STATIC
 
 CLASSFILE  U.jFC
---- PUBLIC FINAL SUPER
+--- PUBLIC FINAL
 INNERCLASS  jFC
 --- PUBLIC STATIC FINAL
 
 CLASSFILE  U.jSFC
---- PUBLIC FINAL SUPER
+--- PUBLIC FINAL
 INNERCLASS  jSFC
 --- PUBLIC STATIC FINAL
 
 CLASSFILE  U.jFUC
---- PUBLIC FINAL SUPER
+--- PUBLIC FINAL
 INNERCLASS  jFUC
 --- PUBLIC STATIC FINAL
 
 CLASSFILE  U.jSFUC
---- PUBLIC FINAL SUPER
+--- PUBLIC FINAL
 INNERCLASS  jSFUC
 --- PUBLIC STATIC FINAL
 
 CLASSFILE  U.jAC
---- PUBLIC SUPER ABSTRACT
+--- PUBLIC ABSTRACT
 INNERCLASS  jAC
 --- PUBLIC STATIC ABSTRACT
 
 CLASSFILE  U.jSAC
---- PUBLIC SUPER ABSTRACT
+--- PUBLIC ABSTRACT
 INNERCLASS  jSAC
 --- PUBLIC STATIC ABSTRACT
 
 CLASSFILE  U.jAUC
---- PUBLIC SUPER ABSTRACT
+--- PUBLIC ABSTRACT
 INNERCLASS  jAUC
 --- PUBLIC STATIC ABSTRACT
 
 CLASSFILE  U.jSAUC
---- PUBLIC SUPER ABSTRACT
+--- PUBLIC ABSTRACT
 INNERCLASS  jSAUC
 --- PUBLIC STATIC ABSTRACT
 
 CLASSFILE  U.jI
 --- PUBLIC INTERFACE ABSTRACT
diff a/test/langtools/tools/javac/ClassFileModifiers/MemberModifiers.out b/test/langtools/tools/javac/ClassFileModifiers/MemberModifiers.out
--- a/test/langtools/tools/javac/ClassFileModifiers/MemberModifiers.out
+++ b/test/langtools/tools/javac/ClassFileModifiers/MemberModifiers.out
@@ -1,36 +1,36 @@
 
 CLASSFILE  MemberModifiers.c
---- SUPER
+---
 METHOD  <init>
 ---
 
 CLASSFILE  MemberModifiers.i
 --- INTERFACE ABSTRACT
 
 CLASSFILE  MemberModifiers
---- PUBLIC FINAL SUPER
+--- PUBLIC FINAL
 FIELD  f
 ---
 METHOD  <init>
 --- PUBLIC
 METHOD  m
 ---
 
 CLASSFILE  MemberModifiersAux.Foo.c
---- SUPER
+---
 METHOD  <init>
 ---
 
 CLASSFILE  MemberModifiersAux.Foo
---- FINAL SUPER
+--- FINAL
 FIELD  f
 ---
 METHOD  <init>
 ---
 METHOD  m
 ---
 
 CLASSFILE  MemberModifiersAux
---- SUPER
+---
 METHOD  <init>
 ---
diff a/test/langtools/tools/javac/DefiniteAssignment/DA_DUConstructors.java b/test/langtools/tools/javac/DefiniteAssignment/DA_DUConstructors.java
--- /dev/null
+++ b/test/langtools/tools/javac/DefiniteAssignment/DA_DUConstructors.java
@@ -0,0 +1,91 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8324873
+ * @summary [lworld] implementation of value classes construction
+ * @enablePreview
+ * @compile/fail/ref=DA_DUConstructors.out -XDrawDiagnostics DA_DUConstructors.java
+ */
+
+public class DA_DUConstructors {
+    // identity
+    class C1 {
+        final int x;
+        final int y = x + 1;
+        C1() {
+            x = 12;
+            super();
+        }
+    }
+
+    class C2 {
+        final int x;
+        C2() {
+            this(x = 3); // error
+        }
+        C2(int i) {
+            x = 4;
+        }
+    }
+
+    class C3 {
+        C3(int i) {}
+    }
+    class C4 extends C3 {
+        final int x;
+        C4() {
+            super(x = 3); // ok
+        }
+    }
+
+    class C5 {
+        final int x;
+        final int y = x + 1; // x is not DA
+        C5() {
+            x = 12; super();
+        }
+        C5(int i) {
+            /* no prologue */
+            x = i;
+        }
+    }
+
+    // value classes
+    value class V1 {
+        int x;
+        int y = x + 1; // allowed
+        V1() {
+            x = 12;
+            // super();
+        }
+    }
+
+    value class V2 {
+        int x;
+        V2() { this(x = 3); } // error
+        V2(int i) { x = 4; }
+    }
+
+    abstract value class AV1 {
+        AV1(int i) {}
+    }
+
+    value class V3 extends AV1 {
+        int x;
+        V3() {
+            super(x = 3); // ok
+        }
+    }
+
+    value class V4 { // OK
+        int x;
+        int y = x + 1;
+
+        V4() {
+            x = 12;
+        }
+
+        V4(int i) {
+            x = i;
+        }
+    }
+}
diff a/test/langtools/tools/javac/DefiniteAssignment/DA_DUConstructors.out b/test/langtools/tools/javac/DefiniteAssignment/DA_DUConstructors.out
--- /dev/null
+++ b/test/langtools/tools/javac/DefiniteAssignment/DA_DUConstructors.out
@@ -0,0 +1,6 @@
+DA_DUConstructors.java:23:17: compiler.err.var.might.already.be.assigned: x
+DA_DUConstructors.java:42:23: compiler.err.var.might.not.have.been.initialized: x
+DA_DUConstructors.java:64:20: compiler.err.var.might.already.be.assigned: x
+- compiler.note.preview.filename: DA_DUConstructors.java, DEFAULT
+- compiler.note.preview.recompile
+3 errors
diff a/test/langtools/tools/javac/Diagnostics/8043251/T8043251.out b/test/langtools/tools/javac/Diagnostics/8043251/T8043251.out
--- a/test/langtools/tools/javac/Diagnostics/8043251/T8043251.out
+++ b/test/langtools/tools/javac/Diagnostics/8043251/T8043251.out
@@ -1,2 +1,2 @@
-T8043251.java:9:42: compiler.err.cant.apply.symbol.noargs: kindname.method, identity, kindname.interface, java.util.function.Function<T,R>, (compiler.misc.wrong.number.type.args: 1)
+T8043251.java:9:42: compiler.err.cant.apply.symbol.noargs: kindname.method, identity, java.util.function.Function<T,R>, (compiler.misc.wrong.number.type.args: 1)
 1 error
diff a/test/langtools/tools/javac/SuperInit/SuperInitFails.java b/test/langtools/tools/javac/SuperInit/SuperInitFails.java
--- a/test/langtools/tools/javac/SuperInit/SuperInitFails.java
+++ b/test/langtools/tools/javac/SuperInit/SuperInitFails.java
@@ -89,11 +89,11 @@
             return;                     // this should FAIL
         super();
     }
 
     public SuperInitFails(short[] x) {
-        this.x = x.length;              // this should FAIL
+        this.x = x.length;              // this should work
         super();
     }
 
     public SuperInitFails(float[] x) {
         System.identityHashCode(this);  // this should FAIL
diff a/test/langtools/tools/javac/SuperInit/SuperInitFails.out b/test/langtools/tools/javac/SuperInit/SuperInitFails.out
--- a/test/langtools/tools/javac/SuperInit/SuperInitFails.out
+++ b/test/langtools/tools/javac/SuperInit/SuperInitFails.out
@@ -1,11 +1,10 @@
 SuperInitFails.java:57:9: compiler.err.cant.ref.before.ctor.called: hashCode()
 SuperInitFails.java:62:9: compiler.err.cant.ref.before.ctor.called: this
 SuperInitFails.java:67:9: compiler.err.cant.ref.before.ctor.called: super
 SuperInitFails.java:72:23: compiler.err.cant.ref.before.ctor.called: this
 SuperInitFails.java:77:23: compiler.err.cant.ref.before.ctor.called: super
-SuperInitFails.java:94:9: compiler.err.cant.ref.before.ctor.called: this
 SuperInitFails.java:99:33: compiler.err.cant.ref.before.ctor.called: this
 SuperInitFails.java:104:14: compiler.err.cant.ref.before.ctor.called: this
 SuperInitFails.java:108:20: compiler.err.not.encl.class: java.lang.Object
 SuperInitFails.java:112:17: compiler.err.cant.ref.before.ctor.called: super
 SuperInitFails.java:119:22: compiler.err.call.must.only.appear.in.ctor
@@ -22,8 +21,6 @@
 SuperInitFails.java:49:33: compiler.err.call.must.only.appear.in.ctor
 SuperInitFails.java:53:32: compiler.err.call.must.only.appear.in.ctor
 SuperInitFails.java:83:18: compiler.err.ctor.calls.not.allowed.here
 SuperInitFails.java:89:13: compiler.err.return.before.superclass.initialized
 SuperInitFails.java:160:18: compiler.err.ctor.calls.not.allowed.here
-- compiler.note.preview.filename: SuperInitFails.java, DEFAULT
-- compiler.note.preview.recompile
-26 errors
+25 errors
diff a/test/langtools/tools/javac/SuperInit/SuperInitGood.java b/test/langtools/tools/javac/SuperInit/SuperInitGood.java
--- a/test/langtools/tools/javac/SuperInit/SuperInitGood.java
+++ b/test/langtools/tools/javac/SuperInit/SuperInitGood.java
@@ -23,10 +23,11 @@
 /*
  * @test
  * @bug 8194743
  * @summary Test valid placements of super()/this() in constructors
  * @enablePreview
+ * @ignore fails at execution time because of Optional
  */
 
 import java.util.concurrent.atomic.AtomicReference;
 
 public class SuperInitGood {
diff a/test/langtools/tools/javac/SuperInit/ValueClassSuperInitFails.java b/test/langtools/tools/javac/SuperInit/ValueClassSuperInitFails.java
--- /dev/null
+++ b/test/langtools/tools/javac/SuperInit/ValueClassSuperInitFails.java
@@ -0,0 +1,181 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8324873
+ * @summary Permit additional statements before this/super in constructors
+ * @compile/fail/ref=ValueClassSuperInitFails.out -XDrawDiagnostics ValueClassSuperInitFails.java
+ * @enablePreview
+ */
+
+abstract value class AR<V> implements java.io.Serializable {
+    public AR(V initialValue) {
+    }
+
+    public AR() {
+    }
+}
+
+value class ValueClassSuperInitFails extends AR <Object> implements Iterable<Object> {
+
+    private int x;
+
+/// GOOD EXAMPLES
+
+    public ValueClassSuperInitFails() {           // this should be OK
+        // super()
+    }
+
+    public ValueClassSuperInitFails(Object x) {
+        this.x = x.hashCode();          // this should be OK
+        // super();  the compiler will introduce the super call at this location
+    }
+
+    public ValueClassSuperInitFails(byte x) {
+        super();                        // this should be OK
+    }
+
+    public ValueClassSuperInitFails(char x) {
+        this((int)x);                   // this should be OK
+    }
+
+/// FAIL EXAMPLES
+
+    {
+        this(1);                        // this should FAIL
+    }
+
+    {
+        super();                        // this should FAIL
+    }
+
+    void normalMethod1() {
+        super();                        // this should FAIL
+    }
+
+    void normalMethod2() {
+        this();                         // this should FAIL
+    }
+
+    void normalMethod3() {
+        Runnable r = () -> super();     // this should FAIL
+    }
+
+    void normalMethod4() {
+        Runnable r = () -> this();      // this should FAIL
+    }
+
+    public ValueClassSuperInitFails(short x) {
+        hashCode();                     // this should FAIL
+        //super();
+    }
+
+    public ValueClassSuperInitFails(float x) {
+        this.hashCode();                // this should FAIL
+        //super();
+    }
+
+    public ValueClassSuperInitFails(int x) {
+        super.hashCode();               // this should FAIL
+        //super();
+    }
+
+    public ValueClassSuperInitFails(long x) {
+        ValueClassSuperInitFails.this.hashCode();      // this should FAIL
+        //super();
+    }
+
+    public ValueClassSuperInitFails(double x) {
+        ValueClassSuperInitFails.super.hashCode();     // this should FAIL
+        //super();
+    }
+
+    public ValueClassSuperInitFails(byte[] x) {
+        {
+            super();                    // this should FAIL
+        }
+    }
+
+    public ValueClassSuperInitFails(char[] x) {
+        if (x.length == 0)
+            return;                     // this should FAIL
+        //super();
+    }
+
+    public ValueClassSuperInitFails(short[] x) {
+        this.x = x.length;              // this should be OK
+        //super();
+    }
+
+    public ValueClassSuperInitFails(float[] x) {
+        System.identityHashCode(this);  // this should FAIL
+        //super();
+    }
+
+    public ValueClassSuperInitFails(int[] x) {
+        this(this);                     // this should FAIL
+    }
+
+    public ValueClassSuperInitFails(long[] x) {
+        this(Object.this);              // this should FAIL
+    }
+
+    public ValueClassSuperInitFails(double[] x) {
+        Iterable.super.spliterator();   // this should FAIL
+        //super();
+    }
+
+    public ValueClassSuperInitFails(byte[][] x) {
+        super(new Object() {
+            {
+                super();                // this should FAIL
+            }
+        });
+    }
+
+    public ValueClassSuperInitFails(char[][] x) {
+        new Inner1();                   // this should FAIL
+        //super();
+    }
+
+    class Inner1 {
+    }
+
+    record Record1(int value) {
+        Record1(float x) {              // this should FAIL
+        }
+    }
+
+    record Record2(int value) {
+        Record2(float x) {              // this should FAIL
+            super();
+        }
+    }
+
+    @Override
+    public java.util.Iterator<Object> iterator() {
+        return null;
+    }
+
+    public ValueClassSuperInitFails(short[][] x) {
+        class Foo {
+            Foo() {
+                ValueClassSuperInitFails.this.hashCode();
+            }
+        };
+        new Foo();                      // this should FAIL
+        //super();
+    }
+
+    public ValueClassSuperInitFails(float[][] x) {
+        Runnable r = () -> {
+            super();                    // this should FAIL
+        };
+    }
+
+    public ValueClassSuperInitFails(int[][] z) {
+        super((Runnable)() -> x);       // this should FAIL
+    }
+
+    public ValueClassSuperInitFails(long[][] z) {
+        super(new Inner1());            // this should FAIL
+    }
+}
diff a/test/langtools/tools/javac/SuperInit/ValueClassSuperInitFails.out b/test/langtools/tools/javac/SuperInit/ValueClassSuperInitFails.out
--- /dev/null
+++ b/test/langtools/tools/javac/SuperInit/ValueClassSuperInitFails.out
@@ -0,0 +1,28 @@
+ValueClassSuperInitFails.java:67:9: compiler.err.cant.ref.before.ctor.called: hashCode()
+ValueClassSuperInitFails.java:72:9: compiler.err.cant.ref.before.ctor.called: this
+ValueClassSuperInitFails.java:77:9: compiler.err.cant.ref.before.ctor.called: super
+ValueClassSuperInitFails.java:82:33: compiler.err.cant.ref.before.ctor.called: this
+ValueClassSuperInitFails.java:87:33: compiler.err.cant.ref.before.ctor.called: super
+ValueClassSuperInitFails.java:109:33: compiler.err.cant.ref.before.ctor.called: this
+ValueClassSuperInitFails.java:114:14: compiler.err.cant.ref.before.ctor.called: this
+ValueClassSuperInitFails.java:118:20: compiler.err.not.encl.class: java.lang.Object
+ValueClassSuperInitFails.java:122:17: compiler.err.cant.ref.before.ctor.called: super
+ValueClassSuperInitFails.java:129:22: compiler.err.call.must.only.appear.in.ctor
+ValueClassSuperInitFails.java:135:9: compiler.err.cant.ref.before.ctor.called: this
+ValueClassSuperInitFails.java:143:9: compiler.err.non.canonical.constructor.invoke.another.constructor: ValueClassSuperInitFails.Record1
+ValueClassSuperInitFails.java:148:9: compiler.err.non.canonical.constructor.invoke.another.constructor: ValueClassSuperInitFails.Record2
+ValueClassSuperInitFails.java:164:9: compiler.err.cant.ref.before.ctor.called: this
+ValueClassSuperInitFails.java:175:31: compiler.err.cant.ref.before.ctor.called: x
+ValueClassSuperInitFails.java:179:15: compiler.err.cant.ref.before.ctor.called: this
+ValueClassSuperInitFails.java:43:13: compiler.err.call.must.only.appear.in.ctor
+ValueClassSuperInitFails.java:47:14: compiler.err.call.must.only.appear.in.ctor
+ValueClassSuperInitFails.java:51:14: compiler.err.call.must.only.appear.in.ctor
+ValueClassSuperInitFails.java:55:13: compiler.err.call.must.only.appear.in.ctor
+ValueClassSuperInitFails.java:59:33: compiler.err.call.must.only.appear.in.ctor
+ValueClassSuperInitFails.java:63:32: compiler.err.call.must.only.appear.in.ctor
+ValueClassSuperInitFails.java:93:18: compiler.err.ctor.calls.not.allowed.here
+ValueClassSuperInitFails.java:99:13: compiler.err.return.before.superclass.initialized
+ValueClassSuperInitFails.java:170:18: compiler.err.ctor.calls.not.allowed.here
+- compiler.note.preview.filename: ValueClassSuperInitFails.java, DEFAULT
+- compiler.note.preview.recompile
+25 errors
diff a/test/langtools/tools/javac/SuperInit/ValueClassSuperInitGood.java b/test/langtools/tools/javac/SuperInit/ValueClassSuperInitGood.java
--- /dev/null
+++ b/test/langtools/tools/javac/SuperInit/ValueClassSuperInitGood.java
@@ -0,0 +1,497 @@
+/*
+ * 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
+ * @bug 8324873
+ * @summary Test valid placements of super()/this() in constructors
+ * @enablePreview
+ * @ignore fails at execution time because of Optional
+ */
+
+public value class ValueClassSuperInitGood {
+
+    ValueClassSuperInitGood(Object obj) {
+    }
+
+    ValueClassSuperInitGood(int x) {
+    }
+
+    // Default constructor provided by compiler
+    static value class Test0 {
+    }
+
+    // No explicit calls to this()/super()
+    static abstract value class Test1 {
+        Test1() {
+        }
+        Test1(int a) {
+            super();
+            this.hashCode();
+        }
+    }
+
+    // Explicit calls to this()/super()
+    static abstract value class Test2<T> {
+        static int i;
+        Test2() {
+            this(0);
+        }
+        Test2(int i) {
+            Test2.i = i;
+            super();
+        }
+        Test2(T obj) {
+            this(java.util.Objects.hashCode(obj));
+        }
+        public T get() {
+            return null;
+        }
+    }
+
+    // Explicit this()/super() with stuff in front
+    static value class Test3 {
+        int x;
+        final int y;
+        final int z;
+
+        Test3() {
+            new Object().hashCode();
+            new Object().hashCode();
+            super();
+            this.x = new Object().hashCode();
+            this.y = new Object().hashCode() % 17;
+            this.z = this.x + this.y;
+        }
+    }
+
+    static abstract value class Test5Abstract {
+        Test5Abstract(Object obj) {}
+    }
+
+    // Reference within constructor to outer class that's also my superclass
+    abstract value class Test5 extends Test5Abstract {
+        Test5(Object obj) {
+            if (obj == null)
+                throw new IllegalArgumentException();
+            super(ValueClassSuperInitGood.this);      // NOT a 'this' reference
+        }
+    }
+
+    // Initialization blocks
+    value class Test6 {
+        final long startTime;
+        final int x;
+        {
+            this.x = 12;
+        }
+        Test6() {
+            long now = System.nanoTime();
+            long then = now + 1000000L;
+            while (System.nanoTime() < then) {
+                try {
+                    Thread.sleep(1);
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                    break;
+                }
+            }
+            super();
+            this.startTime = now;
+        }
+    }
+
+    // Mix up inner classes, proxies, and super() calls
+    // Copied mostly from UnverifiableInitForNestedLocalClassTest.java
+    public static void test7(final String arg) {
+        final String inlined = " inlined ";
+        abstract value class LocalClass {
+            String m() {
+                return "LocalClass " + arg + inlined;
+            }
+
+            abstract value class SubClass extends LocalClass {
+                @Override
+                String m() {
+                    return "SubClass " + arg + inlined;
+                }
+            }
+
+            value class SubSubClass extends SubClass {
+                @Override
+                String m() {
+                    return "SubSubClass " + arg + inlined;
+                }
+            }
+
+            value class AnotherLocal {
+                value class AnotherSub extends LocalClass {
+                    AnotherSub() {
+                    }
+                    AnotherSub(int x) {
+                        this((char)x);
+                    }
+                    AnotherSub(char y) {
+                        super();
+                    }
+                    @Override
+                    String m() {
+                        return "AnotherSub " + arg + inlined;
+                    }
+                }
+            }
+        }
+    }
+
+    // Anonymous inner class
+    public static void test8() {
+        new Test2<Byte>(null) {
+            @Override
+            public Byte get() {
+                return (byte)-1;
+            }
+        };
+    }
+
+    // Qualified super() invocation
+    public static value class Test9 extends Test5 {
+
+        public Test9(ValueClassSuperInitGood implicit, Object obj) {
+            obj.hashCode();
+            implicit.super(obj);
+        }
+    }
+
+    // Copied from WhichImplicitThis6
+    public static abstract value class Test10 {
+        private int i;
+        public Test10(int i) { this.i = i; }
+        public value class Sub extends Test10 {
+            public Sub() {
+                super(i); // i is not inherited, so it is the enclosing i
+            }
+        }
+    }
+
+    // Two constructors where only one invokes super()
+    public static value class Test11 {
+        public Test11() {
+        }
+        public Test11(int x) {
+            super();
+        }
+    }
+
+    // Nested version of the previous test
+    public static value class Test12 {
+        Test12() {
+            class Sub {
+                public Sub() {
+                }
+                public Sub(int j) {
+                    super();
+                }
+            }
+        }
+    }
+
+    // Nested super()'s requiring initialization code appended
+    public static value class Test13 extends Test5Abstract {
+        final int x = new Object().hashCode();
+        Test13() {
+            super(new Object() {
+                public void foo() {
+                    class Bar {
+                        final int y = new Object().hashCode();
+                        Bar() {
+                            super();
+                        }
+                        Bar(int ignored) {
+                        }
+                    }
+                }
+            });
+        }
+    }
+
+    // Initializer in initializer block
+    public static value class Test14 {
+        final int x;                // initialized in constructor
+        final int y;                // initialized in initialization block
+        final int z = 13;           // initialized with intializer value
+        public Test14() {
+            this(0);
+        }
+        public Test14(boolean z) {
+            this.x = z ? 1 : 0;
+        }
+        public Test14(int x) {
+            super();
+            this.x = x;
+        }
+        {
+            this.y = -1;
+        }
+    }
+
+    // Qualified super() invocation with superclass instance
+    public static abstract value class Test15 {
+
+        final String name;
+
+        public Test15(String name) {
+            this.name = name;
+        }
+
+        public abstract value class Test15b extends Test15 {
+
+            public Test15b(String name) {
+                super(name);
+            }
+
+            public String getName() {
+                return Test15.this.name;
+            }
+        }
+    }
+
+    public static value class Test15c extends Test15.Test15b {
+        public Test15c(Test15 a, String name) {
+            a.super(name);
+        }
+    }
+
+    // Mixing up outer instances, proxies, and initializers
+    public static value class Test16 {
+
+        final String x = String.valueOf(new Object().hashCode());
+
+        public void run() {
+
+            final String y = String.valueOf(new Object().hashCode());
+
+            class Sub {
+
+                final String z;
+
+                Sub(String z, int ignored) {
+                    this(z, (float)ignored);
+                }
+
+                Sub(String z, float ignored) {
+                    this.z = z;
+                }
+
+                Sub(String z, byte ignored) {
+                    super();
+                    this.z = z;
+                }
+
+                Sub(String z, char ignored) {
+                    this(z, (int)ignored);
+                }
+
+                String x() {
+                    return x;
+                }
+
+                String y() {
+                    return y;
+                }
+
+                String z() {
+                    return z;
+                }
+            }
+
+            final String z = String.valueOf(new Object().hashCode());
+
+            final Sub[] subs = new Sub[] {
+                new Sub(z, 1),
+                new Sub(z, -1),
+                new Sub(z, (float)0),
+                new Sub(z, (byte)0),
+                new Sub(z, (char)0)
+            };
+
+            for (int i = 0; i < subs.length; i++) {
+                //System.err.println("i = " + i);
+                final Sub sub = subs[i];
+                final String subx = sub.x();
+                final String suby = sub.y();
+                final String subz = sub.z();
+                if (!x.equals(subx))
+                    throw new RuntimeException("x=" + x + " but sub[" + i + "].x()=" + subx);
+                if (!y.equals(suby))
+                    throw new RuntimeException("y=" + y + " but sub[" + i + "].y()=" + suby);
+                if (!z.equals(subz))
+                    throw new RuntimeException("z=" + z + " but sub[" + i + "].z()=" + subz);
+            }
+        }
+    }
+
+    // Records
+    public value class Test17 {
+
+        record Rectangle(float length, float width) { }
+
+        record StringHolder(String string) {
+            StringHolder {
+                java.util.Objects.requireNonNull(string);
+            }
+        }
+
+        record ValueHolder(int value) {
+            ValueHolder(float x) {
+                if (Float.isNaN(x))
+                    throw new IllegalArgumentException();
+                this((int)x);
+            }
+        }
+    }
+
+    static abstract value class AR<V> implements java.io.Serializable {
+        public AR(V initialValue) {
+        }
+
+        public AR() {
+        }
+
+        public final V get() {
+            return null;
+        }
+    }
+
+    // Exceptions thrown by initializer block
+    public static value class Test18 extends AR<Object> {
+
+        {
+            if ((this.get().hashCode() % 3) == 0)
+                throw new MyException();
+        }
+
+        public Test18(Object obj) throws MyException {
+            super(obj);
+        }
+
+        public Test18(boolean fail) throws MyException {
+            Object obj;
+            for (obj = new Object(); true; obj = new Object()) {
+                if (((obj.hashCode() % 3) == 0) != fail)
+                    continue;
+                break;
+            }
+            this(obj);
+        }
+
+        public static class MyException extends Exception {
+        }
+    }
+
+    // super()/this() within outer try block but inside inner class
+    public static value class Test19 {
+        public Test19(int x) {
+            try {
+                new Test1(x) {
+                    @Override
+                    public int hashCode() {
+                        return x ^ super.hashCode();
+                    }
+                };
+            } catch (StackOverflowError e) {
+                // ignore
+            }
+        }
+    }
+
+    // local class declared before super(), but not used until after super()
+    public static value class Test20 {
+        public Test20() {
+            class Foo {
+                Foo() {
+                    Test20.this.hashCode();
+                }
+            }
+            super();
+            new Foo();
+        }
+    }
+
+    // local class inside super() parameter list
+    public static value class Test21 extends AR<Object> {
+        private int x = 1;
+        public Test21() {
+            super(switch ("foo".hashCode()) {
+                default -> {
+                    class Nested {{ System.out.println(x); }}       // class is NOT instantiated - OK
+                    yield "bar";
+                }
+            });
+        }
+    }
+
+    public static void main(String[] args) {
+        new Test0();
+        new Test1() {};
+        new Test1(7) {};
+        new Test2<Byte>() {};
+        new Test2<>(args) {};
+        new Test3();
+        new ValueClassSuperInitGood(3).new Test5(3) {};
+        new ValueClassSuperInitGood(3).new Test6();
+        ValueClassSuperInitGood.test7("foo");
+        ValueClassSuperInitGood.test8();
+        new Test9(new ValueClassSuperInitGood(5), "abc");
+        new Test10(7) {};
+        new Test11(9);
+        new Test12();
+        new Test13();
+        Test14 t14 = new Test14();
+        assert t14.x == 0 && t14.y == -1 && t14.z == 13;
+        t14 = new Test14(7);
+        assert t14.x == 7 && t14.y == -1 && t14.z == 13;
+        new Test15c(new Test15("foo"){}, "bar");
+        new Test16().run();
+        new Test17.StringHolder("foo");
+        try {
+            new Test17.StringHolder(null);
+            throw new Error();
+        } catch (NullPointerException e) {
+            // expected
+        }
+        try {
+            new Test18(true);
+            assert false : "expected exception";
+        } catch (Test18.MyException e) {
+            // expected
+        }
+        try {
+            new Test18(false);
+        } catch (Test18.MyException e) {
+            assert false : "unexpected exception: " + e;
+        }
+        new Test19(123);
+        new Test20();
+        new Test21();
+    }
+}
diff a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/BridgeShouldHaveNoInteriorAnnotationsTest.java b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/BridgeShouldHaveNoInteriorAnnotationsTest.java
--- a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/BridgeShouldHaveNoInteriorAnnotationsTest.java
+++ b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/BridgeShouldHaveNoInteriorAnnotationsTest.java
@@ -69,11 +69,11 @@
     @Target(ElementType.TYPE_USE)
     public @interface NonNull {
     };
 
 
-    // Expected output can't be directly encoded into NestedLambdasCastedTest !!!
+    // Expected output can't be directly encoded into BridgeShouldHaveNoInteriorAnnotationsTest !!!
     static class OutputExpectedOnceHolder {
         public String[] outputs = {
             "0: #120(): CAST, offset=1, type_index=0, location=[TYPE_ARGUMENT(0)]",
             "1: #120(): LOCAL_VARIABLE, {start_pc=5, length=2, index=1}, location=[TYPE_ARGUMENT(0)]",
         };
diff a/test/langtools/tools/javac/annotations/typeAnnotations/failures/common/arrays/DeclarationAnnotation.out b/test/langtools/tools/javac/annotations/typeAnnotations/failures/common/arrays/DeclarationAnnotation.out
--- a/test/langtools/tools/javac/annotations/typeAnnotations/failures/common/arrays/DeclarationAnnotation.out
+++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/common/arrays/DeclarationAnnotation.out
@@ -1,5 +1,5 @@
-DeclarationAnnotation.java:13:21: compiler.err.annotation.type.not.applicable.to.type: DA
 DeclarationAnnotation.java:10:21: compiler.err.annotation.type.not.applicable.to.type: DA
 DeclarationAnnotation.java:11:21: compiler.err.annotation.type.not.applicable.to.type: DA
 DeclarationAnnotation.java:12:21: compiler.err.annotation.type.not.applicable.to.type: DA
 4 errors
+DeclarationAnnotation.java:13:21: compiler.err.annotation.type.not.applicable.to.type: DA
 4 errors
diff a/test/langtools/tools/javac/annotations/typeAnnotations/referenceinfos/NewObjects.java b/test/langtools/tools/javac/annotations/typeAnnotations/referenceinfos/NewObjects.java
--- a/test/langtools/tools/javac/annotations/typeAnnotations/referenceinfos/NewObjects.java
+++ b/test/langtools/tools/javac/annotations/typeAnnotations/referenceinfos/NewObjects.java
@@ -60,18 +60,18 @@
         return "void initObjectGeneric() { Object a = new @TA HashMap<@TB String, @TC String>(); }";
     }
 
     @TADescription(annotation = "TA", type = NEW, offset = ReferenceInfoUtil.IGNORE_VALUE)
     public String eqtestObject() {
-        return "void eqtestObject() { if (null == new @TA String()); }";
+        return "void eqtestObject(String s) { if (s == new @TA String()); }";
     }
 
     @TADescription(annotation = "TA", type = NEW, offset = ReferenceInfoUtil.IGNORE_VALUE)
     @TADescription(annotation = "TB", type = NEW,
             genericLocation = { 3, 0 }, offset = ReferenceInfoUtil.IGNORE_VALUE)
     public String eqtestObjectGeneric() {
-        return "void eqtestObjectGeneric() { if (null == new @TA ArrayList<@TB String >()); }";
+        return "void eqtestObjectGeneric(ArrayList<String> as) { if (as == new @TA ArrayList<@TB String >()); }";
     }
 
     @TADescription(annotation = "TA", type = NEW, offset = ReferenceInfoUtil.IGNORE_VALUE,
             genericLocation = {0, 0})
     @TADescription(annotation = "TB", type = NEW, offset = ReferenceInfoUtil.IGNORE_VALUE)
@@ -164,18 +164,18 @@
         return "void initObjectGeneric() { Object a = new @RTA @RTA HashMap<@RTB @RTB String, @RTC @RTC String>(); }";
     }
 
     @TADescription(annotation = "RTAs", type = NEW, offset = ReferenceInfoUtil.IGNORE_VALUE)
     public String eqtestObjectRepeatableAnnotation() {
-        return "void eqtestObject() { if (null == new @RTA @RTA String()); }";
+        return "void eqtestObject(String s) { if (s == new @RTA @RTA String()); }";
     }
 
     @TADescription(annotation = "RTAs", type = NEW, offset = ReferenceInfoUtil.IGNORE_VALUE)
     @TADescription(annotation = "RTBs", type = NEW,
             genericLocation = { 3, 0 }, offset = ReferenceInfoUtil.IGNORE_VALUE)
     public String eqtestObjectGenericRepeatableAnnotation() {
-        return "void eqtestObjectGeneric() { if (null == new @RTA @RTA ArrayList<@RTB @RTB String >()); }";
+        return "void eqtestObjectGeneric(ArrayList<String> as) { if (as == new @RTA @RTA ArrayList<@RTB @RTB String >()); }";
     }
 
     @TADescription(annotation = "RTAs", type = NEW, offset = ReferenceInfoUtil.IGNORE_VALUE,
             genericLocation = {0, 0})
     @TADescription(annotation = "RTBs", type = NEW, offset = ReferenceInfoUtil.IGNORE_VALUE)
diff a/test/langtools/tools/javac/api/TestApisWithProjections.java b/test/langtools/tools/javac/api/TestApisWithProjections.java
--- /dev/null
+++ b/test/langtools/tools/javac/api/TestApisWithProjections.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2021, 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 test "covers"/verifies com.sun.tools.javac.model.JavacTypes#asMemberOf's calls
+   to asSuper work*properly with primitive types.
+*/
+
+/**
+ * @test
+ * @bug 8244712
+ * @summary Test API usage with reference projection types.
+ * @ignore
+ * @library ./lib
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.code
+ * @build ToolTester
+ */
+
+import java.io.*;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+import javax.tools.*;
+
+import com.sun.tools.javac.api.JavacTaskImpl;
+
+public class TestApisWithProjections extends ToolTester {
+    public static void main(String... args) throws Exception {
+        try (TestApisWithProjections t = new TestApisWithProjections()) {
+            t.run();
+        }
+    }
+
+    void run() throws Exception {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        File file = new File(test_src, "TestApisWithProjections.java");
+        final Iterable<? extends JavaFileObject> compilationUnits =
+            fm.getJavaFileObjects(new File[] {file});
+        task = (JavacTaskImpl)tool.getTask(pw, fm, null, null, null, compilationUnits);
+        elements = task.getElements();
+        types = task.getTypes();
+
+        Iterable<? extends TypeElement> toplevels;
+        toplevels = ElementFilter.typesIn(task.enter(task.parse()));
+
+        for (TypeElement clazz : toplevels) {
+            System.out.format("Testing %s:%n%n", clazz.getSimpleName());
+            testParseType(clazz);
+        }
+
+        pw.close();
+
+        String out = sw.toString();
+        System.out.println(out);
+
+        if (out.contains("com.sun.tools.javac.util"))
+            throw new Exception("Unexpected output from compiler");
+    }
+
+    void testParseType(TypeElement clazz) {
+        DeclaredType type = (DeclaredType)task.parseType("PrimitiveClass<String>", clazz);
+        for (Element member : elements.getAllMembers((TypeElement)type.asElement())) {
+            TypeMirror mt = types.asMemberOf(type, member);
+            System.out.format("%s : %s -> %s%n", member.getSimpleName(), member.asType(), mt);
+        }
+    }
+
+    JavacTaskImpl task;
+    Elements elements;
+    Types types;
+}
+
+abstract class Base<T> {
+    void foo(T t) {}
+}
+
+primitive class PrimitiveClass<T> extends Base<T> {
+}
diff a/test/langtools/tools/javac/classfiles/InnerClasses/SyntheticClasses.java b/test/langtools/tools/javac/classfiles/InnerClasses/SyntheticClasses.java
--- a/test/langtools/tools/javac/classfiles/InnerClasses/SyntheticClasses.java
+++ b/test/langtools/tools/javac/classfiles/InnerClasses/SyntheticClasses.java
@@ -44,10 +44,15 @@
 
     private void run() throws IOException {
         File testClasses = new File(System.getProperty("test.classes"));
         for (File classFile : Objects.requireNonNull(testClasses.listFiles(f -> f.getName().endsWith(".class")))) {
             ClassModel cf = ClassFile.of().parse(classFile.toPath());
+            if ((cf.flags().flagsMask() & (ClassFile.ACC_SYNTHETIC | ClassFile.ACC_ABSTRACT)) == ClassFile.ACC_SYNTHETIC) {
+                if ((cf.flags().flagsMask() & ClassFile.ACC_IDENTITY) == 0) {
+                    throw new IllegalStateException("Missing ACC_IDENTITY on synthetic concrete identity class: " + cf.thisClass().asInternalName());
+                }
+            }
             if (cf.thisClass().asInternalName().matches(".*\\$[0-9]+")) {
                 EnclosingMethodAttribute encl = cf.findAttribute(Attributes.ENCLOSING_METHOD).orElse(null);
                 if (encl != null) {
                     if (encl.enclosingMethodName().isPresent())
                         throw new IllegalStateException("Invalid EnclosingMethod.method: " +
diff a/test/langtools/tools/javac/classfiles/attributes/innerclasses/InnerClassesInAnonymousClassTest.java b/test/langtools/tools/javac/classfiles/attributes/innerclasses/InnerClassesInAnonymousClassTest.java
--- a/test/langtools/tools/javac/classfiles/attributes/innerclasses/InnerClassesInAnonymousClassTest.java
+++ b/test/langtools/tools/javac/classfiles/attributes/innerclasses/InnerClassesInAnonymousClassTest.java
@@ -57,11 +57,11 @@
     }
 
     @Override
     public List<TestCase> generateTestCases() {
         currentClassType = ClassType.CLASS;
-        setPrefix("class Anonymous {} {new Anonymous() {");
+        setPrefix("class Anonymous { int f; } {new Anonymous() {"); // impose identity to make testing predictable.
         List<TestCase> sources = super.generateTestCases();
 
         currentClassType = ClassType.INTERFACE;
         setPrefix("interface Anonymous {} {new Anonymous() {");
         sources.addAll(super.generateTestCases());
@@ -76,8 +76,8 @@
 
     @Override
     public void getAdditionalFlags(Map<String, Set<String>> class2Flags, ClassType type, Modifier... flags) {
         super.getAdditionalFlags(class2Flags, type, flags);
         class2Flags.put("Anonymous", getFlags(currentClassType, Arrays.asList(flags)));
-        class2Flags.put("1", new HashSet<>() {});
+        class2Flags.put("1", new HashSet<>(Arrays.asList("ACC_IDENTITY")));
     }
 }
diff a/test/langtools/tools/javac/classfiles/attributes/innerclasses/InnerClassesInLocalClassTest.java b/test/langtools/tools/javac/classfiles/attributes/innerclasses/InnerClassesInLocalClassTest.java
--- a/test/langtools/tools/javac/classfiles/attributes/innerclasses/InnerClassesInLocalClassTest.java
+++ b/test/langtools/tools/javac/classfiles/attributes/innerclasses/InnerClassesInLocalClassTest.java
@@ -51,11 +51,11 @@
     public InnerClassesInLocalClassTest() {
         innerClassesData = new ArrayList<>();
         for (Modifier outerModifier : LOCAL_CLASS_MODIFIERS) {
             StringBuilder sb = new StringBuilder();
             sb.append(outerModifier.getString()).append(' ');
-            sb.append("class Local {");
+            sb.append("class Local { int f; "); // impose identity to make testing predictable.
             Map<String, Set<String>> class2Flags = new HashMap<>();
             for (int i = 0; i < LOCAL_CLASS_MODIFIERS.length; ++i) {
                 Modifier innerModifier = LOCAL_CLASS_MODIFIERS[i];
                 sb.append(innerModifier.getString()).append(' ')
                         .append("class").append(' ')
@@ -149,10 +149,11 @@
         return list;
     }
 
     private Set<String> getFlags(Modifier modifier) {
         HashSet<String> set = new HashSet<>();
+        set.add("ACC_IDENTITY");
         if (modifier != Modifier.EMPTY) {
             set.add("ACC_" + modifier.getString().toUpperCase());
         }
         return set;
     }
diff a/test/langtools/tools/javac/classfiles/attributes/innerclasses/InnerClassesTestBase.java b/test/langtools/tools/javac/classfiles/attributes/innerclasses/InnerClassesTestBase.java
--- a/test/langtools/tools/javac/classfiles/attributes/innerclasses/InnerClassesTestBase.java
+++ b/test/langtools/tools/javac/classfiles/attributes/innerclasses/InnerClassesTestBase.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2022, 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.
@@ -275,29 +275,33 @@
             StringBuilder sb = new StringBuilder();
             sb.append("public class InnerClassesSrc {")
                     .append(toString(outerMod)).append(' ')
                     .append(outerClassType).append(' ')
                     .append(prefix).append(' ').append('\n');
+            if (outerClassType == ClassType.CLASS && outerMod.contains(Modifier.ABSTRACT))
+                sb.append("int f;\n"); // impose identity to make testing predicatable
             int count = 0;
             Map<String, Set<String>> class2Flags = new HashMap<>();
             List<String> syntheticClasses = new ArrayList<>();
             for (List<Modifier> innerMod : innerMods) {
                 ++count;
                 String privateConstructor = "";
                 if (hasSyntheticClass && !innerMod.contains(Modifier.ABSTRACT)) {
                     privateConstructor = "private A" + count + "() {}";
                     syntheticClasses.add("new A" + count + "();");
                 }
+                String instField = innerClassType == ClassType.CLASS && innerMod.contains(Modifier.ABSTRACT) ?
+                                                "int f; " : ""; // impose identity to make testing predicatable
                 sb.append(toString(innerMod)).append(' ');
-                sb.append(String.format("%s A%d {%s}\n", innerClassType, count, privateConstructor));
+                sb.append(String.format("%s A%d { %s %s}\n", innerClassType, count, instField, privateConstructor));
                 Set<String> flags = getFlags(innerClassType, innerMod);
                 class2Flags.put("A" + count, flags);
             }
             if (hasSyntheticClass) {
                 // Source to generate synthetic classes
                 sb.append(syntheticClasses.stream().collect(Collectors.joining(" ", "{", "}")));
-                class2Flags.put("1", new HashSet<>(Arrays.asList("ACC_STATIC", "ACC_SYNTHETIC")));
+                class2Flags.put("1", new HashSet<>(Arrays.asList("ACC_STATIC", "ACC_IDENTITY", "ACC_SYNTHETIC")));
             }
             sb.append(suffix).append("\n}");
             getAdditionalFlags(class2Flags, outerClassType, outerMod.toArray(new Modifier[outerMod.size()]));
             list.add(new TestCase(sb.toString(), class2Flags));
         }
@@ -372,10 +376,11 @@
 
     public enum ClassType {
         CLASS("class") {
             @Override
             public void addSpecificFlags(Set<String> flags) {
+                flags.add("ACC_IDENTITY");
             }
         },
         INTERFACE("interface") {
             @Override
             public void addFlags(Set<String> flags) {
@@ -409,15 +414,17 @@
             @Override
             public void addSpecificFlags(Set<String> flags) {
                 flags.add("ACC_ENUM");
                 flags.add("ACC_FINAL");
                 flags.add("ACC_STATIC");
+                flags.add("ACC_IDENTITY");
             }
         },
         OTHER("") {
             @Override
             public void addSpecificFlags(Set<String> flags) {
+                flags.add("ACC_IDENTITY");
             }
         };
 
         private final String classType;
 
@@ -437,11 +444,12 @@
 
     public enum Modifier {
         PUBLIC("public"), PRIVATE("private"),
         PROTECTED("protected"), DEFAULT("default"),
         FINAL("final"), ABSTRACT("abstract"),
-        STATIC("static"), EMPTY("");
+        STATIC("static"), EMPTY(""),
+        IDENTITY("identity");
 
         private final String str;
 
         Modifier(String str) {
             this.str = str;
diff a/test/langtools/tools/javac/diags/CheckExamples.java b/test/langtools/tools/javac/diags/CheckExamples.java
--- a/test/langtools/tools/javac/diags/CheckExamples.java
+++ b/test/langtools/tools/javac/diags/CheckExamples.java
@@ -43,16 +43,26 @@
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.*;
 
 /**
  * Check invariants for a set of examples.
+ *
+ * READ THIS IF THIS TEST FAILS AFTER ADDING A NEW KEY TO 'compiler.properties':
+ * The 'examples' subdirectory contains a number of examples which provoke
+ * the reporting of most of the compiler message keys.
+ *
  * -- each example should exactly declare the keys that will be generated when
  *      it is run.
+ * -- this is done by the "// key:"-comment in each fine.
  * -- together, the examples should cover the set of resource keys in the
  *      compiler.properties bundle. A list of exceptions may be given in the
  *      not-yet.txt file. Entries on the not-yet.txt list should not be
  *      covered by examples.
+ * -- some keys are only reported by the compiler when specific options are
+ *      supplied. For the purposes of this test, this can be specified by a
+ *      comment e.g. like this: "// options: -Xlint:empty"
+ *
  * When new keys are added to the resource bundle, it is strongly recommended
  * that corresponding new examples be added here, if at all practical, instead
  * of simply and lazily being added to the not-yet.txt list.
  */
 public class CheckExamples {
diff a/test/langtools/tools/javac/diags/CheckResourceKeys.java b/test/langtools/tools/javac/diags/CheckResourceKeys.java
--- a/test/langtools/tools/javac/diags/CheckResourceKeys.java
+++ b/test/langtools/tools/javac/diags/CheckResourceKeys.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2019, 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.
diff a/test/langtools/tools/javac/diags/examples.not-yet.txt b/test/langtools/tools/javac/diags/examples.not-yet.txt
--- a/test/langtools/tools/javac/diags/examples.not-yet.txt
+++ b/test/langtools/tools/javac/diags/examples.not-yet.txt
@@ -55,10 +55,11 @@
 compiler.misc.bad.enclosing.class                       # bad class file
 compiler.misc.bad.enclosing.method                      # bad class file
 compiler.misc.bad.runtime.invisible.param.annotations   # bad class file
 compiler.misc.bad.signature                             # bad class file
 compiler.misc.bad.requires.flag                         # bad class file
+compiler.misc.bad.access.flags                          # bad class file
 compiler.misc.bad.utf8.byte.sequence.at                 # bad class file
 compiler.misc.bad.type.annotation.value
 compiler.misc.class.file.not.found                      # ClassReader
 compiler.misc.class.file.wrong.class
 compiler.misc.exception.message                         # uncommon completion failure based on a string
@@ -208,12 +209,17 @@
 compiler.warn.profile.target.conflict
 compiler.err.source.target.conflict
 compiler.err.target.default.source.conflict
 compiler.err.preview.not.latest
 compiler.err.preview.without.source.or.release
+
 compiler.misc.illegal.signature                               # the compiler can now detect more non-denotable types before class writing
 
 # this one needs a forged class file to be reproduced
 compiler.err.annotation.unrecognized.attribute.name
 
 # this one is transitional (waiting for FFM API to exit preview)
 compiler.warn.restricted.method
+# Value Objects
+compiler.misc.feature.value.classes
+compiler.misc.feature.super.init                            # temporary as we are using statements before super in value classes
+compiler.warn.preview.feature.use                           # temporary as we are using statements before super in value classes
diff a/test/langtools/tools/javac/diags/examples/ConcreteSuperclassOfValueClass.java b/test/langtools/tools/javac/diags/examples/ConcreteSuperclassOfValueClass.java
--- /dev/null
+++ b/test/langtools/tools/javac/diags/examples/ConcreteSuperclassOfValueClass.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2022, 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.
+ */
+
+// key: compiler.err.concrete.supertype.for.value.class
+// key: compiler.note.preview.filename
+// key: compiler.note.preview.recompile
+// options: --enable-preview -source ${jdk.version}
+
+public class ConcreteSuperclassOfValueClass {
+    static abstract value class V extends ConcreteSuperclassOfValueClass {}
+}
diff a/test/langtools/tools/javac/diags/examples/FeatureStatementsBeforeSuper.java b/test/langtools/tools/javac/diags/examples/FeatureStatementsBeforeSuper.java
--- a/test/langtools/tools/javac/diags/examples/FeatureStatementsBeforeSuper.java
+++ b/test/langtools/tools/javac/diags/examples/FeatureStatementsBeforeSuper.java
@@ -19,12 +19,10 @@
  * 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.
  */
 
- // key: compiler.misc.feature.super.init
- // key: compiler.warn.preview.feature.use
  // options: --enable-preview -source ${jdk.version} -Xlint:preview
 
 class FeatureStatementsBeforeSuper {
     FeatureStatementsBeforeSuper() {
         System.out.println();
diff a/test/langtools/tools/javac/diags/examples/FieldAssigmentAfterSuper.java b/test/langtools/tools/javac/diags/examples/FieldAssigmentAfterSuper.java
--- /dev/null
+++ b/test/langtools/tools/javac/diags/examples/FieldAssigmentAfterSuper.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+// key: compiler.err.cant.ref.after.ctor.called
+// key: compiler.note.preview.filename
+// key: compiler.note.preview.recompile
+// options: --enable-preview -source ${jdk.version}
+
+value class V {
+    int i;
+    V(int i) {
+        super();
+        this.i = i;
+    }
+}
diff a/test/langtools/tools/javac/diags/examples/ReturnBeforeSuperclassInit.java b/test/langtools/tools/javac/diags/examples/ReturnBeforeSuperclassInit.java
--- a/test/langtools/tools/javac/diags/examples/ReturnBeforeSuperclassInit.java
+++ b/test/langtools/tools/javac/diags/examples/ReturnBeforeSuperclassInit.java
@@ -20,12 +20,10 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
 
 // key: compiler.err.return.before.superclass.initialized
-// key: compiler.note.preview.filename
-// key: compiler.note.preview.recompile
 // options: --enable-preview -source ${jdk.version}
 
 class ReturnBeforeSuperclassInit {
     ReturnBeforeSuperclassInit(boolean maybe) {
         if (maybe)
diff a/test/langtools/tools/javac/diags/examples/SuperClassMethodCannotBeSynchronized.java b/test/langtools/tools/javac/diags/examples/SuperClassMethodCannotBeSynchronized.java
--- /dev/null
+++ b/test/langtools/tools/javac/diags/examples/SuperClassMethodCannotBeSynchronized.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2022, 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.
+ */
+
+// key: compiler.err.super.class.method.cannot.be.synchronized
+// key: compiler.note.preview.filename
+// key: compiler.note.preview.recompile
+// options: --enable-preview -source ${jdk.version}
+
+public abstract class SuperClassMethodCannotBeSynchronized {
+    synchronized void foo() {}
+}
+
+value class V extends SuperClassMethodCannotBeSynchronized {}
diff a/test/langtools/tools/javac/diags/examples/ThisExposedPrematurely.java b/test/langtools/tools/javac/diags/examples/ThisExposedPrematurely.java
--- /dev/null
+++ b/test/langtools/tools/javac/diags/examples/ThisExposedPrematurely.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2022, 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.
+ */
+
+// key: compiler.err.cant.ref.before.ctor.called
+// key: compiler.note.preview.filename
+// key: compiler.note.preview.recompile
+// options: --enable-preview -source ${jdk.version}
+
+value class ThisExposedPrematurely {
+    int x;
+    ThisExposedPrematurely() {
+        foo(this); // Error.
+        x = 10;
+    }
+    void foo(ThisExposedPrematurely v) {}
+}
diff a/test/langtools/tools/javac/diags/examples/TypeReqIdentity.java b/test/langtools/tools/javac/diags/examples/TypeReqIdentity.java
--- /dev/null
+++ b/test/langtools/tools/javac/diags/examples/TypeReqIdentity.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2021, 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.
+ */
+
+// key: compiler.misc.type.req.identity
+// key: compiler.err.type.found.req
+// key: compiler.note.preview.filename
+// key: compiler.note.preview.recompile
+// options: --enable-preview -source ${jdk.version}
+
+value class TypeReqIdentity {
+    { synchronized (this) {} }
+}
diff a/test/langtools/tools/javac/diags/examples/ValueWithIdentitySuper.java b/test/langtools/tools/javac/diags/examples/ValueWithIdentitySuper.java
--- /dev/null
+++ b/test/langtools/tools/javac/diags/examples/ValueWithIdentitySuper.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.
+ *
+ * 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.
+ */
+
+// key: compiler.err.value.type.has.identity.super.type
+// key: compiler.note.preview.filename
+// key: compiler.note.preview.recompile
+// options: --enable-preview -source ${jdk.version}
+
+abstract class AC {}
+value class V extends AC {}
diff a/test/langtools/tools/javac/failover/CheckAttributedTree.java b/test/langtools/tools/javac/failover/CheckAttributedTree.java
--- a/test/langtools/tools/javac/failover/CheckAttributedTree.java
+++ b/test/langtools/tools/javac/failover/CheckAttributedTree.java
@@ -404,11 +404,11 @@
                         that.hasTag(VARDEF) ||
                         that.hasTag(METHODDEF) ||
                         that.hasTag(CLASSDEF);
             }
 
-            private final List<String> excludedFields = Arrays.asList("varargsElement", "targetType");
+            private final List<String> excludedFields = Arrays.asList("varargsElement", "targetType", "factoryProduct");
 
             void check(boolean ok, String label, Info self) {
                 if (!ok) {
                     if (gui) {
                         if (viewer == null)
diff a/test/langtools/tools/javac/flags/FlagsTest.java b/test/langtools/tools/javac/flags/FlagsTest.java
--- a/test/langtools/tools/javac/flags/FlagsTest.java
+++ b/test/langtools/tools/javac/flags/FlagsTest.java
@@ -21,11 +21,11 @@
  * questions.
  */
 
 /**
  * @test
- * @bug 8211138
+ * @bug 8211138 8215246
  * @summary Missing Flag enum constants
  * @library /tools/javac/lib
  * @modules jdk.compiler/com.sun.tools.javac.code
  * @run main FlagsTest
  */
diff a/test/langtools/tools/javac/launcher/SourceLauncherTest.java b/test/langtools/tools/javac/launcher/SourceLauncherTest.java
--- a/test/langtools/tools/javac/launcher/SourceLauncherTest.java
+++ b/test/langtools/tools/javac/launcher/SourceLauncherTest.java
@@ -32,10 +32,11 @@
  *          jdk.compiler/com.sun.tools.javac.main
  *          java.base/jdk.internal.classfile.impl
  *          java.base/jdk.internal.module
  * @build toolbox.JavaTask toolbox.JavacTask toolbox.TestRunner toolbox.ToolBox
  * @run main SourceLauncherTest
+ * @ignore Verifier error
  */
 
 import java.lang.classfile.*;
 import java.lang.classfile.attribute.ModuleResolutionAttribute;
 import java.io.ByteArrayOutputStream;
diff a/test/langtools/tools/javac/patterns/NullsInDeconstructionPatterns2.java b/test/langtools/tools/javac/patterns/NullsInDeconstructionPatterns2.java
--- a/test/langtools/tools/javac/patterns/NullsInDeconstructionPatterns2.java
+++ b/test/langtools/tools/javac/patterns/NullsInDeconstructionPatterns2.java
@@ -26,10 +26,11 @@
  * @bug 8302202
  * @summary Testing record patterns with null components
  * @enablePreview
  * @compile NullsInDeconstructionPatterns2.java
  * @run main NullsInDeconstructionPatterns2
+ * @ignore Verifier error
  */
 
 import java.util.Objects;
 import java.util.function.Function;
 
diff a/test/langtools/tools/javac/patterns/PrettyTest.java b/test/langtools/tools/javac/patterns/PrettyTest.java
--- a/test/langtools/tools/javac/patterns/PrettyTest.java
+++ b/test/langtools/tools/javac/patterns/PrettyTest.java
@@ -24,10 +24,11 @@
 /*
  * @test
  * @enablePreview
  * @summary Test behavior of Pretty
  * @modules jdk.compiler
+ * @ignore Verifier error
  */
 
 import java.io.IOException;
 import java.io.StringWriter;
 import java.net.URI;
diff a/test/langtools/tools/javac/patterns/T8314226.java b/test/langtools/tools/javac/patterns/T8314226.java
--- a/test/langtools/tools/javac/patterns/T8314226.java
+++ b/test/langtools/tools/javac/patterns/T8314226.java
@@ -25,10 +25,11 @@
  * @bug 8314226
  * @summary Series of colon-style fallthrough switch cases with guards compiled incorrectly
  * @enablePreview
  * @compile T8314226.java
  * @run main T8314226
+ * @ignore Verifier error
  */
 
 public class T8314226 {
     int multipleGuardedCases(Object obj) {
         switch (obj) {
diff a/test/langtools/tools/javac/patterns/Unnamed.java b/test/langtools/tools/javac/patterns/Unnamed.java
--- a/test/langtools/tools/javac/patterns/Unnamed.java
+++ b/test/langtools/tools/javac/patterns/Unnamed.java
@@ -25,10 +25,11 @@
  * @test
  * @bug 8304246
  * @summary Compiler Implementation for Unnamed patterns and variables
  * @compile Unnamed.java
  * @run main Unnamed
+ * @ignore Verifier error
  */
 
 import java.util.Objects;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
diff a/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java b/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java
--- a/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java
+++ b/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java
@@ -842,32 +842,32 @@
                    class name api/Ann
                    header extends java/lang/Object implements java/lang/annotation/Annotation flags 2601
 
                    class name api/Api
                    header extends nonapi/Impl$Nested$Exp flags 21
-                   innerclass innerClass nonapi/Impl$Nested outerClass nonapi/Impl innerClassName Nested flags 9
-                   innerclass innerClass nonapi/Impl$Nested$Exp outerClass nonapi/Impl$Nested innerClassName Exp flags 9
+                   innerclass innerClass nonapi/Impl$Nested outerClass nonapi/Impl innerClassName Nested flags 29
+                   innerclass innerClass nonapi/Impl$Nested$Exp outerClass nonapi/Impl$Nested innerClassName Exp flags 29
                    method name <init> descriptor ()V flags 1
 
                    class name nonapi/Impl
                    header extends java/lang/Object nestMembers nonapi/Impl$Nested,nonapi/Impl$Nested$Exp flags 21
-                   innerclass innerClass nonapi/Impl$Nested outerClass nonapi/Impl innerClassName Nested flags 9
-                   innerclass innerClass nonapi/Impl$Nested$Exp outerClass nonapi/Impl$Nested innerClassName Exp flags 9
+                   innerclass innerClass nonapi/Impl$Nested outerClass nonapi/Impl innerClassName Nested flags 29
+                   innerclass innerClass nonapi/Impl$Nested$Exp outerClass nonapi/Impl$Nested innerClassName Exp flags 29
                    field name C descriptor Ljava/lang/String; constantValue  flags 19
                    method name <init> descriptor ()V flags 1
                    method name test descriptor ()V flags 1
 
                    class name nonapi/Impl$Nested
                    header extends java/lang/Object nestHost nonapi/Impl flags 21 classAnnotations @Lapi/Ann;
-                   innerclass innerClass nonapi/Impl$Nested outerClass nonapi/Impl innerClassName Nested flags 9
-                   innerclass innerClass nonapi/Impl$Nested$Exp outerClass nonapi/Impl$Nested innerClassName Exp flags 9
+                   innerclass innerClass nonapi/Impl$Nested outerClass nonapi/Impl innerClassName Nested flags 29
+                   innerclass innerClass nonapi/Impl$Nested$Exp outerClass nonapi/Impl$Nested innerClassName Exp flags 29
                    method name <init> descriptor ()V flags 1
 
                    class name nonapi/Impl$Nested$Exp
                    header extends nonapi/Impl$Nested implements java/lang/Runnable nestHost nonapi/Impl flags 21
-                   innerclass innerClass nonapi/Impl$Nested outerClass nonapi/Impl innerClassName Nested flags 9
-                   innerclass innerClass nonapi/Impl$Nested$Exp outerClass nonapi/Impl$Nested innerClassName Exp flags 9
+                   innerclass innerClass nonapi/Impl$Nested outerClass nonapi/Impl innerClassName Nested flags 29
+                   innerclass innerClass nonapi/Impl$Nested$Exp outerClass nonapi/Impl$Nested innerClassName Exp flags 29
                    method name <init> descriptor ()V flags 1
                    method name run descriptor ()V flags 1
                    method name get descriptor ()Lnonapi/Impl$OtherNested; flags 1
 
                    """,
diff a/test/langtools/tools/javac/processing/model/TestSymtabItems.java b/test/langtools/tools/javac/processing/model/TestSymtabItems.java
--- a/test/langtools/tools/javac/processing/model/TestSymtabItems.java
+++ b/test/langtools/tools/javac/processing/model/TestSymtabItems.java
@@ -45,10 +45,12 @@
 import javax.lang.model.type.TypeMirror;
 import javax.lang.model.type.UnknownTypeException;
 import javax.lang.model.util.*;
 
 import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Symbol.Completer;
+import com.sun.tools.javac.code.Symbol.ModuleSymbol;
 import com.sun.tools.javac.code.Symtab;
 import com.sun.tools.javac.file.JavacFileManager;
 import com.sun.tools.javac.main.JavaCompiler;
 import com.sun.tools.javac.model.JavacTypes;
 import com.sun.tools.javac.util.Context;
diff a/test/langtools/tools/javac/processing/model/completionfailure/NoAbortForBadClassFile.java b/test/langtools/tools/javac/processing/model/completionfailure/NoAbortForBadClassFile.java
--- a/test/langtools/tools/javac/processing/model/completionfailure/NoAbortForBadClassFile.java
+++ b/test/langtools/tools/javac/processing/model/completionfailure/NoAbortForBadClassFile.java
@@ -211,11 +211,11 @@
             } catch (CompletionFailure ignore) {
             }
 
             long flags = sym.flags_field;
 
-            flags &= ~(Flags.CLASS_SEEN | Flags.SOURCE_SEEN);
+            flags &= ~(Flags.CLASS_SEEN | Flags.SOURCE_SEEN | Flags.IDENTITY_TYPE); // earlier ACC_SUPER was dropped by javac.
 
             result.add("sym: " + sym.flatname + ", " + sym.owner.flatName() +
                        ", " + sym.type + ", " + sym.members_field + ", " + flags);
         }
 
diff a/test/langtools/tools/javac/processing/model/element/TestValueClasses.java b/test/langtools/tools/javac/processing/model/element/TestValueClasses.java
--- /dev/null
+++ b/test/langtools/tools/javac/processing/model/element/TestValueClasses.java
@@ -0,0 +1,189 @@
+/*
+ * 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 8308590
+ * @summary Test basic modeling for value classes
+ * @library /tools/lib /tools/javac/lib
+ * @modules
+ *     jdk.compiler/com.sun.tools.javac.api
+ *     jdk.compiler/com.sun.tools.javac.main
+ * @build toolbox.ToolBox toolbox.JavacTask JavacTestingAbstractProcessor
+ * @run main TestValueClasses
+ */
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.*;
+
+import javax.annotation.processing.*;
+import javax.lang.model.*;
+import javax.lang.model.element.*;
+import javax.lang.model.type.*;
+import javax.lang.model.util.*;
+import java.time.*;
+
+import toolbox.JavacTask;
+import toolbox.Task;
+import toolbox.Task.Mode;
+import toolbox.Task.OutputKind;
+import toolbox.TestRunner;
+import toolbox.ToolBox;
+
+public class TestValueClasses extends TestRunner {
+
+    protected ToolBox tb;
+
+    TestValueClasses() {
+        super(System.err);
+        tb = new ToolBox();
+    }
+
+    public static void main(String... args) throws Exception {
+        new TestValueClasses().runTests();
+    }
+
+    /**
+     * Run all methods annotated with @Test, and throw an exception if any
+     * errors are reported..
+     *
+     * @throws Exception if any errors occurred
+     */
+    protected void runTests() throws Exception {
+        runTests(m -> new Object[] { Paths.get(m.getName()) });
+    }
+
+    Path[] findJavaFiles(Path... paths) throws IOException {
+        return tb.findJavaFiles(paths);
+    }
+
+    void checkOutputContains(String log, String... expect) throws Exception {
+        for (String e : expect) {
+            if (!log.contains(e)) {
+                throw new Exception("expected output not found: " + e);
+            }
+        }
+    }
+
+    @Test
+    public void testValueClassesProcessor(Path base) throws Exception {
+        Path src = base.resolve("src");
+        Path r = src.resolve("Test");
+
+        Path classes = base.resolve("classes");
+
+        Files.createDirectories(classes);
+
+        tb.writeJavaFiles(r,
+                """
+                interface Interface {}
+
+                value class ValueClass {}
+
+                class IdentityClass {}
+                """
+        );
+
+        List<String> expected = List.of(
+                "- compiler.note.proc.messager: visiting: Interface Modifiers: [abstract]",
+                "- compiler.note.proc.messager: visiting: ValueClass Modifiers: [value, final]",
+                "- compiler.note.proc.messager:     constructor modifiers: []",
+                "- compiler.note.proc.messager: visiting: IdentityClass Modifiers: []",
+                "- compiler.note.proc.messager:     constructor modifiers: []",
+                "- compiler.note.preview.filename: Interface.java, DEFAULT",
+                "- compiler.note.preview.recompile"
+        );
+
+        for (Mode mode : new Mode[] {Mode.API}) {
+            List<String> log = new JavacTask(tb, mode)
+                    .options("--enable-preview", "-source", String.valueOf(Runtime.version().feature()), "-processor", ValueClassesProcessor.class.getName(),
+                            "-XDrawDiagnostics")
+                    .files(findJavaFiles(src))
+                    .outdir(classes)
+                    .run()
+                    .writeAll()
+                    .getOutputLines(Task.OutputKind.DIRECT);
+
+            System.out.println("log:" +log);
+
+            if (!expected.equals(log)) {
+                if (expected.size() == log.size()) {
+                    for (int i = 0; i < expected.size(); i++) {
+                        if (!expected.get(i).equals(log.get(i))) {
+                            System.err.println("failing at line " + (i + 1));
+                            System.err.println("    expecting " + expected.get(i));
+                            System.err.println("    found " + log.get(i));
+                        }
+                    }
+                } else {
+                    System.err.println("expected and log lists differ in length");
+                }
+                throw new AssertionError("Unexpected output: " + log);
+            }
+        }
+    }
+
+    public static final class ValueClassesProcessor extends JavacTestingAbstractProcessor {
+
+        @Override
+        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+            if (!roundEnv.processingOver()) {
+                Messager messager = processingEnv.getMessager();
+                ElementScanner scanner = new ValueClassesScanner(messager);
+                for(Element rootElement : roundEnv.getRootElements()) {
+                    scanner.visit(rootElement);
+                }
+            }
+            return true;
+        }
+
+        class ValueClassesScanner extends ElementScanner<Void, Void> {
+
+            Messager messager;
+
+            public ValueClassesScanner(Messager messager) {
+                this.messager = messager;
+            }
+
+            @Override
+            public Void visitType(TypeElement element, Void p) {
+                messager.printNote("visiting: " + element.getSimpleName() + " Modifiers: " + element.getModifiers());
+                List<? extends Element> enclosedElements = element.getEnclosedElements();
+                for (Element elem : enclosedElements) {
+                    System.out.println("visiting " + elem.getSimpleName());
+                    switch (elem.getSimpleName().toString()) {
+                        case "<init>":
+                            messager.printNote("    constructor modifiers: " + elem.getModifiers());
+                            break;
+                        default:
+                            break;
+                    }
+                }
+                return super.visitType(element, p);
+            }
+        }
+    }
+}
diff a/test/langtools/tools/javac/records/RecordCompilationTests.java b/test/langtools/tools/javac/records/RecordCompilationTests.java
--- a/test/langtools/tools/javac/records/RecordCompilationTests.java
+++ b/test/langtools/tools/javac/records/RecordCompilationTests.java
@@ -96,11 +96,11 @@
 
 class RecordCompilationTests extends CompilationTestCase {
     private static String[] OPTIONS_WITH_AP = {"-processor", SimplestAP.class.getName()};
 
     private static final List<String> BAD_COMPONENT_NAMES = List.of(
-            "clone", "finalize", "getClass", "hashCode",
+            "clone", "finalize", "getClass", "hashCode", "isValueObject",
             "notify", "notifyAll", "toString", "wait");
 
     /* simplest annotation processor just to force a round of annotation processing for all tests
      */
     @SupportedAnnotationTypes("*")
diff a/test/langtools/tools/javac/records/RecordReading.java b/test/langtools/tools/javac/records/RecordReading.java
--- a/test/langtools/tools/javac/records/RecordReading.java
+++ b/test/langtools/tools/javac/records/RecordReading.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2021, 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,21 +23,22 @@
 
 /*
  * @test
  * @summary test the records can be read by javac properly
  * @library /tools/lib
+ * @bug 8273018
  * @modules jdk.compiler/com.sun.tools.javac.api
  *          jdk.compiler/com.sun.tools.javac.main
  * @build toolbox.ToolBox toolbox.JavacTask
  * @run main RecordReading
  */
 
-
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.List;
 import java.util.Objects;
 import toolbox.TestRunner;
 import toolbox.ToolBox;
 import toolbox.JavacTask;
 import toolbox.Task;
@@ -123,7 +124,6 @@
                 """;
         if (!Objects.equals(expected, output)) {
             throw new AssertionError("Unexpected output: " + output);
         }
     }
-
 }
diff a/test/langtools/tools/javac/unnamed/UnnamedLocalVariableTable.java b/test/langtools/tools/javac/unnamed/UnnamedLocalVariableTable.java
--- a/test/langtools/tools/javac/unnamed/UnnamedLocalVariableTable.java
+++ b/test/langtools/tools/javac/unnamed/UnnamedLocalVariableTable.java
@@ -26,10 +26,11 @@
  * @bug 8313323
  * @summary Verify javac does not produce incorrect LocalVariableTable
  * @enablePreview
  * @compile -g UnnamedLocalVariableTable.java
  * @run main UnnamedLocalVariableTable
+ * @ignore Verifier error
  */
 public class UnnamedLocalVariableTable {
     public static void main(String... args) {
         try {
             int _ = 0;
diff a/test/langtools/tools/javac/valhalla/value-objects/ArrayCreationWithQuestion.java b/test/langtools/tools/javac/valhalla/value-objects/ArrayCreationWithQuestion.java
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/ArrayCreationWithQuestion.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2019, 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 8222634
+ * @summary Check array creation with value classes
+ * @modules jdk.compiler/com.sun.tools.javac.util jdk.jdeps/com.sun.tools.javap
+ * @modules jdk.compiler
+ * @compile --enable-preview -source ${jdk.version} ArrayCreationWithQuestion.java
+ * @run main/othervm --enable-preview ArrayCreationWithQuestion
+ */
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.file.Paths;
+
+public class ArrayCreationWithQuestion {
+
+    static value class VT {
+        VT[] a1;
+        VT[] a2;
+
+        VT() {
+            a1 = new VT[42];
+            a2 = new VT[42];
+        }
+    }
+
+    public static void main(String[] args) {
+        new ArrayCreationWithQuestion().run();
+    }
+
+    void run() {
+        String [] params = new String [] { "-v",
+                Paths.get(System.getProperty("test.classes"),
+                        "ArrayCreationWithQuestion$VT.class").toString() };
+        runCheck(params, new String [] {
+                "         3: anewarray     #1                  // class ArrayCreationWithQuestion$VT",
+                "        12: anewarray     #1                  // class ArrayCreationWithQuestion$VT",
+        });
+
+    }
+
+    void runCheck(String [] params, String [] expectedOut) {
+        StringWriter s;
+        String out;
+
+        try (PrintWriter pw = new PrintWriter(s = new StringWriter())) {
+            com.sun.tools.javap.Main.run(params, pw);
+            out = s.toString();
+        }
+        int errors = 0;
+        for (String eo: expectedOut) {
+            if (!out.contains(eo)) {
+                System.err.println("Match not found for string: " + eo);
+                errors++;
+            }
+        }
+        if (errors > 0) {
+            throw new AssertionError("Unexpected javap output: " + out);
+        }
+    }
+}
diff a/test/langtools/tools/javac/valhalla/value-objects/AsSuperTests.java b/test/langtools/tools/javac/valhalla/value-objects/AsSuperTests.java
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/AsSuperTests.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2021, 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.
+ */
+
+/*
+ * @test
+ * @bug 8244712
+ * @summary Javac should switch to reference projection before walking type hierarchy.
+ * @enablePreview
+ * @compile AsSuperTests.java
+ */
+
+/* The following test "covers"/verifies that the asSuper calls in
+   com.sun.tools.javac.comp.Resolve#resolveSelf &&
+   com.sun.tools.javac.comp.Lower#visitSelect
+   com.sun.tools.javac.comp.Resolve#mostSpecific
+   com.sun.tools.javac.comp.Attr#visitSelect
+   com.sun.tools.javac.comp.Resolve.UnboundMethodReferenceLookupHelper#UnboundMethodReferenceLookupHelper
+   work correctly with value classes.
+*/
+
+interface I {
+    default void foo() {
+        System.out.println("I.foo");
+    }
+}
+
+abstract value class Base<T> {
+    static void goo() {}
+    void zoo() {}
+    interface SAM {
+       String m(Foo f);
+    }
+
+    static void test() {
+        SAM s = Base::getX;
+    }
+
+    String getX() { return null; }
+
+    static value class Foo<X> extends Base {}
+}
+
+value class X extends Base implements I {
+
+    static void goo() {}
+
+    public void foo() {
+        I.super.foo();
+        X.this.goo(); // covers the asSuper call in com.sun.tools.javac.comp.Resolve#mostSpecific
+        super.zoo();  // covers the asSuper call in com.sun.tools.javac.comp.Attr#visitSelect
+    }
+}
diff a/test/langtools/tools/javac/valhalla/value-objects/CanonicalCtorTest.java b/test/langtools/tools/javac/valhalla/value-objects/CanonicalCtorTest.java
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/CanonicalCtorTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2018, 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 8208067
+ * @enablePreview
+ * @summary Verify that instance methods are callable from ctor after all instance fields are DA.
+ */
+
+public value class CanonicalCtorTest {
+
+    private final int x, ymx;
+
+    CanonicalCtorTest(int x, int y) {
+
+        ymx = y - x;
+        this.x = x;
+
+        // all fields are assigned now, but we need a explicit `super()` invocation before accessing `this`
+        super();
+
+        validate();                 // OK: DU = {}
+        this.validate();            // OK: DU = {}
+        CanonicalCtorTest.this.validate();          // OK: DU = {}
+
+        assert (this.x > 0);        // OK: DU = {}
+        assert (this.y() > 0);      // OK: DU = {}
+    }
+
+    int x() {
+        return x;
+    }
+
+    int y() {
+        return ymx + x;
+    }
+
+    void validate() {
+        assert (x() > 0 && y() > 0);
+    }
+
+    public static void main(String... av) {
+        CanonicalCtorTest z = new CanonicalCtorTest(1, 10);
+        assert (z.x() == 1);
+        assert (z.y() == 10);
+    }
+}
diff a/test/langtools/tools/javac/valhalla/value-objects/CheckSynchronized.java b/test/langtools/tools/javac/valhalla/value-objects/CheckSynchronized.java
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/CheckSynchronized.java
@@ -0,0 +1,44 @@
+/*
+ * @test /nodynamiccopyright/
+ * @summary Check behavior of synzhronized key word on value classes instances and methods.
+ * @enablePreview
+ * @compile/fail/ref=CheckSynchronized.out -XDrawDiagnostics CheckSynchronized.java
+ */
+
+value final class CheckSynchronized implements java.io.Serializable {
+    synchronized void foo() { // <<-- ERROR, no monitor associated with `this'
+    }
+    void goo() {
+        synchronized(this) {} // <<-- ERROR, no monitor associated with `this'
+    }
+    synchronized static void zoo(CheckSynchronized cs) { // OK, static method.
+        synchronized(cs) {    // <<-- ERROR, no monitor associated with value class instance.
+        }
+
+        CheckSynchronized csr = cs;
+        synchronized(csr) {
+            // Error, no identity.
+        }
+
+        synchronized(x) {
+            // Error, no identity.
+        }
+
+        Object o = cs;
+        synchronized(o) {
+            // Error BUT not discernible at compile time
+        }
+        java.io.Serializable jis = cs;
+        synchronized(jis) {
+            // Error BUT not discernible at compile time
+        }
+    }
+    static int x = 10;
+
+    value record CheckSynchronizedRecord(int x, int y) {
+        synchronized void foo() { // <<-- ERROR, no monitor associated with `this'
+        }
+        synchronized static void zoo() { // OK, static method.
+        }
+    }
+}
diff a/test/langtools/tools/javac/valhalla/value-objects/CheckSynchronized.out b/test/langtools/tools/javac/valhalla/value-objects/CheckSynchronized.out
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/CheckSynchronized.out
@@ -0,0 +1,9 @@
+CheckSynchronized.java:9:23: compiler.err.mod.not.allowed.here: synchronized
+CheckSynchronized.java:39:27: compiler.err.mod.not.allowed.here: synchronized
+CheckSynchronized.java:12:9: compiler.err.type.found.req: CheckSynchronized, (compiler.misc.type.req.identity)
+CheckSynchronized.java:15:9: compiler.err.type.found.req: CheckSynchronized, (compiler.misc.type.req.identity)
+CheckSynchronized.java:19:9: compiler.err.type.found.req: CheckSynchronized, (compiler.misc.type.req.identity)
+CheckSynchronized.java:23:9: compiler.err.type.found.req: int, (compiler.misc.type.req.identity)
+- compiler.note.preview.filename: CheckSynchronized.java, DEFAULT
+- compiler.note.preview.recompile
+6 errors
diff a/test/langtools/tools/javac/valhalla/value-objects/ConstructorRefTest.java b/test/langtools/tools/javac/valhalla/value-objects/ConstructorRefTest.java
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/ConstructorRefTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2022, 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.  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.
+ */
+
+/*
+ * @test
+ * @bug 8281166
+ * @summary javac should generate BSM to invoke constructor for value class
+ * @enablePreview
+ * @compile ConstructorRefTest.java
+ * @run main/othervm ConstructorRefTest
+ */
+
+import java.util.function.Supplier;
+
+public class ConstructorRefTest {
+
+    public static value class V {
+
+        final int x;
+        final int y;
+
+        V() {
+            x = 1234;
+            y = 5678;
+        }
+    }
+
+    public static class P {
+
+        final int x;
+        final int y;
+
+        P() {
+            x = 1234;
+            y = 5678;
+        }
+    }
+
+    public static void main(String [] args) {
+
+        Supplier<P> sxp = P::new;
+        P p = (P) sxp.get();
+        if (p.x != 1234 || p.y != 5678)
+            throw new AssertionError(p);
+
+        Supplier<V> sxv = V::new;
+        V v = (V) sxv.get();
+        if (v.x != 1234 || v.y != 5678)
+            throw new AssertionError(v);
+    }
+}
diff a/test/langtools/tools/javac/valhalla/value-objects/DualNonDuplicateErrors.java b/test/langtools/tools/javac/valhalla/value-objects/DualNonDuplicateErrors.java
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/DualNonDuplicateErrors.java
@@ -0,0 +1,26 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8267843
+ * @summary Check that javac diagnoses `this` being passed around and instance method being invoked before value class instance is fully initialized.
+ * @enablePreview
+ * @compile/fail/ref=DualNonDuplicateErrors.out -XDrawDiagnostics DualNonDuplicateErrors.java
+ */
+
+public value class DualNonDuplicateErrors {
+
+    int x;
+
+    DualNonDuplicateErrors() {
+        // The call below should trigger two errors - they are not duplicates really.
+        // First one is for `this` being passed around ("exposed")
+        // Second is for instance method being invoked thereby allowing that method to
+        // observe the value class instance in a partially initialized state.
+        foo(this);
+        x = 10;
+        super();
+        foo(this); // No error here.
+    }
+
+    void foo(DualNonDuplicateErrors x) {
+    }
+}
diff a/test/langtools/tools/javac/valhalla/value-objects/DualNonDuplicateErrors.out b/test/langtools/tools/javac/valhalla/value-objects/DualNonDuplicateErrors.out
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/DualNonDuplicateErrors.out
@@ -0,0 +1,5 @@
+DualNonDuplicateErrors.java:18:13: compiler.err.cant.ref.before.ctor.called: this
+DualNonDuplicateErrors.java:18:9: compiler.err.cant.ref.before.ctor.called: foo(DualNonDuplicateErrors)
+- compiler.note.preview.filename: DualNonDuplicateErrors.java, DEFAULT
+- compiler.note.preview.recompile
+2 errors
diff a/test/langtools/tools/javac/valhalla/value-objects/GenericPoint.java b/test/langtools/tools/javac/valhalla/value-objects/GenericPoint.java
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/GenericPoint.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2022, 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 value class GenericPoint<T> {
+
+    T x, y;
+
+    GenericPoint(T x, T y) {
+        this.x = x;
+        this.y = y;
+    }
+}
diff a/test/langtools/tools/javac/valhalla/value-objects/IllegalMonitorStateExceptionTest.java b/test/langtools/tools/javac/valhalla/value-objects/IllegalMonitorStateExceptionTest.java
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/IllegalMonitorStateExceptionTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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
+ * @bug 8329400
+ * @summary test to check that IllegalMonitorStateException is thrown for value objects
+ * @enablePreview
+ * @run main IllegalMonitorStateExceptionTest
+ */
+
+public value class IllegalMonitorStateExceptionTest {
+    void m(Object o) {
+        synchronized (o) {}
+    }
+
+    public static void main(String[] args) throws Exception {
+        IllegalMonitorStateExceptionTest v = new IllegalMonitorStateExceptionTest();
+        try {
+            v.m(v);
+            throw new AssertionError("should have failed with IllegalMonitorStateExceptionTest");
+        } catch (IllegalMonitorStateException e) {
+            // as expected
+        }
+    }
+}
diff a/test/langtools/tools/javac/valhalla/value-objects/NoUnnecessaryPreloadsTest.java b/test/langtools/tools/javac/valhalla/value-objects/NoUnnecessaryPreloadsTest.java
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/NoUnnecessaryPreloadsTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2022, 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 8281323
+ * @summary Check emission of Preload attribute to make sure javac does not emit unneeded entries.
+ * @modules jdk.jdeps/com.sun.tools.classfile
+ * @enablePreview
+ * @run main NoUnnecessaryPreloadsTest
+ */
+
+import com.sun.tools.classfile.*;
+import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info;
+
+public class NoUnnecessaryPreloadsTest {
+
+    public value class PreLoadTest1 {
+        byte b;
+        public PreLoadTest1(byte b) {
+            this.b = b;
+        }
+    }
+
+    public class PreLoadTest2 {
+        static class Inner1 {
+            static value class Inner2 {}
+            Inner2 inner;
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+
+        // There should be no Preload attribute in NoUnnecessaryPreloadsTest.class
+        ClassFile cls = ClassFile.read(NoUnnecessaryPreloadsTest.class.getResourceAsStream("NoUnnecessaryPreloadsTest.class"));
+
+        if (cls == null) {
+            throw new AssertionError("Could not locate the class files");
+        }
+
+        /* Check emission of Preload attribute */
+        Preload_attribute preloads = (Preload_attribute) cls.attributes.get(Attribute.Preload);
+        if (preloads != null) {
+            throw new AssertionError("Unexpected Preload attribute!");
+        }
+
+        // There should be no Preload attribute in NoUnnecessaryPreloadsTest$PreloadTest1.class
+        cls = ClassFile.read(NoUnnecessaryPreloadsTest.class.getResourceAsStream("NoUnnecessaryPreloadsTest$PreLoadTest1.class"));
+
+        if (cls == null) {
+            throw new AssertionError("Could not locate the class files");
+        }
+
+        /* Check emission of Preload attribute */
+        preloads = (Preload_attribute) cls.attributes.get(Attribute.Preload);
+        if (preloads != null) {
+            throw new AssertionError("Unexpected Preload attribute!");
+        }
+
+        // There should be no Preload attribute in NoUnnecessaryPreloadsTest$PreloadTest2.class
+        cls = ClassFile.read(NoUnnecessaryPreloadsTest.class.getResourceAsStream("NoUnnecessaryPreloadsTest$PreLoadTest2.class"));
+
+        if (cls == null) {
+            throw new AssertionError("Could not locate the class files");
+        }
+
+        /* Check emission of Preload attribute */
+        preloads = (Preload_attribute) cls.attributes.get(Attribute.Preload);
+        if (preloads != null) {
+            throw new AssertionError("Unexpected Preload attribute!");
+        }
+
+        // There should be no Preload attribute in NoUnnecessaryPreloadsTest$PreloadTest2$Inner2.class
+        cls = ClassFile.read(NoUnnecessaryPreloadsTest.class.getResourceAsStream("NoUnnecessaryPreloadsTest$PreLoadTest2$Inner1$Inner2.class"));
+
+        if (cls == null) {
+            throw new AssertionError("Could not locate the class files");
+        }
+
+        /* Check emission of Preload attribute */
+        preloads = (Preload_attribute) cls.attributes.get(Attribute.Preload);
+        if (preloads != null) {
+            throw new AssertionError("Unexpected Preload attribute!");
+        }
+
+        // There should be ONE Preload attribute entry in NoUnnecessaryPreloadsTest$PreloadTest2$Inner1.class
+        cls = ClassFile.read(NoUnnecessaryPreloadsTest.class.getResourceAsStream("NoUnnecessaryPreloadsTest$PreLoadTest2$Inner1.class"));
+
+        if (cls == null) {
+            throw new AssertionError("Could not locate the class files");
+        }
+
+        /* Check emission of Preload attribute */
+        preloads = (Preload_attribute) cls.attributes.get(Attribute.Preload);
+        if (preloads == null) {
+            throw new AssertionError("Missing Preload attribute!");
+        }
+
+        if (preloads.number_of_classes != 1) {
+            throw new AssertionError("Incorrect number of Preload classes");
+        }
+
+        CONSTANT_Class_info clsInfo = cls.constant_pool.getClassInfo(preloads.value_class_info_index[0]);
+        if (!clsInfo.getName().equals("NoUnnecessaryPreloadsTest$PreLoadTest2$Inner1$Inner2")) {
+            throw new AssertionError("Expected Preload class entry is missing, but found " + clsInfo.getName());
+        }
+    }
+}
diff a/test/langtools/tools/javac/valhalla/value-objects/PreloadAttrTest2.java b/test/langtools/tools/javac/valhalla/value-objects/PreloadAttrTest2.java
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/PreloadAttrTest2.java
@@ -0,0 +1,110 @@
+/*
+ * 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
+ * @bug 8331461
+ * @summary [lworld] javac is generating a class file with the Preload attribute but with minor version '0'
+ * @library /tools/lib
+ * @modules
+ *      jdk.compiler/com.sun.tools.javac.code
+ *      jdk.compiler/com.sun.tools.javac.util
+ *      jdk.compiler/com.sun.tools.javac.api
+ *      jdk.compiler/com.sun.tools.javac.file
+ *      jdk.compiler/com.sun.tools.javac.main
+ *      jdk.jdeps/com.sun.tools.classfile
+ * @build toolbox.ToolBox toolbox.JavacTask
+ * @run main PreloadAttrTest2
+ */
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.util.Assert;
+import com.sun.tools.classfile.ClassFile;
+
+import toolbox.TestRunner;
+import toolbox.ToolBox;
+import toolbox.JavacTask;
+import toolbox.Task;
+
+public class PreloadAttrTest2 extends TestRunner {
+    ToolBox tb = new ToolBox();
+
+    public PreloadAttrTest2() {
+        super(System.err);
+    }
+
+    protected void runTests() throws Exception {
+        runTests(m -> new Object[] { Paths.get(m.getName()) });
+    }
+
+    Path[] findJavaFiles(Path... paths) throws Exception {
+        return tb.findJavaFiles(paths);
+    }
+
+    public static void main(String... args) throws Exception {
+        new PreloadAttrTest2().runTests();
+    }
+
+    @Test
+    public void testValueBased(Path base) throws Exception {
+        Path src = base.resolve("src");
+        tb.writeJavaFiles(src,
+                """
+                value class Val {}
+                """,
+                """
+                class Ident {
+                    Val val;
+                }
+                """);
+        Path classes = base.resolve("classes");
+        tb.createDirectories(classes);
+
+        new toolbox.JavacTask(tb)
+                .options("--enable-preview", "-source", Integer.toString(Runtime.version().feature()))
+                .outdir(classes)
+                .files(findJavaFiles(src))
+                .run()
+                .writeAll();
+        Path classFilePath = classes.resolve("Ident.class");
+        ClassFile classFile = ClassFile.read(classFilePath.toFile());
+        Assert.check(classFile.minor_version == 65535);
+        Assert.check(classFile.attributes.get("Preload") != null);
+
+        // now with the value class in the classpath
+        new toolbox.JavacTask(tb)
+                .options("--enable-preview", "-source", Integer.toString(Runtime.version().feature()), "-cp", classes.toString())
+                .outdir(classes)
+                .files(src.resolve("Ident.java"))
+                .run()
+                .writeAll();
+
+        classFilePath = classes.resolve("Ident.class");
+        classFile = ClassFile.read(classFilePath.toFile());
+        Assert.check(classFile.minor_version == 65535);
+        Assert.check(classFile.attributes.get("Preload") != null);
+    }
+}
diff a/test/langtools/tools/javac/valhalla/value-objects/PreloadAttributeTest.java b/test/langtools/tools/javac/valhalla/value-objects/PreloadAttributeTest.java
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/PreloadAttributeTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2022, 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 8280164
+ * @summary Check emission of Preload attribute
+ * @modules jdk.jdeps/com.sun.tools.classfile
+ * @enablePreview
+ * @run main PreloadAttributeTest
+ */
+
+import com.sun.tools.classfile.*;
+import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info;
+
+public class PreloadAttributeTest {
+
+    final value class V1 {}
+    final value class V2 {}
+    final value class V3 {}
+    final value class V4 {}
+    final value class V5 {}
+    final value class V6 {}
+    final value class V7 {}
+    final value class V8 {}
+    final value class V9 {}
+
+    static final value class X {
+        final V1 [] v1 = null; // field descriptor, encoding array type - no preload.
+        V2 foo() {  // method descriptor encoding value type, to be preloaded
+            return null;
+        }
+        void foo(V3 v3) { // method descriptor encoding value type, to be preloaded
+        }
+        void foo(int x) {
+            V4 [] v4 = null; // local variable encoding array type - no preload.
+        }
+        void goo(V6[] v6) { // parameter uses value type but as array component - no preload.
+            V5 v5 = null;  // preload value type used for local type.
+            if (v5 == null) {
+                // ...
+            } else {
+               V5 [] v52 = null;
+            }
+        }
+        final V7 v7 = null; // field descriptor uses value type - to be preloaded.
+        V8 [] goo(V9 [] v9) { // neither V8 nor V9 call for preload being array component types
+            return null;
+        }
+    }
+    // So we expect ONLY V2, V3 V5, V7 to be in Preload list
+
+    public static void main(String[] args) throws Exception {
+        ClassFile cls = ClassFile.read(PreloadAttributeTest.class.getResourceAsStream("PreloadAttributeTest$X.class"));
+
+        if (cls == null) {
+            throw new AssertionError("Could not locate the class files");
+        }
+
+        /* Check emission of Preload attribute */
+        Preload_attribute preloads = (Preload_attribute) cls.attributes.get(Attribute.Preload);
+        if (preloads == null) {
+            throw new AssertionError("Missing Preload attribute!");
+        }
+        if (preloads.number_of_classes != 4) {
+            throw new AssertionError("Incorrect number of Preload classes");
+        }
+
+        int mask = 0x56;
+        for (int i = 0; i < preloads.number_of_classes; i++) {
+            CONSTANT_Class_info clsInfo = cls.constant_pool.getClassInfo(
+                                  preloads.value_class_info_index[i]);
+            switch (clsInfo.getName()) {
+                case "PreloadAttributeTest$V2":
+                    mask &= ~2; break;
+                case "PreloadAttributeTest$V3":
+                    mask &= ~4; break;
+                case "PreloadAttributeTest$V5":
+                    mask &= ~16; break;
+                case "PreloadAttributeTest$V7" :
+                    mask &= ~64; break;
+                default:
+                    throw new AssertionError("Unexpected Preload class entry!");
+            }
+        }
+        if (mask != 0) {
+          throw new AssertionError("Some Preload class entries are missing!");
+        }
+    }
+}
diff a/test/langtools/tools/javac/valhalla/value-objects/ValueBasedFlagsTest.java b/test/langtools/tools/javac/valhalla/value-objects/ValueBasedFlagsTest.java
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/ValueBasedFlagsTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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
+ * @bug 8331462
+ * @summary [lworld] javac is setting the ACC_STRICT flag for value-based classes
+ * @library /tools/lib
+ * @modules
+ *      jdk.compiler/com.sun.tools.javac.code
+ *      jdk.compiler/com.sun.tools.javac.util
+ *      jdk.compiler/com.sun.tools.javac.api
+ *      jdk.compiler/com.sun.tools.javac.file
+ *      jdk.compiler/com.sun.tools.javac.main
+ *      jdk.jdeps/com.sun.tools.classfile
+ * @build toolbox.ToolBox toolbox.JavacTask
+ * @run main ValueBasedFlagsTest
+ */
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.util.Assert;
+import com.sun.tools.classfile.ClassFile;
+
+import toolbox.TestRunner;
+import toolbox.ToolBox;
+import toolbox.JavacTask;
+import toolbox.Task;
+
+public class ValueBasedFlagsTest extends TestRunner {
+    ToolBox tb = new ToolBox();
+
+    public ValueBasedFlagsTest() {
+        super(System.err);
+    }
+
+    protected void runTests() throws Exception {
+        runTests(m -> new Object[] { Paths.get(m.getName()) });
+    }
+
+    Path[] findJavaFiles(Path... paths) throws Exception {
+        return tb.findJavaFiles(paths);
+    }
+
+    public static void main(String... args) throws Exception {
+        new ValueBasedFlagsTest().runTests();
+    }
+
+    @Test
+    public void testValueBased(Path base) throws Exception {
+        Path src = base.resolve("src");
+        tb.writeJavaFiles(src,
+                """
+                package java.lang;
+                @jdk.internal.ValueBased
+                final class ValueBasedTest {}
+                """);
+        Path classes = base.resolve("classes");
+        tb.createDirectories(classes);
+
+        new toolbox.JavacTask(tb)
+                .options("--patch-module", "java.base=" + src.toString(),
+                        "--enable-preview", "-source", Integer.toString(Runtime.version().feature()))
+                .outdir(classes)
+                .files(findJavaFiles(src))
+                .run()
+                .writeAll();
+        Path classFilePath = classes.resolve("java", "lang", "ValueBasedTest.class");
+        ClassFile classFile = ClassFile.read(classFilePath.toFile());
+        Assert.check((classFile.access_flags.flags & Flags.ACC_STRICT) == 0);
+    }
+}
diff a/test/langtools/tools/javac/valhalla/value-objects/ValueCreationTest.java b/test/langtools/tools/javac/valhalla/value-objects/ValueCreationTest.java
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/ValueCreationTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2022, 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 Check code generation for value creation ops
+ * @modules jdk.compiler/com.sun.tools.javac.util jdk.jdeps/com.sun.tools.javap
+ * @enablePreview
+ * @run main ValueCreationTest
+ */
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.file.Paths;
+
+public class ValueCreationTest {
+
+    static final value class Point {
+
+        final int x;
+        final int y;
+
+        Point (int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+
+        public static void main(String [] args) {
+            Point p = new Point(10, 20);
+        }
+    }
+
+    public static void main(String[] args) {
+        new ValueCreationTest().run();
+    }
+
+    void run() {
+        String [] params = new String [] { "-v",
+                                            Paths.get(System.getProperty("test.classes"),
+                                                "ValueCreationTest$Point.class").toString() };
+        runCheck(params, new String [] {
+
+         "final value class ValueCreationTest$Point",
+         "flags: (0x0010) ACC_FINAL",
+         "ValueCreationTest$Point(int, int);",
+         "descriptor: (II)V",
+         "flags: (0x0000)",
+         "0: aload_0",
+         "1: iload_1",
+         "2: putfield      #1                  // Field x:I",
+         "5: aload_0",
+         "6: iload_2",
+         "7: putfield      #7                  // Field y:I",
+        "10: aload_0",
+        "11: invokespecial #10                 // Method java/lang/Object.\"<init>\":()V",
+         });
+     }
+
+    void runCheck(String [] params, String [] expectedOut) {
+        StringWriter s;
+        String out;
+
+        try (PrintWriter pw = new PrintWriter(s = new StringWriter())) {
+            com.sun.tools.javap.Main.run(params, pw);
+            out = s.toString();
+        }
+        int errors = 0;
+        for (String eo: expectedOut) {
+            if (!out.contains(eo)) {
+                System.err.println("Match not found for string: " + eo);
+                errors++;
+            }
+        }
+        if (errors > 0) {
+            throw new AssertionError("Unexpected javap output: " + out);
+        }
+    }
+}
diff a/test/langtools/tools/javac/valhalla/value-objects/ValueObjectCompilationTests.java b/test/langtools/tools/javac/valhalla/value-objects/ValueObjectCompilationTests.java
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/ValueObjectCompilationTests.java
@@ -0,0 +1,792 @@
+/*
+ * Copyright (c) 2022, 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.
+ */
+
+/**
+ * ValueObjectCompilationTests
+ *
+ * @test
+ * @bug 8287136 8292630 8279368 8287136 8287770 8279840 8279672 8292753 8287763 8279901 8287767 8293183 8293120
+ *      8329345
+ * @summary Negative compilation tests, and positive compilation (smoke) tests for Value Objects
+ * @library /lib/combo /tools/lib
+ * @modules
+ *     jdk.compiler/com.sun.tools.javac.util
+ *     jdk.compiler/com.sun.tools.javac.api
+ *     jdk.compiler/com.sun.tools.javac.main
+ *     jdk.compiler/com.sun.tools.javac.code
+ *     jdk.jdeps/com.sun.tools.classfile
+ * @build toolbox.ToolBox toolbox.JavacTask
+ * @run junit ValueObjectCompilationTests
+ */
+
+import java.io.File;
+
+import java.util.List;
+import java.util.Set;
+
+import com.sun.tools.javac.util.Assert;
+
+import com.sun.tools.classfile.Attribute;
+import com.sun.tools.classfile.Attributes;
+import com.sun.tools.classfile.ClassFile;
+import com.sun.tools.classfile.Code_attribute;
+import com.sun.tools.classfile.ConstantPool;
+import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info;
+import com.sun.tools.classfile.ConstantPool.CONSTANT_Fieldref_info;
+import com.sun.tools.classfile.ConstantPool.CONSTANT_Methodref_info;
+import com.sun.tools.classfile.Field;
+import com.sun.tools.classfile.Instruction;
+import com.sun.tools.classfile.Method;
+
+import com.sun.tools.javac.code.Flags;
+
+import org.junit.jupiter.api.Test;
+import tools.javac.combo.CompilationTestCase;
+import toolbox.ToolBox;
+
+class ValueObjectCompilationTests extends CompilationTestCase {
+
+    private static String[] PREVIEW_OPTIONS = {"--enable-preview", "-source",
+            Integer.toString(Runtime.version().feature())};
+
+    public ValueObjectCompilationTests() {
+        setDefaultFilename("ValueObjectsTest.java");
+        setCompileOptions(PREVIEW_OPTIONS);
+    }
+
+    @Test
+    void testAbstractValueClassConstraints() {
+        assertFail("compiler.err.mod.not.allowed.here",
+                """
+                abstract value class V {
+                    synchronized void foo() {
+                     // Error, abstract value class may not declare a synchronized instance method.
+                    }
+                }
+                """);
+    }
+
+    @Test
+    void testValueModifierConstraints() {
+        assertFail("compiler.err.illegal.combination.of.modifiers",
+                """
+                value @interface IA {}
+                """);
+        assertFail("compiler.err.illegal.combination.of.modifiers",
+                """
+                value interface I {}
+                """);
+        assertFail("compiler.err.mod.not.allowed.here",
+                """
+                class Test {
+                    value int x;
+                }
+                """);
+        assertFail("compiler.err.mod.not.allowed.here",
+                """
+                class Test {
+                    value int foo();
+                }
+                """);
+        assertFail("compiler.err.mod.not.allowed.here",
+                """
+                value enum Enum {}
+                """);
+    }
+
+    @Test
+    void testSuperClassConstraints() {
+        assertFail("compiler.err.super.class.method.cannot.be.synchronized",
+                """
+                abstract class I {
+                    synchronized void foo() {}
+                }
+                value class V extends I {}
+                """);
+        assertFail("compiler.err.concrete.supertype.for.value.class",
+                """
+                class ConcreteSuperType {
+                    static abstract value class V extends ConcreteSuperType {}  // Error: concrete super.
+                }
+                """);
+        assertOK(
+                """
+                value record Point(int x, int y) {}
+                """);
+    }
+
+    @Test
+    void testRepeatedModifiers() {
+        assertFail("compiler.err.repeated.modifier", "value value class ValueTest {}");
+    }
+
+    @Test
+    void testParserTest() {
+        assertOK(
+                """
+                value class Substring implements CharSequence {
+                    private String str;
+                    private int start;
+                    private int end;
+
+                    public Substring(String str, int start, int end) {
+                        checkBounds(start, end, str.length());
+                        this.str = str;
+                        this.start = start;
+                        this.end = end;
+                    }
+
+                    public int length() {
+                        return end - start;
+                    }
+
+                    public char charAt(int i) {
+                        checkBounds(0, i, length());
+                        return str.charAt(start + i);
+                    }
+
+                    public Substring subSequence(int s, int e) {
+                        checkBounds(s, e, length());
+                        return new Substring(str, start + s, start + e);
+                    }
+
+                    public String toString() {
+                        return str.substring(start, end);
+                    }
+
+                    private static void checkBounds(int start, int end, int length) {
+                        if (start < 0 || end < start || length < end)
+                            throw new IndexOutOfBoundsException();
+                    }
+                }
+                """
+        );
+    }
+
+    @Test
+    void testSemanticsViolations() {
+        assertFail("compiler.err.cant.inherit.from.final",
+                """
+                value class Base {}
+                class Subclass extends Base {}
+                """);
+        assertFail("compiler.err.cant.assign.val.to.var",
+                """
+                value class Point {
+                    int x = 10;
+                    int y;
+                    Point (int x, int y) {
+                        this.x = x; // Error, final field 'x' is already assigned to.
+                        this.y = y; // OK.
+                    }
+                }
+                """);
+        assertFail("compiler.err.cant.assign.val.to.var",
+                """
+                value class Point {
+                    int x;
+                    int y;
+                    Point (int x, int y) {
+                        this.x = x;
+                        this.y = y;
+                    }
+
+                    void foo(Point p) {
+                        this.y = p.y; // Error, y is final and can't be written outside of ctor.
+                    }
+                }
+                """);
+        assertFail("compiler.err.var.might.not.have.been.initialized",
+                """
+                value class Point {
+                    int x;
+                    int y;
+                    Point (int x, int y) {
+                        this.x = x;
+                        // y hasn't been initialized
+                    }
+                }
+                """);
+        assertFail("compiler.err.mod.not.allowed.here",
+                """
+                value class V {
+                    synchronized void foo() {}
+                }
+                """);
+        assertOK(
+                """
+                value class V {
+                    synchronized static void soo() {}
+                }
+                """);
+        assertFail("compiler.err.type.found.req",
+                """
+                value class V {
+                    { synchronized(this) {} }
+                }
+                """);
+        assertFail("compiler.err.mod.not.allowed.here",
+                """
+                value record R() {
+                    synchronized void foo() { } // Error;
+                    synchronized static void soo() {} // OK.
+                }
+                """);
+        assertFail("compiler.err.cant.ref.before.ctor.called",
+                """
+                value class V {
+                    int x;
+                    V() {
+                        foo(this); // Error.
+                        x = 10;
+                    }
+                    void foo(V v) {}
+                }
+                """);
+        assertFail("compiler.err.cant.ref.before.ctor.called",
+                """
+                value class V {
+                    int x;
+                    V() {
+                        x = 10;
+                        foo(this); // error
+                    }
+                    void foo(V v) {}
+                }
+                """);
+        assertFail("compiler.err.type.found.req",
+                """
+                interface I {}
+                interface VI extends I {}
+                class C {}
+                value class VC<T extends VC> {
+                    void m(T t) {
+                        synchronized(t) {} // error
+                    }
+                }
+                """);
+        assertFail("compiler.err.type.found.req",
+                """
+                interface I {}
+                interface VI extends I {}
+                class C {}
+                value class VC<T extends VC> {
+                    void foo(Object o) {
+                        synchronized ((VC & I)o) {} // error
+                    }
+                }
+                """);
+        // OK if the value class is abstract
+        assertOK(
+                """
+                interface I {}
+                abstract value class VI implements I {}
+                class C {}
+                value class VC<T extends VC> {
+                    void bar(Object o) {
+                        synchronized ((VI & I)o) {} // error
+                    }
+                }
+                """);
+    }
+
+    @Test
+    void testInteractionWithSealedClasses() {
+        assertOK(
+                """
+                abstract sealed value class SC {}
+                value class VC extends SC {}
+                """
+        );
+        assertOK(
+                """
+                abstract sealed interface SI {}
+                value class VC implements SI {}
+                """
+        );
+        assertOK(
+                """
+                abstract sealed class SC {}
+                final class IC extends SC {}
+                non-sealed class IC2 extends SC {}
+                final class IC3 extends IC2 {}
+                """
+        );
+        assertOK(
+                """
+                abstract sealed interface SI {}
+                final class IC implements SI {}
+                non-sealed class IC2 implements SI {}
+                final class IC3 extends IC2 {}
+                """
+        );
+        assertFail("compiler.err.illegal.combination.of.modifiers",
+                """
+                abstract sealed value class SC {}
+                non-sealed value class VC extends SC {}
+                """
+        );
+        assertFail("compiler.err.illegal.combination.of.modifiers",
+                """
+                sealed value class SI {}
+                non-sealed value class VC extends SI {}
+                """
+        );
+    }
+
+    @Test
+    void testCheckClassFileFlags() throws Exception {
+        for (String source : List.of(
+                """
+                interface I {}
+                class Test {
+                    I i = new I() {};
+                }
+                """,
+                """
+                class C {}
+                class Test {
+                    C c = new C() {};
+                }
+                """,
+                """
+                class Test {
+                    Object o = new Object() {};
+                }
+                """,
+                """
+                class Test {
+                    abstract class Inner {}
+                }
+                """
+        )) {
+            File dir = assertOK(true, source);
+            for (final File fileEntry : dir.listFiles()) {
+                if (fileEntry.getName().contains("$")) {
+                    ClassFile classFile = ClassFile.read(fileEntry);
+                    Assert.check((classFile.access_flags.flags & Flags.ACC_IDENTITY) != 0);
+                }
+            }
+        }
+
+        for (String source : List.of(
+                """
+                class C {}
+                """,
+                """
+                abstract class A {
+                    int i;
+                }
+                """,
+                """
+                abstract class A {
+                    synchronized void m() {}
+                }
+                """,
+                """
+                class C {
+                    synchronized void m() {}
+                }
+                """,
+                """
+                abstract class A {
+                    int i;
+                    { i = 0; }
+                }
+                """,
+                """
+                abstract class A {
+                    A(int i) {}
+                }
+                """,
+                """
+                    enum E {}
+                """,
+                """
+                    record R() {}
+                """
+        )) {
+            File dir = assertOK(true, source);
+            for (final File fileEntry : dir.listFiles()) {
+                ClassFile classFile = ClassFile.read(fileEntry);
+                Assert.check(classFile.access_flags.is(Flags.ACC_IDENTITY));
+            }
+        }
+
+        {
+            String source =
+                    """
+                    abstract value class A {}
+                    value class Sub extends A {} //implicitly final
+                    """;
+            File dir = assertOK(true, source);
+            for (final File fileEntry : dir.listFiles()) {
+                ClassFile classFile = ClassFile.read(fileEntry);
+                switch (classFile.getName()) {
+                    case "Sub":
+                        Assert.check((classFile.access_flags.flags & (Flags.FINAL)) != 0);
+                        break;
+                    case "A":
+                        Assert.check((classFile.access_flags.flags & (Flags.ABSTRACT)) != 0);
+                        break;
+                    default:
+                        throw new AssertionError("you shoulnd't be here");
+                }
+            }
+        }
+
+        for (String source : List.of(
+                """
+                value class V {
+                    int i = 0;
+                    static int j;
+                }
+                """,
+                """
+                abstract value class A {
+                    static int j;
+                }
+
+                value class V extends A {
+                    int i = 0;
+                }
+                """
+        )) {
+            File dir = assertOK(true, source);
+            for (final File fileEntry : dir.listFiles()) {
+                ClassFile classFile = ClassFile.read(fileEntry);
+                for (Field field : classFile.fields) {
+                    if (!field.access_flags.is(Flags.STATIC)) {
+                        Set<String> fieldFlags = field.access_flags.getFieldFlags();
+                        Assert.check(fieldFlags.size() == 2 && fieldFlags.contains("ACC_FINAL") && fieldFlags.contains("ACC_STRICT"));
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    void testConstruction() throws Exception {
+        record Data(String src, boolean isRecord) {}
+        for (Data data : List.of(
+                new Data(
+                    """
+                    value class Test {
+                        int i = 100;
+                    }
+                    """, false),
+                new Data(
+                    """
+                    value class Test {
+                        int i;
+                        Test() {
+                            i = 100;
+                        }
+                    }
+                    """, false),
+                new Data(
+                    """
+                    value class Test {
+                        int i;
+                        Test() {
+                            i = 100;
+                            super();
+                        }
+                    }
+                    """, false),
+                new Data(
+                    """
+                    value class Test {
+                        int i;
+                        Test() {
+                            this.i = 100;
+                            super();
+                        }
+                    }
+                    """, false),
+                new Data(
+                    """
+                    value record Test(int i) {}
+                    """, true)
+        )) {
+            String expectedCodeSequence = "aload_0,bipush,putfield,aload_0,invokespecial,return,";
+            String expectedCodeSequenceRecord = "aload_0,iload_1,putfield,aload_0,invokespecial,return,";
+            File dir = assertOK(true, data.src);
+            for (final File fileEntry : dir.listFiles()) {
+                ClassFile classFile = ClassFile.read(fileEntry);
+                for (Method method : classFile.methods) {
+                    if (method.getName(classFile.constant_pool).equals("<init>")) {
+                        Code_attribute code = (Code_attribute)method.attributes.get("Code");
+                        String foundCodeSequence = "";
+                        for (Instruction inst: code.getInstructions()) {
+                            foundCodeSequence += inst.getMnemonic() + ",";
+                        }
+                        if (!data.isRecord) {
+                            Assert.check(expectedCodeSequence.equals(foundCodeSequence));
+                        } else {
+                            Assert.check(expectedCodeSequenceRecord.equals(foundCodeSequence));
+                        }
+                    }
+                }
+            }
+        }
+
+        String source =
+                """
+                value class Test {
+                    int i = 100;
+                    int j;
+                    {
+                        j = 200;
+                    }
+                }
+                """;
+        String expectedCodeSequence = "aload_0,bipush,putfield,aload_0,invokespecial,aload_0,sipush,putfield,return,";
+        File dir = assertOK(true, source);
+        for (final File fileEntry : dir.listFiles()) {
+            ClassFile classFile = ClassFile.read(fileEntry);
+            for (Method method : classFile.methods) {
+                if (method.getName(classFile.constant_pool).equals("<init>")) {
+                    Code_attribute code = (Code_attribute)method.attributes.get("Code");
+                    String foundCodeSequence = "";
+                    for (Instruction inst: code.getInstructions()) {
+                        foundCodeSequence += inst.getMnemonic() + ",";
+                    }
+                    Assert.check(expectedCodeSequence.equals(foundCodeSequence));
+                }
+            }
+        }
+
+        assertFail("compiler.err.cant.ref.before.ctor.called",
+                """
+                value class Test {
+                    Test() {
+                        m();
+                    }
+                    void m() {}
+                }
+                """
+        );
+        assertFail("compiler.err.cant.ref.after.ctor.called",
+                """
+                value class Test {
+                    int i;
+                    Test() {
+                        super();
+                        this.i = i;
+                    }
+                }
+                """
+        );
+        assertOK(
+                """
+                class UnrelatedThisLeak {
+                    value class V {
+                        int f;
+                        V() {
+                            UnrelatedThisLeak x = UnrelatedThisLeak.this;
+                            f = 10;
+                            x = UnrelatedThisLeak.this;
+                        }
+                    }
+                }
+                """
+        );
+    }
+
+    @Test
+    void testThisCallingConstructor() throws Exception {
+        // make sure that this() calling constructors doesn't initialize final fields
+        String source =
+                """
+                value class Test {
+                    int i;
+                    Test() {
+                        this(0);
+                    }
+
+                    Test(int i) {
+                        this.i = i;
+                    }
+                }
+                """;
+        File dir = assertOK(true, source);
+        File fileEntry = dir.listFiles()[0];
+        ClassFile classFile = ClassFile.read(fileEntry);
+        String expectedCodeSequenceThisCallingConst = "aload_0,iconst_0,invokespecial,return,";
+        String expectedCodeSequenceNonThisCallingConst = "aload_0,iload_1,putfield,aload_0,invokespecial,return,";
+        for (Method method : classFile.methods) {
+            if (method.getName(classFile.constant_pool).equals("<init>")) {
+                if (method.descriptor.getParameterCount(classFile.constant_pool) == 0) {
+                    Code_attribute code = (Code_attribute)method.attributes.get("Code");
+                    String foundCodeSequence = "";
+                    for (Instruction inst: code.getInstructions()) {
+                        foundCodeSequence += inst.getMnemonic() + ",";
+                    }
+                    Assert.check(expectedCodeSequenceThisCallingConst.equals(foundCodeSequence));
+                } else {
+                    Code_attribute code = (Code_attribute)method.attributes.get("Code");
+                    String foundCodeSequence = "";
+                    for (Instruction inst: code.getInstructions()) {
+                        foundCodeSequence += inst.getMnemonic() + ",";
+                    }
+                    Assert.check(expectedCodeSequenceNonThisCallingConst.equals(foundCodeSequence));
+                }
+            }
+        }
+    }
+
+    @Test
+    void testSelectors() throws Exception {
+        assertOK(
+                """
+                value class V {
+                    void selector() {
+                        Class<?> c = int.class;
+                    }
+                }
+                """
+        );
+        assertFail("compiler.err.expected",
+                """
+                value class V {
+                    void selector() {
+                        int i = int.some_selector;
+                    }
+                }
+                """
+        );
+    }
+
+    @Test
+    void testAnonymousValue() throws Exception {
+        assertOK(
+                """
+                class Test {
+                    void m() {
+                        Object o = new value Comparable<String>() {
+                            @Override
+                            public int compareTo(String o) {
+                                return 0;
+                            }
+                        };
+                    }
+                }
+                """
+        );
+        assertOK(
+                """
+                class Test {
+                    void m() {
+                        Object o = new value Comparable<>() {
+                            @Override
+                            public int compareTo(Object o) {
+                                return 0;
+                            }
+                        };
+                    }
+                }
+                """
+        );
+    }
+
+    @Test
+    void testNullAssigment() throws Exception {
+        assertOK(
+                """
+                value final class V {
+                    final int x = 10;
+
+                    value final class X {
+                        final V v;
+                        final V v2;
+
+                        X() {
+                            this.v = null;
+                            this.v2 = null;
+                        }
+
+                        X(V v) {
+                            this.v = v;
+                            this.v2 = v;
+                        }
+
+                        V foo(X x) {
+                            x = new X(null);  // OK
+                            return x.v;
+                        }
+                    }
+                    V bar(X x) {
+                        x = new X(null); // OK
+                        return x.v;
+                    }
+
+                    class Y {
+                        V v;
+                        V [] va = { null }; // OK: array initialization
+                        V [] va2 = new V[] { null }; // OK: array initialization
+                        void ooo(X x) {
+                            x = new X(null); // OK
+                            v = null; // legal assignment.
+                            va[0] = null; // legal.
+                            va = new V[] { null }; // legal
+                        }
+                    }
+                }
+                """
+        );
+    }
+
+    private File findClassFileOrFail(File dir, String name) {
+        for (final File fileEntry : dir.listFiles()) {
+            if (fileEntry.getName().equals(name)) {
+                return fileEntry;
+            }
+        }
+        throw new AssertionError("file not found");
+    }
+
+    private Attribute findAttributeOrFail(Attributes attributes, Class<? extends Attribute> attrClass, int numberOfAttributes) {
+        int attrCount = 0;
+        Attribute result = null;
+        for (Attribute attribute : attributes) {
+            if (attribute.getClass() == attrClass) {
+                attrCount++;
+                if (result == null) {
+                    result = attribute;
+                }
+            }
+        }
+        if (attrCount == 0) throw new AssertionError("attribute not found");
+        if (attrCount != numberOfAttributes) throw new AssertionError("incorrect number of attributes found");
+        return result;
+    }
+
+    private void checkAttributeNotPresent(Attributes attributes, Class<? extends Attribute> attrClass) {
+        for (Attribute attribute : attributes) {
+            if (attribute.getClass() == attrClass) {
+                throw new AssertionError("attribute found");
+            }
+        }
+    }
+}
diff a/test/langtools/tools/javac/valhalla/value-objects/ValueObjectsBinaryCompatibilityTests.java b/test/langtools/tools/javac/valhalla/value-objects/ValueObjectsBinaryCompatibilityTests.java
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/ValueObjectsBinaryCompatibilityTests.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2022, 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 8292817
+ * @summary add binary compatibility tests for value objects
+ * @library /tools/lib
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.main
+ *          jdk.compiler/com.sun.tools.javac.util
+ *          jdk.compiler/com.sun.tools.javac.code
+ *          jdk.jdeps/com.sun.tools.classfile
+ * @build toolbox.ToolBox toolbox.JavacTask
+ * @enablePreview
+ * @run main ValueObjectsBinaryCompatibilityTests
+ */
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import toolbox.TestRunner;
+import toolbox.ToolBox;
+import toolbox.JavaTask;
+import toolbox.JavacTask;
+import toolbox.Task;
+
+public class ValueObjectsBinaryCompatibilityTests extends TestRunner {
+    ToolBox tb;
+
+    ValueObjectsBinaryCompatibilityTests() {
+        super(System.err);
+        tb = new ToolBox();
+    }
+
+    protected void runTests() throws Exception {
+        runTests(m -> new Object[]{Paths.get(m.getName())});
+    }
+
+    public static void main(String... args) throws Exception {
+        new ValueObjectsBinaryCompatibilityTests().runTests();
+    }
+
+    Path[] findJavaFiles(Path... paths) throws IOException {
+        return tb.findJavaFiles(paths);
+    }
+
+    /* 1- compiles the first version of the source code, code1, along with the client source code
+     * 2- executes the client class just to make sure that it works, sanity check
+     * 3- compiles the second version, code2
+     * 4- executes the client class and makes sure that the VM throws the expected error or not
+     *    depending on the shouldFail argument
+     */
+    private void testCompatibilityAfterChange(
+            Path base,
+            String code1,
+            String code2,
+            String clientCode,
+            boolean shouldFail,
+            Class<?> expectedError) throws Exception {
+        Path src = base.resolve("src");
+        Path pkg = src.resolve("pkg");
+        Path src1 = pkg.resolve("Test");
+        Path client = pkg.resolve("Client");
+
+        tb.writeJavaFiles(src1, code1);
+        tb.writeJavaFiles(client, clientCode);
+
+        Path out = base.resolve("out");
+        Files.createDirectories(out);
+
+        new JavacTask(tb)
+                .outdir(out)
+                .options("--enable-preview", "-source", String.valueOf(Runtime.version().feature()))
+                .files(findJavaFiles(pkg))
+                .run();
+
+        // let's execute to check that it's working
+        String output = new JavaTask(tb)
+                .classpath(out.toString())
+                .classArgs("pkg.Client")
+                .vmOptions("--enable-preview")
+                .run()
+                .writeAll()
+                .getOutput(Task.OutputKind.STDOUT);
+
+        // let's first check that it runs wo issues
+        if (!output.contains("Hello World!")) {
+            throw new AssertionError("execution of Client didn't finish");
+        }
+
+        // now lets change the first class
+        tb.writeJavaFiles(src1, code2);
+
+        new JavacTask(tb)
+                .outdir(out)
+                .files(findJavaFiles(src1))
+                .options("--enable-preview", "-source", String.valueOf(Runtime.version().feature()))
+                .run();
+
+        if (shouldFail) {
+            // let's now check that we get the expected error
+            output = new JavaTask(tb)
+                    .classpath(out.toString())
+                    .classArgs("pkg.Client")
+                    .vmOptions("--enable-preview")
+                    .run(Task.Expect.FAIL)
+                    .writeAll()
+                    .getOutput(Task.OutputKind.STDERR);
+            if (!output.contains(expectedError.getName())) {
+                throw new AssertionError(expectedError.getName() + " expected");
+            }
+        } else {
+            new JavaTask(tb)
+                    .classpath(out.toString())
+                    .classArgs("pkg.Client")
+                    .vmOptions("--enable-preview")
+                    .run(Task.Expect.SUCCESS);
+        }
+    }
+
+    @Test
+    public void testAbstractValueToValueClass(Path base) throws Exception {
+        /* Removing the abstract modifier from a value class declaration has the side-effect of making the class final,
+         * with binary compatibility risks outlined in 13.4.2.3
+         */
+        testCompatibilityAfterChange(
+                base,
+                """
+                package pkg;
+                public abstract value class A {}
+                """,
+                """
+                package pkg;
+                public value class A {}
+                """,
+                """
+                package pkg;
+                public value class Client extends A {
+                    public static void main(String... args) {
+                        System.out.println("Hello World!");
+                    }
+                }
+                """,
+                true,
+                IncompatibleClassChangeError.class
+        );
+    }
+
+    @Test
+    public void testAbstractOrFinalIdentityToValueClass(Path base) throws Exception {
+        /* Modifying an abstract or final identity class to be a value class does not break compatibility
+         * with pre-existing binaries
+         */
+        testCompatibilityAfterChange(
+                base,
+                """
+                package pkg;
+                public abstract class A {}
+                """,
+                """
+                package pkg;
+                public abstract value class A {}
+                """,
+                """
+                package pkg;
+                public class Client extends A {
+                    public static void main(String... args) {
+                        System.out.println("Hello World!");
+                    }
+                }
+                """,
+                false,
+                null
+        );
+
+        testCompatibilityAfterChange(
+                base,
+                """
+                package pkg;
+                public final class A {}
+                """,
+                """
+                package pkg;
+                public final value class A {}
+                """,
+                """
+                package pkg;
+                public class Client {
+                    A a;
+                    public static void main(String... args) {
+                        System.out.println("Hello World!");
+                    }
+                }
+                """,
+                false,
+                null
+        );
+    }
+
+    @Test
+    public void testAddingValueModifier(Path base) throws Exception {
+        /* Adding the value modifier to a non-abstract, non-final class declaration has the side-effect of making
+         * the class final, with binary compatibility risks outlined in 13.4.2.3
+         */
+        testCompatibilityAfterChange(
+                base,
+                """
+                package pkg;
+                public class A {}
+                """,
+                """
+                package pkg;
+                public value class A {}
+                """,
+                """
+                package pkg;
+                public class Client extends A {
+                    public static void main(String... args) {
+                        System.out.println("Hello World!");
+                    }
+                }
+                """,
+                true,
+                IncompatibleClassChangeError.class
+        );
+    }
+
+    @Test
+    public void testValueToIdentityClass(Path base) throws Exception {
+        /* If a value class is changed to be an identity class, then an IncompatibleClassChangeError is thrown if a
+         * binary of a pre-existing value subclass of this class is loaded
+         */
+        testCompatibilityAfterChange(
+                base,
+                """
+                package pkg;
+                public abstract value class A {}
+                """,
+                """
+                package pkg;
+                public abstract class A {}
+                """,
+                """
+                package pkg;
+                public value class Client extends A {
+                    public static void main(String... args) {
+                        System.out.println("Hello World!");
+                    }
+                }
+                """,
+                true,
+                IncompatibleClassChangeError.class
+        );
+    }
+}
diff a/test/langtools/tools/javac/valhalla/value-objects/classfile/class_flags/CheckClassfileFlagsTest.java b/test/langtools/tools/javac/valhalla/value-objects/classfile/class_flags/CheckClassfileFlagsTest.java
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/classfile/class_flags/CheckClassfileFlagsTest.java
@@ -0,0 +1,17 @@
+/* @test /nodynamiccopyright/
+ * @bug 8292883
+ * @summary [lworld] javac fails to detect class files with invalid access flags
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.main
+ *          jdk.compiler/com.sun.tools.javac.util
+ * @library /tools/lib
+ * @build toolbox.ToolBox toolbox.JavacTask toolbox.TestRunner
+ * @compile ValueWithInvalidFlags.jcod
+ * @compile/fail/ref=CheckClassfileFlagsTest.out -XDrawDiagnostics CheckClassfileFlagsTest.java
+ */
+
+public class CheckClassfileFlagsTest {
+    void m() {
+        ValueWithInvalidFlags v = new ValueWithInvalidFlags();
+    }
+}
diff a/test/langtools/tools/javac/valhalla/value-objects/classfile/class_flags/CheckClassfileFlagsTest.out b/test/langtools/tools/javac/valhalla/value-objects/classfile/class_flags/CheckClassfileFlagsTest.out
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/classfile/class_flags/CheckClassfileFlagsTest.out
@@ -0,0 +1,2 @@
+CheckClassfileFlagsTest.java:15:9: compiler.err.cant.access: ValueWithInvalidFlags, (compiler.misc.bad.class.file.header: ValueWithInvalidFlags.class, (compiler.misc.bad.signature: "QValueWithInvalidFlags;"))
+1 error
diff a/test/langtools/tools/javac/valhalla/value-objects/classfile/class_flags/ValueWithInvalidFlags.jcod b/test/langtools/tools/javac/valhalla/value-objects/classfile/class_flags/ValueWithInvalidFlags.jcod
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/classfile/class_flags/ValueWithInvalidFlags.jcod
@@ -0,0 +1,73 @@
+class ValueWithInvalidFlags {
+  0xCAFEBABE;
+  0; // minor version
+  63; // version
+  [] { // Constant Pool
+    ; // first element is empty
+    class #2; // #1
+    Utf8 "ValueWithInvalidFlags"; // #2
+    Field #1 #4; // #3
+    NameAndType #5 #6; // #4
+    Utf8 "i"; // #5
+    Utf8 "I"; // #6
+    class #8; // #7
+    Utf8 "java/lang/Object"; // #8
+    Utf8 "<init>"; // #9
+    Utf8 "()QValueWithInvalidFlags;"; // #10
+    Utf8 "Code"; // #11
+    Utf8 "LineNumberTable"; // #12
+    Utf8 "SourceFile"; // #13
+    Utf8 "ValueWithInvalidFlags.java"; // #14
+  } // Constant Pool
+
+  0x0831; // access
+  #1;// this_cpx
+  #7;// super_cpx
+
+  [] { // Interfaces
+  } // Interfaces
+
+  [] { // Fields
+    {  // field
+      0x0010; // access
+      #5; // name_index
+      #6; // descriptor_index
+      [] { // Attributes
+      } // Attributes
+    }
+  } // Fields
+
+  [] { // Methods
+    {  // method
+      0x0009; // access
+      #9; // name_index
+      #10; // descriptor_index
+      [] { // Attributes
+        Attr(#11) { // Code
+          2; // max_stack
+          1; // max_locals
+          Bytes[]{
+            0xCB00014B032A5FCC;
+            0x00034B2AB0;
+          }
+          [] { // Traps
+          } // end Traps
+          [] { // Attributes
+            Attr(#12) { // LineNumberTable
+              [] { // line_number_table
+                0  1;
+                4  2;
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    }
+  } // Methods
+
+  [] { // Attributes
+    Attr(#13) { // SourceFile
+      #14;
+    } // end SourceFile
+  } // Attributes
+} // end class ValueWithInvalidFlags
diff a/test/langtools/tools/javac/valhalla/value-objects/separate_compilation/AbstractValueSuper.java b/test/langtools/tools/javac/valhalla/value-objects/separate_compilation/AbstractValueSuper.java
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/separate_compilation/AbstractValueSuper.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2022, 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 abstract value class AbstractValueSuper {}
diff a/test/langtools/tools/javac/valhalla/value-objects/separate_compilation/ConcreteValue.java b/test/langtools/tools/javac/valhalla/value-objects/separate_compilation/ConcreteValue.java
--- /dev/null
+++ b/test/langtools/tools/javac/valhalla/value-objects/separate_compilation/ConcreteValue.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2022, 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.
+ */
+
+/*
+ * @test
+ * @bug 8288644
+ * @summary [lw4] Unable to extend a separately compiled abstract value class
+ * @enablePreview
+ * @compile AbstractValueSuper.java ConcreteValue.java
+ * @compile ConcreteValue.java
+ * @run main ConcreteValue
+ */
+
+public value class ConcreteValue extends AbstractValueSuper {
+    public static void main(String [] args) {
+    }
+}
diff a/test/langtools/tools/javap/4870651/T4870651.java b/test/langtools/tools/javap/4870651/T4870651.java
--- a/test/langtools/tools/javap/4870651/T4870651.java
+++ b/test/langtools/tools/javap/4870651/T4870651.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 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,15 +25,17 @@
  * @test
  * @bug 4870651 6715757
  * @summary javap should recognize generics, varargs, enum;
  *          javap prints "extends java.lang.Object"
  * @modules jdk.jdeps/com.sun.tools.javap
+ * @modules java.base/jdk.internal.misc
  * @build T4870651 Test
  * @run main T4870651
  */
 
 import java.io.*;
+import jdk.internal.misc.PreviewFeatures;
 
 public class T4870651 {
     public static void main(String[] args) throws Exception {
         new T4870651().run();
     }
@@ -44,11 +46,13 @@
                    "E extends java.lang.Exception & java.lang.Comparable<T>, " +
                    "U extends java.lang.Comparable>",
                "v1(java.lang.String...)");
 
         verify("Test$Enum",
-               "flags: (0x4030) ACC_FINAL, ACC_SUPER, ACC_ENUM",
+               PreviewFeatures.isEnabled()
+                       ? "flags: (0x4030) ACC_FINAL, ACC_IDENTITY, ACC_ENUM"
+                       : "flags: (0x4030) ACC_FINAL, ACC_SUPER, ACC_ENUM",
                "flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM");
 
         if (errors > 0)
             throw new Error(errors + " found.");
     }
diff a/test/langtools/tools/javap/T4975569.java b/test/langtools/tools/javap/T4975569.java
--- a/test/langtools/tools/javap/T4975569.java
+++ b/test/langtools/tools/javap/T4975569.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 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.
@@ -24,28 +24,38 @@
 /*
  * @test
  * @bug 4975569 6622215 8034861
  * @summary javap doesn't print new flag bits
  * @modules jdk.jdeps/com.sun.tools.javap
+ * @modules java.base/jdk.internal.misc
+ * @run main/othervm --enable-preview T4975569
+ * @run main T4975569
  */
 
 import java.io.*;
 import java.util.*;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import jdk.internal.misc.PreviewFeatures;
+
 public class T4975569 {
     private static final String NEW_LINE = System.getProperty("line.separator");
     private static final String TEST_CLASSES = System.getProperty("test.classes", ".");
 
     public static void main(String... args) {
         new T4975569().run();
     }
 
     void run() {
         verify(Anno.class.getName(), "flags: \\(0x2600\\) ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION");
-        verify(E.class.getName(),    "flags: \\(0x4030\\) ACC_FINAL, ACC_SUPER, ACC_ENUM");
+        verify(E.class.getName(), PreviewFeatures.isEnabled()
+                ? "flags: \\(0x4030\\) ACC_FINAL, ACC_IDENTITY, ACC_ENUM"
+                : "flags: \\(0x4030\\) ACC_FINAL, ACC_SUPER, ACC_ENUM");
+        verify(E.class.getName(),    PreviewFeatures.isEnabled()
+                ? "flags: \\(0x4030\\) ACC_FINAL, ACC_IDENTITY, ACC_ENUM"
+                : "flags: \\(0x4030\\) ACC_FINAL, ACC_SUPER, ACC_ENUM");
         verify(S.class.getName(),    "flags: \\(0x1040\\) ACC_BRIDGE, ACC_SYNTHETIC",
                                      "InnerClasses:\n  static [# =\\w]+; +// ");
         verify(V.class.getName(),    "void m\\(java.lang.String...\\)",
                                      "flags: \\(0x0080\\) ACC_VARARGS");
         verify(Prot.class.getName(), "InnerClasses:\n  protected [# =\\w]+; +// ");
@@ -103,6 +113,5 @@
     }
 
     protected class Prot { }
     private class Priv { int i; }
 }
-
diff a/test/lib/jdk/test/lib/combo/ComboInstance.java b/test/lib/jdk/test/lib/combo/ComboInstance.java
--- /dev/null
+++ b/test/lib/jdk/test/lib/combo/ComboInstance.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2017, 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 jdk.test.lib.combo;
+
+import javax.tools.StandardJavaFileManager;
+import java.util.Optional;
+
+/**
+ * This class is the common superclass of all combo test instances. It defines few helper methods
+ * to build compilation tasks using the shared context object, as well as entry points for
+ * signalling test failures.
+ */
+public abstract class ComboInstance<X extends ComboInstance<X>> {
+
+    /** The test instance result status. */
+    private ResultStatus resultStatus = ResultStatus.PASSED;
+
+    /** The test instance execution environment. */
+    private ComboTestHelper<X>.Env env;
+
+    /**
+     * Entry point for executing a combo test instance; first, the test environment is saved
+     * in the corresponding field, then the instance is run (see {@link ComboInstance#doWork()}.
+     * During execution, the result status will be updated to match the test outcome.
+     */
+    final void run(ComboTestHelper<X>.Env env) {
+        try {
+            this.env = env;
+            doWork();
+            if (resultStatus.isSuccess()) {
+                env.info().passCount++;
+            }
+        } catch (Throwable ex) {
+            resultStatus = ResultStatus.ERROR;
+            env.info().errCount++;
+            env.info().lastError = Optional.of(ex);
+        } finally {
+            this.env = null;
+        }
+    }
+
+    /**
+     * Retrieve a unique ID associated with this test instance.
+     */
+    public int id() {
+        return env.info().comboCount;
+    }
+
+    /**
+     * Retrieve shared file manager.
+     */
+    public StandardJavaFileManager fileManager() {
+        return env.fileManager();
+    }
+
+    /**
+     * Create a new compilation task using shared compilation context.
+     */
+    protected ComboTask newCompilationTask() {
+        return new ComboTask(env);
+    }
+
+    /**
+     * Main test execution entry point; subclasses must implement this method to define the test
+     * logic.
+     */
+    protected abstract void doWork() throws Throwable;
+
+    /**
+     * Report a test failure.
+     */
+    protected void fail() {
+        //dump some default info (such as dimension bindings)
+        fail("Combo instance failed; " + env.bindings);
+    }
+
+    /**
+     * Report a test failure with corresponding failure message.
+     */
+    protected void fail(String msg) {
+        resultStatus = ResultStatus.FAILED;
+        env.info().failCount++;
+        env.info().lastFailure = Optional.of(msg);
+    }
+
+    /**
+     * The status associated with this test instance execution.
+     */
+    enum ResultStatus {
+        /** Test passed. */
+        PASSED(true),
+        /** Test failed. */
+        FAILED(false),
+        /** Test thrown unexpected error/exception. */
+        ERROR(false);
+
+        boolean success;
+
+        ResultStatus(boolean success) {
+            this.success = success;
+        }
+
+        boolean isSuccess() {
+            return success;
+        }
+    }
+}
diff a/test/lib/jdk/test/lib/combo/ComboParameter.java b/test/lib/jdk/test/lib/combo/ComboParameter.java
--- /dev/null
+++ b/test/lib/jdk/test/lib/combo/ComboParameter.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2017, 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 jdk.test.lib.combo;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A combo parameter represents an 'hole' in a template that can be replaced with a given string.
+ * The schema of such holes is defined in {@link ComboParameter#pattern}; the main routine for
+ * replacing holes in a template scheme is {@link ComboParameter#expandTemplate(String, Resolver)}.
+ */
+public interface ComboParameter {
+
+    /**
+     * A combo parameter can take the form:
+     * <p>
+     * #{MAJOR}
+     * #{MAJOR.}
+     * #{MAJOR.MINOR}
+     * <p>
+     * where MAJOR can be IDENTIFIER or IDENTIFIER[NUMERIC_INDEX]
+     * and MINOR can be an identifier.
+     */
+    Pattern pattern = Pattern.compile("#\\{([A-Z_][A-Z0-9_]*(?:\\[\\d+\\])?)(?:\\.([A-Z0-9_]*))?\\}");
+
+    /**
+     * Entry point for the customizable replacement logic. Subclasses must implement this method to
+     * specify how a given template hole should be expanded. An optional contextual argument is passed
+     * in as parameter, to make expansion more flexible.
+     */
+    String expand(String optParameter);
+
+    /**
+     * Helper class for defining 'constant' combo parameters - i.e. parameters that always expand
+     * as a given string value - regardless of the context.
+     */
+    class Constant<D> implements ComboParameter {
+
+        D data;
+
+        public Constant(D data) {
+            this.data = data;
+        }
+
+        @Override
+        public String expand(String _unused) {
+            return String.valueOf(data);
+        }
+    }
+
+    /**
+     * Helper interface used to lookup parameters given a parameter name.
+     */
+    interface Resolver {
+        ComboParameter lookup(String name);
+    }
+
+    /**
+     * Main routine for replacing holes in a template string. Holes are repeatedly searches, their
+     * corresponding parameters retrieved, and replaced through expansion; since an expansion can
+     * lead to more holes, the process has to be applied until a fixed point is reached.
+     */
+    static String expandTemplate(String template, Resolver resolver) {
+        CharSequence in = template;
+        StringBuffer out = new StringBuffer();
+        while (true) {
+            boolean more = false;
+            Matcher m = pattern.matcher(in);
+            while (m.find()) {
+                String parameterName = m.group(1);
+                String minor = m.group(2);
+                ComboParameter parameter = resolver.lookup(parameterName);
+                if (parameter == null) {
+                    throw new IllegalStateException("Unhandled parameter name " + parameterName);
+                }
+
+                String replacement = parameter.expand(minor);
+                more |= pattern.matcher(replacement).find();
+                m.appendReplacement(out, replacement);
+            }
+            m.appendTail(out);
+            if (!more)
+                return out.toString();
+            else {
+                in = out;
+                out = new StringBuffer();
+            }
+        }
+    }
+}
diff a/test/lib/jdk/test/lib/combo/ComboTask.java b/test/lib/jdk/test/lib/combo/ComboTask.java
--- /dev/null
+++ b/test/lib/jdk/test/lib/combo/ComboTask.java
@@ -0,0 +1,462 @@
+/*
+ * Copyright (c) 2015, 2017, 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 jdk.test.lib.combo;
+
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.TaskListener;
+import com.sun.tools.javac.api.JavacTool;
+import com.sun.tools.javac.util.Assert;
+import com.sun.tools.javac.util.List;
+import jdk.test.lib.combo.ComboParameter.Resolver;
+
+import javax.lang.model.element.Element;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+/**
+ * This class represents a compilation task associated with a combo test instance. This is a small
+ * wrapper around {@link JavacTask} which allows for fluent setup style and which makes use of
+ * the shared compilation context to speedup performances.
+ */
+public class ComboTask {
+
+    /** Sources to be compiled in this task. */
+    private List<JavaFileObject> sources = List.nil();
+
+    /** Options associated with this task. */
+    private List<String> options = List.nil();
+
+    /** Diagnostic collector. */
+    private DiagnosticCollector diagsCollector = new DiagnosticCollector();
+
+    /** Output writer. */
+    private Writer out;
+
+    /** Listeners associated with this task. */
+    private List<TaskListener> listeners = List.nil();
+
+    /** Underlying javac task object. */
+    private JavacTask task;
+
+    /** Combo execution environment. */
+    private ComboTestHelper<?>.Env env;
+
+    ComboTask(ComboTestHelper<?>.Env env) {
+        this.env = env;
+    }
+
+    /**
+     * Add a new source to this task.
+     */
+    public ComboTask withSource(JavaFileObject comboSource) {
+        sources = sources.prepend(comboSource);
+        return this;
+    }
+
+    /**
+     * Add a new template source with given name to this task; the template is replaced with
+     * corresponding combo parameters (as defined in the combo test environment).
+     */
+    public ComboTask withSourceFromTemplate(String name, String template) {
+        return withSource(new ComboTemplateSource(name, template));
+    }
+
+    /**
+     * Add a new template source with default name ("Test") to this task; the template is replaced with
+     * corresponding combo parameters (as defined in the combo test environment).
+     */
+    public ComboTask withSourceFromTemplate(String template) {
+        return withSource(new ComboTemplateSource("Test", template));
+    }
+
+    /**
+     * Add a new template source with given name to this task; the template is replaced with
+     * corresponding combo parameters (as defined in the combo test environment). A custom resolver
+     * is used to add combo parameter mappings to the current combo test environment.
+     */
+    public ComboTask withSourceFromTemplate(String name, String template, Resolver resolver) {
+        return withSource(new ComboTemplateSource(name, template, resolver));
+    }
+
+    /**
+     * Add a new template source with default name ("Test") to this task; the template is replaced with
+     * corresponding combo parameters (as defined in the combo test environment). A custom resolver
+     * is used to add combo parameter mappings to the current combo test environment.
+     */
+    public ComboTask withSourceFromTemplate(String template, Resolver resolver) {
+        return withSource(new ComboTemplateSource("Test", template, resolver));
+    }
+
+    /**
+     * Add a new option to this task.
+     */
+    public ComboTask withOption(String opt) {
+        options = options.append(opt);
+        return this;
+    }
+
+    /**
+     * Add a set of options to this task.
+     */
+    public ComboTask withOptions(String[] opts) {
+        for (String opt : opts) {
+            options = options.append(opt);
+        }
+        return this;
+    }
+
+    /**
+     * Add a set of options to this task.
+     */
+    public ComboTask withOptions(Iterable<? extends String> opts) {
+        for (String opt : opts) {
+            options = options.append(opt);
+        }
+        return this;
+    }
+
+    /**
+     * Set the output writer associated with this task.
+     */
+    public ComboTask withWriter(Writer out) {
+        this.out = out;
+        return this;
+    }
+
+    /**
+     * Add a task listener to this task.
+     */
+    public ComboTask withListener(TaskListener listener) {
+        listeners = listeners.prepend(listener);
+        return this;
+    }
+
+    /**
+     * Parse the sources associated with this task.
+     */
+    public Result<Iterable<? extends CompilationUnitTree>> parse() throws IOException {
+        return new Result<>(getTask().parse());
+    }
+
+    /**
+     * Parse and analyzes the sources associated with this task.
+     */
+    public Result<Iterable<? extends Element>> analyze() throws IOException {
+        return new Result<>(getTask().analyze());
+    }
+
+    /**
+     * Parse, analyze and perform code generation for the sources associated with this task.
+     */
+    public Result<Iterable<? extends JavaFileObject>> generate() throws IOException {
+        return new Result<>(getTask().generate());
+    }
+
+    /**
+     * Parse, analyze, perform code generation for the sources associated with this task and finally
+     * executes them
+     */
+    public <Z> Optional<Z> execute(Function<ExecutionTask, Z> executionFunc) throws IOException {
+        Result<Iterable<? extends JavaFileObject>> generationResult = generate();
+        Iterable<? extends JavaFileObject> jfoIterable = generationResult.get();
+        if (generationResult.hasErrors()) {
+            // we have nothing else to do
+            return Optional.empty();
+        }
+        java.util.List<URL> urlList = new ArrayList<>();
+        for (JavaFileObject jfo : jfoIterable) {
+            String urlStr = jfo.toUri().toURL().toString();
+            urlStr = urlStr.substring(0, urlStr.length() - jfo.getName().length());
+            urlList.add(new URL(urlStr));
+        }
+        return Optional.of(
+                executionFunc.apply(
+                        new ExecutionTask(new URLClassLoader(urlList.toArray(new URL[urlList.size()])))));
+    }
+
+    /**
+     * Fork a new compilation task; if possible the compilation context from previous executions is
+     * retained (see comments in ReusableContext as to when it's safe to do so); otherwise a brand
+     * new context is created.
+     */
+    public JavacTask getTask() {
+        if (task == null) {
+            ReusableContext context = env.context();
+            String opts = options == null ? "" :
+                    StreamSupport.stream(options.spliterator(), false).collect(Collectors.joining());
+            context.clear();
+            if (!context.polluted && (context.opts == null || context.opts.equals(opts))) {
+                //we can reuse former context
+                env.info().ctxReusedCount++;
+            } else {
+                env.info().ctxDroppedCount++;
+                //it's not safe to reuse context - create a new one
+                context = env.setContext(new ReusableContext());
+            }
+            context.opts = opts;
+            JavacTask javacTask = ((JavacTool)env.javaCompiler()).getTask(out, env.fileManager(),
+                    diagsCollector, options, null, sources, context);
+            javacTask.setTaskListener(context);
+            for (TaskListener l : listeners) {
+                javacTask.addTaskListener(l);
+            }
+            task = javacTask;
+        }
+        return task;
+    }
+
+    /**
+     * This class represents an execution task. It allows the execution of one or more classes previously
+     * added to a given class loader. This class uses reflection to execute any given static public method
+     * in any given class. It's not restricted to the execution of the {@code main} method
+     */
+    public class ExecutionTask {
+        private ClassLoader classLoader;
+        private String methodName = "main";
+        private Class<?>[] parameterTypes = new Class<?>[]{String[].class};
+        private Object[] args = new String[0];
+        private Consumer<Throwable> handler;
+        private Class<?> c;
+
+        private ExecutionTask(ClassLoader classLoader) {
+            this.classLoader = classLoader;
+        }
+
+        /**
+         * Set the name of the class to be loaded.
+         */
+        public ExecutionTask withClass(String className) {
+            Assert.check(className != null, "class name value is null, impossible to proceed");
+            try {
+                c = classLoader.loadClass(className);
+            } catch (Throwable t) {
+                throw new IllegalStateException(t);
+            }
+            return this;
+        }
+
+        /**
+         * Set the name of the method to be executed along with the parameter types to
+         * reflectively obtain the method.
+         */
+        public ExecutionTask withMethod(String methodName, Class<?>... parameterTypes) {
+            this.methodName = methodName;
+            this.parameterTypes = parameterTypes;
+            return this;
+        }
+
+        /**
+         * Set the arguments to be passed to the method.
+         */
+        public ExecutionTask withArguments(Object... args) {
+            this.args = args;
+            return this;
+        }
+
+        /**
+         * Set a handler to handle any exception thrown.
+         */
+        public ExecutionTask withHandler(Consumer<Throwable> handler) {
+            this.handler = handler;
+            return this;
+        }
+
+        /**
+         * Executes the given method in the given class. Returns true if the execution was
+         * successful, false otherwise.
+         */
+        public Object run() {
+            try {
+                java.lang.reflect.Method meth = c.getMethod(methodName, parameterTypes);
+                meth.invoke(null, (Object)args);
+                return true;
+            } catch (Throwable t) {
+                if (handler != null) {
+                    handler.accept(t);
+                }
+                return false;
+            }
+        }
+    }
+
+    /**
+     * This class is used to help clients accessing the results of a given compilation task.
+     * Contains several helper methods to inspect diagnostics generated during the task execution.
+     */
+    public class Result<D> {
+
+        /** The underlying compilation results. */
+        private final D data;
+
+        public Result(D data) {
+            this.data = data;
+        }
+
+        public D get() {
+            return data;
+        }
+
+        /**
+         * Did this task generate any error diagnostics?
+         */
+        public boolean hasErrors() {
+            return diagsCollector.diagsByKind.containsKey(Diagnostic.Kind.ERROR);
+        }
+
+        /**
+         * Did this task generate any warning diagnostics?
+         */
+        public boolean hasWarnings() {
+            return diagsCollector.diagsByKind.containsKey(Diagnostic.Kind.WARNING);
+        }
+
+        /**
+         * Did this task generate any note diagnostics?
+         */
+        public boolean hasNotes() {
+            return diagsCollector.diagsByKind.containsKey(Diagnostic.Kind.NOTE);
+        }
+
+        /**
+         * Did this task generate any diagnostic with given key?
+         */
+        public boolean containsKey(String key) {
+            return diagsCollector.diagsByKeys.containsKey(key);
+        }
+
+        /**
+         * Retrieve the list of diagnostics of a given kind.
+         */
+        public List<Diagnostic<? extends JavaFileObject>> diagnosticsForKind(Diagnostic.Kind kind) {
+            List<Diagnostic<? extends JavaFileObject>> diags = diagsCollector.diagsByKind.get(kind);
+            return diags != null ? diags : List.nil();
+        }
+
+        /**
+         * Retrieve the list of diagnostics with given key.
+         */
+        public List<Diagnostic<? extends JavaFileObject>> diagnosticsForKey(String key) {
+            List<Diagnostic<? extends JavaFileObject>> diags = diagsCollector.diagsByKeys.get(key);
+            return diags != null ? diags : List.nil();
+        }
+
+        /**
+         * Dump useful info associated with this task.
+         */
+        public String compilationInfo() {
+            return "instance#" + env.info().comboCount + ":[ options = " + options
+                    + ", diagnostics = " + diagsCollector.diagsByKeys.keySet()
+                    + ", dimensions = " + env.bindings
+                    + ", sources = \n" + sources.stream().map(s -> {
+                try {
+                    return s.getCharContent(true);
+                } catch (IOException ex) {
+                    return "";
+                }
+            }).collect(Collectors.joining(",")) + "]";
+        }
+    }
+
+    /**
+     * This class represents a Java source file whose contents are defined in terms of a template
+     * string. The holes in such template are expanded using corresponding combo parameter
+     * instances which can be retrieved using a resolver object.
+     */
+    class ComboTemplateSource extends SimpleJavaFileObject {
+
+        String source;
+        Map<String, ComboParameter> localParametersCache = new HashMap<>();
+
+        protected ComboTemplateSource(String name, String template) {
+            this(name, template, null);
+        }
+
+        protected ComboTemplateSource(String name, String template, Resolver resolver) {
+            super(URI.create("myfo:/" + env.info().comboCount + "/" + name + ".java"), Kind.SOURCE);
+            source = ComboParameter.expandTemplate(template, pname -> resolveParameter(pname, resolver));
+        }
+
+        @Override
+        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+            return source;
+        }
+
+        /**
+         * Combo parameter resolver function. First parameters are looked up in the global environment,
+         * then the local environment is looked up as a fallback.
+         */
+        ComboParameter resolveParameter(String pname, Resolver resolver) {
+            //first search the env
+            ComboParameter parameter = env.parametersCache.get(pname);
+            if (parameter == null) {
+                //then lookup local cache
+                parameter = localParametersCache.get(pname);
+                if (parameter == null && resolver != null) {
+                    //if still null and we have a custom resolution function, try that
+                    parameter = resolver.lookup(pname);
+                    if (parameter != null) {
+                       //if a match was found, store it in the local cache to aviod redundant recomputation
+                       localParametersCache.put(pname, parameter);
+                    }
+                }
+            }
+            return parameter;
+        }
+    }
+
+    /**
+     * Helper class to collect all diagnostic generated during the execution of a given compilation task.
+     */
+    class DiagnosticCollector implements DiagnosticListener<JavaFileObject> {
+
+        Map<Diagnostic.Kind, List<Diagnostic<? extends JavaFileObject>>> diagsByKind = new HashMap<>();
+        Map<String, List<Diagnostic<? extends JavaFileObject>>> diagsByKeys = new HashMap<>();
+
+        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+            List<Diagnostic<? extends JavaFileObject>> diags =
+                    diagsByKeys.getOrDefault(diagnostic.getCode(), List.nil());
+            diagsByKeys.put(diagnostic.getCode(), diags.prepend(diagnostic));
+            Diagnostic.Kind kind = diagnostic.getKind();
+            diags = diagsByKind.getOrDefault(kind, List.nil());
+            diagsByKind.put(kind, diags.prepend(diagnostic));
+        }
+    }
+}
diff a/test/lib/jdk/test/lib/combo/ComboTestHelper.java b/test/lib/jdk/test/lib/combo/ComboTestHelper.java
--- /dev/null
+++ b/test/lib/jdk/test/lib/combo/ComboTestHelper.java
@@ -0,0 +1,540 @@
+/*
+ * Copyright (c) 2015, 2017, 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 jdk.test.lib.combo;
+
+import javax.tools.JavaCompiler;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+
+import java.util.stream.Stream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Stack;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+
+/**
+ * An helper class for defining combinatorial (aka "combo" tests). A combo test is made up of one
+ * or more 'dimensions' - each of which represent a different axis of the test space. For instance,
+ * if we wanted to test class/interface declaration, one dimension could be the keyword used for
+ * the declaration (i.e. 'class' vs. 'interface') while another dimension could be the class/interface
+ * modifiers (i.e. 'public', 'pachake-private' etc.). A combo test consists in running a test instance
+ * for each point in the test space; that is, for any combination of the combo test dimension:
+ * <p>
+ * 'public' 'class'
+ * 'public' interface'
+ * 'package-private' 'class'
+ * 'package-private' 'interface'
+ * ...
+ * <p>
+ * A new test instance {@link ComboInstance} is created, and executed, after its dimensions have been
+ * initialized accordingly. Each instance can either pass, fail or throw an unexpected error; this helper
+ * class defines several policies for how failures should be handled during a combo test execution
+ * (i.e. should errors be ignored? Do we want the first failure to result in a failure of the whole
+ * combo test?).
+ * <p>
+ * Additionally, this helper class allows to specify filter methods that can be used to throw out
+ * illegal combinations of dimensions - for instance, in the example above, we might want to exclude
+ * all combinations involving 'protected' and 'private' modifiers, which are disallowed for toplevel
+ * declarations.
+ * <p>
+ * While combo tests can be used for a variety of workloads, typically their main task will consist
+ * in performing some kind of javac compilation. For this purpose, this framework defines an optimized
+ * javac context {@link ReusableContext} which can be shared across multiple combo instances,
+ * when the framework detects it's safe to do so. This allows to reduce the overhead associated with
+ * compiler initialization when the test space is big.
+ */
+public class ComboTestHelper<X extends ComboInstance<X>> {
+
+    /** Failure mode. */
+    FailMode failMode = FailMode.FAIL_FAST;
+
+    /** Ignore mode. */
+    IgnoreMode ignoreMode = IgnoreMode.IGNORE_NONE;
+
+    /** Combo test instance filter. */
+    Optional<Predicate<X>> optFilter = Optional.empty();
+
+    /** Combo test dimensions. */
+    List<DimensionInfo<?>> dimensionInfos = new ArrayList<>();
+
+    /** Combo test stats. */
+    Info info = new Info();
+
+    /** Shared JavaCompiler used across all combo test instances. */
+    JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
+
+    /** Shared file manager used across all combo test instances. */
+    StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
+
+    /** Shared context used across all combo instances. */
+    ReusableContext context = new ReusableContext();
+
+    /**
+     * Set failure mode for this combo test.
+     */
+    public ComboTestHelper<X> withFailMode(FailMode failMode) {
+        this.failMode = failMode;
+        return this;
+    }
+
+    /**
+     * Set ignore mode for this combo test.
+     */
+    public ComboTestHelper<X> withIgnoreMode(IgnoreMode ignoreMode) {
+        this.ignoreMode = ignoreMode;
+        return this;
+    }
+
+    /**
+     * Set a filter for combo test instances to be ignored.
+     */
+    public ComboTestHelper<X> withFilter(Predicate<X> filter) {
+        optFilter = Optional.of(optFilter.map(filter::and).orElse(filter));
+        return this;
+    }
+
+    /**
+     * Adds a new dimension to this combo test, with a given name an array of values.
+     */
+    @SafeVarargs
+    public final <D> ComboTestHelper<X> withDimension(String name, D... dims) {
+        return withDimension(name, null, dims);
+    }
+
+    /**
+     * Adds a new dimension to this combo test, with a given name, an array of values and a
+     * coresponding setter to be called in order to set the dimension value on the combo test instance
+     * (before test execution).
+     */
+    @SuppressWarnings("unchecked")
+    @SafeVarargs
+    public final <D> ComboTestHelper<X> withDimension(String name, DimensionSetter<X, D> setter, D... dims) {
+        dimensionInfos.add(new BasicDimensionInfo<>(name, dims, setter));
+        return this;
+    }
+
+    public enum ArrayDimensionKind {
+        PERMUTATIONS,
+        COMBINATIONS;
+    }
+
+    /**
+     * Adds a new array dimension to this combo test, with a given base name. This allows to specify
+     * multiple dimensions at once; the names of the underlying dimensions will be generated from the
+     * base name, using standard array bracket notation - i.e. "DIM[0]", "DIM[1]", etc.
+     */
+    @SafeVarargs
+    public final <D> ComboTestHelper<X> withArrayDimension(String name, int size, D... dims) {
+        return withArrayDimension(name, null, size, ArrayDimensionKind.PERMUTATIONS, dims);
+    }
+
+    @SafeVarargs
+    public final <D> ComboTestHelper<X> withArrayDimension(String name, int size, ArrayDimensionKind kind, D... dims) {
+        return withArrayDimension(name, null, size, kind, dims);
+    }
+
+    /**
+     * Adds a new array dimension to this combo test, with a given base name, an array of values and a
+     * coresponding array setter to be called in order to set the dimension value on the combo test
+     * instance (before test execution). This allows to specify multiple dimensions at once; the names
+     * of the underlying dimensions will be generated from the base name, using standard array bracket
+     * notation - i.e. "DIM[0]", "DIM[1]", etc.
+     */
+    @SafeVarargs
+    public final <D> ComboTestHelper<X> withArrayDimension(String name, ArrayDimensionSetter<X, D> setter, int size, D... dims) {
+        return withArrayDimension(name, setter, size, ArrayDimensionKind.PERMUTATIONS, dims);
+    }
+
+    @SafeVarargs
+    public final <D> ComboTestHelper<X> withArrayDimension(String name, ArrayDimensionSetter<X, D> setter, int size, ArrayDimensionKind kind, D... dims) {
+        if (kind == ArrayDimensionKind.PERMUTATIONS) {
+            for (int i = 0 ; i < size ; i++) {
+                dimensionInfos.add(new ArrayDimensionInfo<>(name, dims, i, setter));
+            }
+        } else {
+            DependentArrayDimensionInfo<D> prev = null;
+            for (int i = 0 ; i < size ; i++) {
+                prev = new DependentArrayDimensionInfo<>(prev, name, dims, i, setter);
+                dimensionInfos.add(prev);
+            }
+        }
+        return this;
+    }
+
+    /**
+     * Returns the stat object associated with this combo test.
+     */
+    public Info info() {
+        return info;
+    }
+
+    /**
+     * Runs this combo test. This will generate the combinatorial explosion of all dimensions, and
+     * execute a new test instance (built using given supplier) for each such combination.
+     */
+    public void run(Supplier<X> instanceBuilder) {
+        run(instanceBuilder, null);
+    }
+
+    /**
+     * Runs this combo test. This will generate the combinatorial explosion of all dimensions, and
+     * execute a new test instance (built using given supplier) for each such combination. Before
+     * executing the test instance entry point, the supplied initialization method is called on
+     * the test instance; this is useful for ad-hoc test instance initialization once all the dimension
+     * values have been set.
+     */
+    public void run(Supplier<X> instanceBuilder, Consumer<X> initAction) {
+        runInternal(0, new Stack<>(), instanceBuilder, Optional.ofNullable(initAction));
+        end();
+    }
+
+    /**
+     * Generate combinatorial explosion of all dimension values and create a new test instance
+     * for each combination.
+     */
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    private void runInternal(int index, Stack<DimensionBinding<?>> bindings, Supplier<X> instanceBuilder, Optional<Consumer<X>> initAction) {
+        if (index == dimensionInfos.size()) {
+            runCombo(instanceBuilder, initAction, bindings);
+        } else {
+            DimensionInfo<?> dinfo = dimensionInfos.get(index);
+            for (Object d : dinfo.dimensions(bindings)) {
+                bindings.push(new DimensionBinding(d, dinfo));
+                runInternal(index + 1, bindings, instanceBuilder, initAction);
+                bindings.pop();
+            }
+        }
+    }
+
+    /**
+     * Run a new test instance using supplied dimension bindings. All required setters and initialization
+     * method are executed before calling the instance main entry point. Also checks if the instance
+     * is compatible with the specified test filters; if not, the test is simply skipped.
+     */
+    @SuppressWarnings("unchecked")
+    private void runCombo(Supplier<X> instanceBuilder, Optional<Consumer<X>> initAction, List<DimensionBinding<?>> bindings) {
+        X x = instanceBuilder.get();
+        for (DimensionBinding<?> binding : bindings) {
+            binding.init(x);
+        }
+        initAction.ifPresent(action -> action.accept(x));
+        info.comboCount++;
+        if (!optFilter.isPresent() || optFilter.get().test(x)) {
+            x.run(new Env(bindings));
+            if (failMode.shouldStop(ignoreMode, info)) {
+                end();
+            }
+        } else {
+            info.skippedCount++;
+        }
+    }
+
+    /**
+     * This method is executed upon combo test completion (either normal or erroneous). Closes down
+     * all pending resources and dumps useful stats info.
+     */
+    private void end() {
+        try {
+            fm.close();
+            if (info.hasFailures()) {
+                throw new AssertionError("Failure when executing combo:" + info.lastFailure.orElse(""));
+            } else if (info.hasErrors()) {
+                throw new AssertionError("Unexpected exception while executing combo", info.lastError.get());
+            }
+        } catch (IOException ex) {
+            throw new AssertionError("Failure when closing down shared file manager; ", ex);
+        } finally {
+            info.dump();
+        }
+    }
+
+    /**
+     * Functional interface for specifying combo test instance setters.
+     */
+    public interface DimensionSetter<X extends ComboInstance<X>, D> {
+        void set(X x, D d);
+    }
+
+    /**
+     * Functional interface for specifying combo test instance array setters. The setter method
+     * receives an extra argument for the index of the array element to be set.
+     */
+    public interface ArrayDimensionSetter<X extends ComboInstance<X>, D> {
+        void set(X x, D d, int index);
+    }
+
+    /**
+     * Dimension descriptor; each dimension has a name, an array of value and an optional setter
+     * to be called on the associated combo test instance.
+     */
+    abstract class DimensionInfo<D> {
+        String name;
+        Optional<DimensionSetter<X, D>> optSetter;
+
+        DimensionInfo(String name, DimensionSetter<X, D> setter) {
+            this.name = name;
+            this.optSetter = Optional.ofNullable(setter);
+        }
+
+        abstract Iterable<D> dimensions(Stack<DimensionBinding<?>> bindings);
+    }
+
+    class BasicDimensionInfo<D> extends DimensionInfo<D> {
+        D[] dims;
+
+        BasicDimensionInfo(String name, D[] dims, DimensionSetter<X, D> setter) {
+            super(name, setter);
+            this.dims = dims;
+        }
+
+        @Override
+        public Iterable<D> dimensions(Stack<DimensionBinding<?>> bindings) {
+            return new ArrayIterable<D>(0, dims);
+        }
+    }
+
+    /**
+     * Array dimension descriptor. The dimension name is derived from a base name and an index using
+     * standard bracket notation; ; the setter accepts an additional 'index' argument to point
+     * to the array element to be initialized.
+     */
+    class ArrayDimensionInfo<D> extends BasicDimensionInfo<D> {
+        public ArrayDimensionInfo(String name, D[] dims, int index, ArrayDimensionSetter<X, D> setter) {
+            super(String.format("%s[%d]", name, index), dims,
+                    setter != null ? (x, d) -> setter.set(x, d, index) : null);
+        }
+    }
+
+    /**
+     * Dependent array dimension descriptor. The dimension name is derived from a base name and an index using
+     * standard bracket notation; ; the setter accepts an additional 'index' argument to point
+     * to the array element to be initialized.
+     */
+    class DependentArrayDimensionInfo<D> extends BasicDimensionInfo<D> {
+
+        DependentArrayDimensionInfo<D> prev;
+
+        public DependentArrayDimensionInfo(DependentArrayDimensionInfo<D> prev, String name, D[] dims, int index, ArrayDimensionSetter<X, D> setter) {
+            super(String.format("%s[%d]", name, index), dims,
+                    setter != null ? (x, d) -> setter.set(x, d, index) : null);
+            this.prev = prev;
+        }
+
+        @Override
+        public Iterable<D> dimensions(Stack<DimensionBinding<?>> bindings) {
+            if (bindings.peek().info == prev) {
+                Object d = bindings.peek().d;
+                for (int i = 0 ; i < dims.length ; i++) {
+                    if (dims[i] == d) {
+                        return new ArrayIterable<>(i, dims);
+                    }
+                }
+                throw new IllegalStateException("BAD");
+            } else {
+                return super.dimensions(bindings);
+            }
+        }
+    }
+
+    static class ArrayIterable<D> implements Iterable<D> {
+        int start;
+        D[] dims;
+
+        public ArrayIterable(int start, D[] dims) {
+            this.start = start;
+            this.dims = dims;
+        }
+
+        public Iterator<D> iterator() {
+           return new Iterator<D>() {
+                private int curr = start;
+
+                public boolean hasNext() {
+                   return dims.length > curr;
+                }
+
+                public D next() {
+                   return dims[curr++];
+                }
+            };
+        }
+    }
+
+    /**
+     * Failure policies for a combo test run.
+     */
+    public enum FailMode {
+        /** Combo test fails when first failure is detected. */
+        FAIL_FAST,
+        /** Combo test fails after all instances have been executed. */
+        FAIL_AFTER;
+
+        boolean shouldStop(IgnoreMode ignoreMode, Info info) {
+            switch (this) {
+                case FAIL_FAST:
+                    return !ignoreMode.canIgnore(info);
+                default:
+                    return false;
+            }
+        }
+    }
+
+    /**
+     * Ignore policies for a combo test run.
+     */
+    public enum IgnoreMode {
+        /** No error or failure is ignored. */
+        IGNORE_NONE,
+        /** Only errors are ignored. */
+        IGNORE_ERRORS,
+        /** Only failures are ignored. */
+        IGNORE_FAILURES,
+        /** Both errors and failures are ignored. */
+        IGNORE_ALL;
+
+        boolean canIgnore(Info info) {
+            switch (this) {
+                case IGNORE_ERRORS:
+                    return info.failCount == 0;
+                case IGNORE_FAILURES:
+                    return info.errCount == 0;
+                case IGNORE_ALL:
+                    return true;
+                default:
+                    return info.failCount == 0 && info.errCount == 0;
+            }
+        }
+    }
+
+    /**
+     * A dimension binding. This is essentially a pair of a dimension value and its corresponding
+     * dimension info.
+     */
+    class DimensionBinding<D> {
+        D d;
+        DimensionInfo<D> info;
+
+        DimensionBinding(D d, DimensionInfo<D> info) {
+            this.d = d;
+            this.info = info;
+        }
+
+        void init(X x) {
+            info.optSetter.ifPresent(setter -> setter.set(x, d));
+        }
+
+        public String toString() {
+            return String.format("(%s -> %s)", info.name, d);
+        }
+    }
+
+    /**
+     * This class is used to keep track of combo tests stats; info such as numbero of failures/errors,
+     * number of times a context has been shared/dropped are all recorder here.
+     */
+    public static class Info {
+        int failCount;
+        int errCount;
+        int passCount;
+        int comboCount;
+        int skippedCount;
+        int ctxReusedCount;
+        int ctxDroppedCount;
+        Optional<String> lastFailure = Optional.empty();
+        Optional<Throwable> lastError = Optional.empty();
+
+        void dump() {
+            System.err.println(String.format("%d total checks executed", comboCount));
+            System.err.println(String.format("%d successes found", passCount));
+            System.err.println(String.format("%d failures found", failCount));
+            System.err.println(String.format("%d errors found", errCount));
+            System.err.println(String.format("%d skips found", skippedCount));
+            System.err.println(String.format("%d contexts shared", ctxReusedCount));
+            System.err.println(String.format("%d contexts dropped", ctxDroppedCount));
+        }
+
+        public int getComboCount() { return comboCount; }
+
+        public boolean hasFailures() {
+            return failCount != 0;
+        }
+
+        public boolean hasErrors() {
+            return errCount != 0;
+        }
+    }
+
+    /**
+     * THe execution environment for a given combo test instance. An environment contains the
+     * bindings for all the dimensions, along with the combo parameter cache (this is non-empty
+     * only if one or more dimensions are subclasses of the {@code ComboParameter} interface).
+     */
+    class Env {
+        List<DimensionBinding<?>> bindings;
+        Map<String, ComboParameter> parametersCache = new HashMap<>();
+
+        @SuppressWarnings({"Unchecked", "rawtypes"})
+        Env(List<DimensionBinding<?>> bindings) {
+            this.bindings = bindings;
+            for (DimensionBinding<?> binding : bindings) {
+                if (binding.d instanceof ComboParameter) {
+                    parametersCache.put(binding.info.name, (ComboParameter)binding.d);
+                };
+            }
+        }
+
+        Info info() {
+            return ComboTestHelper.this.info();
+        }
+
+        StandardJavaFileManager fileManager() {
+            return fm;
+        }
+
+        JavaCompiler javaCompiler() {
+            return comp;
+        }
+
+        ReusableContext context() {
+            return context;
+        }
+
+        ReusableContext setContext(ReusableContext context) {
+            return ComboTestHelper.this.context = context;
+        }
+    }
+}
+
+
+
diff a/test/lib/jdk/test/lib/combo/ReusableContext.java b/test/lib/jdk/test/lib/combo/ReusableContext.java
--- /dev/null
+++ b/test/lib/jdk/test/lib/combo/ReusableContext.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2015, 2017, 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 jdk.test.lib.combo;
+
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.TaskEvent;
+import com.sun.source.util.TaskEvent.Kind;
+import com.sun.source.util.TaskListener;
+import com.sun.source.util.TreeScanner;
+import com.sun.tools.javac.api.MultiTaskListener;
+import com.sun.tools.javac.code.Kinds;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.Type.ClassType;
+import com.sun.tools.javac.code.TypeTag;
+import com.sun.tools.javac.code.Types;
+import com.sun.tools.javac.comp.Annotate;
+import com.sun.tools.javac.comp.Check;
+import com.sun.tools.javac.comp.CompileStates;
+import com.sun.tools.javac.comp.Enter;
+import com.sun.tools.javac.comp.Modules;
+import com.sun.tools.javac.main.Arguments;
+import com.sun.tools.javac.main.JavaCompiler;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.Log;
+
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A reusable context is a context that can be used safely across multiple compilation rounds
+ * arising from execution of a combo test. It achieves reuse by replacing some components
+ * (most notably JavaCompiler and Log) with reusable counterparts, and by exposing a method
+ * to cleanup leftovers from previous compilation.
+ * <p>
+ * There are, however, situations in which reusing the context is not safe: (i) when different
+ * compilations are using different sets of compiler options (as most option values are cached
+ * inside components themselves) and (ii) when the compilation unit happens to redefine classes
+ * in the java.* packages.
+ */
+class ReusableContext extends Context implements TaskListener {
+
+    Set<CompilationUnitTree> roots = new HashSet<>();
+
+    String opts;
+    boolean polluted = false;
+
+    ReusableContext() {
+        super();
+        put(Log.logKey, ReusableLog.factory);
+        put(JavaCompiler.compilerKey, ReusableJavaCompiler.factory);
+    }
+
+    void clear() {
+        drop(Arguments.argsKey);
+        drop(DiagnosticListener.class);
+        drop(Log.outKey);
+        drop(Log.errKey);
+        drop(JavaFileManager.class);
+        drop(JavacTask.class);
+
+        if (ht.get(Log.logKey) instanceof ReusableLog) {
+            //log already inited - not first round
+            ((ReusableLog)Log.instance(this)).clear();
+            Enter.instance(this).newRound();
+            ((ReusableJavaCompiler)ReusableJavaCompiler.instance(this)).clear();
+            Types.instance(this).newRound();
+            Check.instance(this).newRound();
+            Modules.instance(this).newRound();
+            Annotate.instance(this).newRound();
+            CompileStates.instance(this).clear();
+            MultiTaskListener.instance(this).clear();
+
+            //find if any of the roots have redefined java.* classes
+            Symtab syms = Symtab.instance(this);
+            pollutionScanner.scan(roots, syms);
+            roots.clear();
+        }
+    }
+
+    /**
+     * This scanner detects as to whether the shared context has been polluted. This happens
+     * whenever a compiled program redefines a core class (in 'java.*' package) or when
+     * (typically because of cyclic inheritance) the symbol kind of a core class has been touched.
+     */
+    TreeScanner<Void, Symtab> pollutionScanner = new TreeScanner<Void, Symtab>() {
+        @Override
+        public Void visitClass(ClassTree node, Symtab syms) {
+            Symbol sym = ((JCClassDecl)node).sym;
+            if (sym != null) {
+                syms.removeClass(sym.packge().modle, sym.flatName());
+                Type sup = supertype(sym);
+                if (isCoreClass(sym) ||
+                        (sup != null && isCoreClass(sup.tsym) && sup.tsym.kind != Kinds.Kind.TYP)) {
+                    polluted = true;
+                }
+            }
+            return super.visitClass(node, syms);
+        }
+
+        private boolean isCoreClass(Symbol s) {
+            return s.flatName().toString().startsWith("java.");
+        }
+
+        private Type supertype(Symbol s) {
+            if (s.type == null ||
+                    !s.type.hasTag(TypeTag.CLASS)) {
+                return null;
+            } else {
+                ClassType ct = (ClassType)s.type;
+                return ct.supertype_field;
+            }
+        }
+    };
+
+    @Override
+    public void finished(TaskEvent e) {
+        if (e.getKind() == Kind.PARSE) {
+            roots.add(e.getCompilationUnit());
+        }
+    }
+
+    @Override
+    public void started(TaskEvent e) {
+        //do nothing
+    }
+
+    <T> void drop(Key<T> k) {
+        ht.remove(k);
+    }
+
+    <T> void drop(Class<T> c) {
+        ht.remove(key(c));
+    }
+
+    /**
+     * Reusable JavaCompiler; exposes a method to clean up the component from leftovers associated with
+     * previous compilations.
+     */
+    static class ReusableJavaCompiler extends JavaCompiler {
+
+        static Factory<JavaCompiler> factory = ReusableJavaCompiler::new;
+
+        ReusableJavaCompiler(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void close() {
+            //do nothing
+        }
+
+        void clear() {
+            newRound();
+        }
+
+        @Override
+        protected void checkReusable() {
+            //do nothing - it's ok to reuse the compiler
+        }
+    }
+
+    /**
+     * Reusable Log; exposes a method to clean up the component from leftovers associated with
+     * previous compilations.
+     */
+    static class ReusableLog extends Log {
+
+        static Factory<Log> factory = ReusableLog::new;
+
+        Context context;
+
+        ReusableLog(Context context) {
+            super(context);
+            this.context = context;
+        }
+
+        void clear() {
+            recorded.clear();
+            sourceMap.clear();
+            nerrors = 0;
+            nwarnings = 0;
+            //Set a fake listener that will lazily lookup the context for the 'real' listener. Since
+            //this field is never updated when a new task is created, we cannot simply reset the field
+            //or keep old value. This is a hack to workaround the limitations in the current infrastructure.
+            diagListener = new DiagnosticListener<JavaFileObject>() {
+                DiagnosticListener<JavaFileObject> cachedListener;
+
+                @Override
+                @SuppressWarnings("unchecked")
+                public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+                    if (cachedListener == null) {
+                        cachedListener = context.get(DiagnosticListener.class);
+                    }
+                    cachedListener.report(diagnostic);
+                }
+            };
+        }
+    }
+}
diff a/test/lib/jdk/test/lib/hprof/model/InlinedJavaField.java b/test/lib/jdk/test/lib/hprof/model/InlinedJavaField.java
--- /dev/null
+++ b/test/lib/jdk/test/lib/hprof/model/InlinedJavaField.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2022, 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.
+ */
+
+package jdk.test.lib.hprof.model;
+
+public class InlinedJavaField extends JavaField {
+
+    private final JavaClass inlinedFieldClass;
+
+    public InlinedJavaField(String name, String signature, JavaClass inlinedFieldClass) {
+        super(name, signature);
+        this.inlinedFieldClass = inlinedFieldClass;
+    }
+
+    @Override
+    public boolean hasId() {
+        return false;
+    }
+
+    public JavaClass getInlinedFieldClass() {
+        return inlinedFieldClass;
+    }
+
+}
diff a/test/lib/jdk/test/lib/hprof/model/InlinedJavaObject.java b/test/lib/jdk/test/lib/hprof/model/InlinedJavaObject.java
--- /dev/null
+++ b/test/lib/jdk/test/lib/hprof/model/InlinedJavaObject.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2022, 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.
+ */
+
+package jdk.test.lib.hprof.model;
+
+import java.io.IOException;
+
+/**
+ * Represents Inlined Java object
+ */
+public class InlinedJavaObject extends JavaObject {
+
+    /**
+     * Construct a new InlinedJavaObject.
+     *
+     * @param clazz the class object
+     * @param offset The offset of field data
+     */
+    public InlinedJavaObject(JavaClass clazz, long offset) {
+        super(clazz, offset);
+    }
+
+    @Override
+    public String toString() {
+        return "inlined " + super.toString();
+    }
+
+    @Override
+    protected final long readValueLength() throws IOException {
+        // TODO: revise - clazz.getInlinedInstanceSize()?
+        return 0;
+    }
+
+    @Override
+    protected long dataStartOffset() {
+        return getOffset();
+    }
+
+}
diff a/test/lib/jdk/test/lib/hprof/model/JavaClass.java b/test/lib/jdk/test/lib/hprof/model/JavaClass.java
--- a/test/lib/jdk/test/lib/hprof/model/JavaClass.java
+++ b/test/lib/jdk/test/lib/hprof/model/JavaClass.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2022, 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.
@@ -71,10 +71,13 @@
     // Size of an instance, including VM overhead
     private int instanceSize;
     // Total number of fields including inherited ones
     private int totalNumFields;
 
+    // Size of the instance in the object of the class is inlined.
+    // Calculated lazily.
+    private int inlinedInstanceSize = -1;
 
     public JavaClass(long id, String name, long superclassId, long loaderId,
                      long signersId, long protDomainId,
                      JavaField[] fields, JavaStatic[] statics,
                      int instanceSize) {
@@ -112,10 +115,50 @@
     public void resolve(Snapshot snapshot) {
         if (mySnapshot != null) {
             return;
         }
         mySnapshot = snapshot;
+
+        // Resolve inlined fields. Should be done before resolveSuperclass for correct field counting
+        Snapshot.ClassInlinedFields[] inlinedFields = snapshot.findClassInlinedFields(id);
+        if (inlinedFields != null) {
+            int newCount = fields.length;
+            for (Snapshot.ClassInlinedFields f: inlinedFields) {
+                if (f.synthFieldCount == 0) {
+                    // Empty primitive class. just skip it - no data there.
+                    continue;
+                }
+                JavaHeapObject clazz = snapshot.findThing(f.fieldClassID);
+                if (clazz instanceof JavaClass fieldClass) {
+                    fieldClass.resolve(snapshot);
+
+                    // Set new field.
+                    fields[f.fieldIndex] = new InlinedJavaField(f.fieldName, 'Q' + fieldClass.getName() + ';', fieldClass);
+                    newCount -= (f.synthFieldCount - 1);
+                    // Reset invalid fields.
+                    for (int i = 1; i < f.synthFieldCount; i++) {
+                        fields[f.fieldIndex + i] = null;
+                    }
+                } else {
+                    // The field class not found.
+                    System.out.println("WARNING: class of inlined field not found:" + getName() + "." + f.fieldName);
+                }
+            }
+
+            // Set new fields.
+            JavaField[] newFields = new JavaField[newCount];
+            int oldIndex = 0;
+            for (int i = 0; i < newFields.length; i++) {
+                while (fields[oldIndex] == null) {
+                    oldIndex++;
+                }
+                newFields[i] = fields[oldIndex];
+                oldIndex++;
+            }
+            fields = newFields;
+        }
+
         resolveSuperclass(snapshot);
         if (superclass != null) {
             ((JavaClass) superclass).addSubclass(this);
         }
 
@@ -126,10 +169,11 @@
         for (int i = 0; i < statics.length; i++) {
             statics[i].resolve(this, snapshot);
         }
         snapshot.getJavaLangClass().addInstance(this);
         super.resolve(snapshot);
+
         return;
     }
 
     /**
      * Resolve our superclass.  This might be called well before
@@ -375,10 +419,48 @@
      */
     public int getInstanceSize() {
         return instanceSize + mySnapshot.getMinimumObjectSize();
     }
 
+    public int getInlinedInstanceSize() {
+        if (inlinedInstanceSize < 0) {
+            int size = 0;
+            for (JavaField f: fields) {
+                if (f instanceof InlinedJavaField inlinedField) {
+                    size += inlinedField.getInlinedFieldClass().getInlinedInstanceSize();
+                } else {
+                    char sig = f.getSignature().charAt(0);
+                    switch (sig) {
+                        case 'L':
+                        case '[':
+                            size += mySnapshot.getIdentifierSize();
+                            break;
+                        case 'B':
+                        case 'Z':
+                            size += 1;
+                            break;
+                        case 'C':
+                        case 'S':
+                            size += 2;
+                            break;
+                        case 'I':
+                        case 'F':
+                            size += 4;
+                            break;
+                        case 'J':
+                        case 'D':
+                            size += 8;
+                            break;
+                        default:
+                            throw new RuntimeException("unknown field type: " + sig);
+                    }
+                }
+            }
+            inlinedInstanceSize = size;
+        }
+        return inlinedInstanceSize;
+    }
 
     /**
      * @return The size of all instances of this class.  Correctly handles
      *          arrays.
      */
diff a/test/lib/jdk/test/lib/hprof/model/JavaObject.java b/test/lib/jdk/test/lib/hprof/model/JavaObject.java
--- a/test/lib/jdk/test/lib/hprof/model/JavaObject.java
+++ b/test/lib/jdk/test/lib/hprof/model/JavaObject.java
@@ -40,19 +40,24 @@
  */
 public class JavaObject extends JavaLazyReadObject {
 
     private Object clazz;       // Number before resolve
                                 // JavaClass after resolve
+
+    public JavaObject(Object clazz, long offset) {
+        super(offset);
+        this.clazz = clazz;
+    }
+
     /**
      * Construct a new JavaObject.
      *
      * @param classID id of the class object
      * @param offset The offset of field data
      */
     public JavaObject(long classID, long offset) {
-        super(offset);
-        this.clazz = makeId(classID);
+        this(makeId(classID), offset);
     }
 
     public void resolve(Snapshot snapshot) {
         if (clazz instanceof JavaClass) {
             return;
@@ -209,21 +214,21 @@
      *     class ID
      *     data length (int)
      *     byte[length]
      */
     @Override
-    protected final long readValueLength() throws IOException {
+    protected long readValueLength() throws IOException {
         long lengthOffset = getOffset() + 2 * idSize() + 4;
         return buf().getInt(lengthOffset);
     }
 
     @Override
     protected final JavaThing[] readValue() throws IOException {
         return parseFields(false);
     }
 
-    private long dataStartOffset() {
+    protected long dataStartOffset() {
         return getOffset() + idSize() + 4 + idSize() + 4;
     }
 
     private JavaThing[] parseFields(boolean verbose) {
         JavaClass cl = getClazz();
@@ -253,75 +258,81 @@
                 target -= fields.length;
             }
             JavaField f = fields[fieldNo];
             char sig = f.getSignature().charAt(0);
             try {
-                switch (sig) {
-                    case 'L':
-                    case '[': {
-                        long id = objectIdAt(offset);
-                        offset += idSize();
-                        JavaObjectRef ref = new JavaObjectRef(id);
-                        fieldValues[target+fieldNo] = ref.dereference(snapshot, f, verbose);
-                        break;
-                    }
-                    case 'Z': {
-                        byte value = byteAt(offset);
-                        offset++;
-                        fieldValues[target+fieldNo] = new JavaBoolean(value != 0);
-                        break;
-                    }
-                    case 'B': {
-                        byte value = byteAt(offset);
-                        offset++;
-                        fieldValues[target+fieldNo] = new JavaByte(value);
-                        break;
-                    }
-                    case 'S': {
-                        short value = shortAt(offset);
-                        offset += 2;
-                        fieldValues[target+fieldNo] = new JavaShort(value);
-                        break;
-                    }
-                    case 'C': {
-                        char value = charAt(offset);
-                        offset += 2;
-                        fieldValues[target+fieldNo] = new JavaChar(value);
-                        break;
-                    }
-                    case 'I': {
-                        int value = intAt(offset);
-                        offset += 4;
-                        fieldValues[target+fieldNo] = new JavaInt(value);
-                        break;
-                    }
-                    case 'J': {
-                        long value = longAt(offset);
-                        offset += 8;
-                        fieldValues[target+fieldNo] = new JavaLong(value);
-                        break;
-                    }
-                    case 'F': {
-                        float value = floatAt(offset);
-                        offset += 4;
-                        fieldValues[target+fieldNo] = new JavaFloat(value);
-                        break;
-                    }
-                    case 'D': {
-                        double value = doubleAt(offset);
-                        offset += 8;
-                        fieldValues[target+fieldNo] = new JavaDouble(value);
-                        break;
-                    }
-                    default:
-                        throw new RuntimeException("invalid signature: " + sig);
+                if (f instanceof InlinedJavaField inlinedField) {
+                    JavaClass fieldClass = inlinedField.getInlinedFieldClass();
+                    fieldValues[target+fieldNo] = new InlinedJavaObject(fieldClass, offset);
+                    offset += fieldClass.getInlinedInstanceSize();
+                } else {
+                    switch (sig) {
+                        case 'L':
+                        case '[': {
+                            long id = objectIdAt(offset);
+                            offset += idSize();
+                            JavaObjectRef ref = new JavaObjectRef(id);
+                            fieldValues[target + fieldNo] = ref.dereference(snapshot, f, verbose);
+                            break;
+                        }
+                        case 'Z': {
+                            byte value = byteAt(offset);
+                            offset++;
+                            fieldValues[target + fieldNo] = new JavaBoolean(value != 0);
+                            break;
+                        }
+                        case 'B': {
+                            byte value = byteAt(offset);
+                            offset++;
+                            fieldValues[target + fieldNo] = new JavaByte(value);
+                            break;
+                        }
+                        case 'S': {
+                            short value = shortAt(offset);
+                            offset += 2;
+                            fieldValues[target + fieldNo] = new JavaShort(value);
+                            break;
+                        }
+                        case 'C': {
+                            char value = charAt(offset);
+                            offset += 2;
+                            fieldValues[target + fieldNo] = new JavaChar(value);
+                            break;
+                        }
+                        case 'I': {
+                            int value = intAt(offset);
+                            offset += 4;
+                            fieldValues[target + fieldNo] = new JavaInt(value);
+                            break;
+                        }
+                        case 'J': {
+                            long value = longAt(offset);
+                            offset += 8;
+                            fieldValues[target + fieldNo] = new JavaLong(value);
+                            break;
+                        }
+                        case 'F': {
+                            float value = floatAt(offset);
+                            offset += 4;
+                            fieldValues[target + fieldNo] = new JavaFloat(value);
+                            break;
+                        }
+                        case 'D': {
+                            double value = doubleAt(offset);
+                            offset += 8;
+                            fieldValues[target + fieldNo] = new JavaDouble(value);
+                            break;
+                        }
+                        default:
+                            throw new RuntimeException("invalid signature: " + sig);
 
+                    }
                 }
-        } catch (IOException exp) {
-            System.err.println("lazy read failed at offset " + offset);
-            exp.printStackTrace();
-            return Snapshot.EMPTY_JAVATHING_ARRAY;
+            } catch (IOException exp) {
+                System.err.println("lazy read failed at offset " + offset);
+                exp.printStackTrace();
+                return Snapshot.EMPTY_JAVATHING_ARRAY;
             }
         }
         return fieldValues;
     }
 
diff a/test/lib/jdk/test/lib/hprof/model/JavaValueArray.java b/test/lib/jdk/test/lib/hprof/model/JavaValueArray.java
--- a/test/lib/jdk/test/lib/hprof/model/JavaValueArray.java
+++ b/test/lib/jdk/test/lib/hprof/model/JavaValueArray.java
@@ -32,40 +32,18 @@
 
 import java.io.IOException;
 import java.util.Objects;
 
 /**
- * An array of values, that is, an array of ints, boolean, floats or the like.
+ * An array of values, that is, an array of ints, boolean, floats, etc.
+ * or flat array of primitive objects.
  *
  * @author      Bill Foote
  */
 public class JavaValueArray extends JavaLazyReadObject
                 /*imports*/ implements ArrayTypeCodes {
 
-    private static String arrayTypeName(byte sig) {
-        switch (sig) {
-            case 'B':
-                return "byte[]";
-            case 'Z':
-                return "boolean[]";
-            case 'C':
-                return "char[]";
-            case 'S':
-                return "short[]";
-            case 'I':
-                return "int[]";
-            case 'F':
-                return "float[]";
-            case 'J':
-                return "long[]";
-            case 'D':
-                return "double[]";
-            default:
-                throw new RuntimeException("invalid array element sig: " + sig);
-        }
-    }
-
     private static int elementSize(byte type) {
         switch (type) {
             case 'B':
             case 'Z':
                 return 1;
@@ -97,11 +75,11 @@
     protected final long readValueLength() throws IOException {
         long offset = getOffset() + idSize() + 4;
         // length of the array in elements
         long len = buf().getInt(offset);
         // byte length of array
-        return len * elementSize(getElementType());
+        return len * elementSize(getRealElementType());
     }
 
     private long dataStartOffset() {
         return getOffset() + idSize() + 4 + 4 + 1;
     }
@@ -169,20 +147,29 @@
                                   res[i] = new JavaDouble(doubleAt(offset));
                                   offset += 8;
                               }
                               return res;
                 }
+                case 'Q': {
+                    for (int i = 0; i < len; i++) {
+                        res[i] = new InlinedJavaObject(flatArrayElementClass, offset);
+                        offset += flatArrayElementClass.getInlinedInstanceSize();
+                    }
+                    return res;
+                }
                 default: {
                              throw new RuntimeException("unknown primitive type?");
                 }
             }
         }
     }
 
     // JavaClass set only after resolve.
     private JavaClass clazz;
 
+    private long objID;
+
     // This field contains elementSignature byte and
     // divider to be used to calculate length. Note that
     // length of content byte[] is not same as array length.
     // Actual array length is (byte[].length / divider)
     private int data;
@@ -194,32 +181,58 @@
     private static final int LENGTH_DIVIDER_MASK = 0x0FF00;
 
     // Number of bits to shift to get length divider
     private static final int LENGTH_DIVIDER_SHIFT = 8;
 
-    public JavaValueArray(byte elementSignature, long offset) {
+    // Flat array support.
+    private JavaClass flatArrayElementClass;
+
+    public JavaValueArray(long id, byte elementSignature, long offset) {
         super(offset);
+        this.objID = id;
         this.data = (elementSignature & SIGNATURE_MASK);
     }
 
     public JavaClass getClazz() {
         return clazz;
     }
 
+    public boolean isFlatArray() {
+        return flatArrayElementClass != null;
+    }
+
+    public JavaClass getFlatElementClazz() {
+        return flatArrayElementClass;
+    }
+
     public void visitReferencedObjects(JavaHeapObjectVisitor v) {
         super.visitReferencedObjects(v);
     }
 
     public void resolve(Snapshot snapshot) {
         if (clazz instanceof JavaClass) {
             return;
         }
-        byte elementSig = getElementType();
-        clazz = snapshot.findClass(arrayTypeName(elementSig));
-        if (clazz == null) {
-            clazz = snapshot.getArrayClass("" + ((char) elementSig));
+
+        byte elementType = getElementType();
+        String elementSig = "" + (char)elementType;
+        // Check if this is a flat array of primitive objects.
+        Number elementClassID = snapshot.findFlatArrayElementType(objID);
+        if (elementClassID != null) {
+            // This is flat array.
+            JavaHeapObject elementClazz = snapshot.findThing(getIdValue(elementClassID));
+            if (elementClazz instanceof JavaClass elementJavaClazz) {
+                flatArrayElementClass = elementJavaClazz;
+                // need to resolve the element class
+                flatArrayElementClass.resolve(snapshot);
+                elementSig = "Q" + flatArrayElementClass.getName() + ";";
+            } else {
+                // The class not found.
+                System.out.println("WARNING: flat array element class not found");
+            }
         }
+        clazz = snapshot.getArrayClass(elementSig);
         getClazz().addInstance(this);
         super.resolve(snapshot);
     }
 
     public int getLength() {
@@ -241,10 +254,13 @@
                 break;
             case 'J':
             case 'D':
                 divider = 8;
                 break;
+            case 'Q':
+                divider = flatArrayElementClass.getInlinedInstanceSize();
+                break;
             default:
                 throw new RuntimeException("unknown primitive type: " +
                                 elementSignature);
             }
             data |= (divider << LENGTH_DIVIDER_SHIFT);
@@ -255,10 +271,14 @@
     public JavaThing[] getElements() {
         return getValue();
     }
 
     public byte getElementType() {
+        return isFlatArray() ? (byte)'Q' : getRealElementType();
+    }
+
+    private byte getRealElementType() {
         return (byte) (data & SIGNATURE_MASK);
     }
 
     private void checkIndex(int index) {
         Objects.checkIndex(index, getLength());
@@ -277,11 +297,11 @@
     public String valueString(boolean bigLimit) {
         // Char arrays deserve special treatment
         StringBuilder result;
         JavaThing[] things = getValue();
         byte elementSignature = getElementType();
-        if (elementSignature == 'C')  {
+        if (elementSignature == 'C' && !isFlatArray())  {
             result = new StringBuilder();
             for (int i = 0; i < things.length; i++) {
                 result.append(things[i]);
             }
         } else {
diff a/test/lib/jdk/test/lib/hprof/model/Snapshot.java b/test/lib/jdk/test/lib/hprof/model/Snapshot.java
--- a/test/lib/jdk/test/lib/hprof/model/Snapshot.java
+++ b/test/lib/jdk/test/lib/hprof/model/Snapshot.java
@@ -67,10 +67,14 @@
 
     // name-to-class map
     private Map<String, JavaClass> classes =
                  new TreeMap<String, JavaClass>();
 
+    private Map<Number, Number> flatArrays = new HashMap<>();
+
+    private Map<Number, ClassInlinedFields[]> inlinedFields = new HashMap<>();
+
     // new objects relative to a baseline - lazily initialized
     private volatile Map<JavaHeapObject, Boolean> newObjects;
 
     // allocation site traces for all objects - lazily initialized
     private volatile Map<JavaHeapObject, StackTrace> siteTraces;
@@ -207,10 +211,45 @@
         // Add the class
         addFakeClass(makeId(classID), c);
         return c;
     }
 
+    public void addFlatArray(long objID, long elementClassID) {
+        flatArrays.put(makeId(objID), makeId(elementClassID));
+    }
+
+    /**
+     * @return null if the array is not flat array
+     */
+    Number findFlatArrayElementType(long arrayObjectID) {
+        return flatArrays.get(makeId(arrayObjectID));
+    }
+
+    public static class ClassInlinedFields {
+        final int fieldIndex;
+        final int synthFieldCount;
+        final String fieldName;
+        final long fieldClassID;
+
+        public ClassInlinedFields(int fieldIndex, int synthFieldCount, String fieldName, long fieldClassID) {
+            this.fieldIndex = fieldIndex;
+            this.synthFieldCount = synthFieldCount;
+            this.fieldName = fieldName;
+            this.fieldClassID =  fieldClassID;
+        }
+    }
+
+    public void addClassInlinedFields(long classID, ClassInlinedFields[] fields) {
+        inlinedFields.put(makeId(classID), fields);
+    }
+
+    /**
+     * @return null if the class has no inlined fields
+     */
+    ClassInlinedFields[] findClassInlinedFields(long classID) {
+        return inlinedFields.get(makeId(classID));
+    }
 
     /**
      * @return true iff it's possible that some JavaThing instances might
      *          isNew set
      *
diff a/test/lib/jdk/test/lib/hprof/parser/HprofReader.java b/test/lib/jdk/test/lib/hprof/parser/HprofReader.java
--- a/test/lib/jdk/test/lib/hprof/parser/HprofReader.java
+++ b/test/lib/jdk/test/lib/hprof/parser/HprofReader.java
@@ -94,10 +94,16 @@
     static final int HPROF_CPU_SAMPLES   = 0x0d;
     static final int HPROF_CONTROL_SETTINGS = 0x0e;
     static final int HPROF_LOCKSTATS_WAIT_TIME = 0x10;
     static final int HPROF_LOCKSTATS_HOLD_TIME = 0x11;
 
+    static final int HPROF_FLAT_ARRAYS          = 0x12;
+    static final int HPROF_INLINED_FIELDS       = 0x13;
+
+    static final int HPROF_FLAT_ARRAY           = 0x01;
+    static final int HPROF_CLASS_WITH_INLINED_FIELDS = 0x01;
+
     static final int HPROF_GC_ROOT_UNKNOWN       = 0xff;
     static final int HPROF_GC_ROOT_JNI_GLOBAL    = 0x01;
     static final int HPROF_GC_ROOT_JNI_LOCAL     = 0x02;
     static final int HPROF_GC_ROOT_JAVA_FRAME    = 0x03;
     static final int HPROF_GC_ROOT_NATIVE_STACK  = 0x04;
@@ -240,32 +246,31 @@
                     }
                     break;
                 }
 
                 case HPROF_HEAP_DUMP: {
-                    if (dumpsToSkip <= 0) {
+                    if (dumpsToSkip == 0) {
                         try {
                             readHeapDump(length, currPos);
                         } catch (EOFException exp) {
                             handleEOF(exp, snapshot);
                         }
                         if (debugLevel > 0) {
                             System.out.println("    Finished processing instances in heap dump.");
                         }
-                        return snapshot;
                     } else {
                         dumpsToSkip--;
                         skipBytes(length);
                     }
                     break;
                 }
 
                 case HPROF_HEAP_DUMP_END: {
                     if (version >= VERSION_JDK6) {
-                        if (dumpsToSkip <= 0) {
-                            skipBytes(length);  // should be no-op
-                            return snapshot;
+                        if (dumpsToSkip == 0) {
+                            // update dumpsToSkip to skip other dumps
+                            dumpsToSkip--;
                         } else {
                             // skip this dump (of the end record for a sequence of dump segments)
                             dumpsToSkip--;
                         }
                     } else {
@@ -276,11 +281,11 @@
                     break;
                 }
 
                 case HPROF_HEAP_DUMP_SEGMENT: {
                     if (version >= VERSION_JDK6) {
-                        if (dumpsToSkip <= 0) {
+                        if (dumpsToSkip == 0) {
                             try {
                                 // read the dump segment
                                 readHeapDump(length, currPos);
                             } catch (EOFException exp) {
                                 handleEOF(exp, snapshot);
@@ -350,10 +355,19 @@
                 {
                     // Ignore these record types
                     skipBytes(length);
                     break;
                 }
+                case HPROF_FLAT_ARRAYS: {
+                    readFlatArrays(length);
+                    break;
+                }
+                case HPROF_INLINED_FIELDS: {
+                    readInlinedFields(length);
+                    break;
+                }
+
                 default: {
                     skipBytes(length);
                     warn("Ignoring unrecognized record type " + type);
                 }
             }
@@ -850,11 +864,11 @@
             }
         }
         if (primitiveSignature != 0x00) {
             long size = elSize * (long)num;
             bytesRead += size;
-            JavaValueArray va = new JavaValueArray(primitiveSignature, start);
+            JavaValueArray va = new JavaValueArray(id, primitiveSignature, start);
             skipBytes(size);
             snapshot.addHeapObject(id, va);
             snapshot.setSiteTrace(va, stackTrace);
         } else {
             long sz = (long)num * identifierSize;
@@ -900,10 +914,62 @@
                 throw new IOException("Invalid type id of " + typeId);
             }
         }
     }
 
+    private void readFlatArrays(long length) throws IOException {
+        while (length > 0) {
+            byte tag = in.readByte();
+            length--;
+            switch (tag) {
+                case HPROF_FLAT_ARRAY: {
+                    long objId = readID();
+                    length -= identifierSize;
+                    long elementClassId = readID();
+                    length -= identifierSize;
+                    snapshot.addFlatArray(objId, elementClassId);
+                    break;
+                }
+                default: {
+                    throw new IOException("Invalid tag " + tag);
+                }
+            }
+        }
+    }
+
+    private void readInlinedFields(long length) throws IOException {
+        while (length > 0) {
+            byte tag = in.readByte();
+            length--;
+            switch (tag) {
+                case HPROF_CLASS_WITH_INLINED_FIELDS: {
+                    long classID = readID();
+                    length -= identifierSize;
+                    int fieldNum = in.readUnsignedShort();
+                    length -= 2;
+                    Snapshot.ClassInlinedFields[] fields = new Snapshot.ClassInlinedFields[fieldNum];
+                    for (int i = 0; i < fieldNum; i++) {
+                        int fieldIndex = in.readUnsignedShort();
+                        length -= 2;
+                        int synthFieldCount = in.readUnsignedShort();
+                        length -= 2;
+                        String fieldName = getNameFromID(readID());
+                        length -= identifierSize;
+                        long fieldClassId = readID();
+                        length -= identifierSize;
+                        fields[i] = new Snapshot.ClassInlinedFields(fieldIndex, synthFieldCount, fieldName, fieldClassId);
+                    }
+                    snapshot.addClassInlinedFields(classID, fields);
+                    break;
+                }
+                default: {
+                    throw new IOException("Invalid tag " + tag);
+                }
+            }
+        }
+    }
+
     private void handleEOF(EOFException exp, Snapshot snapshot) {
         if (debugLevel > 0) {
             exp.printStackTrace();
         }
         warn("Unexpected EOF. Will miss information...");
diff a/test/lib/jdk/test/whitebox/WhiteBox.java b/test/lib/jdk/test/whitebox/WhiteBox.java
--- a/test/lib/jdk/test/whitebox/WhiteBox.java
+++ b/test/lib/jdk/test/whitebox/WhiteBox.java
@@ -144,10 +144,24 @@
   private native int encodeConstantPoolIndyIndex0(int index);
   public         int encodeConstantPoolIndyIndex(int index) {
     return encodeConstantPoolIndyIndex0(index);
   }
 
+  private native Object[] getObjectsViaKlassOopMaps0(Object thing);
+  public Object[] getObjectsViaKlassOopMaps(Object thing) {
+    Objects.requireNonNull(thing);
+    return getObjectsViaKlassOopMaps0(thing);
+  }
+
+  private native Object[] getObjectsViaOopIterator0(Object thing);
+  public Object[] getObjectsViaOopIterator(Object thing) {
+    Objects.requireNonNull(thing);
+    return getObjectsViaOopIterator0(thing);
+  }
+
+  public native Object[] getObjectsViaFrameOopIterator(int depth);
+
   private native int getFieldEntriesLength0(Class<?> aClass);
   public         int getFieldEntriesLength(Class<?> aClass) {
     Objects.requireNonNull(aClass);
     return getFieldEntriesLength0(aClass);
   }
diff a/test/lib/org/openjdk/asmtools/JtregDriver.java b/test/lib/org/openjdk/asmtools/JtregDriver.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/JtregDriver.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2022, 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 org.openjdk.asmtools;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.nio.file.Path;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Files;
+import java.io.PrintWriter;
+import java.io.IOException;
+import org.openjdk.asmtools.jdis.uEscWriter;
+
+/**
+ * An entry point to AsmTools for use with the jtreg '@run driver' command.
+ * Unlike 'Main', never invokes 'System.exit' (which crashes jtreg).
+ *
+ * Also adjusts file paths:
+ *
+ * - For jasm and jcoder, source files are expected to appear in ${test.src},
+ *   and output is sent to ${test.classes}
+ *
+ * - For other tools, class files are expected to appear in ${test.classes},
+ *   and output is sent to the scratch working directory
+ *
+ * Example jtreg usage:
+ *
+ * @library /test/lib
+ * @build org.openjdk.asmtools.* org.openjdk.asmtools.jasm.*
+ * @run driver org.openjdk.asmtools.JtregDriver jasm -strict TestFile.jasm
+ */
+public class JtregDriver {
+
+    public static void main(String... args) throws Throwable {
+        // run AltLoaderMain from a class loader that prefers the modified AsmTools classes
+        ClassLoader loader = JtregDriver.class.getClassLoader();
+        Path file = Path.of(loader.getResource("org/openjdk/asmtools/JtregDriver.class").toURI());
+        Path root = file.getParent().getParent().getParent().getParent();
+        ClassLoader altLoader = new AsmToolsClassLoader(root);
+        Class<?> altMain = altLoader.loadClass(AltLoaderMain.class.getName());
+        try {
+            altMain.getMethod("main", String[].class).invoke(null, (Object) args);
+        } catch (InvocationTargetException e) {
+            throw e.getCause();
+        }
+    }
+
+    public static class AltLoaderMain {
+
+        public static void main(String... args) throws IOException {
+            if (args.length == 0) {
+                throw new IllegalArgumentException("Missing asmtools command");
+            }
+            String cmd = args[0];
+            if (!cmd.equals("jasm") && !cmd.equals("jdis") && !cmd.equals("jcoder")
+                    && !cmd.equals("jdec") && !cmd.equals("jcdec")) {
+                throw new IllegalArgumentException("Unrecognized asmtools command: " + cmd);
+            }
+            boolean isAssembler = cmd.equals("jasm") || cmd.equals("jcoder");
+            String srcDir = System.getProperty("test.src", ".");
+            String clsDir = System.getProperty("test.classes", ".");
+            String fileDir = isAssembler ? srcDir : clsDir;
+
+            ArrayList<String> toolArgList = new ArrayList<String>();
+
+            if (isAssembler) {
+                Path destPath = Path.of(clsDir);
+                if (!Files.exists(destPath)) {
+                    // jtreg creates classes dir on demand, might not have happened yet
+                    Files.createDirectories(destPath);
+                }
+                toolArgList.add("-d");
+                toolArgList.add(clsDir);
+            }
+
+            boolean isOptionArg = false; // marks an argument to a previous option
+            for (int i = 1; i < args.length; i++) {
+                String arg = args[i];
+                if (isOptionArg) {
+                    isOptionArg = false; // reset for next
+                } else {
+                    if (arg.equals("-d")) {
+                        isOptionArg = true;
+                    } else if (!arg.startsWith("-") && !arg.startsWith("/")) {
+                        // adjust filename
+                        arg = Path.of(fileDir, arg).toString();
+                    }
+                }
+                toolArgList.add(arg);
+            }
+
+            String[] toolArgs = toolArgList.toArray(new String[0]);
+            boolean success = switch (cmd) {
+                case "jasm" -> {
+                    PrintWriter out = new PrintWriter(System.out);
+                    yield new org.openjdk.asmtools.jasm.Main(out, "jasm").compile(toolArgs);
+                }
+                case "jdis" -> {
+                    PrintWriter out = new PrintWriter(new uEscWriter(System.out));
+                    PrintWriter err = new PrintWriter(System.err);
+                    yield new org.openjdk.asmtools.jdis.Main(out, err, "jdis").disasm(toolArgs);
+                }
+                case "jcoder" -> {
+                    PrintWriter out = new PrintWriter(System.out);
+                    yield new org.openjdk.asmtools.jcoder.Main(out, "jcoder").compile(toolArgs);
+                }
+                case "jdec" -> {
+                    PrintWriter out = new PrintWriter(new uEscWriter(System.out));
+                    PrintWriter err = new PrintWriter(System.err);
+                    yield new org.openjdk.asmtools.jdec.Main(out, err, "jdec").decode(toolArgs);
+                }
+                case "jcdec" -> {
+                    PrintWriter out = new PrintWriter(new uEscWriter(System.out));
+                    yield new org.openjdk.asmtools.jcdec.Main(out, "jcdec").decode(toolArgs);
+                }
+                default -> throw new AssertionError();
+            };
+            if (!success) {
+                throw new RuntimeException("asmtools execution failed");
+            }
+        }
+
+    }
+
+    /**
+     * Class loader for the AsmTools classes. Allows classes colocated with
+     * JtregDriver to have priority over versions of the classes loaded by
+     * jtreg (which includes its own copy of AsmTools).
+     */
+    private static class AsmToolsClassLoader extends ClassLoader {
+        private final Path root;
+        private final String separator;
+
+        public AsmToolsClassLoader(Path root) {
+            super(AsmToolsClassLoader.class.getClassLoader());
+            this.root = root;
+            this.separator = root.getFileSystem().getSeparator();
+        }
+
+        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+            if (name.startsWith("org.openjdk.asmtools.")) {
+                Class<?> result = findClass(name);
+                if (resolve) resolveClass(result);
+                return result;
+            } else {
+                return super.loadClass(name, resolve);
+            }
+        }
+
+        protected Class<?> findClass(String name) throws ClassNotFoundException {
+            String filename = name.replace(".",separator) + ".class";
+            try {
+                Path classFile = root.resolve(filename);
+                byte[] bytes = Files.readAllBytes(classFile);
+                return defineClass(name, bytes, 0, bytes.length);
+            } catch (InvalidPathException | IOException e) {
+                throw new ClassNotFoundException("can't read class " + filename, e);
+            }
+        }
+
+    }
+
+}
diff a/test/lib/org/openjdk/asmtools/Main.java b/test/lib/org/openjdk/asmtools/Main.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/Main.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2009, 2020, 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 org.openjdk.asmtools;
+
+import org.openjdk.asmtools.util.I18NResourceBundle;
+import org.openjdk.asmtools.util.ProductInfo;
+
+/**
+ * Wrapper class that reads the first command line argument and invokes a corresponding
+ * tool.
+ */
+public class Main {
+
+    public static final I18NResourceBundle i18n
+            = I18NResourceBundle.getBundleForClass(Main.class);
+
+    /**
+     * Parses the first argument and deligates execution to an appropriate tool
+     *
+     * @param args - command line arguments
+     */
+    public static void main(String args[]) {
+        if (args.length == 0) {
+            usage(i18n.getString("main.error.no_arguments"), 1);
+        }
+        String cmd = args[0];
+        if (cmd.equals("-?") || cmd.equals("-h") || cmd.equals("-help")) {
+            usage(null, 0);
+        } else if (cmd.equals("-version")) {
+            printVersion();
+        } else {
+            String[] newArgs = new String[args.length - 1];
+            System.arraycopy(args, 1, newArgs, 0, args.length - 1);
+            if (cmd.equals("jasm")) {
+                jasm(newArgs);
+            } else if (cmd.equals("jdis")) {
+                jdis(newArgs);
+            } else if (cmd.equals("jcoder")) {
+                jcoder(newArgs);
+            } else if (cmd.equals("jdec")) {
+                jdec(newArgs);
+            } else if (cmd.equals("jcdec")) {
+                jcdec(newArgs);
+            } else {
+                usage(i18n.getString("main.error.unknown_tool", cmd), 1);
+            }
+        }
+    }
+
+    /**
+     * Prints usage info and error message, afterwards invokes System.exit()
+     *
+     * @param msg - error message to print, or null if no errors occurred
+     * @param exitCode - exit code to be returned by System.exit()
+     */
+    public static void usage(String msg, int exitCode) {
+        System.err.println(i18n.getString("main.usage", "asmtools.jar"));
+        if (msg != null) {
+            System.err.println(msg);
+        }
+        System.exit(exitCode);
+    }
+
+    /**
+     * Prints the tools version
+     */
+    public static void printVersion() {
+        System.out.println(ProductInfo.FULL_VERSION);
+    }
+
+    /**
+     * Invokes jasm main class with passed arguments
+     */
+    public static void jasm(String[] args) {
+        org.openjdk.asmtools.jasm.Main.main(args);
+    }
+
+    /**
+     * Invokes jcdec main class with passed arguments
+     */
+    public static void jcdec(String[] args) {
+        org.openjdk.asmtools.jcdec.Main.main(args);
+    }
+
+    /**
+     * Invokes jcoder main class with passed arguments
+     */
+    public static void jcoder(String[] args) {
+        org.openjdk.asmtools.jcoder.Main.main(args);
+    }
+
+    /**
+     * Invokes jdec main class with passed arguments
+     */
+    public static void jdec(String[] args) {
+        org.openjdk.asmtools.jdec.Main.main(args);
+    }
+
+    /**
+     * Invokes jdis main class with passed arguments
+     */
+    public static void jdis(String[] args) {
+        org.openjdk.asmtools.jdis.Main.main(args);
+    }
+}
diff a/test/lib/org/openjdk/asmtools/asmutils/HexUtils.java b/test/lib/org/openjdk/asmtools/asmutils/HexUtils.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/asmutils/HexUtils.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2014, 2019, 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 org.openjdk.asmtools.asmutils;
+
+/**
+ *
+ */
+public class HexUtils {
+    /*======================================================== Hex */
+
+    private static final String hexString = "0123456789ABCDEF";
+    private static final char hexTable[] = hexString.toCharArray();
+
+    public static String toHex(long val, int width) {
+        StringBuffer sb = new StringBuffer();
+        for (int i = width - 1; i >= 0; i--) {
+            sb.append(hexTable[((int) (val >> (4 * i))) & 0xF]);
+        }
+        String s = sb.toString();
+        return "0x" + (s.isEmpty() ? "0" : s);
+    }
+
+    public static String toHex(long val) {
+        int width;
+        for (width = 16; width > 0; width--) {
+            if ((val >> (width - 1) * 4) != 0) {
+                break;
+            }
+        }
+        return toHex(val, width);
+    }
+
+    public static String toHex(int val) {
+        int width;
+        for (width = 8; width > 0; width--) {
+            if ((val >> (width - 1) * 4) != 0) {
+                break;
+            }
+        }
+        return toHex(val, width);
+    }
+
+}
diff a/test/lib/org/openjdk/asmtools/asmutils/StringUtils.java b/test/lib/org/openjdk/asmtools/asmutils/StringUtils.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/asmutils/StringUtils.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.asmtools.asmutils;
+
+/**
+ * Utility class to share common tools/methods.
+ */
+public class StringUtils {
+    /**
+     * Converts CONSTANT_Utf8_info string to a printable string for jdis/jdes.
+     * @param utf8 UTF8 string taken from within ConstantPool of a class file
+     * @return output string for jcod/jasm
+     */
+    public static String Utf8ToString(String utf8) {
+        StringBuilder sb = new StringBuilder("\"");
+        for (int k = 0; k < utf8.length(); k++) {
+            char c = utf8.charAt(k);
+            switch (c) {
+                case '\t':
+                    sb.append('\\').append('t');
+                    break;
+                case '\n':
+                    sb.append('\\').append('n');
+                    break;
+                case '\r':
+                    sb.append('\\').append('r');
+                    break;
+                case '\b':
+                    sb.append('\\').append('b');
+                    break;
+                case '\f':
+                    sb.append('\\').append('f');
+                    break;
+                case '\"':
+                    sb.append('\\').append('\"');
+                    break;
+                case '\'':
+                    sb.append('\\').append('\'');
+                    break;
+                case '\\':
+                    sb.append('\\').append('\\');
+                    break;
+                default:
+                    sb.append(c);
+            }
+        }
+        return sb.append('\"').toString();
+    }
+}
diff a/test/lib/org/openjdk/asmtools/common/Module.java b/test/lib/org/openjdk/asmtools/common/Module.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/common/Module.java
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 2016, 2020, 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 org.openjdk.asmtools.common;
+
+import org.openjdk.asmtools.jdis.Indenter;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static java.lang.String.format;
+
+/**
+ * Internal presentation of a module
+ */
+public final class Module extends Indenter {
+
+  //* A module name and module_flags
+  public final Header header;
+  //* A service dependence's of this module
+  public final Set<Uses> uses;
+  //* Modules on which the current module has a dependence.
+  public final Set<Dependence> requires;
+  //* A module exports, may be qualified or unqualified.
+  public final Map<Exported, Set<String>> exports;
+  //* Packages, to be opened by the current module
+  public final Map<Opened, Set<String>> opens;
+  //* A service that a module provides one or more implementations of.
+  public final Map<Provided, Set<String>> provides;
+
+  private Module(Builder builder) {
+    this.header = builder.header;
+    this.requires = Collections.unmodifiableSet(builder.requires);
+    this.exports = Collections.unmodifiableMap(builder.exports);
+    this.opens = Collections.unmodifiableMap(builder.opens);
+    this.uses = Collections.unmodifiableSet(builder.uses);
+    this.provides = Collections.unmodifiableMap(builder.provides);
+  }
+
+  public String getModuleFlags () {
+    return Modifier.getModuleModifiers(header.getFlags());
+  }
+  public String getModuleName () { return header.getModuleName();  }
+  public String getModuleVersion()  { return header.getModuleVersion(); };
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    int l = 0;
+    requires.stream()
+        .sorted()
+        .forEach(d -> sb.append(getIndentString()).append(format("requires %s;%s%n",
+            d.toString(),
+            d.getModuleVersion() == null ? "" : " // @" + d.getModuleVersion())));
+    //
+    l = newLine(sb,l);
+    exports.entrySet().stream()
+        .filter(e -> e.getValue().isEmpty())
+        .sorted(Map.Entry.comparingByKey())
+        .map(e -> format("%sexports %s;%n", getIndentString(), e.getKey().toString()))
+        .forEach(sb::append);
+    exports.entrySet().stream()
+        .filter(e -> !e.getValue().isEmpty())
+        .sorted(Map.Entry.comparingByKey())
+        .map(e -> format("%sexports %s to%n%s;%n", getIndentString(), e.getKey().toString(),
+            e.getValue().stream().sorted()
+                .map(mn -> format("%s          %s", getIndentString(), mn))
+                .collect(Collectors.joining(",\n"))))
+        .forEach(sb::append);
+    //
+    l = newLine(sb,l);
+    opens.entrySet().stream()
+        .filter(e -> e.getValue().isEmpty())
+        .sorted(Map.Entry.comparingByKey())
+        .map(e -> format("%sopens %s;%n", getIndentString(), e.getKey().toString()))
+        .forEach(sb::append);
+    opens.entrySet().stream()
+        .filter(e -> !e.getValue().isEmpty())
+        .sorted(Map.Entry.comparingByKey())
+        .map(e -> format("%sopens %s to%n%s;%n", getIndentString(), e.getKey().toString(),
+            e.getValue().stream().sorted()
+                .map(mn -> format("%s          %s", getIndentString(), mn))
+                .collect(Collectors.joining(",\n"))))
+        .forEach(sb::append);
+    //
+    l = newLine(sb,l);
+    uses.stream().sorted()
+        .map(s -> format("%suses %s;%n", getIndentString(), s))
+        .forEach(sb::append);
+    //
+    l = newLine(sb,l);
+    provides.entrySet().stream()
+        .filter(e -> !e.getValue().isEmpty())
+        .sorted(Map.Entry.comparingByKey())
+        .map(e -> format("%sprovides %s with%n%s;%n", getIndentString(), e.getKey().toString(),
+            e.getValue().stream().sorted()
+                .map(mn -> format("%s          %s", getIndentString(), mn))
+                .collect(Collectors.joining(",\n"))))
+        .forEach(sb::append);
+    //
+    if( Character.isWhitespace(sb.charAt(sb.length()-1)) )
+      sb.deleteCharAt(sb.length()-1);
+    return sb.toString();
+  }
+
+  private int newLine(StringBuilder sb, int length) {
+    if(sb.length() > length) {
+      sb.append("\n");
+      return sb.length() + 1;
+    }
+    return length;
+  }
+
+  /**
+   * Modules flags
+   */
+  public enum Modifier {
+    ACC_NONE(0x0000, "", ""),
+    ACC_OPEN(0x0020, "open", "ACC_OPEN"),
+    ACC_TRANSITIVE(0x0020, "transitive", "ACC_TRANSITIVE"),
+    ACC_STATIC_PHASE(0x0040, "static", "ACC_STATIC_PHASE"),
+    ACC_SYNTHETIC(0x1000, "", "ACC_SYNTHETIC"),
+    ACC_MANDATED(0x8000, "", "ACC_MANDATED");
+    private final int value;
+    private final String keyword;
+    private final String flag;
+    Modifier(int value, String keyword, String flagName) {
+      this.value = value;
+      this.keyword = keyword;
+      this.flag = flagName;
+    }
+
+    public int asInt() { return value; }
+
+    public static String getModuleModifiers(int flag) {
+      return asString(flag, false, ACC_TRANSITIVE);
+    }
+
+    public static String getModuleFlags(int flag) {
+      return asString(flag, true, ACC_TRANSITIVE);
+    }
+
+    public static String getStatementModifiers(int flag) {
+      return asString(flag, false, ACC_OPEN);
+    }
+
+    public static String getStatementFlags(int flag) {
+      return asString(flag, true, ACC_OPEN);
+    }
+
+    private static String asString(int value, boolean flagFormat, Modifier skipped ) {
+      String buf = "";
+      for(Module.Modifier m : values()) {
+        if( m != skipped && (value & m.value) != 0) {
+          buf += ((flagFormat) ? m.flag : m.keyword) + " ";
+          value ^= m.value;
+        }
+      }
+      if( flagFormat && value != 0 )
+        buf += String.format("0x%04X ", value);
+      return buf;
+    }
+  }
+
+  // A module header consists of a module name and module flags
+  public final static class Header extends VersionedFlaggedTargetType{
+    Header(String typeName, int flag) { this(typeName, flag, null); }
+    Header(String typeName, int flag, String moduleVersion) { super(typeName, flag, moduleVersion); }
+    public String getModuleName()    { return getTypeName(); }
+    public int    getModuleFlags()   { return getFlags();    }
+    public String getModuleVersion() { return getVersion();  }
+  }
+
+  //* A module on which the current module has a dependence.
+  public final static class Dependence extends VersionedFlaggedTargetType {
+    public Dependence(String moduleName, int flag) {this(moduleName, flag, null);}
+    public Dependence(String moduleName, int flag, String moduleVersion) {super(moduleName, flag, moduleVersion);}
+    public Dependence(String moduleName, boolean transitive, boolean staticPhase) { this(moduleName,transitive,staticPhase,null);}
+    public Dependence(String moduleName, boolean transitive, boolean staticPhase, String moduleVersion) {
+      this(moduleName,
+          (transitive ? Modifier.ACC_TRANSITIVE.value : Modifier.ACC_NONE.value) |
+          (staticPhase ? Modifier.ACC_STATIC_PHASE.value : Modifier.ACC_NONE.value), moduleVersion);
+    }
+    public String getModuleVersion()          { return getVersion();  }
+  }
+
+  public final static class Uses extends TargetType {
+    public Uses(String typeName) { super(typeName); }
+  }
+
+  //* A provided type of the current module.
+  public final static class Provided extends TargetType {
+    public Provided(String typeName) { super(typeName); }
+  }
+
+  //* An opened package of the current module.
+  public final static class Opened extends FlaggedTargetType {
+    public Opened(String typeName) {
+      super(typeName, 0);
+    }
+    public Opened(String typeName, int opensFlags) {
+      super(typeName, opensFlags);
+    }
+  }
+
+  //* An exported package of the current module.
+  public final static class Exported extends FlaggedTargetType {
+    public Exported(String typeName) {
+      super(typeName, 0);
+    }
+
+    public Exported(String typeName, int exportsFlags) {
+      super(typeName, exportsFlags);
+    }
+  }
+
+  public static class VersionedFlaggedTargetType extends FlaggedTargetType {
+    private String version;
+
+    VersionedFlaggedTargetType(String typeName, int flag) {
+      this(typeName,flag, null);
+    }
+
+    VersionedFlaggedTargetType(String typeName, int flag, String version) {
+      super(typeName, flag);
+      this.version = version != null && !version.isEmpty() ? version : null;
+    }
+    public String getVersion() { return version; }
+
+    @Override
+    public int hashCode() {
+      int code = version == null ? 0 : version.hashCode();
+      return code + super.hashCode();
+    }
+  }
+
+  public static class FlaggedTargetType extends TargetType {
+    private int flag;
+
+    FlaggedTargetType(String typeName, int flag) {
+      super(typeName);
+      this.flag = flag;
+    }
+
+    public boolean isFlagged() {
+      return true;
+    }
+
+    public int getFlags() {
+      return flag;
+    }
+
+    public void setFlag(int value) { flag = value; }
+
+    @Override
+    public int hashCode() {
+      return super.hashCode() + flag;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      return super.equals(o) && ((FlaggedTargetType) o).flag == this.flag;
+    }
+
+    @Override
+    public String toString() {
+      return Modifier.getStatementModifiers(this.flag)+ super.toString();
+    }
+  }
+
+  public static class TargetType implements Comparable<TargetType> {
+    private String typeName;
+
+    TargetType(String typeName) { this.typeName = typeName; }
+
+    public String getTypeName() {
+      return typeName;
+    }
+
+    public void setTypeName(String value) { typeName = value; }
+
+    public boolean isFlagged() {
+      return false;
+    }
+
+    @Override
+    public int hashCode() { return typeName.hashCode() * 11; }
+
+    @Override
+    public boolean equals(Object o) {
+      if (o instanceof TargetType) {
+        TargetType t = (TargetType) o;
+        return this.typeName.equals(t.getTypeName());
+      }
+      return false;
+    }
+
+    @Override
+    public int compareTo(TargetType t) {
+      return this.typeName.compareTo(t.getTypeName());
+    }
+
+    @Override
+    public String toString() {
+      return typeName;
+    }
+  }
+
+  /**
+   * The module builder.
+   */
+  public static final class Builder {
+    final Header header;
+    final Set<Dependence> requires = new HashSet<>();
+    final Map<Exported, Set<String>> exports = new HashMap<>();
+    final Map<Opened, Set<String>> opens = new HashMap<>();
+    final Set<Uses> uses = new HashSet<>();
+    final Map<Provided, Set<String>> provides = new HashMap<>();
+
+
+    public Builder() {
+      this("", Modifier.ACC_NONE.asInt(), null);
+    }
+
+    public Builder(String moduleName, int moduleFlags, String moduleVersion) {
+      header = new Header( moduleName,moduleFlags, moduleVersion);
+    }
+
+    public Builder setModuleFlags(int moduleFlags) {
+      header.setFlag(header.getFlags() | moduleFlags);
+      return this;
+    }
+
+    public Builder setModuleFlags(Modifier... moduleFlags) {
+      for (Modifier m : moduleFlags)
+        setModuleFlags(m.value);
+      return this;
+    }
+
+    public Builder setModuleName(String value) {
+      header.setTypeName(value);
+      return this;
+    }
+
+    public Builder require(String d, boolean transitive, boolean staticPhase, String version) {
+      requires.add(new Dependence(d, transitive, staticPhase, version));
+      return this;
+    }
+
+    public Builder require(String d, int requiresFlag, String version) {
+      requires.add(new Dependence(d, requiresFlag, version));
+      return this;
+    }
+
+    public Builder require(String d, int requiresFlag) {
+      requires.add(new Dependence(d, requiresFlag, null));
+      return this;
+    }
+
+    public Builder opens(Opened p, Set<String> ms) {
+      return add(opens, p, ms);
+    }
+
+    public Builder opens(String packageName, int exportFlags, Set<String> ms) {
+      return add(opens, new Opened(packageName, exportFlags), ms);
+    }
+
+    public Builder opens(String packageName, int exportFlags) {
+      return add(opens, new Opened(packageName, exportFlags), new HashSet<>());
+    }
+
+
+    public Builder exports(Exported p, Set<String> ms) {
+      return add(exports, p, ms);
+    }
+
+    public Builder exports(String packageName, int exportFlags, Set<String> ms) {
+      return add(exports, new Exported(packageName, exportFlags), ms);
+    }
+
+    public Builder exports(String packageName, int exportFlags) {
+      return add(exports, new Exported(packageName, exportFlags), new HashSet<>());
+    }
+
+    public Builder uses(String serviceName) {
+      uses.add(new Uses(serviceName));
+      return this;
+    }
+
+
+    public Builder uses(Set<String> serviceNames) {
+      uses.addAll(serviceNames.stream().map(Uses::new).collect(Collectors.toList()));
+      return this;
+    }
+
+    public Builder provides(Provided t, Set<String> implementations) {
+      return add(provides, t, implementations);
+    }
+
+    public Builder provides(String serviceName, Set<String> implementations) {
+      return add(provides, new Provided(serviceName), implementations);
+    }
+
+
+    /**
+     * @return The new module
+     */
+    public Module build() {
+      return new Module(this);
+    }
+
+    private <T extends TargetType> Builder  add( Map<T, Set<String>> collection, T source, Set<String> target) {
+      Objects.requireNonNull(source);
+      Objects.requireNonNull(target);
+      if (!collection.containsKey(source))
+        collection.put(source, new HashSet<>());
+      collection.get(source).addAll(target);
+      return this;
+    }
+  }
+}
diff a/test/lib/org/openjdk/asmtools/common/Tool.java b/test/lib/org/openjdk/asmtools/common/Tool.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/common/Tool.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2019, 2020, 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 org.openjdk.asmtools.common;
+
+
+import java.io.DataInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
+
+public abstract class Tool {
+
+    // Name of the program.
+    protected final String programName;
+    // Errors counter.
+    protected int nerrors = 0;
+    // The stream where error message are printed.
+    protected PrintWriter err;
+
+    // Output stream
+    protected PrintWriter out;
+
+    // A consumer to print a error message if the tool can't read a file
+    protected Consumer<String> printCannotReadMsg;
+
+    // A supplier to get a status of a debug flag
+    protected BooleanSupplier DebugFlag = () -> false;
+
+    public Tool(PrintWriter out, String programName) {
+        this(out, out, programName);
+    }
+
+    public Tool(PrintWriter out, PrintWriter err, String programName) {
+        this.out = out;
+        this.err = err;
+        this.programName = programName;
+    }
+
+
+    public String getError(String msg) {
+        return programName + ": " + msg;
+    }
+
+    /**
+     * Top level error message
+     */
+    public void error(String msg) {
+        err.println(getError(msg));
+        err.flush();
+    }
+
+    /**
+     * Top level print message
+     */
+    public void println(String msg) {
+        out.println(msg);
+        out.flush();
+    }
+
+    public void println() {
+        println("");
+    }
+
+    public void print(String msg) {
+        out.print(getError(msg));
+        out.flush();
+    }
+
+    /**
+     * @param fname file name
+     * @return DataInputStream or null if the method can't read a file
+     */
+    public DataInputStream getDataInputStream(String fname) {
+        try {
+            return new DataInputStream(new FileInputStream(fname));
+        } catch (IOException ex) {
+            if (fname.matches("^[A-Za-z]+:.*")) {
+                try {
+                    final URI uri = new URI(fname);
+                    final URL url = uri.toURL();
+                    final URLConnection conn = url.openConnection();
+                    conn.setUseCaches(false);
+                    return new DataInputStream(conn.getInputStream());
+                } catch (URISyntaxException | IOException e) {
+                    if (DebugFlag.getAsBoolean())
+                        e.printStackTrace();
+                }
+            }
+            if (printCannotReadMsg != null)
+                printCannotReadMsg.accept(fname);
+        }
+        return null;
+    }
+
+    /**
+     * Usage
+     */
+    protected abstract void usage();
+}
diff a/test/lib/org/openjdk/asmtools/i18n.properties b/test/lib/org/openjdk/asmtools/i18n.properties
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/i18n.properties
@@ -0,0 +1,33 @@
+# Copyright (c) 2014, 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.
+
+main.usage=\
+Usage: \n\
+to run an assembly tool: \n\
+\   $ java -jar {0} toolName [args...] \n\
+\   where toolName one of: jasm, jdis, jcoder, jdec, jcdec \n\
+to get the version: \n\
+\   $ java -jar {0} -version \n\
+to get this message \n\
+\   $ java -jar {0} -?|-h|-help\n
+
+main.error.no_arguments=No arguments provided!  See options above.
+main.error.unknown_tool=Tool name ''{0}'' unrecognized.  See usage above for possible tool choices.
diff a/test/lib/org/openjdk/asmtools/jasm/AnnotationData.java b/test/lib/org/openjdk/asmtools/jasm/AnnotationData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/AnnotationData.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jasm;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * JVMS 4.7.16.
+ *
+ * annotation {
+ *     u2 type_index;
+ *     u2 num_element_value_pairs;
+ *     {   u2            element_name_index;
+ *         element_value value;
+ *     } element_value_pairs[num_element_value_pairs];
+ * }
+ */
+class AnnotationData implements Data {
+
+    boolean invisible;
+    Argument typeCPX;
+    ArrayList<ElemValuePair> elemValuePairs;
+    int annotationLength = 0;
+
+    /**
+     * AnnotationElemValue
+     *
+     * Used to store Annotation Data
+     */
+    static public class ElemValuePair implements Data {
+
+        ConstantPool.ConstCell name;
+        Data value;
+
+        public ElemValuePair(ConstantPool.ConstCell name, Data value) {
+            this.name = name;
+            this.value = value;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            name.write(out);
+            value.write(out);
+        }
+
+        @Override
+        public int getLength() {
+            return 2 + value.getLength();
+        }
+    }
+
+    public AnnotationData(Argument typeCPX, boolean invisible) {
+        this.typeCPX = typeCPX;
+        this.elemValuePairs = new ArrayList<>();
+        this.invisible = invisible;
+    }
+
+    public void add(ElemValuePair elemValuePair) {
+        elemValuePairs.add(elemValuePair);
+        annotationLength += elemValuePair.getLength();
+    }
+
+    @Override
+    public void write(CheckedDataOutputStream out) throws IOException {
+        out.writeShort(typeCPX.arg);
+        out.writeShort(elemValuePairs.size());
+
+        for (Data pair : elemValuePairs) {
+            pair.write(out);
+        }
+    }
+
+    @Override
+    public int getLength() {
+        return 4 + annotationLength;
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/Argument.java b/test/lib/org/openjdk/asmtools/jasm/Argument.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/Argument.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1996, 2014, 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 org.openjdk.asmtools.jasm;
+
+/**
+ *
+ */
+class Argument {
+
+    static final int NotSet = -1;
+    int arg;
+
+    Argument() {
+        arg = NotSet;
+    }
+
+    Argument(int arg) {
+        this.arg = arg;
+    }
+
+    public int hashCode() {
+        return arg;
+    }
+
+    /**
+     * Compares this object to the specified object.
+     *
+     * @param obj the object to compare with
+     * @return true if the objects are the same; false otherwise.
+     */
+    public boolean equals(Object obj) {
+        throw new Parser.CompilerError("ConstCell.equals");
+    }
+
+    boolean isSet() {
+        return arg != NotSet;
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/AttrData.java b/test/lib/org/openjdk/asmtools/jasm/AttrData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/AttrData.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 1996, 2014, 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 org.openjdk.asmtools.jasm;
+
+import java.io.IOException;
+
+/**
+ * AttrData
+ *
+ * AttrData is the base class for many attributes (or parts of attributes), and it is
+ * instantiated directly for simple attributes (like Synthetic or Deprecated).
+ */
+class AttrData implements Data {
+
+    private final ClassData clsData;
+    private final Argument attrNameCPX;
+
+    AttrData(ClassData cdata, String name) {
+        clsData = cdata;
+        attrNameCPX = cdata.pool.FindCellAsciz(name);
+    }
+
+    protected ClassData getClassData() {
+        return clsData;
+    }
+
+    // full length of the attribute
+    // declared in Data
+    public int getLength() {
+        return 6 + attrLength();
+    }
+
+    // subclasses must redefine this
+    public int attrLength() {
+        return 0;
+    }
+
+    public void write(CheckedDataOutputStream out) throws IOException {
+        out.writeShort(attrNameCPX.arg);
+        out.writeInt(attrLength()); // attr len
+    }
+} // end class AttrData
+
diff a/test/lib/org/openjdk/asmtools/jasm/BootstrapMethodData.java b/test/lib/org/openjdk/asmtools/jasm/BootstrapMethodData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/BootstrapMethodData.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 1996, 2014, 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 org.openjdk.asmtools.jasm;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ *
+ */
+class BootstrapMethodData extends Argument implements Data {
+
+    ConstantPool.ConstCell bootstrapMethodHandle;
+    ArrayList<ConstantPool.ConstCell> arguments;
+    public int placeholder_index = -1;
+
+    public BootstrapMethodData(ConstantPool.ConstCell bsmHandle, ArrayList<ConstantPool.ConstCell> arguments) {
+        super();
+        this.bootstrapMethodHandle = bsmHandle;
+        this.arguments = arguments;
+    }
+
+    public BootstrapMethodData(int placeholder) {
+        super();
+        this.bootstrapMethodHandle = null;
+        this.arguments = null;
+        this.placeholder_index = placeholder;
+    }
+
+    public int getLength() {
+        return 4 + arguments.size() * 2;
+    }
+
+    public boolean isPlaceholder() {
+        return placeholder_index > -1;
+    }
+
+    public void write(CheckedDataOutputStream out) throws IOException {
+        out.writeShort(bootstrapMethodHandle.arg);
+        out.writeShort(arguments.size());
+
+        for (ConstantPool.ConstCell argument : arguments) {
+            out.writeShort(argument.arg);
+        }
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/CFVersion.java b/test/lib/org/openjdk/asmtools/jasm/CFVersion.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/CFVersion.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2017, 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 org.openjdk.asmtools.jasm;
+
+/*
+ * Class File Version
+ */
+public class CFVersion implements Cloneable{
+    /**
+     * Default versions of class file
+     */
+    public static final short DEFAULT_MAJOR_VERSION = 45;
+    public static final short DEFAULT_MINOR_VERSION = 3;
+    public static final short DEFAULT_MODULE_MAJOR_VERSION = 53;
+    public static final short DEFAULT_MODULE_MINOR_VERSION = 0;
+    public static final short UNDEFINED_VERSION = -1;
+
+    private short major_version;
+    private short minor_version;
+    private boolean frozen;
+    private boolean isSet;
+
+    public CFVersion() {
+        frozen = false;
+        isSet = false;
+        major_version = UNDEFINED_VERSION;
+        minor_version = UNDEFINED_VERSION;
+    }
+
+    public CFVersion(boolean frozenCFV, short major_version, short minor_version) {
+        isSet = true;
+        frozen = frozenCFV;
+        this.major_version = major_version;
+        this.minor_version = minor_version;
+    }
+
+    public void setMajorVersion(short major_version) {
+        if ( !frozen ) {
+            isSet = true;
+            this.major_version = major_version;
+        }
+    }
+
+    public void setMinorVersion(short minor_version) {
+        if (!frozen) {
+            isSet = true;
+            this.minor_version = minor_version;
+        }
+    }
+
+    public String asString() {
+        return (isSet) ? this.major_version + ":" +this.minor_version : "(undef):(undef)";
+    }
+
+    public void initModuleDefaults() {
+        if( ! isSet) {
+            major_version = DEFAULT_MODULE_MAJOR_VERSION;
+            minor_version = DEFAULT_MODULE_MINOR_VERSION;
+        }
+    }
+
+    public void initClassDefaults() {
+        if( !isSet ) {
+            major_version = DEFAULT_MAJOR_VERSION;
+            minor_version = DEFAULT_MINOR_VERSION;
+        }
+    }
+
+    public short minor_version() {
+        return this.minor_version;
+    }
+
+    public short major_version() {
+        return this.major_version;
+    }
+
+    public CFVersion clone() {
+        try {
+            return (CFVersion)super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/CPXAttr.java b/test/lib/org/openjdk/asmtools/jasm/CPXAttr.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/CPXAttr.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 1996, 2014, 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 org.openjdk.asmtools.jasm;
+
+import java.io.IOException;
+
+/**
+ * Constant Pool Index Attribute
+ */
+class CPXAttr extends AttrData {
+
+    Argument cell;
+
+    public CPXAttr(ClassData cls, String attrName, Argument cell) {
+        super(cls, attrName);
+        this.cell = cell;
+    }
+
+    public int attrLength() {
+        return 2;
+    }
+
+    public void write(CheckedDataOutputStream out) throws IOException {
+        super.write(out);  // attr name, attr len
+        out.writeShort(cell.arg);
+    }
+} // end class CPXAttr
+
diff a/test/lib/org/openjdk/asmtools/jasm/CheckedDataOutputStream.java b/test/lib/org/openjdk/asmtools/jasm/CheckedDataOutputStream.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/CheckedDataOutputStream.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014, 2017, 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.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.openjdk.asmtools.jasm;
+
+import java.io.DataOutput;
+import java.io.IOException;
+
+/**
+ *
+ */
+public interface CheckedDataOutputStream {
+
+    public void write(int b) throws IOException;
+
+    public void write(byte b[], int off, int len) throws IOException;
+
+    public void writeBoolean(boolean v) throws IOException;
+
+    public void writeByte(int v) throws IOException;
+
+    public void writeShort(int v) throws IOException;
+
+    public void writeChar(int v) throws IOException;
+
+    public void writeInt(int v) throws IOException;
+
+    public void writeLong(long v) throws IOException;
+
+    public void writeFloat(float v) throws IOException;
+
+    public void writeDouble(double v) throws IOException;
+
+    public void writeBytes(String s) throws IOException;
+
+    public void writeChars(String s) throws IOException;
+
+    public void writeUTF(String s) throws IOException;
+
+}
diff a/test/lib/org/openjdk/asmtools/jasm/ClassArrayAttr.java b/test/lib/org/openjdk/asmtools/jasm/ClassArrayAttr.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/ClassArrayAttr.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.asmtools.jasm;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Base class of the "classes[]" data of attributes
+ * <p>
+ * JEP 181 (Nest-based Access Control): class file 55.0
+ * NestMembers_attribute {
+ * u2 attribute_name_index;
+ * u4 attribute_length;
+ * u2 number_of_classes;
+ * u2 classes[number_of_classes];
+ * }
+ * <p>
+ * JEP 360 (Sealed types): class file 59.65535
+ * PermittedSubclasses_attribute {
+ * u2 attribute_name_index;
+ * u4 attribute_length;
+ * u2 number_of_classes;
+ * u2 classes[number_of_classes];
+ * }
+ */
+public class ClassArrayAttr extends AttrData {
+
+    List<ConstantPool.ConstCell> classes;
+
+    public ClassArrayAttr(String attributeName, ClassData cdata, List<ConstantPool.ConstCell> classes) {
+        super(cdata, attributeName);
+        this.classes = classes;
+    }
+
+    @Override
+    public int attrLength() {
+        return 2 + classes.size() * 2;
+    }
+
+    @Override
+    public void write(CheckedDataOutputStream out) throws IOException {
+        super.write(out);
+        out.writeShort(classes.size());
+        for (ConstantPool.ConstCell c : classes) {
+            out.writeShort(c.arg);
+        }
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/ClassData.java b/test/lib/org/openjdk/asmtools/jasm/ClassData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/ClassData.java
@@ -0,0 +1,650 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jasm;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.openjdk.asmtools.jasm.Tables.*;
+
+/**
+ * ClassData
+ *
+ * This is the main data structure for representing parsed class data. This structure
+ * renders directly to a class file.
+ *
+ */
+class ClassData extends MemberData {
+
+    /* ClassData Fields */
+    CFVersion cfv;
+    ConstantPool.ConstCell me, father;
+    String myClassName;
+    AttrData sourceFileNameAttr;
+    ArrayList<Argument> interfaces;
+    ArrayList<FieldData> fields = new ArrayList<>();
+    ArrayList<MethodData> methods = new ArrayList<>();
+    DataVectorAttr<InnerClassData> innerClasses = null;
+    DataVectorAttr<BootstrapMethodData> bootstrapMethodsAttr = null;
+
+    // JEP 181 - NestHost, NestMembers attributes since class version 55.0
+    CPXAttr nestHostAttr;
+    NestMembersAttr nestMembersAttr;
+
+    // JEP 359 - Record attribute since class file 58.65535
+    private RecordData recordData;
+
+    // JEP 360 - PermittedSubclasses attribute since class file 59.65535
+    private PermittedSubclassesAttr permittedSubclassesAttr;
+
+    private PreloadAttr preloadAttr;
+
+    ModuleAttr moduleAttribute = null;
+    Environment env;
+    protected ConstantPool pool;
+
+    private static final String DEFAULT_EXTENSION = ".class";
+    String fileExtension = DEFAULT_EXTENSION;
+    public CDOutputStream cdos;
+
+    /**
+     * Initializes the ClassData.
+     *
+     * @param me The constant pool reference to this class
+     * @param father The constant pool reference to the super class
+     * @param interfaces A list of interfaces that this class implements
+     */
+    public final void init(int access, ConstantPool.ConstCell me, ConstantPool.ConstCell father, ArrayList<Argument> interfaces) {
+        this.access = access;
+
+        // normalize the modifiers to access flags
+        if (Modifiers.hasPseudoMod(access)) {
+            createPseudoMod();
+        }
+
+        this.me = me;
+        if (father == null) {
+            father = pool.FindCellClassByName("java/lang/Object");
+        }
+        this.father = father;
+        this.interfaces = interfaces;
+        // Set default class file version if it is not set.
+        cfv.initClassDefaults();
+    }
+
+    public final void initAsModule() {
+        this.access = RuntimeConstants.ACC_MODULE;
+        // this_class" module-info
+        this.me = pool.FindCellClassByName("module-info");
+        // super_class: zero
+        this.father = new ConstantPool.ConstCell(0);
+        cfv.initModuleDefaults();
+    }
+
+    /**
+     * canonical default constructor
+     *
+     * @param env The error reporting environment.
+     * @param cfv The class file version that this class file supports.
+     */
+    public ClassData(Environment env, CFVersion cfv) {
+        super(null, 0);  // for a class, these get inited in the super - later.
+        cls = this;
+
+        this.env = env;
+        this.cfv = cfv;
+
+        pool = new ConstantPool(env);
+        cdos = new CDOutputStream();
+    }
+
+    /**
+     * Predicate that describes if this class has an access flag indicating that it is an
+     * interface.
+     *
+     * @return True if the classes access flag indicates it is an interface.
+     */
+    public final boolean isInterface() {
+        return Modifiers.isInterface(access);
+    }
+
+    /*
+     * After a constant pool has been explicitly declared,
+     * this method links the Constant_InvokeDynamic Constant_Dynamic
+     * constants with any bootstrap methods that they index in the
+     * Bootstrap Methods Attribute
+     */
+    protected void relinkBootstrapMethods() {
+        if (bootstrapMethodsAttr == null) {
+            return;
+        }
+
+        env.traceln("relinkBootstrapMethods");
+
+        for (ConstantPool.ConstCell cell : pool) {
+            ConstantPool.ConstValue ref = null;
+            if (cell != null) {
+                ref = cell.ref;
+            }
+            if (ref != null
+                    && (ref.tag == ConstType.CONSTANT_INVOKEDYNAMIC || ref.tag == ConstType.CONSTANT_DYNAMIC)) {
+                // Find only the Constant
+                ConstantPool.ConstValue_IndyOrCondyPair refval = (ConstantPool.ConstValue_IndyOrCondyPair) ref;
+                BootstrapMethodData bsmdata = refval.bsmData;
+                // only care about BSM Data that were placeholders
+                if (bsmdata != null && bsmdata.isPlaceholder()) {
+                    // find the real BSM Data at the index
+                    int bsmindex = bsmdata.placeholder_index;
+                    if (bsmindex < 0 || bsmindex > bootstrapMethodsAttr.size()) {
+                        // bad BSM index --
+                        // give a warning, but place the index in the arg anyway
+                        env.traceln("Warning: (ClassData.relinkBootstrapMethods()): Bad bootstrapMethods index: " + bsmindex);
+                        // env.error("const.bsmindex", bsmindex);
+                        bsmdata.arg = bsmindex;
+                    } else {
+
+                        BootstrapMethodData realbsmdata = bootstrapMethodsAttr.get(bsmindex);
+                        // make the IndyPairs BSM Data point to the one from the attribute
+                        refval.bsmData = realbsmdata;
+                    }
+                }
+            }
+        }
+    }
+
+    protected void numberBootstrapMethods() {
+        env.traceln("Numbering Bootstrap Methods");
+        if (bootstrapMethodsAttr == null) {
+            return;
+        }
+
+        int index = 0;
+        for (BootstrapMethodData data : bootstrapMethodsAttr) {
+            data.arg = index++;
+        }
+    }
+
+    // API
+    // Record
+    public RecordData setRecord(int where) {
+        if( recordAttributeExists() ) {
+            env.error(where, "warn.record.repeated");
+        }
+        this.recordData = new RecordData(cls);
+        return this.recordData;
+    }
+
+    /**
+     * Rejects a record: removes the record attribute if there are no components
+     */
+    public void rejectRecord() {
+        this.recordData = null;
+    }
+
+    // Field
+    public ConstantPool.ConstValue_Pair mkNape(ConstantPool.ConstCell name, ConstantPool.ConstCell sig) {
+        return new ConstantPool.ConstValue_Pair(ConstType.CONSTANT_NAMEANDTYPE, name, sig);
+    }
+
+    public ConstantPool.ConstValue_Pair mkNape(String name, String sig) {
+        return mkNape(pool.FindCellAsciz(name), pool.FindCellAsciz(sig));
+    }
+
+    public FieldData addFieldIfAbsent(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell sig) {
+        ConstantPool.ConstValue_Pair nape = mkNape(name, sig);
+        env.traceln(" [ClassData.addFieldIfAbsent]:  #" + nape.left.arg + ":#" + nape.right.arg);
+        FieldData fd = getField(nape);
+        if( fd == null ) {
+            env.traceln(" [ClassData.addFieldIfAbsent]:  new field.");
+            fd = addField(access,nape);
+        }
+        return fd;
+    }
+
+    private FieldData getField(ConstantPool.ConstValue_Pair nape) {
+        for (FieldData fd : fields) {
+            if( fd.getNameDesc().equals(nape) ) {
+                return fd;
+            }
+        }
+        return null;
+    }
+
+    public FieldData addField(int access, ConstantPool.ConstValue_Pair nape) {
+        env.traceln(" [ClassData.addField]:  #" + nape.left.arg + ":#" + nape.right.arg);
+        FieldData res = new FieldData(this, access, nape);
+        fields.add(res);
+        return res;
+    }
+
+    public FieldData addField(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell sig) {
+        return addField(access, mkNape(name, sig));
+    }
+
+    public FieldData addField(int access, String name, String type) {
+        return addField(access, pool.FindCellAsciz(name), pool.FindCellAsciz(type));
+    }
+
+    public ConstantPool.ConstCell LocalFieldRef(FieldData field) {
+        return pool.FindCell(ConstType.CONSTANT_FIELD, me, pool.FindCell(field.getNameDesc()));
+    }
+
+    public ConstantPool.ConstCell LocalFieldRef(ConstantPool.ConstValue nape) {
+        return pool.FindCell(ConstType.CONSTANT_FIELD, me, pool.FindCell(nape));
+    }
+
+    public ConstantPool.ConstCell LocalFieldRef(ConstantPool.ConstCell name, ConstantPool.ConstCell sig) {
+        return LocalFieldRef(mkNape(name, sig));
+    }
+
+    public ConstantPool.ConstCell LocalFieldRef(String name, String sig) {
+        return LocalFieldRef(pool.FindCellAsciz(name), pool.FindCellAsciz(sig));
+    }
+
+    MethodData curMethod;
+
+    public MethodData StartMethod(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell sig, ArrayList exc_table) {
+        EndMethod();
+        env.traceln(" [ClassData.StartMethod]:  #" + name.arg + ":#" + sig.arg);
+        curMethod = new MethodData(this, access, name, sig, exc_table);
+        methods.add(curMethod);
+        return curMethod;
+    }
+
+    public void EndMethod() {
+        curMethod = null;
+    }
+
+    public ConstantPool.ConstCell LocalMethodRef(ConstantPool.ConstValue nape) {
+        return pool.FindCell(ConstType.CONSTANT_METHOD, me, pool.FindCell(nape));
+    }
+
+    public ConstantPool.ConstCell LocalMethodRef(ConstantPool.ConstCell name, ConstantPool.ConstCell sig) {
+        return LocalMethodRef(mkNape(name, sig));
+    }
+
+    void addLocVarData(int opc, Argument arg) {
+    }
+
+    public void addInnerClass(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell innerClass, ConstantPool.ConstCell outerClass) {
+        env.traceln("addInnerClass (with indexes: Name (" + name.toString() + "), Inner (" + innerClass.toString() + "), Outer (" + outerClass.toString() + ").");
+        if (innerClasses == null) {
+            innerClasses = new DataVectorAttr<>(this, AttrTag.ATT_InnerClasses.parsekey());
+        }
+        innerClasses.add(new InnerClassData(access, name, innerClass, outerClass));
+    }
+
+    public void addBootstrapMethod(BootstrapMethodData bsmData) {
+        env.traceln("addBootstrapMethod");
+        if (bootstrapMethodsAttr == null) {
+            bootstrapMethodsAttr = new DataVectorAttr<>(this, AttrTag.ATT_BootstrapMethods.parsekey());
+        }
+        bootstrapMethodsAttr.add(bsmData);
+    }
+
+    public void addNestHost(ConstantPool.ConstCell hostClass) {
+        env.traceln("addNestHost");
+        nestHostAttr = new CPXAttr(this, AttrTag.ATT_NestHost.parsekey(), hostClass);
+    }
+
+    public void addNestMembers(List<ConstantPool.ConstCell> classes) {
+        env.traceln("addNestMembers");
+        nestMembersAttr = new NestMembersAttr(this, classes);
+    }
+
+    public void addPermittedSubclasses(List<ConstantPool.ConstCell> classes) {
+        env.traceln("addPermittedSubclasses");
+        permittedSubclassesAttr = new PermittedSubclassesAttr(this, classes);
+    }
+
+    public void addPreloads(List<ConstantPool.ConstCell> classes) {
+        env.traceln("addPreloads");
+        preloadAttr = new PreloadAttr(this, classes);
+    }
+
+    public void endClass() {
+        sourceFileNameAttr = new CPXAttr(this,
+                AttrTag.ATT_SourceFile.parsekey(),
+                pool.FindCellAsciz(env.getSimpleInputFileName()));
+        pool.NumberizePool();
+        pool.CheckGlobals();
+        numberBootstrapMethods();
+        try {
+            me = pool.uncheckedGetCell(me.arg);
+            env.traceln("me=" + me);
+            ConstantPool.ConstValue_Cell me_value = (ConstantPool.ConstValue_Cell) me.ref;
+            ConstantPool.ConstCell ascicell = me_value.cell;
+            env.traceln("ascicell=" + ascicell);
+            ConstantPool.ConstValue_String me_str = (ConstantPool.ConstValue_String) ascicell.ref;
+            myClassName = me_str.value;
+            env.traceln("-------------------");
+            env.traceln("-- Constant Pool --");
+            env.traceln("-------------------");
+            pool.printPool();
+            env.traceln("-------------------");
+            env.traceln(" ");
+            env.traceln(" ");
+            env.traceln("-------------------");
+            env.traceln("-- Inner Classes --");
+            env.traceln("-------------------");
+            printInnerClasses();
+
+        } catch (Throwable e) {
+            env.traceln("check name:" + e);
+            env.error("no.classname");
+            e.printStackTrace();
+        }
+    }
+
+    public void endModule(ModuleAttr moduleAttr) {
+        moduleAttribute = moduleAttr.build();
+        pool.NumberizePool();
+        pool.CheckGlobals();
+        myClassName = "module-info";
+    }
+
+    private void printInnerClasses() {
+        if (innerClasses != null) {
+            int i = 1;
+            for (InnerClassData entry : innerClasses) {
+                env.trace(" InnerClass[" + i + "]: (" + Modifiers.toString(entry.access, CF_Context.CTX_INNERCLASS) + "]), ");
+                env.trace("Name:  " + entry.name.toString() + " ");
+                env.trace("IC_info:  " + entry.innerClass.toString() + " ");
+                env.trace("OC_info:  " + entry.outerClass.toString() + " ");
+                env.traceln(" ");
+                i += 1;
+            }
+        } else {
+            env.traceln("<< NO INNER CLASSES >>");
+        }
+
+    }
+
+    public void write(CheckedDataOutputStream out) throws IOException {
+
+        // Write the header
+        out.writeInt(JAVA_MAGIC);
+        out.writeShort(cfv.minor_version());
+        out.writeShort(cfv.major_version());
+
+        pool.write(out);
+        out.writeShort(access); // & MM_CLASS; // Q
+        out.writeShort(me.arg);
+        out.writeShort(father.arg);
+
+        // Write the interface names
+        if (interfaces != null) {
+            out.writeShort(interfaces.size());
+            for (Argument intf : interfaces) {
+                out.writeShort(intf.arg);
+            }
+        } else {
+            out.writeShort(0);
+        }
+
+        // Write the fields
+        if (fields != null) {
+            out.writeShort(fields.size());
+            for (FieldData field : fields) {
+                field.write(out);
+            }
+        } else {
+            out.writeShort(0);
+        }
+
+        // Write the methods
+        if (methods != null) {
+            out.writeShort(methods.size());
+            for (MethodData method : methods) {
+                method.write(out);
+            }
+        } else {
+            out.writeShort(0);
+        }
+
+        // Write the attributes
+        DataVector attrs = getAttrVector();
+        attrs.write(out);
+    } // end ClassData.write()
+
+    @Override
+    protected DataVector getAttrVector() {
+        DataVector attrs = new DataVector();
+        if( moduleAttribute != null ) {
+            if (annotAttrVis != null)
+                attrs.add(annotAttrVis);
+            if (annotAttrInv != null)
+                attrs.add(annotAttrInv);
+            attrs.add(moduleAttribute);
+        } else {
+            attrs.add(sourceFileNameAttr);
+            // JEP 359 since class file 58.65535
+            if( recordData != null ) {
+                attrs.add(recordData);
+            }
+            if (innerClasses != null)
+                attrs.add(innerClasses);
+            if (syntheticAttr != null)
+                attrs.add(syntheticAttr);
+            if (deprecatedAttr != null)
+                attrs.add(deprecatedAttr);
+            if (annotAttrVis != null)
+                attrs.add(annotAttrVis);
+            if (annotAttrInv != null)
+                attrs.add(annotAttrInv);
+            if (type_annotAttrVis != null)
+                attrs.add(type_annotAttrVis);
+            if (type_annotAttrInv != null)
+                attrs.add(type_annotAttrInv);
+            if (bootstrapMethodsAttr != null)
+                attrs.add(bootstrapMethodsAttr);
+            // since class version 55.0
+            if(nestHostAttributeExists())
+                attrs.add(nestHostAttr);
+            if(nestMembersAttributesExist())
+                attrs.add(nestMembersAttr);
+            // since class version 59.65535 (JEP 360)
+            if ( permittedSubclassesAttributesExist() )
+                attrs.add(permittedSubclassesAttr);
+            if (preloadAttributeExists())
+                attrs.add(preloadAttr);
+        }
+        return attrs;
+    }
+
+    static char fileSeparator; //=System.getProperty("file.separator");
+
+    /**
+     * Writes to the directory passed with -d option
+     */
+    public void write(File destdir) throws IOException {
+        File outfile;
+        if (destdir == null) {
+            int startofname = myClassName.lastIndexOf("/");
+            if (startofname != -1) {
+                myClassName = myClassName.substring(startofname + 1);
+            }
+            outfile = new File(myClassName + fileExtension);
+        } else {
+            env.traceln("writing -d " + destdir.getPath());
+            if (fileSeparator == 0) {
+                fileSeparator = System.getProperty("file.separator").charAt(0);
+            }
+            if (fileSeparator != '/') {
+                myClassName = myClassName.replace('/', fileSeparator);
+            }
+            outfile = new File(destdir, myClassName + fileExtension);
+            File outdir = new File(outfile.getParent());
+            if (!outdir.exists() && !outdir.mkdirs()) {
+                env.error("cannot.write", outdir.getPath());
+                return;
+            }
+        }
+
+        DataOutputStream dos = new DataOutputStream(
+                new BufferedOutputStream(new FileOutputStream(outfile)));
+        cdos.setDataOutputStream(dos);
+        try {
+            write(cdos);
+        } finally {
+            dos.close();
+        }
+    }  // end write()
+
+    public void setByteLimit(int bytelimit) {
+        cdos.enable();
+        cdos.setLimit(bytelimit);
+    }
+
+    public boolean nestHostAttributeExists() {
+        return nestHostAttr != null;
+    }
+
+    public boolean nestMembersAttributesExist() { return nestMembersAttr != null;  }
+
+    public boolean permittedSubclassesAttributesExist() { return permittedSubclassesAttr != null;  }
+
+    public boolean recordAttributeExists() { return recordData != null;  }
+
+    public boolean preloadAttributeExists() { return preloadAttr != null; }
+
+    /**
+     * This is a wrapper for DataOutputStream, used for debugging purposes. it allows
+     * writing the byte-stream of a class up to a given byte number.
+     */
+    static private class CDOutputStream implements CheckedDataOutputStream {
+
+        private int bytelimit;
+        private DataOutputStream dos;
+        public boolean enabled = false;
+
+        public CDOutputStream() {
+            dos = null;
+        }
+
+        public CDOutputStream(OutputStream out) {
+            setOutputStream(out);
+        }
+
+        public final void setOutputStream(OutputStream out) {
+            dos = new DataOutputStream(out);
+        }
+
+        public void setDataOutputStream(DataOutputStream dos) {
+            this.dos = dos;
+        }
+
+        public void setLimit(int lim) {
+            bytelimit = lim;
+        }
+
+        public void enable() {
+            enabled = true;
+        }
+
+        private synchronized void check(String loc) throws IOException {
+            if (enabled && dos.size() >= bytelimit) {
+                throw new IOException(loc);
+            }
+        }
+
+        @Override
+        public synchronized void write(int b) throws IOException {
+            dos.write(b);
+            check("Writing byte: " + b);
+        }
+
+        @Override
+        public synchronized void write(byte b[], int off, int len) throws IOException {
+            dos.write(b, off, len);
+            check("Writing byte-array: " + b);
+        }
+
+        @Override
+        public final void writeBoolean(boolean v) throws IOException {
+            dos.writeBoolean(v);
+            check("Writing writeBoolean: " + (v ? "true" : "false"));
+        }
+
+        @Override
+        public final void writeByte(int v) throws IOException {
+            dos.writeByte(v);
+            check("Writing writeByte: " + v);
+        }
+
+        @Override
+        public void writeShort(int v) throws IOException {
+            dos.writeShort(v);
+            check("Writing writeShort: " + v);
+        }
+
+        @Override
+        public void writeChar(int v) throws IOException {
+            dos.writeChar(v);
+            check("Writing writeChar: " + v);
+        }
+
+        @Override
+        public void writeInt(int v) throws IOException {
+            dos.writeInt(v);
+            check("Writing writeInt: " + v);
+        }
+
+        @Override
+        public void writeLong(long v) throws IOException {
+            dos.writeLong(v);
+            check("Writing writeLong: " + v);
+        }
+
+        @Override
+        public void writeFloat(float v) throws IOException {
+            dos.writeFloat(v);
+            check("Writing writeFloat: " + v);
+        }
+
+        @Override
+        public void writeDouble(double v) throws IOException {
+            dos.writeDouble(v);
+            check("Writing writeDouble: " + v);
+        }
+
+        @Override
+        public void writeBytes(String s) throws IOException {
+            dos.writeBytes(s);
+            check("Writing writeBytes: " + s);
+        }
+
+        @Override
+        public void writeChars(String s) throws IOException {
+            dos.writeChars(s);
+            check("Writing writeChars: " + s);
+        }
+
+        @Override
+        public void writeUTF(String s) throws IOException {
+            dos.writeUTF(s);
+            check("Writing writeUTF: " + s);
+        }
+    }
+}// end class ClassData
diff a/test/lib/org/openjdk/asmtools/jasm/CodeAttr.java b/test/lib/org/openjdk/asmtools/jasm/CodeAttr.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/CodeAttr.java
@@ -0,0 +1,529 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jasm;
+
+import org.openjdk.asmtools.jasm.OpcodeTables.Opcode;
+import org.openjdk.asmtools.jasm.Tables.AttrTag;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import static org.openjdk.asmtools.jasm.RuntimeConstants.SPLIT_VERIFIER_CFV;
+
+class CodeAttr extends AttrData {
+
+    protected ClassData cls;
+
+    protected MethodData mtd;
+    protected Environment env;
+    protected Argument max_stack, max_locals;
+    protected Instr zeroInstr, lastInstr;
+    protected int cur_pc = 0;
+    protected DataVector<TrapData> trap_table; // TrapData
+    protected DataVectorAttr<LineNumData> lin_num_tb; // LineNumData
+    protected int lastln = 0;
+    protected DataVectorAttr<LocVarData> loc_var_tb;  // LocVarData
+    protected DataVector<DataVectorAttr<? extends Data>> attrs;
+    protected ArrayList<Integer> slots;
+    protected HashMap<String, LocVarData> locvarsHash;
+    protected HashMap<String, Label> labelsHash;
+    protected HashMap<String, Trap> trapsHash;
+    protected StackMapData curMapEntry = null;
+    protected DataVectorAttr<StackMapData> stackMap;
+    // type annotations
+    protected DataVectorAttr<TypeAnnotationData> type_annotAttrVis = null;
+    protected DataVectorAttr<TypeAnnotationData> type_annotAttrInv = null;
+
+    public CodeAttr(MethodData mtd, int pos, int paramcnt, Argument max_stack, Argument max_locals) {
+        super(mtd.cls, AttrTag.ATT_Code.parsekey());
+        this.mtd = mtd;
+        this.cls = mtd.cls;
+        this.env = cls.env;
+        this.max_stack = max_stack;
+        this.max_locals = max_locals;
+        lastInstr = zeroInstr = new Instr();
+        trap_table = new DataVector<>(0); // TrapData
+        attrs = new DataVector<>();
+        if (env.debugInfoFlag) {
+            lin_num_tb = new DataVectorAttr<>(cls, AttrTag.ATT_LineNumberTable.parsekey());
+            attrs.add(lin_num_tb);
+        }
+        slots = new ArrayList<>(paramcnt);
+        for (int k = 0; k < paramcnt; k++) {
+            slots.add(k, 1);
+        }
+    }
+
+    void endCode() {
+        checkTraps();
+        checkLocVars();
+        checkLabels();
+        //
+        if (type_annotAttrVis != null) {
+            attrs.add(type_annotAttrVis);
+        }
+        if (type_annotAttrInv != null) {
+            attrs.add(type_annotAttrInv);
+        }
+    }
+
+    public void addAnnotations(ArrayList<AnnotationData> list) {
+        for (AnnotationData item : list) {
+            boolean invisible = item.invisible;
+            if (item instanceof TypeAnnotationData) {
+                // Type Annotations
+                TypeAnnotationData ta = (TypeAnnotationData) item;
+                if (invisible) {
+                    if (type_annotAttrInv == null) {
+                        type_annotAttrInv = new DataVectorAttr(cls,
+                                AttrTag.ATT_RuntimeInvisibleTypeAnnotations.parsekey());
+                    }
+                    type_annotAttrInv.add(ta);
+                } else {
+                    if (type_annotAttrVis == null) {
+                        type_annotAttrVis = new DataVectorAttr(cls,
+                                AttrTag.ATT_RuntimeVisibleTypeAnnotations.parsekey());
+                    }
+                    type_annotAttrVis.add(ta);
+                }
+            }
+        }
+    }
+
+    /* -------------------------------------- Traps */
+    Trap trapDecl(int pos, String name) {
+        Trap local;
+        if (trapsHash == null) {
+            trapsHash = new HashMap<>(10);
+            local = null;
+        } else {
+            local = trapsHash.get(name);
+        }
+        if (local == null) {
+            local = new Trap(pos, name);
+            trapsHash.put(name, local);
+        }
+        return local;
+    }
+
+    void beginTrap(int pos, String name) {
+        Trap trap = trapDecl(pos, name);
+        if (trap.start_pc != Argument.NotSet) {
+            env.error("trap.tryredecl", name);
+            return;
+        }
+        trap.start_pc = cur_pc;
+    }
+
+    void endTrap(int pos, String name) {
+        Trap trap = trapDecl(pos, name);
+        if (trap.end_pc != Argument.NotSet) {
+            env.error("trap.endtryredecl", name);
+            return;
+        }
+        trap.end_pc = cur_pc;
+    }
+
+    void trapHandler(int pos, String name, Argument type) {
+        Trap trap = trapDecl(pos, name);
+        trap.refd = true;
+        TrapData trapData = new TrapData(pos, trap, cur_pc, type);
+        trap_table.addElement(trapData);
+    }
+
+    void checkTraps() {
+        if (trapsHash == null) {
+            return;
+        }
+        for (Trap trap : trapsHash.values()) {
+            if (!trap.refd) {
+                env.error(trap.pos, "warn.trap.notref", trap.name);
+            }
+        }
+
+        for (TrapData trapData : trap_table) {
+            Trap trapLabel = trapData.trap;
+            if (trapLabel.start_pc == Argument.NotSet) {
+                env.error(trapData.pos, "trap.notry", trapLabel.name);
+            }
+            if (trapLabel.end_pc == Argument.NotSet) {
+                env.error(trapData.pos, "trap.noendtry", trapLabel.name);
+            }
+        }
+    }
+
+    /* -------------------------------------- Labels */
+    Label labelDecl(String name) {
+        Label local;
+        if (labelsHash == null) {
+            labelsHash = new HashMap<>(10);
+            local = null;
+        } else {
+            local = labelsHash.get(name);
+        }
+        if (local == null) {
+            local = new Label(name);
+            labelsHash.put(name, local);
+        }
+        return local;
+    }
+
+    public Label LabelDef(int pos, String name) {
+        Label label = labelDecl(name);
+        if (label.defd) {
+            env.error(pos, "label.redecl", name);
+            return null;
+        }
+        label.defd = true;
+        label.arg = cur_pc;
+        return label;
+    }
+
+    public Label LabelRef(String name) {
+        Label label = labelDecl(name);
+        label.refd = true;
+        return label;
+    }
+
+    void checkLabels() {
+        if (labelsHash == null) {
+            return;
+        }
+
+        for (Label local : labelsHash.values()) {
+            // check that every label is defined
+            if (!local.defd) {
+                env.error("label.undecl", local.name);
+            }
+        }
+    }
+
+    /* -------------------------------------- Variables */
+    LocVarData locvarDecl(String name) {
+        LocVarData local;
+        if (locvarsHash == null) {
+            locvarsHash = new HashMap<>(10);
+            local = null;
+        } else {
+            local = locvarsHash.get(name);
+        }
+        if (local == null) {
+            local = new LocVarData(name);
+            locvarsHash.put(name, local);
+        }
+        return local;
+    }
+
+    public void LocVarDataDef(int slot) {
+        slots.set(slot, 1);
+        if ((max_locals != null) && (max_locals.arg < slots.size())) {
+            env.error("warn.illslot", Integer.toString(slot));
+        }
+    }
+
+    public void LocVarDataDef(String name, ConstantPool.ConstCell type) {
+        LocVarData locvar = locvarDecl(name);
+        if (locvar.defd) {
+            env.error("locvar.redecl", name);
+            return;
+        }
+        locvar.defd = true;
+        locvar.start_pc = (short) cur_pc;
+        locvar.name_cpx = cls.pool.FindCellAsciz(name);
+        locvar.sig_cpx = type;
+        int k;
+        findSlot:
+        {
+            for (k = 0; k < slots.size(); k++) {
+                if (slots.get(k) == 0) {
+                    break findSlot;
+                }
+            }
+            k = slots.size();
+        }
+        LocVarDataDef(k);
+        locvar.arg = k;
+        if (loc_var_tb == null) {
+            loc_var_tb = new DataVectorAttr<>(cls, AttrTag.ATT_LocalVariableTable.parsekey());
+            attrs.add(loc_var_tb);
+        }
+        loc_var_tb.add(locvar);
+    }
+
+    public Argument LocVarDataRef(String name) {
+        LocVarData locvar = locvarDecl(name);
+        if (!locvar.defd) {
+            env.error("locvar.undecl", name);
+            locvar.defd = true; // to avoid multiple error messages
+        }
+        locvar.refd = true;
+        return locvar;
+    }
+
+    public void LocVarDataEnd(int slot) {
+        slots.set(slot, 0);
+    }
+
+    public void LocVarDataEnd(String name) {
+        LocVarData locvar = locvarsHash.get(name);
+        if (locvar == null) {
+            env.error("locvar.undecl", name);
+            return;
+        } else if (!locvar.defd) {
+            env.error("locvar.undecl", name);
+            return;
+        }
+        locvar.length = (short) (cur_pc - locvar.start_pc);
+
+        slots.set(locvar.arg, 0);
+        locvarsHash.put(name, new LocVarData(name));
+    }
+
+    void checkLocVars() {
+        if (locvarsHash == null) {
+            return;
+        }
+        for (LocVarData locvar : locvarsHash.values()) {
+            if (!locvar.defd) {
+                continue;
+            } // this is false locvar
+            // set end of scope, if not set
+            if (slots.get(locvar.arg) == 1) {
+                locvar.length = (short) (cur_pc - locvar.start_pc);
+                slots.set(locvar.arg, 0);
+            }
+        }
+    }
+
+    /* -------------------------------------- StackMap */
+    public StackMapData getStackMap() {
+        if (curMapEntry == null) {
+            curMapEntry = new StackMapData(env);
+            if (cls.cfv.major_version() >= SPLIT_VERIFIER_CFV) {
+                curMapEntry.setIsStackMapTable(true);
+            }
+        }
+        return curMapEntry;
+    }
+
+    /*====================================================== Instr */
+    void addInstr(int mnenoc_pos, Opcode opcode, Argument arg, Object arg2) {
+        Instr newInstr = new Instr(cur_pc, cls.env.pos, opcode, arg, arg2);
+        lastInstr.next = newInstr;
+        lastInstr = newInstr;
+        int len = opcode.length();
+        switch (opcode) {
+            case opc_tableswitch:
+                len = ((SwitchTable) arg2).recalcTableSwitch(cur_pc);
+                break;
+            case opc_lookupswitch:
+                len = ((SwitchTable) arg2).calcLookupSwitch(cur_pc);
+                break;
+            case opc_ldc:
+                ((ConstantPool.ConstCell) arg).setRank(ConstantPool.ReferenceRank.LDC);
+                break;
+            default:
+                if (arg instanceof ConstantPool.ConstCell) {
+                    ((ConstantPool.ConstCell) arg).setRank(ConstantPool.ReferenceRank.ANY);
+                }
+        }
+        if (env.debugInfoFlag) {
+            int ln = env.lineNumber(mnenoc_pos);
+            if (ln != lastln) { // only one entry in lin_num_tb per line
+                lin_num_tb.add(new LineNumData(cur_pc, ln));
+                lastln = ln;
+            }
+        }
+        if (curMapEntry != null) {
+            curMapEntry.pc = cur_pc;
+            StackMapData prevStackFrame = null;
+            if (stackMap == null) {
+                if (cls.cfv.major_version() >= SPLIT_VERIFIER_CFV) {
+                    stackMap = new DataVectorAttr<>(cls, AttrTag.ATT_StackMapTable.parsekey());
+                } else {
+                    stackMap = new DataVectorAttr<>(cls, AttrTag.ATT_StackMap.parsekey());
+                }
+                attrs.add(stackMap);
+            } else if (stackMap.size() > 0) {
+                prevStackFrame = stackMap.get(stackMap.size() - 1);
+            }
+            curMapEntry.setOffset(prevStackFrame);
+            stackMap.add(curMapEntry);
+            curMapEntry = null;
+        }
+
+        cur_pc += len;
+    }
+
+    /*====================================================== Attr interface */
+    // subclasses must redefine this
+    @Override
+    public int attrLength() {
+        return 2 + 2 + 4 // for max_stack, max_locals, and cur_pc
+                + cur_pc //      + 2+trap_table.size()*8
+                + trap_table.getLength() + attrs.getLength();
+    }
+
+    @Override
+    public void write(CheckedDataOutputStream out)
+            throws IOException, Parser.CompilerError {
+        int mxstck = (max_stack != null) ? max_stack.arg : 0;
+        int mxloc = (max_locals != null) ? max_locals.arg : slots.size();
+        super.write(out);  // attr name, attr len
+        out.writeShort(mxstck);
+        out.writeShort(mxloc);
+        out.writeInt(cur_pc);
+        for (Instr instr = zeroInstr.next; instr != null; instr = instr.next) {
+            instr.write(out, env);
+        }
+
+        trap_table.write(out);
+
+        attrs.write(out);
+    }
+
+    /*-------------------------------------------------------- */
+    /* CodeAttr inner classes */
+    static public class Local extends Argument {
+
+        String name;
+        boolean defd = false, refd = false;
+
+        public Local(String name) {
+            this.name = name;
+        }
+    }
+
+    /**
+     *
+     */
+    static public class Label extends Local {
+
+        public Label(String name) {
+            super(name);
+        }
+    }
+
+    /**
+     *
+     */
+    class LocVarData extends Local implements Data {
+
+        // arg means slot
+        short start_pc, length;
+        ConstantPool.ConstCell name_cpx, sig_cpx;
+
+        public LocVarData(String name) {
+            super(name);
+        }
+
+        @Override
+        public int getLength() {
+            return 10;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeShort(start_pc);
+            out.writeShort(length);
+            out.writeShort(name_cpx.arg);
+            out.writeShort(sig_cpx.arg);
+            out.writeShort(arg);
+        }
+    }
+
+    /**
+     *
+     */
+    class LineNumData implements Data {
+
+        int start_pc, line_number;
+
+        public LineNumData(int start_pc, int line_number) {
+            this.start_pc = start_pc;
+            this.line_number = line_number;
+        }
+
+        @Override
+        public int getLength() {
+            return 4;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeShort(start_pc);
+            out.writeShort(line_number);
+        }
+    }
+
+    /**
+     *
+     */
+    class Trap extends Local {
+
+        int start_pc = Argument.NotSet, end_pc = Argument.NotSet;
+        int pos;
+
+        Trap(int pos, String name) {
+            super(name);
+            this.pos = pos;
+        }
+    }
+
+    /**
+     *
+     */
+    class TrapData implements Data {
+
+        int pos;
+        Trap trap;
+        int handler_pc;
+        Argument catchType;
+
+        public TrapData(int pos, Trap trap, int handler_pc, Argument catchType) {
+            this.pos = pos;
+            this.trap = trap;
+            this.handler_pc = handler_pc;
+            this.catchType = catchType;
+        }
+
+        @Override
+        public int getLength() {
+            return 8; // add the length of number of elements
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeShort(trap.start_pc);
+            out.writeShort(trap.end_pc);
+            out.writeShort(handler_pc);
+            if (catchType.isSet()) {
+                out.writeShort(catchType.arg);
+            } else {
+                out.writeShort(0);
+            }
+        }
+    }  // end TrapData
+} // end CodeAttr
+
diff a/test/lib/org/openjdk/asmtools/jasm/ConstantPool.java b/test/lib/org/openjdk/asmtools/jasm/ConstantPool.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/ConstantPool.java
@@ -0,0 +1,1264 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jasm;
+
+import org.openjdk.asmtools.jasm.Tables.ConstType;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.stream.Collectors;
+
+/**
+ * ConstantPool
+ *
+ * ConstantPool is the class responsible for maintaining constants for a given class file.
+ *
+ */
+public class ConstantPool implements Iterable<ConstantPool.ConstCell> {
+
+
+    static public enum ReferenceRank {
+        LDC(0),  // 0 - highest - ref from ldc
+        ANY(1),  // 1 - any ref
+        NO(2);   // 2 - no ref
+        final int rank;
+        ReferenceRank(int rank) {
+            this.rank = rank;
+        }
+    }
+
+    /*-------------------------------------------------------- */
+    /* ConstantPool Inner Classes */
+    /**
+     * ConstValue
+     *
+     * A (typed) tagged value in the constant pool.
+     */
+    static public class ConstValue {
+
+        protected ConstType tag;
+        protected boolean isSet = false;
+        private boolean visited = false;
+
+        public ConstValue(ConstType tag) {
+            this.tag = tag;
+        }
+
+        public int size() {
+            return 1;
+        }
+
+        public boolean hasValue() {
+            return isSet;
+        }
+
+        /**
+         * Compute the hash-code, based on the value of the native (_hashCode()) hashcode.
+         */
+        @Override
+        public int hashCode() {
+            if (visited) {
+                throw new Parser.CompilerError("CV hash:" + this);
+            }
+            visited = true;
+            int res = _hashCode() + tag.value() * 1023;
+            visited = false;
+            return res;
+        }
+
+        // sub-classes override this.
+        // this is the default for getting a hash code.
+        protected int _hashCode() {
+            return 37;
+        }
+
+        /**
+         * Compares this object to the specified object.
+         *
+         * Sub-classes must override this
+         *
+         * @param obj the object to compare with
+         * @return true if the objects are the same; false otherwise.
+         */
+        @Override
+        public boolean equals(Object obj) {
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            String tagstr = tag.printval();
+            String retval = "";
+            if (tagstr == null) {
+                return "BOGUS_TAG:" + tag;
+            }
+
+            String valueStr = _toString();
+            if (valueStr != null) {
+                retval = "<" + tagstr + " " + valueStr + ">";
+            } else {
+                retval = "<" + tagstr + ">";
+            }
+            return retval;
+        }
+
+        protected String _toString() {
+            return "";
+        }
+
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeByte(tag.value());
+        }
+    } // end ConstValue
+
+    /**
+     * ConstValue
+     *
+     * A (typed) tagged value in the constant pool.
+     */
+    static public class ConstValue_Zero extends ConstValue {
+
+        public ConstValue_Zero() {
+            super(ConstType.CONSTANT_ZERO);
+            isSet = false;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            throw new Parser.CompilerError("Trying to write Constant 0.");
+        }
+    }
+
+    /**
+     * ConstValue
+     *
+     * A (typed) tagged value in the constant pool.
+     */
+    static public class ConstValue_String extends ConstValue {
+
+        String value;
+
+        public ConstValue_String(String value) {
+            super(ConstType.CONSTANT_UTF8);
+            this.value = value;
+            isSet = (value != null);
+        }
+
+        @Override
+        protected String _toString() {
+            return value;
+        }
+
+        @Override
+        protected int _hashCode() {
+            return value.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if ((obj == null) || !(obj instanceof ConstValue_String)) {
+                return false;
+            }
+            ConstValue_String dobj = (ConstValue_String) obj;
+            if (tag != dobj.tag) {
+                return false;
+            }
+            return value.equals(dobj.value);
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            super.write(out);
+            out.writeUTF(value);
+        }
+    }
+
+    /**
+     * ConstValue
+     *
+     * A (typed) tagged value in the constant pool.
+     */
+    static public class ConstValue_Integer extends ConstValue {
+
+        Integer value;
+
+        public ConstValue_Integer(ConstType tag, Integer value) {
+            super(tag);
+            this.value = value;
+            isSet = (value != null);
+        }
+
+        @Override
+        protected String _toString() {
+            return value.toString();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if ((obj == null) || !(obj instanceof ConstValue_Integer)) {
+                return false;
+            }
+            ConstValue_Integer dobj = (ConstValue_Integer) obj;
+            if (tag != dobj.tag) {
+                return false;
+            }
+            return value.equals(dobj.value);
+        }
+
+        @Override
+        protected int _hashCode() {
+            return value.hashCode();
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            super.write(out);
+            out.writeInt(value.intValue());
+        }
+    }
+
+    /**
+     * ConstValue
+     *
+     * A (typed) tagged value in the constant pool.
+     */
+    static public class ConstValue_Long extends ConstValue {
+
+        Long value;
+
+        public ConstValue_Long(ConstType tag, Long value) {
+            super(tag);
+            this.value = value;
+            isSet = (value != null);
+        }
+
+        @Override
+        public int size() {
+            return 2;
+        }
+
+        @Override
+        protected String _toString() {
+            return value.toString();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if ((obj == null) || !(obj instanceof ConstValue_Long)) {
+                return false;
+            }
+            ConstValue_Long dobj = (ConstValue_Long) obj;
+            if (tag != dobj.tag) {
+                return false;
+            }
+            return value.equals(dobj.value);
+        }
+
+        @Override
+        protected int _hashCode() {
+            return value.hashCode();
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            super.write(out);
+            out.writeLong(value.longValue());
+        }
+    }
+
+    /**
+     * ConstValue
+     *
+     * A (typed) tagged value in the constant pool.
+     */
+    static public class ConstValue_Cell extends ConstValue {
+
+        ConstCell cell;
+
+        public ConstValue_Cell(ConstType tag, ConstCell cell) {
+            super(tag);
+            this.cell = cell;
+            isSet = (cell != null);
+        }
+
+        @Override
+        protected String _toString() {
+            return cell.toString();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if ((obj == null) || !(obj instanceof ConstValue_Cell)) {
+                return false;
+            }
+            ConstValue_Cell dobj = (ConstValue_Cell) obj;
+            if (tag != dobj.tag) {
+                return false;
+            }
+            return cell.equals(dobj.cell);
+        }
+
+        @Override
+        protected int _hashCode() {
+            return cell.hashCode();
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            super.write(out);
+            cell.write(out);
+        }
+    }
+
+    /**
+     * ConstValue
+     *
+     * A (typed) tagged value in the constant pool.
+     */
+    static public class ConstValue_Pair extends ConstValue {
+
+        ConstCell left, right;
+
+        public ConstValue_Pair(ConstType tag, ConstCell left, ConstCell right) {
+            super(tag);
+            this.left = left;
+            this.right = right;
+            isSet = (left != null && right != null);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if ((obj == null) || !(obj instanceof ConstValue_Pair)) {
+                return false;
+            }
+            ConstValue_Pair dobj = (ConstValue_Pair) obj;
+            if (tag != dobj.tag) {
+                return false;
+            }
+            if (dobj.left != null)
+                if (!dobj.left.equals(left))
+                    return false;
+            if (dobj.right != null)
+                if (!dobj.right.equals(right))
+                    return false;
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return super.toString() + "{" + left + "," + right + "}";
+        }
+
+        @Override
+        protected int _hashCode() {
+            return left.hashCode() * right.hashCode();
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            super.write(out);
+            if (tag == ConstType.CONSTANT_METHODHANDLE) {
+                out.writeByte(left.arg); // write subtag value
+            } else {
+                out.writeShort(left.arg);
+            }
+            out.writeShort(right.arg);
+        }
+    }
+
+    static public class ConstValue_IndyOrCondyPair extends ConstValue {
+        BootstrapMethodData bsmData;
+        ConstantPool.ConstCell napeCell;
+
+        protected ConstValue_IndyOrCondyPair(ConstType tag, BootstrapMethodData bsmdata, ConstCell napeCell) {
+            super(tag);
+            assert (tag == ConstType.CONSTANT_DYNAMIC && ConstValue_CondyPair.class.isAssignableFrom(getClass())) ||
+                   tag == ConstType.CONSTANT_INVOKEDYNAMIC && ConstValue_IndyPair.class.isAssignableFrom(getClass());
+
+            this.bsmData = bsmdata;
+            this.napeCell = napeCell;
+            isSet = (bsmdata != null && napeCell != null);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if ((obj == null) || !(getClass().isInstance(obj))) {
+                return false;
+            }
+
+            ConstValue_IndyOrCondyPair iobj = (ConstValue_IndyOrCondyPair) obj;
+            return (iobj.bsmData == bsmData) && (iobj.napeCell == napeCell);
+        }
+
+        @Override
+        public String toString() {
+            return super.toString() + "{" + bsmData + "," + napeCell + "}";
+        }
+
+        @Override
+        protected int _hashCode() {
+            if (bsmData.isPlaceholder()) {
+                return napeCell.hashCode();
+            }
+            return bsmData.hashCode() * napeCell.hashCode();
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            super.write(out);
+            out.writeShort(bsmData.arg);
+            out.writeShort(napeCell.arg);
+        }
+    }
+    /**
+     * ConstValue
+     *
+     * A (typed) tagged value in the constant pool.
+     */
+    static public class ConstValue_CondyPair extends ConstValue_IndyOrCondyPair {
+
+        public ConstValue_CondyPair(BootstrapMethodData bsmdata, ConstCell napeCell) {
+            super(ConstType.CONSTANT_DYNAMIC, bsmdata, napeCell);
+        }
+    }
+
+    /**
+     * ConstValue
+     *
+     * A (typed) tagged value in the constant pool.
+     */
+    static public class ConstValue_IndyPair extends ConstValue_IndyOrCondyPair {
+
+        public ConstValue_IndyPair(BootstrapMethodData bsmdata, ConstCell napeCell) {
+            super(ConstType.CONSTANT_INVOKEDYNAMIC, bsmdata, napeCell);
+        }
+    }
+
+    /*-------------------------------------------------------- */
+    /* ConstantPool Inner Classes */
+    /**
+     * ConstantCell
+     *
+     * ConstantCell is a type of data that can be in a constant pool.
+     */
+    static public class ConstCell extends Argument implements Data {
+
+        ConstValue ref;
+        // 0 - highest - ref from ldc, 1 - any ref, 2 - no ref
+        ReferenceRank rank = ReferenceRank.NO;
+
+        ConstCell(int arg, ConstValue ref) {
+            this.arg = arg;
+            this.ref = ref;
+        }
+
+        ConstCell(ConstValue ref) {
+            this(NotSet, ref);
+        }
+
+        ConstCell(int arg) {
+            this(arg, null);
+        }
+
+        @Override
+        public int getLength() {
+            return 2;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeShort(arg);
+        }
+
+        public void setRank(ReferenceRank rank) {
+            // don't change a short ref to long due to limitation of ldc - max 256 indexes allowed
+            if( this.rank != ReferenceRank.LDC) {
+                this.rank = rank;
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            if (arg == NotSet) {
+                if (ref != null) {
+                    return ref.hashCode();
+                } else {
+                    throw new Parser.CompilerError("Can't generate Hash Code, Null ConstCell Reference.");
+                }
+            }
+            return arg;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == null) {
+                return false;
+            }
+            ConstCell cc = (ConstCell)obj;
+            if( cc.ref == null ) {
+                return this.ref == null && cc.rank == this.rank;
+            }
+            return cc.ref.equals(this.ref) && cc.rank == this.rank;
+        }
+
+        public boolean isUnset() {
+            return (arg == NotSet) && (ref == null);
+        }
+
+        @Override
+        public String toString() {
+            return "#" + arg + "=" + ref;
+        }
+    }
+
+    /**
+     * CPVisitor
+     *
+     * CPVisitor base class defining a visitor for decoding constants.
+     */
+    public static class CPTagVisitor<R> implements Constants {
+
+        public CPTagVisitor() {
+        }
+
+        public final R visit(ConstType tag) {
+            R retVal = null;
+            switch (tag) {
+                case CONSTANT_UTF8:
+                    retVal = visitUTF8(tag);
+                    break;
+                case CONSTANT_INTEGER:
+                    retVal = visitInteger(tag);
+                    break;
+                case CONSTANT_FLOAT:
+                    retVal = visitFloat(tag);
+                    break;
+                case CONSTANT_DOUBLE:
+                    retVal = visitDouble(tag);
+                    break;
+                case CONSTANT_LONG:
+                    retVal = visitLong(tag);
+                    break;
+                case CONSTANT_METHODTYPE:
+                    retVal = visitMethodtype(tag);
+                    break;
+                case CONSTANT_STRING:
+                    retVal = visitString(tag);
+                    break;
+                case CONSTANT_CLASS:
+                    retVal = visitClass(tag);
+                    break;
+                case CONSTANT_METHOD:
+                    retVal = visitMethod(tag);
+                    break;
+                case CONSTANT_FIELD:
+                    retVal = visitField(tag);
+                    break;
+                case CONSTANT_INTERFACEMETHOD:
+                    retVal = visitInterfacemethod(tag);
+                    break;
+                case CONSTANT_NAMEANDTYPE:
+                    retVal = visitNameandtype(tag);
+                    break;
+                case CONSTANT_METHODHANDLE:
+                    retVal = visitMethodhandle(tag);
+                    break;
+                case CONSTANT_DYNAMIC:
+                    retVal = visitDynamic(tag);
+                    break;
+                case CONSTANT_INVOKEDYNAMIC:
+                    retVal = visitInvokedynamic(tag);
+                    break;
+                default:
+                    visitDefault(tag);
+            }
+            return retVal;
+        }
+
+        public R visitUTF8(ConstType tag) {
+            return null;
+        }
+
+        public R visitInteger(ConstType tag) {
+            return null;
+        }
+
+        public R visitFloat(ConstType tag) {
+            return null;
+        }
+
+        public R visitDouble(ConstType tag) {
+            return null;
+        }
+
+        public R visitLong(ConstType tag) {
+            return null;
+        }
+
+        public R visitMethodtype(ConstType tag) {
+            return null;
+        }
+
+        public R visitString(ConstType tag) {
+            return null;
+        }
+
+        public R visitClass(ConstType tag) {
+            return null;
+        }
+
+        public R visitMethod(ConstType tag) {
+            return null;
+        }
+
+        public R visitField(ConstType tag) {
+            return null;
+        }
+
+        public R visitInterfacemethod(ConstType tag) {
+            return null;
+        }
+
+        public R visitNameandtype(ConstType tag) {
+            return null;
+        }
+
+        public R visitMethodhandle(ConstType tag) {
+            return null;
+        }
+
+        public R visitDynamic(ConstType tag) {
+            return null;
+        }
+
+        public R visitInvokedynamic(ConstType tag) {
+            return null;
+        }
+
+        public R visitModule(ConstType tag) {
+            return null;
+        }
+
+        public R visitPackage(ConstType tag) {
+            return null;
+        }
+
+        public void visitDefault(ConstType tag) {
+        }
+    }
+
+    /**
+    * CPVisitor
+    *
+    * CPVisitor base class defining a visitor for decoding constants.
+    */
+   public static class CPVisitor<R> implements Constants {
+
+        public CPVisitor() {
+        }
+
+        public final R visit(ConstValue val) {
+            R retVal = null;
+            ConstType tag = val.tag;
+            switch (tag) {
+                case CONSTANT_UTF8:
+                    retVal = visitUTF8((ConstValue_String) val);
+                    break;
+                case CONSTANT_INTEGER:
+                    retVal = visitInteger((ConstValue_Integer) val);
+                    break;
+                case CONSTANT_FLOAT:
+                    retVal = visitFloat((ConstValue_Integer) val);
+                    break;
+                case CONSTANT_DOUBLE:
+                    retVal = visitDouble((ConstValue_Long) val);
+                    break;
+                case CONSTANT_LONG:
+                    retVal = visitLong((ConstValue_Long) val);
+                    break;
+                case CONSTANT_METHODTYPE:
+                    retVal = visitMethodtype((ConstValue_Cell) val);
+                    break;
+                case CONSTANT_STRING:
+                    retVal = visitString((ConstValue_Cell) val);
+                    break;
+                case CONSTANT_CLASS:
+                    retVal = visitClass((ConstValue_Cell) val);
+                    break;
+                case CONSTANT_METHOD:
+                    retVal = visitMethod((ConstValue_Pair) val);
+                    break;
+                case CONSTANT_FIELD:
+                    retVal = visitField((ConstValue_Pair) val);
+                    break;
+                case CONSTANT_INTERFACEMETHOD:
+                    retVal = visitInterfacemethod((ConstValue_Pair) val);
+                    break;
+                case CONSTANT_NAMEANDTYPE:
+                    retVal = visitNameandtype((ConstValue_Pair) val);
+                    break;
+                case CONSTANT_METHODHANDLE:
+                    retVal = visitMethodhandle((ConstValue_Pair) val);
+                    break;
+                case CONSTANT_DYNAMIC:
+                    retVal = visitDynamic((ConstValue_CondyPair) val);
+                    break;
+                case CONSTANT_INVOKEDYNAMIC:
+                    retVal = visitInvokedynamic((ConstValue_IndyPair) val);
+                    break;
+                case CONSTANT_MODULE:
+                    retVal = visitModule((ConstValue_Cell) val);
+                    break;
+                case CONSTANT_PACKAGE:
+                    retVal = visitPackage((ConstValue_Cell) val);
+                    break;
+                default:
+                    visitDefault(tag);
+            }
+            return retVal;
+        }
+
+        public R visitUTF8(ConstValue_String p) {
+            return null;
+        }
+
+        ;
+        public R visitInteger(ConstValue_Integer p) {
+            return null;
+        }
+
+        ;
+        public R visitFloat(ConstValue_Integer p) {
+            return null;
+        }
+
+        ;
+        public R visitDouble(ConstValue_Long p) {
+            return null;
+        }
+
+        ;
+        public R visitLong(ConstValue_Long p) {
+            return null;
+        }
+
+        ;
+        public R visitMethodtype(ConstValue_Cell p) {
+            return null;
+        }
+
+        ;
+        public R visitString(ConstValue_Cell p) {
+            return null;
+        }
+
+        ;
+        public R visitClass(ConstValue_Cell p) {
+            return null;
+        }
+
+        ;
+        public R visitMethod(ConstValue_Pair p) {
+            return null;
+        }
+
+        ;
+        public R visitField(ConstValue_Pair p) {
+            return null;
+        }
+
+        ;
+        public R visitInterfacemethod(ConstValue_Pair p) {
+            return null;
+        }
+
+        ;
+        public R visitNameandtype(ConstValue_Pair p) {
+            return null;
+        }
+
+        ;
+        public R visitMethodhandle(ConstValue_Pair p) {
+            return null;
+        }
+
+        ;
+        public R visitDynamic(ConstValue_CondyPair p) { return null;}
+
+        ;
+        public R visitInvokedynamic(ConstValue_IndyPair p) { return null;}
+
+        ;
+        public R visitModule(ConstValue_Cell p) { return null; }
+
+        ;
+        public R visitPackage(ConstValue_Cell p) { return null; }
+        ;
+
+        public void visitDefault(ConstType tag) {}
+        ;
+
+    }
+
+
+
+  /*-------------------------------------------------------- */
+  /* Constant Pool Fields */
+
+    private ArrayList<ConstCell> pool = new ArrayList<>(20);
+
+    private final ConstValue ConstValue0
+            = new ConstValue_String("");
+//    private final ConstValue ConstValue0 =
+//            new ConstValue(CONSTANT_UTF8, "");
+    private final ConstCell nullConst
+            = new ConstCell(null);
+    private final ConstCell constant_0
+            = new ConstCell(new ConstValue_Zero());
+//    private final ConstCell constant_0 =
+//            new ConstCell(new ConstValue(CONSTANT_ZERO, null));
+
+    // For hashing by value
+    Hashtable<ConstValue, ConstCell> cpoolHashByValue
+            = new Hashtable<>(40);
+
+    public Environment env;
+
+    private static boolean debugCP = false;
+
+    /*-------------------------------------------------------- */
+    /**
+     * main constructor
+     *
+     * @param env The error reporting environment
+     */
+    public ConstantPool(Environment env) {
+        this.env = env;
+        pool.add(constant_0);
+
+    }
+
+    public void debugStr(String s) {
+        if (debugCP) {
+            env.traceln(s);
+        }
+    }
+
+    @Override
+    public Iterator<ConstCell> iterator() {
+        return pool.iterator();
+    }
+
+
+    /*
+     * Fix Refs in constant pool.
+     *
+     * This is used when scanning JASM files produced from JDis with the verbose
+     * option (eg. where the constant pool is declared in the jasm itself).  In
+     * this scenario, we need two passes - the first pass to scan the entries
+     * (which creates constant references with indexes, but no reference values);
+     * and the second pass, which links references to existing constants.
+     *
+     */
+    public void fixRefsInPool() {
+        // used to fix CP refs when a constant pool is constructed by refs alone.
+        env.traceln("Fixing CP for explicit Constant Entries.");
+        int i = 0;
+        // simply iterate through the pool.
+        for (ConstCell item : pool) {
+            i += 1;
+            // first item is always null
+            if (item == null) {
+                continue;
+            }
+
+            checkAndFixCPRef(i, item);
+        }
+    }
+
+    protected void CheckGlobals() {
+        env.traceln("Checking Globals");
+        //
+        // This fn will put empty UTF8 string entries on any unset
+        // CP entries - before the last CP entry.
+        //
+        for (int cpx = 1; cpx < pool.size(); cpx++) {
+            ConstCell cell = pool.get(cpx);
+            if (cell == nullConst) { // gap
+                cell = new ConstCell(cpx, ConstValue0);
+                pool.set(cpx, cell);
+            }
+            ConstValue cval = cell.ref;
+            if ((cval == null) || !cval.hasValue()) {
+                String name = Integer.toString(cpx);
+                env.error("const.undecl", name);
+            }
+        }
+    }
+
+    /*
+     *  Helper function for "fixRefsInPool"
+     *
+     *  Does recursive checking of references,
+     * using a locally-defined visitor.
+     */
+    private void checkAndFixCPRef(int i, ConstCell item) {
+        ConstValue cv = item.ref;
+        if (cv != null) {
+            fixCPVstr.visit(cv);
+        }
+    }
+
+    private CPVisitor<Void> fixCPVstr = new CPVisitor<Void>() {
+        @Override
+        public Void visitUTF8(ConstValue_String p) {
+            return null;
+        }
+
+        ;
+        @Override
+        public Void visitInteger(ConstValue_Integer p) {
+            return null;
+        }
+
+        ;
+        @Override
+        public Void visitFloat(ConstValue_Integer p) {
+            return null;
+        }
+
+        ;
+        @Override
+        public Void visitDouble(ConstValue_Long p) {
+            return null;
+        }
+
+        ;
+        @Override
+        public Void visitLong(ConstValue_Long p) {
+            return null;
+        }
+
+        ;
+        @Override
+        public Void visitMethodtype(ConstValue_Cell p) {
+            handleClassRef(p);
+            return null;
+        }
+
+        ;
+        @Override
+        public Void visitString(ConstValue_Cell p) {
+            handleClassRef(p);
+            return null;
+        }
+
+        ;
+        @Override
+        public Void visitClass(ConstValue_Cell p) {
+            handleClassRef(p);
+            return null;
+        }
+
+        ;
+        @Override
+        public Void visitMethod(ConstValue_Pair p) {
+            handleMemberRef(p);
+            return null;
+        }
+
+        ;
+        @Override
+        public Void visitField(ConstValue_Pair p) {
+            handleMemberRef(p);
+            return null;
+        }
+
+        ;
+        @Override
+        public Void visitInterfacemethod(ConstValue_Pair p) {
+            handleMemberRef(p);
+            return null;
+        }
+
+        ;
+        @Override
+        public Void visitNameandtype(ConstValue_Pair p) {
+            handleMemberRef(p);
+            return null;
+        }
+
+        ;
+        @Override
+        public Void visitMethodhandle(ConstValue_Pair p) {
+            handleMemberRef(p);
+            return null;
+        }
+
+        ;
+        @Override
+        public Void visitDynamic(ConstValue_CondyPair p) {
+            return null;
+        }
+
+        ;
+        @Override
+        public Void visitInvokedynamic(ConstValue_IndyPair p) {
+            return null;
+        }
+        ;
+
+        @Override
+        public Void visitModule(ConstValue_Cell p) {
+            handleClassRef(p);
+            return null;
+        }
+        ;
+
+        @Override
+        public Void visitPackage(ConstValue_Cell p) {
+            handleClassRef(p);
+            return null;
+        }
+        ;
+
+
+        public void handleClassRef(ConstValue_Cell cv) {
+            ConstCell clref = cv.cell;
+            if (clref.ref == null) {
+                ConstCell refval = cpool_get(clref.arg);
+                if (refval != null) {
+                    checkAndFixCPRef(clref.arg, refval);
+                    clref.ref = refval.ref;
+                } else {
+                    clref.ref = null;
+                }
+                // env.traceln("FIXED ConstPool[" + i + "](" + cv.TagString(cv.tag) + ") = " + cv.value);
+            }
+        }
+
+        public void handleMemberRef(ConstValue_Pair cv) {
+            // env.traceln("ConstPool[" + i + "](" + cv.TagString(cv.tag) + ") = " + cv.value);
+            ConstCell clref = cv.left;
+            ConstCell typref = cv.right;
+            if (clref.ref == null) {
+                ConstCell refval = cpool_get(clref.arg);
+                if (refval != null) {
+                    checkAndFixCPRef(clref.arg, refval);
+                    clref.ref = refval.ref;
+                } else {
+                    clref.ref = null;
+                }
+                // env.traceln("FIXED ConstPool[" + i + "](" + cv.TagString(cv.tag) + ") = " + cv.value);
+            }
+            if (typref.ref == null) {
+                ConstCell refval = cpool_get(typref.arg);
+                if (refval != null) {
+                    checkAndFixCPRef(typref.arg, refval);
+                    typref.ref = refval.ref;
+                } else {
+                    typref.ref = null;
+                }
+                // env.traceln("FIXED ConstPool[" + i + "](" + cv.TagString(cv.tag) + ") = " + cv.value);
+            }
+        }
+
+    };
+
+    /*
+     * Help debug Constant Pools
+     */
+    public void printPool() {
+        int i = 0;
+        for (ConstCell item : pool) {
+            env.traceln("^^^^^^^^^^^^^  const #" + i + ": " + item);
+            i += 1;
+        }
+    }
+
+    private ConstCell cpool_get(int cpx) {
+        if (cpx >= pool.size()) {
+            return null;
+        }
+        return pool.get(cpx);
+    }
+
+    private void cpool_set(int cpx, ConstCell cell, int sz) {
+        debugStr("cpool_set1: " + cpx + " " + cell);
+        debugStr("param_size: " + sz);
+        debugStr("pool_size: " + pool.size());
+        cell.arg = cpx;
+        if (cpx + sz >= pool.size()) {
+            debugStr("calling ensureCapacity( " + (cpx + sz + 1) + ")");
+            int low = pool.size();
+            int high = cpx + sz;
+            for (int i = 0; i < high - low; i++) {
+                pool.add(nullConst);
+            }
+        }
+        pool.set(cpx, cell);
+        if (sz == 2) {
+            pool.set(cpx + 1, new ConstCell(cpx + 1, ConstValue0));
+        }
+        debugStr(" cpool_set2: " + cpx + " " + cell);
+    }
+
+    protected ConstCell uncheckedGetCell(int cpx) { // by index
+        return pool.get(cpx);
+    }
+
+    public ConstCell getCell(int cpx) { // by index
+        ConstCell cell = cpool_get(cpx);
+        if (cell != null) {
+            return cell;
+        }
+        cell = new ConstCell(cpx, null);
+        return cell;
+    }
+
+    public void setCell(int cpx, ConstCell cell) {
+        ConstValue value = cell.ref;
+        if (value == null) {
+            throw new Parser.CompilerError(env.errorStr("comperr.constcell.nullvalset"));
+        }
+        int sz = value.size();
+
+        if (cpx == 0) {
+            // It is correct to warn about redeclaring constant zero,
+            // since this value is never written out to a class file.
+            env.error("warn.const0.redecl");
+        } else {
+            if ((cpool_get(cpx) != null) || ((sz == 2) && (cpool_get(cpx + 1) != null))) {
+                String name = "#" + cpx;
+                env.error("const.redecl", name);
+                return;
+            }
+            if (cell.isSet() && (cell.arg != cpx)) {
+                env.traceln("setCell: new ConstCell");
+                cell = new ConstCell(value);
+            }
+        }
+        cpool_set(cpx, cell, sz);
+    }
+
+    protected void NumberizePool() {
+        env.traceln("NumberizePool");
+
+        for (ReferenceRank rank : ReferenceRank.values()) {
+            for (ConstCell cell : cpoolHashByValue.values().stream().
+                     filter(v-> !v.isSet() && rank.equals(v.rank)).
+                     collect(Collectors.toList())) {
+
+                ConstValue value = cell.ref;
+                if (value == null) {
+                    throw new Parser.CompilerError(env.errorStr("comperr.constcell.nullvalhash"));
+                }
+                int sz = value.size(), cpx;
+find:
+                for (cpx = 1; cpx < pool.size(); cpx++) {
+                    if ((pool.get(cpx) == nullConst) && ((sz == 1) || (pool.get(cpx + 1) == nullConst))) {
+                        break find;
+                    }
+                }
+                cpool_set(cpx, cell, sz);
+            }
+        }
+
+        ConstCell firstCell = cpool_get(0);
+        firstCell.arg = 0;
+    }
+
+    public ConstCell FindCell(ConstValue ref) {
+        if (ref == null) {
+            throw new Parser.CompilerError(env.errorStr("comperr.constcell.nullval"));
+        }
+        ConstCell pconst = null;
+        try {
+            pconst = cpoolHashByValue.get(ref);
+        } catch (Parser.CompilerError e) {
+            throw new Parser.CompilerError(env.errorStr("comperr.constcell.nullvalhash"));
+        }
+        // If we fund a cached ConstValue
+        if (pconst != null) {
+            ConstValue value = pconst.ref;
+            if (!value.equals(ref)) {
+                throw new Parser.CompilerError(env.errorStr("comperr.val.noteq"));
+            }
+            return pconst;
+        }
+        // If we didn't find a cached ConstValue
+        //      Add it to the cache
+        pconst = new ConstCell(ref);
+        cpoolHashByValue.put(ref, pconst);
+        return pconst;
+    }
+
+    public ConstCell FindCell(ConstType tag, String value) {
+        return FindCell(new ConstValue_String(value));
+    }
+
+    public ConstCell FindCell(ConstType tag, Integer value) {
+        return FindCell(new ConstValue_Integer(tag, value));
+    }
+
+    public ConstCell FindCell(ConstType tag, Long value) {
+        return FindCell(new ConstValue_Long(tag, value));
+    }
+
+    public ConstCell FindCell(ConstType tag, ConstCell value) {
+        return FindCell(new ConstValue_Cell(tag, value));
+    }
+
+    public ConstCell FindCell(ConstType tag, ConstCell left, ConstCell right) {
+        return FindCell(new ConstValue_Pair(tag, left, right));
+    }
+
+    public ConstCell FindCellAsciz(String str) {
+        return FindCell(ConstType.CONSTANT_UTF8, str);
+    }
+
+    public ConstCell FindCellClassByName(String name) { return FindCell(ConstType.CONSTANT_CLASS, FindCellAsciz(name)); }
+
+    public ConstCell FindCellModuleByName(String name) { return FindCell(ConstType.CONSTANT_MODULE, FindCellAsciz(name)); }
+
+    public ConstCell FindCellPackageByName(String name) { return FindCell(ConstType.CONSTANT_PACKAGE, FindCellAsciz(name)); }
+
+    public void write(CheckedDataOutputStream out) throws IOException {
+        // Write the constant pool
+        int length = pool.size();
+        out.writeShort(length);
+        int i;
+        env.traceln("wr.pool:size=" + length);
+        for (i = 1; i < length;) {
+            ConstCell cell = pool.get(i);
+            ConstValue value = cell.ref;
+            if (cell.arg != i) {
+                throw new Parser.CompilerError(env.errorStr("comperr.constcell.invarg", Integer.toString(i), cell.arg));
+            }
+            value.write(out);
+            i += value.size();
+        }
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/Constants.java b/test/lib/org/openjdk/asmtools/jasm/Constants.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/Constants.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 1996, 2017, 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 org.openjdk.asmtools.jasm;
+
+/**
+ * This interface defines constant that are used throughout the compiler. It inherits from
+ * RuntimeConstants, which is an autogenerated class that contains constants defined in
+ * the interpreter.
+ */
+public interface Constants extends RuntimeConstants {
+
+
+    /**
+     * End of input
+     */
+    public static final int EOF        = -1;
+
+    /*
+     * Flags
+     */
+    public static final int F_VERBOSE        = 1 << 0;
+    public static final int F_DUMP           = 1 << 1;
+    public static final int F_WARNINGS       = 1 << 2;
+    public static final int F_DEBUG          = 1 << 3;
+    public static final int F_OPTIMIZE       = 1 << 4;
+    public static final int F_DEPENDENCIES   = 1 << 5;
+
+    /*
+     * Type codes
+     */
+    public static final int TC_BOOLEAN   = 0;
+    public static final int TC_BYTE      = 1;
+    public static final int TC_CHAR      = 2;
+    public static final int TC_SHORT     = 3;
+    public static final int TC_INT       = 4;
+    public static final int TC_LONG      = 5;
+    public static final int TC_FLOAT     = 6;
+    public static final int TC_DOUBLE    = 7;
+    public static final int TC_NULL      = 8;
+    public static final int TC_ARRAY     = 9;
+    public static final int TC_CLASS     = 10;
+    public static final int TC_VOID      = 11;
+    public static final int TC_METHOD    = 12;
+    public static final int TC_ERROR     = 13;
+
+    /*
+     * Type Masks
+     */
+    public static final int TM_NULL      = 1 << TC_NULL;
+    public static final int TM_VOID      = 1 << TC_VOID;
+    public static final int TM_BOOLEAN   = 1 << TC_BOOLEAN;
+    public static final int TM_BYTE      = 1 << TC_BYTE;
+    public static final int TM_CHAR      = 1 << TC_CHAR;
+    public static final int TM_SHORT     = 1 << TC_SHORT;
+    public static final int TM_INT       = 1 << TC_INT;
+    public static final int TM_LONG      = 1 << TC_LONG;
+    public static final int TM_FLOAT     = 1 << TC_FLOAT;
+    public static final int TM_DOUBLE    = 1 << TC_DOUBLE;
+    public static final int TM_ARRAY     = 1 << TC_ARRAY;
+    public static final int TM_CLASS     = 1 << TC_CLASS;
+    public static final int TM_METHOD    = 1 << TC_METHOD;
+    public static final int TM_ERROR     = 1 << TC_ERROR;
+
+    public static final int TM_INT32     = TM_BYTE | TM_SHORT | TM_CHAR | TM_INT;
+    public static final int TM_NUM32     = TM_INT32 | TM_FLOAT;
+    public static final int TM_NUM64     = TM_LONG | TM_DOUBLE;
+    public static final int TM_INTEGER   = TM_INT32 | TM_LONG;
+    public static final int TM_REAL      = TM_FLOAT | TM_DOUBLE;
+    public static final int TM_NUMBER    = TM_INTEGER | TM_REAL;
+    public static final int TM_REFERENCE = TM_ARRAY | TM_CLASS | TM_NULL;
+
+    /*
+     * Class status
+     */
+    public static final int CS_UNDEFINED        = 0;
+    public static final int CS_UNDECIDED        = 1;
+    public static final int CS_BINARY           = 2;
+    public static final int CS_SOURCE           = 3;
+    public static final int CS_PARSED           = 4;
+    public static final int CS_COMPILED         = 5;
+    public static final int CS_NOTFOUND         = 6;
+
+    /*
+     * Attributes
+     */
+    public static final int ATT_ALL             = -1;
+    public static final int ATT_CODE            = 1;
+
+    /*
+     * Number of bits used in file offsets
+     */
+    public static final int OFFSETBITS          = 19;
+    public static final int MAXFILESIZE         = (1 << OFFSETBITS) - 1;
+    public static final int MAXLINENUMBER       = (1 << (32 - OFFSETBITS)) - 1;
+
+    /*
+     * Operator precedence
+     */
+    /* Who uses this????
+    public static final int opPrecedence[] = {
+        10,  11,  11,  11,  11,  11,  11,  11,  11,  11,
+        11,  11,  11,  12,  13,  14,  15,  16,  17,  18,
+        18,  19,  19,  19,  19,  19,  20,  20,  20,  21,
+        21,  22,  22,  22,  23,  24,  24,  24,  24,  24,
+        24,  25,  25,  26,  26,  26,  26,  26,  26
+    };
+    * */
+}
diff a/test/lib/org/openjdk/asmtools/jasm/Data.java b/test/lib/org/openjdk/asmtools/jasm/Data.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/Data.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jasm;
+
+import java.io.IOException;
+
+/**
+ * Base contract for writeable structures
+ */
+interface Data {
+
+    void write(CheckedDataOutputStream out) throws IOException;
+
+    int getLength();
+
+    default String tabString(int tabLevel) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < tabLevel; i++) {
+            sb.append('\t');
+        }
+        return sb.toString();
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/DataVector.java b/test/lib/org/openjdk/asmtools/jasm/DataVector.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/DataVector.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 1996, 2019, 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 org.openjdk.asmtools.jasm;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ *
+ */
+public class DataVector<T extends Data> implements Iterable<T> {
+
+    ArrayList<T> elements;
+
+    public DataVector(int initSize) {
+        elements = new ArrayList<>(initSize);
+    }
+
+    public DataVector() {
+        this(12);
+    }
+
+    public Iterator<T> iterator() {
+        return elements.iterator();
+    }
+
+    public void add(T element) {
+        elements.add(element);
+    }
+
+    public void addAll(List<T> collection) {
+        elements.addAll(collection);
+    }
+
+    // full length of the attribute conveyor
+    // declared in Data
+    public int getLength() {
+        int length = 0;
+        // calculate overall size here rather than in add()
+        // because it may not be available at the time of invoking of add()
+        for (T element : elements) {
+            length += element.getLength();
+        }
+
+        return 2 + length; // add the length of number of elements
+    }
+
+    public void write(CheckedDataOutputStream out)
+            throws IOException {
+        out.writeShort(elements.size());
+        writeElements(out);
+    }
+
+    public void writeElements(CheckedDataOutputStream out)
+            throws IOException {
+        for (Data element : elements) {
+            element.write(out);
+        }
+    }
+
+    /* for compatibility with Vector */
+    public void addElement(T element) {
+        elements.add(element);
+    }
+
+    public int size() {
+        return elements.size();
+    }
+
+    public Data elementAt(int k) {
+        return elements.get(k);
+    }
+}// end class DataVector
+
diff a/test/lib/org/openjdk/asmtools/jasm/DataVectorAttr.java b/test/lib/org/openjdk/asmtools/jasm/DataVectorAttr.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/DataVectorAttr.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 1996, 2014, 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 org.openjdk.asmtools.jasm;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ *
+ */
+// public class DataVectorAttr extends AttrData implements Constants {
+// }
+class DataVectorAttr<T extends Data> extends AttrData implements Iterable<T> {
+
+    private ArrayList<T> elements;
+    private boolean byteIndex;
+
+    private DataVectorAttr(ClassData cls, String name, boolean byteIndex, ArrayList<T> initialData) {
+        super(cls, name);
+        this.elements = initialData;
+        this.byteIndex = byteIndex;
+    }
+
+    DataVectorAttr(ClassData cls, String name, ArrayList<T> initialData) {
+        this(cls, name, false, initialData);
+    }
+
+    DataVectorAttr(ClassData cls, String name) {
+        this(cls, name, false, new ArrayList<>());
+
+    }
+
+    DataVectorAttr(ClassData cls, String name, boolean byteIndex) {
+        this(cls, name, byteIndex, new ArrayList<>());
+
+    }
+
+    public T get(int index) {
+        return elements.get(index);
+    }
+
+    public void add(T element) {
+        elements.add(element);
+    }
+
+    public void put(int i, T element) {
+        elements.set(i, element);
+    }
+
+    public int size() {
+        return elements.size();
+    }
+
+    @Override
+    public Iterator<T> iterator() {
+        return elements.iterator();
+    }
+
+    @Override
+    public int attrLength() {
+        int length = 0;
+        // calculate overall size here rather than in add()
+        // because it may not be available at the time of invoking of add()
+        for (T elem : elements) {
+            length += elem.getLength();
+        }
+
+        // add the length of number of elements
+        if (byteIndex) {
+            length += 1;
+        } else {
+            length += 2;
+        }
+
+        return length;
+    }
+
+    @Override
+    public void write(CheckedDataOutputStream out) throws IOException {
+        super.write(out);  // attr name, attr len
+        if (byteIndex) {
+            out.writeByte(elements.size());
+        } else {
+            out.writeShort(elements.size());
+        } // number of elements
+        for (T elem : elements) {
+            elem.write(out);
+        }
+    }
+
+}
diff a/test/lib/org/openjdk/asmtools/jasm/DefaultAnnotationAttr.java b/test/lib/org/openjdk/asmtools/jasm/DefaultAnnotationAttr.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/DefaultAnnotationAttr.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 1996, 2014, 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 org.openjdk.asmtools.jasm;
+
+import java.io.IOException;
+
+/**
+ * DefaultAnnotationAttr
+ *
+ * Used to represent Default Annotation Attributes
+ *
+ */
+public class DefaultAnnotationAttr extends AttrData {
+
+    Data element; // Data
+
+    public DefaultAnnotationAttr(ClassData cls, String name, Data element) {
+        super(cls, name);
+        this.element = element;
+    }
+
+    public DefaultAnnotationAttr(ClassData cls, String name) {
+        super(cls, name);
+        this.element = null;
+    }
+
+    public void add(Data element) {
+        this.element = element;
+    }
+
+    @Override
+    public int attrLength() {
+        return element.getLength(); // add the length of number of elements
+    }
+
+    @Override
+    public void write(CheckedDataOutputStream out) throws IOException {
+        super.write(out);  // attr name, attr len
+        element.write(out);
+    }
+}// end class DataVectorAttr
+
diff a/test/lib/org/openjdk/asmtools/jasm/Environment.java b/test/lib/org/openjdk/asmtools/jasm/Environment.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/Environment.java
@@ -0,0 +1,454 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jasm;
+
+import static org.openjdk.asmtools.jasm.Constants.EOF;
+import static org.openjdk.asmtools.jasm.Constants.OFFSETBITS;
+import org.openjdk.asmtools.util.I18NResourceBundle;
+
+import java.io.*;
+import java.nio.file.Paths;
+import java.text.MessageFormat;
+
+/**
+ * An input stream for java programs. The stream treats either "\n", "\r" or "\r\n" as the
+ * end of a line, it always returns \n. It also parses UNICODE characters expressed as
+ * \uffff. However, if it sees "\\", the second slash cannot begin a unicode sequence. It
+ * keeps track of the current position in the input stream.
+ *
+ * An position consists of: ((linenr &lt;&lt; OFFSETBITS) | offset) this means that both
+ * the line number and the exact offset into the file are encoded in each position
+ * value.<p>
+ */
+public class Environment {
+
+    /*-------------------------------------------------------- */
+    /* Environment Inner Classes */
+    /**
+     * A sorted list of error messages
+     */
+    final class ErrorMessage {
+
+        int where;
+        String message;
+        ErrorMessage next;
+
+        /**
+         * Constructor
+         */
+        ErrorMessage(int where, String message) {
+            this.where = where;
+            this.message = message;
+        }
+    }
+
+    /*-------------------------------------------------------- */
+    /* Environment Fields */
+    static boolean traceFlag = false;
+    boolean debugInfoFlag = false;
+
+    private String inputFileName;
+    private String simpleInputFileName;
+    public PrintWriter out;
+    private boolean nowarn;
+    private byte[] data;
+    private int bytepos;
+    private int linepos;
+    public int pos;
+    /*-------------------------------------------------------- */
+
+    public Environment(DataInputStream dis, String inputFileName, PrintWriter out, boolean nowarn) throws IOException {
+        this.out = out;
+        this.inputFileName = inputFileName;
+        this.nowarn = nowarn;
+        // Read the file
+        data = new byte[dis.available()];
+        dis.read(data);
+        dis.close();
+        bytepos = 0;
+        linepos = 1;
+    }
+
+    public String getInputFileName() {
+        return inputFileName;
+    }
+
+    public String getSimpleInputFileName() {
+        if( simpleInputFileName == null ) {
+            simpleInputFileName = Paths.get(inputFileName).getFileName().toString();
+        }
+        return simpleInputFileName;
+    }
+
+    int lookForward() {
+        try {
+            return data[bytepos];
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return EOF;
+        }
+    }
+
+    int convertUnicode() {
+        int c;
+        try {
+            while ((c = data[bytepos]) == 'u') {
+                bytepos++;
+            }
+            int d = 0;
+            for (int i = 0; i < 4; i++) {
+                switch (c) {
+                    case '0':
+                    case '1':
+                    case '2':
+                    case '3':
+                    case '4':
+                    case '5':
+                    case '6':
+                    case '7':
+                    case '8':
+                    case '9':
+                        d = (d << 4) + c - '0';
+                        break;
+                    case 'a':
+                    case 'b':
+                    case 'c':
+                    case 'd':
+                    case 'e':
+                    case 'f':
+                        d = (d << 4) + 10 + c - 'a';
+                        break;
+                    case 'A':
+                    case 'B':
+                    case 'C':
+                    case 'D':
+                    case 'E':
+                    case 'F':
+                        d = (d << 4) + 10 + c - 'A';
+                        break;
+                    default:
+                        error(pos, "invalid.escape.char");
+                        return d;
+                }
+                ++bytepos;
+                c = data[bytepos];
+            }
+            return d;
+        } catch (ArrayIndexOutOfBoundsException e) {
+            error(pos, "invalid.escape.char");
+            return EOF;
+        }
+    }
+
+    public int read() {
+        int c;
+        pos = (linepos << OFFSETBITS) | bytepos;
+        try {
+            c = data[bytepos];
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return EOF;
+        }
+        bytepos++;
+
+        // parse special characters
+        switch (c) {
+            /*            case '\\':
+             if (lookForward() != 'u') {
+             return '\\';
+             }
+             // we have a unicode sequence
+             return convertUnicode();*/
+            case '\n':
+                linepos++;
+                return '\n';
+
+            case '\r':
+                if (lookForward() == '\n') {
+                    bytepos++;
+                }
+                linepos++;
+                return '\n';
+
+            default:
+                return c;
+        }
+    }
+
+    int lineNumber(int lcpos) {
+        return lcpos >>> OFFSETBITS;
+    }
+
+    int lineNumber() {
+        return lineNumber(pos);
+    }
+
+    int lineOffset(int lcpos) {
+        return lcpos & ((1 << OFFSETBITS) - 1);
+    }
+
+    int lineOffset() {
+        return lineOffset(pos);
+    }
+
+    /*==============================================================  Environment */
+    /**
+     * The number of errors and warnings
+     */
+    public int nerrors;
+    public int nwarnings;
+    public static final I18NResourceBundle i18n
+            = I18NResourceBundle.getBundleForClass(Main.class);
+
+    /**
+     * Error String
+     */
+    String errorString(String err, Object arg1, Object arg2, Object arg3) {
+        String str = null;
+
+        //str = getProperty(err);
+        str = i18n.getString(err);
+        if (str == null) {
+            return "error message '" + err + "' not found";
+        }
+
+        StringBuffer buf = new StringBuffer();
+        for (int i = 0; i < str.length(); i++) {
+            char c = str.charAt(i);
+            if ((c == '%') && (i + 1 < str.length())) {
+                switch (str.charAt(++i)) {
+                    case 's':
+                        String arg = arg1.toString();
+                        for (int j = 0; j < arg.length(); j++) {
+                            switch (c = arg.charAt(j)) {
+                                case ' ':
+                                case '\t':
+                                case '\n':
+                                case '\r':
+                                    buf.append((char) c);
+                                    break;
+
+                                default:
+                                    if ((c > ' ') && (c <= 255)) {
+                                        buf.append((char) c);
+                                    } else {
+                                        buf.append('\\');
+                                        buf.append('u');
+                                        buf.append(Integer.toString(c, 16));
+                                    }
+                            }
+                        }
+                        arg1 = arg2;
+                        arg2 = arg3;
+                        break;
+
+                    case '%':
+                        buf.append('%');
+                        break;
+
+                    default:
+                        buf.append('?');
+                        break;
+                }
+            } else {
+                buf.append((char) c);
+            }
+        }
+        // KTL
+        // Need to do message format to substitute args
+        String msg = buf.toString();
+        MessageFormat form = new MessageFormat(msg);
+        Object args[] = {arg1, arg2, arg3};
+        msg = form.format(args);
+
+        return msg;
+
+    }
+
+    /**
+     * List of outstanding error messages
+     */
+    ErrorMessage errors;
+
+    /**
+     * Insert an error message in the list of outstanding error messages. The list is
+     * sorted on input position.
+     */
+    void insertError(int where, String message) {
+        //output("ERR = " + message);
+        ErrorMessage msg = new ErrorMessage(where, message);
+        if (errors == null) {
+            errors = msg;
+        } else if (errors.where > where) {
+            msg.next = errors;
+            errors = msg;
+        } else {
+            ErrorMessage m = errors;
+            for (; (m.next != null) && (m.next.where <= where); m = m.next);
+            msg.next = m.next;
+            m.next = msg;
+        }
+    }
+
+    /**
+     * Flush outstanding errors
+     */
+    public void flushErrors() {
+        if (errors == null) {
+            traceln("flushErrors: errors == null");
+            return;
+        }
+
+        // Report the errors
+        for (ErrorMessage msg = errors; msg != null; msg = msg.next) {
+            int off = lineOffset(msg.where);
+
+            int i, j;
+            for (i = off; (i > 0) && (data[i - 1] != '\n') && (data[i - 1] != '\r'); i--);
+            for (j = off; (j < data.length) && (data[j] != '\n') && (data[j] != '\r'); j++);
+
+            outputln( String.format( "%s (%d:%d) %s", getSimpleInputFileName(), lineNumber(msg.where), off - i, msg.message));
+            outputln(new String(data, i, j - i));
+
+            char strdata[] = new char[(off - i) + 1];
+            for (j = i; j < off; j++) {
+                strdata[j - i] = (data[j] == '\t') ? '\t' : ' ';
+            }
+            strdata[off - i] = '^';
+            outputln(new String(strdata));
+        }
+        errors = null;
+    }
+
+    /**
+     * Output a string. This can either be an error message or something for debugging.
+     * This should be used instead of print.
+     */
+    public void output(String msg) {
+        int len = msg.length();
+        for (int i = 0; i < len; i++) {
+            out.write(msg.charAt(i));
+        }
+        out.flush();
+    }
+
+    /**
+     * Output a string. This can either be an error message or something for debugging.
+     * This should be used instead of println.
+     */
+    public void outputln(String msg) {
+        output((msg == null ? "" : msg) + "\n");
+    }
+
+    /**
+     * Issue an error. source - the input source, usually a file name string offset - the
+     * offset in the source of the error err - the error number (as defined in this
+     * interface) arg1 - an optional argument to the error (null if not applicable) arg2 -
+     * a second optional argument to the error (null if not applicable) arg3 - a third
+     * optional argument to the error (null if not applicable)
+     */
+    /**
+     * Issue an error
+     */
+    public void error(int where, String err, Object arg1, Object arg2, Object arg3) {
+        String msg;
+        if (err.startsWith("warn.")) {
+            if (nowarn) {
+                return;
+            }
+            nwarnings++;
+            msg = "Warning: ";
+        } else {
+            err = "err." + err;
+            nerrors++;
+            msg = "Error: ";
+        }
+        msg = msg + errorString(err, arg1, arg2, arg3);
+        traceln(msg);
+        insertError(where, msg);
+    }
+
+    public final void error(int where, String err, Object arg1, Object arg2) {
+        error(where, err, arg1, arg2, null);
+    }
+
+    public final void error(int where, String err, Object arg1) {
+        error(where, err, arg1, null, null);
+    }
+
+    public final void error(int where, String err) {
+        error(where, err, null, null, null);
+    }
+
+    public final void error(String err, Object arg1, Object arg2, Object arg3) {
+        error(pos, err, arg1, arg2, arg3);
+    }
+
+    public final void error(String err, Object arg1, Object arg2) {
+        error(pos, err, arg1, arg2, null);
+    }
+
+    public final void error(String err, Object arg1) {
+        error(pos, err, arg1, null, null);
+    }
+
+    public final void error(String err) {
+        error(pos, err, null, null, null);
+    }
+
+    public final String errorStr(String err, Object arg1, Object arg2, Object arg3) {
+        return errorString(err, arg1, arg2, arg3);
+    }
+
+    public final String errorStr(String err, Object arg1, Object arg2) {
+        return errorStr(err, arg1, arg2, null);
+    }
+
+    public final String errorStr(String err, Object arg1) {
+        return errorStr(err, arg1, null, null);
+    }
+
+    public final String errorStr(String err) {
+        return errorStr(err, null, null, null);
+    }
+
+    /*==============================================================  trace */
+    public boolean isTraceEnabled() {
+        return traceFlag;
+    }
+
+    public boolean isDebugEnabled() {
+        return debugInfoFlag;
+    }
+
+    void trace(String message) {
+        if (traceFlag) {
+            output(message);
+        }
+    }
+
+    void traceln(String message) {
+        if (traceFlag) {
+            outputln(message);
+        }
+    }
+
+} // end Environment
diff a/test/lib/org/openjdk/asmtools/jasm/FieldData.java b/test/lib/org/openjdk/asmtools/jasm/FieldData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/FieldData.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jasm;
+
+import org.openjdk.asmtools.jasm.Tables.AttrTag;
+import java.io.IOException;
+
+/**
+ *  field_info
+ */
+class FieldData extends MemberData {
+
+    /* FieldData Fields */
+    private ConstantPool.ConstValue_Pair nape;
+    private AttrData initValue;
+
+    public FieldData(ClassData cls, int acc, ConstantPool.ConstValue_Pair nape) {
+        super(cls, acc);
+        this.nape = nape;
+        if (Modifiers.hasPseudoMod(acc)) {
+            createPseudoMod();
+        }
+    }
+
+    public ConstantPool.ConstValue_Pair getNameDesc() {
+        return nape;
+    }
+
+    public void SetValue(Argument value_cpx) {
+        initValue = new CPXAttr(cls, AttrTag.ATT_ConstantValue.parsekey(),
+                value_cpx);
+    }
+
+    @Override
+    protected DataVector getAttrVector() {
+        return getDataVector(initValue, syntheticAttr, deprecatedAttr, signatureAttr);
+    }
+
+    public void write(CheckedDataOutputStream out) throws IOException, Parser.CompilerError {
+        out.writeShort(access);
+        out.writeShort(nape.left.arg);
+        out.writeShort(nape.right.arg);
+        DataVector attrs = getAttrVector();
+        attrs.write(out);
+    }
+} // end FieldData
+
diff a/test/lib/org/openjdk/asmtools/jasm/InnerClassData.java b/test/lib/org/openjdk/asmtools/jasm/InnerClassData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/InnerClassData.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 1996, 2014, 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 org.openjdk.asmtools.jasm;
+
+import java.io.IOException;
+
+/**
+ *
+ */
+class InnerClassData implements Data {
+
+    int access;
+    ConstantPool.ConstCell name, innerClass, outerClass;
+
+    public InnerClassData(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell innerClass, ConstantPool.ConstCell outerClass) {
+        this.access = access;
+        this.name = name;
+        this.innerClass = innerClass;
+        this.outerClass = outerClass;
+    }
+
+    @Override
+    public int getLength() {
+        return 8;
+    }
+
+    @Override
+    public void write(CheckedDataOutputStream out) throws IOException {
+        out.writeShort(innerClass.arg);
+        if (outerClass.isSet()) {
+            out.writeShort(outerClass.arg);
+        } else {
+            out.writeShort(0);
+        }
+        if (name.isSet()) {
+            out.writeShort(name.arg);
+        } else {
+            out.writeShort(0);
+        }
+        out.writeShort(access);
+    }
+}// end class InnerClassData
+
diff a/test/lib/org/openjdk/asmtools/jasm/Instr.java b/test/lib/org/openjdk/asmtools/jasm/Instr.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/Instr.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 1996, 2014, 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 org.openjdk.asmtools.jasm;
+
+import java.io.IOException;
+
+import static org.openjdk.asmtools.jasm.OpcodeTables.*;
+
+/**
+ *
+ */
+class Instr {
+
+    Instr next = null;
+    int pc;
+    int pos;
+    Opcode opc;
+    Argument arg;
+    Object arg2; // second or unusual argument
+
+    public Instr(int pc, int pos, Opcode opc, Argument arg, Object arg2) {
+        this.pc = pc;
+        this.pos = pos;
+        this.opc = opc;
+        this.arg = arg;
+        this.arg2 = arg2;
+    }
+
+    public Instr() {
+    }
+
+    public void write(CheckedDataOutputStream out, Environment env) throws IOException {
+        OpcodeType type = opc.type();
+        switch (type) {
+            case NORMAL: {
+                if (opc == Opcode.opc_bytecode) {
+                    out.writeByte(arg.arg);
+                    return;
+                }
+                out.writeByte(opc.value());
+                int opcLen = opc.length();
+                if (opcLen == 1) {
+                    return;
+                }
+
+                switch (opc) {
+                    case opc_tableswitch:
+                        ((SwitchTable) arg2).writeTableSwitch(out);
+                        return;
+                    case opc_lookupswitch:
+                        ((SwitchTable) arg2).writeLookupSwitch(out);
+                        return;
+                }
+
+                int iarg;
+                try {
+                    iarg = arg.arg;
+                } catch (NullPointerException e) {
+                    throw new Parser.CompilerError(env.errorStr("comperr.instr.nullarg", opc.parsekey()));
+                }
+//env.traceln("instr:"+opcNamesTab[opc]+" len="+opcLen+" arg:"+iarg);
+                switch (opc) {
+                    case opc_jsr:
+                    case opc_goto:
+                    case opc_ifeq:
+                    case opc_ifge:
+                    case opc_ifgt:
+                    case opc_ifle:
+                    case opc_iflt:
+                    case opc_ifne:
+                    case opc_if_icmpeq:
+                    case opc_if_icmpne:
+                    case opc_if_icmpge:
+                    case opc_if_icmpgt:
+                    case opc_if_icmple:
+                    case opc_if_icmplt:
+                    case opc_if_acmpeq:
+                    case opc_if_acmpne:
+                    case opc_ifnull:
+                    case opc_ifnonnull:
+                    case opc_jsr_w:
+                    case opc_goto_w:
+                        iarg = iarg - pc;
+                        break;
+                    case opc_iinc:
+                        iarg = (iarg << 8) | (((Argument) arg2).arg & 0xFF);
+                        break;
+                    case opc_invokeinterface:
+                        iarg = ((iarg << 8) | (((Argument) arg2).arg & 0xFF)) << 8;
+                        break;
+                    case opc_invokedynamic: // JSR-292
+                        iarg = (iarg << 16);
+                        break;
+                    case opc_ldc:
+                        if ((iarg & 0xFFFFFF00) != 0) {
+                            throw new Parser.CompilerError(
+                                    env.errorStr("comperr.instr.arglong", opc.parsekey(), iarg));
+                        }
+                        break;
+                }
+                switch (opcLen) {
+                    case 1:
+                        return;
+                    case 2:
+                        out.writeByte(iarg);
+                        return;
+                    case 3:
+                        out.writeShort(iarg);
+                        return;
+                    case 4: // opc_multianewarray only
+                        out.writeShort(iarg);
+                        iarg = ((Argument) arg2).arg;
+                        out.writeByte(iarg);
+                        return;
+                    case 5:
+                        out.writeInt(iarg);
+                        return;
+                    default:
+                        throw new Parser.CompilerError(
+                                env.errorStr("comperr.instr.opclen", opc.parsekey()));
+                }
+            }
+            case WIDE:
+                out.writeByte(Opcode.opc_wide.value());
+                out.writeByte(opc.value() & 0xFF);
+                out.writeShort(arg.arg);
+                if (opc == Opcode.opc_iinc_w) {
+                    out.writeShort(((Argument) arg2).arg);
+                }
+                return;
+            case PRIVELEGED:
+            case NONPRIVELEGED:
+                out.writeByte(opc.value() >> 8);
+                out.writeByte(opc.value() & 0xFF);
+                return;
+            default:
+                throw new Parser.CompilerError(
+                        env.errorStr("comperr.instr.opclen", opc.parsekey()));
+        } // end writeSpecCode
+
+    }
+} // end Instr
+
diff a/test/lib/org/openjdk/asmtools/jasm/JasmTokens.java b/test/lib/org/openjdk/asmtools/jasm/JasmTokens.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/JasmTokens.java
@@ -0,0 +1,493 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jasm;
+
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.Optional;
+
+/**
+ *
+ * JasmTokens
+ *
+ * This class contains tokens specific to parsing JASM syntax.
+ *
+ * The classes in JasmTokens are following a Singleton Pattern. These classes are Enums,
+ * and they are contained in private hash maps (lookup tables and reverse lookup tables).
+ * These hash maps all have public accessors, which clients use to look-up enums.
+ *
+ * Tokens in this table carry no external state, and are typically treated as constants.
+ * They do not need to be reset.
+ */
+public class JasmTokens {
+
+    /*-------------------------------------------------------- */
+    /* Marker: describes the type of Keyword */
+    static public enum KeywordType {
+        TOKEN            (0, "TOKEN"),
+        VALUE            (1, "VALUE"),
+        JASMIDENTIFIER   (2, "JASM"),
+        KEYWORD          (3, "KEYWORD");
+
+        private final Integer value;
+        private final String printval;
+
+        KeywordType(Integer val, String print) {
+            value = val;
+            printval = print;
+        }
+
+        public String printval() {
+            return printval;
+        }
+    }
+
+
+    /*--------------------------------------------------------  */
+    /* Marker - describes the type of token                     */
+    /*    this is rather cosmetic, no function currently.       */
+    static public enum TokenType {
+        MODIFIER            (1, "Modifier"),
+        OPERATOR            (2, "Operator"),
+        VALUE               (3, "Value"),
+        TYPE                (4, "Type"),
+        EXPRESSION          (5, "Expression"),
+        STATEMENT           (6, "Statement"),
+        DECLARATION         (7, "Declaration"),
+        PUNCTUATION         (8, "Punctuation"),
+        SPECIAL             (9, "Special"),
+        JASM                (10, "Jasm"),
+        MISC                (11, "Misc"),
+        JASM_IDENT          (12, "Jasm identifier"),
+        MODULE_NAME         (13, "Module Name"),
+        TYPE_PATH_KIND      (14, "Type path kind")          // Table 4.7.20.2-A Interpretation of type_path_kind values
+        ;
+
+        private final Integer value;
+        private final String printval;
+
+        TokenType(Integer val, String print) {
+            value = val;
+            printval = print;
+        }
+        public String printval() {
+            return printval;
+        }
+    }
+
+    public enum AnnotationType {
+        Visible("@+"),
+        Invisible("@-"),
+        VisibleType("@T+"),
+        InvisibleType("@T-");
+
+        private final String jasmPrefix;
+
+        AnnotationType(String jasmPrefix) {
+            this.jasmPrefix = jasmPrefix;
+        }
+
+        /**
+         * isAnnotationToken
+         *
+         * examines the beginning of a string to see if it starts with an annotation
+         * characters ('@+' = visible annotation, '@-' = invisible).
+         *
+         * @param str String to be analyzed
+         * @return True if the string starts with an annotation char.
+         */
+        static public boolean isAnnotationToken(String str) {
+            return (str.startsWith(AnnotationType.Invisible.jasmPrefix) ||
+                    str.startsWith(AnnotationType.Visible.jasmPrefix));
+        }
+
+        /**
+         * isTypeAnnotationToken
+         *
+         * examines the beginning of a string to see if it starts with type annotation
+         * characters ('@T+' = visible type annotation, '@T-' = invisible).
+         *
+         * @param str String to be analyzed
+         * @return True if the string starts with an annotation char.
+         */
+        static public boolean isTypeAnnotationToken(String str) {
+            return (str.startsWith(AnnotationType.InvisibleType.jasmPrefix) ||
+                    str.startsWith(AnnotationType.VisibleType.jasmPrefix));
+        }
+
+        /**
+         * isAnnotation
+         *
+         * examines the beginning of a string to see if it starts with an annotation character
+         *
+         * @param str String to be analyzed
+         * @return True if the string starts with an annotation char.
+         */
+        static public boolean isAnnotation(String str) {
+            return (str.startsWith("@"));
+        }
+
+        /**
+         * isInvisibleAnnotationToken
+         *
+         * examines the end of an annotation token to determine visibility ('+' = visible
+         * annotation, '-' = invisible).
+         *
+         * @param str String to be analyzed
+         * @return True if the token implies invisible annotation.
+         */
+        static public boolean isInvisibleAnnotationToken(String str) {
+            return (str.endsWith("-"));
+        }
+    }
+
+    /**
+     * Scanner Tokens (Definitive List)
+     */
+    public enum Token {
+        EOF                 (-1, "EOF",         "EOF",  EnumSet.of(TokenType.MISC)),
+        COMMA               (0, "COMMA",        ",",    EnumSet.of(TokenType.OPERATOR)),
+        ASSIGN              (1, "ASSIGN",       "=",    EnumSet.of(TokenType.OPERATOR)),
+
+        ASGMUL              (2, "ASGMUL",       "*=",   EnumSet.of(TokenType.OPERATOR)),
+        ASGDIV              (3, "ASGDIV",       "/=",   EnumSet.of(TokenType.OPERATOR)),
+        ASGREM              (4, "ASGREM",       "%=",   EnumSet.of(TokenType.OPERATOR)),
+        ASGADD              (5, "ASGADD",       "+=",   EnumSet.of(TokenType.OPERATOR)),
+        ASGSUB              (6, "ASGSUB",       "-=",   EnumSet.of(TokenType.OPERATOR)),
+        ASGLSHIFT           (7, "ASGLSHIFT",    "<<=",  EnumSet.of(TokenType.OPERATOR)),
+        ASGRSHIFT           (8, "ASGRSHIFT",    ">>=",  EnumSet.of(TokenType.OPERATOR)),
+        ASGURSHIFT          (9, "ASGURSHIFT",   "<<<=", EnumSet.of(TokenType.OPERATOR)),
+        ASGBITAND           (10, "ASGBITAND",   "&=",   EnumSet.of(TokenType.OPERATOR)),
+        ASGBITOR            (11, "ASGBITOR",    "|=",   EnumSet.of(TokenType.OPERATOR)),
+        ASGBITXOR           (12, "ASGBITXOR",   "^=",   EnumSet.of(TokenType.OPERATOR)),
+
+        COND                (13, "COND",        "?:",   EnumSet.of(TokenType.OPERATOR)),
+        OR                  (14, "OR",          "||",   EnumSet.of(TokenType.OPERATOR)),
+        AND                 (15, "AND",         "&&",   EnumSet.of(TokenType.OPERATOR)),
+        BITOR               (16, "BITOR",       "|",    EnumSet.of(TokenType.OPERATOR)),
+        BITXOR              (17, "BITXOR",      "^",    EnumSet.of(TokenType.OPERATOR)),
+        BITAND              (18, "BITAND",      "&",    EnumSet.of(TokenType.OPERATOR)),
+        NE                  (19, "NE",          "!=",   EnumSet.of(TokenType.OPERATOR)),
+        EQ                  (20, "EQ",          "==",   EnumSet.of(TokenType.OPERATOR)),
+        GE                  (21, "GE",          ">=",   EnumSet.of(TokenType.OPERATOR)),
+        GT                  (22, "GT",          ">",    EnumSet.of(TokenType.OPERATOR)),
+        LE                  (23, "LE",          "<=",   EnumSet.of(TokenType.OPERATOR)),
+        LT                  (24, "LT",          "<",    EnumSet.of(TokenType.OPERATOR)),
+        INSTANCEOF          (25, "INSTANCEOF",  "instanceof",  EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)),
+        LSHIFT              (26, "LSHIFT",      "<<",   EnumSet.of(TokenType.OPERATOR)),
+        RSHIFT              (27, "RSHIFT",      ">>",   EnumSet.of(TokenType.OPERATOR)),
+        URSHIFT             (28, "URSHIFT",     "<<<",  EnumSet.of(TokenType.OPERATOR)),
+        ADD                 (29, "ADD",         "+",    EnumSet.of(TokenType.OPERATOR)),
+        SUB                 (30, "SUB",         "-",    EnumSet.of(TokenType.OPERATOR)),
+        DIV                 (31, "DIV",         "/",    EnumSet.of(TokenType.OPERATOR)),
+        REM                 (32, "REM",         "%",    EnumSet.of(TokenType.OPERATOR)),
+        MUL                 (33, "MUL",         "*",    EnumSet.of(TokenType.OPERATOR)),
+        CAST                (34, "CAST",        "cast", EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)),
+        POS                 (35, "POS",         "+",    EnumSet.of(TokenType.OPERATOR)),
+        NEG                 (36, "NEG",         "-",    EnumSet.of(TokenType.OPERATOR)),
+        NOT                 (37, "NOT",         "!",    EnumSet.of(TokenType.OPERATOR)),
+        BITNOT              (38, "BITNOT",      "~",    EnumSet.of(TokenType.OPERATOR)),
+        PREINC              (39, "PREINC",      "++",   EnumSet.of(TokenType.OPERATOR)),
+        PREDEC              (40, "PREDEC",      "--",   EnumSet.of(TokenType.OPERATOR)),
+        NEWARRAY            (41, "NEWARRAY",    "new",  EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)),
+        NEWINSTANCE         (42, "NEWINSTANCE", "new",  EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)),
+        NEWFROMNAME         (43, "NEWFROMNAME", "new",  EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)),
+        POSTINC             (44, "POSTINC",     "++",   EnumSet.of(TokenType.OPERATOR)),
+        POSTDEC             (45, "POSTDEC",     "--",   EnumSet.of(TokenType.OPERATOR)),
+        FIELD               (46, "FIELD",       "field", EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)),
+        METHOD              (47, "METHOD",      "method",  EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)),
+        ARRAYACCESS         (48, "ARRAYACCESS", "[]",   EnumSet.of(TokenType.OPERATOR)),
+        NEW                 (49, "NEW",         "new",  EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)),
+        INC                 (50, "INC",         "++",   EnumSet.of(TokenType.OPERATOR)),
+        DEC                 (51, "DEC",         "--",   EnumSet.of(TokenType.OPERATOR)),
+
+        CONVERT             (55, "CONVERT",     "convert", EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)),
+        EXPR                (56, "EXPR",        "expr", EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)),
+        ARRAY               (57, "ARRAY",       "array", EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)),
+        GOTO                (58, "GOTO",        "goto", EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)),
+
+    /*
+     * Value tokens
+     */
+        IDENT               (60, "IDENT",       "Identifier", EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME, TokenType.JASM_IDENT), KeywordType.VALUE),
+        BOOLEANVAL          (61, "BOOLEANVAL",  "Boolean",    EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME),   KeywordType.VALUE),
+        BYTEVAL             (62, "BYTEVAL",     "Byte",       EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME)),
+        CHARVAL             (63, "CHARVAL",     "Char",       EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME)),
+        SHORTVAL            (64, "SHORTVAL",    "Short",      EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME)),
+        INTVAL              (65, "INTVAL",      "Integer",    EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME),   KeywordType.VALUE),
+        LONGVAL             (66, "LONGVAL",     "Long",       EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME),   KeywordType.VALUE),
+        FLOATVAL            (67, "FLOATVAL",    "Float",      EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME),   KeywordType.VALUE),
+        DOUBLEVAL           (68, "DOUBLEVAL",   "Double",     EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME),   KeywordType.VALUE),
+        STRINGVAL           (69, "STRINGVAL",   "String",     EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME),   KeywordType.VALUE),
+
+    /*
+     * Type keywords
+     */
+        BYTE                (70, "BYTE",        "byte",     EnumSet.of(TokenType.TYPE, TokenType.MODULE_NAME )),
+        CHAR                (71, "CHAR",        "char",     EnumSet.of(TokenType.TYPE, TokenType.MODULE_NAME )),
+        SHORT               (72, "SHORT",       "short",    EnumSet.of(TokenType.TYPE, TokenType.MODULE_NAME )),
+        INT                 (73, "INT",         "int",      EnumSet.of(TokenType.TYPE, TokenType.MODULE_NAME )),
+        LONG                (74, "LONG",        "long",     EnumSet.of(TokenType.TYPE, TokenType.MODULE_NAME )),
+        FLOAT               (75, "FLOAT",       "float",    EnumSet.of(TokenType.TYPE, TokenType.MODULE_NAME)),
+        DOUBLE              (76, "DOUBLE",      "double",   EnumSet.of(TokenType.TYPE, TokenType.MODULE_NAME)),
+        VOID                (77, "VOID",        "void",     EnumSet.of(TokenType.TYPE, TokenType.MODULE_NAME)),
+        BOOLEAN             (78, "BOOLEAN",     "boolean",  EnumSet.of(TokenType.TYPE, TokenType.MODULE_NAME)),
+
+    /*
+     * Expression keywords
+     */
+        TRUE                (80, "TRUE",        "true",     EnumSet.of(TokenType.EXPRESSION, TokenType.MODULE_NAME )),
+        FALSE               (81, "FALSE",       "false",    EnumSet.of(TokenType.EXPRESSION, TokenType.MODULE_NAME )),
+        THIS                (82, "THIS",        "this",     EnumSet.of(TokenType.EXPRESSION, TokenType.MODULE_NAME )),
+        SUPER               (83, "SUPER",       "super",    EnumSet.of(TokenType.MODIFIER,   TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        NULL                (84, "NULL",        "null",     EnumSet.of(TokenType.EXPRESSION, TokenType.MODULE_NAME )),
+
+    /*
+     * Statement keywords
+     */
+        IF                  (90, "IF",          "if",       EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )),
+        ELSE                (91, "ELSE",        "else",     EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )),
+        FOR                 (92, "FOR",         "for",      EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )),
+        WHILE               (93, "WHILE",       "while",    EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )),
+        DO                  (94, "DO",          "do",       EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )),
+        SWITCH              (95, "SWITCH",      "switch",   EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )),
+        CASE                (96, "CASE",        "case",     EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )),
+        DEFAULT             (97,  "DEFAULT",    "default",  EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        BREAK               (98, "BREAK",       "break",    EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )),
+        CONTINUE            (99, "CONTINUE",    "continue", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )),
+        RETURN              (100, "RETURN",     "return",   EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )),
+        TRY                 (101, "TRY",        "try",      EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )),
+
+        CATCH               (102, "CATCH",      "catch",    EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )),
+        FINALLY             (103, "FINALLY",    "finally",  EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )),
+        THROW               (104, "THROW",      "throw",            EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )),
+        STAT                (105, "STAT",       "stat",             EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )),
+        EXPRESSION          (106, "EXPRESSION", "expression",       EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )),
+        DECLARATION         (107, "DECLARATION", "declaration",     EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )),
+        VARDECLARATION      (108, "VARDECLARATION", "vdeclaration", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )),
+
+    /*
+     * Declaration keywords
+     */
+        IMPORT              (110, "IMPORT",     "import",   EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME )),
+        CLASS               (111, "CLASS",      "class",    EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        EXTENDS             (112, "EXTENDS",    "extends",  EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        IMPLEMENTS          (113, "IMPLEMENTS", "implements",   EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        INTERFACE           (114, "INTERFACE",  "interface",    EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        PACKAGE             (115, "PACKAGE",    "package",  EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        ENUM                (116, "ENUM",       "enum",     EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        MANDATED            (117, "MANDATED",   "mandated", EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        THROWS              (118, "THROWS",     "throws",   EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+
+    /*
+     * Modifier keywords
+     */
+        ANNOTATION_ACCESS   (119, "ANNOTATION_ACCESS",  "annotation",       EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        PRIVATE             (120, "PRIVATE",            "private",          EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        PUBLIC              (121, "PUBLIC",             "public",           EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        PROTECTED           (122, "PROTECTED",          "protected",        EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        CONST               (123, "CONST",              "const",            EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME), KeywordType.KEYWORD),
+        STATIC              (124, "STATIC",             "static",           EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        TRANSIENT           (125, "TRANSIENT",          "transient",        EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        SYNCHRONIZED        (126, "SYNCHRONIZED",       "synchronized",     EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        NATIVE              (127, "NATIVE",             "native",           EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        FINAL               (128, "FINAL",              "final",            EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        VOLATILE            (129, "VOLATILE",           "volatile",         EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        ABSTRACT            (130, "ABSTRACT",           "abstract",         EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        TRANSITIVE          (131, "TRANSITIVE",         "transitive",       EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        OPEN                (132, "OPEN",               "open",             EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+
+    /*
+     * Punctuation
+     */
+        AT_SIGN             (133, "AT",         ";",       EnumSet.of(TokenType.PUNCTUATION), KeywordType.VALUE),
+        SEMICOLON           (134, "SEMICOLON",  ";",       EnumSet.of(TokenType.PUNCTUATION), KeywordType.VALUE),
+        COLON               (135, "COLON",      ":",       EnumSet.of(TokenType.PUNCTUATION), KeywordType.VALUE),
+        QUESTIONMARK        (136, "QUESTIONMARK", "?",     EnumSet.of(TokenType.PUNCTUATION)),
+        LBRACE              (137, "LBRACE",     "{",       EnumSet.of(TokenType.PUNCTUATION), KeywordType.VALUE),
+        RBRACE              (138, "RBRACE",     "}",       EnumSet.of(TokenType.PUNCTUATION), KeywordType.VALUE),
+        LPAREN              (139, "LPAREN",     "(",       EnumSet.of(TokenType.PUNCTUATION)),
+        RPAREN              (140, "RPAREN",     ")",       EnumSet.of(TokenType.PUNCTUATION)),
+        LSQBRACKET          (141, "LSQBRACKET", "[",       EnumSet.of(TokenType.PUNCTUATION)),
+        RSQBRACKET          (142, "RSQBRACKET", "]",       EnumSet.of(TokenType.PUNCTUATION)),
+
+        ESCAPED_COLON       (201, "ESCCOLON",     "\\:",     EnumSet.of(TokenType.PUNCTUATION, TokenType.MODULE_NAME)),
+        ESCAPED_ATSIGH      (202, "ESCATSIGH",    "\\@",     EnumSet.of(TokenType.PUNCTUATION, TokenType.MODULE_NAME)),
+        ESCAPED_BACKSLASH   (203, "ESCBACKSLASH", "\\\\",    EnumSet.of(TokenType.PUNCTUATION, TokenType.MODULE_NAME)),
+    /*
+     * Special tokens
+     */
+        ERROR               (145, "ERROR",      "error",    EnumSet.of(TokenType.MODIFIER,    TokenType.MODULE_NAME)),
+        COMMENT             (146, "COMMENT",    "comment",  EnumSet.of(TokenType.MODIFIER,    TokenType.MODULE_NAME)),
+        TYPE                (147, "TYPE",       "type",     EnumSet.of(TokenType.MODIFIER,    TokenType.MODULE_NAME)),
+        LENGTH              (148, "LENGTH",     "length",   EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME )),
+        INLINERETURN        (149, "INLINERETURN", "inline-return",  EnumSet.of(TokenType.MODIFIER)),
+        INLINEMETHOD        (150, "INLINEMETHOD", "inline-method",  EnumSet.of(TokenType.MODIFIER)),
+        INLINENEWINSTANCE   (151, "INLINENEWINSTANCE", "inline-new",EnumSet.of(TokenType.MODIFIER)),
+
+    /*
+     * Added for jasm
+     */
+        METHODREF           (152, "METHODREF",  "Method",   EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        FIELDREF            (153, "FIELD",      "Field",    EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        STACK               (154, "STACK",      "stack",    EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        LOCAL               (155, "LOCAL",      "locals",   EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        CPINDEX             (156, "CPINDEX",    "CPINDEX",  EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME )),
+        CPNAME              (157, "CPNAME",     "CPName",   EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME )),
+        SIGN                (158, "SIGN",       "SIGN",     EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME )),
+        BITS                (159, "BITS",       "bits",                 EnumSet.of(TokenType.MISC, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+
+        INF                 (160, "INF",        "Inf", "Infinity",  EnumSet.of(TokenType.MISC, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        NAN                 (161, "NAN",        "NaN",                  EnumSet.of(TokenType.MISC, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+
+        INNERCLASS          (162, "INNERCLASS", "InnerClass",       EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        OF                  (163, "OF",         "of",               EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        SYNTHETIC           (164, "SYNTHETIC",  "synthetic",  EnumSet.of(TokenType.MODIFIER, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        STRICT              (165, "STRICT",     "strict",     EnumSet.of(TokenType.MODIFIER, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        DEPRECATED          (166, "DEPRECATED", "deprecated", EnumSet.of(TokenType.MODIFIER, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        VERSION             (167, "VERSION",    "version",    EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        MODULE              (168, "MODULE",     "module",   EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        ANNOTATION          (169, "ANNOTATION", "@",        EnumSet.of(TokenType.MISC, TokenType.MODULE_NAME )),
+        PARAM_NAME          (173, "PARAM_NAME", "#",        EnumSet.of(TokenType.MISC, TokenType.MODULE_NAME )),
+
+        VARARGS             (170, "VARARGS",    "varargs",  EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        BRIDGE              (171, "BRIDGE",     "bridge",   EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+
+        // Declaration keywords
+        BOOTSTRAPMETHOD     (172, "BOOTSTRAPMETHOD", "BootstrapMethod", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        NESTHOST            (173, "NESTHOST",       "NestHost",         EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        NESTMEMBERS         (174, "NESTMEMBERS",    "NestMembers",      EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        //
+        RECORD              (175, "RECORD",    "Record",                EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        COMPONENT           (176, "COMPONENT", "Component",             EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        //
+        PERMITTEDSUBCLASSES (177, "PERMITTEDSUBCLASSES", "PermittedSubclasses", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+
+        //Module statements
+        REQUIRES            (180, "REQUIRES", "requires", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        EXPORTS             (182, "EXPORTS",  "exports",  EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        TO                  (183, "TO",       "to",       EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        USES                (184, "USES",     "uses",     EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        PROVIDES            (185, "PROVIDES", "provides", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        WITH                (186, "WITH",     "with",     EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        OPENS               (187, "OPENS",    "opens",    EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+
+        // Table 4.7.20.2-1 type_path_kind
+        ARRAY_TYPEPATH         (188, TypeAnnotationTypes.EPathKind.ARRAY.parseKey(),    TypeAnnotationTypes.EPathKind.ARRAY.parseKey(),
+                EnumSet.of(TokenType.TYPE_PATH_KIND, TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        INNER_TYPE_TYPEPATH    (189, TypeAnnotationTypes.EPathKind.INNER_TYPE.parseKey(),    TypeAnnotationTypes.EPathKind.INNER_TYPE.parseKey(),
+                EnumSet.of(TokenType.TYPE_PATH_KIND, TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        WILDCARD_TYPEPATH      (190, TypeAnnotationTypes.EPathKind.WILDCARD.parseKey(),    TypeAnnotationTypes.EPathKind.WILDCARD.parseKey(),
+                EnumSet.of(TokenType.TYPE_PATH_KIND, TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        TYPE_ARGUMENT_TYPEPATH (191, TypeAnnotationTypes.EPathKind.TYPE_ARGUMENT.parseKey(),    TypeAnnotationTypes.EPathKind.TYPE_ARGUMENT.parseKey(),
+                EnumSet.of(TokenType.TYPE_PATH_KIND, TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+
+        // Valhalla
+        VALUE              (200, "VALUE",     "value",     EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        PRIMITIVE          (202, "PRIMITIVE", "primitive", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD),
+        PRELOAD            (203, "PRELOAD",    "Preload",  EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD);
+
+
+        final static EnumSet<Token> ALL_TOKENS = EnumSet.allOf(Token.class);
+        // Misc Keywords
+        final private Integer value;                    // 160
+        final private String  printval;                 // INF
+        final private String  parsekey;                 // inf
+        final private String  alias;                    // Infinity
+        final private EnumSet<TokenType>  tokenType;    // TokenType.MISC, TokenType.MODULE_NAME
+        final private KeywordType key_type;             // KeywordType.KEYWORD
+
+        public static Optional<Token> get(String  parsekey, KeywordType ktype) {
+            return ALL_TOKENS.stream().
+                    filter(t->t.key_type == ktype).
+                    filter(t->t.parsekey.equals(parsekey) || ( t.alias != null && t.alias.equals(parsekey))).
+                    findFirst();
+        }
+
+        /**
+         * Checks that this enum element is in an enum list
+         *
+         * @param tokens the list of enum elements for checking
+         * @return true if a tokens list contains this enum element
+         */
+        public boolean in(Token... tokens) {
+            return (tokens == null) ? false : Arrays.asList(tokens).contains(this);
+        }
+
+        // By default, if a KeywordType is not specified, it has the value 'TOKEN'
+        Token(Integer val, String print, String parsekey, EnumSet<TokenType> ttype) {
+            this(val, print, parsekey, null, ttype, KeywordType.TOKEN);
+        }
+
+        Token(Integer val, String print, String parsekey, String als, EnumSet<TokenType> ttype) {
+            this(val, print, parsekey, als, ttype, KeywordType.TOKEN);
+        }
+
+        Token(Integer val, String print, String parsekey, EnumSet<TokenType> ttype, KeywordType ktype) {
+            this(val, print, parsekey, null, ttype, ktype);
+        }
+
+        Token(Integer val, String print, String parsekey, String als, EnumSet<TokenType> ttype, KeywordType ktype) {
+            this.value = val;
+            this.printval = print;
+            this.parsekey = parsekey;
+            this.tokenType = ttype;
+            this.key_type = ktype;
+            this.alias = als;
+        }
+
+        public String printValue() {
+            return printval;
+        }
+
+        public String parseKey() {
+            return parsekey;
+        }
+
+        public int value() {
+            return value;
+        }
+
+        public boolean possibleJasmIdentifier() {
+            return tokenType.contains(TokenType.JASM_IDENT);
+        }
+
+        public boolean possibleModuleName() {  return tokenType.contains(TokenType.MODULE_NAME)  && !tokenType.contains(TokenType.PUNCTUATION); }
+
+        /**
+         * Checks a token belonging to the table: Table 4.7.20.2-A. Interpretation of type_path_kind values
+         *
+         * @return true if token is ARRAY, INNER_TYPE, WILDCARD or TYPE_ARGUMENT
+         */
+        public boolean possibleTypePathKind() { return tokenType.contains(TokenType.TYPE_PATH_KIND); }
+
+        @Override
+        public String toString() {
+            return "<" + printval + "> [" + value + "]";
+        }
+    }
+
+    public static Token keyword_token_ident(String idValue) {
+        return Token.get(idValue,KeywordType.KEYWORD).orElse(Token.IDENT);
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/Main.java b/test/lib/org/openjdk/asmtools/jasm/Main.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/Main.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 1996, 2019, 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 org.openjdk.asmtools.jasm;
+
+import static org.openjdk.asmtools.jasm.CFVersion.DEFAULT_MAJOR_VERSION;
+import static org.openjdk.asmtools.jasm.CFVersion.DEFAULT_MINOR_VERSION;
+
+import org.openjdk.asmtools.common.Tool;
+import org.openjdk.asmtools.util.I18NResourceBundle;
+import org.openjdk.asmtools.util.ProductInfo;
+
+import java.io.*;
+import java.util.ArrayList;
+
+/**
+ *
+ *
+ */
+public class Main extends Tool {
+
+    public static final I18NResourceBundle i18n
+            = I18NResourceBundle.getBundleForClass(Main.class);
+
+    private File destDir = null;
+    private boolean traceFlag = false;
+    private long tm = System.currentTimeMillis();
+    private ArrayList<String> v = new ArrayList<>();
+    private boolean nowrite = false;
+    private boolean nowarn = false;
+    private boolean strict = false;
+    private String props = null;
+    private int nwarnings = 0;
+    private CFVersion cfv = new CFVersion();
+    private int bytelimit = 0;
+    private boolean debugScanner = false;
+    private boolean debugMembers = false;
+    private boolean debugCP = false;
+    private boolean debugAnnot = false;
+    private boolean debugInstr = false;
+
+
+    public Main(PrintWriter out, String programName) {
+        super(out, programName);
+        printCannotReadMsg = (fname) ->
+            error( i18n.getString("jasm.error.cannot_read", fname));
+    }
+
+    public Main(PrintStream out, String program) {
+        this(new PrintWriter(out), program);
+    }
+
+    @Override
+    public void usage() {
+        println(i18n.getString("jasm.usage"));
+        println(i18n.getString("jasm.opt.d"));
+        println(i18n.getString("jasm.opt.g"));
+        println(i18n.getString("jasm.opt.v"));
+        println(i18n.getString("jasm.opt.nowrite"));
+        println(i18n.getString("jasm.opt.nowarn"));
+        println(i18n.getString("jasm.opt.strict"));
+        println(i18n.getString("jasm.opt.cv", DEFAULT_MAJOR_VERSION, DEFAULT_MINOR_VERSION));
+        println(i18n.getString("jasm.opt.version"));
+    }
+
+    /**
+     * Run the compiler
+     */
+    private synchronized boolean parseArgs(String argv[]) {
+        // Parse arguments
+        boolean frozenCFV = false;
+        for (int i = 0; i < argv.length; i++) {
+            String arg = argv[i];
+            switch (arg) {
+                case "-v":
+                    traceFlag = true;
+                    break;
+                case "-g":
+                    super.DebugFlag = () -> true;
+                    break;
+                case "-nowrite":
+                    nowrite = true;
+                    break;
+                case "-strict":
+                    strict = true;
+                    break;
+                case "-nowarn":
+                    nowarn = true;
+                    break;
+                case "-version":
+                    println(ProductInfo.FULL_VERSION);
+                    break;
+                case "-d":
+                    if ((i + 1) >= argv.length) {
+                        error(i18n.getString("jasm.error.d_requires_argument"));
+                        usage();
+                        return false;
+                    }
+                    destDir = new File(argv[++i]);
+                    if (!destDir.exists()) {
+                        error(i18n.getString("jasm.error.does_not_exist", destDir.getPath()));
+                        return false;
+                    }
+                    break;
+                // non-public options
+                case "-XdScanner":
+                    debugScanner = true;
+                    break;
+                case "-XdMember":
+                    debugMembers = true;
+                    break;
+                case "-XdCP":
+                    debugCP = true;
+                    break;
+                case "-XdInstr":
+                    debugInstr = true;
+                    break;
+                case "-XdAnnot":
+                    debugAnnot = true;
+                    break;
+                case "-XdAll":
+                    debugScanner = true;
+                    debugMembers = true;
+                    debugCP = true;
+                    debugInstr = true;
+                    debugAnnot = true;
+                    break;
+                case "-Xdlimit":
+                    // parses file until the specified byte number
+                    if (i + 1 > argv.length) {
+                        println(" Error: Unspecified byte-limit");
+                        return false;
+                    } else {
+                        i++;
+                        String bytelimstr = argv[i];
+                        bytelimit = 0;
+                        try {
+                            bytelimit = Integer.parseInt(bytelimstr);
+                        } catch (NumberFormatException e) {
+                            println(" Error: Unspecified byte-limit");
+                            return false;
+                        }
+                    }
+                    break;
+                case "-fixcv":
+                    // overrides cf version if it's defined in the source file.
+                    frozenCFV = true;
+                // public options
+                case "-cv":
+                    if ((i + 1) >= argv.length) {
+                        error(i18n.getString("jasm.error.cv_requires_arg"));
+                        usage();
+                        return false;
+                    }
+                    String[] versions = {"", ""};                      // workaround for String.split()
+                    int index = argv[++i].indexOf(".");                //
+                    if (index != -1) {                                 //
+                        versions[0] = argv[i].substring(0, index);     //
+                        versions[1] = argv[i].substring(index + 1);    //
+                    }                                                  //
+                    if (versions.length != 2) {
+                        error(i18n.getString("jasm.error.invalid_major_minor_param"));
+                        usage();
+                        return false;
+                    }
+                    try {
+                        cfv = new CFVersion(frozenCFV, Short.parseShort(versions[0]), Short.parseShort(versions[1]) );
+                    } catch (NumberFormatException e) {
+                        error(i18n.getString("jasm.error.invalid_major_minor_param"));
+                        usage();
+                        return false;
+                    }
+                    break;
+                default:
+                    if (arg.startsWith("-")) {
+                        error(i18n.getString("jasm.error.invalid_option", arg));
+                        usage();
+                        return false;
+                    } else {
+                        v.add(argv[i]);
+                    }
+                    break;
+            }
+        }
+        if (v.size() == 0) {
+            usage();
+            return false;
+        }
+        if (strict) {
+            nowarn = false;
+        }
+        return true;
+    }
+
+    private void reset() {
+        destDir = null;
+        traceFlag = false;
+        super.DebugFlag = () -> false;
+        System.currentTimeMillis();
+        v = new ArrayList<>();
+        nowrite = false;
+        nowarn = false;
+        strict = false;
+        props = null;
+        nwarnings = 0;
+        bytelimit = 0;
+    }
+
+    /**
+     * Run the compiler
+     */
+    public synchronized boolean compile(String argv[]) {
+        // Reset the state of all objs
+        reset();
+
+        boolean validArgs = parseArgs(argv);
+        if (!validArgs) {
+            return false;
+        }
+        // compile all input files
+        Environment sf = null;
+        try {
+            for (String inpname : v) {
+                Parser p;
+
+                DataInputStream dataInputStream = getDataInputStream(inpname);
+                if( dataInputStream == null ) {
+                    nerrors++;
+                    continue;
+                }
+                sf = new Environment(dataInputStream, inpname, out, nowarn);
+                sf.traceFlag = traceFlag;
+                sf.debugInfoFlag = DebugFlag.getAsBoolean();
+                p = new Parser(sf, cfv.clone() );
+                p.setDebugFlags(debugScanner, debugMembers, debugCP, debugAnnot, debugInstr);
+                p.parseFile();
+
+                nerrors += sf.nerrors;
+                nwarnings += sf.nwarnings;
+                if (nowrite || (nerrors > 0)) {
+                    sf.flushErrors();
+                    continue;
+                }
+                try {
+                    ClassData[] clsData = p.getClassesData();
+                    for (int i = 0; i < clsData.length; i++) {
+                        ClassData cd = clsData[i];
+                        if (bytelimit > 0) {
+                            cd.setByteLimit(bytelimit);
+                        }
+                        cd.write(destDir);
+                    }
+                } catch (IOException ex) {
+                    if (bytelimit > 0) {
+                        // IO Error thrown from user-specified byte count
+                        ex.printStackTrace();
+                        error("UserSpecified byte-limit at byte[" + bytelimit + "]: " +
+                                ex.getMessage() + "\n" +
+                                inpname + ": [" + sf.lineNumber() + ", " + sf.lineOffset() + "]");
+                    } else {
+                        String er = i18n.getString("jasm.error.cannot_write", ex.getMessage());
+                        error(er + "\n" + inpname + ": [" + sf.lineNumber() + ", " + sf.lineOffset() + "]");
+                    }
+                }
+                sf.flushErrors(); // possible errors from write()
+            }
+        } catch (Error ee) {
+            if (DebugFlag.getAsBoolean()) {
+                ee.printStackTrace();
+            }
+            String er = ee.getMessage() + "\n" + i18n.getString("jasm.error.fatal_error");
+            error(er + "\n" + sf.getInputFileName() + ": [" + sf.lineNumber() + ", " + sf.lineOffset() + "]");
+        } catch (Exception ee) {
+            if (DebugFlag.getAsBoolean()) {
+                ee.printStackTrace();
+            }
+            String er = ee.getMessage() + "\n" + ee.getMessage() + "\n" + i18n.getString("jasm.error.fatal_exception");
+            error(er + "\n" + sf.getInputFileName() + ": [" + sf.lineNumber() + ", " + sf.lineOffset() + "]");
+        }
+
+        boolean errs = nerrors > 0;
+        boolean warns = (nwarnings > 0) && (!nowarn);
+        boolean errsOrWarns = errs || warns;
+        if (!errsOrWarns) {
+            return true;
+        }
+        if (errs) {
+            out.print(nerrors > 1 ? (nerrors + " errors") : "1 error");
+        }
+        if (errs && warns) {
+            out.print(", ");
+        }
+        if (warns) {
+            out.print(nwarnings > 1 ? (nwarnings + " warnings") : "1 warning");
+        }
+        println();
+        if (strict) {
+            return !errsOrWarns;
+        } else {
+            return !errs;
+        }
+    }
+
+    /**
+     * main program
+     */
+    public static void main(String argv[]) {
+        Main compiler = new Main(new PrintWriter(System.out), "jasm");
+        System.exit(compiler.compile(argv) ? 0 : 1);
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/MemberData.java b/test/lib/org/openjdk/asmtools/jasm/MemberData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/MemberData.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jasm;
+
+import static org.openjdk.asmtools.jasm.RuntimeConstants.DEPRECATED_ATTRIBUTE;
+import static org.openjdk.asmtools.jasm.RuntimeConstants.SYNTHETIC_ATTRIBUTE;
+import org.openjdk.asmtools.jasm.Tables.AttrTag;
+import java.util.ArrayList;
+
+/**
+ * The common base structure for field_info, method_info, and component_info
+ */
+abstract public class MemberData {
+
+    protected int access;
+    protected AttrData syntheticAttr, deprecatedAttr;
+    protected DataVectorAttr<AnnotationData> annotAttrVis = null;
+    protected DataVectorAttr<AnnotationData> annotAttrInv = null;
+    protected DataVectorAttr<TypeAnnotationData> type_annotAttrVis = null;
+    protected DataVectorAttr<TypeAnnotationData> type_annotAttrInv = null;
+    protected ClassData cls;
+    protected AttrData signatureAttr;
+
+    public MemberData(ClassData cls, int access) {
+        this.cls = cls;
+        init(access);
+    }
+
+    public MemberData(ClassData cls) {
+        this.cls = cls;
+    }
+
+    public void init(int access) {
+        this.access = access;
+    }
+
+    public void createPseudoMod() {
+        // If a member has a Pseudo-modifier
+
+        // create the appropriate marker attributes,
+        // and clear the PseudoModifiers from the access flags.
+        if (Modifiers.isSyntheticPseudoMod(access)) {
+            syntheticAttr = new AttrData(cls, AttrTag.ATT_Synthetic.parsekey());
+            access &= ~SYNTHETIC_ATTRIBUTE;
+        }
+        if (Modifiers.isDeprecatedPseudoMod(access)) {
+            deprecatedAttr = new AttrData(cls, AttrTag.ATT_Deprecated.parsekey());
+            access &= ~DEPRECATED_ATTRIBUTE;
+        }
+    }
+
+    public void setSignatureAttr(ConstantPool.ConstCell value_cpx) {
+        signatureAttr = new CPXAttr(cls, Tables.AttrTag.ATT_Signature.parsekey(), value_cpx);
+    }
+
+    protected abstract DataVector getAttrVector();
+
+    protected final DataVector getDataVector(Data... extraAttrs) {
+        DataVector attrs = new DataVector();
+        for( Data extra : extraAttrs ) {
+            if (extra != null) {
+                attrs.add(extra);
+            }
+        }
+        // common set for [ FieldData, MethodData, RecordData ]
+        if (annotAttrVis != null) {
+            attrs.add(annotAttrVis);
+        }
+        if (annotAttrInv != null) {
+            attrs.add(annotAttrInv);
+        }
+        if (type_annotAttrVis != null) {
+            attrs.add(type_annotAttrVis);
+        }
+        if (type_annotAttrInv != null) {
+            attrs.add(type_annotAttrInv);
+        }
+        return attrs;
+    }
+
+    public void addAnnotations(ArrayList<AnnotationData> list) {
+        for (AnnotationData item : list) {
+            boolean invisible = item.invisible;
+            if (item instanceof TypeAnnotationData) {
+                // Type Annotations
+                TypeAnnotationData ta = (TypeAnnotationData) item;
+                if (invisible) {
+                    if (type_annotAttrInv == null) {
+                        type_annotAttrInv = new DataVectorAttr(cls,
+                                AttrTag.ATT_RuntimeInvisibleTypeAnnotations.parsekey());
+                    }
+                    type_annotAttrInv.add(ta);
+                } else {
+                    if (type_annotAttrVis == null) {
+                        type_annotAttrVis = new DataVectorAttr(cls,
+                                AttrTag.ATT_RuntimeVisibleTypeAnnotations.parsekey());
+                    }
+                    type_annotAttrVis.add(ta);
+                }
+            } else {
+                // Regular Annotations
+                if (invisible) {
+                    if (annotAttrInv == null) {
+                        annotAttrInv = new DataVectorAttr(cls,
+                                AttrTag.ATT_RuntimeInvisibleAnnotations.parsekey());
+                    }
+                    annotAttrInv.add(item);
+                } else {
+                    if (annotAttrVis == null) {
+                        annotAttrVis = new DataVectorAttr(cls,
+                                AttrTag.ATT_RuntimeVisibleAnnotations.parsekey());
+                    }
+                    annotAttrVis.add(item);
+                }
+            }
+        }
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/MethodData.java b/test/lib/org/openjdk/asmtools/jasm/MethodData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/MethodData.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 1996, 2014, 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 org.openjdk.asmtools.jasm;
+
+import org.openjdk.asmtools.jasm.Tables.AttrTag;
+import org.openjdk.asmtools.jasm.ConstantPool.ConstCell;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeMap;
+
+/**
+ *
+ */
+class MethodData extends MemberData {
+
+    /**
+     * MethodParamData
+     */
+    class ParamNameData implements Data {
+
+        int access;
+        ConstCell name;
+
+        public ParamNameData(int access, ConstCell name) {
+            this.access = access;
+            this.name = name;
+        }
+
+        @Override
+        public int getLength() {
+            return 4;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            int nm = 0;
+            int ac = 0;
+            if (name != null) {
+                nm = name.arg;
+                ac = access;
+            }
+            out.writeShort(nm);
+            out.writeShort(ac);
+        }
+    }// end class MethodParamData
+
+    /**
+     * Used to store Parameter Arrays (as attributes)
+     */
+    static public class DataPArrayAttr<T extends Data> extends AttrData implements Constants {
+
+        TreeMap<Integer, ArrayList<T>> elements; // Data
+        int paramsTotal;
+
+        public DataPArrayAttr(ClassData cls, String name, int paramsTotal, TreeMap<Integer, ArrayList<T>> elements) {
+            super(cls, name);
+            this.paramsTotal = paramsTotal;
+            this.elements = elements;
+        }
+
+        public DataPArrayAttr(ClassData cls, String name, int paramsTotal) {
+            this(cls, name, paramsTotal, new TreeMap<Integer, ArrayList<T>>());
+        }
+
+        public void put(int paramNum, T element) {
+            ArrayList<T> v = get(paramNum);
+            if (v == null) {
+                v = new ArrayList<>();
+                elements.put(paramNum, v);
+            }
+
+            v.add(element);
+        }
+
+        public ArrayList<T> get(int paramNum) {
+            return elements.get(paramNum);
+        }
+
+        @Override
+        public int attrLength() {
+            int length = 1;  // One byte for the parameter count
+
+            // calculate overall size here rather than in add()
+            // because it may not be available at the time of invoking of add()
+            for (int i = 0; i < paramsTotal; i++) {
+                ArrayList<T> attrarray = get(i);
+                if (attrarray != null) {
+                    for (Data item : attrarray) {
+                        length += item.getLength();
+                    }
+                }
+                length += 2; // 2 bytes for the annotation count for each parameter
+            }
+
+            return length;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            super.write(out);  // attr name, attr len
+            out.writeByte(paramsTotal); // number of parameters total (in byte)
+
+            for (int i = 0; i < paramsTotal; i++) {
+                ArrayList<T> attrarray = get(i);
+                if (attrarray != null) {
+                    // write out the number of annotations for the current param
+                    out.writeShort(attrarray.size());
+                    for (T item : attrarray) {
+                        item.write(out); // write the current annotation
+                    }
+                } else {
+                    out.writeShort(0);
+                    // No annotations to write out
+                }
+            }
+        }
+    }// end class DataPArrayAttr
+
+
+    /* Method Data Fields */
+    protected Environment env;
+    protected ConstCell nameCell, sigCell;
+    protected CodeAttr code;
+    protected DataVectorAttr<ConstCell> exceptions = null;
+    protected DataVectorAttr<ParamNameData> paramNames = null;
+    protected DataPArrayAttr<AnnotationData> pannotAttrVis = null;
+    protected DataPArrayAttr<AnnotationData> pannotAttrInv = null;
+    protected DefaultAnnotationAttr defaultAnnot = null;
+
+    public MethodData(ClassData cls, int acc,
+            ConstCell name, ConstCell sig, ArrayList<ConstCell> exc_table) {
+        super(cls, acc);
+        this.env = cls.env;
+        nameCell = name;
+        sigCell = sig;
+        if ((exc_table != null) && (!exc_table.isEmpty())) {
+            exceptions = new DataVectorAttr<>(cls,
+                    AttrTag.ATT_Exceptions.parsekey(),
+                    exc_table);
+        }
+        // Normalize the modifiers to access flags
+        if (Modifiers.hasPseudoMod(acc)) {
+            createPseudoMod();
+        }
+    }
+
+    public void addMethodParameter(int totalParams, int paramNum, ConstCell name, int access) {
+        env.traceln("addMethodParameter Param[" + paramNum + "] (name: " + name.toString() + ", Flags (" + access + ").");
+        if (paramNames == null) {
+            paramNames = new DataVectorAttr<>(cls, AttrTag.ATT_MethodParameters.parsekey(), true);
+            for (int i = 0; i < totalParams; i++) {
+                // initialize the paramName array (in case the name is not given in Jasm syntax)
+                paramNames.add(new ParamNameData(0, null));
+            }
+        }
+        paramNames.put(paramNum, new ParamNameData(access, name));
+    }
+
+    public CodeAttr startCode(int pos, int paramcnt, Argument max_stack, Argument max_locals) {
+        code = new CodeAttr(this, pos, paramcnt, max_stack, max_locals);
+        return code;
+    }
+
+    public void addDefaultAnnotation(DefaultAnnotationAttr data) {
+        defaultAnnot = data;
+    }
+
+    public void addParamAnnotation(int totalParams, int paramNum, AnnotationData data) {
+        if (!data.invisible) {
+            if (pannotAttrVis == null) {
+                pannotAttrVis = new DataPArrayAttr<>(cls,
+                        AttrTag.ATT_RuntimeVisibleParameterAnnotations.parsekey(),
+                        totalParams);
+            }
+            pannotAttrVis.put(paramNum, data);
+
+        } else {
+            if (pannotAttrInv == null) {
+                pannotAttrInv = new DataPArrayAttr<>(cls,
+                        AttrTag.ATT_RuntimeInvisibleParameterAnnotations.parsekey(),
+                        totalParams);
+            }
+            pannotAttrInv.put(paramNum, data);
+        }
+    }
+
+    @Override
+    protected DataVector getAttrVector() {
+        DataVector dv = getDataVector( exceptions, syntheticAttr, deprecatedAttr, paramNames, code, defaultAnnot);
+        if (pannotAttrVis != null) {
+            dv.add(pannotAttrVis);
+        }
+        if (pannotAttrInv != null) {
+            dv.add(pannotAttrInv);
+        }
+        return dv;
+    }
+
+    /*====================================================== Write */
+    public void write(CheckedDataOutputStream out) throws IOException, Parser.CompilerError {
+        out.writeShort(access);
+        out.writeShort(nameCell.arg);
+        out.writeShort(sigCell.arg);
+        getAttrVector().write(out);
+    }
+} // end MethodData
+
diff a/test/lib/org/openjdk/asmtools/jasm/Modifiers.java b/test/lib/org/openjdk/asmtools/jasm/Modifiers.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/Modifiers.java
@@ -0,0 +1,481 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jasm;
+
+import static org.openjdk.asmtools.jasm.JasmTokens.Token;
+import static org.openjdk.asmtools.jasm.RuntimeConstants.*;
+import static org.openjdk.asmtools.jasm.Tables.CF_Context;
+
+/**
+ *
+ *
+ */
+public class Modifiers {
+
+    /*
+     * Modifier masks
+     */
+    public static final int MM_ATTR        = SYNTHETIC_ATTRIBUTE | DEPRECATED_ATTRIBUTE;
+
+    public static final int MM_ACCESS      = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED;
+
+    public static final int MM_INTRF       = MM_ACCESS  | ACC_ABSTRACT  | ACC_INTERFACE | MM_ATTR | ACC_ANNOTATION;
+
+    public static final int MM_CLASS       = MM_ACCESS  | ACC_FINAL     |  ACC_SUPER    | ACC_ABSTRACT | ACC_ENUM |
+                                             MM_ATTR    |  ACC_MODULE |
+                                             ACC_VALUE | ACC_PRIMITIVE;
+
+    public static final int MM_FIELD       = MM_ACCESS    | ACC_STATIC | ACC_FINAL    |  ACC_VOLATILE | ACC_TRANSIENT |
+                                            ACC_SYNTHETIC | ACC_ENUM   |
+                                            ACC_MANDATED |         // JEP 359 Record
+                                            MM_ATTR;
+
+    public static final int MM_I_METHOD    = ACC_ABSTRACT | ACC_PUBLIC | ACC_PRIVATE | ACC_STATIC | ACC_VARARGS |
+                                            ACC_BRIDGE    | ACC_SYNTHETIC ; // interface method
+
+    public static final int MM_A_METHOD    = MM_ACCESS | ACC_ABSTRACT | MM_ATTR;
+
+    public static final int MM_N_METHOD    = MM_ACCESS | ACC_STRICT | ACC_VARARGS | ACC_SYNTHETIC | MM_ATTR;  // <init>
+
+    public static final int MM_METHOD      = MM_ACCESS    | ACC_STATIC | ACC_FINAL    | ACC_SYNCHRONIZED |  ACC_BRIDGE |
+                                             ACC_VARARGS  | ACC_NATIVE | ACC_ABSTRACT |  ACC_STRICT      | ACC_SYNTHETIC |
+                                             ACC_MANDATED |         // JEP 359 Record
+                                             MM_ATTR ;
+
+    public static final int MM_INNERCLASS  = MM_ACCESS    | ACC_STATIC    | ACC_FINAL      | ACC_SUPER | ACC_INTERFACE |
+                                             ACC_ABSTRACT | ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM  | MM_ATTR |
+                                             ACC_VALUE | ACC_PRIMITIVE;
+
+    public static final int MM_REQUIRES    = ACC_TRANSITIVE | ACC_STATIC_PHASE  | ACC_SYNTHETIC | ACC_MANDATED ;
+
+    public static final int MM_EXPORTS     = ACC_SYNTHETIC | ACC_MANDATED ;
+
+    private Modifiers() {
+    }
+
+    public static boolean validRequires(int mod) {
+        return (mod & ~MM_REQUIRES) == 0;
+    }
+
+    public static boolean validExports(int mod) { return (mod & ~MM_EXPORTS) == 0; }
+
+    public static boolean validInnerClass(int mod) {
+        return (mod & ~MM_INNERCLASS) == 0;
+    }
+
+    public static boolean validField(int mod) {
+        return (mod & ~MM_FIELD) == 0;
+    }
+
+    public static boolean validMethod(int mod) {
+        return (mod & ~MM_METHOD) == 0;
+    }
+
+    public static boolean validInterface(int mod) {
+        return (mod & ~MM_INTRF) == 0;
+    }
+
+    public static int getInvalidModifiers4Interface(int mod) {
+        return  mod & ~MM_INTRF;
+    }
+
+    public static boolean validClass(int mod) {
+        return (mod & ~MM_CLASS) == 0;
+    }
+
+    public static int getInvalidModifiers4Class(int mod) {
+        return (mod & ~MM_CLASS);
+    }
+
+    public static boolean validAbstractMethod(int mod) {
+        return (mod & ~MM_A_METHOD) == 0;
+    }
+
+    public static boolean validInitMethod(int mod) {
+        return (mod & ~MM_N_METHOD) == 0;
+    }
+
+    public static boolean validInterfaceMethod(int mod, ClassData cd) {
+        return ((mod & ~MM_I_METHOD) == 0) &&
+            (cd.cfv.major_version() >= 52 || isPublic(mod) && isAbstract(mod) && !isStatic(mod));
+    }
+
+    public static boolean validInterfaceField(int mod) {
+        return mod == (ACC_STATIC | ACC_PUBLIC | ACC_FINAL);
+    }
+
+    public static boolean isPublic(int mod) {
+        return (mod & ACC_PUBLIC) != 0;
+    }
+
+    public static boolean isPrivate(int mod) {
+        return (mod & ACC_PRIVATE) != 0;
+    }
+
+    public static boolean isProtected(int mod) {
+        return (mod & ACC_PROTECTED) != 0;
+    }
+
+    public static boolean isInterface(int mod) {
+        return (mod & ACC_INTERFACE) != 0;
+    }
+
+    public static boolean isAbstract(int mod) {
+        return (mod & ACC_ABSTRACT) != 0;
+    }
+
+    public static boolean isFinal(int mod) {
+        return (mod & ACC_FINAL) != 0;
+    }
+
+    public static boolean isStatic(int mod) {
+        return (mod & ACC_STATIC) != 0;
+    }
+
+    public static boolean isSynthetic(int mod) {
+        return (mod & ACC_SYNTHETIC) != 0;
+    }
+
+    public static boolean isDeprecated(int mod) {
+        return (mod & DEPRECATED_ATTRIBUTE) != 0;
+    }
+
+    public static boolean isTransient(int mod) {
+        return (mod & ACC_TRANSIENT) != 0;
+    }
+
+    public static boolean isAnnotation(int mod) {
+        return (mod & ACC_ANNOTATION) != 0;
+    }
+
+    public static boolean isNative(int mod) {
+        return (mod & ACC_NATIVE) != 0;
+    }
+
+    public static boolean isStrict(int mod) {
+        return (mod & ACC_STRICT) != 0;
+    }
+
+    public static boolean isEnum(int mod) {
+        return (mod & ACC_ENUM) != 0;
+    }
+
+    public static boolean isSuper(int mod) {
+        return (mod & ACC_SUPER) != 0;
+    }
+
+    public static boolean isModule(int mod) { return (mod & ACC_MODULE)!=0; }
+
+    public static boolean isMandated(int mod) { return (mod & ACC_MANDATED) != 0; }
+
+    public static boolean isSynchronized(int mod) {
+        return (mod & ACC_SYNCHRONIZED) != 0;
+    }
+
+    public static boolean isBridge(int mod) {
+        return (mod & ACC_BRIDGE) != 0;
+    }
+
+    public static boolean isVolatile(int mod) {
+        return (mod & ACC_VOLATILE) != 0;
+    }
+
+    public static boolean isVarArgs(int mod) {
+        return (mod & ACC_VARARGS) != 0;
+    }
+
+    public static boolean isSyntheticPseudoMod(int mod) {
+        return (mod & SYNTHETIC_ATTRIBUTE) != 0;
+    }
+
+    public static boolean isDeprecatedPseudoMod(int mod) {
+        return (mod & DEPRECATED_ATTRIBUTE) != 0;
+    }
+
+    public static boolean hasPseudoMod(int mod) {
+        return isSyntheticPseudoMod(mod) || isDeprecatedPseudoMod(mod);
+    }
+
+    public static boolean isTransitive(int mod) { return (mod & ACC_TRANSITIVE) != 0;  }
+
+    public static boolean isStaticPhase(int mod) { return (mod & ACC_STATIC_PHASE) != 0;  }
+
+    public static boolean isValue(int mod) {
+        return (mod & ACC_VALUE) != 0;
+    }
+
+    public static boolean isPrimitive(int mod) {
+        return (mod & ACC_PRIMITIVE) != 0;
+    }
+
+    /*
+     * Checks that only one (or none) of the Access flags are set.
+     */
+    public static boolean validAccess(int mod) {
+        boolean retval = true;
+        switch (mod & MM_ACCESS) {
+            case 0:
+            case ACC_PUBLIC:
+            case ACC_PRIVATE:
+            case ACC_PROTECTED:
+                break;
+            default:
+                retval = false;
+        }
+        return retval;
+    }
+
+    /*
+     * Are both flags set
+     *
+     */
+    public static boolean both(int mod, int flagA, int flagB) {
+        return (mod & (flagA | flagB)) == (flagA | flagB);
+    }
+
+    /**
+     * Check the modifier flags for the class
+     *
+     * @param env       The error reporting environment.
+     * @param mod       The modifier flags being checked
+     * @param scanner   The file parser
+     */
+    public static void checkClassModifiers(Environment env, int mod, Scanner scanner) {
+        if (isInterface(mod)) {
+            if( isEnum(mod) ) {
+                env.error(scanner.pos, "warn.invalid.modifier.class.intenum");
+            } else if ( !validInterface(mod) ) {
+                env.error(scanner.pos, "warn.invalid.modifier.int",
+                        toString(mod & ~MM_INTRF, CF_Context.CTX_CLASS));
+            }
+            if (!isAbstract(mod)) {
+                env.error(scanner.pos, "warn.invalid.modifier.int.abs");
+            }
+        } else {
+            if ( scanner.token != Token.CLASS && !isEnum(mod) && scanner.token != Token.ANNOTATION) {
+                env.error(scanner.pos, "warn.missing.modifier.class");
+            }
+            if (! validClass(mod)) {
+                env.error(scanner.pos, "warn.invalid.modifier.class",
+                            toString(mod & ~MM_CLASS, CF_Context.CTX_CLASS));
+            }
+            if (isAbstract(mod) && Modifiers.isFinal(mod)) {
+                env.error(scanner.pos, "warn.invalid.modifier.class.finabs");
+            }
+        }
+    }
+
+    /**
+     * Check the modifier flags for the field
+     *
+     * @param cd The ClassData for the current class
+     * @param mod The modifier flags being checked
+     * @param pos the position of the parser in the file
+     */
+    public static void checkFieldModifiers(ClassData cd, int mod, int pos) {
+        Environment env = cd.env;
+        if (cd.isInterface()) {
+            // For interfaces
+            if (!validInterfaceField(mod)) {
+                env.error(pos, "warn.invalid.modifier.intfield");
+            }
+        } else {
+            // For non-interfaces
+            if (!validField(mod)) {
+                env.error(pos, "warn.invalid.modifier.field",
+                        toString(mod & ~MM_FIELD, CF_Context.CTX_METHOD));
+            }
+            if (both(mod, ACC_FINAL, ACC_VOLATILE)) {
+                env.error(pos, "warn.invalid.modifier.fiva");
+            }
+            if (!validAccess(mod)) {
+                env.error(pos, "warn.invalid.modifier.acc");
+            }
+        }
+
+    }
+
+    /**
+     * Check the modifier flags for the method
+     *
+     * @param cd The ClassData for the current class
+     * @param mod The modifier flags being checked
+     * @param pos the position of the parser in the file
+     */
+    public static void checkMethodModifiers(ClassData cd, int mod, int pos, boolean is_init, boolean is_clinit) {
+        Environment env = cd.env;
+        if (!is_clinit) {
+            if (cd.isInterface()) {
+                if (is_init) {
+                    env.error(pos, "warn.init.in_int");
+                } else if (!validInterfaceMethod(mod, cd)) {
+                    int badflags = (mod & ~MM_I_METHOD);
+                    env.error(pos, "warn.invalid.modifier.intmth", toString(badflags, CF_Context.CTX_METHOD)
+                            + "   *****" + toString(mod, CF_Context.CTX_METHOD) + "*****");
+                }
+            } else {
+                if (is_init && !validInitMethod(mod)) {
+                    int badflags = (mod & ~MM_N_METHOD);
+                    env.error(pos, "warn.invalid.modifier.init", toString(badflags, CF_Context.CTX_METHOD)
+                            + "   *****" + toString(mod, CF_Context.CTX_METHOD) + "*****");
+                } else if (isAbstract(mod)) {
+                    if (!validAbstractMethod(mod)) {
+                        int badflags = (mod & ~MM_A_METHOD);
+                        env.error(pos, "warn.invalid.modifier.abst", toString(badflags, CF_Context.CTX_METHOD)
+                                + "   *****" + toString(mod, CF_Context.CTX_METHOD) + "*****");
+                    }
+                } else {
+                    if (!validMethod(mod)) {
+                        env.error(pos, "warn.invalid.modifier.mth",
+                                toString(mod & ~MM_METHOD, CF_Context.CTX_METHOD));
+                    }
+                }
+                if (!validAccess(mod)) {
+                    env.error(pos, "warn.invalid.modifier.acc");
+                }
+            }
+        }
+    }
+
+    /**
+     * Check the modifier flags for the inner-class
+     *
+     * @param cd The ClassData for the current class
+     * @param mod The modifier flags being checked
+     * @param pos the position of the parser in the file
+     */
+    public static void checkInnerClassModifiers(ClassData cd, int mod, int pos) {
+        Environment env = cd.env;
+
+        if (!validInnerClass(mod)) {
+            int badflags = (mod & ~MM_INNERCLASS);
+            env.error(pos, "warn.invalid.modifier.innerclass",
+                    toString(badflags, CF_Context.CTX_INNERCLASS)
+                    + "   *****" + toString(mod, CF_Context.CTX_INNERCLASS) + "*****");
+        }
+
+    }
+
+    private static StringBuffer _accessString(int mod, CF_Context context) {
+        StringBuffer sb = new StringBuffer();
+        if (context == CF_Context.CTX_CLASS && isModule(mod)) {
+            sb.append(Token.MODULE.parseKey() + " ");
+        }
+        if (isPublic(mod)) {
+            sb.append(Token.PUBLIC.parseKey() + " ");
+        }
+        if (isPrivate(mod)) {
+            sb.append(Token.PRIVATE.parseKey() + " ");
+        }
+        if (isProtected(mod)) {
+            sb.append(Token.PROTECTED.parseKey() + " ");
+        }
+        if (isStatic(mod)) {
+            sb.append(Token.STATIC.parseKey() + " ");
+        }
+        if (context == CF_Context.CTX_METHOD && isFinal(mod)) {
+            sb.append(Token.FINAL.parseKey() + " ");
+        }
+        if (context == CF_Context.CTX_FIELD && isTransient(mod)) {
+            sb.append(Token.TRANSIENT.parseKey() + " ");
+        }
+        if (context == CF_Context.CTX_CLASS && isSuper(mod)) {
+            sb.append(Token.SUPER.parseKey() + " ");
+        }
+        if (context == CF_Context.CTX_METHOD && isSynchronized(mod)) {
+            sb.append(Token.SYNCHRONIZED.parseKey() + " ");
+        }
+        if (context == CF_Context.CTX_METHOD) {
+            if (isBridge(mod)) {
+                sb.append(Token.BRIDGE.parseKey() + " ");
+            }
+            if (isVarArgs(mod)) {
+                sb.append(Token.VARARGS.parseKey() + " ");
+            }
+            if (isNative(mod)) {
+                sb.append(Token.NATIVE.parseKey() + " ");
+            }
+            if (isStrict(mod)) {
+                sb.append(Token.STRICT.parseKey() + " ");
+            }
+        }
+        if (isAbstract(mod)) {
+            if ((context != CF_Context.CTX_CLASS) || !isInterface(mod)) {
+                sb.append(Token.ABSTRACT.parseKey() + " ");
+            }
+        }
+        if (  context.isOneOf(CF_Context.CTX_CLASS, CF_Context.CTX_INNERCLASS, CF_Context.CTX_FIELD) && isFinal(mod)) {
+            sb.append(Token.FINAL.parseKey() + " ");
+        }
+        if (context.isOneOf(CF_Context.CTX_CLASS, CF_Context.CTX_INNERCLASS) && isInterface(mod)) {
+            if (isAnnotation(mod)) {
+                sb.append(Token.ANNOTATION_ACCESS.parseKey() + " ");
+            }
+            sb.append(Token.INTERFACE.parseKey() + " ");
+        }
+        if (isSynthetic(mod)) {
+            sb.append(Token.SYNTHETIC.parseKey() + " ");
+        }
+        if (context == CF_Context.CTX_FIELD && isVolatile(mod)) {
+            sb.append(Token.VOLATILE.parseKey() + " ");
+        }
+        if (isEnum(mod)) {
+            sb.append(Token.ENUM.parseKey() + " ");
+        }
+        if (context.isOneOf(CF_Context.CTX_METHOD, CF_Context.CTX_FIELD) && isMandated(mod)) {
+            sb.append(Token.MANDATED.parseKey() + " ");
+        }
+        if (context.isOneOf(CF_Context.CTX_CLASS, CF_Context.CTX_INNERCLASS) && isPrimitive(mod)) {
+            sb.append(Token.PRIMITIVE.parseKey() + " ");
+        }
+        if (context.isOneOf(CF_Context.CTX_CLASS, CF_Context.CTX_INNERCLASS) && isValue(mod)) {
+            sb.append(Token.VALUE.parseKey() + " ");
+        }
+
+        return sb;
+    }
+
+    public static String toString(int mod, CF_Context context) {
+        StringBuffer sb = _accessString(mod, context);
+
+        if (isSyntheticPseudoMod(mod)) {
+            sb.append("Synthetic(Pseudo) ");
+        }
+        if (isDeprecatedPseudoMod(mod)) {
+            sb.append("Deprecated(Pseudo) ");
+        }
+
+        return sb.toString().trim();
+    }
+
+    public static String moduleFlags( int flags ) {
+        return "";
+    }
+
+    public static String accessString(int mod, CF_Context context) {
+        return (context == CF_Context.CTX_MODULE) ?
+            moduleFlags(mod) :
+            _accessString(mod, context).toString();
+    }
+
+}
diff a/test/lib/org/openjdk/asmtools/jasm/ModuleAttr.java b/test/lib/org/openjdk/asmtools/jasm/ModuleAttr.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/ModuleAttr.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.asmtools.jasm;
+
+import org.openjdk.asmtools.common.Module;
+
+import java.io.IOException;
+import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * The module attribute
+ */
+class ModuleAttr extends AttrData {
+  // shared data
+  private Module.Builder builder;
+  private final ClassData clsData;
+  private final Function<String, ConstantPool.ConstCell> findCellAsciz;
+  private final Function<String, ConstantPool.ConstCell> findCellClassByName;
+  private final Function<String, ConstantPool.ConstCell> findCellModuleByName;
+  private final Function<String, ConstantPool.ConstCell> findCellPackageByName;
+
+  // entries to populate tables of the module attribute
+  BiConsumer<String, Integer> requires       = (mn, f) -> this.builder.require(mn, f);
+  BiConsumer<String, Set<String>> exports    = (pn, ms) -> this.builder.exports(new Module.Exported(pn), ms);
+  BiConsumer<String, Set<String>> opens      = (pn, ms) -> this.builder.opens(new Module.Opened(pn), ms);
+  BiConsumer<String, Set<String>> provides   = (tn, ts) -> this.builder.provides(new Module.Provided(tn), ts);
+  Consumer<Set<String>>           uses       = (ts) -> this.builder.uses(ts);
+
+  ModuleAttr(ClassData cdata) {
+    super(cdata, Tables.AttrTag.ATT_Module.parsekey());
+    builder = new Module.Builder();
+    clsData = cdata;
+    findCellAsciz = (name) -> clsData.pool.FindCellAsciz(name);
+    findCellClassByName = (name) -> clsData.pool.FindCellClassByName(name);
+    findCellModuleByName = (name) -> clsData.pool.FindCellModuleByName(name);
+    findCellPackageByName = (name) -> clsData.pool.FindCellPackageByName(name);
+  }
+
+  void openModule() {
+    builder.setModuleFlags(Module.Modifier.ACC_OPEN);
+  }
+  void setModuleName(String value) { builder.setModuleName(value);}
+
+  ModuleAttr build() {
+    Module module = builder.build();
+    Content.instance.header = new HeaderStruct(module.header, findCellModuleByName, findCellAsciz);
+    Content.instance.requiresStruct = new SetStruct<>(module.requires, findCellModuleByName, findCellAsciz);
+    Content.instance.exportsMapStruct = new MapStruct<>(module.exports, findCellPackageByName, findCellModuleByName );
+    Content.instance.opensMapStruct = new MapStruct<>(module.opens,findCellPackageByName, findCellModuleByName );
+    Content.instance.usesStruct = new SetStruct<>(module.uses, findCellClassByName, null);
+    Content.instance.providesMapStruct = new MapStruct<>(module.provides, findCellClassByName, findCellClassByName);
+    return this;
+  }
+
+  @Override
+  public int attrLength() {
+    return Content.instance.getLength();
+  }
+
+  @Override
+  public void write(CheckedDataOutputStream out) throws IOException {
+    super.write(out);
+    Content.instance.write(out);
+  }
+
+  private enum Content implements Data {
+    instance {
+      @Override
+      public int getLength() {
+        return header.getLength() +
+            requiresStruct.getLength() +
+            exportsMapStruct.getLength() +
+            opensMapStruct.getLength() +
+            usesStruct.getLength() +
+            providesMapStruct.getLength();
+      }
+
+      @Override
+      public void write(CheckedDataOutputStream out) throws IOException {
+        // keep order!
+        header.write(out);
+        requiresStruct.write(out);
+        exportsMapStruct.write(out);
+        opensMapStruct.write(out);
+        usesStruct.write(out);
+        providesMapStruct.write(out);
+      }
+    };
+
+    HeaderStruct header ;
+    SetStruct<Module.Dependence>  requiresStruct;
+    MapStruct<Module.Exported>    exportsMapStruct;
+    MapStruct<Module.Opened>      opensMapStruct;
+    SetStruct<Module.Uses>        usesStruct;
+    MapStruct<Module.Provided>    providesMapStruct;
+  }
+
+  /**
+   * u2 {exports|opens}_count;
+   * {  u2 {exports|opens}_index;
+   * u2 {exports|opens}_flags;
+   * u2 {exports|opens}_to_count;
+   * u2 {exports|opens}_to_index[{exports|opens}_to_count];
+   * } {exports|opens}[{exports|opens}_count];
+   * or
+   * u2 provides_count;
+   * {  u2 provides_index;
+   * u2 provides_with_count;
+   * u2 provides_with_index[provides_with_count];
+   * } provides[provides_count];
+   */
+  private class MapStruct<T extends Module.TargetType> implements Data {
+    final List<Triplet<ConstantPool.ConstCell, Integer, List<ConstantPool.ConstCell>>> exportsOpensList = new ArrayList<>();
+    final List<Pair<ConstantPool.ConstCell, List<ConstantPool.ConstCell>>> providesList = new ArrayList<>();
+
+    MapStruct(Map<T, Set<String>> source,
+              Function<String,ConstantPool.ConstCell> nameFinder,
+              Function<String,ConstantPool.ConstCell> targetFinder) {
+      Objects.requireNonNull(source);
+      source.entrySet().stream()
+          .sorted(Map.Entry.comparingByKey())
+          .forEach(e -> {
+                ArrayList<ConstantPool.ConstCell> to = new ArrayList<>();
+                e.getValue().forEach(mn -> to.add(targetFinder.apply(mn)));
+                if (e.getKey().isFlagged()) {
+                  exportsOpensList.add(new Triplet<>
+                      ( nameFinder.apply(e.getKey().getTypeName()),
+                        ((Module.FlaggedTargetType) e.getKey()).getFlags(),
+                        to));
+                } else {
+                  providesList.add(new Pair<>(nameFinder.apply(e.getKey().getTypeName()),
+                      to));
+                }
+              }
+          );
+    }
+
+    @Override
+    public void write(CheckedDataOutputStream out) throws IOException {
+      if (providesList.isEmpty()) {
+        out.writeShort(exportsOpensList.size());          // u2 {exports|opens}_count;
+        for (Triplet<ConstantPool.ConstCell, Integer, List<ConstantPool.ConstCell>> triplet : exportsOpensList) {
+          out.writeShort(triplet.first.arg);              // {  u2 {exports|opens}_index;
+          out.writeShort(triplet.second);                 //    u2 {exports|opens}_flags;
+          out.writeShort(triplet.third.size());           //    u2 {exports|opens}_to_count;
+          for (ConstantPool.ConstCell to : triplet.third)
+            out.writeShort(to.arg);                       // u2 {exports|opens}_to_index[{exports|opens}_to_count]; }
+        }
+      } else {
+        out.writeShort(providesList.size());              // u2 provides_count;
+        for (Pair<ConstantPool.ConstCell, List<ConstantPool.ConstCell>> pair : providesList) {
+          out.writeShort(pair.first.arg);                 // {  u2 provides_index;
+          out.writeShort(pair.second.size());             //    u2 provides_with_count;
+          for (ConstantPool.ConstCell to : pair.second)
+            out.writeShort(to.arg);                       // u2 provides_with_index[provides_with_count]; }
+        }
+      }
+    }
+
+    @Override
+    public int getLength() {
+      if (providesList.isEmpty()) {
+        // (u2:{exports|opens}_count) + (u2:{exports|opens}_index + u2:{exports|opens}_flags u2:{exports|opens}_to_count) * {exports|opens}_count +
+        return 2 + 6 * exportsOpensList.size() +
+        //  (u2:{exports|opens}_to_index) * {exports|opens}_to_count
+            exportsOpensList.stream().mapToInt(p -> p.third.size()).filter(s -> s > 0).sum() * 2;
+      } else {
+        // (u2 : provides_count) + (u2:provides_index + u2:provides_with_count) * provides_count +
+        return 2 + 4 * providesList.size() +
+        // (u2:provides_with_index) * provides_with_count
+            providesList.stream().mapToInt(p -> p.second.size()).filter(s -> s > 0).sum() * 2;
+      }
+    }
+  }
+
+  private class HeaderStruct implements Data {
+    final ConstantPool.ConstCell index;
+    final int flags;
+    final ConstantPool.ConstCell versionIndex;
+
+    HeaderStruct(Module.Header source,
+                 Function<String,ConstantPool.ConstCell> nameFinder,
+                 Function<String,ConstantPool.ConstCell> versionFinder) {
+      index = nameFinder.apply(source.getModuleName());
+      versionIndex = (source.getModuleVersion() == null ) ? null : versionFinder.apply(source.getModuleVersion());
+      flags = source.getModuleFlags();
+    }
+
+    @Override
+    public void write(CheckedDataOutputStream out) throws IOException {
+      out.writeShort(index.arg);                                    // u2 module_name_index;
+      out.writeShort(flags);                                        // u2 module_flags;
+      out.writeShort(versionIndex == null ? 0 : versionIndex.arg);  // u2 module_version_index;
+    }
+
+    @Override
+    public int getLength() {
+      // u2:module_name_index) +  u2:module_flags +u2:module_version_index
+      return 6;
+    }
+  }
+
+  /**
+   * u2 uses_count;
+   * u2 uses_index[uses_count];
+   * or
+   * u2 requires_count;
+   * {  u2 requires_index;
+   *    u2 requires_flags;
+   *    u2 requires_version_index;
+   * } requires[requires_count];
+   */
+  private class SetStruct<T extends Module.TargetType> implements Data {
+    final List<ConstantPool.ConstCell> usesList = new ArrayList<>();
+    final List<Triplet<ConstantPool.ConstCell, Integer, ConstantPool.ConstCell>> requiresList = new ArrayList<>();
+
+    SetStruct(Set<T> source,
+              Function<String,ConstantPool.ConstCell> nameFinder,
+              Function<String,ConstantPool.ConstCell> versionFinder) {
+      Objects.requireNonNull(source);
+      source.forEach(e -> {
+        if (e.isFlagged()) {
+          requiresList.add(new Triplet<>(
+              nameFinder.apply(e.getTypeName()),
+              ((Module.FlaggedTargetType) e).getFlags(),
+              (((Module.VersionedFlaggedTargetType) e).getVersion() == null) ?
+                  null :
+                  versionFinder.apply(((Module.VersionedFlaggedTargetType) e).getVersion())));
+        } else {
+          usesList.add(nameFinder.apply((e.getTypeName())));
+        }
+      });
+    }
+
+    @Override
+    public void write(CheckedDataOutputStream out) throws IOException {
+      if (usesList.isEmpty()) {
+        out.writeShort(requiresList.size());                  // u2 requires_count;
+        for (Triplet<ConstantPool.ConstCell, Integer, ConstantPool.ConstCell> r : requiresList) {
+          out.writeShort(r.first.arg);                        // u2 requires_index;
+          out.writeShort(r.second);                           // u2 requires_flags;
+          out.writeShort(r.third == null ? 0 : r.third.arg);  // u2 requires_version_index;
+        }
+      } else {
+        out.writeShort(usesList.size());                      // u2 uses_count;
+        for (ConstantPool.ConstCell u : usesList)
+          out.writeShort(u.arg);                              // u2 uses_index[uses_count];
+      }
+    }
+
+    @Override
+    public int getLength() {
+      return usesList.isEmpty() ?
+          // (u2:requires_count) + (u2:requires_index + u2:requires_flags + u2:requires_version_index) * requires_count
+          2 + 6 * requiresList.size() :
+          // (u2:uses_count) + (u2:uses_index) * uses_count
+          2 + 2 * usesList.size();
+    }
+  }
+
+  // Helper classes
+  private class Pair<F, S> {
+    final F first;
+    final S second;
+
+    Pair(F first, S second) {
+      this.first = first;
+      this.second = second;
+    }
+  }
+
+  public class Triplet<F, S, T>  extends Pair<F,S> {
+    private final T third;
+    Triplet(F first, S second, T third) {
+      super(first,second);
+      this.third = third;
+    }
+  }
+
+}
diff a/test/lib/org/openjdk/asmtools/jasm/NestMembersAttr.java b/test/lib/org/openjdk/asmtools/jasm/NestMembersAttr.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/NestMembersAttr.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018, 2020, 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 org.openjdk.asmtools.jasm;
+
+import java.util.List;
+
+/**
+ * The "classes[]" data of attributes
+ * JEP 181 (Nest-based Access Control): class file 55.0
+ * NestMembers_attribute {
+ * u2 attribute_name_index;
+ * u4 attribute_length;
+ * u2 number_of_classes;
+ * u2 classes[number_of_classes];
+ * }
+ */
+public class NestMembersAttr extends ClassArrayAttr {
+    public NestMembersAttr(ClassData cdata, List<ConstantPool.ConstCell> classes) {
+        super(Tables.AttrTag.ATT_NestMembers.parsekey(), cdata, classes);
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/OpcodeTables.java b/test/lib/org/openjdk/asmtools/jasm/OpcodeTables.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/OpcodeTables.java
@@ -0,0 +1,569 @@
+/*
+ * Copyright (c) 1996, 2014, 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 org.openjdk.asmtools.jasm;
+
+import java.util.HashMap;
+
+/**
+ *
+ * OpcodeTables
+ *
+ * The OpcodeTables class follows a Singleton Pattern. This class contains Enums, that are
+ * contained in private hash maps (lookup tables and reverse lookup tables). These hash
+ * maps all have public accessors, which clients use to look-up opcodes.
+ *
+ * Tokens in this table carry no external state, and are typically treated as constants.
+ * They do not need to be reset.
+ *
+ */
+public class OpcodeTables {
+
+    /**
+     * Initialized keyword and token Hash Maps (and Reverse Tables)
+     */
+    static private final int MaxOpcodes = 301;
+    static private HashMap<Integer, Opcode> IntToNormalOpcodes = new HashMap<>(MaxOpcodes);
+    static private HashMap<Integer, Opcode> IntToAllOpcodes = new HashMap<>(MaxOpcodes);
+    static private HashMap<String, Opcode> mnemocodes = new HashMap<>(MaxOpcodes);
+
+    static private HashMap<Integer, Opcode> IntToPrivOpcode = new HashMap<>(MaxOpcodes);
+    static private HashMap<String, Opcode> PrivMnemocodes = new HashMap<>(MaxOpcodes);
+
+    static private HashMap<Integer, Opcode> IntToNonPrivOpcode = new HashMap<>(MaxOpcodes);
+    static private HashMap<String, Opcode> NonPrivMnemocodes = new HashMap<>(MaxOpcodes);
+
+    static {
+        // register all of the tokens
+        for (Opcode opc : Opcode.values()) {
+            registerOpcode(opc);
+        }
+
+    }
+
+    private static void registerOpcode(Opcode opc) {
+        IntToAllOpcodes.put(opc.value, opc);
+        mnemocodes.put(opc.parsekey, opc);
+        if (opc.alias != null) {
+            mnemocodes.put(opc.alias, opc);
+        }
+
+        if (opc.type == OpcodeType.PRIVELEGED) {
+            PrivMnemocodes.put(opc.parsekey, opc);
+            IntToPrivOpcode.put(opc.baseVal, opc);
+        } else if (opc.type == OpcodeType.NONPRIVELEGED) {
+            NonPrivMnemocodes.put(opc.parsekey, opc);
+            IntToNonPrivOpcode.put(opc.baseVal, opc);
+        }
+
+    }
+
+    public static Opcode opcode(String mnemonic) {
+        return mnemocodes.get(mnemonic);
+    }
+
+    public static Opcode opcode(Integer mnem_code) {
+        return IntToAllOpcodes.get(mnem_code);
+    }
+
+    /*-------------------------------------------------------- */
+    /**
+     * Marker: describes the type of Opcode.
+     *
+     * certain types of Opcodes will be added to specific lookup tables.
+     */
+    static public enum OpcodeType {
+        NORMAL            (0, "Normal"),
+        NONPRIVELEGED     (1, "NonPriv"),
+        PRIVELEGED        (2, "Priv"),
+        WIDE              (3, "Wide");
+
+        private final Integer value;
+        private final String printval;
+
+        OpcodeType(Integer val, String print) {
+            value = val;
+            printval = print;
+        }
+
+        public String printval() {
+            return printval;
+        }
+
+    }
+
+    /*-------------------------------------------------------- */
+    /* Opcode Enums */
+    static public enum Opcode {
+     /* Opcodes */
+    opc_dead                (-2, " opc_dead", 0),
+    opc_label               (-1, "opc_label", 0),
+    opc_nop                 (0, "nop", 1),
+    opc_aconst_null         (1, "aconst_null", 1),
+    opc_iconst_m1           (2, "iconst_m1", 1),
+    opc_iconst_0            (3, "iconst_0", 1),
+    opc_iconst_1            (4, "iconst_1", 1),
+    opc_iconst_2            (5, "iconst_2", 1),
+    opc_iconst_3            (6, "iconst_3", 1),
+    opc_iconst_4            (7, "iconst_4", 1),
+    opc_iconst_5            (8, "iconst_5", 1),
+    opc_lconst_0            (9, "lconst_0", 1),
+    opc_lconst_1            (10, "lconst_1", 1),
+    opc_fconst_0            (11, "fconst_0", 1),
+    opc_fconst_1            (12, "fconst_1", 1),
+    opc_fconst_2            (13, "fconst_2", 1),
+    opc_dconst_0            (14, "dconst_0", 1),
+    opc_dconst_1            (15, "dconst_1", 1),
+    opc_bipush              (16, "bipush", 2),
+    opc_sipush              (17, "sipush", 3),
+    opc_ldc                 (18, "ldc", 2),
+    opc_ldc_w               (19, "ldc_w", 3),
+    opc_ldc2_w              (20, "ldc2_w", 3),
+    opc_iload               (21, "iload", 2),
+    opc_lload               (22, "lload", 2),
+    opc_fload               (23, "fload", 2),
+    opc_dload               (24, "dload", 2),
+    opc_aload               (25, "aload", 2),
+    opc_iload_0            (26, "iload_0", 1),
+    opc_iload_1            (27, "iload_1", 1),
+    opc_iload_2            (28, "iload_2", 1),
+    opc_iload_3            (29, "iload_3", 1),
+    opc_lload_0            (30, "lload_0", 1),
+    opc_lload_1            (31, "lload_1", 1),
+    opc_lload_2            (32, "lload_2", 1),
+    opc_lload_3            (33, "lload_3", 1),
+    opc_fload_0            (34, "fload_0", 1),
+    opc_fload_1            (35, "fload_1", 1),
+    opc_fload_2            (36, "fload_2", 1),
+    opc_fload_3            (37, "fload_3", 1),
+    opc_dload_0            (38, "dload_0", 1),
+    opc_dload_1            (39, "dload_1", 1),
+    opc_dload_2            (40, "dload_2", 1),
+    opc_dload_3            (41, "dload_3", 1),
+    opc_aload_0            (42, "aload_0", 1),
+    opc_aload_1            (43, "aload_1", 1),
+    opc_aload_2            (44, "aload_2", 1),
+    opc_aload_3            (45, "aload_3", 1),
+    opc_iaload            (46, "iaload", 1),
+    opc_laload            (47, "laload", 1),
+    opc_faload            (48, "faload", 1),
+    opc_daload            (49, "daload", 1),
+    opc_aaload            (50, "aaload", 1),
+    opc_baload            (51, "baload", 1),
+    opc_caload            (52, "caload", 1),
+    opc_saload            (53, "saload", 1),
+    opc_istore            (54, "istore", 2),
+    opc_lstore            (55, "lstore", 2),
+    opc_fstore            (56, "fstore", 2),
+    opc_dstore            (57, "dstore", 2),
+    opc_astore            (58, "astore", 2),
+    opc_istore_0            (59, "istore_0", 1),
+    opc_istore_1            (60, "istore_1", 1),
+    opc_istore_2            (61, "istore_2", 1),
+    opc_istore_3            (62, "istore_3", 1),
+    opc_lstore_0            (63, "lstore_0", 1),
+    opc_lstore_1            (64, "lstore_1", 1),
+    opc_lstore_2            (65, "lstore_2", 1),
+    opc_lstore_3            (66, "lstore_3", 1),
+    opc_fstore_0            (67, "fstore_0", 1),
+    opc_fstore_1            (68, "fstore_1", 1),
+    opc_fstore_2            (69, "fstore_2", 1),
+    opc_fstore_3            (70, "fstore_3", 1),
+    opc_dstore_0            (71, "dstore_0", 1),
+    opc_dstore_1            (72, "dstore_1", 1),
+    opc_dstore_2            (73, "dstore_2", 1),
+    opc_dstore_3            (74, "dstore_3", 1),
+    opc_astore_0            (75, "astore_0", 1),
+    opc_astore_1            (76, "astore_1", 1),
+    opc_astore_2            (77, "astore_2", 1),
+    opc_astore_3            (78, "astore_3", 1),
+    opc_iastore             (79, "iastore", 1),
+    opc_lastore             (80, "lastore", 1),
+    opc_fastore             (81, "fastore", 1),
+    opc_dastore             (82, "dastore", 1),
+    opc_aastore             (83, "aastore", 1),
+    opc_bastore             (84, "bastore", 1),
+    opc_castore             (85, "castore", 1),
+    opc_sastore             (86, "sastore", 1),
+    opc_pop                 (87, "pop", 1),
+    opc_pop2                (88, "pop2", 1),
+    opc_dup                 (89, "dup", 1),
+    opc_dup_x1              (90, "dup_x1", 1),
+    opc_dup_x2              (91, "dup_x2", 1),
+    opc_dup2                (92, "dup2", 1),
+    opc_dup2_x1             (93, "dup2_x1", 1),
+    opc_dup2_x2             (94, "dup2_x2", 1),
+    opc_swap                (95, "swap", 1),
+    opc_iadd                (96, "iadd", 1),
+    opc_ladd                (97, "ladd", 1),
+    opc_fadd                (98, "fadd", 1),
+    opc_dadd                (99, "dadd", 1),
+    opc_isub                (100, "isub", 1),
+    opc_lsub                (101, "lsub", 1),
+    opc_fsub                (102, "fsub", 1),
+    opc_dsub                (103, "dsub", 1),
+    opc_imul                (104, "imul", 1),
+    opc_lmul                (105, "lmul", 1),
+    opc_fmul                (106, "fmul", 1),
+    opc_dmul                (107, "dmul", 1),
+    opc_idiv                (108, "idiv", 1),
+    opc_ldiv                (109, "ldiv", 1),
+    opc_fdiv                (110, "fdiv", 1),
+    opc_ddiv                (111, "ddiv", 1),
+    opc_irem                (112, "irem", 1),
+    opc_lrem                (113, "lrem", 1),
+    opc_frem                (114, "frem", 1),
+    opc_drem                (115, "drem", 1),
+    opc_ineg                (116, "ineg", 1),
+    opc_lneg                (117, "lneg", 1),
+    opc_fneg                (118, "fneg", 1),
+    opc_dneg                (119, "dneg", 1),
+    opc_ishl                (120, "ishl", 1),
+    opc_lshl                (121, "lshl", 1),
+    opc_ishr                (122, "ishr", 1),
+    opc_lshr                (123, "lshr", 1),
+    opc_iushr               (124, "iushr", 1),
+    opc_lushr               (125, "lushr", 1),
+    opc_iand                (126, "iand", 1),
+    opc_land                (127, "land", 1),
+    opc_ior                 (128, "ior", 1),
+    opc_lor                 (129, "lor", 1),
+    opc_ixor                (130, "ixor", 1),
+    opc_lxor                (131, "lxor", 1),
+    opc_iinc                (132, "iinc", 3),
+    opc_i2l                 (133, "i2l", 1),
+    opc_i2f                 (134, "i2f", 1),
+    opc_i2d                 (135, "i2d", 1),
+    opc_l2i                 (136, "l2i", 1),
+    opc_l2f                 (137, "l2f", 1),
+    opc_l2d                 (138, "l2d", 1),
+    opc_f2i                 (139, "f2i", 1),
+    opc_f2l                 (140, "f2l", 1),
+    opc_f2d                 (141, "f2d", 1),
+    opc_d2i                 (142, "d2i", 1),
+    opc_d2l                 (143, "d2l", 1),
+    opc_d2f                 (144, "d2f", 1),
+    opc_i2b                 (145, "i2b", 1),
+    opc_i2c                 (146, "i2c", 1),
+    opc_i2s                 (147, "i2s", 1),
+    opc_lcmp                (148, "lcmp", 1),
+    opc_fcmpl               (149, "fcmpl", 1),
+    opc_fcmpg               (150, "fcmpg", 1),
+    opc_dcmpl               (151, "dcmpl", 1),
+    opc_dcmpg               (152, "dcmpg", 1),
+    opc_ifeq                (153, "ifeq", 3),
+    opc_ifne                (154, "ifne", 3),
+    opc_iflt                (155, "iflt", 3),
+    opc_ifge                (156, "ifge", 3),
+    opc_ifgt                (157, "ifgt", 3),
+    opc_ifle                (158, "ifle", 3),
+    opc_if_icmpeq           (159, "if_icmpeq", 3),
+    opc_if_icmpne           (160, "if_icmpne", 3),
+    opc_if_icmplt           (161, "if_icmplt", 3),
+    opc_if_icmpge           (162, "if_icmpge", 3),
+    opc_if_icmpgt           (163, "if_icmpgt", 3),
+    opc_if_icmple           (164, "if_icmple", 3),
+    opc_if_acmpeq           (165, "if_acmpeq", 3),
+    opc_if_acmpne           (166, "if_acmpne", 3),
+    opc_goto                (167, "goto", 3),
+    opc_jsr                 (168, "jsr", 3),
+    opc_ret                 (169, "ret", 2),
+    opc_tableswitch         (170, "tableswitch", 99),
+    opc_lookupswitch        (171, "lookupswitch", 99),
+    opc_ireturn             (172, "ireturn", 1),
+    opc_lreturn             (173, "lreturn", 1),
+    opc_freturn             (174, "freturn", 1),
+    opc_dreturn             (175, "dreturn", 1),
+    opc_areturn             (176, "areturn", 1),
+    opc_return              (177, "return", 1),
+    opc_getstatic           (178, "getstatic", 3),
+    opc_putstatic           (179, "putstatic", 3),
+    opc_getfield            (180, "getfield", 3),
+    opc_putfield            (181, "putfield", 3),
+    opc_invokevirtual       (182, "invokevirtual", 3),
+    opc_invokespecial       (183, "invokespecial", "invokenonvirtual", 3),
+    opc_invokestatic        (184, "invokestatic", 3),
+    opc_invokeinterface     (185, "invokeinterface", 5),
+    opc_invokedynamic       (186, "invokedynamic", 5),
+    opc_new                 (187, "new", 3),
+    opc_newarray            (188, "newarray", 2),
+    opc_anewarray           (189, "anewarray", 3),
+    opc_arraylength         (190, "arraylength", 1),
+    opc_athrow              (191, "athrow", 1),
+    opc_checkcast           (192, "checkcast", 3),
+    opc_instanceof          (193, "instanceof", 3),
+    opc_monitorenter        (194, "monitorenter", 1),
+    opc_monitorexit         (195, "monitorexit", 1),
+
+        // Wide Marker (not really an opcode)
+        opc_wide            (196, null, 0),
+    opc_multianewarray      (197, "multianewarray", 4),
+    opc_ifnull              (198, "ifnull", 3),
+    opc_ifnonnull           (199, "ifnonnull", 3),
+    opc_goto_w              (200, "goto_w", 5),
+    opc_jsr_w               (201, "jsr_w", 5),
+    opc_aconst_init         (203, "aconst_init", 3),
+    opc_withfield           (204, "withfield", 3),
+
+        /* Pseudo-instructions */
+    opc_bytecode            (210, "bytecode", 1),
+    opc_try                 (211, "try", 0),
+    opc_endtry              (212, "endtry", 0),
+    opc_catch               (213, "catch", 0),
+    opc_var                 (214, "var", 0),
+    opc_endvar              (215, "endvar", 0),
+    opc_locals_map          (216, "locals_map", 0),
+    opc_stack_map           (217, "stack_map", 0),
+    opc_stack_frame_type    (218, "stack_frame_type", 0),
+
+
+        // Priv/NonPriv Marker (not really an opcode)
+        opc_nonpriv         (254, "priv", 0),
+        opc_priv            (255, "nonpriv", 0),
+
+
+        /* Wide instructions */
+        opc_iload_w                     (opc_iload.value, "iload_w", 4, OpcodeType.WIDE),
+        opc_lload_w                     (opc_lload.value, "lload_w", 4, OpcodeType.WIDE),
+        opc_fload_w                     (opc_fload.value, "fload_w", 4, OpcodeType.WIDE),
+        opc_dload_w                     (opc_dload.value, "dload_w", 4, OpcodeType.WIDE),
+        opc_aload_w                     (opc_aload.value, "aload_w", 4, OpcodeType.WIDE),
+        opc_istore_w                    (opc_istore.value, "istore_w", 4, OpcodeType.WIDE),
+        opc_lstore_w                    (opc_lstore.value, "lstore_w", 4, OpcodeType.WIDE),
+        opc_fstore_w                    (opc_fstore.value, "fstore_w", 4, OpcodeType.WIDE),
+        opc_dstore_w                    (opc_dstore.value, "dstore_w", 4, OpcodeType.WIDE),
+        opc_astore_w                    (opc_astore.value, "astore_w", 4, OpcodeType.WIDE),
+        opc_ret_w                       (opc_ret.value, "ret_w", 4, OpcodeType.WIDE),
+        opc_iinc_w                      (opc_iinc.value, "iinc_w", 6, OpcodeType.WIDE),
+
+
+        /* Priveleged instructions */
+    opc_load_ubyte                  (0, "load_ubyte", OpcodeType.NONPRIVELEGED),
+    opc_priv_load_ubyte        (0, "priv_load_ubyte", OpcodeType.PRIVELEGED),
+    opc_load_byte            (1, "load_byte", OpcodeType.NONPRIVELEGED),
+    opc_priv_load_byte        (1, "priv_load_byte", OpcodeType.PRIVELEGED),
+    opc_load_char            (2, "load_char", OpcodeType.NONPRIVELEGED),
+    opc_priv_load_char        (2, "priv_load_char", OpcodeType.PRIVELEGED),
+    opc_load_short            (3, "load_short", OpcodeType.NONPRIVELEGED),
+    opc_priv_load_short        (3, "priv_load_short", OpcodeType.PRIVELEGED),
+    opc_load_word            (4, "load_word", OpcodeType.NONPRIVELEGED),
+    opc_priv_load_word        (4, "priv_load_word", OpcodeType.PRIVELEGED),
+    opc_load_char_oe            (10, "load_char_oe", OpcodeType.NONPRIVELEGED),
+    opc_priv_load_char_oe        (10, "priv_load_char_oe", OpcodeType.PRIVELEGED),
+    opc_load_short_oe        (11, "load_short_oe", OpcodeType.NONPRIVELEGED),
+    opc_priv_load_short_oe        (11, "priv_load_short_oe", OpcodeType.PRIVELEGED),
+    opc_load_word_oe            (12, "load_word_oe", OpcodeType.NONPRIVELEGED),
+    opc_priv_load_word_oe        (12, "priv_load_word_oe", OpcodeType.PRIVELEGED),
+    opc_ncload_ubyte            (16, "ncload_ubyte", OpcodeType.NONPRIVELEGED),
+    opc_priv_ncload_ubyte        (16, "priv_ncload_ubyte", OpcodeType.PRIVELEGED),
+    opc_ncload_byte            (17, "ncload_byte", OpcodeType.NONPRIVELEGED),
+    opc_priv_ncload_byte        (17, "priv_ncload_byte", OpcodeType.PRIVELEGED),
+    opc_ncload_char            (18, "ncload_char", OpcodeType.NONPRIVELEGED),
+    opc_priv_ncload_char        (18, "priv_ncload_char", OpcodeType.PRIVELEGED),
+    opc_ncload_short            (19, "ncload_short", OpcodeType.NONPRIVELEGED),
+    opc_priv_ncload_short        (19, "priv_ncload_short", OpcodeType.PRIVELEGED),
+    opc_ncload_word            (20, "ncload_word", OpcodeType.NONPRIVELEGED),
+    opc_priv_ncload_word        (20, "priv_ncload_word", OpcodeType.PRIVELEGED),
+    opc_ncload_char_oe        (26, "ncload_char_oe", OpcodeType.NONPRIVELEGED),
+    opc_priv_ncload_char_oe        (26, "priv_ncload_char_oe", OpcodeType.PRIVELEGED),
+    opc_ncload_short_oe        (27, "ncload_short_oe", OpcodeType.NONPRIVELEGED),
+    opc_priv_ncload_short_oe        (27, "priv_ncload_short_oe", OpcodeType.PRIVELEGED),
+    opc_ncload_word_oe        (28, "ncload_word_oe", OpcodeType.NONPRIVELEGED),
+    opc_priv_ncload_word_oe        (28, "priv_ncload_word_oe", OpcodeType.PRIVELEGED),
+    opc_cache_flush            (30, "cache_flush", OpcodeType.NONPRIVELEGED),
+    opc_priv_cache_flush        (30, "priv_cache_flush", OpcodeType.PRIVELEGED),
+    opc_store_byte            (32, "store_byte", OpcodeType.NONPRIVELEGED),
+    opc_priv_store_byte        (32, "priv_store_byte", OpcodeType.PRIVELEGED),
+    opc_store_short            (34, "store_short", OpcodeType.NONPRIVELEGED),
+    opc_priv_store_short        (34, "priv_store_short", OpcodeType.PRIVELEGED),
+    opc_store_word            (36, "store_word", OpcodeType.NONPRIVELEGED),
+    opc_priv_store_word        (36, "priv_store_word", OpcodeType.PRIVELEGED),
+    opc_store_short_oe        (42, "store_short_oe", OpcodeType.NONPRIVELEGED),
+    opc_priv_store_short_oe        (42, "priv_store_short_oe", OpcodeType.PRIVELEGED),
+    opc_store_word_oe        (44, "store_word_oe", OpcodeType.NONPRIVELEGED),
+    opc_priv_store_word_oe        (44, "priv_store_word_oe", OpcodeType.PRIVELEGED),
+    opc_ncstore_byte            (48, "ncstore_byte", OpcodeType.NONPRIVELEGED),
+    opc_priv_ncstore_byte        (48, "priv_ncstore_byte", OpcodeType.PRIVELEGED),
+    opc_ncstore_short        (50, "ncstore_short", OpcodeType.NONPRIVELEGED),
+    opc_priv_ncstore_short        (50, "priv_ncstore_short", OpcodeType.PRIVELEGED),
+    opc_ncstore_word        (52, "ncstore_word", OpcodeType.NONPRIVELEGED),
+    opc_priv_ncstore_word        (52, "priv_ncstore_word", OpcodeType.PRIVELEGED),
+    opc_ncstore_short_oe        (58, "ncstore_short_oe", OpcodeType.NONPRIVELEGED),
+    opc_priv_ncstore_short_oe    (58, "priv_ncstore_short_oe", OpcodeType.PRIVELEGED),
+    opc_ncstore_word_oe        (60, "ncstore_word_oe", OpcodeType.NONPRIVELEGED),
+    opc_priv_ncstore_word_oe        (60, "priv_ncstore_word_oe", OpcodeType.PRIVELEGED),
+    opc_zero_line            (62, "zero_line", OpcodeType.NONPRIVELEGED),
+    opc_priv_zero_line        (62, "priv_zero_line", OpcodeType.PRIVELEGED),
+    opc_ret_from_sub            (5, "ret_from_sub", OpcodeType.NONPRIVELEGED),
+    opc_enter_sync_method        (63, "enter_sync_method", OpcodeType.NONPRIVELEGED),
+    opc_priv_ret_from_trap        (5, "priv_ret_from_trap", OpcodeType.PRIVELEGED),
+    opc_priv_read_dcache_tag    (6, "priv_read_dcache_tag", OpcodeType.PRIVELEGED),
+    opc_priv_read_dcache_data    (7, "priv_read_dcache_data", OpcodeType.PRIVELEGED),
+    opc_priv_read_icache_tag    (14, "priv_read_icache_tag", OpcodeType.PRIVELEGED),
+    opc_priv_read_icache_data    (15, "priv_read_icache_data", OpcodeType.PRIVELEGED),
+    opc_priv_powerdown        (22, "priv_powerdown", OpcodeType.PRIVELEGED),
+    opc_priv_read_scache_data    (23, "priv_read_scache_data", OpcodeType.PRIVELEGED),
+    opc_priv_cache_index_flush    (31, "priv_cache_index_flush", OpcodeType.PRIVELEGED),
+    opc_priv_write_dcache_tag    (38, "priv_write_dcache_tag", OpcodeType.PRIVELEGED),
+    opc_priv_write_dcache_data    (39, "priv_write_dcache_data", OpcodeType.PRIVELEGED),
+    opc_priv_write_icache_tag    (46, "priv_write_icache_tag", OpcodeType.PRIVELEGED),
+    opc_priv_write_icache_data    (47, "priv_write_icache_data", OpcodeType.PRIVELEGED),
+    opc_priv_reset            (54, "priv_reset", OpcodeType.PRIVELEGED),
+    opc_priv_write_scache_data    (55, "priv_write_scache_data", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_0        (64, "priv_read_reg_0", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_1        (65, "priv_read_reg_1", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_2        (66, "priv_read_reg_2", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_3        (67, "priv_read_reg_3", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_4        (68, "priv_read_reg_4", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_5        (69, "priv_read_reg_5", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_6        (70, "priv_read_reg_6", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_7        (71, "priv_read_reg_7", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_8        (72, "priv_read_reg_8", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_9        (73, "priv_read_reg_9", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_10        (74, "priv_read_reg_10", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_11        (75, "priv_read_reg_11", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_12        (76, "priv_read_reg_12", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_13        (77, "priv_read_reg_13", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_14        (78, "priv_read_reg_14", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_15        (79, "priv_read_reg_15", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_16        (80, "priv_read_reg_16", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_17        (81, "priv_read_reg_17", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_18        (82, "priv_read_reg_18", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_19        (83, "priv_read_reg_19", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_20        (84, "priv_read_reg_20", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_21        (85, "priv_read_reg_21", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_22        (86, "priv_read_reg_22", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_23        (87, "priv_read_reg_23", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_24        (88, "priv_read_reg_24", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_25        (89, "priv_read_reg_25", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_26        (90, "priv_read_reg_26", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_27        (91, "priv_read_reg_27", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_28        (92, "priv_read_reg_28", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_29        (93, "priv_read_reg_29", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_30        (94, "priv_read_reg_30", OpcodeType.PRIVELEGED),
+    opc_priv_read_reg_31        (95, "priv_read_reg_31", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_0        (96, "priv_write_reg_0", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_1        (97, "priv_write_reg_1", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_2        (98, "priv_write_reg_2", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_3        (99, "priv_write_reg_3", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_4        (100, "priv_write_reg_4", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_5        (101, "priv_write_reg_5", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_6        (102, "priv_write_reg_6", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_7        (103, "priv_write_reg_7", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_8        (104, "priv_write_reg_8", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_9        (105, "priv_write_reg_9", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_10        (106, "priv_write_reg_10", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_11        (107, "priv_write_reg_11", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_12        (108, "priv_write_reg_12", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_13        (109, "priv_write_reg_13", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_14        (110, "priv_write_reg_14", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_15        (111, "priv_write_reg_15", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_16        (112, "priv_write_reg_16", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_17        (113, "priv_write_reg_17", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_18        (114, "priv_write_reg_18", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_19        (115, "priv_write_reg_19", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_20        (116, "priv_write_reg_20", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_21        (117, "priv_write_reg_21", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_22        (118, "priv_write_reg_22", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_23        (119, "priv_write_reg_23", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_24        (120, "priv_write_reg_24", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_25        (121, "priv_write_reg_25", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_26        (122, "priv_write_reg_26", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_27        (123, "priv_write_reg_27", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_28        (124, "priv_write_reg_28", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_29        (125, "priv_write_reg_29", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_30        (126, "priv_write_reg_30", OpcodeType.PRIVELEGED),
+    opc_priv_write_reg_31        (127, "priv_write_reg_31", OpcodeType.PRIVELEGED);
+
+        private Integer value;
+        private String parsekey;
+        private String alias;
+        private Integer length;
+        private Integer baseVal;
+        private OpcodeType type;
+
+        Opcode(Integer val, String parse, OpcodeType tp) {
+            init(val, parse, null, 2, tp);
+        }
+
+        Opcode(Integer val, String parse, int len, OpcodeType tp) {
+            init(val, parse, null, len, tp);
+        }
+
+        Opcode(Integer val, String parse) {
+            init(val, parse, null, 2, OpcodeType.NORMAL);
+        }
+
+        Opcode(Integer val, String parse, int len) {
+            init(val, parse, null, len, OpcodeType.NORMAL);
+        }
+
+        Opcode(Integer val, String parse, String als, int len) {
+            init(val, parse, als, len, OpcodeType.NORMAL);
+        }
+
+        Opcode(Integer val, String parse, String als, int len, OpcodeType tp) {
+            init(val, parse, als, len, tp);
+        }
+
+        private void init(Integer val, String parse, String als, int len, OpcodeType tp) {
+            type = tp;
+            baseVal = null;
+            switch (tp) {
+                case NORMAL:
+                    value = val;
+                    break;
+                case WIDE:
+                    value = (opc_wide.value << 8) | val;
+                    break;
+                case PRIVELEGED:
+                    value = (opc_priv.value * 0xFF) + val;
+                    baseVal = val;
+                    break;
+                case NONPRIVELEGED:
+                    value = (opc_nonpriv.value * 0xFF) + val;
+                    baseVal = val;
+                    break;
+            }
+            parsekey = parse;
+            alias = als;
+            length = len;
+        }
+
+        public Integer value() {
+            return value;
+        }
+
+        public int length() {
+            return length;
+        }
+
+        public String parsekey() {
+            return parsekey;
+        }
+
+        public OpcodeType type() {
+            return type;
+        }
+    }
+
+}
diff a/test/lib/org/openjdk/asmtools/jasm/ParseBase.java b/test/lib/org/openjdk/asmtools/jasm/ParseBase.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/ParseBase.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 1996, 2014, 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 org.openjdk.asmtools.jasm;
+
+/**
+ *
+ */
+public class ParseBase {
+
+    protected boolean debugFlag;
+    protected Scanner scanner;
+    protected Parser parser;
+    protected Environment env;
+
+    public ParseBase() {
+        init(null, null, null);
+    }
+
+    public void init(Scanner scnr, Parser prsr, Environment envr) {
+        debugFlag = false;
+        scanner = scnr;
+        parser = prsr;
+        env = envr;
+    }
+
+    public void enableDebug(boolean debState) {
+        debugFlag = debState;
+    }
+
+    protected void debugStr(String str) {
+        if (debugFlag) {
+            env.traceln(str);
+        }
+    }
+
+    protected void debugScan(String str) {
+        if (debugFlag) {
+            scanner.debugScan(str);
+        }
+    }
+
+}
diff a/test/lib/org/openjdk/asmtools/jasm/Parser.java b/test/lib/org/openjdk/asmtools/jasm/Parser.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/Parser.java
@@ -0,0 +1,2087 @@
+/*
+ * Copyright (c) 1996, 2021, 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 org.openjdk.asmtools.jasm;
+
+import org.openjdk.asmtools.common.Module;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+import static org.openjdk.asmtools.jasm.ConstantPool.*;
+import static org.openjdk.asmtools.jasm.JasmTokens.Token;
+import static org.openjdk.asmtools.jasm.JasmTokens.Token.*;
+import static org.openjdk.asmtools.jasm.RuntimeConstants.*;
+import static org.openjdk.asmtools.jasm.Tables.*;
+
+/**
+ * This class is used to parse Jasm statements and expressions.
+ * The result is a parse tree.<p>
+ * <p>
+ * This class implements an operator precedence parser. Errors are
+ * reported to the Environment object, if the error can't be
+ * resolved immediately, a SyntaxError exception is thrown.<p>
+ * <p>
+ * Error recovery is implemented by catching Scanner.SyntaxError exceptions
+ * and discarding input scanner.tokens until an input token is reached that
+ * is possibly a legal continuation.<p>
+ * <p>
+ * The parse tree that is constructed represents the input
+ * exactly (no rewrites to simpler forms). This is important
+ * if the resulting tree is to be used for code formatting in
+ * a programming environment. Currently only documentation comments
+ * are retained.<p>
+ * <p>
+ * A parser owns several components (scanner, constant-parser,
+ * instruction-parser, annotations-parser) to which it delegates certain
+ * parsing responsibilities.  This parser contains functions to parse the
+ * overall form of a class, and any members (fields, methods, inner-classes).
+ * <p>
+ * <p>
+ * Syntax errors, should always be caught inside the
+ * parser for error recovery.
+ */
+class Parser extends ParseBase {
+
+    /* Parser Fields */
+    protected ConstantPool pool = null;
+
+    ClassData cd = null;
+
+    CodeAttr curCode;
+
+    private ArrayList<ClassData> clsDataList = new ArrayList<>();
+    private String pkg = null;
+    private String pkgPrefix = "";
+    private ArrayList<AnnotationData> pkgAnnttns = null;
+    private ArrayList<AnnotationData> clsAnnttns = null;
+    private ArrayList<AnnotationData> memberAnnttns = null;
+    private boolean explicitcp = false;
+    private ModuleAttr moduleAttribute;
+    private CFVersion currentCFV;
+    /**
+     * other parser components
+     */
+    private ParserAnnotation annotParser;       // For parsing Annotations
+    private ParserCP cpParser;                  // for parsing Constants
+    private ParserInstr instrParser;            // for parsing Instructions
+
+
+    /**
+     * Create a parser
+     */
+    protected Parser(Environment sf, CFVersion cfVersion) throws IOException {
+        super.init(new Scanner(sf), this, sf);
+        this.currentCFV = cfVersion;
+        this.annotParser = new ParserAnnotation(scanner, this, env);
+        this.cpParser = new ParserCP(scanner, this, env);
+        this.instrParser = new ParserInstr(scanner, this, cpParser, env);
+    }
+
+    void setDebugFlags(boolean debugScanner, boolean debugMembers,
+                       boolean debugCP, boolean debugAnnot, boolean debugInstr) {
+
+        enableDebug(debugMembers);
+        scanner.enableDebug(debugScanner);
+        cpParser.enableDebug(debugCP);
+        annotParser.enableDebug(debugAnnot);
+        instrParser.enableDebug(debugInstr);
+    }
+
+    String encodeClassString(String classname) {
+        return "L" + classname + ";";
+    }
+
+
+    /*-------------------------------------------------------- */
+
+    /**
+     * Parses version in package statements
+     */
+
+    private void parseVersionPkg() throws IOException {
+        if (scanner.token == SEMICOLON) {
+            return;
+        }
+        parse_ver:
+        {
+            if (scanner.token != Token.VERSION) {
+                break parse_ver;
+            }
+            scanner.scan();
+            if (scanner.token != Token.INTVAL) {
+                break parse_ver;
+            }
+            currentCFV.setMajorVersion((short) scanner.intValue);
+            scanner.scan();
+            if (scanner.token != Token.COLON) {
+                break parse_ver;
+            }
+            scanner.scan();
+            if (scanner.token != Token.INTVAL) {
+                break parse_ver;
+            }
+            currentCFV.setMinorVersion((short) scanner.intValue);
+            scanner.scan();
+            debugScan("     [Parser.parseVersionPkg]: " + currentCFV.asString());
+            return;
+        }
+        env.error(scanner.pos, "version.expected");
+        throw new Scanner.SyntaxError();
+    }
+
+    private void parseVersion() throws IOException {
+        if (scanner.token == Token.LBRACE) {
+            return;
+        }
+        parse_ver:
+        {
+            if (scanner.token != Token.VERSION) {
+                break parse_ver;
+            }
+            scanner.scan();
+            if (scanner.token != Token.INTVAL) {
+                break parse_ver;
+            }
+            cd.cfv.setMajorVersion((short) scanner.intValue);
+            scanner.scan();
+            if (scanner.token != Token.COLON) {
+                break parse_ver;
+            }
+            scanner.scan();
+            if (scanner.token != Token.INTVAL) {
+                break parse_ver;
+            }
+            cd.cfv.setMinorVersion((short) scanner.intValue);
+            scanner.scan();
+            debugStr("parseVersion: " + cd.cfv.asString());
+            return;
+        }
+        env.error(scanner.pos, "version.expected");
+        throw new Scanner.SyntaxError();
+    }
+
+
+    /*---------------------------------------------*/
+
+    /**
+     * Parse an internal name: identifier.
+     */
+    String parseIdent() throws Scanner.SyntaxError, IOException {
+        String v = scanner.idValue;
+        scanner.expect(Token.IDENT);
+        return v;
+    }
+
+    /**
+     * Parse a local variable
+     */
+    void parseLocVarDef() throws Scanner.SyntaxError, IOException {
+        if (scanner.token == Token.INTVAL) {
+            int v = scanner.intValue;
+            scanner.scan();
+            curCode.LocVarDataDef(v);
+        } else {
+            String name = scanner.stringValue, type;
+            scanner.expect(Token.IDENT);
+            if (scanner.token == Token.COLON) {
+                scanner.scan();
+                type = parseIdent();
+            } else {
+                type = "I";                  // TBD
+            }
+            curCode.LocVarDataDef(name, pool.FindCellAsciz(type));
+        }
+    }
+
+    Argument parseLocVarRef() throws Scanner.SyntaxError, IOException {
+        if (scanner.token == Token.INTVAL) {
+            int v = scanner.intValue;
+            scanner.scan();
+            return new Argument(v);
+        } else {
+            String name = scanner.stringValue;
+            scanner.expect(Token.IDENT);
+            return curCode.LocVarDataRef(name);
+        }
+    }
+
+    void parseLocVarEnd() throws Scanner.SyntaxError, IOException {
+        if (scanner.token == Token.INTVAL) {
+            int v = scanner.intValue;
+            scanner.scan();
+            curCode.LocVarDataEnd(v);
+        } else {
+            String name = scanner.stringValue;
+            scanner.expect(Token.IDENT);
+            curCode.LocVarDataEnd(name);
+        }
+    }
+
+    void parseMapItem(DataVector map) throws Scanner.SyntaxError, IOException {
+        StackMapType itemType = stackMapType(scanner.intValue, null);
+        ConstType tag = null;
+        Argument arg = null;
+        Token ptoken = scanner.token;
+        int iValue = scanner.intValue;
+        String sValue = scanner.stringValue;
+        scanner.scan();
+        resolve:
+        {
+            switch (ptoken) {
+                case INTVAL:
+                    break resolve;
+                case CLASS:
+                    itemType = StackMapType.ITEM_Object;
+                    tag = ConstType.CONSTANT_CLASS;
+                    break resolve;
+                case CPINDEX:
+                    itemType = StackMapType.ITEM_Object;
+                    arg = pool.getCell(iValue);
+                    break resolve;
+                case IDENT:
+                    itemType = stackMapType(sValue);
+                    tag = Tables.tag(sValue);
+                    if (itemType != null) { // itemType OK
+                        if ((tag != null) // ambiguity: "int," or "int 77,"?
+                                && (scanner.token != SEMICOLON)
+                                && (scanner.token != COMMA)) {
+                            itemType = StackMapType.ITEM_Object;
+                        }
+                        break resolve;
+                    } else if (tag != null) { // tag OK
+                        itemType = StackMapType.ITEM_Object;
+                        break resolve;
+                    }
+            }
+            // resolution failed:
+            itemType = StackMapType.ITEM_Bogus;
+            env.error("itemtype.expected", "<" + ptoken.printValue() + ">");
+        }
+        switch (itemType) {
+            case ITEM_Object:  // followed by CP index
+                if (arg == null) {
+                    arg = pool.FindCell(cpParser.parseConstValue(tag));
+                }
+                map.addElement(new StackMapData.StackMapItem2(itemType, arg));
+                break;
+            case ITEM_NewObject:  // followed by label
+                arg = instrParser.parseLabelRef();
+                map.addElement(new StackMapData.StackMapItem2(itemType, arg));
+                break;
+            default:
+                map.addElement(new StackMapData.StackMapItem1(itemType));
+        }
+    }
+
+    /**
+     * Parse an external name: CPINDEX, string, or identifier.
+     */
+    ConstCell parseName() throws Scanner.SyntaxError, IOException {
+        debugScan("------- [Parser.parseName]: ");
+        String v;
+        switch (scanner.token) {
+            case CPINDEX: {
+                int cpx = scanner.intValue;
+                scanner.scan();
+                return pool.getCell(cpx);
+            }
+            case STRINGVAL:
+                v = scanner.stringValue;
+                scanner.scan();
+                return pool.FindCellAsciz(v);
+
+            // In many cases, Identifiers can correctly have the same
+            // names as keywords.  We need to allow these.
+            case OPEN:
+            case MODULE:
+            case VARARGS:
+            case REQUIRES:
+            case EXPORTS:
+            case TO:
+            case USES:
+            case PROVIDES:
+            case WITH:
+            case OPENS:
+
+            case ARRAY_TYPEPATH:
+            case INNER_TYPE_TYPEPATH:
+            case WILDCARD_TYPEPATH:
+            case TYPE_ARGUMENT_TYPEPATH:
+            case PERMITTEDSUBCLASSES:
+            case INF:
+            case NAN:
+            case COMPONENT:
+
+            case SYNTHETIC:
+            case DEPRECATED:
+            case VERSION:
+            case BITS:
+            case STACK:
+            case LOCAL:
+            case OF:
+            case INNERCLASS:
+            case STRICT:
+            case FIELDREF:
+            case METHODREF:
+            case IDENT:
+            case BRIDGE:
+            case VALUE:
+            case PRIMITIVE:
+                v = scanner.idValue;
+                scanner.scan();
+                return pool.FindCellAsciz(v);
+            default:
+                env.error(scanner.pos, "name.expected", scanner.token);
+                throw new Scanner.SyntaxError();
+        }
+    }
+
+    /**
+     * Parses a field or method reference for method handle.
+     */
+    ConstCell parseMethodHandle(SubTag subtag) throws Scanner.SyntaxError, IOException {
+        ConstCell refCell;
+        final int pos = parser.env.pos;
+        switch (subtag) {
+            // If the value of the reference_kind item is
+            // 1 (REF_getField), 2 (REF_getStatic), 3 (REF_putField)  or 4 (REF_putStatic),
+            // then the constant_pool entry at that index must be a CONSTANT_Fieldref_info structure (4.4.2)
+            // representing a field for which a method handle is to be created. jvms-4.4.8-200-C-A
+            case REF_GETFIELD:
+            case REF_GETSTATIC:
+            case REF_PUTFIELD:
+            case REF_PUTSTATIC:
+                refCell = pool.FindCell(cpParser.parseConstValue(ConstType.CONSTANT_FIELD));
+                break;
+            //  If the value of the reference_kind item is
+            //  5 (REF_invokeVirtual) or 8 (REF_newInvokeSpecial),
+            //  then the constant_pool entry at that index must be a CONSTANT_Methodref_info structure (4.4.2)
+            //  representing a class's method or constructor (2.9.1) for which a method handle is to be created.
+            //  jvms-4.4.8-200-C-B
+            case REF_INVOKEVIRTUAL:
+            case REF_NEWINVOKESPECIAL:
+                cpParser.setExitImmediately(true);
+                refCell = cpParser.parseConstRef(ConstType.CONSTANT_METHOD, ConstType.CONSTANT_INTERFACEMETHOD);
+                cpParser.setExitImmediately(false);
+                checkReferenceIndex(pos, ConstType.CONSTANT_METHOD, null);
+                break;
+            case REF_INVOKESTATIC:
+            case REF_INVOKESPECIAL:
+                // CODETOOLS-7902333
+                // 4.4.8. The CONSTANT_MethodHandle_info Structure
+                // reference_index
+                // The value of the reference_index item must be a valid index into the constant_pool table.
+                // The constant_pool entry at that index must be as follows:
+                // If the value of the reference_kind item is 6 (REF_invokeStatic) or 7 (REF_invokeSpecial),
+                // then if the class file version number is less than 52.0, the constant_pool entry at that index must be
+                // a CONSTANT_Methodref_info structure representing a class's method for which a method handle is to be created;
+                // if the class file version number is 52.0 or above, the constant_pool entry at that index must be
+                // either a CONSTANT_Methodref_info structure or a CONSTANT_InterfaceMethodref_info structure (4.4.2)
+                // representing a class's or interface's method for which a method handle is to be created.
+                ConstType ctype01 = ConstType.CONSTANT_METHOD;
+                ConstType ctype02 = ConstType.CONSTANT_INTERFACEMETHOD;
+                if (this.cd.cfv.major_version() >= 52 && Modifiers.isInterface(this.cd.access)) {
+                    ctype01 = ConstType.CONSTANT_INTERFACEMETHOD;
+                    ctype02 = ConstType.CONSTANT_METHOD;
+                }
+                cpParser.setExitImmediately(true);
+                refCell = cpParser.parseConstRef(ctype01, ctype02);
+                cpParser.setExitImmediately(false);
+                checkReferenceIndex(pos, ctype01, ctype02);
+                break;
+
+            case REF_INVOKEINTERFACE:
+                cpParser.setExitImmediately(true);
+                refCell = cpParser.parseConstRef(ConstType.CONSTANT_INTERFACEMETHOD, ConstType.CONSTANT_METHOD);
+                cpParser.setExitImmediately(false);
+                checkReferenceIndex(pos, ConstType.CONSTANT_INTERFACEMETHOD, null);
+                break;
+            default:
+                // should not reach
+                throw new Scanner.SyntaxError();
+        }
+        return refCell;
+    }
+
+    /**
+     * Check the pair reference_kind:reference_index where reference_kind is any from:
+     * REF_invokeVirtual, REF_newInvokeSpecial, REF_invokeStatic, REF_invokeSpecial, REF_invokeInterface
+     * and reference_index is one of [Empty], Method or InterfaceMethod
+     * There are possible entries:
+     * ldc Dynamic REF_newInvokeSpecial:InterfaceMethod  LdcConDyTwice."<init>":
+     * ldc Dynamic REF_invokeInterface:LdcConDyTwice."<init>":
+     * ldc Dynamic REF_newInvokeSpecial:Method LdcConDyTwice."<init>":
+     * ldc MethodHandle REF_newInvokeSpecial:InterfaceMethod  LdcConDyTwice."<init>":
+     * ldc MethodHandle REF_invokeInterface:LdcConDyTwice."<init>":
+     * ldc MethodHandle REF_newInvokeSpecial:Method LdcConDyTwice."<init>":
+     * invokedynamic MethodHandle REF_invokeStatic:Method java/lang/invoke/StringConcatFactory.makeConcatWithConstants:
+     * invokedynamic MethodHandle REF_invokeStatic:java/lang/invoke/StringConcatFactory.makeConcatWithConstants
+     * ....
+     * @param position   the position in a source file
+     * @param defaultTag expected reference_index tag (Method or InterfaceMethod)
+     * @param defaultTag 2nd expected reference_index tag (Method or InterfaceMethod)
+     */
+    private void checkReferenceIndex(int position, ConstType defaultTag, ConstType default2Tag) {
+        if ( ! scanner.token.in(COLON, SEMICOLON) ) {
+            if (default2Tag != null) {
+                env.error(position, "wrong.tag2", defaultTag.parseKey(), default2Tag.parseKey());
+            } else {
+                env.error(position, "wrong.tag", defaultTag.parseKey());
+            }
+            throw new Scanner.SyntaxError().Fatal();
+        }
+    }
+
+    /**
+     * Parses a sub-tag value in method handle.
+     */
+    SubTag parseSubtag() throws Scanner.SyntaxError, IOException {
+        SubTag subtag = null;
+        switch (scanner.token) {
+            case IDENT:
+                subtag = subtag(scanner.stringValue);
+                break;
+            case INTVAL:
+                subtag = subtag(scanner.intValue);
+                break;
+        }
+        if (subtag == null) {
+            env.error("subtag.expected");
+            throw new Scanner.SyntaxError();
+        }
+        scanner.scan();
+        return subtag;
+    }
+
+    ConstCell parseClassName(boolean uncond) throws Scanner.SyntaxError, IOException {
+        String v;
+        switch (scanner.token) {
+            case CPINDEX: {
+                int cpx = scanner.intValue;
+                scanner.scan();
+                return pool.getCell(cpx);
+            }
+            case STRINGVAL:
+                v = scanner.stringValue;
+                scanner.scan();
+                v = prependPackage(v, uncond);
+                return pool.FindCellAsciz(v);
+            // Some identifiers might coincide with token names.
+            // these should be OK to use as identifier names.
+            case OPEN:
+            case MODULE:
+            case VARARGS:
+            case REQUIRES:
+            case EXPORTS:
+            case TO:
+            case USES:
+            case PROVIDES:
+            case WITH:
+            case OPENS:
+
+            case ARRAY_TYPEPATH:
+            case INNER_TYPE_TYPEPATH:
+            case WILDCARD_TYPEPATH:
+            case TYPE_ARGUMENT_TYPEPATH:
+            case PERMITTEDSUBCLASSES:
+            case INF:
+            case NAN:
+            case COMPONENT:
+
+            case SYNTHETIC:
+            case DEPRECATED:
+            case VERSION:
+            case BITS:
+            case STACK:
+            case LOCAL:
+            case OF:
+            case INNERCLASS:
+            case STRICT:
+            case FIELDREF:
+            case METHODREF:
+            case BRIDGE:
+            case IDENT:
+            case VALUE:
+            case PRIMITIVE:
+                v = scanner.idValue;
+                scanner.scan();
+                v = prependPackage(v, uncond);
+                return pool.FindCellAsciz(v);
+            default:
+                ConstType key = Tables.tag(scanner.token.value());
+                env.traceln("%%%%% Unrecognized token [" + scanner.token + "]: '" + (key == null ? "null" : key.parseKey()) + "'.");
+                env.error(scanner.prevPos, "name.expected", "\"" + scanner.token.parseKey() + "\"");
+                throw new Scanner.SyntaxError();
+        }
+    }
+
+    private String prependPackage(String className, boolean uncond) {
+        if (uncond || (scanner.token == Token.FIELD)) {
+            if ((!className.contains("/"))             // class identifier doesn't contain "/"
+                    && (!className.contains("["))) {    // class identifier doesn't contain "["
+                className = pkgPrefix + className; // add package
+            }
+        }
+        return className;
+    }
+
+    /**
+     * Parse a signed integer of size bytes long.
+     * size = 1 or 2
+     */
+    Argument parseInt(int size) throws Scanner.SyntaxError, IOException {
+        if (scanner.token == Token.BITS) {
+            scanner.scan();
+        }
+        if (scanner.token != Token.INTVAL) {
+            env.error(scanner.pos, "int.expected");
+            throw new Scanner.SyntaxError();
+        }
+        int arg = scanner.intValue * scanner.sign;
+        switch (size) {
+            case 1:
+//                if ((arg>127)||(arg<-128)) { // 0xFF not allowed
+                if ((arg > 255) || (arg < -128)) { // to allow 0xFF
+                    env.error(scanner.pos, "value.large", "1 byte");
+                    throw new Scanner.SyntaxError();
+                }
+                break;
+            case 2:
+//                if ((arg > 32767) || (arg < -32768)) { //this seems
+// natural but is not backward compatible. Some tests contain
+// expressions like:
+//                sipush    0x8765;
+
+                if ((arg > 65535) || (arg < -32768)) {
+                    env.error(scanner.pos, "value.large", "2 bytes");
+                    throw new Scanner.SyntaxError();
+                }
+                break;
+            default:
+                throw new InternalError("parseInt(" + size + ")");
+        }
+        scanner.scan();
+        return new Argument(arg);
+    }
+
+    /**
+     * Parse an unsigned integer of size bytes long.
+     * size = 1 or 2
+     */
+    Argument parseUInt(int size) throws Scanner.SyntaxError, IOException {
+        if (scanner.token != Token.INTVAL) {
+            env.error(scanner.pos, "int.expected");
+            throw new Scanner.SyntaxError();
+        }
+        if (scanner.sign == -1) {
+            env.error(scanner.pos, "neg.forbidden");
+            throw new Scanner.SyntaxError();
+        }
+        int arg = scanner.intValue;
+        switch (size) {
+            case 1:
+                if (arg > 255) {
+                    env.error(scanner.pos, "value.large", "1 byte");
+                    throw new Scanner.SyntaxError();
+                }
+                break;
+            case 2:
+                if (arg > 65535) {
+                    env.error(scanner.pos, "value.large", "2 bytes");
+                    throw new Scanner.SyntaxError();
+                }
+                break;
+            default:
+                throw new InternalError("parseUInt(" + size + ")");
+        }
+        scanner.scan();
+        return new Argument(arg);
+    }
+
+    /**
+     * Parse constant declaration
+     */
+    private void parseConstDef() throws IOException {
+        for (; ; ) {
+            if (scanner.token == Token.CPINDEX) {
+                int cpx = scanner.intValue;
+                scanner.scan();
+                scanner.expect(Token.ASSIGN);
+                env.traceln("parseConstDef:" + cpx);
+                pool.setCell(cpx, cpParser.parseConstRef(null));
+            } else {
+                env.error("const.def.expected");
+                throw new Scanner.SyntaxError();
+            }
+            if (scanner.token != COMMA) {
+                scanner.expect(SEMICOLON);
+                return;
+            }
+            scanner.scan(); // COMMA
+        }
+    }
+
+    /**
+     * Parse the modifiers
+     */
+    private int scanModifier(int mod) throws IOException {
+        int nextmod, prevpos;
+
+        while (true) {
+            nextmod = 0;
+            switch (scanner.token) {
+                case PUBLIC:
+                    nextmod = ACC_PUBLIC;
+                    break;
+                case PRIVATE:
+                    nextmod = ACC_PRIVATE;
+                    break;
+                case PROTECTED:
+                    nextmod = ACC_PROTECTED;
+                    break;
+                case STATIC:
+                    nextmod = ACC_STATIC;
+                    break;
+                case FINAL:
+                    nextmod = ACC_FINAL;
+                    break;
+                case SYNCHRONIZED:
+                    nextmod = ACC_SYNCHRONIZED;
+                    break;
+                case SUPER:
+                    nextmod = ACC_SUPER;
+                    break;
+                case VOLATILE:
+                    nextmod = ACC_VOLATILE;
+                    break;
+                case BRIDGE:
+                    nextmod = ACC_BRIDGE;
+                    break;
+                case TRANSIENT:
+                    nextmod = ACC_TRANSIENT;
+                    break;
+                case VARARGS:
+                    nextmod = ACC_VARARGS;
+                    break;
+                case NATIVE:
+                    nextmod = ACC_NATIVE;
+                    break;
+                case INTERFACE:
+                    nextmod = ACC_INTERFACE;
+                    break;
+                case ABSTRACT:
+                    nextmod = ACC_ABSTRACT;
+                    break;
+                case STRICT:
+                    nextmod = ACC_STRICT;
+                    break;
+                case ENUM:
+                    nextmod = ACC_ENUM;
+                    break;
+                case SYNTHETIC:
+                    nextmod = ACC_SYNTHETIC;
+                    break;
+                case ANNOTATION_ACCESS:
+                    nextmod = ACC_ANNOTATION;
+                    break;
+
+                case DEPRECATED:
+                    nextmod = DEPRECATED_ATTRIBUTE;
+                    break;
+                case MANDATED:
+                    nextmod = ACC_MANDATED;
+                    break;
+                case VALUE:
+                    nextmod = ACC_VALUE;
+                    break;
+                case PRIMITIVE:
+                    nextmod = ACC_PRIMITIVE;
+                    break;
+                default:
+                    return nextmod;
+            }
+            prevpos = scanner.pos;
+            scanner.scan();
+            if ((mod & nextmod) == 0) {
+                return nextmod;
+            }
+            env.error(prevpos, "warn.repeated.modifier");
+        }
+    }
+
+    int scanModifiers() throws IOException {
+        int mod = 0, nextmod;
+
+        while (true) {
+            nextmod = scanModifier(mod);
+            if (nextmod == 0) {
+                return mod;
+            }
+            mod = mod | nextmod;
+        }
+    }
+
+    /**
+     * Parse a field.
+     */
+    private void parseField(int mod) throws Scanner.SyntaxError, IOException {
+        debugStr("  [Parser.parseField]: <<<Begin>>>");
+        // check access modifiers:
+        Modifiers.checkFieldModifiers(cd, mod, scanner.pos);
+
+        while (true) {
+            ConstCell nameCell = parseName();
+            scanner.expect(Token.COLON);
+            ConstCell typeCell = parseName();
+
+            // Define the variable
+            FieldData fld = cd.addField(mod, nameCell, typeCell);
+
+            if (memberAnnttns != null) {
+                fld.addAnnotations(memberAnnttns);
+            }
+
+            // Parse the optional attribute: signature
+            if (scanner.token == Token.COLON) {
+                scanner.scan();
+                ConstCell signatureCell = parseName();
+                fld.setSignatureAttr(signatureCell);
+            }
+
+            // Parse the optional initializer
+            if (scanner.token == Token.ASSIGN) {
+                scanner.scan();
+                fld.SetValue(cpParser.parseConstRef(null));
+            }
+
+            // If the next scanner.token is a comma, then there is more
+            debugScan("  [Parser.parseField]: Field: " + fld + " ");
+
+            if (scanner.token != COMMA) {
+                scanner.expect(SEMICOLON);
+                return;
+            }
+            scanner.scan();
+        }  // end while
+    }  // end parseField
+
+    /**
+     * Scan method's signature to determine size of parameters.
+     */
+    private int countParams(ConstCell sigCell) throws Scanner.SyntaxError {
+        String sig;
+        try {
+            ConstValue_String strConst = (ConstValue_String) sigCell.ref;
+            sig = strConst.value;
+        } catch (NullPointerException | ClassCastException e) {
+            return 0; // ??? TBD
+        }
+        int siglen = sig.length(), k = 0, loccnt = 0, errparam = 0;
+        boolean arraytype = false;
+        scan:
+        {
+            if (k >= siglen) {
+                break scan;
+            }
+            if (sig.charAt(k) != '(') {
+                errparam = 1;
+                break scan;
+            }
+            for (k = 1; k < siglen; k++) {
+                switch (sig.charAt(k)) {
+                    case ')':
+                        if (arraytype) {
+                            errparam = 2;
+                            break scan;
+                        }
+                        return loccnt;
+                    case '[':
+                        arraytype = true;
+                        break;
+                    case 'B':
+                    case 'C':
+                    case 'F':
+                    case 'I':
+                    case 'S':
+                    case 'Z':
+                        loccnt++;
+                        arraytype = false;
+                        break;
+                    case 'D':
+                    case 'J':
+                        loccnt++;
+                        if (arraytype) {
+                            arraytype = false;
+                        } else {
+                            loccnt++;
+                        }
+                        break;
+                    case 'L':
+                    case 'Q':
+                        for (; ; k++) {
+                            if (k >= siglen) {
+                                errparam = 3;
+                                break scan;
+                            }
+                            if (sig.charAt(k) == ';') {
+                                break;
+                            }
+                        }
+                        loccnt++;
+                        arraytype = false;
+                        break;
+                    default:
+                        errparam = 4;
+                        break scan;
+                }
+            }
+        }
+        env.error(scanner.pos, "msig.malformed", Integer.toString(k), Integer.toString(errparam));
+        return loccnt;
+    }
+
+    /**
+     * Parse a method.
+     */
+    private void parseMethod(int mod) throws Scanner.SyntaxError, IOException {
+
+        // The start of the method
+        int posa = scanner.pos;
+        debugStr("  [Parser.parseMethod]: <<<Begin>>>");
+
+        ConstCell nameCell = parseName();
+        ConstValue_String strConst = (ConstValue_String) nameCell.ref;
+        String name = strConst.value;
+        boolean is_clinit = name.equals("<clinit>");
+        boolean is_init = name.equals("<init>")
+            && !Modifiers.isStatic(mod); // TODO: not a good way to detect factories...
+        DefaultAnnotationAttr defAnnot = null;
+
+        // check access modifiers:
+        Modifiers.checkMethodModifiers(cd, mod, posa, is_init, is_clinit);
+
+        scanner.expect(Token.COLON);
+        ConstCell typeCell = parseName();
+        int paramcnt = countParams(typeCell);
+        if ((!Modifiers.isStatic(mod)) && !is_clinit) {
+            paramcnt++;
+        }
+        if (paramcnt > 255) {
+            env.error(scanner.pos, "warn.msig.more255", Integer.toString(paramcnt));
+        }
+        // Parse throws clause
+        ArrayList<ConstCell> exc_table = null;
+        if (scanner.token == Token.THROWS) {
+            scanner.scan();
+            exc_table = new ArrayList<>();
+            for (; ; ) {
+                posa = scanner.pos;
+                ConstCell exc = cpParser.parseConstRef(ConstType.CONSTANT_CLASS);
+                if (exc_table.contains(exc)) {
+                    env.error(posa, "warn.exc.repeated");
+                } else {
+                    exc_table.add(exc);
+                    env.traceln("THROWS:" + exc.arg);
+                }
+                if (scanner.token != COMMA) {
+                    break;
+                }
+                scanner.scan();
+            }
+        }
+        if (scanner.token == Token.DEFAULT) {
+            // need to scan the annotation value
+            defAnnot = annotParser.parseDefaultAnnotation();
+        }
+
+        MethodData curMethod = cd.StartMethod(mod, nameCell, typeCell, exc_table);
+        Argument max_stack = null, max_locals = null;
+
+        if (scanner.token == Token.STACK) {
+            scanner.scan();
+            max_stack = parseUInt(2);
+        }
+        if (scanner.token == Token.LOCAL) {
+            scanner.scan();
+            max_locals = parseUInt(2);
+        }
+        if (scanner.token == Token.INTVAL) {
+            annotParser.parseParamAnnots(paramcnt, curMethod);
+        }
+
+        if (scanner.token == SEMICOLON) {
+            if ((max_stack != null) || (max_locals != null)) {
+                env.error("token.expected", "{");
+            }
+            scanner.scan();
+        } else {
+            scanner.expect(Token.LBRACE);
+            curCode = curMethod.startCode(posa, paramcnt, max_stack, max_locals);
+            while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) {
+                instrParser.parseInstr();
+                if (scanner.token == Token.RBRACE) {
+                    break;
+                }
+                // code's type annotation(s)
+                if (scanner.token == Token.ANNOTATION) {
+                    curCode.addAnnotations(annotParser.scanAnnotations());
+                    break;
+                }
+                scanner.expect(SEMICOLON);
+            }
+            curCode.endCode();
+            scanner.expect(Token.RBRACE);
+        }
+
+        if (defAnnot != null) {
+            curMethod.addDefaultAnnotation(defAnnot);
+        }
+        if (memberAnnttns != null) {
+            curMethod.addAnnotations(memberAnnttns);
+        }
+        cd.EndMethod();
+        debugStr("  [Parser.parseMethod]: Method: " + curMethod);
+
+    }  // end parseMethod
+
+    /**
+     * Parse a (CPX based) BootstrapMethod entry.
+     */
+    private void parseCPXBootstrapMethod() throws Scanner.SyntaxError, IOException {
+        // Parses in the form:
+        // BOOTSTRAPMETHOD CPX_MethodHandle (CPX_Arg)* ;
+        if (scanner.token == Token.CPINDEX) {
+            // CPX can be a CPX to an MethodHandle constant,
+            int cpx = scanner.intValue;
+            ConstCell MHCell = pool.getCell(cpx);
+            scanner.scan();
+            ArrayList<ConstCell> bsm_args = new ArrayList<>(256);
+
+            while (scanner.token != SEMICOLON) {
+                if (scanner.token == Token.CPINDEX) {
+                    bsm_args.add(pool.getCell(scanner.intValue));
+
+                } else {
+                    // throw error, bootstrap method is not recognizable
+                    env.error(scanner.pos, "invalid.bootstrapmethod");
+                    throw new Scanner.SyntaxError();
+                }
+                scanner.scan();
+            }
+            BootstrapMethodData bsmData = new BootstrapMethodData(MHCell, bsm_args);
+            cd.addBootstrapMethod(bsmData);
+        } else {
+            // throw error, bootstrap method is not recognizable
+            env.error(scanner.pos, "invalid.bootstrapmethod");
+            throw new Scanner.SyntaxError();
+        }
+    }
+
+    /**
+     * Parse a NestHost entry
+     */
+    private void parseNestHost() throws Scanner.SyntaxError, IOException {
+        // Parses in the form:
+        // NESTHOST IDENT;
+        debugStr("  [Parser.parseNestHost]: <<<Begin>>>");
+        String className = prependPackage(parseIdent(), true);
+        ConstCell hostClass = pool.FindCellClassByName(className);
+        debugScan("  [Parser.parseNestHost]: NestHost: class " + className);
+        scanner.expect(SEMICOLON);
+        cd.addNestHost(hostClass);
+    }
+
+    /**
+     * Parse a list of classes belonging to the
+     * [NestMembers | PermittedSubclasses | Preload]  entry
+     */
+    private void parseClasses(Consumer<ArrayList<ConstCell>> classesConsumer)
+            throws Scanner.SyntaxError, IOException {
+        ArrayList<ConstCell> classes = new ArrayList<>();
+        // Parses in the form:
+        // (NESTMEMBERS|PERMITTEDSUBCLASSES)? IDENT(, IDENT)*;
+        debugStr("  [Parser.parseClasses]: <<<Begin>>>");
+        while (true) {
+            String className = prependPackage(parseIdent(), true);
+            classes.add(pool.FindCellClassByName(className));
+            debugScan("  [Parser.parseClasses]: class " + className);
+            if (scanner.token != COMMA) {
+                scanner.expect(SEMICOLON);
+                classesConsumer.accept(classes);
+                return;
+            }
+            scanner.scan();
+        }
+    }
+
+    /**
+     * Parse the Record entry
+     */
+    private void parseRecord() throws Scanner.SyntaxError, IOException {
+        // Parses in the form:
+        // RECORD { (COMPONENT)+ }
+        // where
+        // COMPONENT Component (ANNOTATION)* NAME:DESCRIPTOR(:SIGNATURE)? (,|;)
+        // NAME = (CPINDEX | IDENT)
+        // DESCRIPTOR = (CPINDEX | STRING)
+        // SIGNATURE  = (CPINDEX | STRING)
+        debugScan("[Parser.parseRecord]:  Begin");
+        scanner.expect(Token.LBRACE);
+
+        ArrayList<AnnotationData> componentAnntts = null;
+        boolean grouped = false;
+        RecordData rd = cd.setRecord(scanner.pos);
+
+        while (true) {
+            if (scanner.token == Token.RBRACE) {
+                if (rd.isEmpty()) {
+                    env.error(scanner.pos, "warn.no.components.in.record.attribute");
+                    cd.rejectRecord();
+                } else if (grouped) {
+                    env.error(scanner.pos, "grouped.component.expected");
+                }
+                scanner.scan();
+                break;
+            }
+
+            ConstCell nameCell, descCell, signatureCell = null;
+            if (scanner.token == Token.ANNOTATION) {
+                componentAnntts = annotParser.scanAnnotations();
+            }
+
+            scanner.expect(Token.COMPONENT);
+
+            nameCell = parseName();
+            scanner.expect(Token.COLON);
+            descCell = parseName();
+            // Parse the optional attribute: signature
+            if (scanner.token == Token.COLON) {
+                scanner.scan();
+                signatureCell = parseName();
+            }
+
+            rd.addComponent(nameCell, descCell, signatureCell, componentAnntts);
+
+            switch (scanner.token) {
+                case COMMA:
+                    grouped = true;
+                    break;
+                case SEMICOLON:
+                    grouped = false;
+                    componentAnntts = null;
+                    break;
+                default:
+                    env.error(scanner.pos, "one.of.two.token.expected",
+                            "<" + SEMICOLON.printValue() + ">",
+                            "<" + COMMA.printValue() + ">");
+                    break;
+            }
+            // next component
+            scanner.scan();
+        }  // end while
+        debugScan("[Parser.parseRecord]:  End");
+    }
+
+    /**
+     * Parse an inner class.
+     */
+    private void parseInnerClass(int mod) throws Scanner.SyntaxError, IOException {
+        // Parses in the form:
+        // MODIFIERS (INNERCLASSNAME =)? (INNERCLASS) (OF OUTERCLASS)? ;
+        //
+        // where
+        //    INNERCLASSNAME = (IDENT | CPX_IN-CL-NM)
+        //    INNERCLASS = (CLASS IDENT | CPX_IN-CL) (S2)
+        //    OUTERCLASS = (CLASS IDENT | CPX_OT-CL) (S3)
+        //
+        // Note:
+        //    If a class reference cannot be identified using IDENT, CPX indexes must be used.
+
+        // check access modifiers:
+        debugScan("[Parser.parseInnerClass]:  Begin ");
+        Modifiers.checkInnerClassModifiers(cd, mod, scanner.pos);
+
+        ConstCell nameCell;
+        ConstCell innerClass = null;
+        ConstCell outerClass = null;
+
+
+        if (scanner.token == Token.CLASS) {
+            nameCell = pool.getCell(0);  // no NameIndex
+            parseInnerClass_s2(mod, nameCell, innerClass, outerClass);
+        } else {
+            if ((scanner.token == Token.IDENT) || scanner.checkTokenIdent()) {
+                // Got a Class Name
+                nameCell = parseName();
+                parseInnerClass_s1(mod, nameCell, innerClass, outerClass);
+            } else if (scanner.token == Token.CPINDEX) {
+                // CPX can be either a CPX to an InnerClassName,
+                // or a CPX to an InnerClassInfo
+                int cpx = scanner.intValue;
+                nameCell = pool.getCell(cpx);
+                ConstValue nameCellValue = nameCell.ref;
+
+                if (nameCellValue instanceof ConstValue_String) {
+                    // got a name cell
+                    scanner.scan();
+                    parseInnerClass_s1(mod, nameCell, innerClass, outerClass);
+                } else {
+                    // got a CPRef cell
+                    nameCell = pool.getCell(0);  // no NameIndex
+                    parseInnerClass_s2(mod, nameCell, innerClass, outerClass);
+                }
+            } else {
+                pic_error();
+            }
+
+        }
+    }
+
+    private void parseInnerClass_s1(int mod, ConstCell nameCell, ConstCell innerClass, ConstCell outerClass) throws IOException {
+        // next scanner.token must be '='
+        if (scanner.token == Token.ASSIGN) {
+            scanner.scan();
+            parseInnerClass_s2(mod, nameCell, innerClass, outerClass);
+        } else {
+            pic_error();
+        }
+
+    }
+
+    private void parseInnerClass_s2(int mod, ConstCell nameCell, ConstCell innerClass, ConstCell outerClass) throws IOException {
+        // scanner.token is either "CLASS IDENT" or "CPX_Class"
+        if ((scanner.token == Token.CPINDEX) || (scanner.token == Token.CLASS)) {
+            if (scanner.token == Token.CPINDEX) {
+                innerClass = cpParser.parseConstRef(ConstType.CONSTANT_CLASS);
+            }
+
+            if (scanner.token == Token.CLASS) {
+                // next symbol needs to be InnerClass
+                scanner.scan();
+                innerClass = cpParser.parseConstRef(ConstType.CONSTANT_CLASS);
+            }
+
+            // See if declaration is terminated
+            if (scanner.token == SEMICOLON) {
+                // InnerClass is complete, no OUTERINFO;
+                outerClass = pool.getCell(0);
+                pic_tracecreate(mod, nameCell, innerClass, outerClass);
+                cd.addInnerClass(mod, nameCell, innerClass, outerClass);
+            } else if (scanner.token == Token.OF) {
+                // got an outer class reference
+                parseInnerClass_s3(mod, nameCell, innerClass, outerClass);
+            } else {
+                pic_error();
+            }
+
+        } else {
+            pic_error();
+        }
+
+    }
+
+    private void parseInnerClass_s3(int mod, ConstCell nameCell, ConstCell innerClass, ConstCell outerClass) throws IOException {
+        scanner.scan();
+        if ((scanner.token == Token.CLASS) || (scanner.token == Token.CPINDEX)) {
+            if (scanner.token == Token.CLASS) {
+                // next symbol needs to be InnerClass
+                scanner.scan();
+                outerClass = cpParser.parseConstRef(ConstType.CONSTANT_CLASS);
+            }
+            if (scanner.token == Token.CPINDEX) {
+                outerClass = cpParser.parseConstRef(ConstType.CONSTANT_CLASS);
+            }
+
+            if (scanner.token == SEMICOLON) {
+                pic_tracecreate(mod, nameCell, innerClass, outerClass);
+                cd.addInnerClass(mod, nameCell, innerClass, outerClass);
+            } else {
+                pic_error();
+            }
+        } else {
+            pic_error();
+        }
+    }
+
+    private void pic_tracecreate(int mod, ConstCell nameCell, ConstCell innerClass, ConstCell outerClass) {
+        // throw error, IC is not recognizable
+        env.trace(" Creating InnerClass: [" + Modifiers.toString(mod, CF_Context.CTX_INNERCLASS) + "], ");
+
+        if (nameCell != pool.getCell(0)) {
+            ConstValue value = nameCell.ref;
+            if (value != null) {
+                env.trace(value.toString() + " = ");
+            }
+        }
+
+        ConstValue_Cell ici_val = (ConstValue_Cell) innerClass.ref;
+        ConstCell ici_ascii = ici_val.cell;
+        // Constant pool may not be numberized yet.
+        //
+        // check values before dereference on a trace.
+        if (ici_ascii.ref == null) {
+            env.trace("<#cpx-unresolved> ");
+        } else {
+            ConstValue_String cval = (ConstValue_String) ici_ascii.ref;
+            if (cval.value == null) {
+                env.trace("<#cpx-0> ");
+            } else {
+                env.trace(cval.value + " ");
+            }
+        }
+
+        if (outerClass != pool.getCell(0)) {
+            if (outerClass.arg != 0) {
+                ConstValue_Cell oci_val = (ConstValue_Cell) outerClass.ref;
+                ConstCell oci_ascii = oci_val.cell;
+                if (oci_ascii.ref == null) {
+                    env.trace(" of <#cpx-unresolved>  ");
+                } else {
+                    ConstValue_String cval = (ConstValue_String) oci_ascii.ref;
+                    if (cval.value == null) {
+                        env.trace(" of <#cpx-0>  ");
+                    } else {
+                        env.trace(" of " + cval.value);
+                    }
+                }
+            }
+        }
+
+        env.traceln("");
+    }
+
+    private void pic_error() {
+        // throw error, IC is not recognizable
+        env.error(scanner.pos, "invalid.innerclass");
+        throw new Scanner.SyntaxError();
+    }
+
+    /**
+     * The match() method is used to quickly match opening
+     * brackets (ie: '(', '{', or '[') with their closing
+     * counter part. This is useful during error recovery.<p>
+     * <p>
+     * Scan to a matching '}', ']' or ')'. The current scanner.token must be
+     * a '{', '[' or '(';
+     */
+    private void match(Token open, Token close) throws IOException {
+        int depth = 1;
+
+        while (true) {
+            scanner.scan();
+            if (scanner.token == open) {
+                depth++;
+            } else if (scanner.token == close) {
+                if (--depth == 0) {
+                    return;
+                }
+            } else if (scanner.token == Token.EOF) {
+                env.error(scanner.pos, "unbalanced.paren");
+                return;
+            }
+        }
+    }
+
+    /**
+     * Recover after a syntax error in a field. This involves
+     * discarding scanner.tokens until an EOF or a possible legal
+     * continuation is encountered.
+     */
+    private void recoverField() throws Scanner.SyntaxError, IOException {
+        while (true) {
+            switch (scanner.token) {
+                case EOF:
+                case STATIC:
+                case FINAL:
+                case PUBLIC:
+                case PRIVATE:
+                case SYNCHRONIZED:
+                case TRANSIENT:
+                case PROTECTED:
+                case VOLATILE:
+                case NATIVE:
+//                case INTERFACE: see below
+                case ABSTRACT:
+                case ANNOTATION_ACCESS:
+                    // possible begin of a field, continue
+                    return;
+
+                case LBRACE:
+                    match(Token.LBRACE, Token.RBRACE);
+                    scanner.scan();
+                    break;
+
+                case LPAREN:
+                    match(Token.LPAREN, Token.RPAREN);
+                    scanner.scan();
+                    break;
+
+                case LSQBRACKET:
+                    match(Token.LSQBRACKET, Token.RSQBRACKET);
+                    scanner.scan();
+                    break;
+
+                case RBRACE:
+                case INTERFACE:
+                case CLASS:
+                case IMPORT:
+                case PACKAGE:
+                    // begin of something outside a class, panic more
+                    endClass();
+                    scanner.debugStr("    [Parser.recoverField]: pos: [" + scanner.pos + "]: ");
+                    throw new Scanner.SyntaxError().Fatal();
+                default:
+                    // don't know what to do, skip
+                    scanner.scan();
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Parse a class or interface declaration.
+     */
+    private void parseClass(int mod) throws IOException {
+        int posa = scanner.pos;
+        debugStr("   [Parser.parseClass]:  Begin ");
+        // check access modifiers:
+        Modifiers.checkClassModifiers(env, mod, scanner);
+
+        if (cd == null) {
+            cd = new ClassData(env, currentCFV.clone());
+            pool = cd.pool;
+        }
+
+        if (clsAnnttns != null) {
+            cd.addAnnotations(clsAnnttns);
+        }
+
+        // move the tokenizer to the identifier:
+        if (scanner.token == Token.CLASS) {
+            scanner.scan();
+        } else if (scanner.token == Token.ANNOTATION) {
+            scanner.scan();
+            if (scanner.token == Token.INTERFACE) {
+                mod |= ACC_ANNOTATION | ACC_INTERFACE;
+                scanner.scan();
+            } else {
+                env.error(scanner.prevPos, "token.expected", Token.ANNOTATION.parseKey() + Token.INTERFACE.parseKey());
+                throw new Scanner.SyntaxError();
+            }
+        }
+
+        // Parse the class name
+        ConstCell nm = cpParser.parseConstRef(ConstType.CONSTANT_CLASS, null, true);
+
+        if (scanner.token == Token.FIELD) { // DOT
+            String fileExtension;
+            scanner.scan();
+            switch (scanner.token) {
+                case STRINGVAL:
+                    fileExtension = scanner.stringValue;
+                    break;
+                case IDENT:
+                    fileExtension = scanner.idValue;
+                    break;
+                default:
+                    env.error(scanner.pos, "name.expected");
+                    throw new Scanner.SyntaxError();
+            }
+            scanner.scan();
+            cd.fileExtension = "." + fileExtension;
+        } else if (scanner.token == Token.MODULE) {
+            env.error(scanner.prevPos, "token.expected", Token.OPEN.parseKey());
+            throw new Scanner.SyntaxError();
+        } else if (scanner.token == SEMICOLON) {
+            // drop the semi-colon following a name
+            scanner.scan();
+        }
+
+        // Parse extends clause
+        ConstCell sup = null;
+        if (scanner.token == Token.EXTENDS) {
+            scanner.scan();
+            sup = cpParser.parseConstRef(ConstType.CONSTANT_CLASS);
+            while (scanner.token == COMMA) {
+                scanner.scan();
+                env.error(posa, "multiple.inherit");
+                cpParser.parseConstRef(ConstType.CONSTANT_CLASS);
+            }
+        }
+
+        // Parse implements clause
+        ArrayList<Argument> impl = new ArrayList<>();
+        if (scanner.token == Token.IMPLEMENTS) {
+            do {
+                scanner.scan();
+                Argument intf = cpParser.parseConstRef(ConstType.CONSTANT_CLASS);
+                if (impl.contains(intf)) {
+                    env.error(posa, "warn.intf.repeated", intf);
+                } else {
+                    impl.add(intf);
+                }
+            } while (scanner.token == COMMA);
+        }
+        parseVersion();
+        scanner.expect(Token.LBRACE);
+
+        // Begin a new class
+        cd.init(mod, nm, sup, impl);
+
+        // Parse constant declarations
+
+        // Parse class members
+        while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) {
+            switch (scanner.token) {
+                case SEMICOLON:
+                    // Empty fields are allowed
+                    scanner.scan();
+                    break;
+                case CONST:
+                    scanner.scan();
+                    parseConstDef();
+                    explicitcp = true;
+                    break;
+                default:   // scanner.token is some member.
+                    parseClassMembers();
+            }  // end switch
+        } // while
+        scanner.expect(Token.RBRACE);
+        // End the class
+        endClass();
+    } // end parseClass
+
+    /**
+     * Parses a package or type name in a module statement(s)
+     */
+    private String parseTypeName() throws IOException {
+        String name = "", field = "";
+        while (true) {
+            if (scanner.token.possibleModuleName()) {
+                name = name + field + scanner.idValue;
+                scanner.scan();
+            } else {
+                env.error(scanner.pos, "name.expected", "\"" + scanner.token.parseKey() + "\"");
+                throw new Scanner.SyntaxError();
+            }
+            if (scanner.token == Token.FIELD) {
+                env.error(scanner.pos, "warn.dot.will.be.converted");
+                field = "/";
+                scanner.scan();
+            } else {
+                break;
+            }
+        }
+        return name;
+    }
+
+    /**
+     * Parses a module name in a module statement(s)
+     */
+    private String parseModuleName() throws IOException {
+        String name = "", field = "";
+        while (true) {
+            if (scanner.token.possibleModuleName()) {
+                name = name + field + scanner.idValue;
+                scanner.scanModuleStatement();
+            } else {
+                env.error(scanner.pos, "module.name.expected", "\"" + scanner.token.parseKey() + "\"");
+                throw new Scanner.SyntaxError().Fatal();
+            }
+            if (scanner.token == Token.FIELD) {
+                field = Character.toString((char) scanner.token.value());
+                scanner.scanModuleStatement();
+            } else {
+                break;
+            }
+        }
+        return name;
+    }
+
+    /**
+     * Parse a module declaration.
+     */
+    private void parseModule() throws IOException {
+        debugStr("   [Parser.parseModule]:  Begin ");
+        if (cd == null) {
+            cd = new ClassData(env, currentCFV.clone());
+            pool = cd.pool;
+        }
+        if (clsAnnttns != null) {
+            cd.addAnnotations(clsAnnttns);
+        }
+        moduleAttribute = new ModuleAttr(cd);
+
+        if (scanner.token == Token.OPEN) {
+            moduleAttribute.openModule();
+            scanner.scan();
+        }
+
+        // move the tokenizer to the identifier:
+        if (scanner.token == Token.MODULE) {
+            scanner.scanModuleStatement();
+            // scanner.scan();
+        } else {
+            env.error(scanner.pos, "token.expected", Token.MODULE.parseKey());
+            throw new Scanner.SyntaxError().Fatal();
+        }
+        // Parse the module name
+        String moduleName = parseModuleName();
+        if (moduleName.isEmpty()) {
+            env.error(scanner.pos, "name.expected");
+            throw new Scanner.SyntaxError().Fatal();
+        }
+        moduleAttribute.setModuleName(moduleName);
+
+        parseVersion();
+        scanner.expect(Token.LBRACE);
+
+        // Begin a new class as module
+        cd.initAsModule();
+
+        // Parse module statement(s)
+        while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) {
+            switch (scanner.token) {
+                case REQUIRES:
+                    scanRequires(moduleAttribute.requires);
+                    break;
+                case EXPORTS:
+                    scanStatement(moduleAttribute.exports,
+                            this::parseTypeName,
+                            this::parseModuleName,
+                            Token.TO,
+                            true,
+                            "exports.expected");
+                    break;
+                case OPENS:
+                    scanStatement(moduleAttribute.opens,
+                            this::parseTypeName,
+                            this::parseModuleName,
+                            Token.TO, true, "opens.expected");
+                    break;
+                case USES:
+                    scanStatement(moduleAttribute.uses, "uses.expected");
+                    break;
+                case PROVIDES:
+                    scanStatement(moduleAttribute.provides,
+                            this::parseTypeName,
+                            this::parseTypeName,
+                            Token.WITH,
+                            false,
+                            "provides.expected");
+                    break;
+                case SEMICOLON:
+                    // Empty fields are allowed
+                    scanner.scan();
+                    break;
+                default:
+                    env.error(scanner.pos, "module.statement.expected");
+                    throw new Scanner.SyntaxError().Fatal();
+            }  // end switch
+        } // while
+        scanner.expect(Token.RBRACE);
+        // End the module
+        endModule();
+    } // end parseModule
+
+    /**
+     * Scans  ModuleStatement: requires [transitive] [static] ModuleName ;
+     */
+    private void scanRequires(BiConsumer<String, Integer> action) throws IOException {
+        int flags = 0;
+        String mn = "";
+        scanner.scanModuleStatement();
+        while (scanner.token != SEMICOLON) {
+            switch (scanner.token) {
+                case STATIC:
+                    if (((flags & (1 << Module.Modifier.ACC_STATIC_PHASE.asInt())) != 0) || !mn.isEmpty()) {
+                        env.error(scanner.pos, "requires.expected");
+                        throw new Scanner.SyntaxError().Fatal();
+                    }
+                    flags |= Module.Modifier.ACC_STATIC_PHASE.asInt();
+                    break;
+                case TRANSITIVE:
+                    if (((flags & (1 << Module.Modifier.ACC_TRANSITIVE.asInt())) != 0) || !mn.isEmpty()) {
+                        env.error(scanner.pos, "requires.expected");
+                        throw new Scanner.SyntaxError().Fatal();
+                    }
+                    flags |= Module.Modifier.ACC_TRANSITIVE.asInt();
+                    break;
+                case IDENT:
+                    if (!mn.isEmpty()) {
+                        env.error(scanner.pos, "requires.expected");
+                        throw new Scanner.SyntaxError().Fatal();
+                    }
+                    mn = parseModuleName();
+                    continue;
+                default:
+                    if (mn.isEmpty() && scanner.token.possibleModuleName()) {
+                        mn = parseModuleName();
+                        continue;
+                    } else {
+                        env.error(scanner.pos, "requires.expected");
+                        throw new Scanner.SyntaxError().Fatal();
+                    }
+            }
+            scanner.scanModuleStatement();
+        }
+        // Token.SEMICOLON
+        if (mn.isEmpty()) {
+            env.error(scanner.pos, "requires.expected");
+            throw new Scanner.SyntaxError().Fatal();
+        }
+        action.accept(mn, flags);
+        scanner.scanModuleStatement();
+    }
+
+    /**
+     * Scans  ModuleStatement: uses TypeName;
+     */
+    private void scanStatement(Consumer<Set<String>> action, String err) throws IOException {
+        HashSet<String> names = scanList(() -> scanner.scan(), this::parseTypeName, err, true);
+        // Token.SEMICOLON
+        if (names.size() != 1) {
+            env.error(scanner.pos, err);
+            throw new Scanner.SyntaxError().Fatal();
+        }
+        action.accept(names);
+        scanner.scan();
+    }
+
+    /**
+     * Scans  Module Statement(s):
+     * exports  packageName [to ModuleName {, ModuleName}] ;
+     * opens    packageName [to ModuleName {, ModuleName}] ;
+     * provides TypeName with TypeName [,typeName] ;
+     */
+    private void scanStatement(BiConsumer<String, Set<String>> action,
+                               NameSupplier source,
+                               NameSupplier target,
+                               Token startList,
+                               boolean emptyListAllowed,
+                               String err) throws IOException {
+        String typeName = "";
+        HashSet<String> names = new HashSet<>();
+        scanner.scan();
+        while (scanner.token != SEMICOLON) {
+            if (scanner.token == Token.IDENT) {
+                if (typeName.isEmpty()) {
+                    typeName = source.get();
+                    continue;
+                }
+                env.error(scanner.pos, err);
+                throw new Scanner.SyntaxError().Fatal();
+            }
+            if (scanner.token == startList) {
+                if (typeName.isEmpty()) {
+                    env.error(scanner.pos, err);
+                    throw new Scanner.SyntaxError().Fatal();
+                }
+                names = scanList(scanner.token == Token.TO ? () -> scanner.scanModuleStatement() : () -> scanner.scan(), target, err, false);
+                break;
+            } else {
+                env.error(scanner.pos, err);
+                throw new Scanner.SyntaxError().Fatal();
+            }
+        }
+        // Token.SEMICOLON
+        if (typeName.isEmpty() || (names.isEmpty() && !emptyListAllowed)) {
+            env.error(scanner.pos, err);
+            throw new Scanner.SyntaxError().Fatal();
+        }
+        action.accept(typeName, names);
+        scanner.scan();
+    }
+
+    /**
+     * Scans the "to" or "with" part of ModuleStatement: exports PackageName  [to  ModuleName {, ModuleName}] ;,
+     * opens  packageName   [to  ModuleName {, ModuleName}] ;
+     * provides TypeName with TypeName [,typeName] ;
+     * uses TypeName;
+     * : [ModuleName {, ModuleName}]; , [TypeName [,typeName]]; or TypeName;
+     */
+    private HashSet<String> scanList(Method scanMethod, NameSupplier target, String err, boolean onlyOneElement) throws IOException {
+        HashSet<String> names = new HashSet<>();
+        boolean comma = false, first = true;
+        scanMethod.call();
+        while (scanner.token != SEMICOLON) {
+            switch (scanner.token) {
+                case COMMA:
+                    if (comma || first || onlyOneElement) {
+                        env.error(scanner.pos, err);
+                        throw new Scanner.SyntaxError().Fatal();
+                    }
+                    comma = true;
+                    break;
+                case IDENT:
+                    if (!first && !comma) {
+                        env.error(scanner.pos, err);
+                        throw new Scanner.SyntaxError().Fatal();
+                    }
+                    names.add(target.get());
+                    comma = false;
+                    first = false;
+                    continue;
+                default:
+                    env.error(scanner.pos, err);
+                    throw new Scanner.SyntaxError().Fatal();
+            }
+            scanner.scan();
+        }
+        // Token.SEMICOLON
+        if (names.isEmpty() || comma) {
+            env.error(scanner.pos, err);
+            throw new Scanner.SyntaxError().Fatal();
+        }
+        return names;
+    }
+
+    private void parseClassMembers() throws IOException {
+        debugScan("[Parser.parseClassMembers]:  Begin ");
+        // Parse annotations
+        if (scanner.token == Token.ANNOTATION) {
+            memberAnnttns = annotParser.scanAnnotations();
+        }
+        // Parse modifiers
+        int mod = scanModifiers();
+        try {
+            switch (scanner.token) {
+                case FIELDREF:
+                    scanner.scan();
+                    parseField(mod);
+                    break;
+                case METHODREF:
+                    scanner.scan();
+                    parseMethod(mod);
+                    break;
+                case INNERCLASS:
+                    scanner.scan();
+                    parseInnerClass(mod);
+                    break;
+                case BOOTSTRAPMETHOD:
+                    scanner.scan();
+                    parseCPXBootstrapMethod();
+                    break;
+                case NESTHOST:
+                    if (cd.nestHostAttributeExists()) {
+                        env.error(scanner.pos, "extra.nesthost.attribute");
+                        throw new Scanner.SyntaxError();
+                    } else if (cd.nestMembersAttributesExist()) {
+                        env.error(scanner.pos, "both.nesthost.nestmembers.found");
+                        throw new Scanner.SyntaxError();
+                    }
+                    scanner.scan();
+                    parseNestHost();
+                    break;
+                case NESTMEMBERS:
+                    if (cd.nestMembersAttributesExist()) {
+                        env.error(scanner.pos, "extra.nestmembers.attribute");
+                        throw new Scanner.SyntaxError();
+                    } else if (cd.nestHostAttributeExists()) {
+                        env.error(scanner.pos, "both.nesthost.nestmembers.found");
+                        throw new Scanner.SyntaxError();
+                    }
+                    scanner.scan();
+                    parseClasses(list -> cd.addNestMembers(list));
+                    break;
+                case PERMITTEDSUBCLASSES:         // JEP 360
+                    if (cd.nestMembersAttributesExist()) {
+                        env.error(scanner.pos, "extra.permittedsubclasses.attribute");
+                        throw new Scanner.SyntaxError();
+                    }
+                    scanner.scan();
+                    parseClasses(list -> cd.addPermittedSubclasses(list));
+                    break;
+                case RECORD:                    // JEP 359
+                    if (cd.recordAttributeExists()) {
+                        env.error(scanner.pos, "extra.record.attribute");
+                        throw new Scanner.SyntaxError();
+                    }
+                    scanner.scan();
+                    parseRecord();
+                    break;
+                case PRELOAD:
+                    if (cd.preloadAttributeExists()) {
+                        env.error(scanner.pos, "extra.preload.attribute");
+                        throw new Scanner.SyntaxError();
+                    }
+                    scanner.scan();
+                    parseClasses(list -> cd.addPreloads(list));
+                    break;
+                default:
+                    env.error(scanner.pos, "field.expected");
+                    throw new Scanner.SyntaxError();
+            }  // end switch
+        } catch (Scanner.SyntaxError e) {
+            recoverField();
+        }
+        memberAnnttns = null;
+    }
+
+    /**
+     * Recover after a syntax error in the file.
+     * This involves discarding scanner.tokens until an EOF
+     * or a possible legal continuation is encountered.
+     */
+    private void recoverFile() throws IOException {
+        while (true) {
+            env.traceln("recoverFile: scanner.token=" + scanner.token);
+            switch (scanner.token) {
+                case CLASS:
+                case INTERFACE:
+                    // Start of a new source file statement, continue
+                    return;
+
+                case LBRACE:
+                    match(Token.LBRACE, Token.RBRACE);
+                    scanner.scan();
+                    break;
+
+                case LPAREN:
+                    match(Token.LPAREN, Token.RPAREN);
+                    scanner.scan();
+                    break;
+
+                case LSQBRACKET:
+                    match(Token.LSQBRACKET, Token.RSQBRACKET);
+                    scanner.scan();
+                    break;
+
+                case EOF:
+                    return;
+
+                default:
+                    // Don't know what to do, skip
+                    scanner.scan();
+                    break;
+            }
+        }
+    }
+
+    /**
+     * End class
+     */
+    private void endClass() {
+        if (explicitcp) {
+            // Fix references in the constant pool (for explicitly coded CPs)
+            pool.fixRefsInPool();
+            // Fix any bootstrap Method references too
+            cd.relinkBootstrapMethods();
+        }
+        cd.endClass();
+        clsDataList.add(cd);
+        cd = null;
+    }
+
+    /**
+     * End module
+     */
+    private void endModule() {
+        cd.endModule(moduleAttribute);
+        clsDataList.add(cd);
+        cd = null;
+    }
+
+    final ClassData[] getClassesData() {
+        return clsDataList.toArray(new ClassData[0]);
+    }
+
+    /**
+     * Determines whether the JASM file is for a package-info class
+     * or for a module-info class.
+     * <p>
+     * creates the correct kind of ClassData accordingly.
+     *
+     * @throws IOException
+     */
+    private void parseJasmPackages() throws IOException {
+        try {
+            // starting annotations could either be
+            // a package annotation, or a class annotation
+            if (scanner.token == Token.ANNOTATION) {
+                if (cd == null) {
+                    cd = new ClassData(env, currentCFV.clone());
+                    pool = cd.pool;
+                }
+                pkgAnnttns = annotParser.scanAnnotations();
+            }
+            if (scanner.token == Token.PACKAGE) {
+                // Package statement
+                scanner.scan();
+                int where = scanner.pos;
+                String id = parseIdent();
+                parseVersionPkg();
+                scanner.expect(SEMICOLON);
+
+                if (pkg == null) {
+                    pkg = id;
+                    pkgPrefix = id + "/";
+                } else {
+                    env.error(where, "package.repeated");
+                }
+                debugScan("[Parser.parseJasmPackages] {PARSED} package-prefix: " + pkgPrefix + " ");
+            }
+        } catch (Scanner.SyntaxError e) {
+            recoverFile();
+        }
+        // skip bogus semi colons
+        while (scanner.token == SEMICOLON) {
+            scanner.scan();
+        }
+
+        // checks that we compile module or package compilation unit
+        if (scanner.token == Token.EOF) {
+            env.traceln("Scanner:  EOF");
+            String sourceName = env.getSimpleInputFileName();
+            int mod = ACC_INTERFACE | ACC_ABSTRACT;
+
+            // package-info
+            if (sourceName.endsWith("package-info.jasm")) {
+                env.traceln("Creating \"package-info.jasm\": package: " + pkg + " " + currentCFV.asString());
+
+                if (cd == null) {
+                    cd = new ClassData(env, currentCFV.clone());
+                    pool = cd.pool;
+                } else {
+                    cd.cfv = currentCFV.clone();
+                }
+                ConstCell me = pool.FindCellClassByName(pkgPrefix + "package-info");
+
+                // Interface package-info should be marked synthetic and abstract
+                if (currentCFV.major_version() > 49) {
+                    mod |= SYNTHETIC_ATTRIBUTE;
+                }
+                cd.init(mod, me, new ConstCell(0), null);
+
+                if (pkgAnnttns != null) {
+                    cd.addAnnotations(pkgAnnttns);
+                }
+
+                endClass();
+            }
+            return;
+        }
+
+        if (pkg == null && pkgAnnttns != null) { // RemoveModules
+            clsAnnttns = pkgAnnttns;
+            pkgAnnttns = null;
+        }
+    }
+
+    /**
+     * Parse an Jasm file.
+     */
+    void parseFile() {
+        try {
+            // First, parse any package identifiers (and associated package annotations)
+            parseJasmPackages();
+
+            while (scanner.token != Token.EOF) {
+                // Second, parse any class identifiers (and associated class annotations)
+                try {
+                    // Parse annotations
+                    if (scanner.token == Token.ANNOTATION) {
+                        if (cd == null) {
+                            cd = new ClassData(env, currentCFV.clone());
+                            pool = cd.pool;
+                        } else {
+                            cd.cfv = currentCFV.clone();
+                        }
+                        clsAnnttns = annotParser.scanAnnotations();
+                    }
+
+                    // Parse class modifiers
+                    int mod = scanModifiers();
+                    if (mod == 0) {
+                        switch (scanner.token) {
+                            case OPEN:
+                            case MODULE:
+                            case CLASS:
+                            case CPINDEX:
+                            case STRINGVAL:
+                            case IDENT:
+                                // this is a class declaration anyway
+                                break;
+                            case SEMICOLON:
+                                // Bogus semi colon
+                                scanner.scan();
+                                continue;
+                            default:
+                                // no class declaration found
+                                debugScan(" [Parser.parseFile]: ");
+                                env.error(scanner.pos, "toplevel.expected");
+                                throw new Scanner.SyntaxError();
+                        }
+                    } else if (Modifiers.isInterface(mod) && (scanner.token != Token.CLASS)) {
+                        // rare syntactic sugar:
+                        // interface <ident> == abstract interface class <ident>
+                        mod |= ACC_ABSTRACT;
+                    }
+                    if (scanner.token == Token.MODULE || scanner.token == Token.OPEN)
+                        parseModule();
+                    else
+                        parseClass(mod);
+                    clsAnnttns = null;
+
+                } catch (Scanner.SyntaxError e) {
+                    // KTL
+                    env.traceln("^^^^^^^ Syntax Error ^^^^^^^^^^^^");
+                    if (scanner.debugFlag)
+                        e.printStackTrace();
+                    if (e.isFatal()) {
+                        break;
+                    }
+                    recoverFile();
+                }
+            }
+        } catch (IOException e) {
+            env.error(scanner.pos, "io.exception", env.getSimpleInputFileName());
+        } catch (Error er) {
+            er.printStackTrace();
+        }
+    } //end parseFile
+
+    @FunctionalInterface
+    interface NameSupplier {
+        String get() throws IOException;
+    }
+
+    @FunctionalInterface
+    interface Method {
+        void call() throws IOException;
+    }
+
+    /**
+     * The main compile error for the parser
+     */
+    static class CompilerError extends Error {
+
+        CompilerError(String message) {
+            super(message);
+        }
+    }
+}  //end Parser
diff a/test/lib/org/openjdk/asmtools/jasm/ParserAnnotation.java b/test/lib/org/openjdk/asmtools/jasm/ParserAnnotation.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/ParserAnnotation.java
@@ -0,0 +1,1085 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jasm;
+
+import org.openjdk.asmtools.jasm.TypeAnnotationTargetInfoData.*;
+
+import static org.openjdk.asmtools.jasm.JasmTokens.AnnotationType.isInvisibleAnnotationToken;
+import static org.openjdk.asmtools.jasm.TypeAnnotationTypes.*;
+import static org.openjdk.asmtools.jasm.JasmTokens.*;
+import static org.openjdk.asmtools.jasm.ConstantPool.*;
+import static org.openjdk.asmtools.jasm.Tables.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.TreeMap;
+
+/**
+ * ParserAnnotation
+ *
+ * ParserAnnotation is a parser class owned by Parser.java. It is primarily responsible
+ * for parsing Annotations (for classes, methods or fields).
+ *
+ * ParserAnnotation can parse the different types of Annotation Attributes:
+ * Runtime(In)Visible Annotations (JDK 6+) Default Annotations (JDK 6+)
+ * Runtime(In)VisibleParameter Annotations (JDK 7+) Runtime(In)VisibleType Annotations
+ * (JSR308, JDK8+)
+ */
+public class ParserAnnotation extends ParseBase {
+
+    /*-------------------------------------------------------- */
+    /* Annotation Inner Classes */
+    /**
+     * AnnotationElemValue
+     *
+     * Used to store Annotation values
+     */
+    static class AnnotationElemValue implements Data {
+
+        AnnotationData annotation;
+
+        AnnotationElemValue(AnnotationData annotation) {
+            this.annotation = annotation;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeByte('@');
+            annotation.write(out);
+        }
+
+        @Override
+        public int getLength() {
+            return 1 + annotation.getLength();
+        }
+    }
+
+    /**
+     * ClassElemValue
+     *
+     * Annotation Element value referring to a class
+     */
+    static class ClassElemValue implements Data {
+
+        ConstCell indx;
+
+        ClassElemValue(ConstCell indx) {
+            this.indx = indx;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeByte('c');
+            indx.write(out);
+        }
+
+        @Override
+        public int getLength() {
+            return 3;
+        }
+    }
+
+    /**
+     * ArrayElemValue
+     *
+     * Annotation Element value referring to an Array
+     */
+    static class ArrayElemValue implements Data {
+
+        ArrayList<Data> elemValues;
+        int arrayLength = 0;
+
+        ArrayElemValue() {
+            this.elemValues = new ArrayList<>();
+        }
+
+        void add(Data elemValue) {
+            elemValues.add(elemValue);
+            arrayLength += elemValue.getLength();
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeByte('[');
+            out.writeShort(elemValues.size());
+
+            for (Data eval : elemValues) {
+                eval.write(out);
+            }
+        }
+
+        @Override
+        public int getLength() {
+            return 3 + arrayLength;
+        }
+    }
+
+    /**
+     * ConstElemValue
+     *
+     * Annotation Element value referring to a Constant
+     */
+    static class ConstElemValue implements Data {
+
+        char tag;
+        ConstCell indx;
+
+        ConstElemValue(char tag, ConstCell indx) {
+            this.tag = tag;
+            this.indx = indx;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeByte(tag);
+            indx.write(out);
+        }
+
+        @Override
+        public int getLength() {
+            return 3;
+        }
+    }
+
+    /**
+     * EnumElemValue
+     *
+     * Element Value for Enums
+     */
+    static class EnumElemValue implements Data {
+
+        ConstCell type;
+        ConstCell value;
+
+        EnumElemValue(ConstCell type, ConstCell value) {
+            this.type = type;
+            this.value = value;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeByte('e');
+            type.write(out);
+            value.write(out);
+        }
+
+        @Override
+        public int getLength() {
+            return 5;
+        }
+    }
+
+    /**
+     * local handles on the scanner, main parser, and the error reporting env
+     */
+    private static TTVis ttVisitor;
+
+    protected ParserAnnotation(Scanner scanner, Parser parser, Environment env) {
+        super.init(scanner, parser, env);
+        ttVisitor = new TTVis();
+        ttVisitor.init(env, scanner);
+    }
+
+    protected void scanParamName(int totalParams, int paramNum, MethodData curMethod) throws IOException {
+        debugScan(" - - - > [ParserAnnotation.scanParamName]: Begin ");
+        scanner.scan();
+        scanner.expect(Token.LBRACE);
+        // First scan the Name (String, or CPX to name)
+        ConstCell nameCell;
+        if ((scanner.token == Token.IDENT) || scanner.checkTokenIdent()) {
+            // Got a Class Name
+            nameCell = parser.parseName();
+        } else if (scanner.token == Token.CPINDEX) {
+            int cpx = scanner.intValue;
+            nameCell = parser.pool.getCell(cpx);
+            // check the constant
+            ConstValue nameCellValue = nameCell.ref;
+            if (!(nameCellValue instanceof ConstValue_String)) {
+                // throw an error
+                env.error(scanner.pos, "paramname.constnum.invaltype", cpx);
+                throw new Scanner.SyntaxError();
+            }
+
+        } else {
+            // throw scan error - unexpected token
+            env.error(scanner.pos, "paramname.token.unexpected", scanner.stringValue);
+            throw new Scanner.SyntaxError();
+        }
+
+        // Got the name cell. Next, scan the access flags
+        int mod = parser.scanModifiers();
+
+        scanner.expect(Token.RBRACE);
+
+        curMethod.addMethodParameter(totalParams, paramNum, nameCell, mod);
+
+        debugScan(" - - - > [ParserAnnotation.scanParamName]: End ");
+    }
+
+    /**
+     * The main entry for parsing an annotation list.
+     *
+     * @return An ArrayList of parsed annotations
+     * @throws IOException
+     */
+    ArrayList<AnnotationData> scanAnnotations() throws IOException {
+        ArrayList<AnnotationData> list = new ArrayList<>();
+        while (scanner.token == Token.ANNOTATION) {
+            if ( JasmTokens.AnnotationType.isAnnotationToken(scanner.stringValue)) {
+                list.add(parseAnnotation());
+            } else if (JasmTokens.AnnotationType.isTypeAnnotationToken(scanner.stringValue)) {
+                list.add(parseTypeAnnotation());
+            } else {
+                return null;
+            }
+        }
+        if (list.size() > 0) {
+            return list;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * parseDefaultAnnotation
+     *
+     * parses a default Annotation attribute
+     *
+     * @return the parsed Annotation Attribute
+     * @throws org.openjdk.asmtools.jasm.Scanner.SyntaxError
+     * @throws IOException
+     */
+    protected DefaultAnnotationAttr parseDefaultAnnotation() throws Scanner.SyntaxError, IOException {
+        scanner.scan();
+        DefaultAnnotationAttr attr = null;
+        Data value = null;
+        scanner.expect(Token.LBRACE);
+
+        if ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) {
+            value = scanAnnotationData("default");
+        }
+        scanner.expect(Token.RBRACE);
+        attr = new DefaultAnnotationAttr(parser.cd,
+                AttrTag.ATT_AnnotationDefault.parsekey(),
+                value);
+        return attr;
+    }
+
+    /**
+     * parseParamAnnots
+     *
+     * Parses Parameter Annotations attributes.
+     *
+     * @param _totalParams
+     * @param curMethod
+     * @throws org.openjdk.asmtools.jasm.Scanner.SyntaxError
+     * @throws IOException
+     */
+    protected void parseParamAnnots(int _totalParams, MethodData curMethod) throws Scanner.SyntaxError, IOException {
+        debugScan(" - - - > [ParserAnnotation.parseParamAnnots]: Begin, totalParams =  " + _totalParams + " ");
+        // The _method thinks there are N+1 params in the signature
+        // (N = total params in the call list) + 1 (return value)
+        int totalParams = _totalParams - 1;
+        TreeMap<Integer, ArrayList<AnnotationData>> pAnnots = new TreeMap<>();
+
+        while (scanner.token == Token.INTVAL) {
+            // Create the Parameter Array for  Param Annotations
+
+            // Do something with Parameter annotations
+            // --------------------
+            // First - validate that the parameter number (integer)
+            // (eg >= 0, < numParams, and param num is not previously set)
+            int paramNum = scanner.intValue;
+            Integer iParamNum = Integer.valueOf(paramNum);
+            if (paramNum < 0 || paramNum >= totalParams) {
+                //invalid Parameter number.  Throw an error.
+                env.error(scanner.pos, "invalid.paramnum", paramNum);
+            }
+            if (pAnnots.get(iParamNum) != null) {
+                // paramter is already populated with annotations/pnames, Throw an error.
+                env.error(scanner.pos, "duplicate.paramnum", paramNum);
+            }
+            // 2nd - Parse the COLON (invalid if not present)
+            scanner.scan();
+            scanner.expect(Token.COLON);
+
+            // 3rd - parse either an optional ParamName, or a list of annotations
+            if (scanner.token == Token.PARAM_NAME) {
+                //parse the ParamName
+                scanParamName(totalParams, iParamNum, curMethod);
+            }
+
+            // 4th - parse each Annotation (followed by comma, followed by annotation
+            //       assign array of annotations to param array
+            if (scanner.token == Token.ANNOTATION) {
+                ArrayList<AnnotationData> pAnnot = scanAnnotations();
+                pAnnots.put(iParamNum, pAnnot);
+
+                for (AnnotationData data : pAnnot) {
+                    curMethod.addParamAnnotation(totalParams, paramNum, data);
+                }
+            }
+
+        }
+    }
+
+    /* ************************* Private Members  *************************** */
+    /**
+     * parseTypeAnnotation
+     *
+     * parses an individual annotation.
+     *
+     * @return a parsed annotation.
+     * @throws IOException
+     */
+    private AnnotationData parseTypeAnnotation() throws Scanner.SyntaxError, IOException {
+        boolean invisible = isInvisibleAnnotationToken(scanner.stringValue);
+        scanner.scan();
+        debugScan("     [ParserAnnotation.parseTypeAnnotation]: id = " + scanner.stringValue + " ");
+        String annoName = "L" + scanner.stringValue + ";";
+        TypeAnnotationData anno = new TypeAnnotationData(parser.pool.FindCellAsciz(annoName), invisible);
+        scanner.scan();
+        debugScan("     [ParserAnnotation.parseTypeAnnotation]:new type annotation: " + annoName + " ");
+
+        scanner.expect(Token.LBRACE);
+
+        // Scan the usual annotation data
+        _scanAnnotation(anno);
+
+        // scan the Target (u1: target_type, union{...}: target_info)
+        _scanTypeTarget(anno);
+
+        if( scanner.token != Token.RBRACE ) {
+            // scan the Location (type_path: target_path)
+            _scanTargetPath(anno);
+        }
+
+        scanner.expect(Token.RBRACE);
+        return anno;
+    }
+
+    /**
+     * scanAnnotation
+     *
+     * parses an individual annotation.
+     *
+     * @return a parsed annotation.
+     * @throws IOException
+     */
+    private AnnotationData parseAnnotation() throws Scanner.SyntaxError, IOException {
+        debugScan(" - - - > [ParserAnnotation.parseAnnotation]: Begin ");
+        boolean invisible = isInvisibleAnnotationToken(scanner.stringValue);
+        scanner.scan();
+        String annoName = "L" + scanner.stringValue + ";";
+
+        AnnotationData anno = new AnnotationData(parser.pool.FindCellAsciz(annoName), invisible);
+        scanner.scan();
+        debugScan("[ParserAnnotation.parseAnnotation]: new annotation: " + annoName);
+        _scanAnnotation(anno);
+
+        return anno;
+    }
+
+    /**
+     * _scanAnnotation
+     *
+     * parses an individual annotation-data.
+     *
+     * @return a parsed annotation.
+     * @throws IOException
+     */
+    private void _scanAnnotation(AnnotationData annotData) throws Scanner.SyntaxError, IOException {
+        debugScan(" - - - > [ParserAnnotation._scanAnnotation]: Begin");
+        scanner.expect(Token.LBRACE);
+
+        while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) {
+            ConstCell nameCell = parser.parseName();
+            scanner.expect(Token.ASSIGN);
+
+            ConstValue cellref = nameCell.ref;
+            if (cellref.tag != ConstType.CONSTANT_UTF8) {
+                throw new Scanner.SyntaxError();
+            }
+            String name = ((ConstValue_String) cellref)._toString();
+            debugScan("     [ParserAnnotation._scanAnnotation]: Annot - Field Name: " + name);
+            Data data = scanAnnotationData(name);
+            annotData.add(new AnnotationData.ElemValuePair(nameCell, data));
+
+            // consume tokens inbetween annotation fields
+            if (scanner.token == Token.COMMA) {
+                scanner.scan();
+            }
+        }
+        scanner.expect(Token.RBRACE);
+    }
+
+    /**
+     * _scanAnnotation
+     *
+     * parses an individual annotation-data.
+     *
+     * @return a parsed annotation.
+     * @throws IOException
+     */
+    private void _scanTypeTarget(TypeAnnotationData annotData) throws Scanner.SyntaxError, IOException {
+        debugScan("     [ParserAnnotation._scanTypeTarget]: Begin ");
+        scanner.expect(Token.LBRACE);
+
+        //Scan the target_type and the target_info
+        scanner.expect(Token.IDENT);
+        debugScan("     [ParserAnnotation._scanTypeTarget]: TargetType: " + scanner.idValue);
+        ETargetType targetType = ETargetType.getTargetType(scanner.idValue);
+        if (targetType == null) {
+            env.error(scanner.pos, "incorrect.typeannot.target", scanner.idValue);
+            throw new Scanner.SyntaxError();
+        }
+
+        debugScan("     [ParserAnnotation._scanTypeTarget]: Got TargetType: " + targetType);
+
+        if (ttVisitor.scanner == null) {
+            ttVisitor.scanner = scanner;
+        }
+        ttVisitor.visitExcept(targetType);
+
+        annotData.targetInfo = ttVisitor.getTargetInfo();
+        annotData.targetType = targetType;
+        debugScan("     [ParserAnnotation._scanTypeTarget]: Got TargetInfo: " + annotData.targetInfo);
+
+        scanner.expect(Token.RBRACE);
+    }
+
+    /**
+     * TTVis
+     *
+     * Target Type visitor, used for constructing the target-info within a type
+     * annotation. visitExcept() is the entry point. ti is the constructed target info.
+     */
+    private static class TTVis extends TypeAnnotationTypes.TypeAnnotationTargetVisitor {
+
+        private TypeAnnotationTargetInfoData ti;
+        private IOException IOProb;
+        private Scanner.SyntaxError SyProb;
+        private Scanner scanner;
+        private Environment env;
+
+        public TTVis() {
+            super();
+            reset();
+        }
+
+        public void init(Environment en, Scanner scn) {
+            if (scanner == null) {
+                scanner = scn;
+            }
+            if (env == null) {
+                env = en;
+            }
+        }
+
+        public final void reset() {
+            ti = null;
+            IOProb = null;
+            SyProb = null;
+        }
+
+        //This is the entry point for a visitor that tunnels exceptions
+        public void visitExcept(ETargetType tt) throws IOException, Scanner.SyntaxError {
+            IOProb = null;
+            SyProb = null;
+            ti = null;
+
+            visit(tt);
+
+            if (IOProb != null) {
+                throw IOProb;
+            }
+
+            if (SyProb != null) {
+                throw SyProb;
+            }
+        }
+
+        public TypeAnnotationTargetInfoData getTargetInfo() {
+            return ti;
+        }
+
+        // this fn gathers intvals, and tunnels any exceptions thrown by
+        // the scanner
+        private int scanIntVal(ETargetType tt) {
+            int ret = -1;
+            if (scanner.token == Token.INTVAL) {
+                ret = scanner.intValue;
+                try {
+                    scanner.scan();
+                } catch (IOException e) {
+                    IOProb = e;
+                } catch (Scanner.SyntaxError e) {
+                    SyProb = e;
+                }
+            } else {
+                env.error(scanner.pos, "incorrect.typeannot.targtype.int", tt.parseKey(), scanner.token);
+                SyProb = new Scanner.SyntaxError();
+            }
+            return ret;
+        }
+
+        // this fn gathers intvals, and tunnels any exceptions thrown by
+        // the scanner
+        private String scanStringVal(ETargetType tt) {
+            String ret = "";
+            if (scanner.token == Token.STRINGVAL) {
+                ret = scanner.stringValue;
+                try {
+                    scanner.scan();
+                } catch (IOException e) {
+                    IOProb = e;
+                } catch (Scanner.SyntaxError e) {
+                    SyProb = e;
+                }
+            } else {
+                env.error(scanner.pos, "incorrect.typeannot.targtype.string", tt.parseKey(), scanner.token);
+                SyProb = new Scanner.SyntaxError();
+            }
+            return ret;
+        }
+
+        // this fn gathers intvals, and tunnels any exceptions thrown by
+        // the scanner
+        private void scanBrace(boolean left) {
+            try {
+                scanner.expect(left ? Token.LBRACE : Token.RBRACE);
+            } catch (IOException e) {
+                IOProb = e;
+            } catch (Scanner.SyntaxError e) {
+                SyProb = e;
+            }
+        }
+
+        private boolean error() {
+            return IOProb != null || SyProb != null;
+        }
+
+        @Override
+        public void visit_type_param_target(ETargetType tt) {
+            env.traceln("Type Param Target: ");
+            int byteval = scanIntVal(tt); // param index
+            if (!error()) {
+                ti = new TypeAnnotationTargetInfoData.type_parameter_target(tt, byteval);
+            }
+        }
+
+        @Override
+        public void visit_supertype_target(ETargetType tt) {
+            env.traceln("SuperType Target: ");
+            int shortval = scanIntVal(tt); // type index
+            if (!error()) {
+                ti = new TypeAnnotationTargetInfoData.supertype_target(tt, shortval);
+            }
+        }
+
+        @Override
+        public void visit_typeparam_bound_target(ETargetType tt) {
+            env.traceln("TypeParam Bound Target: ");
+            int byteval1 = scanIntVal(tt); // param index
+            if (error()) {
+                return;
+            }
+            int byteval2 = scanIntVal(tt); // bound index
+            if (error()) {
+                return;
+            }
+            ti = new TypeAnnotationTargetInfoData.type_parameter_bound_target(tt, byteval1, byteval2);
+        }
+
+        @Override
+        public void visit_empty_target(ETargetType tt) {
+            env.traceln("Empty Target: ");
+            if (!error()) {
+                ti = new TypeAnnotationTargetInfoData.empty_target(tt);
+            }
+        }
+
+        @Override
+        public void visit_methodformalparam_target(ETargetType tt) {
+            env.traceln("MethodParam Target: ");
+            int byteval = scanIntVal(tt); // param index
+            if (!error()) {
+                ti = new formal_parameter_target(tt, byteval);
+            }
+        }
+
+        @Override
+        public void visit_throws_target(ETargetType tt) {
+            env.traceln("Throws Target: ");
+            int shortval = scanIntVal(tt); // exception index
+            if (!error()) {
+                ti = new throws_target(tt, shortval);
+            }
+        }
+
+        @Override
+        public void visit_localvar_target(ETargetType tt) {
+            env.traceln("LocalVar Target: ");
+            localvar_target locvartab = new localvar_target(tt, 0);
+            ti = locvartab;
+
+            while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) {
+                // consume the left brace
+                scanBrace(true);
+                if (error()) {
+                    return;
+                }
+                // scan the local var triple
+                int shortval1 = scanIntVal(tt); // startPC
+                if (error()) {
+                    return;
+                }
+                int shortval2 = scanIntVal(tt); // length
+                if (error()) {
+                    return;
+                }
+                int shortval3 = scanIntVal(tt); // CPX
+                locvartab.addEntry(shortval1, shortval2, shortval3);
+                scanBrace(false);
+                if (error()) {
+                    return;
+                }
+            }
+        }
+
+        @Override
+        public void visit_catch_target(ETargetType tt) {
+            env.traceln("Catch Target: ");
+            int shortval = scanIntVal(tt); // catch index
+
+            ti = new catch_target(tt, shortval);
+        }
+
+        @Override
+        public void visit_offset_target(ETargetType tt) {
+            env.traceln("Offset Target: ");
+            int shortval = scanIntVal(tt); // offset index
+            if (!error()) {
+                ti = new offset_target(tt, shortval);
+            }
+        }
+
+        @Override
+        public void visit_typearg_target(ETargetType tt) {
+            env.traceln("TypeArg Target: ");
+            int shortval = scanIntVal(tt); // offset
+            if (error()) {
+                return;
+            }
+            int byteval = scanIntVal(tt); // type index
+            if (error()) {
+                return;
+            }
+            ti = new type_argument_target(tt, shortval, byteval);
+        }
+
+    }
+
+    /**
+     * _scanTargetPath
+     *
+     * parses and fills the type_path structure (4.7.20.2)
+     *
+     * type_path {
+     *     u1 path_length;
+     *     {   u1 type_path_kind;
+     *         u1 type_argument_index;
+     *     } path[path_length];
+     * }
+     *
+     * @throws Scanner.SyntaxError, IOException
+     */
+    private void _scanTargetPath(TypeAnnotationData annotData) throws Scanner.SyntaxError, IOException {
+        // parse the location info
+        scanner.expect(Token.LBRACE);
+
+        while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) {
+            TypePathEntry tpe = _scanTypePathEntry();
+            annotData.addTypePathEntry(tpe);
+            // throw away comma
+            if (scanner.token == Token.COMMA) {
+                scanner.scan();
+            }
+        }
+
+        scanner.expect(Token.RBRACE);
+    }
+
+    /**
+     * _scanTypeLocation
+     *
+     * parses a path entry of the type_path.
+     *
+     * {   u1 type_path_kind;
+     *     u1 type_argument_index;
+     * }
+     *
+     * @return a parsed type path.
+     * @throws Scanner.SyntaxError, IOException
+     */
+    private TypePathEntry _scanTypePathEntry() throws Scanner.SyntaxError, IOException {
+        TypePathEntry tpe;
+
+        if ( (scanner.token != Token.EOF) && scanner.token.possibleTypePathKind() ) {
+            EPathKind pathKind = EPathKind.getPathKind(scanner.stringValue);
+            if (pathKind == EPathKind.TYPE_ARGUMENT) {
+                scanner.scan();
+                // need to scan the index
+                // Take the form:  TYPE_ARGUMENT{#}
+                scanner.expect(Token.LBRACE);
+                int index = 0;
+                if ((scanner.token != Token.EOF) && (scanner.token == Token.INTVAL)) {
+                    index = scanner.intValue;
+                    scanner.scan();
+                } else {
+                    // incorrect Arg index
+                    env.error(scanner.pos, "incorrect.typeannot.pathentry.argindex", scanner.token);
+                    throw new Scanner.SyntaxError();
+                }
+                tpe = new TypePathEntry(pathKind, index);
+                scanner.expect(Token.RBRACE);
+            } else {
+                tpe = new TypePathEntry(pathKind, 0);
+                scanner.scan();
+            }
+        } else {
+            // unexpected Type Path
+            env.error(scanner.pos, "incorrect.typeannot.pathentry", scanner.token);
+            throw new Scanner.SyntaxError();
+        }
+
+        return tpe;
+    }
+
+    /**
+     * scanAnnotationArray
+     *
+     * Scans an Array of annotations.
+     *
+     * @param name Name of the annotation
+     * @return Array Element
+     * @throws IOException if scanning errors exist
+     */
+    private ArrayElemValue scanAnnotationArray(String name) throws IOException {
+        scanner.scan();
+        ArrayElemValue arrayElem = new ArrayElemValue();
+
+        while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) {
+            Data data = scanAnnotationData(name + " {}");
+            arrayElem.add(data);
+
+            // consume tokens inbetween annotation fields
+            if (scanner.token == Token.COMMA) {
+                scanner.scan();
+            }
+        }
+
+        scanner.expect(Token.RBRACE);
+        return arrayElem;
+    }
+
+    /**
+     * scanAnnotationEnum
+     *
+     * Scans an annotation enum val.
+     *
+     * @param name Annotation Name
+     * @return Constant element value for the Class Annotation.
+     * @throws IOException
+     */
+    private Data scanAnnotationClass(String name) throws IOException {
+        Data constVal = null;
+        // scan the next identifier.
+        // if it is an Ident, consume it as the class name.
+        scanner.scan();
+        switch (scanner.token) {
+            case IDENT:
+                env.traceln("[AnnotationParser.scanAnnotationData]:: Constant Class Field: " + name + " = " + scanner.stringValue);
+                //need to encode the stringval as an (internal) descriptor.
+                String desc = parser.encodeClassString(scanner.stringValue);
+
+                // note: for annotations, a class field points to a string with the class descriptor.
+                constVal = new ConstElemValue('c', parser.pool.FindCellAsciz(desc));
+                scanner.scan();
+                break;
+            case CPINDEX:
+                // could be a reference to a class name
+                env.traceln("[AnnotationParser.scanAnnotationData]:: Constant Class Field: " + name + " = " + scanner.stringValue);
+                Integer ConstNmCPX = Integer.valueOf(scanner.stringValue);
+                constVal = new ClassElemValue(parser.pool.getCell(ConstNmCPX));
+                scanner.scan();
+                break;
+            default:
+                env.error(scanner.pos, "incorrect.annot.class", scanner.stringValue);
+                throw new Scanner.SyntaxError();
+        }
+
+        return constVal;
+    }
+
+    /**
+     * scanAnnotationEnum
+     *
+     * Scans an annotation enum val.
+     *
+     * @param name Annotation Name
+     * @return Enumeration Element Value
+     * @throws IOException for scanning errors.
+     */
+    private EnumElemValue scanAnnotationEnum(String name) throws IOException {
+        scanner.scan();
+        EnumElemValue enumval = null;
+        switch (scanner.token) {
+            case IDENT:
+                // could be a string identifying enum class and name
+                String enumClassName = scanner.stringValue;
+                scanner.scan();
+                // could be a string identifying enum class and name
+                switch (scanner.token) {
+                    case IDENT:
+                        // could be a string identifying enum class and name
+                        String enumTypeName = scanner.stringValue;
+                        env.traceln("[AnnotationParser.scanAnnotationEnum]:: Constant Enum Field: " + name + " = " + enumClassName + " " + enumTypeName);
+                        String encodedClass = parser.encodeClassString(enumClassName);
+                        ConstElemValue classConst = new ConstElemValue('s', parser.pool.FindCellAsciz(encodedClass));
+                        ConstElemValue typeConst = new ConstElemValue('s', parser.pool.FindCellAsciz(enumTypeName));
+                        enumval = new EnumElemValue(classConst.indx, typeConst.indx);
+                        scanner.scan();
+                        break;
+
+                    default:
+                        env.error(scanner.pos, "incorrect.annot.enum", scanner.stringValue);
+                        throw new Scanner.SyntaxError();
+                }
+                break;
+            case CPINDEX:
+                Integer typeNmCPX = Integer.valueOf(scanner.stringValue);
+                scanner.scan();
+                //need two indexes to form a proper enum
+                switch (scanner.token) {
+                    case CPINDEX:
+                        Integer ConstNmCPX = Integer.valueOf(scanner.stringValue);
+                        env.traceln("[AnnotationParser.scanAnnotationEnum]:: Enumeration Field: " + name + " = #" + typeNmCPX + " #" + ConstNmCPX);
+                        enumval = new EnumElemValue(parser.pool.getCell(typeNmCPX), parser.pool.getCell(ConstNmCPX));
+                        scanner.scan();
+                        break;
+                    default:
+                        env.error(scanner.pos, "incorrect.annot.enum.cpx");
+                        throw new Scanner.SyntaxError();
+                }
+                break;
+        }
+
+        return enumval;
+    }
+
+    /**
+     * scanAnnotationData
+     *
+     * parses the internals of an annotation.
+     *
+     * @param name Annotation Name
+     * @return a Data data structure containing the annotation data.
+     * @throws IOException for scanning errors.
+     */
+    private Data scanAnnotationData(String name) throws IOException {
+        Data data = null;
+        switch (scanner.token) {
+            // This handles the Annotation types (as normalized in the constant pool)
+            // Some primitive types (Boolean, char, short, byte) are identified by a keyword.
+            case INTVAL:
+                env.traceln("[AnnotationParser.scanAnnotationData]:: Integer Field: " + name + " = " + scanner.intValue);
+                data = new ConstElemValue('I', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, scanner.intValue));
+                scanner.scan();
+                break;
+            case DOUBLEVAL:
+                env.traceln("[AnnotationParser.scanAnnotationData]:: Double Field: " + name + " = " + scanner.doubleValue);
+                double dval = scanner.doubleValue;
+                long ivdal = Double.doubleToLongBits(dval);
+                Long val = ivdal;
+                data = new ConstElemValue('D', parser.pool.FindCell(ConstType.CONSTANT_DOUBLE, val));
+                scanner.scan();
+                break;
+            case FLOATVAL:
+                env.traceln("[AnnotationParser.scanAnnotationData]:: Float Field: " + name + " = " + scanner.floatValue);
+                float fval = scanner.floatValue;
+                int ifval = Float.floatToIntBits(fval);
+                Integer val1 = ifval;
+                data = new ConstElemValue('F', parser.pool.FindCell(ConstType.CONSTANT_FLOAT, val1));
+                scanner.scan();
+                break;
+            case LONGVAL:
+                env.traceln("[AnnotationParser.scanAnnotationData]:: Long Field: " + name + " = " + scanner.longValue);
+                data = new ConstElemValue('J', parser.pool.FindCell(ConstType.CONSTANT_LONG, scanner.longValue));
+                scanner.scan();
+                break;
+            case STRINGVAL:
+                env.traceln("[AnnotationParser.scanAnnotationData]:: String Field: " + name + " = " + scanner.stringValue);
+                data = new ConstElemValue('s', parser.pool.FindCellAsciz(scanner.stringValue));
+                scanner.scan();
+                break;
+            case CLASS:
+                env.traceln("[AnnotationParser.scanAnnotationData]:: Class) keyword: " + scanner.stringValue);
+                data = scanAnnotationClass(name);
+                break;
+            case ENUM:
+                // scan the next two identifiers (eg ident.ident), or 2 CPRefs.
+                // if it is an Ident, use consume it as the class name.
+                env.traceln("[AnnotationParser.scanAnnotationData]:: Enum) keyword: " + scanner.stringValue);
+                data = scanAnnotationEnum(name);
+                break;
+            case IDENT:
+                env.traceln("[AnnotationParser.scanAnnotationData]:: JASM Keyword: (annotation field name: " + name + ") keyword: " + scanner.stringValue);
+                data = scanAnnotationIdent(scanner.stringValue, name);
+                break;
+            case ANNOTATION:
+                env.traceln("[AnnotationParser.scanAnnotationData]:: Annotation Field: " + name + " = " + scanner.stringValue);
+                data = new AnnotationElemValue(parseAnnotation());
+                break;
+            case LBRACE:
+                env.traceln("[AnnotationParser.scanAnnotationData]:: Annotation Array Field: " + name);
+                data = scanAnnotationArray(name);
+                break;
+            default:
+                env.error(scanner.pos, "incorrect.annot.token", scanner.token);
+                throw new Scanner.SyntaxError();
+        }
+
+        return data;
+    }
+
+    /**
+     * scanAnnotationIdent
+     *
+     * parses the identifier of an annotation.
+     *
+     * @param ident Basic Type identifier
+     * @param name Annotation Name
+     * @return Basic Type Annotation data
+     * @throws IOException if scanning errors occur
+     */
+    private Data scanAnnotationIdent(String ident, String name) throws IOException {
+        // Handle JASM annotation Keyword Identifiers
+        Data data;
+        BasicType type = basictype(ident);
+        switch (type) {
+
+            case T_BOOLEAN:
+                // consume the keyword, get the value
+                scanner.scan();
+                switch (scanner.token) {
+                    case INTVAL:
+                        // Handle Boolean value in integer form
+                        env.traceln("Boolean Field: " + name + " = " + scanner.intValue);
+                        Integer val = scanner.intValue;
+                        if (val > 1 || val < 0) {
+                            env.traceln("Warning: Boolean Field: " + name + " value is not 0 or 1, value = " + scanner.intValue);
+                        }
+                        data = new ConstElemValue('Z', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, val));
+                        scanner.scan();
+                        break;
+                    case IDENT:
+                        // handle boolean value with true/false keywords
+                        int val1;
+                        switch (scanner.stringValue) {
+                            case "true":
+                                val1 = 1;
+                                break;
+                            case "false":
+                                val1 = 0;
+                                break;
+                            default:
+                                throw new IOException("Incorrect Annotation (boolean), expected true/false), got \"" + scanner.stringValue + "\".");
+                        }
+                        env.traceln("Boolean Field: " + name + " = " + scanner.stringValue);
+                        data = new ConstElemValue('Z', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, val1));
+                        scanner.scan();
+                        break;
+                    default:
+                        env.error(scanner.pos, "incorrect.annot.bool", scanner.stringValue);
+                        throw new Scanner.SyntaxError();
+                }
+                break;
+            case T_BYTE:
+                // consume the keyword, get the value
+                scanner.scan();
+                switch (scanner.token) {
+                    case INTVAL:
+                        env.traceln("Byte Field: " + name + " = " + scanner.intValue);
+                        Integer val = scanner.intValue;
+                        if (val > 0xFF) {
+                            env.traceln("Warning: Byte Field: " + name + " value is greater than 0xFF, value = " + scanner.intValue);
+                        }
+                        data = new ConstElemValue('B', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, val));
+                        scanner.scan();
+                        break;
+                    default:
+                        env.error(scanner.pos, "incorrect.annot.byte", scanner.stringValue);
+                        throw new Scanner.SyntaxError();
+                }
+                break;
+            case T_CHAR:
+                // consume the keyword, get the value
+                scanner.scan();
+                switch (scanner.token) {
+                    case INTVAL:
+                        env.traceln("Char Field: " + name + " = " + scanner.intValue);
+                        Integer val = scanner.intValue;
+                        // Bounds check?
+                        data = new ConstElemValue('C', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, val));
+                        scanner.scan();
+                        break;
+                    default:
+                        env.error(scanner.pos, "incorrect.annot.char", scanner.stringValue);
+                        throw new Scanner.SyntaxError();
+                }
+                break;
+            case T_SHORT:
+                // consume the keyword, get the value
+                scanner.scan();
+                switch (scanner.token) {
+                    case INTVAL:
+                        env.traceln("Short Field: " + name + " = " + scanner.intValue);
+                        Integer val = scanner.intValue;
+                        if (val > 0xFFFF) {
+                            env.traceln("Warning: Short Field: " + name + " value is greater than 0xFFFF, value = " + scanner.intValue);
+                        }
+                        data = new ConstElemValue('S', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, val));
+                        scanner.scan();
+                        break;
+                    default:
+                        env.error(scanner.pos, "incorrect.annot.short", scanner.stringValue);
+                        throw new Scanner.SyntaxError();
+                }
+                break;
+            default:
+                env.error(scanner.pos, "incorrect.annot.keyword", ident);
+                throw new Scanner.SyntaxError();
+        }
+        return data;
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/ParserCP.java b/test/lib/org/openjdk/asmtools/jasm/ParserCP.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/ParserCP.java
@@ -0,0 +1,665 @@
+/*
+ * Copyright (c) 1996, 2021, 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 org.openjdk.asmtools.jasm;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.function.BiFunction;
+
+import static org.openjdk.asmtools.jasm.JasmTokens.Token;
+import static org.openjdk.asmtools.jasm.Tables.*;
+
+/**
+ * ParserCP
+ *
+ * ParseCP is a parser class owned by Parser.java. It is primarily responsible for parsing
+ * the constant pool and constant declarations.
+ */
+public class ParserCP extends ParseBase {
+
+    /**
+     * Stop parsing a source file immediately and interpret any issue as an error
+     */
+    private boolean exitImmediately = false;
+
+    /**
+     * local handles on the scanner, main parser, and the error reporting env
+     */
+    /**
+     * Visitor object
+     */
+    private ParserCPVisitor pConstVstr;
+    /**
+     * counter of left braces
+     */
+    private int lbrace = 0;
+
+
+    /**
+     * main constructor
+     *
+     * @param scanner
+     * @param parser
+     * @param env
+     */
+    protected ParserCP(Scanner scanner, Parser parser, Environment env) {
+        super.init(scanner, parser, env);
+        pConstVstr = new ParserCPVisitor();
+    }
+
+    /**
+     * In particular cases it's necessary to interpret a warning issue as an error and
+     * stop parsing a source file immediately
+     * cpParser.setExitImmediately(true);
+     * çparseConstRef(...);
+     * cpParser.setExitImmediately(false);
+     */
+    public void setExitImmediately(boolean exitImmediately) {
+        this.exitImmediately = exitImmediately;
+    }
+
+    public boolean isExitImmediately() {
+        return exitImmediately;
+    }
+
+    /**
+     * ParserCPVisitor
+     *
+     * This inner class overrides a constant pool visitor to provide specific parsing
+     * instructions (per method) for each type of Constant.
+     *
+     * Note: since the generic visitor throws no exceptions, this derived class tunnels
+     * the exceptions, rethrown in the visitEcept method.
+     */
+    class ParserCPVisitor extends ConstantPool.CPTagVisitor<ConstantPool.ConstValue> {
+
+        private IOException IOProb;
+        private Scanner.SyntaxError SyProb;
+
+
+        public ParserCPVisitor() {
+            IOProb = null;
+            SyProb = null;
+        }
+
+        //This is the entry point for a visitor that tunnels exceptions
+        public ConstantPool.ConstValue visitExcept(ConstType tag) throws IOException, Scanner.SyntaxError {
+            IOProb = null;
+            SyProb = null;
+            debugStr("------- [ParserCPVisitor.visitExcept]: ");
+            ConstantPool.ConstValue ret = visit(tag);
+
+            if (IOProb != null) {
+                throw IOProb;
+            }
+
+            if (SyProb != null) {
+                throw SyProb;
+            }
+
+            return ret;
+        }
+
+        @Override
+        public ConstantPool.ConstValue visitUTF8(ConstType tag) {
+            debugStr("------- [ParserCPVisitor.visitUTF8]: ");
+            try {
+                scanner.expect(Token.STRINGVAL);
+            } catch (IOException e) {
+                IOProb = e;
+            }
+            ConstantPool.ConstValue_String obj
+                    = new ConstantPool.ConstValue_String(scanner.stringValue);
+            return obj;
+        }
+
+        @Override
+        public ConstantPool.ConstValue visitInteger(ConstType tag) {
+            debugStr("------- [ParserCPVisitor.visitInteger]: ");
+            ConstantPool.ConstValue_Integer obj;
+            int v = 0;
+            try {
+                if (scanner.token == Token.BITS) {
+                    scanner.scan();
+                    scanner.inBits = true;
+                }
+                v = scanner.intValue * scanner.sign;
+                scanner.expect(Token.INTVAL);
+            } catch (IOException e) {
+                IOProb = e;
+            }
+            obj = new ConstantPool.ConstValue_Integer(tag, v);
+            return obj;
+        }
+
+        @Override
+        public ConstantPool.ConstValue visitLong(ConstType tag) {
+            debugStr("------- [ParserCPVisitor.visitLong]: ");
+            ConstantPool.ConstValue_Long obj = null;
+            try {
+                long v;
+                if (scanner.token == Token.BITS) {
+                    scanner.scan();
+                    scanner.inBits = true;
+                }
+                switch (scanner.token) {
+                    case INTVAL:
+                        v = scanner.intValue;
+                        break;
+                    case LONGVAL:
+                        v = scanner.longValue;
+                        break;
+                    default:
+                        env.error(scanner.prevPos, "token.expected", "Integer");
+                        throw new Scanner.SyntaxError();
+                }
+                obj = new ConstantPool.ConstValue_Long(tag, v * scanner.sign);
+                scanner.scan();
+            } catch (IOException e) {
+                IOProb = e;
+            } catch (Scanner.SyntaxError e) {
+                SyProb = e;
+            }
+            return obj;
+        }
+
+        @Override
+        public ConstantPool.ConstValue visitFloat(ConstType tag) {
+            debugStr("------- [ParserCPVisitor.visitFloat]: ");
+            ConstantPool.ConstValue_Integer obj = null;
+            try {
+                int v;
+                float f;
+                scanner.inBits = false;  // this needs to be initialized for each float!
+                if (scanner.token == Token.BITS) {
+                    scanner.scan();
+                    scanner.inBits = true;
+                }
+i2f:            {
+                    switch (scanner.token) {
+                        case INTVAL:
+                            if (scanner.inBits) {
+                                v = scanner.intValue;
+                                break i2f;
+                            } else {
+                                f = (float) scanner.intValue;
+                                break;
+                            }
+                        case FLOATVAL:
+                            f = scanner.floatValue;
+                            break;
+                        case DOUBLEVAL:
+                            f = (float) scanner.doubleValue; // to be excluded?
+                            break;
+                        case INF:
+                            f = Float.POSITIVE_INFINITY;
+                            break;
+                        case NAN:
+                            f = Float.NaN;
+                            break;
+                        default:
+                            env.traceln("token=" + scanner.token);
+                            env.error(scanner.pos, "token.expected", "<Float>");
+                            throw new Scanner.SyntaxError();
+                    }
+                    v = Float.floatToIntBits(f);
+                }
+                if (scanner.sign == -1) {
+                    v = v ^ 0x80000000;
+                }
+                obj = new ConstantPool.ConstValue_Integer(tag, v);
+                scanner.scan();
+            } catch (IOException e) {
+                IOProb = e;
+            } catch (Scanner.SyntaxError e) {
+                SyProb = e;
+            }
+            return obj;
+        }
+
+        @Override
+        public ConstantPool.ConstValue visitDouble(ConstType tag) {
+            debugStr("------- [ParserCPVisitor.visitDouble]: ");
+            ConstantPool.ConstValue_Long obj = null;
+            try {
+                long v;
+                double d;
+                if (scanner.token == Token.BITS) {
+                    scanner.scan();
+                    scanner.inBits = true;
+                }
+d2l:            {
+                    switch (scanner.token) {
+                        case INTVAL:
+                            if (scanner.inBits) {
+                                v = scanner.intValue;
+                                break d2l;
+                            } else {
+                                d = scanner.intValue;
+                                break;
+                            }
+                        case LONGVAL:
+                            if (scanner.inBits) {
+                                v = scanner.longValue;
+                                break d2l;
+                            } else {
+                                d = (double) scanner.longValue;
+                                break;
+                            }
+                        case FLOATVAL:
+                            d = scanner.floatValue;
+                            break;
+                        case DOUBLEVAL:
+                            d = scanner.doubleValue;
+                            break;
+                        case INF:
+                            d = Double.POSITIVE_INFINITY;
+                            break;
+                        case NAN:
+                            d = Double.NaN;
+                            break;
+                        default:
+                            env.error(scanner.pos, "token.expected", "Double");
+                            throw new Scanner.SyntaxError();
+                    }
+                    v = Double.doubleToLongBits(d);
+                }
+                if (scanner.sign == -1) {
+                    v = v ^ 0x8000000000000000L;
+                }
+                obj = new ConstantPool.ConstValue_Long(tag, v);
+                scanner.scan();
+            } catch (IOException e) {
+                IOProb = e;
+            } catch (Scanner.SyntaxError e) {
+                SyProb = e;
+            }
+            return obj;
+        }
+
+        private ConstantPool.ConstCell visitName(ConstType tag) {
+            debugStr("------- [ParserCPVisitor.visitName]: ");
+            ConstantPool.ConstCell obj = null;
+            try {
+                obj = parser.parseName();
+            } catch (IOException e) {
+                IOProb = e;
+            }
+            return obj;
+        }
+
+        @Override
+        public ConstantPool.ConstValue visitMethodtype(ConstType tag) {
+            debugStr("------- [ParserCPVisitor.visitMethodtype]: ");
+            ConstantPool.ConstValue_Cell obj = null;
+            ConstantPool.ConstCell cell = visitName(tag);
+            if (IOProb == null) {
+                obj = new ConstantPool.ConstValue_Cell(tag, cell);
+            }
+            return obj;
+        }
+
+        @Override
+        public ConstantPool.ConstValue visitString(ConstType tag) {
+            debugStr("------- [ParserCPVisitor.visitString]: ");
+            ConstantPool.ConstValue_Cell obj = null;
+            ConstantPool.ConstCell cell = visitName(tag);
+            if (IOProb == null) {
+                obj = new ConstantPool.ConstValue_Cell(tag, cell);
+            }
+            return obj;
+        }
+
+        @Override
+        public ConstantPool.ConstValue visitClass(ConstType tag) {
+            debugStr("------- [ParserCPVisitor.visitClass]: ");
+            ConstantPool.ConstValue_Cell obj = null;
+            try {
+                ConstantPool.ConstCell cell = parser.parseClassName(true);
+                obj = new ConstantPool.ConstValue_Cell(tag, cell);
+            } catch (IOException e) {
+                IOProb = e;
+            }
+            return obj;
+        }
+
+        @Override
+        public ConstantPool.ConstValue visitMethodhandle(ConstType tag) {
+            debugStr("------- [ParserCPVisitor.visitMethodHandle]: ");
+            ConstantPool.ConstValue_Pair obj = null;
+            try {
+                ConstantPool.ConstCell refCell;
+                ConstantPool.ConstCell subtagCell;
+                SubTag subtag;
+                // MethodHandle    [INVOKESUBTAG|INVOKESUBTAG_INDEX] :    CONSTANT_FIELD | [FIELDREF|METHODREF|INTERFACEMETHODREF]
+                if (scanner.token == Token.INTVAL) {
+                    // INVOKESUBTAG_INDEX
+                    // Handle explicit constant pool form
+                    subtag = subtag(scanner.intValue);
+                    subtagCell = new ConstantPool.ConstCell(subtag.value());
+                    scanner.scan();
+                    scanner.expect(Token.COLON);
+                    if (scanner.token == Token.CPINDEX) {
+                        // CONSTANT_FIELD
+                        int cpx = scanner.intValue;
+                        refCell = parser.pool.getCell(cpx);
+                        scanner.scan();
+                    } else {
+                        // [FIELDREF|METHODREF|INTERFACEMETHODREF]
+                        refCell = parser.parseMethodHandle(subtag);
+                    }
+                } else {
+                    // INVOKESUBTAG : REF_INVOKEINTERFACE, REF_NEWINVOKESPECIAL, ...
+                    // normal JASM
+                    subtag = parser.parseSubtag();
+                    subtagCell = new ConstantPool.ConstCell(subtag.value());
+                    scanner.expect(Token.COLON);
+                    if (scanner.token == Token.CPINDEX) {
+                        // CODETOOLS-7901522: Jasm doesn't allow to create REF_invoke* referring an InterfaceMethod
+                        // Parsing the case when refCell is CP index (#1)
+                        // const #1 = InterfaceMethod m:"()V";
+                        // const #2 = MethodHandle REF_invokeSpecial:#1;
+                        int cpx = scanner.intValue;
+                        refCell = parser.pool.getCell(cpx);
+                        scanner.scan();
+                    } else {
+                        refCell = parser.parseMethodHandle(subtag);
+                    }
+                }
+                obj = new ConstantPool.ConstValue_Pair(tag, subtagCell, refCell);
+            } catch (IOException e) {
+                IOProb = e;
+            }
+            return obj;
+        }
+
+        private ConstantPool.ConstValue_Pair visitMember(ConstType tag) {
+            debugStr("------- [ParserCPVisitor.visitMember]: ");
+            ConstantPool.ConstValue_Pair obj = null;
+            try {
+                Token prevtoken = scanner.token;
+                ConstantPool.ConstCell firstName, ClassCell, NameCell, NapeCell;
+                firstName = parser.parseClassName(false);
+                if (scanner.token == Token.FIELD) { // DOT
+                    scanner.scan();
+                    if (prevtoken == Token.CPINDEX) {
+                        ClassCell = firstName;
+                    } else {
+                        ClassCell = parser.pool.FindCell(ConstType.CONSTANT_CLASS, firstName);
+                    }
+                    NameCell = parser.parseName();
+                } else {
+                    // no class provided - assume current class
+                    ClassCell = parser.cd.me;
+                    NameCell = firstName;
+                }
+                if (scanner.token == Token.COLON) {
+                    // name and type separately
+                    scanner.scan();
+                    NapeCell = parser.pool.FindCell(ConstType.CONSTANT_NAMEANDTYPE, NameCell, parser.parseName());
+                } else {
+                    // name and type as single name
+                    NapeCell = NameCell;
+                }
+                obj = new ConstantPool.ConstValue_Pair(tag, ClassCell, NapeCell);
+            } catch (IOException e) {
+                IOProb = e;
+            }
+            return obj;
+        }
+
+        @Override
+        public ConstantPool.ConstValue visitField(ConstType tag) {
+            debugStr("------- [ParserCPVisitor.visitField]: ");
+            return visitMember(tag);
+        }
+
+        @Override
+        public ConstantPool.ConstValue visitMethod(ConstType tag) {
+            debugStr("------- [ParserCPVisitor.visitMethod]: ");
+            return visitMember(tag);
+        }
+
+        @Override
+        public ConstantPool.ConstValue visitInterfacemethod(ConstType tag) {
+            debugStr("------- [ParserCPVisitor.visitInterfacemethod]: ");
+            return visitMember(tag);
+        }
+
+        @Override
+        public ConstantPool.ConstValue visitNameandtype(ConstType tag) {
+            debugStr("------- [ParserCPVisitor.visitNameandtype]: ");
+            ConstantPool.ConstValue_Pair obj = null;
+            try {
+                ConstantPool.ConstCell NameCell = parser.parseName(), TypeCell;
+                scanner.expect(Token.COLON);
+                TypeCell = parser.parseName();
+                obj = new ConstantPool.ConstValue_Pair(tag, NameCell, TypeCell);
+            } catch (IOException e) {
+                IOProb = e;
+            }
+            return obj;
+        }
+
+        @Override
+        public ConstantPool.ConstValue_IndyPair visitInvokedynamic(ConstType tag) {
+            debugStr("------- [ParserCPVisitor.visitInvokeDynamic]: ");
+            final BiFunction<BootstrapMethodData, ConstantPool.ConstCell, ConstantPool.ConstValue_IndyPair> ctor =
+                    (bsmData, napeCell) -> new ConstantPool.ConstValue_IndyPair(bsmData, napeCell);
+            return visitBsm(ctor);
+        }
+
+        @Override
+        public ConstantPool.ConstValue_CondyPair visitDynamic(ConstType tag) {
+            debugStr("------- [ParserCPVisitor.visitDynamic]: ");
+            final BiFunction<BootstrapMethodData, ConstantPool.ConstCell, ConstantPool.ConstValue_CondyPair> ctor =
+                    (bsmData, napeCell) -> new ConstantPool.ConstValue_CondyPair(bsmData, napeCell);
+            return visitBsm(ctor);
+        }
+
+        private <E extends ConstantPool.ConstValue_IndyOrCondyPair> E visitBsm(BiFunction<BootstrapMethodData, ConstantPool.ConstCell, E> ctor) {
+            E obj = null;
+            try {
+                if (scanner.token == Token.INTVAL) {
+                    // Handle explicit constant pool form
+                    int bsmIndex = scanner.intValue;
+                    scanner.scan();
+                    scanner.expect(Token.COLON);
+                    if (scanner.token != Token.CPINDEX) {
+                        env.traceln("token=" + scanner.token);
+                        env.error(scanner.pos, "token.expected", "<CPINDEX>");
+                        throw new Scanner.SyntaxError();
+                    }
+                    int cpx = scanner.intValue;
+                    scanner.scan();
+                    // Put a placeholder in place of BSM.
+                    // resolve placeholder after the attributes are scanned.
+                    BootstrapMethodData bsmData = new BootstrapMethodData(bsmIndex);
+                    obj =   ctor.apply(bsmData, parser.pool.getCell(cpx));
+                } else {
+                    // Handle full form
+                    ConstantPool.ConstCell MHCell = parser.pool.FindCell(parseConstValue(ConstType.CONSTANT_METHODHANDLE));
+                    scanner.expect(Token.COLON);
+                    ConstantPool.ConstCell NapeCell = parser.pool.FindCell(parseConstValue(ConstType.CONSTANT_NAMEANDTYPE));
+                    if(scanner.token == Token.LBRACE) {
+                        ParserCP.this.lbrace++;
+                        scanner.scan();
+                    }
+                    ArrayList<ConstantPool.ConstCell> bsm_args = new ArrayList<>(256);
+                    while(true) {
+                        if( ParserCP.this.lbrace > 0 ) {
+                            if(scanner.token == Token.RBRACE ) {
+                                ParserCP.this.lbrace--;
+                                scanner.scan();
+                                break;
+                            } else if(scanner.token == Token.SEMICOLON) {
+                                scanner.expect(Token.RBRACE);
+                            }
+                        } else if(scanner.token == Token.SEMICOLON) {
+                            break;
+                        }
+                        if (scanner.token == Token.COMMA) {
+                            scanner.scan();
+                        }
+                        bsm_args.add(parseConstRef(null));
+                    }
+                    if( ParserCP.this.lbrace == 0 ) {
+                        scanner.check(Token.SEMICOLON);
+                    }
+                    BootstrapMethodData bsmData = new BootstrapMethodData(MHCell, bsm_args);
+                    parser.cd.addBootstrapMethod(bsmData);
+                    obj = ctor.apply(bsmData, NapeCell);
+                }
+            } catch (IOException e) {
+                IOProb = e;
+            }
+            return obj;
+        }
+    } // End Visitor
+
+    /**
+     * Parse CONSTVALUE
+     */
+    protected ConstantPool.ConstValue parseConstValue(ConstType tag) throws IOException, Scanner.SyntaxError {
+        return pConstVstr.visitExcept(tag);
+    }
+
+    /**
+     * Parse [TAG] CONSTVALUE
+     */
+    protected ConstantPool.ConstValue parseTagConstValue(ConstType defaultTag) throws Scanner.SyntaxError, IOException {
+        return parseTagConstValue(defaultTag, null, false);
+    }
+
+    private ConstType scanConstByID(boolean ignoreKeywords) {
+        ConstType tag = null;
+        if (!ignoreKeywords) {
+            ConstType tg = Tables.tag(scanner.idValue);
+            if (tg != null) {
+                tag = tg;
+            }
+            debugStr(" *^*^*^*^ [ParserCP.scanConst]: {TAG = " + (tg == null ? "null" : tg.toString()) + " ");
+        }
+        return tag;
+    }
+
+    private ConstType scanConstPrimVal() throws Scanner.SyntaxError, IOException {
+        ConstType tag = null;
+        switch (scanner.token) {
+            case INTVAL:
+                tag = ConstType.CONSTANT_INTEGER;
+                break;
+            case LONGVAL:
+                tag = ConstType.CONSTANT_LONG;
+                break;
+            case FLOATVAL:
+                tag = ConstType.CONSTANT_FLOAT;
+                break;
+            case DOUBLEVAL:
+                tag = ConstType.CONSTANT_DOUBLE;
+                break;
+            case STRINGVAL:
+            case BITS:
+            case IDENT:
+                tag = ConstType.CONSTANT_STRING;
+                break;
+            default:
+                // problem - no constant value
+                System.err.println("NEAR: " + scanner.token.printValue());
+                env.error(scanner.pos, "value.expected");
+                throw new Scanner.SyntaxError();
+        }
+        return tag;
+    }
+
+    private void checkWrongTag(ConstType tag, ConstType defaultTag, ConstType default2Tag) throws Scanner.SyntaxError, IOException {
+        if (defaultTag != null) {
+            if (tag != defaultTag) {
+                if (default2Tag == null) {
+                    if( exitImmediately ) {
+                        env.error("wrong.tag", defaultTag.parseKey());
+                        throw new Scanner.SyntaxError().Fatal();
+                    }
+                    env.error("warn.wrong.tag", defaultTag.parseKey());
+                } else if (tag != default2Tag) {
+                    if( exitImmediately ) {
+                        env.error("wrong.tag2", defaultTag.parseKey(), default2Tag.parseKey());
+                        throw new Scanner.SyntaxError().Fatal();
+                    }
+                    env.error("warn.wrong.tag2", defaultTag.parseKey(), default2Tag.parseKey());
+                }
+            }
+        }
+    }
+
+    protected ConstantPool.ConstValue parseTagConstValue(ConstType defaultTag, ConstType default2Tag, boolean ignoreKeywords) throws Scanner.SyntaxError, IOException {
+        debugScan(" *^*^*^*^ [ParserCP.parseTagConstValue]: Begin default_tag:  ignoreKeywords: " + (ignoreKeywords ? "true" : "false"));
+        // Lookup the Tag from the scanner
+        ConstType tag = scanConstByID(ignoreKeywords);
+        debugStr(" *^*^*^*^ [ParserCP.parseTagConstValue]: {tag = " + tag + ", defaulttag = " + defaultTag + "} ");
+
+        // If the scanned tag is null
+        if (tag == null) {
+            // and, if the expected tag is null
+            if (defaultTag == null) {
+                // return some other type of constant as the tag
+                tag = scanConstPrimVal();
+            } else {
+                // otherwise, make the scanned-tag the same constant-type
+                // as the expected tag.
+                tag = defaultTag;
+            }
+        } else {
+            // If the scanned tag is some constant type
+            // and the scanned type does not equal the expected type
+            checkWrongTag(tag, defaultTag, default2Tag);
+            scanner.scan();
+        }
+        return parseConstValue(tag);
+    } // end parseTagConstValue
+
+    protected ConstantPool.ConstCell parseConstRef(ConstType defaultTag) throws Scanner.SyntaxError, IOException {
+        return parseConstRef(defaultTag, null, false);
+    }
+
+    protected ConstantPool.ConstCell parseConstRef(ConstType defaultTag, ConstType default2Tag) throws Scanner.SyntaxError, IOException {
+        return parseConstRef(defaultTag, default2Tag, false);
+    }
+
+    /**
+     * Parse an instruction argument, one of: * #NUMBER, #NAME, [TAG] CONSTVALUE
+     */
+    protected ConstantPool.ConstCell parseConstRef(ConstType defaultTag,
+            ConstType default2Tag,
+            boolean ignoreKeywords) throws Scanner.SyntaxError, IOException {
+        if (scanner.token == Token.CPINDEX) {
+            int cpx = scanner.intValue;
+            scanner.scan();
+            return parser.pool.getCell(cpx);
+        } else {
+            ConstantPool.ConstValue ref = parseTagConstValue(defaultTag, default2Tag, ignoreKeywords);
+            return parser.pool.FindCell(ref);
+        }
+    } // end parseConstRef
+
+}
diff a/test/lib/org/openjdk/asmtools/jasm/ParserInstr.java b/test/lib/org/openjdk/asmtools/jasm/ParserInstr.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/ParserInstr.java
@@ -0,0 +1,414 @@
+/*
+ * Copyright (c) 1996, 2014, 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 org.openjdk.asmtools.jasm;
+
+import static org.openjdk.asmtools.jasm.JasmTokens.*;
+import static org.openjdk.asmtools.jasm.Tables.*;
+import static org.openjdk.asmtools.jasm.OpcodeTables.*;
+import java.io.IOException;
+import java.lang.reflect.Modifier;
+
+/**
+ * ParserInstr
+ *
+ * ParserInstr is a parser class owned by Parser.java. It is primarily responsible for
+ * parsing instruction byte codes.
+ */
+public class ParserInstr extends ParseBase {
+
+    /**
+     * local handle for the constant parser - needed for parsing constants during
+     * instruction construction.
+     */
+    private ParserCP cpParser = null;
+
+    /**
+     * main constructor
+     *
+     * @param scanner
+     * @param parser
+     * @param env
+     */
+    protected ParserInstr(Scanner scanner, Parser parser, ParserCP cpParser, Environment env) {
+        super.init(scanner, parser, env);
+        this.cpParser = cpParser;
+    }
+
+    /**
+     * Parse an instruction.
+     */
+    protected void parseInstr() throws Scanner.SyntaxError, IOException {
+        // ignore possible line numbers after java disassembler
+        if (scanner.token == Token.INTVAL) {
+            scanner.scan();
+        }
+        // ignore possible numeric labels after java disassembler
+        if (scanner.token == Token.INTVAL) {
+            scanner.scan();
+        }
+        if (scanner.token == Token.COLON) {
+            scanner.scan();
+        }
+
+        String mnemocode;
+        int mnenoc_pos;
+        for (;;) { // read labels
+            if (scanner.token != Token.IDENT) {
+                return;
+            }
+            mnemocode = scanner.idValue;
+            mnenoc_pos = scanner.pos;
+            scanner.scan();
+            if (scanner.token != Token.COLON) {
+                break;
+            }
+            // actually it was a label
+            scanner.scan();
+            parser.curCode.LabelDef(mnenoc_pos, mnemocode);
+        }
+
+        Opcode opcode = OpcodeTables.opcode(mnemocode);
+        if (opcode == null) {
+            debugScan(" Error:  mnemocode = '" + mnemocode + "'.   ");
+        }
+        OpcodeType optype = opcode.type();
+
+        Argument arg = null;
+        Object arg2 = null;
+        StackMapData sMap = null;
+
+        debugScan(" --IIIII---[ParserInstr:[parseInstr]:  (Pos: " + mnenoc_pos + ") mnemocode: '" + opcode.parsekey() + "' ");
+
+        switch (optype) {
+            case NORMAL:
+                switch (opcode) {
+
+                    // pseudo-instructions:
+                    case opc_bytecode:
+                        for (;;) {
+                            parser.curCode.addInstr(mnenoc_pos, Opcode.opc_bytecode, parser.parseUInt(1), null);
+                            if (scanner.token != Token.COMMA) {
+                                return;
+                            }
+                            scanner.scan();
+                        }
+                    case opc_try:
+                        for (;;) {
+                            parser.curCode.beginTrap(scanner.pos, parser.parseIdent());
+                            if (scanner.token != Token.COMMA) {
+                                return;
+                            }
+                            scanner.scan();
+                        }
+                    case opc_endtry:
+                        for (;;) {
+                            parser.curCode.endTrap(scanner.pos, parser.parseIdent());
+                            if (scanner.token != Token.COMMA) {
+                                return;
+                            }
+                            scanner.scan();
+                        }
+                    case opc_catch:
+                        parser.curCode.trapHandler(scanner.pos, parser.parseIdent(),
+                                cpParser.parseConstRef(ConstType.CONSTANT_CLASS));
+                        return;
+                    case opc_var:
+                        for (;;) {
+                            parser.parseLocVarDef();
+                            if (scanner.token != Token.COMMA) {
+                                return;
+                            }
+                            scanner.scan();
+                        }
+                    case opc_endvar:
+                        for (;;) {
+                            parser.parseLocVarEnd();
+                            if (scanner.token != Token.COMMA) {
+                                return;
+                            }
+                            scanner.scan();
+                        }
+                    case opc_locals_map:
+                        sMap = parser.curCode.getStackMap();
+                        if (sMap.localsMap != null) {
+                            env.error(scanner.pos, "localsmap.repeated");
+                        }
+                        ;
+                        DataVector localsMap = new DataVector();
+                        sMap.localsMap = localsMap;
+                        if (scanner.token == Token.SEMICOLON) {
+                            return;  // empty locals_map allowed
+                        }
+                        for (;;) {
+                            parser.parseMapItem(localsMap);
+                            if (scanner.token != Token.COMMA) {
+                                return;
+                            }
+                            scanner.scan();
+                        }
+                    case opc_stack_map:
+                        sMap = parser.curCode.getStackMap();
+                        if (sMap.stackMap != null) {
+                            env.error(scanner.pos, "stackmap.repeated");
+                        }
+                        ;
+                        DataVector stackMap = new DataVector();
+                        sMap.stackMap = stackMap;
+                        if (scanner.token == Token.SEMICOLON) {
+                            return;  // empty stack_map allowed
+                        }
+                        for (;;) {
+                            parser.parseMapItem(stackMap);
+                            if (scanner.token != Token.COMMA) {
+                                return;
+                            }
+                            scanner.scan();
+                        }
+                    case opc_stack_frame_type:
+                        sMap = parser.curCode.getStackMap();
+                        if (sMap.stackFrameType != null) {
+                            env.error(scanner.pos, "frametype.repeated");
+                        }
+                        ;
+                        sMap.setStackFrameType(parser.parseIdent());
+                        return;
+
+                    // normal instructions:
+                    case opc_aload:
+                    case opc_astore:
+                    case opc_fload:
+                    case opc_fstore:
+                    case opc_iload:
+                    case opc_istore:
+                    case opc_lload:
+                    case opc_lstore:
+                    case opc_dload:
+                    case opc_dstore:
+                    case opc_ret:
+                    case opc_aload_w:
+                    case opc_astore_w:
+                    case opc_fload_w:
+                    case opc_fstore_w:
+                    case opc_iload_w:
+                    case opc_istore_w:
+                    case opc_lload_w:
+                    case opc_lstore_w:
+                    case opc_dload_w:
+                    case opc_dstore_w:
+                    case opc_ret_w:
+                        // loc var
+                        arg = parser.parseLocVarRef();
+                        break;
+                    case opc_iinc: // loc var, const
+                        arg = parser.parseLocVarRef();
+                        scanner.expect(Token.COMMA);
+                        arg2 = parser.parseInt(1);
+                        break;
+                    case opc_tableswitch:
+                    case opc_lookupswitch:
+                        arg2 = parseSwitchTable();
+                        break;
+                    case opc_newarray: {
+                        int type;
+                        if (scanner.token == Token.INTVAL) {
+                            type = scanner.intValue;
+                        } else if ((type = Tables.basictypeValue(scanner.idValue)) == -1) {
+                            env.error(scanner.pos, "type.expected");
+                            throw new Scanner.SyntaxError();
+                        }
+                        scanner.scan();
+                        arg = new Argument(type);
+                        break;
+                    }
+                    case opc_new:
+                    case opc_aconst_init:
+                    case opc_anewarray:
+                    case opc_instanceof:
+                    case opc_checkcast:
+                        arg = cpParser.parseConstRef(ConstType.CONSTANT_CLASS);
+                        break;
+                    case opc_bipush:
+                        arg = parser.parseInt(1);
+                        break;
+                    case opc_sipush:
+                        arg = parser.parseInt(2);
+                        break;
+                    case opc_ldc:
+                    case opc_ldc_w:
+                    case opc_ldc2_w:
+                        arg = cpParser.parseConstRef(null);
+                        break;
+                    case opc_putstatic:
+                    case opc_getstatic:
+                    case opc_putfield:
+                    case opc_getfield:
+                    case opc_withfield:
+                        arg = cpParser.parseConstRef(ConstType.CONSTANT_FIELD);
+                        break;
+                    case opc_invokevirtual:
+                        arg = cpParser.parseConstRef(ConstType.CONSTANT_METHOD);
+                        break;
+                    case opc_invokestatic:
+                    case opc_invokespecial:
+                        ConstType ctype01  = ConstType.CONSTANT_METHOD;
+                        ConstType ctype02  = ConstType.CONSTANT_INTERFACEMETHOD;
+                        if(Modifier.isInterface(this.parser.cd.access)) {
+                            ctype01  = ConstType.CONSTANT_INTERFACEMETHOD;
+                            ctype02  = ConstType.CONSTANT_METHOD;
+                        }
+                        arg = cpParser.parseConstRef(ctype01, ctype02);
+                        break;
+                    case opc_jsr:
+                    case opc_goto:
+                    case opc_ifeq:
+                    case opc_ifge:
+                    case opc_ifgt:
+                    case opc_ifle:
+                    case opc_iflt:
+                    case opc_ifne:
+                    case opc_if_icmpeq:
+                    case opc_if_icmpne:
+                    case opc_if_icmpge:
+                    case opc_if_icmpgt:
+                    case opc_if_icmple:
+                    case opc_if_icmplt:
+                    case opc_if_acmpeq:
+                    case opc_if_acmpne:
+                    case opc_ifnull:
+                    case opc_ifnonnull:
+                    case opc_jsr_w:
+                    case opc_goto_w:
+                        arg = parseLabelRef();
+                        break;
+
+                    case opc_invokeinterface:
+                        arg = cpParser.parseConstRef(ConstType.CONSTANT_INTERFACEMETHOD);
+                        scanner.expect(Token.COMMA);
+                        arg2 = parser.parseUInt(1);
+                        break;
+                    case opc_invokedynamic:
+                        arg = cpParser.parseConstRef(ConstType.CONSTANT_INVOKEDYNAMIC);
+                        break;
+
+                    case opc_multianewarray:
+                        arg = cpParser.parseConstRef(ConstType.CONSTANT_CLASS);
+                        scanner.expect(Token.COMMA);
+                        arg2 = parser.parseUInt(1);
+                        break;
+                    case opc_wide:
+                    case opc_nonpriv:
+                    case opc_priv:
+                        int opc2 = (opcode.value() << 8) | parser.parseUInt(1).arg;
+                        opcode = opcode(opc2);
+                        break;
+                }
+                break;
+            case WIDE:
+                arg = parser.parseLocVarRef();
+                if (opcode == Opcode.opc_iinc_w) { // loc var, const
+                    scanner.expect(Token.COMMA);
+                    arg2 = parser.parseInt(2);
+                }
+                break;
+            case NONPRIVELEGED:
+            case PRIVELEGED:
+                break;
+            default:
+                env.error(scanner.prevPos, "wrong.mnemocode", mnemocode);
+                throw new Scanner.SyntaxError();
+        }
+        // env.traceln(" [ParserInstr.parseInstr] ===============> Adding Instruction: [" + mnenoc_pos + "]: instr: "+ mnemocode /* opcNamesTab[opc] */);
+        parser.curCode.addInstr(mnenoc_pos, opcode, arg, arg2);
+    } //end parseInstr
+
+    /**
+     * Parse a Switch Table. return value: SwitchTable.
+     */
+    protected SwitchTable parseSwitchTable() throws Scanner.SyntaxError, IOException {
+        scanner.expect(Token.LBRACE);
+        Argument label;
+        int numpairs = 0, key;
+        SwitchTable table = new SwitchTable(env);
+tableScan:
+        {
+            while (numpairs < 1000) {
+//              env.traceln("start tableScan:" + token);
+                switch (scanner.token) {
+                    case INTVAL:
+//                        env.traceln("enter tableScan:" + token);
+                        key = scanner.intValue * scanner.sign;
+                        scanner.scan();
+                        scanner.expect(Token.COLON);
+                        table.addEntry(key, parseLabelRef());
+                        numpairs++;
+                        if (scanner.token != Token.SEMICOLON) {
+//                            env.traceln("break tableScan1:" + token);
+                            break tableScan;
+                        }
+                        scanner.scan();
+                        break;
+                    case DEFAULT:
+                        scanner.scan();
+                        scanner.expect(Token.COLON);
+                        if (table.deflabel != null) {
+                            env.error("default.redecl");
+                        }
+                        table.deflabel = parseLabelRef();
+                        if (scanner.token != Token.SEMICOLON) {
+//                            env.traceln("break tableScan2:" + token);
+                            break tableScan;
+                        }
+                        scanner.scan();
+                        break;
+                    default:
+//                      env.traceln("break tableScan3:" + token + "val=" + intValue);
+                        break tableScan;
+                } // end switch
+            } // while (numpairs<1000)
+            env.error("long.switchtable", "1000");
+        } // end tableScan
+        scanner.expect(Token.RBRACE);
+        return table;
+    } // end parseSwitchTable
+
+    /**
+     * Parse a label instruction argument
+     */
+    protected Argument parseLabelRef() throws Scanner.SyntaxError, IOException {
+        switch (scanner.token) {
+            case INTVAL: {
+                int v = scanner.intValue * scanner.sign;
+                scanner.scan();
+                return new Argument(v);
+            }
+            case IDENT: {
+                String label = scanner.stringValue;
+                scanner.scan();
+                return parser.curCode.LabelRef(label);
+            }
+        }
+        env.error("label.expected");
+        throw new Scanner.SyntaxError();
+    }
+
+}
diff a/test/lib/org/openjdk/asmtools/jasm/PermittedSubclassesAttr.java b/test/lib/org/openjdk/asmtools/jasm/PermittedSubclassesAttr.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/PermittedSubclassesAttr.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018, 2020, 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 org.openjdk.asmtools.jasm;
+
+import java.util.List;
+
+/**
+ * The "classes[]" data of attributes
+ * JEP 360 (Sealed types): class file 59.65535
+ * PermittedSubclasses_attribute {
+ * u2 attribute_name_index;
+ * u4 attribute_length;
+ * u2 number_of_classes;
+ * u2 classes[number_of_classes];
+ * }
+ */
+public class PermittedSubclassesAttr extends ClassArrayAttr {
+    public PermittedSubclassesAttr(ClassData cdata, List<ConstantPool.ConstCell> classes) {
+        super(Tables.AttrTag.ATT_PermittedSubclasses.parsekey(), cdata, classes);
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/PreloadAttr.java b/test/lib/org/openjdk/asmtools/jasm/PreloadAttr.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/PreloadAttr.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2022, 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 org.openjdk.asmtools.jasm;
+
+import java.util.List;
+
+public class PreloadAttr extends ClassArrayAttr {
+    public PreloadAttr(ClassData cdata, List<ConstantPool.ConstCell> classes) {
+        super(Tables.AttrTag.ATT_Preload.parsekey(), cdata, classes);
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/RecordData.java b/test/lib/org/openjdk/asmtools/jasm/RecordData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/RecordData.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2019, 2020, 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 org.openjdk.asmtools.jasm;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.openjdk.asmtools.jasm.RuntimeConstants.*;
+
+/**
+ * The record attribute (JEP 359 since class file 58.65535)
+ */
+public class RecordData extends AttrData {
+    private List<ComponentData> components = new ArrayList<>();
+
+    public RecordData(ClassData cls) {
+        super(cls, Tables.AttrTag.ATT_Record.parsekey());
+    }
+
+    public void addComponent(ConstantPool.ConstCell nameCell,
+                             ConstantPool.ConstCell descCell,
+                             ConstantPool.ConstCell signature,
+                             ArrayList<AnnotationData> annotations) {
+        // Define a field if absent
+        FieldData fd = getClassData().addFieldIfAbsent(ACC_MANDATED & ACC_PRIVATE & ACC_FINAL, nameCell, descCell);
+        ComponentData cd = new ComponentData(fd);
+        if( annotations != null ) {
+            cd.addAnnotations(annotations);
+        }
+        if( signature != null ) {
+            cd.setSignatureAttr(signature);
+        }
+        components.add(cd);
+    }
+
+    public boolean isEmpty() {
+        return components.isEmpty();
+    }
+
+    @Override
+    public void write(CheckedDataOutputStream out) throws IOException {
+        super.write(out);
+        out.writeShort(components.size());
+        for (ComponentData cd : components) {
+            cd.write(out);
+        }
+    }
+
+    @Override
+    public int attrLength() {
+        int compsLength = components.stream().mapToInt(c -> c.getLength()).sum();
+        return 2 + compsLength;
+    }
+
+    class ComponentData extends MemberData {
+        private FieldData field;
+
+        public ComponentData(FieldData field) {
+            super(getClassData());
+            this.field = field;
+        }
+
+        @Override
+        protected DataVector getAttrVector() {
+            return getDataVector(signatureAttr);
+        }
+
+        public void write(CheckedDataOutputStream out) throws IOException, Parser.CompilerError {
+            out.writeShort(field.getNameDesc().left.arg);
+            out.writeShort(field.getNameDesc().right.arg);
+            DataVector attrs = getAttrVector();
+            attrs.write(out);
+        }
+
+        public int getLength() {
+            return 4 + getAttrVector().getLength();
+        }
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/RuntimeConstants.java b/test/lib/org/openjdk/asmtools/jasm/RuntimeConstants.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/RuntimeConstants.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 1996, 2017, 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 org.openjdk.asmtools.jasm;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ */
+public interface RuntimeConstants {
+    /* Access Flags */
+
+    int ACC_NONE          = 0x0000; // <<everywhere>>
+    int ACC_PUBLIC        = 0x0001; // class, inner, field, method
+    int ACC_PRIVATE       = 0x0002; //        inner, field, method
+    int ACC_PROTECTED     = 0x0004; //        inner, field, method
+    int ACC_STATIC        = 0x0008; //        inner, field, method
+    int ACC_FINAL         = 0x0010; // class, inner, field, method
+    int ACC_TRANSITIVE    = 0x0010; //                                      requires(module)
+    int ACC_SUPER         = 0x0020; // class
+    int ACC_STATIC_PHASE  = 0x0020; //                                      requires(module)
+    int ACC_SYNCHRONIZED  = 0x0020; //                      method
+    int ACC_OPEN          = 0x0020; //                              module
+    int ACC_VALUE         = 0x0040; // class, inner
+    int ACC_VOLATILE      = 0x0040; //               field
+    int ACC_BRIDGE        = 0x0040; //                      method
+    int ACC_TRANSIENT     = 0x0080; //               field
+    int ACC_VARARGS       = 0x0080; //                      method
+    int ACC_NATIVE        = 0x0100; //                      method
+    int ACC_INTERFACE     = 0x0200; // class, inner
+    int ACC_ABSTRACT      = 0x0400; // class, inner,        method
+    int ACC_PRIMITIVE     = 0x0800; // class, inner
+    int ACC_STRICT        = 0x0800; //                      method
+    int ACC_SYNTHETIC     = 0x1000; // class, inner, field, method, module  requires(module) exports(module)
+    int ACC_ANNOTATION    = 0x2000; // class, inner
+    int ACC_ENUM          = 0x4000; // class, inner, field
+    int ACC_MODULE        = 0x8000; // class
+    int ACC_MANDATED      = 0x8000; //                      method  module  requires(module) exports(module)
+
+   /* Attribute codes */
+   int SYNTHETIC_ATTRIBUTE          = 0x00010000; // actually, this is an attribute
+   int DEPRECATED_ATTRIBUTE         = 0x00020000; // actually, this is an attribute
+
+   Map<Integer,String> ACC_NAMES = new HashMap() {{
+                        put(ACC_PUBLIC       ,"public");
+                        put(ACC_PRIVATE      ,"private");
+                        put(ACC_PROTECTED    ,"protected");
+                        put(ACC_STATIC       ,"static");
+                        put(ACC_FINAL        ,"final");
+                        put(ACC_SUPER        ,"super");
+                        put(ACC_SYNCHRONIZED ,"synchronized");
+                        put(ACC_VOLATILE     ,"volatile");
+                        put(ACC_BRIDGE       ,"bridge");
+                        put(ACC_TRANSIENT    ,"transient");
+                        put(ACC_VARARGS      ,"varargs");
+                        put(ACC_VALUE        ,"value");
+                        put(ACC_NATIVE       ,"native");
+                        put(ACC_INTERFACE    ,"interface");
+                        put(ACC_ABSTRACT     ,"abstract");
+                        put(ACC_PRIMITIVE    ,"primitive");
+                        put(ACC_STRICT       ,"strict");
+                        put(ACC_SYNTHETIC    ,"synthetic");
+                        put(ACC_ANNOTATION   ,"annotation");
+                        put(ACC_ENUM         ,"enum");
+                        put(ACC_MODULE       ,"module");
+                        put(ACC_MANDATED     ,"mandated");
+                        put(SYNTHETIC_ATTRIBUTE     ,"synthetic");
+  }};
+
+    /* The version of a class file since which the compact format of stack map is necessary */
+    int SPLIT_VERIFIER_CFV = 50;
+
+}
diff a/test/lib/org/openjdk/asmtools/jasm/Scanner.java b/test/lib/org/openjdk/asmtools/jasm/Scanner.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/Scanner.java
@@ -0,0 +1,1209 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jasm;
+
+import static java.lang.String.format;
+import static org.openjdk.asmtools.jasm.JasmTokens.*;
+import static org.openjdk.asmtools.jasm.Constants.EOF;
+import static org.openjdk.asmtools.jasm.Constants.OFFSETBITS;
+import java.io.IOException;
+import java.util.function.Predicate;
+
+/**
+ * A Scanner for Jasm tokens. Errors are reported to the environment object.<p>
+ *
+ * The scanner keeps track of the current token, the value of the current token (if any),
+ * and the start position of the current token.<p>
+ *
+ * The scan() method advances the scanner to the next token in the input.<p>
+ *
+ * The match() method is used to quickly match opening brackets (ie: '(', '{', or '[')
+ * with their closing counter part. This is useful during error recovery.<p>
+ *
+ * The compiler treats either "\n", "\r" or "\r\n" as the end of a line.<p>
+ */
+public class Scanner extends ParseBase {
+
+    /**
+     * SyntaxError is the generic error thrown for parsing problems.
+     */
+    protected static class SyntaxError extends Error {
+        boolean fatalError = false;
+        SyntaxError Fatal() { fatalError = true; return this; }
+        boolean isFatal() {return fatalError;}
+    }
+
+    /**
+     * Input stream
+     */
+    protected Environment in;
+
+    /**
+     * The current character
+     */
+    protected int ch;
+
+    /**
+     * Current token
+     */
+//    protected int token;
+    protected Token token;
+
+    /**
+     * The position of the current token
+     */
+    protected int pos;
+
+    /*
+     * Token values.
+     */
+    protected char charValue;
+    protected int intValue;
+    protected long longValue;
+    protected float floatValue;
+    protected double doubleValue;
+    protected String stringValue;
+    protected String idValue;
+    protected int radix;        // Radix, when reading int or long
+
+    /*   doc comment preceding the most recent token  */
+    protected String docComment;
+
+    /* A growable character buffer. */
+    private int count;
+    private char buffer[] = new char[32];
+    //
+    private Predicate<Integer> escapingAllowed;
+    /**
+     * The position of the previous token
+     */
+    protected int prevPos;
+    protected int sign;              // sign, when reading number
+    protected boolean inBits;        // inBits prefix, when reading number
+
+    /**
+     * main constructor.
+     *
+     * Create a scanner to scan an input stream.
+     */
+    protected Scanner(Environment env) throws IOException {
+        super.init(this, null, env);
+        escapingAllowed = noFunc;
+        this.in = env;
+        ch = env.read();
+        xscan();
+    }
+
+    protected void scanModuleStatement() throws IOException {
+        try {
+            escapingAllowed = yesAndProcessFunc;
+            scan();
+        } finally {
+            escapingAllowed = noFunc;
+        }
+    }
+
+    /**
+     * scan
+     *
+     * Scan the next token.
+     *
+     * @throws IOException
+     */
+    protected void scan() throws IOException {
+        int signloc = 1, cnt = 0;
+        prevPos = pos;
+prefix:
+        for (;;) {
+            xscan();
+            switch (token) {
+                case SIGN:
+                    signloc = signloc * intValue;
+                    break;
+                default:
+                    break prefix;
+            }
+            cnt++;
+        }
+        switch (token) {
+            case INTVAL:
+            case LONGVAL:
+            case FLOATVAL:
+            case DOUBLEVAL:
+            case INF:
+            case NAN:
+                sign = signloc;
+                break;
+            default:
+        }
+    }
+
+    /**
+     * Check the token may be identifier
+     */
+    protected final boolean checkTokenIdent() {
+        return token.possibleJasmIdentifier();
+    }
+
+    static String readableConstant(int t) {
+        return "<" + Tables.tag(t) + "> [" + t + "]";
+    }
+
+    /**
+     * Expects a token, scans the next token or throws an exception.
+     */
+    protected final void expect(Token t) throws SyntaxError, IOException {
+        check(t);
+        scan();
+    }
+
+    /**
+     * Checks a token, throws an exception if not the same
+     */
+    protected final void check(Token t) throws SyntaxError, IOException {
+        if (token != t) {
+            if ((t != Token.IDENT) || !checkTokenIdent()) {
+                env.traceln("expect: " + t + " instead of " + token);
+                switch (t) {
+                    case IDENT:
+                        env.error(pos, "identifier.expected");
+                        break;
+                    default:
+                        env.error(pos, "token.expected", "<" + t.printValue() + ">");
+                        break;
+                }
+
+                if (debugFlag) {
+                    debugStr("<<<<<PROBLEM>>>>>>>: ");
+                    throw new Error("<<<<<PROBLEM>>>>>>>");
+                } else {
+                    throw new SyntaxError();
+                }
+            }
+        }
+    }
+
+    private void putCh(int ch) {
+        if (count == buffer.length) {
+            char newBuffer[] = new char[buffer.length * 2];
+            System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
+            buffer = newBuffer;
+        }
+        buffer[count++] = (char) ch;
+    }
+
+    private String bufferString() {
+        char buf[] = new char[count];
+        System.arraycopy(buffer, 0, buf, 0, count);
+        return new String(buf);
+    }
+
+    /**
+     * Returns true if the character is a unicode digit.
+     *
+     * @param ch the character to be checked
+     */
+    public static boolean isUCDigit(int ch) {
+        if ((ch >= '0') && (ch <= '9')) {
+            return true;
+        }
+        switch (ch >> 8) {
+            case 0x06:
+                return ((ch >= 0x0660) && (ch <= 0x0669)) ||        // Arabic-Indic
+                        ((ch >= 0x06f0) && (ch <= 0x06f9));         // Eastern Arabic-Indic
+            case 0x07:
+            case 0x08:
+            default:
+                return false;
+            case 0x09:
+                return ((ch >= 0x0966) && (ch <= 0x096f)) ||        // Devanagari
+                        ((ch >= 0x09e6) && (ch <= 0x09ef));         // Bengali
+            case 0x0a:
+                return ((ch >= 0x0a66) && (ch <= 0x0a6f)) ||        // Gurmukhi
+                        ((ch >= 0x0ae6) && (ch <= 0x0aef));         // Gujarati
+            case 0x0b:
+                return ((ch >= 0x0b66) && (ch <= 0x0b6f)) ||        // Oriya
+                        ((ch >= 0x0be7) && (ch <= 0x0bef));         // Tamil
+            case 0x0c:
+                return ((ch >= 0x0c66) && (ch <= 0x0c6f)) ||        // Telugu
+                        ((ch >= 0x0ce6) && (ch <= 0x0cef));         // Kannada
+            case 0x0d:
+                return ((ch >= 0x0d66) && (ch <= 0x0d6f));          // Malayalam
+            case 0x0e:
+                return ((ch >= 0x0e50) && (ch <= 0x0e59)) ||        // Thai
+                        ((ch >= 0x0ed0) && (ch <= 0x0ed9));         // Lao
+            case 0x0f:
+                return false;
+            case 0x10:
+                return ((ch >= 0x1040) && (ch <= 0x1049));         // Tibetan
+        }
+    }
+
+    /**
+     * Returns true if the character is a Unicode letter.
+     *
+     * @param ch the character to be checked
+     */
+    public static boolean isUCLetter(int ch) {
+        // fast check for Latin capitals and small letters
+        if (((ch >= 'A') && (ch <= 'Z'))
+                || ((ch >= 'a') && (ch <= 'z'))) {
+            return true;
+        }
+        // rest of ISO-LATIN-1
+        if (ch < 0x0100) {
+            // fast check
+            if (ch < 0x00c0) {
+                return (ch == '_') || (ch == '$');
+            }
+            // various latin letters and diacritics,
+            // but *not* the multiplication and division symbols
+            return ((ch >= 0x00c0) && (ch <= 0x00d6))
+                    || ((ch >= 0x00d8) && (ch <= 0x00f6))
+                    || ((ch >= 0x00f8) && (ch <= 0x00ff));
+        }
+        // other non CJK alphabets and symbols, but not digits
+        if (ch <= 0x1fff) {
+            return !isUCDigit(ch);
+        }
+        // rest are letters only in five ranges:
+        //        Hiragana, Katakana, Bopomofo and Hangul
+        //        CJK Squared Words
+        //        Korean Hangul Symbols
+        //        Han (Chinese, Japanese, Korean)
+        //        Han compatibility
+        return ((ch >= 0x3040) && (ch <= 0x318f))
+                || ((ch >= 0x3300) && (ch <= 0x337f))
+                || ((ch >= 0x3400) && (ch <= 0x3d2d))
+                || ((ch >= 0x4e00) && (ch <= 0x9fff))
+                || ((ch >= 0xf900) && (ch <= 0xfaff));
+    }
+
+    /**
+     * Scan a comment. This method should be called once the initial /, * and the next
+     * character have been read.
+     */
+    private void skipComment() throws IOException {
+        while (true) {
+            switch (ch) {
+                case EOF:
+                    env.error(pos, "eof.in.comment");
+                    return;
+                case '*':
+                    if ((ch = in.read()) == '/') {
+                        ch = in.read();
+                        return;
+                    }
+                    break;
+                default:
+                    ch = in.read();
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Scan a doc comment. This method should be called once the initial /, * and * have
+     * been read. It gathers the content of the comment (without leading spaces and '*'s)
+     * in the string buffer.
+     */
+    @SuppressWarnings("empty-statement")
+    private String scanDocComment() throws IOException {
+        count = 0;
+
+        if (ch == '*') {
+            do {
+                ch = in.read();
+            } while (ch == '*');
+            if (ch == '/') {
+                ch = in.read();
+                return "";
+            }
+        }
+        switch (ch) {
+            case '\n':
+            case ' ':
+                ch = in.read();
+                break;
+        }
+
+        boolean seenstar = false;
+        int c = count;
+        while (true) {
+            switch (ch) {
+                case EOF:
+                    env.error(pos, "eof.in.comment");
+                    return bufferString();
+                case '\n':
+                    putCh('\n');
+                    ch = in.read();
+                    seenstar = false;
+                    c = count;
+                    break;
+                case ' ':
+                case '\t':
+                    putCh(ch);
+                    ch = in.read();
+                    break;
+                case '*':
+                    if (seenstar) {
+                        if ((ch = in.read()) == '/') {
+                            ch = in.read();
+                            count = c;
+                            return bufferString();
+                        }
+                        putCh('*');
+                    } else {
+                        seenstar = true;
+                        count = c;
+                        while ((ch = in.read()) == '*');
+                        switch (ch) {
+                            case ' ':
+                                ch = in.read();
+                                break;
+                            case '/':
+                                ch = in.read();
+                                count = c;
+                                return bufferString();
+                        }
+                    }
+                    break;
+                default:
+                    if (!seenstar) {
+                        seenstar = true;
+                    }
+                    putCh(ch);
+                    ch = in.read();
+                    c = count;
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Scan a decimal at this point
+     */
+    private void scanCPRef() throws IOException {
+        switch (ch = in.read()) {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9': {
+                boolean overflow = false;
+                long value = ch - '0';
+                count = 0;
+                putCh(ch);                // save character in buffer
+numberLoop:
+                for (;;) {
+                    switch (ch = in.read()) {
+                        case '0':
+                        case '1':
+                        case '2':
+                        case '3':
+                        case '4':
+                        case '5':
+                        case '6':
+                        case '7':
+                        case '8':
+                        case '9':
+                            putCh(ch);
+                            if (overflow) {
+                                break;
+                            }
+                            value = (value * 10) + (ch - '0');
+                            overflow = (value > 0xFFFF);
+                            break;
+                        default:
+                            break numberLoop;
+                    }
+                } // while true
+                intValue = (int) value;
+                stringValue = bufferString();
+                token = Token.CPINDEX;
+                if (overflow) {
+                    env.error(pos, "overflow");
+                }
+                break;
+            }
+            default:
+                stringValue = Character.toString((char)ch);
+                env.error(in.pos, "invalid.number", stringValue);
+                intValue = 0;
+                token = Token.CPINDEX;
+                ch = in.read();
+        }
+    } // scanCPRef()
+
+    /**
+     * Scan a number. The first digit of the number should be the current character. We
+     * may be scanning hex, decimal, or octal at this point
+     */
+    private void scanNumber() throws IOException {
+        boolean seenNonOctal = false;
+        boolean overflow = false;
+        radix = (ch == '0' ? 8 : 10);
+        long value = ch - '0';
+        count = 0;
+        putCh(ch);                // save character in buffer
+numberLoop:
+        for (;;) {
+            switch (ch = in.read()) {
+                case '.':
+                    if (radix == 16) {
+                        break numberLoop; // an illegal character
+                    }
+                    scanReal();
+                    return;
+
+                case '8':
+                case '9':
+                    // We can't yet throw an error if reading an octal.  We might
+                    // discover we're really reading a real.
+                    seenNonOctal = true;
+                case '0':
+                case '1':
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7':
+                    putCh(ch);
+                    if (radix == 10) {
+                        overflow = overflow || (value * 10) / 10 != value;
+                        value = (value * 10) + (ch - '0');
+                        overflow = overflow || (value - 1 < -1);
+                    } else if (radix == 8) {
+                        overflow = overflow || (value >>> 61) != 0;
+                        value = (value << 3) + (ch - '0');
+                    } else {
+                        overflow = overflow || (value >>> 60) != 0;
+                        value = (value << 4) + (ch - '0');
+                    }
+                    break;
+                case 'd':
+                case 'D':
+                case 'e':
+                case 'E':
+                case 'f':
+                case 'F':
+                    if (radix != 16) {
+                        scanReal();
+                        return;
+                    }
+                // fall through
+                case 'a':
+                case 'A':
+                case 'b':
+                case 'B':
+                case 'c':
+                case 'C':
+                    putCh(ch);
+                    if (radix != 16) {
+                        break numberLoop; // an illegal character
+                    }
+                    overflow = overflow || (value >>> 60) != 0;
+                    value = (value << 4) + 10
+                            + Character.toLowerCase((char) ch) - 'a';
+                    break;
+                case 'l':
+                case 'L':
+                    ch = in.read();        // skip over 'l'
+                    longValue = value;
+                    token = Token.LONGVAL;
+                    break numberLoop;
+                case 'x':
+                case 'X':
+                   // if the first character is a '0' and this is the second
+                    // letter, then read in a hexadecimal number.  Otherwise, error.
+                    if (count == 1 && radix == 8) {
+                        radix = 16;
+                        break;
+                    } else {
+                        // we'll get an illegal character error
+                        break numberLoop;
+                    }
+                default:
+                    intValue = (int) value;
+                    token = Token.INTVAL;
+                    break numberLoop;
+            }
+        } // while true
+        // we have just finished reading the number.  The next thing better
+        // not be a letter or digit.
+        if (isUCDigit(ch) || isUCLetter(ch) || ch == '.') {
+            env.error(in.pos, "invalid.number", Character.toString((char)ch));
+            do {
+                ch = in.read();
+            } while (isUCDigit(ch) || isUCLetter(ch) || ch == '.');
+            intValue = 0;
+            token = Token.INTVAL;
+        } else if (radix == 8 && seenNonOctal) {
+            intValue = 0;
+            token = Token.INTVAL;
+            env.error(in.pos, "invalid.octal.number");
+        } else if (overflow
+                || (token == Token.INTVAL
+                && ((radix == 10) ? (intValue - 1 < -1)
+                        : ((value & 0xFFFFFFFF00000000L) != 0)))) {
+            intValue = 0;        // so we don't get second overflow in Parser
+            longValue = 0;
+            env.error(pos, "overflow");
+        }
+    } // scanNumber()
+
+    /**
+     * Scan a float. We are either looking at the decimal, or we have already seen it and
+     * put it into the buffer. We haven't seen an exponent. Scan a float. Should be called
+     * with the current character is either the 'e', 'E' or '.'
+     */
+    private void scanReal() throws IOException {
+        boolean seenExponent = false;
+        boolean isSingleFloat = false;
+        char lastChar;
+        if (ch == '.') {
+            putCh(ch);
+            ch = in.read();
+        }
+
+numberLoop:
+        for (;; ch = in.read()) {
+            switch (ch) {
+                case '0':
+                case '1':
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7':
+                case '8':
+                case '9':
+                    putCh(ch);
+                    break;
+                case 'e':
+                case 'E':
+                    if (seenExponent) {
+                        break numberLoop; // we'll get a format error
+                    }
+                    putCh(ch);
+                    seenExponent = true;
+                    break;
+                case '+':
+                case '-':
+                    lastChar = buffer[count - 1];
+                    if (lastChar != 'e' && lastChar != 'E') {
+                        break numberLoop; // this isn't an error, though!
+                    }
+                    putCh(ch);
+                    break;
+                case 'f':
+                case 'F':
+                    ch = in.read(); // skip over 'f'
+                    isSingleFloat = true;
+                    break numberLoop;
+                case 'd':
+                case 'D':
+                    ch = in.read(); // skip over 'd'
+                // fall through
+                default:
+                    break numberLoop;
+            } // sswitch
+        } // loop
+
+        // we have just finished reading the number.  The next thing better
+        // not be a letter or digit.
+        if (isUCDigit(ch) || isUCLetter(ch) || ch == '.') {
+            env.error(in.pos, "invalid.number", Character.toString((char)ch));
+            do {
+                ch = in.read();
+            } while (isUCDigit(ch) || isUCLetter(ch) || ch == '.');
+            doubleValue = 0;
+            token = Token.DOUBLEVAL;
+        } else {
+            token = isSingleFloat ? Token.FLOATVAL : Token.DOUBLEVAL;
+            try {
+                lastChar = buffer[count - 1];
+                if (lastChar == 'e' || lastChar == 'E'
+                        || lastChar == '+' || lastChar == '-') {
+                    env.error(in.pos - 1, "float.format");
+                } else if (isSingleFloat) {
+                    floatValue = Float.valueOf(bufferString());
+                    if (Float.isInfinite(floatValue)) {
+                        env.error(pos, "overflow");
+                    }
+                } else {
+                    doubleValue = Double.valueOf(bufferString());
+                    if (Double.isInfinite(doubleValue)) {
+                        env.error(pos, "overflow");
+                        env.error(pos, "overflow");
+                    }
+                }
+            } catch (NumberFormatException ee) {
+                env.error(pos, "float.format");
+                doubleValue = 0;
+                floatValue = 0;
+            }
+        }
+    } // scanReal
+
+    /**
+     * Scan an escape character.
+     *
+     * @return the character or '\\'
+     */
+    private int scanEscapeChar() throws IOException {
+        int p = in.pos;
+
+        switch (ch = in.read()) {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7': {
+                int n = ch - '0';
+                for (int i = 2; i > 0; i--) {
+                    switch (ch = in.read()) {
+                        case '0':
+                        case '1':
+                        case '2':
+                        case '3':
+                        case '4':
+                        case '5':
+                        case '6':
+                        case '7':
+                            n = (n << 3) + ch - '0';
+                            break;
+                        default:
+                            if (n > 0xFF) {
+                                env.error(p, "invalid.escape.char");
+                            }
+                            return n;
+                    }
+                }
+                ch = in.read();
+                if (n > 0xFF) {
+                    env.error(p, "invalid.escape.char");
+                }
+                return n;
+            }
+            case 'r':
+                ch = in.read();
+                return '\r';
+            case 'n':
+                ch = in.read();
+                return '\n';
+            case 'f':
+                ch = in.read();
+                return '\f';
+            case 'b':
+                ch = in.read();
+                return '\b';
+            case 't':
+                ch = in.read();
+                return '\t';
+            case '\\':
+                ch = in.read();
+                return '\\';
+            case '\"':
+                ch = in.read();
+                return '\"';
+            case '\'':
+                ch = in.read();
+                return '\'';
+            case 'u':
+                int unich = in.convertUnicode();
+                ch = in.read();
+                return unich;
+        }
+        return '\\';
+    }
+
+    /**
+     * Scan a string. The current character should be the opening " of the string.
+     */
+    private void scanString() throws IOException {
+        token = Token.STRINGVAL;
+        count = 0;
+        ch = in.read();
+
+        // Scan a String
+        while (true) {
+            switch (ch) {
+                case EOF:
+                    env.error(pos, "eof.in.string");
+                    stringValue = bufferString();
+                    return;
+                case '\n':
+                    ch = in.read();
+                    env.error(pos, "newline.in.string");
+                    stringValue = bufferString();
+                    return;
+                case '"':
+                    ch = in.read();
+                    stringValue = bufferString();
+                    return;
+                case '\\': {
+                    int c = scanEscapeChar();
+                    if (c >= 0) {
+                        putCh((char) c);
+                    }
+                    break;
+                }
+                default:
+                    putCh(ch);
+                    ch = in.read();
+                    break;
+            }
+        }
+    }
+
+
+    /**
+     * Scan an Identifier. The current character should be the first character of the
+     * identifier.
+     */
+    private void scanIdentifier(char[] prefix) throws IOException {
+        int firstChar;
+        count = 0;
+        if(prefix != null) {
+            for(;;) {
+                for (int i = 0; i < prefix.length; i++)
+                    putCh(prefix[i]);
+                ch = in.read();
+                if (ch == '\\') {
+                    ch = in.read();
+                    if (ch == 'u') {
+                        ch = in.convertUnicode();
+                        if (!isUCLetter(ch) && !isUCDigit(ch)) {
+                            prefix = new char[]{(char)ch};
+                            continue;
+                        }
+                    } else if (escapingAllowed.test(ch)) {
+                        prefix = new char[]{(char)ch};
+                        continue;
+                    }
+                    int p = in.pos;
+                    env.error(p, "invalid.escape.char");
+                }
+                break;
+            }
+        }
+        firstChar = ch;
+        boolean firstIteration = true;
+scanloop:
+        while (true) {
+            putCh(ch);
+            ch = in.read();
+
+            // Check to see if the annotation marker is at
+            // the front of the identifier.
+            if (firstIteration && firstChar == '@') {
+                // May be a type annotation
+                if (ch == 'T') {  // type annotation
+                    putCh(ch);
+                    ch = in.read();
+                }
+
+                // is either a runtime visible or invisible annotation
+                if (ch == '+' || ch == '-') {  // regular annotation
+                    // possible annotation -
+                    // need to eat up the '@+' or '@-'
+                    putCh(ch);
+                    ch = in.read();
+                }
+                idValue = bufferString();
+                stringValue = idValue;
+                token = Token.ANNOTATION;
+                return;
+            }
+
+            firstIteration = false;
+            switch (ch) {
+                case 'a':
+                case 'b':
+                case 'c':
+                case 'd':
+                case 'e':
+                case 'f':
+                case 'g':
+                case 'h':
+                case 'i':
+                case 'j':
+                case 'k':
+                case 'l':
+                case 'm':
+                case 'n':
+                case 'o':
+                case 'p':
+                case 'q':
+                case 'r':
+                case 's':
+                case 't':
+                case 'u':
+                case 'v':
+                case 'w':
+                case 'x':
+                case 'y':
+                case 'z':
+                case 'A':
+                case 'B':
+                case 'C':
+                case 'D':
+                case 'E':
+                case 'F':
+                case 'G':
+                case 'H':
+                case 'I':
+                case 'J':
+                case 'K':
+                case 'L':
+                case 'M':
+                case 'N':
+                case 'O':
+                case 'P':
+                case 'Q':
+                case 'R':
+                case 'S':
+                case 'T':
+                case 'U':
+                case 'V':
+                case 'W':
+                case 'X':
+                case 'Y':
+                case 'Z':
+                case '0':
+                case '1':
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7':
+                case '8':
+                case '9':
+                case '$':
+                case '_':
+                case '-':
+                case '[':
+                case ']':
+                case '(':
+                case ')':
+                case '<':
+                case '>':
+                    break;
+                case '/': {// may be comment right after identifier
+                    int c = in.lookForward();
+                    if ((c == '*') || (c == '/')) {
+                        break scanloop; // yes, comment
+                    }
+                    break; // no, continue to parse identifier
+                }
+                case '\\':
+                    ch = in.read();
+                    if ( ch == 'u') {
+                        ch = in.convertUnicode();
+                        if (isUCLetter(ch) || isUCDigit(ch)) {
+                            break;
+                        }
+                    } else if( escapingAllowed.test(ch)) {
+                        break;
+                    }
+                    int p = in.pos;
+                    env.error(p, "invalid.escape.char");
+                default:
+//                    if ((!isUCDigit(ch)) && (!isUCLetter(ch))) {
+                    break scanloop;
+//                    }
+            } // end switch
+        } // end scanloop
+        idValue = bufferString();
+        stringValue = idValue;
+        token = keyword_token_ident(idValue);
+        debugStr(format("##### SCANNER (scanIdent) ######## token = %s value = \"%s\"\n", token, idValue));
+    } // end scanIdentifier
+
+//==============================
+    @SuppressWarnings("empty-statement")
+    protected final void xscan() throws IOException {
+        docComment = null;
+loop:
+        for (;;) {
+            pos = in.pos;
+            switch (ch) {
+                case EOF:
+                    token = Token.EOF;
+                    break loop;
+                case '\n':
+                case ' ':
+                case '\t':
+                case '\f':
+                    ch = in.read();
+                    break;
+                case '/':
+                    switch (ch = in.read()) {
+                        case '/':
+                            // Parse a // comment
+                            while (((ch = in.read()) != EOF) && (ch != '\n'));
+                            break;
+                        case '*':
+                            ch = in.read();
+                            if (ch == '*') {
+                                docComment = scanDocComment();
+                            } else {
+                                skipComment();
+                            }
+                            break;
+                        default:
+                            token = Token.DIV;
+                            break loop;
+                    }
+                    break;
+                case '"':
+                    scanString();
+                    break loop;
+                case '-':
+                    intValue = -1;
+                    token = Token.SIGN;
+                    ch = in.read();
+                    break loop;
+                case '+':
+                    intValue = +1;
+                    ch = in.read();
+                    token = Token.SIGN;
+                    break loop;
+                case '0':
+                case '1':
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7':
+                case '8':
+                case '9':
+                    scanNumber();
+                    break loop;
+                case '.':
+                    switch (ch = in.read()) {
+                        case '0':
+                        case '1':
+                        case '2':
+                        case '3':
+                        case '4':
+                        case '5':
+                        case '6':
+                        case '7':
+                        case '8':
+                        case '9':
+                            count = 0;
+                            putCh('.');
+                            scanReal();
+                            break;
+                        default:
+                            token = Token.FIELD;
+                    }
+                    break loop;
+                case '{':
+                    ch = in.read();
+                    token = Token.LBRACE;
+                    break loop;
+                case '}':
+                    ch = in.read();
+                    token = Token.RBRACE;
+                    break loop;
+                case ',':
+                    ch = in.read();
+                    token = Token.COMMA;
+                    break loop;
+                case ';':
+                    ch = in.read();
+                    token = Token.SEMICOLON;
+                    break loop;
+                case ':':
+                    ch = in.read();
+                    token = Token.COLON;
+                    break loop;
+                case '=':
+                    if ((ch = in.read()) == '=') {
+                        ch = in.read();
+                        token = Token.EQ;
+                        break loop;
+                    }
+                    token = Token.ASSIGN;
+                    break loop;
+                case 'a':
+                case 'b':
+                case 'c':
+                case 'd':
+                case 'e':
+                case 'f':
+                case 'g':
+                case 'h':
+                case 'i':
+                case 'j':
+                case 'k':
+                case 'l':
+                case 'm':
+                case 'n':
+                case 'o':
+                case 'p':
+                case 'q':
+                case 'r':
+                case 's':
+                case 't':
+                case 'u':
+                case 'v':
+                case 'w':
+                case 'x':
+                case 'y':
+                case 'z':
+                case 'A':
+                case 'B':
+                case 'C':
+                case 'D':
+                case 'E':
+                case 'F':
+                case 'G':
+                case 'H':
+                case 'I':
+                case 'J':
+                case 'K':
+                case 'L':
+                case 'M':
+                case 'N':
+                case 'O':
+                case 'P':
+                case 'Q':
+                case 'R':
+                case 'S':
+                case 'T':
+                case 'U':
+                case 'V':
+                case 'W':
+                case 'X':
+                case 'Y':
+                case 'Z':
+                case '$':
+                case '_':
+                case '@':
+                case '[':
+                case ']':
+                case '(':
+                case ')':
+                case '<':
+                case '>':
+                    scanIdentifier(null);
+                    break loop;
+                case '\u001a':
+                    // Our one concession to DOS.
+                    if ((ch = in.read()) == EOF) {
+                        token = Token.EOF;
+                        break loop;
+                    }
+                    env.error(pos, "funny.char");
+                    ch = in.read();
+                    break;
+                case '#':
+                    int c = in.lookForward();
+                    if (c == '{') {
+                        // '#' char denotes a "paramMethod name" token
+                        ch = in.read();
+                        token = Token.PARAM_NAME;
+                        break loop;
+                    }
+                    // otherwise, it is a normal cpref
+                    scanCPRef();
+                    break loop;
+                case '\\':
+                    ch = in.read();
+                    if ( ch == 'u') {
+                        ch = in.convertUnicode();
+                        if (isUCLetter(ch)) {
+                            scanIdentifier(null);
+                            break loop;
+                        }
+                    } else if( escapingAllowed.test(ch)) {
+                        scanIdentifier(new char[]{'\\', (char)ch});
+                        break loop;
+                    }
+//                    if ((ch = in.read()) == 'u') {
+//                        ch = in.convertUnicode();
+//                        if (isUCLetter(ch)) {
+//                            scanIdentifier();
+//                            break loop;
+//                        }
+//                    }
+                default:
+                    env.out.println("funny.char:" + env.lineNumber(pos) + "/" + (pos & ((1 << OFFSETBITS) - 1)));
+                    env.error(pos, "funny.char");
+                    ch = in.read();
+            }
+        }
+    }
+
+    @Override
+    protected void debugScan(String dbstr) {
+        if (token == null) {
+            env.traceln(dbstr + "<<<NULL TOKEN>>>");
+            return;
+        }
+        env.trace(dbstr + token);
+        switch (token) {
+            case IDENT:
+                env.traceln(" = '" + stringValue + "' {idValue = '" + idValue + "'}");
+                break;
+            case STRINGVAL:
+                env.traceln(" = {stringValue}: \"" + stringValue + "\"");
+                break;
+            case INTVAL:
+                env.traceln(" = {intValue}: " + intValue + "}");
+                break;
+            case FLOATVAL:
+                env.traceln(" = {floatValue}: " + floatValue);
+                break;
+            case DOUBLEVAL:
+                env.traceln(" = {doubleValue}: " + doubleValue);
+                break;
+            default:
+                env.traceln("");
+        }
+    }
+
+    private Predicate<Integer> noFunc = (ch)-> false;
+    private Predicate<Integer> yesAndProcessFunc = (ch) -> {
+        boolean res = ((ch == '\\') || (ch == ':') || (ch == '@'));
+        if (res)
+            putCh('\\');
+        return res;
+    };
+}
diff a/test/lib/org/openjdk/asmtools/jasm/StackMapData.java b/test/lib/org/openjdk/asmtools/jasm/StackMapData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/StackMapData.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 1996, 2014, 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 org.openjdk.asmtools.jasm;
+
+import static org.openjdk.asmtools.jasm.Tables.*;
+import java.io.IOException;
+
+/**
+ *
+ */
+public class StackMapData implements Data {
+
+    /**
+     *
+     */
+    static public class StackMapItem1 implements Data {
+
+        StackMapType itemType;
+
+        StackMapItem1(StackMapType itemType) {
+            this.itemType = itemType;
+        }
+
+        @Override
+        public int getLength() {
+            return 1;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeByte(itemType.value());
+        }
+    }
+
+    /**
+     *
+     */
+    static public class StackMapItem2 implements Data {
+
+        StackMapType itemType;
+        Argument arg;
+
+        StackMapItem2(StackMapType itemType, Argument arg) {
+            this.itemType = itemType;
+            this.arg = arg;
+        }
+
+        @Override
+        public int getLength() {
+            return 3;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeByte(itemType.value());
+            out.writeShort(arg.arg);
+        }
+    }
+
+    int pc;
+    int offset;
+    int type;
+    String stackFrameType = null;
+    boolean isStackMapTable = false;
+    DataVector localsMap, stackMap;
+    Environment env;
+
+    StackMapData(Environment env) {
+        this.env = env;
+    }
+
+    void setPC(int pc) {
+        this.pc = pc;
+    }
+
+    void setOffset(int offset) {
+        this.offset = offset;
+    }
+
+    void setOffset(StackMapData prevFrame) {
+        offset = (prevFrame == null) ? pc : (pc - prevFrame.pc - 1);
+    }
+
+    void setStackFrameType(String stackFrameType) {
+        this.stackFrameType = stackFrameType;
+
+        if (stackFrameType != null) {
+            type = stackMapFrameTypeValue(stackFrameType);
+        }
+
+        if (stackFrameType == null || type == -1) {
+            env.error(pc, "invalid.stack.frame.type", stackFrameType, "" + type);
+        }
+    }
+
+    void setIsStackMapTable(boolean isStackMapTable) {
+        this.isStackMapTable = isStackMapTable;
+    }
+
+    void setLocalsMap(DataVector localsMap) {
+        this.localsMap = localsMap;
+    }
+
+    void setStackMap(DataVector stackMap) {
+        this.stackMap = stackMap;
+    }
+
+    @Override
+    public int getLength() {
+        int res = 0;
+        StackMapFrameType frame_type = StackMapFrameType.FULL_FRAME;
+        //    int frame_type = FULL_FRAME;
+
+        if (isStackMapTable) {
+            if (stackFrameType != null) {
+                frame_type = stackMapFrameType(type);
+            }
+            res += 1;
+        }
+
+        switch (frame_type) {
+            case SAME_FRAME:
+                break;
+            case SAME_LOCALS_1_STACK_ITEM_FRAME:
+                res += stackMap.getLength() - 2;
+                break;
+            case SAME_LOCALS_1_STACK_ITEM_EXTENDED_FRAME:
+                res += stackMap.getLength();
+                break;
+            case CHOP_1_FRAME:
+            case CHOP_2_FRAME:
+            case CHOP_3_FRAME:
+                res += 2;
+                break;
+            case SAME_FRAME_EX:
+                res += 2;
+                break;
+            case APPEND_FRAME:
+                res += 2 + (localsMap == null ? 0 : (localsMap.getLength() - 2));
+                break;
+            case FULL_FRAME:
+                res += 2;
+                res += (localsMap == null ? 2 : localsMap.getLength());
+                res += (stackMap == null ? 2 : stackMap.getLength());
+                break;
+            default:
+                ;
+        }
+        return res;
+    }
+
+    @Override
+    public void write(CheckedDataOutputStream out) throws IOException {
+        StackMapFrameType frame_type = StackMapFrameType.FULL_FRAME;
+
+        if (isStackMapTable) {
+            if (stackFrameType != null) {
+                frame_type = stackMapFrameType(type);
+            }
+        }
+
+        switch (frame_type) {
+            case SAME_FRAME:
+                if (offset >= 64) {
+                    env.error(pc, "invalid.offset.same.frame", "" + offset);
+                }
+                out.writeByte(offset);
+                break;
+            case SAME_LOCALS_1_STACK_ITEM_FRAME:
+                if (stackMap == null) {
+                    env.error(pc, "no.stack.map.same.locals");
+                    break;
+                }
+
+                if (stackMap.elements.size() != 1) {
+                    env.error(pc, "should.be.only.one.stack.map.element");
+                    break;
+                }
+
+                if (offset >= 64) {
+                    env.error(pc, "invalid.offset.same.locals", "" + offset);
+                    break;
+                }
+                out.writeByte(frame_type.value() + offset);
+                stackMap.writeElements(out);
+                break;
+            case SAME_LOCALS_1_STACK_ITEM_EXTENDED_FRAME:
+                if (stackMap == null) {
+                    env.error(pc, "no.stack.map.same.locals");
+                    break;
+                }
+
+                if (stackMap.elements.size() != 1) {
+                    env.error(pc, "should.be.only.one.stack.map.element");
+                    break;
+                }
+                out.writeByte(frame_type.value());
+                out.writeShort(offset);
+                stackMap.writeElements(out);
+                break;
+            case CHOP_1_FRAME:
+            case CHOP_2_FRAME:
+            case CHOP_3_FRAME:
+            case SAME_FRAME_EX:
+                boolean error = false;
+
+                if (stackMap != null) {
+                    env.error(pc, "unexpected.stack.maps");
+                    error = true;
+                }
+
+                if (localsMap != null) {
+                    env.error(pc, "unexpected.locals.maps");
+                    error = true;
+                }
+
+                if (error) {
+                    break;
+                }
+                out.writeByte(frame_type.value());
+                out.writeShort(offset);
+                break;
+            case APPEND_FRAME:
+                if (localsMap == null) {
+                    env.error(pc, "no.locals.map.append");
+                    break;
+                }
+
+                if (localsMap.elements.size() > 3) {
+                    env.error(pc, "more.locals.map.elements");
+                    break;
+                }
+                out.writeByte(frame_type.value() + localsMap.elements.size() - 1);
+                out.writeShort(offset);
+                localsMap.writeElements(out);
+                break;
+            case FULL_FRAME:
+                if (isStackMapTable) {
+                    out.writeByte(frame_type.value());
+                    out.writeShort(offset);
+                } else {
+                    out.writeShort(pc);
+                }
+
+                if (localsMap == null) {
+                    out.writeShort(0);
+                } else {
+                    localsMap.write(out);
+                }
+
+                if (stackMap == null) {
+                    out.writeShort(0);
+                } else {
+                    stackMap.write(out);
+                }
+                break;
+            default:
+                env.error(pc, "invalid.stack.frame.type", "" + frame_type);
+        }
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/SwitchTable.java b/test/lib/org/openjdk/asmtools/jasm/SwitchTable.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/SwitchTable.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 1996, 2014, 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 org.openjdk.asmtools.jasm;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ *
+ */
+class SwitchTable {
+
+    Argument deflabel = null;
+    ArrayList<Argument> labels = new ArrayList<>();
+    ArrayList<Integer> keys = new ArrayList<>();
+
+// for tableswitch:
+    Argument[] resLabels;
+    int high, low;
+
+    int pc, pad;
+    Environment env;
+
+    SwitchTable(Environment env) {
+        this.env = env;
+    }
+
+    void addEntry(int key, Argument label) {
+        keys.add(key);
+        labels.add(label);
+    }
+
+// for lookupswitch:
+    int calcLookupSwitch(int pc) {
+        this.pc = pc;
+        pad = ((3 - pc) & 0x3);
+        int len = 1 + pad + (keys.size() + 1) * 8;
+        if (deflabel == null) {
+            deflabel = new Argument(pc + len);
+        }
+        return len;
+    }
+
+    void writeLookupSwitch(CheckedDataOutputStream out) throws IOException {
+        env.traceln("  writeLookupSwitch: pc=" + pc + " pad=" + pad + " deflabel=" + deflabel.arg);
+        int k;
+        for (k = 0; k < pad; k++) {
+            out.writeByte(0);
+        }
+        out.writeInt(deflabel.arg - pc);
+        out.writeInt(keys.size());
+        for (k = 0; k < keys.size(); k++) {
+            out.writeInt(keys.get(k));
+            out.writeInt((labels.get(k)).arg - pc);
+        }
+    }
+
+    int recalcTableSwitch(int pc) {
+        int k;
+        int numpairs = keys.size();
+        int high1 = Integer.MIN_VALUE, low1 = Integer.MAX_VALUE;
+        int numslots = 0;
+        if (numpairs > 0) {
+            for (k = 0; k < numpairs; k++) {
+                int key = keys.get(k);
+                if (key > high1) {
+                    high1 = key;
+                }
+                if (key < low1) {
+                    low1 = key;
+                }
+            }
+            numslots = high1 - low1 + 1;
+        }
+//      if (numslots>2000) env.error("long.switchtable", "2000");
+        env.traceln("  recalcTableSwitch: low=" + low1 + " high=" + high1);
+        this.pc = pc;
+        pad = ((3 - pc) & 0x3);
+        int len = 1 + pad + (numslots + 3) * 4;
+        if (deflabel == null) {
+            deflabel = new Argument(pc + len);
+        }
+        Argument[] resLabels1 = new Argument[numslots];
+        for (k = 0; k < numslots; k++) {
+            resLabels1[k] = deflabel;
+        }
+        for (k = 0; k < numpairs; k++) {
+            env.traceln("   keys.data[" + k + "]=" + keys.get(k));
+            resLabels1[keys.get(k) - low1] = labels.get(k);
+        }
+        this.resLabels = resLabels1;
+        this.labels = null;
+        this.keys = null;
+        this.high = high1;
+        this.low = low1;
+        return len;
+    }
+
+    void writeTableSwitch(CheckedDataOutputStream out) throws IOException {
+        int k;
+        for (k = 0; k < pad; k++) {
+            out.writeByte(0);
+        }
+        out.writeInt(deflabel.arg - pc);
+        out.writeInt(low);
+        out.writeInt(high);
+        for (k = 0; k < resLabels.length; k++) {
+            out.writeInt(resLabels[k].arg - pc);
+        }
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/Tables.java b/test/lib/org/openjdk/asmtools/jasm/Tables.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/Tables.java
@@ -0,0 +1,689 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jasm;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+
+/**
+ *
+ * Tables
+ *
+ * The classes in Tables are following a Singleton Pattern. These classes are Enums, and
+ * they are contained in private hash maps (lookup tables and reverse lookup tables).
+ * These hash maps all have public accessors, which clients use to look-up enums.
+ *
+ * Tokens in this table carry no external state, and are typically treated as constants.
+ * They do not need to be reset.
+ *
+ */
+public class Tables {
+
+    public static final int JAVA_MAGIC = 0xCAFEBABE;
+    /**
+     * Lookup-tables for various types.
+     */
+    private static HashMap<String, AttrTag> NameToAttrTag = new HashMap<>(9);
+    private static HashMap<Integer, AttrTag> AttrTags = new HashMap<>(9);
+
+    private static HashMap<String, SubTag> NameToSubTag = new HashMap<>(9);
+    private static HashMap<Integer, SubTag> SubTags = new HashMap<>(9);
+
+    private static HashMap<String, BasicType> NameToBasicType = new HashMap<>(10);
+    private static HashMap<Integer, BasicType> BasicTypes = new HashMap<>(10);
+
+    private static HashMap<String, AnnotElemType> NameToAnnotElemType = new HashMap<>(10);
+    private static HashMap<Character, AnnotElemType> AnnotElemTypes = new HashMap<>(10);
+
+    private static HashMap<String, StackMapType> KeyToStackMapType = new HashMap<>(10);
+    private static HashMap<String, StackMapType> NameToStackMapType = new HashMap<>(10);
+    private static HashMap<Integer, StackMapType> StackMapTypes = new HashMap<>(10);
+
+    private static HashMap<String, StackMapFrameType> NameToStackMapFrameType = new HashMap<>(10);
+    private static HashMap<Integer, StackMapFrameType> StackMapFrameTypes = new HashMap<>(10);
+
+    private static HashMap<String, ConstType> NameToConstantType = new HashMap<>(ConstType.maxTag);
+    private static HashMap<Integer, ConstType> ConstantTypes = new HashMap<>(ConstType.maxTag);
+
+    static {
+        // register all of the tokens
+        for (ConstType ct : ConstType.values()) {
+            registerConstantType(ct);
+        }
+
+        /* Type codes for SubTags */
+        for (AttrTag at : AttrTag.values()) {
+            registerAttrtag(at);
+        }
+
+        /* Type codes for SubTags */
+        for (SubTag st : SubTag.values()) {
+            registerSubtag(st);
+        }
+
+        /* Type codes for BasicTypes */
+        for (BasicType bt : BasicType.values()) {
+            registerBasicType(bt);
+        }
+
+        /* Type codes for BasicTypes */
+        for (AnnotElemType aet : AnnotElemType.values()) {
+            registerAnnotElemType(aet);
+        }
+
+        /* Type codes for StackMapTypes */
+        for (StackMapType smt : StackMapType.values()) {
+            registerStackMapType(smt);
+        }
+
+        /* Type codes for StackMapFrame attribute */
+        for (StackMapFrameType smft : StackMapFrameType.values()) {
+            registerStackMapFrameType(smft);
+        }
+
+    }
+
+    /**
+     * ConstType
+     *
+     * A (typed) tag (constant) representing the type of Constant in the Constant Pool.
+     */
+    static public enum ConstType {
+        CONSTANT_ZERO                       (-3, "CONSTANT_ZERO", ""),
+        CONSTANT_UTF8                       (1, "CONSTANT_UTF8", "Asciz"),
+        CONSTANT_UNICODE                    (2, "CONSTANT_UNICODE", ""),
+        CONSTANT_INTEGER                    (3, "CONSTANT_INTEGER", "int"),
+        CONSTANT_FLOAT                      (4, "CONSTANT_FLOAT", "float"),
+        CONSTANT_LONG                       (5, "CONSTANT_LONG", "long"),
+        CONSTANT_DOUBLE                     (6, "CONSTANT_DOUBLE", "double"),
+        CONSTANT_CLASS                      (7, "CONSTANT_CLASS", "class"),
+        CONSTANT_STRING                     (8, "CONSTANT_STRING", "String"),
+        CONSTANT_FIELD                      (9, "CONSTANT_FIELD", "Field"),
+        CONSTANT_METHOD                     (10, "CONSTANT_METHOD", "Method"),
+        CONSTANT_INTERFACEMETHOD            (11, "CONSTANT_INTERFACEMETHOD", "InterfaceMethod"),
+        CONSTANT_NAMEANDTYPE                (12, "CONSTANT_NAMEANDTYPE", "NameAndType"),
+        // Constant 13 reserved
+        // Constant 14 reserved
+        CONSTANT_METHODHANDLE               (15, "CONSTANT_METHODHANDLE", "MethodHandle"),
+        CONSTANT_METHODTYPE                 (16, "CONSTANT_METHODTYPE", "MethodType"),
+        CONSTANT_DYNAMIC                    (17, "CONSTANT_DYNAMIC", "Dynamic"),
+        CONSTANT_INVOKEDYNAMIC              (18, "CONSTANT_INVOKEDYNAMIC", "InvokeDynamic"),
+        CONSTANT_MODULE                     (19, "CONSTANT_MODULE",        "Module"),
+        CONSTANT_PACKAGE                    (20, "CONSTANT_PACKAGE",       "Package");
+
+        static final public int maxTag = 20;
+
+        private final int value;
+        private final String parseKey;
+        private final String printval;
+
+        ConstType(int val, String print, String parse) {
+            value = val;
+            parseKey = parse;
+            printval = print;
+        }
+
+        public int value() {
+            return value;
+        }
+
+        public String parseKey() {
+            return parseKey;
+        }
+
+        public String printval() {
+            return printval;
+        }
+
+        public void print(PrintWriter out) {
+            out.print(parseKey);
+        }
+
+        @Override
+        public String toString() {
+            return "<" + printval + "> [" + Integer.toString(value) + "]";
+        }
+    };
+
+    static public ConstType tag(int i) {
+        return ConstantTypes.get(i);
+    }
+
+    static public ConstType tag(String parsekey) {
+        return NameToConstantType.get(parsekey);
+    }
+
+    private static void registerConstantType(ConstType tt) {
+        NameToConstantType.put(tt.parseKey, tt);
+        ConstantTypes.put(tt.value, tt);
+    }
+
+    /**
+     * Attribute descriptor enums
+     */
+    static public enum AttrTag {
+
+        // Constant for ME Spec (StackMap does not appear in SE VM Spec)
+        ATT_Unrecognized                            (0, "ATT_Unrecognized", ""),
+        ATT_StackMap                                (1, "ATT_StackMap", "StackMap"),
+        // Numbers corespond to VM spec (chapter 4.7.X)
+        ATT_ConstantValue                           (2, "ATT_ConstantValue", "ConstantValue"),
+        ATT_Code                                    (3, "ATT_Code", "Code"),
+        ATT_StackMapTable                           (4, "ATT_StackMapTable", "StackMapTable"),
+        ATT_Exceptions                              (5, "ATT_Exceptions", "Exceptions"),
+        ATT_InnerClasses                            (6, "ATT_InnerClasses", "InnerClasses"),
+        ATT_EnclosingMethod                         (7, "ATT_EnclosingMethod", "EnclosingMethod"),
+        ATT_Synthetic                               (8, "ATT_Synthetic", "Synthetic"),
+        ATT_Signature                               (9, "ATT_Signature", "Signature"),
+        ATT_SourceFile                              (10, "ATT_SourceFile", "SourceFile"),
+        ATT_SourceDebugExtension                    (11, "ATT_SourceDebugExtension", "SourceDebugExtension"),
+        ATT_LineNumberTable                         (12, "ATT_LineNumberTable", "LineNumberTable"),
+        ATT_LocalVariableTable                      (13, "ATT_LocalVariableTable", "LocalVariableTable"),
+        ATT_LocalVariableTypeTable                  (14, "ATT_LocalVariableTypeTable", "LocalVariableTypeTable"),
+        ATT_Deprecated                              (15, "ATT_Deprecated", "Deprecated"),
+        ATT_RuntimeVisibleAnnotations               (16, "ATT_RuntimeVisibleAnnotations", "RuntimeVisibleAnnotations"),
+        ATT_RuntimeInvisibleAnnotations             (17, "ATT_RuntimeInvisibleAnnotations", "RuntimeInvisibleAnnotations"),
+        ATT_RuntimeVisibleParameterAnnotations      (18, "ATT_RuntimeVisibleParameterAnnotations", "RuntimeVisibleParameterAnnotations"),
+        ATT_RuntimeInvisibleParameterAnnotations    (19, "ATT_RuntimeInvisibleParameterAnnotations", "RuntimeInvisibleParameterAnnotations"),
+        ATT_AnnotationDefault                       (20, "ATT_AnnotationDefault", "AnnotationDefault"),
+        ATT_BootstrapMethods                        (21, "ATT_BootstrapMethods", "BootstrapMethods"),
+        ATT_RuntimeVisibleTypeAnnotations           (22, "ATT_RuntimeVisibleTypeAnnotations", "RuntimeVisibleTypeAnnotations"),
+        ATT_RuntimeInvisibleTypeAnnotations         (23, "ATT_RuntimeInvisibleTypeAnnotations", "RuntimeInvisibleTypeAnnotations"),
+        ATT_MethodParameters                        (24, "ATT_MethodParameters", "MethodParameters"),
+        ATT_Module                                  (25, "ATT_Module",  "Module"),
+        ATT_Version                                 (26, "ATT_Version", "Version"),
+        ATT_TargetPlatform                          (27, "ATT_TargetPlatform", "TargetPlatform"),
+        ATT_MainClass                               (28, "ATT_MainClass", "MainClass"),
+        ATT_ModulePackages                          (29, "ATT_ModulePackages", "ModulePackages"),
+        ATT_ModuleMainClass                         (30, "ATT_ModuleMainClass", "ModuleMainClass"),
+        ATT_ModuleTarget                            (31, "ATT_ModuleTarget", "ModuleTarget"),
+        // JEP 181: class file 55.0
+        ATT_NestHost                                (32, "ATT_NestHost", "NestHost"),
+        ATT_NestMembers                             (33, "ATT_NestMembers", "NestMembers"),
+        //  JEP 359 Record(Preview): class file 58.65535
+        //  Record_attribute {
+        //    u2 attribute_name_index;
+        //    u4 attribute_length;
+        //    u2 components_count;
+        //    component_info components[components_count];
+        // }
+        ATT_Record                                  (34, "ATT_Record", "Record"),
+        // JEP 360 (Sealed types): class file 59.65535
+        // PermittedSubclasses_attribute {
+        //    u2 attribute_name_index;
+        //    u4 attribute_length;
+        //    u2 number_of_classes;
+        //    u2 classes[number_of_classes];
+        // }
+        ATT_PermittedSubclasses                     (35, "ATT_PermittedSubclasses", "PermittedSubclasses"),
+        ATT_Preload                                 (36, "ATT_Preload", "Preload");
+
+        private final Integer value;
+        private final String printval;
+        private final String parsekey;
+
+        AttrTag(Integer val, String print, String parse) {
+            value = val;
+            printval = print;
+            parsekey = parse;
+        }
+
+        public String printval() {
+            return printval;
+        }
+
+        public String parsekey() {
+            return parsekey;
+        }
+    }
+
+    private static void registerAttrtag(AttrTag tg) {
+        NameToAttrTag.put(tg.parsekey, tg);
+        AttrTags.put(tg.value, tg);
+    }
+
+    public static AttrTag attrtag(int val) {
+        AttrTag tg = AttrTags.get(val);
+        if (tg == null) {
+            tg = AttrTag.ATT_Unrecognized;
+        }
+        return tg;
+    }
+
+    public static AttrTag attrtag(String idValue) {
+        AttrTag tg = NameToAttrTag.get(idValue);
+        if (tg == null) {
+            tg = AttrTag.ATT_Unrecognized;
+        }
+        return tg;
+    }
+
+    public static String attrtagName(int subtag) {
+        AttrTag tg = AttrTags.get(subtag);
+        return tg.parsekey;
+    }
+
+    public static int attrtagValue(String idValue) {
+        AttrTag tg = attrtag(idValue);
+        return tg.value;
+    }
+
+
+    /*-------------------------------------------------------- */
+    /**
+     * SubTag enums
+     */
+    static public enum SubTag {
+        REF_GETFIELD            (1, "REF_getField"),
+        REF_GETSTATIC           (2, "REF_getStatic"),
+        REF_PUTFIELD            (3, "REF_putField"),
+        REF_PUTSTATIC           (4, "REF_putStatic"),
+        REF_INVOKEVIRTUAL       (5, "REF_invokeVirtual"),
+        REF_INVOKESTATIC        (6, "REF_invokeStatic"),
+        REF_INVOKESPECIAL       (7, "REF_invokeSpecial"),
+        REF_NEWINVOKESPECIAL    (8, "REF_newInvokeSpecial"),
+        REF_INVOKEINTERFACE     (9, "REF_invokeInterface");
+
+        private final Integer value;
+        private final String printval;
+
+        SubTag(Integer val, String print) {
+            value = val;
+            printval = print;
+        }
+
+        public String printval() {
+            return printval;
+        }
+
+        public Integer value() {
+            return value;
+        }
+    }
+
+    private static void registerSubtag(SubTag tg) {
+        NameToSubTag.put(tg.printval, tg);
+        SubTags.put(tg.value, tg);
+    }
+
+    public static SubTag subtag(String subtag) {
+        return NameToSubTag.get(subtag);
+    }
+
+    public static SubTag subtag(int subtag) {
+        return SubTags.get(subtag);
+    }
+
+    public static String subtagName(int subtag) {
+        String retval = null;
+        SubTag tg = SubTags.get(subtag);
+        if (tg != null) {
+            retval = tg.printval;
+        }
+        return retval;
+    }
+
+    public static int subtagValue(String idValue) {
+        int retval = 0;
+        SubTag tg = NameToSubTag.get(idValue);
+        if (tg != null) {
+            retval = tg.value;
+        }
+        return retval;
+    }
+
+    /*-------------------------------------------------------- */
+    /**
+     * BasicType enums
+     */
+    static public enum BasicType {
+        T_INT       (0x0000000a, "int"),
+        T_LONG      (0x0000000b, "long"),
+        T_FLOAT     (0x00000006, "float"),
+        T_DOUBLE    (0x00000007, "double"),
+        T_CLASS     (0x00000002, "class"),
+        T_BOOLEAN   (0x00000004, "boolean"),
+        T_CHAR      (0x00000005, "char"),
+        T_BYTE      (0x00000008, "byte"),
+        T_SHORT     (0x00000009, "short");
+
+        private final Integer value;
+        private final String printval;
+
+        BasicType(Integer val, String print) {
+            value = val;
+            printval = print;
+        }
+
+        public String printval() {
+            return printval;
+        }
+    }
+
+    private static void registerBasicType(BasicType typ) {
+        NameToBasicType.put(typ.printval, typ);
+        BasicTypes.put(typ.value, typ);
+    }
+
+    public static BasicType basictype(String idValue) {
+        return NameToBasicType.get(idValue);
+    }
+
+    public static BasicType basictype(int subtag) {
+        return BasicTypes.get(subtag);
+    }
+
+    public static String basictypeName(int subtag) {
+        String retval = null;
+        BasicType tg = BasicTypes.get(subtag);
+        if (tg != null) {
+            retval = tg.printval;
+        }
+        return retval;
+    }
+
+    public static int basictypeValue(String idValue) {
+        int retval = -1;
+        BasicType tg = NameToBasicType.get(idValue);
+        if (tg != null) {
+            retval = tg.value;
+        }
+        return retval;
+    }
+
+    /*-------------------------------------------------------- */
+    /**
+     * AnnotElemType enums
+     */
+    static public enum AnnotElemType {
+
+        AE_BYTE         ('B', "byte"),
+        AE_CHAR         ('C', "char"),
+        AE_SHORT        ('S', "short"),
+        AE_INT          ('I', "int"),
+        AE_LONG         ('J', "long"),
+        AE_FLOAT        ('F', "float"),
+        AE_DOUBLE       ('D', "double"),
+        AE_BOOLEAN      ('Z', "boolean"),
+        AE_STRING       ('s', "string"),
+        AE_ENUM         ('e', "enum"),
+        AE_CLASS        ('c', "class"),
+        AE_ANNOTATION   ('@', "annotation"),
+        AE_ARRAY        ('[', "array"),
+        AE_UNKNOWN      ((char)0, "unknown");
+
+        private char value;
+        private final String printval;
+
+        AnnotElemType(char val, String print) {
+            value = val;
+            printval = print;
+        }
+
+        public char val() {
+            return value;
+        }
+
+        public String printval() {
+            return printval;
+        }
+    }
+
+    private static void registerAnnotElemType(AnnotElemType typ) {
+        NameToAnnotElemType.put(typ.printval, typ);
+        AnnotElemTypes.put(typ.value, typ);
+    }
+
+    public static AnnotElemType annotElemType(String idValue) {
+        return NameToAnnotElemType.get(idValue);
+    }
+
+    public static AnnotElemType annotElemType(char subtag) {
+        AnnotElemType type = AnnotElemTypes.get(subtag);
+        if ( type == null ) {
+            type = AnnotElemType.AE_UNKNOWN;
+        }
+        return type;
+    }
+
+    public static String annotElemTypeName(char subtag) {
+        String retval = null;
+        AnnotElemType tg = AnnotElemTypes.get(subtag);
+        if (tg != null) {
+            retval = tg.printval;
+        }
+        return retval;
+    }
+
+    public static char annotElemTypeVal(String idValue) {
+        char retval = 0;
+        AnnotElemType tg = NameToAnnotElemType.get(idValue);
+        if (tg != null) {
+            retval = tg.value;
+        }
+        return retval;
+    }
+
+
+    /*-------------------------------------------------------- */
+    /**
+     * MapTypes table. These constants are used in stackmap pseudo-instructions only.
+     */
+    static public enum StackMapType {
+        /* Type codes for StackMap attribute */
+        ITEM_Bogus      (0,     "bogus",    "B"),           // an unknown or uninitialized value
+        ITEM_Integer    (1,     "int",      "I"),           // a 32-bit integer
+        ITEM_Float      (2,     "float",    "F"),           // not used
+        ITEM_Double     (3,     "double",   "D"),           // not used
+        ITEM_Long       (4,     "long",     "L"),           // a 64-bit integer
+        ITEM_Null       (5,     "null",     "N"),           // the type of null
+        ITEM_InitObject (6,     "this",     "IO"),          // "this" in constructor
+        ITEM_Object     (7,     "CP",       "O"),           // followed by 2-byte index of class name
+        ITEM_NewObject  (8,     "at",       "NO"),          // followed by 2-byte ref to "new"
+        ITEM_UNKNOWN    (null,  "UNKNOWN",  "UNKNOWN");     // placeholder for wrong types
+
+        private Integer value;
+        private final String printval;
+        private final String parsekey;
+
+        StackMapType(Integer val, String print, String parse) {
+            value = val;
+            printval = print;
+            parsekey = parse;
+        }
+
+        public String parsekey() {
+            return parsekey;
+        }
+
+        public String printval() {
+            return printval;
+        }
+
+        public Integer value() {
+            return value;
+        }
+    }
+
+    private static void registerStackMapType(StackMapType typ) {
+        KeyToStackMapType.put(typ.parsekey, typ);
+        NameToStackMapType.put(typ.printval, typ);
+        StackMapTypes.put(typ.value, typ);
+    }
+
+    public static StackMapType stackMapType(int subtag, PrintWriter out) {
+        StackMapType type = StackMapTypes.get(subtag);
+        if (type == null || type == StackMapType.ITEM_UNKNOWN) {
+            if (out != null)
+                out.println("// Unknown StackMap type " + subtag);
+            type = StackMapType.ITEM_UNKNOWN;
+            type.value = subtag;
+        }
+        return type;
+    }
+
+    public static StackMapType stackMapType(String subtag) {
+        return NameToStackMapType.get(subtag);
+    }
+
+    public static StackMapType stackMapTypeKey(String subtag) {
+        return KeyToStackMapType.get(subtag);
+    }
+
+    public static String stackMapTypeName(int subtag) {
+        String retval = null;
+        StackMapType tg = StackMapTypes.get(subtag);
+        if (tg != null) {
+            retval = tg.printval;
+        }
+        return retval;
+    }
+
+    public static int stackMapTypeValue(String idValue) {
+        int retval = 0;
+        StackMapType tg = NameToStackMapType.get(idValue);
+        if (tg != null) {
+            retval = tg.value;
+        }
+        return retval;
+    }
+
+
+    /*-------------------------------------------------------- */
+    /**
+     * StackMap-FrameType table. These constants are used in stackmap pseudo-instructions
+     * only.
+     */
+    static public enum StackMapFrameType {
+        /* Type codes for StackMapFrame attribute */
+        SAME_FRAME                              (0, "same"),
+        SAME_LOCALS_1_STACK_ITEM_FRAME          (64, "stack1"),
+        SAME_LOCALS_1_STACK_ITEM_EXTENDED_FRAME (247, "stack1_ex"),
+        CHOP_1_FRAME                            (250, "chop1"),
+        CHOP_2_FRAME                            (249, "chop2"),
+        CHOP_3_FRAME                            (248, "chop3"),
+        SAME_FRAME_EX                           (251, "same_ex"),
+        APPEND_FRAME                            (252, "append"),
+        FULL_FRAME                              (255, "full");
+
+        private final Integer value;
+        private final String parsekey;
+
+        StackMapFrameType(Integer val, String print) {
+            value = val;
+            parsekey = print;
+        }
+
+        public String parsekey() {
+            return parsekey;
+        }
+
+        public Integer value() {
+            return value;
+        }
+    }
+
+    private static void registerStackMapFrameType(StackMapFrameType typ) {
+        NameToStackMapFrameType.put(typ.parsekey, typ);
+        StackMapFrameTypes.put(typ.value, typ);
+    }
+
+    public static StackMapFrameType stackMapFrameTypeVal(int subtag) {
+        return StackMapFrameTypes.get(subtag);
+    }
+
+    public static String stackMapFrameTypeName(int subtag) {
+        String retval = null;
+        StackMapFrameType tg = StackMapFrameTypes.get(subtag);
+        if (tg != null) {
+            retval = tg.parsekey;
+        }
+        return retval;
+    }
+
+    public static StackMapFrameType stackMapFrameType(int subtag) {
+        StackMapFrameType frametype;
+        if (subtag < StackMapFrameType.SAME_LOCALS_1_STACK_ITEM_FRAME.value()) {
+            // type is same_frame;
+            frametype = StackMapFrameType.SAME_FRAME;
+        } else if (subtag >= StackMapFrameType.SAME_LOCALS_1_STACK_ITEM_FRAME.value()
+                && subtag <= 127) {
+            // type is same_locals_1_stack_item_frame
+            frametype = StackMapFrameType.SAME_LOCALS_1_STACK_ITEM_FRAME;
+
+        } else if (subtag >= StackMapFrameType.APPEND_FRAME.value()
+                && subtag < StackMapFrameType.FULL_FRAME.value()) {
+            // type is append_frame
+            frametype = StackMapFrameType.APPEND_FRAME;
+        } else {
+            frametype = StackMapFrameTypes.get(subtag);
+        }
+        return frametype;
+    }
+
+    public static int stackMapFrameTypeValue(String idValue) {
+        int retval = 0;
+        StackMapFrameType tg = NameToStackMapFrameType.get(idValue);
+        if (tg != null) {
+            retval = tg.value;
+        }
+        return retval;
+    }
+
+    /**
+     * CF_Context enums
+     */
+    public enum CF_Context {
+
+        CTX_CLASS       (0, "class"),
+        CTX_FIELD       (1, "field"),
+        CTX_METHOD      (2, "method"),
+        CTX_INNERCLASS  (3, "inner-class"),
+        CTX_MODULE      (4, "module") ;
+
+        private final int value;
+        private final String printval;
+
+        CF_Context(int val, String print) {
+            value = val;
+            printval = print;
+        }
+
+        boolean isOneOf(CF_Context... items) {
+            for(CF_Context item : items) {
+                if(item.value == value) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public int val() {
+            return value;
+        }
+
+        public String printval() {
+            return printval;
+        }
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/TypeAnnotationData.java b/test/lib/org/openjdk/asmtools/jasm/TypeAnnotationData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/TypeAnnotationData.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jasm;
+
+import org.openjdk.asmtools.jasm.TypeAnnotationTypes.ETargetType;
+import org.openjdk.asmtools.jasm.TypeAnnotationTypes.TypePathEntry;
+
+import java.io.IOException;
+
+/**
+ * JVMS 4.7.20.
+ * type_annotation {
+ *     u1 target_type;
+ *     union {
+ *         type_parameter_target;
+ *         supertype_target;
+ *         type_parameter_bound_target;
+ *         empty_target;
+ *         formal_parameter_target;
+ *         throws_target;
+ *         localvar_target;
+ *         catch_target;
+ *         offset_target;
+ *         type_argument_target;
+ *     } target_info;
+ *     type_path target_path;
+ *     u2        type_index;
+ *     //
+ *     //
+ *     u2        num_element_value_pairs;
+ *     {   u2            element_name_index;
+ *         element_value value;
+ *     } element_value_pairs[num_element_value_pairs];
+ * }
+ */
+public class TypeAnnotationData extends AnnotationData {
+
+    protected ETargetType targetType;
+    protected TypeAnnotationTargetInfoData targetInfo;
+    protected TypeAnnotationTypePathData typePath;
+
+    public TypeAnnotationData(Argument typeCPX, boolean invisible) {
+        super(typeCPX, invisible);
+        typePath = new TypeAnnotationTypePathData();
+    }
+
+    @Override
+    public int getLength() {
+        // lengthOf(annotations[]) + lengthOf(targetType) + lengthOf(targetInfo) + lengthOf(targetInfo)
+        return super.getLength() + 1 + targetInfo.getLength() + typePath.getLength();
+    }
+
+    @Override
+    public void write(CheckedDataOutputStream out) throws IOException {
+        out.writeByte(targetType.value);
+        targetInfo.write(out);
+        typePath.write(out);
+        super.write(out);
+    }
+
+    public void addTypePathEntry(TypePathEntry path) {
+        typePath.addTypePathEntry(path);
+    }
+
+    @Override
+    public String toString() {
+        return toString(0);
+    }
+
+    public String toString(int tabLevel) {
+        StringBuilder sb = new StringBuilder(tabString(tabLevel));
+        sb.append(targetType.toString()).
+                append(' ').
+                append(targetInfo.toString(tabLevel)).
+                append(typePath.toString(tabLevel));
+        return sb.toString();
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/TypeAnnotationTargetInfoData.java b/test/lib/org/openjdk/asmtools/jasm/TypeAnnotationTargetInfoData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/TypeAnnotationTargetInfoData.java
@@ -0,0 +1,546 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.asmtools.jasm;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * TargetInfo (4.7.20.1. The target_info union)
+ *
+ * BaseClass for any Type Annotation Target-Info.
+ */
+public abstract class TypeAnnotationTargetInfoData implements Data {
+
+    protected TypeAnnotationTypes.ETargetType targettype = null;
+
+    public TypeAnnotationTargetInfoData(TypeAnnotationTypes.ETargetType tt) {
+        targettype = tt;
+    }
+
+    public TypeAnnotationTypes.ETargetType getTargetType() {
+        return targettype;
+    }
+
+    public void print(PrintWriter out, String tab) {
+        // print the TargetType and TargetInfo
+        out.print(tab + " {");
+        targettype.print(out);
+        _print(out, tab);
+        out.print(tab + "} ");
+    }
+
+    public abstract void _print(PrintWriter out, String tab);
+
+    public abstract void write(CheckedDataOutputStream out) throws IOException;
+
+    @Override
+    public String toString() {
+        return toString(0);
+    }
+
+    protected abstract void _toString(StringBuilder sb, int tabLevel);
+
+    public  String toString(int tabLevel)  {
+        StringBuilder sb = new StringBuilder(tabString(tabLevel));
+        // first print the target info name (
+        sb.append(targettype.targetInfo().printValue()).append("_target ");
+        // get the sub-classes parts
+        _toString(sb, tabLevel);
+        return sb.toString();
+    }
+
+    /**
+     * type_parameter_target (4.7.20.1. The target_info union)
+     *
+     * The type_parameter_target item indicates that an annotation appears on the declaration of the i'th type parameter
+     * of a generic class, generic interface, generic method, or generic constructor.
+     *
+     * type_parameter_target {
+     *     u1 type_parameter_index;
+     * }
+     */
+    public static class type_parameter_target extends TypeAnnotationTargetInfoData {
+
+        int typeParamIndex;
+
+        public type_parameter_target(TypeAnnotationTypes.ETargetType tt, int index) {
+            super(tt);
+            typeParamIndex = index;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeByte(typeParamIndex);
+        }
+
+        @Override
+        public void _print(PrintWriter out, String tab) {
+            out.print(" ");
+            out.print(typeParamIndex);
+        }
+
+        @Override
+        public int getLength() {
+            return 1;
+        }
+
+        @Override
+        protected void _toString(StringBuilder sb, int tabLevel) {
+            sb.append(tabString(tabLevel)).append(String.format("{ type_parameter_index: %d; }",typeParamIndex));
+        }
+    }
+
+    /**
+     * supertype_target (4.7.20.1. The target_info union)
+     *
+     * The supertype_target item indicates that an annotation appears on a type in the extends or implements clause of
+     * a class or interface declaration.
+     *
+     * supertype_target {
+     *     u2 supertype_index;
+     * }
+     */
+    public static class supertype_target extends TypeAnnotationTargetInfoData {
+
+        int superTypeIndex;
+
+        public supertype_target(TypeAnnotationTypes.ETargetType tt, int index) {
+            super(tt);
+            superTypeIndex = index;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeShort(superTypeIndex);
+        }
+
+        @Override
+        public void _print(PrintWriter out, String tab) {
+            out.print(" ");
+            out.print(superTypeIndex);
+        }
+
+        @Override
+        public int getLength() {
+            return 2;
+        }
+
+        @Override
+        protected void _toString(StringBuilder sb, int tabLevel) {
+            sb.append(tabString(tabLevel)).append(String.format("{ supertype_index: %d; }",superTypeIndex));
+        }
+    }
+
+    /**
+     * type_parameter_bound_target (4.7.20.1. The target_info union)
+     *
+     * The type_parameter_bound_target item indicates that an annotation appears on the i'th bound of the j'th type parameter
+     * declaration of a generic class, interface, method, or constructor.
+     *
+     * type_parameter_bound_target {
+     *     u1 type_parameter_index;
+     *     u1 bound_index;
+     * }
+     */
+    public static class type_parameter_bound_target extends TypeAnnotationTargetInfoData {
+
+        int typeParamIndex;
+        int boundIndex;
+
+        public type_parameter_bound_target(TypeAnnotationTypes.ETargetType tt, int pindx, int bindx) {
+            super(tt);
+            typeParamIndex = pindx;
+            boundIndex = bindx;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeByte(typeParamIndex);
+            out.writeByte(boundIndex);
+        }
+
+        @Override
+        public void _print(PrintWriter out, String tab) {
+            out.print(" ");
+            out.print(typeParamIndex);
+            out.print(" ");
+            out.print(boundIndex);
+        }
+
+        @Override
+        public int getLength() {
+            return 2;
+        }
+
+        @Override
+        protected void _toString(StringBuilder sb, int tabLevel) {
+            sb.append(tabString(tabLevel)).append(String.format("{ type_parameter_index: %d; bound_index: %d; }",
+                    typeParamIndex, boundIndex));
+        }
+    }
+
+    /**
+     * empty_target (4.7.20.1. The target_info union)
+     *
+     * The empty_target item indicates that an annotation appears on either the type in a field declaration,
+     * the return type of a method, the type of a newly constructed object, or the receiver type of a method or constructor.
+     *
+     * empty_target {
+     * }
+     */
+    public static class empty_target extends TypeAnnotationTargetInfoData {
+
+        public empty_target(TypeAnnotationTypes.ETargetType tt) {
+            super(tt);
+        }
+
+        @Override
+        public void _print(PrintWriter out, String tab) {
+            // do nothing
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            // do nothing
+        }
+
+        @Override
+        public int getLength() { return 0; }
+
+        @Override
+        protected void _toString(StringBuilder sb, int tabLevel) {
+            sb.append(tabString(tabLevel)).append("{ }");
+        }
+    }
+
+    /**
+     * formal_parameter_target (4.7.20.1. The target_info union)
+     *
+     * The formal_parameter_target item indicates that an annotation appears on the type in a formal parameter
+     * declaration of a method, constructor, or lambda expression.
+     *
+     * formal_parameter_target {
+     *     u1 formal_parameter_index;
+     * }
+     */
+    public static class formal_parameter_target extends TypeAnnotationTargetInfoData {
+
+        int formalParamIndex;
+
+        public formal_parameter_target(TypeAnnotationTypes.ETargetType tt, int index) {
+            super(tt);
+            formalParamIndex = index;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeByte(formalParamIndex);
+        }
+
+        @Override
+        public void _print(PrintWriter out, String tab) {
+            out.print(" ");
+            out.print(formalParamIndex);
+        }
+
+        @Override
+        public int getLength() {
+            return 1;
+        }
+
+        @Override
+        protected void _toString(StringBuilder sb, int tabLevel) {
+            sb.append(tabString(tabLevel)).append(String.format("{ formal_parameter_index: %d; }",formalParamIndex));
+        }
+    }
+
+    /**
+     * throws_target (4.7.20.1. The target_info union)
+     *
+     * The throws_target item indicates that an annotation appears on the i'th type in the throws clause of a method or
+     * constructor declaration.
+     *
+     * throws_target {
+     *     u2 throws_type_index;
+     * }
+     */
+    public static class throws_target extends TypeAnnotationTargetInfoData {
+
+        int throwsTypeIndex;
+
+        public throws_target(TypeAnnotationTypes.ETargetType tt, int index) {
+            super(tt);
+            throwsTypeIndex = index;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeShort(throwsTypeIndex);
+        }
+
+        @Override
+        public void _print(PrintWriter out, String tab) {
+            out.print(" ");
+            out.print(throwsTypeIndex);
+        }
+
+        @Override
+        public int getLength() {
+            return 2;
+        }
+
+        @Override
+        protected void _toString(StringBuilder sb, int tabLevel) {
+            sb.append(tabString(tabLevel)).append(String.format("{ throws_type_index: %d; }",throwsTypeIndex));
+        }
+    }
+
+    /**
+     * localvar_target (4.7.20.1. The target_info union)
+     *
+     * The localvar_target item indicates that an annotation appears on the type in a local variable declaration,
+     * including a variable declared as a resource in a try-with-resources statement.
+     *
+     * localvar_target {
+     *     u2 table_length;
+     *     {   u2 start_pc;
+     *         u2 length;
+     *         u2 index;
+     *     } table[table_length];
+     * }
+     */
+    public static class localvar_target extends TypeAnnotationTargetInfoData {
+
+        public class LocalVar_Entry {
+
+            public int startPC;
+            public int length;
+            public int cpx;
+
+            public LocalVar_Entry(int st, int len, int index) {
+                startPC = st;
+                length = len;
+                cpx = index;
+            }
+
+            void write(CheckedDataOutputStream out) throws IOException {
+                out.writeShort(startPC);
+                out.writeShort(length);
+                out.writeShort(cpx);
+            }
+
+            public void _print(PrintWriter out, String tab) {
+                out.print(tab + "{");
+                out.print(startPC);
+                out.print(" ");
+                out.print(length);
+                out.print(" ");
+                out.print(cpx);
+                out.print("}");
+            }
+
+            public String toString() {
+                return String.format("start_pc: %d, length: %d, index: %d", startPC, length, cpx);
+            }
+        }
+
+        ArrayList<LocalVar_Entry> table = null;
+
+        public localvar_target(TypeAnnotationTypes.ETargetType tt, int size) {
+            super(tt);
+            table = new ArrayList<>(size);
+        }
+
+        public void addEntry(int startPC, int length, int cpx) {
+            LocalVar_Entry entry = new LocalVar_Entry(startPC, length, cpx);
+            table.add(entry);
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeShort(table.size());
+            for (LocalVar_Entry entry : table) {
+                entry.write(out);
+            }
+        }
+
+        @Override
+        public void _print(PrintWriter out, String tab) {
+            String innerTab = tab + " ";
+            for (LocalVar_Entry entry : table) {
+                entry._print(out, innerTab);
+            }
+            out.print(tab);
+        }
+
+        @Override
+        public int getLength() {
+            return 2 + // U2 for table size
+                    (6 * table.size()); // (3 * U2) for each table entry
+        }
+
+        @Override
+        protected void _toString(StringBuilder sb, int tabLevel) {
+            int i = 0;
+            sb.append(tabString(tabLevel)).append(String.format("{ %d  {", table.size()));
+            for (LocalVar_Entry entry : table) {
+                sb.append(String.format(" [%d]: %s;", i++, entry.toString()));
+            }
+            sb.append(" } }");
+        }
+    }
+
+    /**
+     * catch_target (4.7.20.1. The target_info union)
+     *
+     * The catch_target item indicates that an annotation appears on the i'th type in an exception parameter declaration.
+     *
+     * catch_target {
+     *     u2 exception_table_index;
+     * }
+     */
+    public static class catch_target extends TypeAnnotationTargetInfoData {
+
+        int exceptionTableIndex;
+
+        public catch_target(TypeAnnotationTypes.ETargetType tt, int index) {
+            super(tt);
+            exceptionTableIndex = index;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeShort(exceptionTableIndex);
+        }
+
+        @Override
+        public void _print(PrintWriter out, String tab) {
+            out.print(" ");
+            out.print(exceptionTableIndex);
+        }
+
+        @Override
+        public int getLength() {
+            return 2;
+        }
+
+        @Override
+        protected void _toString(StringBuilder sb, int tabLevel) {
+            sb.append(tabString(tabLevel)).append(String.format("{ exception_table_index: %d; }",exceptionTableIndex));
+        }
+    }
+
+    /**
+     * offset_target (4.7.20.1. The target_info union)
+     *
+     *  The offset_target item indicates that an annotation appears on either the type in an instanceof expression or
+     *  a new expression, or the type before the :: in a method reference expression.
+     *
+     *  offset_target {
+     *     u2 offset;
+     * }
+     */
+    public static class offset_target extends TypeAnnotationTargetInfoData {
+
+        int offset;
+
+        public offset_target(TypeAnnotationTypes.ETargetType tt, int offset) {
+            super(tt);
+            this.offset = offset;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeShort(offset);
+        }
+
+        @Override
+        public void _print(PrintWriter out, String tab) {
+            out.print(" ");
+            out.print(offset);
+        }
+
+        @Override
+        public int getLength() {
+            return 2;
+        }
+
+        @Override
+        protected void _toString(StringBuilder sb, int tabLevel) {
+            sb.append(tabString(tabLevel)).append(String.format("{ offset: %d; }", offset));
+        }
+    }
+
+    /**
+     * type_argument_target (4.7.20.1. The target_info union)
+     *
+     *  The type_argument_target item indicates that an annotation appears either on the i'th type in a cast expression,
+     *  or on the i'th type argument in the explicit type argument list for any of the following: a new expression,
+     *  an explicit constructor invocation statement, a method invocation expression, or a method reference expression
+     *
+     *  type_argument_target {
+     *     u2 offset;
+     *     u1 type_argument_index;
+     * }
+     */
+    public static class type_argument_target extends TypeAnnotationTargetInfoData {
+
+        int offset;
+        int typeArgumentIndex;
+
+        public type_argument_target(TypeAnnotationTypes.ETargetType tt, int offset, int index) {
+            super(tt);
+            this.offset = offset;
+            typeArgumentIndex = index;
+        }
+
+        @Override
+        public void write(CheckedDataOutputStream out) throws IOException {
+            out.writeShort(offset);
+            out.writeByte(typeArgumentIndex);
+        }
+
+        @Override
+        public void _print(PrintWriter out, String tab) {
+            out.print(" ");
+            out.print(offset);
+            out.print(" ");
+            out.print(typeArgumentIndex);
+        }
+
+        @Override
+        public int getLength() {
+            return 3;
+        }
+
+        @Override
+        protected void _toString(StringBuilder sb, int tabLevel) {
+            sb.append(tabString(tabLevel)).append(String.format("{ offset: %d; type_argument_index: %d; }",
+                    offset, typeArgumentIndex));
+        }
+    }
+
+}
+
diff a/test/lib/org/openjdk/asmtools/jasm/TypeAnnotationTypePathData.java b/test/lib/org/openjdk/asmtools/jasm/TypeAnnotationTypePathData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/TypeAnnotationTypePathData.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.asmtools.jasm;
+
+import org.openjdk.asmtools.jasm.TypeAnnotationTypes.TypePathEntry;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * JVMS 4.7.20.2. The type_path structure
+ *
+ * type_path {
+ *     u1 path_length;
+ *     {   u1 type_path_kind;
+ *         u1 type_argument_index;
+ *     } path[path_length];
+ * }
+ */
+public class TypeAnnotationTypePathData implements Data {
+
+    private ArrayList<TypePathEntry> typePathEntries = new ArrayList<>();
+
+    public void addTypePathEntry( TypePathEntry entry) {
+        typePathEntries.add(entry);
+    }
+
+    @Override
+    public void write(CheckedDataOutputStream out) throws IOException {
+        out.writeByte(typePathEntries.size());
+        for (TypePathEntry entry : typePathEntries) {
+            out.writeByte(entry.getTypePathKind());
+            out.writeByte(entry.getTypeArgumentIndex());
+        }
+    }
+
+    @Override
+    public int getLength() {
+        return 1 + typePathEntries.size() * 2;
+    }
+
+    public String toString(int tabLevel) {
+        String buffer = "";
+        if( typePathEntries.size() > 0 ) {
+        StringBuilder sb = new StringBuilder(tabString(tabLevel));
+        sb.append(" [ ");
+        boolean first = true;
+        for (TypePathEntry entry : typePathEntries) {
+                if (!first)
+                    sb.append(", ");
+                first = false;
+            sb.append(entry.toString());
+            }
+            sb.append("]");
+        buffer = sb.toString();
+        }
+        return buffer;
+    }
+
+    /**
+     * jdis: print the type_path structure
+     */
+    public void print(PrintWriter out, String tab) {
+        if( typePathEntries.size() > 0 ) {
+            out.print(tab + " {");
+            boolean first = true;
+            for (TypePathEntry entry : typePathEntries) {
+                if (!first) {
+                    out.print(", ");
+                }
+                first = false;
+                out.print(entry.toString());
+            }
+            out.print(tab + "} ");
+        }
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/TypeAnnotationTypes.java b/test/lib/org/openjdk/asmtools/jasm/TypeAnnotationTypes.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/TypeAnnotationTypes.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jasm;
+
+import java.io.PrintWriter;
+
+/**
+ * Type annotation types: target_type, target_info &amp;&amp; target_path
+ */
+public class TypeAnnotationTypes {
+
+    /**
+     * Interpretation of type_path_kind values (Table 4.7.20.2-A)
+     */
+    public enum EPathKind {
+        ARRAY(0),
+        INNER_TYPE(1),
+        WILDCARD(2),
+        TYPE_ARGUMENT(3);
+
+        private final int tag;
+        public static final int maxLen = 3;
+
+        EPathKind(int tag) {
+            this.tag = tag;
+        }
+
+        public int tag() {
+            return tag;
+        }
+
+        public String parseKey() {
+            return this.toString();
+        }
+
+        static EPathKind getPathKind(String token) {
+            for (EPathKind pk : values()) {
+                if( pk.parseKey().equals(token)) {
+                    return pk;
+                }
+            }
+            return null;
+        }
+    }
+
+    // will throw ArrayIndexOutOfBounds if i < 0 or i > 3
+    static public EPathKind getPathKind(int i) {
+        return EPathKind.values()[i];
+    }
+
+    static public class TypePathEntry {
+
+        private final EPathKind kind;
+        private final int typeArgumentIndex;
+
+        public TypePathEntry(int kind, int typeArgumentIndex) {
+            this.kind = getPathKind(kind);
+            this.typeArgumentIndex = typeArgumentIndex;
+        }
+
+        public TypePathEntry(EPathKind kind, int typeArgumentIndex) {
+            this.kind = kind;
+            this.typeArgumentIndex = typeArgumentIndex;
+        }
+
+        public int getTypePathKind() {
+            return kind.tag();
+        }
+
+        public int getTypeArgumentIndex() {
+            return typeArgumentIndex;
+        }
+
+        @Override
+        public String toString() {
+            // Chapter 4.7.20.2 The type_path structure
+            // if the value of the type_path_kind is 0,1, or 2, thebn the value of the
+            // type_argument_index item is 0.
+            return kind.parseKey() +  ( kind.tag == 3 ?
+                    JasmTokens.Token.LBRACE.parseKey() + typeArgumentIndex + JasmTokens.Token.RBRACE.parseKey() :
+                    "");
+        }
+    }
+
+    /**
+     *     union {
+     *         type_parameter_target;
+     *         supertype_target;
+     *         type_parameter_bound_target;
+     *         empty_target;
+     *         method_formal_parameter_target;
+     *         throws_target;
+     *         localvar_target;
+     *         catch_target;
+     *         offset_target;
+     *         type_argument_target;
+     *     } target_info;
+     */
+    public enum ETargetInfo {
+        TYPEPARAM           ("TYPEPARAM", "type_parameter"),
+        SUPERTYPE           ("SUPERTYPE", "supertype"),
+        TYPEPARAM_BOUND     ("TYPEPARAM_BOUND", "type_parameter_bound"),
+        EMPTY               ("EMPTY", "empty"),
+        METHODPARAM         ("METHODPARAM", "formal_parameter"),
+        EXCEPTION           ("EXCEPTION", "throws"),
+        LOCALVAR            ("LOCALVAR", "localvar"),
+        CATCH               ("CATCH", "catch"),
+        OFFSET              ("OFFSET", "offset"),
+        TYPEARG             ("TYPEARG", "type_argument");
+
+        private final String parseKey;
+        private final String printValue;
+
+        ETargetInfo(String parse, String printValue) {
+            parseKey = parse;
+            this.printValue = printValue;
+        }
+        public String parseKey() {
+            return this.parseKey;
+        }
+
+        public String printValue() {
+            return this.printValue;
+        }
+    }
+
+    /**
+     *  Interpretation of target_type values (Table 4.7.20-A./B.)
+     */
+    static public enum ETargetType {
+        class_type_param            (0x00, "CLASS_TYPE_PARAMETER",  ETargetInfo.TYPEPARAM, "class/interface type parameter"),
+        meth_type_param             (0x01, "METHOD_TYPE_PARAMETER",  ETargetInfo.TYPEPARAM, "method/constructor type parameter"),
+        class_exts_impls            (0x10, "CLASS_EXTENDS",  ETargetInfo.SUPERTYPE, "class extends/implements"),
+        class_type_param_bnds       (0x11, "CLASS_TYPE_PARAMETER_BOUND",  ETargetInfo.TYPEPARAM_BOUND, "class/interface type parameter bounds"),
+        meth_type_param_bnds        (0x12, "METHOD_TYPE_PARAMETER_BOUND",  ETargetInfo.TYPEPARAM_BOUND, "method/constructor type parameter bounds"),
+        field                       (0x13, "FIELD",  ETargetInfo.EMPTY, "field"),
+        meth_ret_type               (0x14, "METHOD_RETURN",  ETargetInfo.EMPTY, "method return type"),
+        meth_receiver               (0x15, "METHOD_RECEIVER",  ETargetInfo.EMPTY, "method receiver"),
+        meth_formal_param           (0x16, "METHOD_FORMAL_PARAMETER",  ETargetInfo.METHODPARAM, "method formal parameter type"),
+        throws_type                 (0x17, "THROWS",  ETargetInfo.EXCEPTION, "exception type in throws"),
+
+        local_var                   (0x40, "LOCAL_VARIABLE",  ETargetInfo.LOCALVAR, "local variable"),
+        resource_var                (0x41, "RESOURCE_VARIABLE",  ETargetInfo.LOCALVAR, "resource variable"),
+        exception_param             (0x42, "EXCEPTION_PARAM",  ETargetInfo.CATCH, "exception parameter"),
+        type_test                   (0x43, "INSTANCEOF",  ETargetInfo.OFFSET, "type test (instanceof)"),
+        obj_creat                   (0x44, "NEW",  ETargetInfo.OFFSET, "object creation (new)"),
+        constr_ref_receiver         (0x45, "CONSTRUCTOR_REFERENCE_RECEIVER", ETargetInfo.OFFSET, "constructor reference receiver"),
+        meth_ref_receiver           (0x46, "METHOD_REFERENCE_RECEIVER", ETargetInfo.OFFSET, "method reference receiver"),
+        cast                        (0x47, "CAST",  ETargetInfo.TYPEARG, "cast"),
+        constr_invoc_typearg        (0x48, "CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT",  ETargetInfo.TYPEARG, "type argument in constructor call"),
+        meth_invoc_typearg          (0x49, "METHOD_INVOCATION_TYPE_ARGUMENT", ETargetInfo.TYPEARG, "type argument in method call"),
+        constr_ref_typearg          (0x4A, "CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT", ETargetInfo.TYPEARG, "type argument in constructor reference"),
+        meth_ref_typearg            (0x4B, "METHOD_REFERENCE_TYPE_ARGUMENT",  ETargetInfo.TYPEARG, "type argument in method reference");
+
+        public static final int maxTag = 0x9A;
+        public static final int maxLen = 36;
+
+        public final int value;
+        private final String parseKey;
+        private final ETargetInfo targetInfo;
+        private final String printVal;
+
+        ETargetType(int val, String parse, ETargetInfo targetInfo, String printVal) {
+            value = val;
+            parseKey = parse;
+            this.targetInfo = targetInfo;
+            this.printVal = printVal;
+        }
+
+        public String parseKey() {
+            return parseKey;
+        }
+
+        public String infoKey() {
+            return targetInfo.parseKey();
+        }
+
+        public ETargetInfo targetInfo() {
+            return targetInfo;
+        }
+
+        public void print(PrintWriter out) {
+            out.print(parseKey);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s[%#x]", parseKey, value);
+        }
+
+        public static ETargetType getTargetType(int typeCode)  {
+            for( ETargetType type: ETargetType.values() ) {
+                if (type.value == typeCode) {
+                    return type;
+                }
+            }
+            return null;
+        }
+
+        static public ETargetType getTargetType(String typeName) {
+            for( ETargetType type: ETargetType.values() ) {
+                if (type.parseKey.equals(typeName)) {
+                    return type;
+                }
+            }
+            return null;
+        }
+    };
+
+    /* TypeAnnotationVisitor Methods */
+    public static class TypeAnnotationTargetVisitor {
+
+        public final void visit(ETargetType tt) {
+            switch (tt) {
+                case class_type_param:
+                case meth_type_param:
+                    visit_type_param_target(tt);
+                    break;
+                case class_exts_impls:
+                    visit_supertype_target(tt);
+                    break;
+                case class_type_param_bnds:
+                case meth_type_param_bnds:
+                    visit_typeparam_bound_target(tt);
+                    break;
+                case field:
+                case meth_ret_type:
+                case meth_receiver:
+                    visit_empty_target(tt);
+                    break;
+                case meth_formal_param:
+                    visit_methodformalparam_target(tt);
+                    break;
+                case throws_type:
+                    visit_throws_target(tt);
+                    break;
+                case local_var:
+                case resource_var:
+                    visit_localvar_target(tt);
+                    break;
+                case exception_param:
+                    visit_catch_target(tt);
+                    break;
+                case type_test:
+                case obj_creat:
+                case constr_ref_receiver:
+                case meth_ref_receiver:
+                    visit_offset_target(tt);
+                    break;
+
+                case cast:
+                case constr_invoc_typearg:
+                case meth_invoc_typearg:
+                case constr_ref_typearg:
+                case meth_ref_typearg:
+
+                    visit_typearg_target(tt);
+                    break;
+            }
+        }
+
+        public void visit_type_param_target(ETargetType tt) {
+        }
+
+        public void visit_supertype_target(ETargetType tt) {
+        }
+
+        public void visit_typeparam_bound_target(ETargetType tt) {
+        }
+
+        public void visit_empty_target(ETargetType tt) {
+        }
+
+        public void visit_methodformalparam_target(ETargetType tt) {
+        }
+
+        public void visit_throws_target(ETargetType tt) {
+        }
+
+        public void visit_localvar_target(ETargetType tt) {
+        }
+
+        public void visit_catch_target(ETargetType tt) {
+        }
+
+        public void visit_offset_target(ETargetType tt) {
+        }
+
+        public void visit_typearg_target(ETargetType tt) {
+        }
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jasm/i18n.properties b/test/lib/org/openjdk/asmtools/jasm/i18n.properties
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jasm/i18n.properties
@@ -0,0 +1,195 @@
+# Copyright (c) 2014, 2021, 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.
+
+jasm.usage=\
+Usage: java -jar asmtools.jar jasm [options] file.jasm...\n\
+where possible options include:
+
+jasm.opt.d=\
+\     -d destdir  directory to place resulting .class files
+jasm.opt.v=\
+\     -v          add trace information
+jasm.opt.g=\
+\     -g          add debug information
+jasm.opt.version=\
+\     -version    prints the program version
+jasm.opt.nowrite=\
+\     -nowrite    do not write resulting .class files
+jasm.opt.strict=\
+\     -strict     consider warnings as errors
+jasm.opt.nowarn=\
+\     -nowarn     do not print warnings
+jasm.opt.cv=\
+\     -cv major.minor  set operating class file version (by default {0}.{1})
+
+
+jasm.error.d_requires_argument=-d requires argument
+jasm.error.does_not_exist={0} does not exist
+jasm.error.cv_requires_arg=-cv requires argument
+jasm.error.invalid_major_minor_param=invalid parameter major.minor
+jasm.error.invalid_option=invalid option: {0}
+jasm.error.cannot_read=cannot read {0}
+jasm.error.cannot_write=cannot write {0}
+jasm.error.fatal_error=fatal error
+jasm.error.fatal_exception=fatal exception
+
+
+# Scanner:
+err.invalid.escape.char=Invalid escape character.
+err.eof.in.comment=Comment not terminated at end of input.
+err.invalid.number=Invalid character "{0}" in number.
+err.invalid.octal.number=Invalid character in octal number.
+err.overflow=Numeric overflow.
+err.float.format=Invalid floating point format.
+err.eof.in.string=String not terminated at end of input.
+err.newline.in.string=String not terminated at end of line.
+err.funny.char=Invalid character in input.
+err.unbalanced.paren=Unbalanced parentheses.
+# Parser:
+err.package.repeated=Package statement repeated.
+warn.intf.repeated=Interface {0} repeated.
+warn.exc.repeated=Exception repeated in throws clause.
+warn.record.repeated=Record attribute repeated.
+err.multiple.inherit=Multiple inheritance is not supported.
+err.toplevel.expected=Class, module or interface declaration expected.
+err.const.def.expected=Constant declaration expected.
+err.const.undecl=Constant #{0} not declared.
+err.const.redecl=Constant {0} redeclared.
+warn.const0.redecl=Re-declaration of Constant #0 cannot be written to the class file.
+err.field.expected=Class member declaration expected.
+err.token.expected={0} expected.
+err.identifier.expected=Identifier expected.
+err.extra.nesthost.attribute=There may be at most one NestHost attribute.
+err.extra.nestmembers.attribute=There may be at most one NestMembers attribute.
+err.extra.permittedsubclasses.attribute=There may be at most one PermittedSubclasses attribute.
+err.extra.record.attribute=There may be at most one Record attribute.
+err.extra.preload.attribute=There may be at most one Preload attribute.
+err.grouped.component.expected=Either an annotation or a record component expected.
+warn.no.components.in.record.attribute=Record should have at least one component.
+err.one.of.two.token.expected=Either #{0} or #{1} token expected.
+
+err.both.nesthost.nestmembers.found=The attributes table of a ClassFile structure must not contain both a NestMembers attribute and a NestHost attribute.
+err.name.expected=Name expected, got {0}.
+err.module.name.expected=Module name expected, got {0}.
+err.int.expected=Integer expected.
+err.neg.forbidden=Negative integer is not allowed here.
+err.value.large=Value doesn't fit in {0}.
+err.value.expected=Value expected.
+err.wrong.mnemocode=Invalid mnemocode ({0}).
+err.default.redecl=Default statement already declared in this table.
+err.long.switchtable=Switchtable too long: > {0}.
+err.io.exception=I/O error in {0}.
+warn.wrong.tag=Wrong tag: {0} expected.
+err.wrong.tag=Wrong tag: {0} expected.
+warn.wrong.tag2=Wrong tag: Either {0} or {1} expected.
+err.wrong.tag2=Wrong tag: Either {0} or {1} expected.
+# Code Gen:
+err.locvar.redecl=Local variable {0} redeclared.
+err.locvar.undecl=Local variable {0} not declared.
+#err.locvar.expected=Local variable expected.
+err.label.redecl=Label {0} redeclared.
+err.label.undecl=Label {0} not declared.
+err.label.expected=Label expected.
+err.subtag.expected=Subtag expected.
+err.type.expected=Type expected.
+err.trap.tryredecl=<try {0}> redeclared.
+err.trap.endtryredecl=<endtry {0}> redeclared.
+err.trap.notry=No <try {0}> found.
+err.trap.noendtry=No <endtry {0}> found.
+warn.trap.notref=No <catch {0}> declared.
+err.cannot.write=Cannot write to {0}.
+err.msig.malformed=Malformed method signature at char {0}. [err={1}]
+err.no.classname=Class name not defined.
+warn.msig.more255=Number of parameters too large ({0}>255).
+warn.illslot=Local variable at Illegal slot {0}.
+warn.repeated.modifier=Repeated modifier.
+warn.invalid.modifier.init=invalid modifier for <init> method  \"{0}\".
+warn.invalid.modifier.fiva=at most one of final and volatile modifiers can be used for a field
+warn.invalid.modifier.intfield=interface field must be public static final only
+warn.init.in_int=<init> method cannot be placed in an interface.
+warn.invalid.modifier.intmth=interface method must be abstract public only  \"{0}\".
+warn.invalid.modifier.abst=invalid modifier for abstract method.
+#
+warn.invalid.modifier.field=invalid modifier(s) for a field \"{0}\"
+warn.invalid.modifier.mth=invalid modifier(s) for a method \"{0}\"
+warn.invalid.modifier.innerclass=invalid modifier for an inner class \"{0}\"
+#
+warn.invalid.modifier.class=invalid modifier(s) for a class \"{0}\"
+warn.invalid.modifier.int=invalid modifier(s) for an interface \"{0}\"
+#
+warn.invalid.modifier.acc=at most one of public, protected, and private modifiers can be used.
+warn.invalid.modifier.int.abs=interface class must have abstract modifier.
+warn.missing.modifier.class=class or enum declaration missing.
+warn.invalid.modifier.class.finabs=class cannot be both abstract and final.
+warn.invalid.modifier.class.intenum=cannot be both interface and enum.
+err.itemtype.expected=StackMap item type expected instead of {0}.
+err.localsmap.repeated=locals_map redeclared.
+err.invalid.stack.frame.type=invalid stack frame type.
+err.invalid.offset.same.frame=offset value more than 64 for the 'same_frame' type frame.
+err.no.stack.map.same.locals=stack map element for the 'same_locals_1_stack_item_frame' type frame is absent.
+err.should.be.only.one.stack.map.element=should be only one stack map element for the 'same_locals_1_stack_item_frame' type frame.
+err.invalid.offset.same.locals=offset value more than 64 for the 'same_locals_1_stack_item_frame' type frame.
+err.unexpected.stack.maps=there are unexpected stack maps.
+err.unexpected.locals.maps=there are unexpected locals maps.
+err.no.locals.map.append=locals map element for the 'append_frame' type frame is absent.
+err.more.locals.map.elements=there are more than 3 locals map element for the 'append_frame' type frame.
+err.stackmap.repeated=stack_map redeclared.
+err.version.expected=class file version expected
+err.invalid.innerclass=Invalid declaration of Inner Class
+err.invalid.bootstrapmethod=Invalid declaration of BootstrapMethod Entry
+err.frametype.repeated=Frametype repeated
+err.invalid.paramnum=Invalid Parameter Number: {0}.
+err.duplicate.paramnum=Duplicate Parameter Number: {0}.
+err.paramname.constnum.invaltype=ParameterName CPX at {0} is not a ConstantString.
+err.paramname.token.unexpected=Incorrect ParamName, unrecognized token: \"{0}\".
+#
+# annotations Errors
+#
+err.incorrect.annot.class=Incorrect Annotation (class), expected class name or CPX), got \"{0}\".
+err.incorrect.annot.enum=Incorrect Annotation (enum), expected type field IDENT, \"{0}\".
+err.incorrect.annot.enum.cpx==Incorrect Annotation (enum), expected type field CPX.
+err.incorrect.annot.token=Incorrect Annotation, unrecognized token: \"{0}\".
+err.incorrect.annot.bool=Incorrect Annotation (boolean), expected Integer), got \"{0}\".
+err.incorrect.annot.byte=Incorrect Annotation (byte), expected Integer), got \"{0}\".
+err.incorrect.annot.char=Incorrect Annotation (char), expected Integer), got \"{0}\".
+err.incorrect.annot.short=Incorrect Annotation (short), expected Integer), got \"{0}\".
+err.incorrect.annot.keyword=Incorrect Annotation keyword \"{0}\".
+err.incorrect.typeannot.target=Incorrect TypeAnnotation target \"{0}\".
+err.incorrect.typeannot.targtype.string=Incorrect TypeAnnotation \"{0}\" argument: (expected String),  \"{1}\".
+err.incorrect.typeannot.targtype.int=Incorrect TypeAnnotation \"{0}\" argument: (expected Integer),  \"{1}\".
+err.incorrect.typeannot.pathentry=Incorrect TypeAnnotation TargetPath PathEntry \"{0}\".
+err.incorrect.typeannot.pathentry.argindex=Incorrect TypeAnnotation  TargetPath PathEntry ArgIndex (expected Integer),  \"{0}\".
+#
+# module Errors
+err.module.statement.expected= Module statement expected.
+err.requires.expected=Module statement \"requires [transitive] [static] ModuleName;\" expected.
+warn.dot.will.be.converted=Forward slash \"/\" expected instead of dot \".\". The dot is replaced by \"/\".
+#
+# Compiler Errors
+#
+comperr.constcell.nullvalset="Cell without value in setCell"
+comperr.constcell.nullvalhash="Cell without value in cpoolHashByValue"
+comperr.constcell.invarg="Cell[{0}] has #{1}"
+comperr.constcell.nullval="ConstCell.value=null??"
+comperr.val.noteq="Values not eq"
+comperr.instr.nullarg="null arg for {0}"
+comperr.instr.arglong="Too long argument of {0}: {1}"
+comperr.instr.opclen="Wrong opcLength({0})"
diff a/test/lib/org/openjdk/asmtools/jcdec/Main.java b/test/lib/org/openjdk/asmtools/jcdec/Main.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jcdec/Main.java
@@ -0,0 +1,957 @@
+/*
+ * Copyright (c) 2009, 2017, 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 org.openjdk.asmtools.jcdec;
+
+import static org.openjdk.asmtools.jcoder.JcodTokens.*;
+import org.openjdk.asmtools.jdis.uEscWriter;
+import org.openjdk.asmtools.util.I18NResourceBundle;
+import org.openjdk.asmtools.util.ProductInfo;
+import java.io.DataInputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Main program of the JavaCard DeCoder
+ *
+ */
+public class Main {
+
+    /*-------------------------------------------------------- */
+    /* Main Fields */
+    /**
+     * Name of the program.
+     */
+    String program;
+
+    public static final I18NResourceBundle i18n
+            = I18NResourceBundle.getBundleForClass(Main.class);
+    /**
+     * The stream where error message are printed.
+     */
+    PrintWriter out;
+    boolean DebugFlag = false;
+    boolean printDetails = false;
+    int shift = 0;
+    private static final char hexTable[] = {
+        '0', '1', '2', '3', '4', '5', '6', '7',
+        '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+    };
+    /*-------------------------------------------------------- */
+
+    static String toHex(long val, int width) {
+        StringBuffer s = new StringBuffer();
+        for (int i = width * 2 - 1; i >= 0; i--) {
+            s.append(hexTable[((int) (val >> (4 * i))) & 0xF]);
+        }
+        return "0x" + s.toString();
+    }
+
+    static String toHex(long val) {
+        int width;
+        for (width = 8; width > 0; width--) {
+            if ((val >> (width - 1) * 8) != 0) {
+                break;
+            }
+        }
+        return toHex(val, width);
+    }
+
+    void printByteHex(PrintWriter out, int b) {
+        out.print(hexTable[(b >> 4) & 0xF]);
+        out.print(hexTable[b & 0xF]);
+    }
+
+    /*========================================================*/
+    void out_begin(String s) {
+        for (int i = 0; i < shift; i++) {
+            out.print("  ");
+        }
+        out.println(s);
+        shift++;
+    }
+
+    void out_print(String s) {
+        for (int i = 0; i < shift; i++) {
+            out.print("  ");
+        }
+        out.print(s);
+    }
+
+    void out_println(String s) {
+        for (int i = 0; i < shift; i++) {
+            out.print("  ");
+        }
+        out.println(s);
+    }
+
+    void out_end(String s) {
+        shift--;
+        for (int i = 0; i < shift; i++) {
+            out.print("  ");
+        }
+        out.println(s);
+    }
+
+    String startArray(int length) {
+        return "[" + (printDetails ? Integer.toString(length) : "") + "]";
+    }
+
+    void printBytes(DataInputStream in, int len) throws IOException {
+        try {
+            for (int i = 0; i < len; i++) {
+                if (i % 8 == 0) {
+                    out_print("0x");
+                }
+                printByteHex(out, in.readByte());
+                if (i % 8 == 7) {
+                    out.println(";");
+                }
+            }
+        } finally {
+            if (len % 8 != 0) {
+                out.println(";");
+            }
+        }
+    }
+
+    /*========================================================*/
+    static final int EXPORT_MAGIC = 0x00FACADE;
+    static final int HEADER_MAGIC = 0xDECAFFED;
+    static String[] compNames = {
+        "Header",
+        "Directory",
+        "Applet",
+        "Import",
+        "ConstantPool",
+        "Class",
+        "Method",
+        "StaticField",
+        "RefLocation",
+        "Export",
+        "Descriptor"
+    };
+
+    static String compName(int compNum) {
+        try {
+            return compNames[compNum - 1];
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return "tag " + compNum + "???";
+        }
+    }
+    String[] cPoolStrings;
+
+    void decodeAttr(DataInputStream in) throws IOException {
+        int name_cpx = in.readUnsignedShort(), len = in.readInt();
+        String AttrName = null;
+        String endingComment = "Attr(#" + name_cpx + ")";
+        try {
+            endingComment = AttrName = cPoolStrings[name_cpx];
+        } catch (ArrayIndexOutOfBoundsException e) {
+        }
+        if (printDetails) {
+            out_begin("Attr(#" + name_cpx + ", " + len + ") { // " + AttrName);
+        } else {
+            out_begin("Attr(#" + name_cpx + ") { // " + AttrName);
+        }
+        if (AttrName == null) {
+            printBytes(in, len);
+        } else if (AttrName.equals("ConstantValue")) {
+            if (len != 2) {
+                out_println("// invalid length of ConstantValue attr: " + len + " (should be 2)");
+                printBytes(in, len);
+            } else {
+                out_println("#" + in.readUnsignedShort() + ";");
+            }
+        } else {
+            printBytes(in, len);
+        }
+        out_end("} // end " + endingComment);
+    }
+
+    void decodeExp(String inpName) throws IOException {
+        DataInputStream in = new DataInputStream(new FileInputStream(inpName));
+        out_println("file " + inpName);
+        out_begin("{  // export file");
+
+        int magic = in.readInt();
+        out_print(toHex(magic, 4) + ";  // ");
+        if (magic != EXPORT_MAGIC) {
+            out.print("wrong magic: 0x" + Integer.toString(EXPORT_MAGIC, 16) + " expected");
+        } else {
+            out_print("magic");
+        }
+        out.println();
+        out_println(in.readUnsignedByte() + "b;  // minor version");
+        out_println(in.readUnsignedByte() + "b;  // major version");
+
+        int cp_count = in.readUnsignedShort();
+        cPoolStrings = new String[cp_count];
+        out_begin(startArray(cp_count) + " { //  Constant Pool");
+        for (int i = 0; i < cp_count; i++) {
+            int tag = in.readUnsignedByte();
+            ConstType tg = constType(tag);
+            switch (tg) {
+                case CONSTANT_UTF8:
+                    out_print("Utf8 \"");
+
+                    StringBuffer sb = new StringBuffer();
+                    String s = in.readUTF();
+                    cPoolStrings[i] = s;
+                    for (int k = 0; k < s.length(); k++) {
+                        char c = s.charAt(k);
+                        switch (c) {
+                            case '\t':
+                                sb.append('\\').append('t');
+                                break;
+                            case '\n':
+                                sb.append('\\').append('n');
+                                break;
+                            case '\r':
+                                sb.append('\\').append('r');
+                                break;
+                            case '\"':
+                                sb.append('\\').append('\"');
+                                break;
+                            default:
+                                sb.append(c);
+                        }
+                    }
+                    out.println(sb.append("\"; // #").append(i).toString());
+                    break;
+
+                case CONSTANT_INTEGER:
+                    out_println("int " + toHex(in.readInt(), 4) + "; // #" + i);
+                    break;
+
+                case CONSTANT_CLASS:
+                    out_println("class #" + in.readUnsignedShort() + "; // #" + i);
+                    break;
+
+                case CONSTANT_JAVACARD_PACKAGE:
+                    out_begin("package { // #" + i);
+                    out_println(toHex(in.readUnsignedByte(), 1) + ";  // flags");
+                    out_println("#" + in.readUnsignedShort() + "; // name");
+                    out_println(in.readUnsignedByte() + "b;  // minor version");
+                    out_println(in.readUnsignedByte() + "b;  // major version");
+                    int aid_len = in.readUnsignedByte();
+                    out_begin("Bytes" + startArray(aid_len) + "b {");
+                    printBytes(in, aid_len);
+                    out_end("};"); // Bytes[]
+                    out_end("};"); // package info
+                    break;
+
+                default:
+                    throw new Error("invalid constant type: " + (int) tag);
+            }
+        }
+        ;
+        out_end("} // Constant pool");
+        out_println("#" + in.readUnsignedShort() + ";  // this package");
+        int class_count = in.readUnsignedByte();
+        out_begin(startArray(class_count) + "b { //  classes");
+        for (int i = 0; i < class_count; i++) {
+            out_begin("{ // class " + i);
+
+            out_println(in.readUnsignedByte() + "b; // token");
+
+            int flags = in.readUnsignedShort();
+            out_print("0x");
+            printByteHex(out, flags >> 8);
+            printByteHex(out, flags);
+            out.println("; // flags");
+
+            out_println("#" + in.readUnsignedShort() + ";  // this class");
+
+            int sup_count = in.readUnsignedShort();
+            out_begin(startArray(sup_count) + " { // supers");
+            for (int k = 0; k < sup_count; k++) {
+                out_println("#" + in.readUnsignedShort() + ";");
+            }
+            out_end("} // supers");
+
+            int int_count = in.readUnsignedByte();
+            out_begin(startArray(int_count) + "b { // interfaces");
+            for (int k = 0; k < int_count; k++) {
+                out_println("#" + in.readUnsignedShort() + ";");
+            }
+            out_end("} // interfaces");
+
+            int field_count = in.readUnsignedShort();
+            out_begin(startArray(field_count) + " { // fields");
+            for (int k = 0; k < field_count; k++) {
+                out_begin("{ // field " + k);
+                out_println(in.readUnsignedByte() + "b; // token");
+
+                int f_flags = in.readUnsignedShort();
+                out_print("0x");
+                printByteHex(out, f_flags >> 8);
+                printByteHex(out, f_flags);
+                out.println("; // flags");
+
+                out_println("#" + in.readUnsignedShort() + ";  // this field name");
+                out_println("#" + in.readUnsignedShort() + ";  // this field descriptor");
+
+                int attr_count = in.readUnsignedShort();
+                out_begin(startArray(attr_count) + " { // Attributes");
+                for (int ai = 0; ai < attr_count; ai++) {
+                    decodeAttr(in);
+                }
+                out_end("} // Attributes");
+                out_end("};");
+            }
+            out_end("} // fields");
+
+            int mth_count = in.readUnsignedShort();
+            out_begin(startArray(mth_count) + " { // methods");
+            for (int k = 0; k < mth_count; k++) {
+                out_begin("{ // method " + k);
+                out_println(in.readUnsignedByte() + "b; // token");
+
+                int mth_flags = in.readUnsignedShort();
+                out_print("0x");
+                printByteHex(out, mth_flags >> 8);
+                printByteHex(out, mth_flags);
+                out.println("; // flags");
+
+                out_println("#" + in.readUnsignedShort() + ";  // this method name");
+                out_println("#" + in.readUnsignedShort() + ";  // this method descriptor");
+                out_end("};");
+            }
+            out_end("} // methods");
+            out_end("};");
+        }
+        out_end("} // classes");
+        endComponent(in);
+    }
+
+    DataInputStream beginComponent(String inpName) throws IOException {
+        DataInputStream in = new DataInputStream(new FileInputStream(inpName));
+        out_println("file " + inpName);
+
+        int tag = in.readUnsignedByte();
+        out_print("Component(" + tag);
+        int size = in.readUnsignedShort();
+        if (printDetails) {
+            out.print(", " + size);
+        }
+        out_begin(") { // " + compName(tag));
+        return in;
+    }
+
+    void endComponent(DataInputStream in) throws IOException {
+        out_end("};"); // Component
+        int avail = in.available();
+        if (avail > 0) {
+            out.println("=========== extra bytes:");
+            for (int k = 0; k < 8; k++) {
+                printBytes(in, avail >= 8 ? 8 : avail);
+                avail = in.available();
+                if (avail == 0) {
+                    break;
+                }
+            }
+            if (avail > 0) {
+                out.println("  there is also " + avail + " bytes available");
+            }
+        }
+        in.close();
+    }
+
+    ArrayList<Integer> methodsLengths = null;
+    ArrayList<Integer> methodsOffsets = null;
+
+    void decodeHeader(String inpName) throws IOException {
+        DataInputStream in = beginComponent(inpName);
+
+        int magic = in.readInt();
+        out_print(toHex(magic, 4) + ";  // ");
+        if (magic != HEADER_MAGIC) {
+            out.print("wrong magic: 0x" + Integer.toString(HEADER_MAGIC, 16) + " expected");
+        } else {
+            out_print("magic");
+        }
+        out.println();
+        out_println(in.readUnsignedByte() + "b;  // minor version");
+        out_println(in.readUnsignedByte() + "b;  // major version");
+        out_println(toHex(in.readUnsignedByte(), 1) + ";  // flags");
+
+        out_begin("{  // package info");
+        out_println(in.readUnsignedByte() + "b;  // minor version");
+        out_println(in.readUnsignedByte() + "b;  // major version");
+        int aid_len = in.readUnsignedByte();
+        out_begin("Bytes" + startArray(aid_len) + "b {");
+        printBytes(in, aid_len);
+        out_end("};"); // Bytes[]
+        out_end("};"); // package info
+        endComponent(in);
+    }
+
+    void decodeDirectory(String inpName) throws IOException {
+        DataInputStream in = beginComponent(inpName);
+
+        int i;
+        out_begin("{  // component sizes");
+        for (i = 0; i < 11; i++) {
+            out_println(in.readUnsignedShort() + ";  // " + (i + 1));
+        }
+        out_end("};");
+
+        out_begin("{  // static field size");
+        out_println(in.readUnsignedShort() + ";  // image size");
+        out_println(in.readUnsignedShort() + ";  // array init count");
+        out_println(in.readUnsignedShort() + ";  // array init size");
+        out_end("};");
+
+        out_println(in.readUnsignedByte() + "b;  // import count");
+        out_println(in.readUnsignedByte() + "b;  // applet count");
+
+        int custom_count = in.readUnsignedByte();
+        out_begin(startArray(custom_count) + "b { // custom components");
+        for (i = 0; i < custom_count; i++) {
+            out_print("Comp(" + in.readUnsignedByte());  // tag;
+            int size2 = in.readUnsignedShort();
+            if (printDetails) {
+                out_print(", " + size2);
+            }
+            out_begin(") {");
+            int aid_len = in.readUnsignedByte();
+            out_begin("Bytes" + startArray(aid_len) + "b {");
+            printBytes(in, aid_len);
+            out_end("};");
+            out_end("};");
+        }
+        out_end("};");
+
+        endComponent(in);
+    }
+
+    void decodeApplet(String inpName) throws IOException {
+        DataInputStream in = beginComponent(inpName);
+
+        int applet_count = in.readUnsignedByte();
+        out_begin(startArray(applet_count) + "b { // applets");
+        for (int i = 0; i < applet_count; i++) {
+            out_begin("{ // applet " + i);
+            int aid_len = in.readUnsignedByte();
+            out_begin("Bytes" + startArray(aid_len) + "b {");
+            printBytes(in, aid_len);
+            out_end("};"); // Bytes[]
+            out_println(in.readUnsignedShort() + ";  // install method offset");
+            out_end("};"); // applet
+        }
+        out_end("};"); // applets
+        endComponent(in);
+    }
+
+    void decodeImport(String inpName) throws IOException {
+        DataInputStream in = beginComponent(inpName);
+
+        int package_count = in.readUnsignedByte();
+        out_begin(startArray(package_count) + "b { //  packages");
+        for (int i = 0; i < package_count; i++) {
+            out_begin("{ // package " + i);
+            out_println(in.readUnsignedByte() + "b;  // minor version");
+            out_println(in.readUnsignedByte() + "b;  // major version");
+            int aid_len = in.readUnsignedByte();
+            out_begin("Bytes" + startArray(aid_len) + "b {");
+            printBytes(in, aid_len);
+            out_end("};"); // Bytes[]
+            out_end("};"); // package info
+        }
+        out_end("};"); //  package info
+        endComponent(in);
+    }
+
+    static String[] refNames = {
+        "Classref",
+        "InstanceFieldref",
+        "VirtualMethodref",
+        "SuperMethodref",
+        "StaticFieldref",
+        "StaticMethodref"
+    };
+
+    void decodeConstantPool(String inpName) throws IOException {
+        DataInputStream in = beginComponent(inpName);
+
+        int items_count = in.readUnsignedShort();
+        out_begin(startArray(items_count) + " { //  items");
+        for (int i = 0; i < items_count; i++) {
+            int tag = in.readUnsignedByte();
+            int info1 = in.readUnsignedByte(),
+                    info2 = in.readUnsignedByte(),
+                    info3 = in.readUnsignedByte();
+            out_print(tag + "b ");
+            if ((tag > 0) && (tag <= 6)) {
+                if ((info1 & 0x80) == 0) {
+                    if (tag <= 4) {
+                        out_print(((info1 << 8) | info2) + " " + info3 + "b;");
+                    } else {
+                        out_print(info1 + "b " + ((info2 << 8) | info3) + ";");
+                    }
+                    out.print(" // internal ");
+                } else {
+                    out.print(info1 + "b " + info2 + "b " + info3 + "b;");
+                    out.print(" // external ");
+                }
+                out.println(refNames[tag - 1]);
+            } else {
+                out.print(info1 + "b " + info2 + "b " + info3 + "b;");
+                out.println(" // unknown tag ");
+            }
+        }
+        out_end("};"); //  CP array
+        endComponent(in);
+    }
+
+    void printClassref(DataInputStream in) throws IOException {
+        int info1 = in.readUnsignedByte(),
+                info2 = in.readUnsignedByte();
+        if ((info1 & 0x80) == 0) {
+            out_print(((info1 << 8) | info2) + ";");
+            out_print(" // internal ");
+        } else {
+            out_print(info1 + "b " + info2 + "b;");
+            out_print(" // external ");
+        }
+        out_println(" Classref ");
+    }
+
+    void decodeClass(String inpName) throws IOException {
+        DataInputStream in = beginComponent(inpName);
+
+        for (int i = 0; in.available() > 0; i++) {
+            out_begin("{ // class " + i);
+            int bitfield = in.readUnsignedByte();
+            int interface_count = bitfield & 0x0F;
+            out_print("0x");
+            printByteHex(out, bitfield);
+            out.println("; // bitfield");
+            if ((bitfield & 0x80) != 0) {
+                // interface
+                for (int k = 0; k < interface_count; k++) {
+                    printClassref(in);
+                }
+            } else {
+                // class
+                printClassref(in);
+                out_println(in.readUnsignedByte() + "b;  // declared instance size");
+                out_println(in.readUnsignedByte() + "b;  // first reference token");
+                out_println(in.readUnsignedByte() + "b;  // reference count");
+                out_println(in.readUnsignedByte() + "b;  // public method table base");
+                int pumrc = in.readUnsignedByte();
+                out_println(pumrc + "b;  // public method table count");
+                out_println(in.readUnsignedByte() + "b;  // package method table base");
+                int pamrc = in.readUnsignedByte();
+                out_println(pamrc + "b;  // package method table count");
+                out_begin("{ // public method table");
+                for (int k = 0; k < pumrc; k++) {
+                    out_println(in.readUnsignedShort() + ";");
+                }
+                out_end("};");
+                out_begin("{ // package method table");
+                for (int k = 0; k < pamrc; k++) {
+                    out_println(in.readUnsignedShort() + ";");
+                }
+                out_end("};");
+                out_begin("{ // implemented interfaces");
+                for (int k = 0; k < interface_count; k++) {
+                    out_begin("{ // interface " + k);
+                    printClassref(in);
+                    int count = in.readUnsignedByte();
+                    out_begin("Bytes" + startArray(count) + "b {");
+                    printBytes(in, count);
+                    out_end("};"); // Bytes[]
+                    out_end("};");
+                }
+                out_end("};");
+            }
+            out_end("};");
+        }
+        endComponent(in);
+    }
+
+    void decodeDescriptor(String inpName) throws IOException {
+        DataInputStream in = beginComponent(inpName);
+
+        methodsLengths = new ArrayList<>();
+        methodsOffsets = new ArrayList<>();
+        int class_count = in.readUnsignedByte();
+        out_begin(startArray(class_count) + "b { // classes");
+        for (int c = 0; c < class_count; c++) {
+            out_begin("{ // class " + c);
+            out_println(in.readUnsignedByte() + "b; // token");
+            out_print("0x");
+            printByteHex(out, in.readUnsignedByte());
+            out.println("; // flags");
+            printClassref(in);
+            int icount = in.readUnsignedByte();
+            out_println(icount + "b; // interface count");
+            int fcount = in.readUnsignedShort();
+            out_println(fcount + "; // field count");
+            int mcount = in.readUnsignedShort();
+            out_println(mcount + "; // method count");
+            if (icount != 0) {
+                out_begin("{ // interfaces");
+                for (int i = 0; i < icount; i++) {
+                    printClassref(in);
+                }
+                out_end("};");
+            }
+            for (int i = 0; i < fcount; i++) {
+                out_begin("{ // field " + i);
+                out_println(in.readUnsignedByte() + "b; // token");
+                int flags = in.readUnsignedByte();
+                out_print("0x");
+                printByteHex(out, flags);
+                out.println("; // flags");
+                if ((flags & 0x08) == 0) {
+                    printClassref(in);
+                    out_println(in.readUnsignedByte() + "b; // token");
+                } else { // static field
+                    int info1 = in.readUnsignedByte(),
+                            info2 = in.readUnsignedByte(),
+                            info3 = in.readUnsignedByte();
+                    if ((info1 & 0x80) == 0) {
+                        out_print(info1 + "b " + ((info2 << 8) | info3) + ";");
+                        out.println(" // internal field");
+                    } else {
+                        out.print(info1 + "b " + info2 + "b " + info3 + "b;");
+                        out.println(" // external field");
+                    }
+                }
+                int type = in.readUnsignedShort();
+                if ((type & 0x8000) == 0) {
+                    out_println(type + "; // reference type");
+                } else {
+                    out_print("0x");
+                    printByteHex(out, type >> 8);
+                    printByteHex(out, type);
+                    out.println("; // primitive type");
+                }
+                out_end("};");
+            }
+            for (int i = 0; i < mcount; i++) {
+                int token = in.readUnsignedByte();
+                int flags = in.readUnsignedByte();
+                int m_offset = in.readUnsignedShort();
+                int t_offset = in.readUnsignedShort();
+                int bytecode_count = in.readUnsignedShort();
+                if (m_offset != 0) {
+                    out_begin("{ // method " + i + " (" + methodsLengths.size() + ")");
+                    methodsLengths.add(bytecode_count);
+                    methodsOffsets.add(m_offset);
+                } else {
+                    out_begin("{ // method " + i);
+                }
+                out_println(token + "b; // token");
+                out_print("0x");
+                printByteHex(out, flags);
+                out.println("; // flags");
+                out_println(m_offset + "; // method offset");
+                out_println(t_offset + "; // type offset");
+                out_println(bytecode_count + "; // bytecode count");
+                out_println(in.readUnsignedShort() + "; // exception handler count");
+                out_println(in.readUnsignedShort() + "; // exception handler index");
+                out_end("};");
+            }
+            out_end("};"); // class i
+        }
+        out_end("}; // classes");
+
+        int cp_count = in.readUnsignedShort();
+        out_begin(startArray(cp_count) + " { // constant pool types");
+        for (int i = 0; i < cp_count; i++) {
+            int type = in.readUnsignedShort();
+            if (type == 0xFFFF) {
+                out_println("0xFFFF;");
+            } else {
+                out_println(type + "; ");
+            }
+        }
+        out_end("}; // constant pool types");
+
+        out_begin("{ // type descriptors");
+        for (int i = 0; in.available() > 0; i++) {
+            int nibble_count = in.readUnsignedByte();
+            out_print(nibble_count + "b; ");
+            printBytes(in, (nibble_count + 1) / 2);
+        }
+        out_end("}; // type descriptors");
+        endComponent(in);
+    }
+
+    void decodeMethod(String inpName) throws IOException {
+        DataInputStream in = beginComponent(inpName);
+
+        int handler_count = in.readUnsignedByte();
+        out_begin(startArray(handler_count) + "b { // exception handlers");
+        for (int i = 0; i < handler_count; i++) {
+            out_print(in.readUnsignedShort() + ", ");
+            int bitfield = in.readUnsignedShort();
+            out.print("0x");
+            printByteHex(out, bitfield >> 8);
+            printByteHex(out, bitfield);
+            out.print(", " + in.readUnsignedShort() + ", ");
+            out.println(in.readUnsignedShort() + "; // handler " + i);
+        }
+        out_end("};"); // handlers
+
+        if (methodsLengths == null) {
+            out.println("// Descriptor.cap absent - methods not printed");
+        } else {
+            int f_offset = 1 + handler_count * 8;
+            for (int i = 0; i < methodsLengths.size(); i++) {
+                out_begin("{ // method " + i);
+                int m_offset = methodsOffsets.get(i);
+                if (m_offset != f_offset) {
+                    out.println("file offset=" + f_offset + " but m_offset=" + m_offset);
+                    break;
+                }
+                int bitfield = in.readUnsignedByte();
+                if ((bitfield & 0x80) == 0) {
+                    out_print("0x");
+                    printByteHex(out, bitfield);
+                    out.println("; // flags, max_stack");
+                    out_print("0x");
+                    printByteHex(out, in.readUnsignedByte());
+                    out.println("; // nargs, max_locals");
+                    f_offset += 2;
+                } else {
+                    out_print("0x");
+                    printByteHex(out, bitfield);
+                    out.println("; // flags, padding");
+                    out_println(in.readUnsignedByte() + "b; // max_stack");
+                    out_println(in.readUnsignedByte() + "b; // nargs");
+                    out_println(in.readUnsignedByte() + "b; // max_locals");
+                    f_offset += 4;
+                }
+                int bytecode_count = methodsLengths.get(i);
+                out_begin("{ // bytecodes");
+                printBytes(in, bytecode_count);
+                f_offset += bytecode_count;
+                out_end("};");
+                out_end("};");
+            }
+        }
+
+        endComponent(in);
+    }
+
+    void decodeStaticField(String inpName) throws IOException {
+        DataInputStream in = beginComponent(inpName);
+
+        int image_size = in.readUnsignedShort();
+        out_println(image_size + "; // image size");
+        int reference_count = in.readUnsignedShort();
+        out_println(reference_count + "; // reference count");
+        int array_init_count = in.readUnsignedShort();
+        out_begin(startArray(array_init_count) + " { // array_init_info");
+        for (int i = 0; i < array_init_count; i++) {
+            out_println(in.readUnsignedByte() + "b // type ");
+            int count = in.readUnsignedShort();
+            out_begin("Bytes" + startArray(count) + "s { // values");
+            printBytes(in, count);
+            out_end("};"); // Bytes[]
+        }
+        out_end("};"); // array_init_info
+        int default_value_count = in.readUnsignedShort();
+        out_println(default_value_count + "; // default value count");
+        int non_default_value_count = in.readUnsignedShort();
+        out_begin("Bytes" + startArray(non_default_value_count) + "s { // non default values");
+        printBytes(in, non_default_value_count);
+        out_end("};"); // Bytes[]
+
+        endComponent(in);
+    }
+
+    void decodeRefLocation(String inpName) throws IOException {
+        DataInputStream in = beginComponent(inpName);
+
+        int byte_index_count = in.readUnsignedShort();
+        out_begin("Bytes" + startArray(byte_index_count) + "s { // offsets to byte indices");
+        printBytes(in, byte_index_count);
+        out_end("};"); // Bytes[]
+
+        byte_index_count = in.readUnsignedShort();
+        out_begin("Bytes" + startArray(byte_index_count) + "s { // offsets to byte2 indices");
+        printBytes(in, byte_index_count);
+        out_end("};"); // Bytes[]
+
+        endComponent(in);
+    }
+
+    void decodeExport(String inpName) throws IOException {
+        DataInputStream in = beginComponent(inpName);
+        int class_count = in.readUnsignedByte();
+        out_begin(startArray(class_count) + "b { // classes");
+        for (int i = 0; i < class_count; i++) {
+            out_begin("{ // class " + i);
+            out_println(in.readUnsignedShort() + "; // class offset");
+            int fcount = in.readUnsignedByte();
+            out_println(fcount + "b; // static field count");
+            int mcount = in.readUnsignedByte();
+            out_println(mcount + "b; // static method count");
+            out_begin("{ // static field offsets");
+            for (int j = 0; j < fcount; j++) {
+                out_println(in.readUnsignedShort() + "; // field " + j + " offset");
+            }
+            out_end("};");
+            out_begin("{ // static method offsets");
+            for (int j = 0; j < mcount; j++) {
+                out_println(in.readUnsignedShort() + "; // method " + j + " offset");
+            }
+            out_end("};");
+            out_end("};"); // class i
+        }
+        out_end("};"); // classes
+        endComponent(in);
+    }
+    /*========================================================*/
+
+    /**
+     * Constructor.
+     */
+    public Main(PrintWriter out, String program) {
+        this.out = out;
+        this.program = program;
+    }
+
+    public void error(String msg) {
+        out.println(program + ": " + msg);
+    }
+
+    /**
+     * Usage
+     */
+    public void usage() {
+        out.println(i18n.getString("jcdec.usage"));
+        out.println(i18n.getString("jcdec.opt.g"));
+        out.println(i18n.getString("jcdec.opt.version"));
+    }
+
+    /**
+     * Run the decoder
+     */
+    public synchronized boolean decode(String argv[]) {
+//      int flags = F_WARNINGS;
+        long tm = System.currentTimeMillis();
+        ArrayList<String> vargs = new ArrayList<>();
+        ArrayList<String> vj = new ArrayList<>();
+        boolean nowrite = false;
+        int addOptions = 0;
+
+        // Parse arguments
+        for (int i = 0; i < argv.length; i++) {
+            String arg = argv[i];
+            if (arg.equals("-g")) {
+                printDetails = true;
+                vargs.add(arg);
+            } else if (arg.equals("-v")) {
+                DebugFlag = true;
+                vargs.add(arg);
+                out.println("arg[" + i + "]=" + argv[i] + "/verbose");
+            } else if (arg.equals("-version")) {
+                out.println(ProductInfo.FULL_VERSION);
+            } else if (arg.startsWith("-")) {
+//out.println("arg["+i+"]="+argv[i]+"/invalid flag");
+                error(i18n.getString("jcdec.error.invalid_flag", arg));
+                usage();
+                return false;
+            } else {
+                vargs.add(arg);
+                vj.add(arg);
+            }
+        }
+
+        if (vj.isEmpty()) {
+            usage();
+            return false;
+        }
+
+//        String[] names = new String[vj.size()];
+//        vj.copyInto(names);
+        String[] names = null;
+        names = vj.toArray(names);
+decode:
+        for (int k = 0; k < names.length; k++) {
+            String inpname = names[k];
+            try {
+                if (inpname.endsWith(".cap")) {
+                    String shortName = inpname.substring(0, inpname.length() - 4);
+                    if (shortName.endsWith("Header")) {
+                        decodeHeader(inpname);
+                    } else if (shortName.endsWith("Directory")) {
+                        decodeDirectory(inpname);
+                    } else if (shortName.endsWith("Applet")) {
+                        decodeApplet(inpname);
+                    } else if (shortName.endsWith("Import")) {
+                        decodeImport(inpname);
+                    } else if (shortName.endsWith("ConstantPool")) {
+                        decodeConstantPool(inpname);
+                    } else if (shortName.endsWith("Class")) {
+                        decodeClass(inpname);
+                    } else if (shortName.endsWith("Descriptor")) {
+                        decodeDescriptor(inpname);
+                    } else if (shortName.endsWith("Method")) {
+                        decodeMethod(inpname);
+                    } else if (shortName.endsWith("StaticField")) {
+                        decodeStaticField(inpname);
+                    } else if (shortName.endsWith("RefLocation")) {
+                        decodeRefLocation(inpname);
+                    } else if (shortName.endsWith("Export")) {
+                        decodeExport(inpname);
+                    } else {
+                        continue decode;
+                    }
+                    out.println("");
+                } else if (inpname.endsWith(".exp")) {
+                    decodeExp(inpname);
+                    out.println("");
+                }
+                continue decode;
+            } catch (FileNotFoundException ee) {
+                error(i18n.getString("jcdec.error.cannot_read", inpname));
+            } catch (Error ee) {
+                ee.printStackTrace();
+                error(i18n.getString("jcdec.error.fatal_error"));
+            } catch (Exception ee) {
+                ee.printStackTrace();
+                error(i18n.getString("jcdec.error.fatal_exception"));
+            }
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Main program
+     */
+    public static void main(String argv[]) {
+        Main decoder = new Main(new PrintWriter(new uEscWriter(System.out)), "jcdec");
+        System.exit(decoder.decode(argv) ? 0 : 1);
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jcdec/i18n.properties b/test/lib/org/openjdk/asmtools/jcdec/i18n.properties
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jcdec/i18n.properties
@@ -0,0 +1,34 @@
+# Copyright (c) 2014, 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.
+
+jcdec.error.invalid_flag=invalid flag: " {0}
+jcdec.error.fatal_error=fatal error
+jcdec.error.fatal_exception=fatal exception
+jcdec.error.cannot_read=cannot read {0}
+jcdec.usage=\
+Usage: java -jar asmtools.jar jcdec [options] FILE.class... > FILE.jcod\n\
+where possible options include:
+
+jcdec.opt.g=\
+\     -g:  detailed output format
+jcdec.opt.version=\
+\     -version:  print version number and date
+
diff a/test/lib/org/openjdk/asmtools/jcoder/ByteBuffer.java b/test/lib/org/openjdk/asmtools/jcoder/ByteBuffer.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jcoder/ByteBuffer.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2009, 2019, 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 org.openjdk.asmtools.jcoder;
+
+/**
+ * Compiles just 1 source file
+ */
+class ByteBuffer extends java.io.OutputStream {
+
+    String myname;
+    /**
+     * The buffer where elements are stored.
+     */
+    byte data[];
+    /**
+     * The number of elements in the buffer.
+     */
+    int length;
+    /**
+     * The size of the increment. If it is 0 the size of the the buffer is doubled
+     * everytime it needs to grow.
+     */
+    protected int capacityIncrement;
+
+    /**
+     * Constructs an empty vector with the specified storage capacity and the specified
+     * capacityIncrement.
+     *
+     * @param initialCapacity the initial storage capacity of the vector
+     * @param capacityIncrement how much to increase the element's size by.
+     */
+    public ByteBuffer(int initialCapacity, int capacityIncrement) {
+//      super();
+        this.data = new byte[initialCapacity];
+        this.capacityIncrement = capacityIncrement;
+    }
+
+    /**
+     * Constructs an empty vector with the specified storage capacity.
+     *
+     * @param initialCapacity the initial storage capacity of the vector
+     */
+    public ByteBuffer(int initialCapacity) {
+        this(initialCapacity, 0);
+    }
+
+    /**
+     * Constructs an empty vector.
+     */
+    public ByteBuffer() {
+        this(30);
+    }
+
+    /**
+     * Constructs a full vector.
+     */
+    public ByteBuffer(byte data[], int capacityIncrement) {
+        this.length = data.length;
+        this.data = data;
+        this.capacityIncrement = capacityIncrement;
+    }
+
+    /**
+     * Constructs a full vector.
+     */
+    public ByteBuffer(byte data[]) {
+        this(data, 0);
+    }
+
+    /**
+     * Returns the number of elements in the vector. Note that this is not the same as the
+     * vector's capacity.
+     */
+    public final int size() {
+        return length;
+    }
+
+    /**
+     * Ensures that the vector has at least the specified capacity.
+     *
+     * @param minCapacity the desired minimum capacity
+     */
+    public final synchronized void ensureCapacity(int minCapacity) {
+        int oldCapacity = data.length;
+        if (minCapacity <= oldCapacity) {
+            return;
+        }
+        byte oldData[] = data;
+        int newCapacity = (capacityIncrement > 0) ? (oldCapacity + capacityIncrement) : (oldCapacity * 2);
+        if (newCapacity < minCapacity) {
+            newCapacity = minCapacity;
+        }
+        data = new byte[newCapacity];
+        System.arraycopy(oldData, 0, data, 0, length);
+    }
+
+    /*======================================*/
+    public void write(int val) {
+        ensureCapacity(length + 1);
+        data[length++] = (byte) val;
+    }
+
+    public void writeAt(int index, long val, int width) {
+        for (int i = 0; i < width; i++) {
+            data[index + i] = (byte) (val >> (width - 1 - i) * 8);
+        }
+    }
+
+    public void append(long val, int width) {
+        ensureCapacity(length + width);
+        writeAt(length, val, width);
+        length += width;
+    }
+
+    /*======================================================*/
+} // end ByteBuffer
+
diff a/test/lib/org/openjdk/asmtools/jcoder/ErrorMessage.java b/test/lib/org/openjdk/asmtools/jcoder/ErrorMessage.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jcoder/ErrorMessage.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2009, 2014, 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 org.openjdk.asmtools.jcoder;
+
+/**
+ * A sorted list of error messages
+ */
+final class ErrorMessage {
+
+    int where;
+    String message;
+    ErrorMessage next;
+
+    /**
+     * Constructor
+     */
+    ErrorMessage(int where, String message) {
+        this.where = where;
+        this.message = message;
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jcoder/JcodTokens.java b/test/lib/org/openjdk/asmtools/jcoder/JcodTokens.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jcoder/JcodTokens.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 1996, 2017, 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 org.openjdk.asmtools.jcoder;
+
+import static org.openjdk.asmtools.jasm.Tables.*;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+
+/**
+ *
+ * JcodTokens
+ *
+ * This class contains tokens specific to parsing JCOD syntax.
+ *
+ * The classes in JcodTokens are following a Singleton Pattern. These classes are Enums,
+ * and they are contained in private hash maps (lookup tables and reverse lookup tables).
+ * These hash maps all have public accessors, which clients use to look-up enums.
+ *
+ * Tokens in this table carry no external state, and are typically treated as constants.
+ * They do not need to be reset.
+ */
+public class JcodTokens {
+
+    /*-------------------------------------------------------- */
+    /* Marker: describes the type of Keyword */
+    static public enum KeywordType {
+
+        TOKEN(0, "TOKEN"),
+        KEYWORD(3, "KEYWORD");
+
+        private final Integer value;
+        private final String printval;
+
+        KeywordType(Integer val, String print) {
+            value = val;
+            printval = print;
+        }
+
+        public String printval() {
+            return printval;
+        }
+    }
+
+    /*-------------------------------------------------------- */
+    /* Marker - describes the type of token */
+    /*    this is rather cosmetic, no function currently. */
+    static public enum TokenType {
+        VALUE               (0, "Value"),
+        KEYWORDS            (1, "Keywords"),
+        PUNCTUATION         (2, "Punctuation"),
+        JDEC                (3, "JDec"),
+        STACKMAP            (4, "StackMap"),
+        MISC                (5, "Misc");
+
+        private final Integer value;
+        private final String printval;
+
+        TokenType(Integer val, String print) {
+            value = val;
+            printval = print;
+        }
+
+        public String printval() {
+            return printval;
+        }
+    }
+
+  /*-------------------------------------------------------- */
+  /** Scanner Tokens (Definitive List) */
+    static public enum Token {
+        EOF                 (-1, "EOF",             "EOF",          TokenType.MISC),
+        IDENT               (60, "IDENT",           "IDENT",        TokenType.VALUE),
+        LONGSTRINGVAL       (61, "LONGSTRINGVAL",   "LONGSTRING",   TokenType.VALUE),
+        INTVAL              (65, "INTVAL",          "INT",          TokenType.VALUE),
+        LONGVAL             (66, "LONGVAL",         "LONG",         TokenType.VALUE),
+        STRINGVAL           (69, "STRINGVAL",       "STRING",       TokenType.VALUE),
+
+        CLASS               (70, "CLASS",           "class",        TokenType.KEYWORDS, KeywordType.KEYWORD),
+        INTERFACE           (71, "INTERFACE",       "interface",    TokenType.KEYWORDS, KeywordType.KEYWORD),
+        DIV                 (72, "DIV",             "div",          TokenType.KEYWORDS),
+        EQ                  (73, "EQ",              "eq",           TokenType.KEYWORDS),
+        ASSIGN              (74, "ASSIGN",          "assign",       TokenType.KEYWORDS),
+        MODULE              (75, "MODULE",          "module",       TokenType.KEYWORDS, KeywordType.KEYWORD),
+
+        COLON               (134, "COLON",        ":",    TokenType.PUNCTUATION),
+        SEMICOLON           (135, "SEMICOLON",    ";",    TokenType.PUNCTUATION, KeywordType.KEYWORD),
+        COMMA               (0,   "COMMA",        ",",    TokenType.PUNCTUATION, KeywordType.KEYWORD),
+        LBRACE              (138, "LBRACE",       "{",    TokenType.PUNCTUATION, KeywordType.KEYWORD),
+        RBRACE              (139, "RBRACE",       "}",    TokenType.PUNCTUATION, KeywordType.KEYWORD),
+        LPAREN              (140, "LPAREN",       "(",    TokenType.PUNCTUATION, KeywordType.KEYWORD),
+        RPAREN              (141, "RPAREN",       ")",    TokenType.PUNCTUATION, KeywordType.KEYWORD),
+        LSQBRACKET          (142, "LSQBRACKET",   "[",    TokenType.PUNCTUATION, KeywordType.KEYWORD),
+        RSQBRACKET          (143, "RSQBRACKET",   "]",    TokenType.PUNCTUATION, KeywordType.KEYWORD),
+
+
+        BYTEINDEX           (156, "BYTEINDEX",   "b",       TokenType.JDEC, KeywordType.KEYWORD),
+        SHORTINDEX          (157, "SHORTINDEX",  "s",       TokenType.JDEC, KeywordType.KEYWORD),
+        ATTR                (158, "ATTR",        "Attr",    TokenType.JDEC, KeywordType.KEYWORD),
+        BYTES               (159, "BYTES",       "Bytes",   TokenType.JDEC, KeywordType.KEYWORD),
+        MACRO               (160, "MACRO",        "Attr",    TokenType.JDEC),
+        COMP                (161, "COMP",        "Component", TokenType.JDEC, KeywordType.KEYWORD),
+        FILE                (162, "FILE",        "file",    TokenType.JDEC, KeywordType.KEYWORD),
+
+        ZEROINDEX           (163, "ZEROINDEX",   "z",       TokenType.STACKMAP, KeywordType.KEYWORD);
+
+        private Integer value;
+        private String printval;
+        private String parsekey;
+        private TokenType tk_type;
+        private KeywordType key_type;
+
+        // By default, if a KeywordType is not specified, it has the value 'TOKEN'
+        Token(Integer val, String print, String op) {
+            init(val, print, op, TokenType.VALUE, KeywordType.TOKEN);
+        }
+
+        Token(Integer val, String print, String op, TokenType tt) {
+            init(val, print, op, tt, KeywordType.TOKEN);
+        }
+
+        Token(Integer val, String print, String op, TokenType tt, KeywordType kt) {
+            init(val, print, op, tt, kt);
+        }
+
+        private void init(Integer val, String print, String op, TokenType tt, KeywordType kt) {
+            value = val;
+            printval = print;
+            parsekey = op;
+            tk_type = tt;
+            key_type = kt;
+        }
+
+        public String printval() {
+            return printval;
+        }
+
+        public String parsekey() {
+            return parsekey;
+        }
+
+        public int value() {
+            return value;
+        }
+
+        @Override
+        public String toString() {
+            return "<" + printval + "> [" + value + "]";
+        }
+
+    }
+
+    /**
+     * Initialized keyword and token Hash Maps (and Reverse Tables)
+     */
+    protected static final int MaxTokens = 172;
+    private static HashMap<Integer, Token> TagToTokens = new HashMap<>(MaxTokens);
+    private static HashMap<String, Token> SymbolToTokens = new HashMap<>(MaxTokens);
+    private static HashMap<String, Token> ParsekeyToTokens = new HashMap<>(MaxTokens);
+
+    protected static final int MaxKeywords = 40;
+    private static HashMap<Integer, Token> TagToKeywords = new HashMap<>(MaxKeywords);
+    private static HashMap<String, Token> SymbolToKeywords = new HashMap<>(MaxKeywords);
+    private static HashMap<String, Token> ParsekeyToKeywords = new HashMap<>(MaxKeywords);
+
+    static {
+
+        // register all of the tokens
+        for (Token tk : Token.values()) {
+            registerToken(tk);
+        }
+
+        SymbolToKeywords.put(Token.INTVAL.printval(), Token.INTVAL);
+        ParsekeyToKeywords.put(Token.INTVAL.parsekey(), Token.INTVAL);
+        SymbolToKeywords.put(Token.STRINGVAL.printval(), Token.STRINGVAL);
+        ParsekeyToKeywords.put(Token.STRINGVAL.parsekey(), Token.STRINGVAL);
+    }
+
+    private static void registerToken(Token tk) {
+        // Tag is a keyword
+        if (tk.key_type == KeywordType.KEYWORD) {
+            TagToKeywords.put(tk.value, tk);
+            SymbolToKeywords.put(tk.printval, tk);
+            if (tk.parsekey != null) {
+                ParsekeyToKeywords.put(tk.parsekey, tk);
+            }
+        }
+
+        // Finally, register all tokens
+        TagToTokens.put(tk.value, tk);
+        SymbolToTokens.put(tk.printval, tk);
+        ParsekeyToTokens.put(tk.printval, tk);
+    }
+
+    /* Token accessors */
+    public static Token token(int tk) {
+        return TagToTokens.get(tk);
+    }
+
+    public static Token keyword_token(int tk) {
+        return TagToKeywords.get(tk);
+    }
+
+    /* Reverse lookup accessors */
+    public static Token token(String parsekey) {
+        return ParsekeyToTokens.get(parsekey);
+    }
+
+    public static Token keyword_token(String parsekey) {
+        return ParsekeyToKeywords.get(parsekey);
+    }
+
+    /* Reverse lookup by ID accessors */
+    public static Token token_ID(String ID) {
+        return ParsekeyToTokens.get(ID);
+    }
+
+    public static Token keyword_token_ID(String ID) {
+        return ParsekeyToKeywords.get(ID);
+    }
+
+    public static String keywordName(int token) {
+        String retval = "";
+        if (token > TagToTokens.size()) {
+            retval = null;
+        } else {
+            Token tk = keyword_token(token);
+            if (tk != null) {
+                retval = tk.parsekey;
+            }
+        }
+        return retval;
+    }
+
+    public static Token keyword_token_ident(String idValue) {
+        Token kwd = keyword_token(idValue);
+
+        if (kwd == null) {
+            kwd = Token.IDENT;
+        }
+        return kwd;
+    }
+
+    public static int keyword_token_int(String idValue) {
+        return keyword_token_ident(idValue).value();
+    }
+
+    private static HashMap<String, ConstType> NameToConstantType = new HashMap<>(ConstType.maxTag);
+    private static HashMap<Integer, ConstType> ConstantTypes = new HashMap<>(ConstType.maxTag);
+
+    static {
+        // register all of the tokens
+        for (ConstType ct : ConstType.values()) {
+            registerConstantType(ct);
+        }
+    }
+
+    /**
+     * ConstType
+     *
+     * A (typed) tag (constant) representing the type of Constant in the Constant Pool.
+     *
+     * This is more-or-less a copy of jasm.ConstType. Unfortunately, there's no way to
+     * sub-class (or slightly alter) the members of an enum. This enum set is slightly
+     * modified from the Jasm one.
+     */
+    static public enum ConstType {
+//        CONSTANT_ZERO                       (-3, "CONSTANT_ZERO", ""),
+        CONSTANT_UTF8                       (1, "CONSTANT_UTF8", "Asciz", "Utf8"),
+        CONSTANT_UNICODE                    (2, "CONSTANT_UNICODE", ""),
+        CONSTANT_INTEGER                    (3, "CONSTANT_INTEGER", "int", "u4"),
+        CONSTANT_FLOAT                      (4, "CONSTANT_FLOAT", "float"),
+        CONSTANT_LONG                       (5, "CONSTANT_LONG", "long"),
+        CONSTANT_DOUBLE                     (6, "CONSTANT_DOUBLE", "double"),
+        // Class is removed for JavaCard (???)
+        CONSTANT_CLASS                      (7, "CONSTANT_CLASS", "class"),
+        CONSTANT_STRING                     (8, "CONSTANT_STRING", "String"),
+        CONSTANT_FIELD                      (9, "CONSTANT_FIELD", "Field"),
+        CONSTANT_METHOD                     (10, "CONSTANT_METHOD", "Method"),
+        CONSTANT_INTERFACEMETHOD            (11, "CONSTANT_INTERFACEMETHOD", "InterfaceMethod"),
+        CONSTANT_NAMEANDTYPE                (12, "CONSTANT_NAMEANDTYPE", "NameAndType"),
+        // added for JavaCard
+        CONSTANT_JAVACARD_PACKAGE           (13, "CONSTANT_PACKAGE", "package"),  // in javacard export file
+        // Constant 14 reserved
+        CONSTANT_METHODHANDLE               (15, "CONSTANT_METHODHANDLE", "MethodHandle"),
+        CONSTANT_METHODTYPE                 (16, "CONSTANT_METHODTYPE", "MethodType"),
+        CONSTANT_DYNAMIC                    (17, "CONSTANT_DYNAMIC", "Dynamic"),
+        CONSTANT_INVOKEDYNAMIC              (18, "CONSTANT_INVOKEDYNAMIC", "InvokeDynamic"),
+        CONSTANT_MODULE                     (19, "CONSTANT_MODULE",  "Module"),
+        CONSTANT_MODULE_PACKAGE             (20, "CONSTANT_PACKAGE", "Package");
+
+        public static final int maxTag = 20;
+
+        private final int value;
+        private final String parseKey;
+        private final String printval;
+        private final String alias;
+
+        ConstType(int val, String print, String parse) {
+            value = val;
+            parseKey = parse;
+            printval = print;
+            alias = null;
+        }
+
+        ConstType(int val, String print, String parse, String als) {
+            value = val;
+            parseKey = parse;
+            printval = print;
+            alias = als;
+        }
+
+        public int value() {
+            return value;
+        }
+
+        public String parseKey() {
+            return parseKey;
+        }
+
+        public String printval() {
+            return printval;
+        }
+
+        public void print(PrintWriter out) {
+            out.print(parseKey);
+        }
+
+        @Override
+        public String toString() {
+            return "<" + printval + "> [" + Integer.toString(value) + "]";
+        }
+    };
+
+    static public ConstType constType(int i) {
+        return ConstantTypes.get(i);
+    }
+
+    static public ConstType constType(String parsekey) {
+        return NameToConstantType.get(parsekey);
+    }
+
+    private static void registerConstantType(ConstType tt) {
+        NameToConstantType.put(tt.parseKey, tt);
+        if (tt.alias != null) {
+            NameToConstantType.put(tt.alias, tt);
+        }
+        ConstantTypes.put(tt.value, tt);
+    }
+
+    public static int constValue(String stringValue) {
+        ConstType Val = constType(stringValue);
+        int val = -1;
+
+        if (Val != null) {
+            val = Val.value();
+        } else {
+            StackMapType smt = stackMapTypeKey(stringValue);
+
+            if (smt != null) {
+                val = smt.value();
+            }
+        }
+        return val;
+    }
+
+}
diff a/test/lib/org/openjdk/asmtools/jcoder/Jcoder.java b/test/lib/org/openjdk/asmtools/jcoder/Jcoder.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jcoder/Jcoder.java
@@ -0,0 +1,766 @@
+/*
+ * Copyright (c) 2009, 2020, 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 org.openjdk.asmtools.jcoder;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Stack;
+
+import static org.openjdk.asmtools.jcoder.JcodTokens.ConstType;
+import static org.openjdk.asmtools.jcoder.JcodTokens.Token;
+
+/**
+ * Compiles just 1 source file
+ */
+class Jcoder {
+
+    /*-------------------------------------------------------- */
+    /* Jcoder Fields */
+    private ArrayList<ByteBuffer> Classes = new ArrayList<>();
+    private ByteBuffer buf;
+    private DataOutputStream bufstream;
+    private int depth = 0;
+    private String tabStr = "";
+    private Context context = null;
+    protected SourceFile env;
+    protected Scanner scanner;
+
+    /*-------------------------------------------------------- */
+    /* Jcoder inner classes */
+
+    /*-------------------------------------------------------- */
+    /* ContextTag (marker) - describes the type of token */
+    /*    this is rather cosmetic, no function currently. */
+    private enum ContextTag {
+        NULL                ( ""),
+        CLASS               ( "Class"),
+        CONSTANTPOOL        ( "Constant-Pool"),
+        INTERFACES          ( "Interfaces"),
+        INTERFACE           ( "Interface"),
+        METHODS             ( "Methods"),
+        METHOD              ( "Method"),
+        FIELDS              ( "Fields"),
+        FIELD               ( "Field"),
+        ATTRIBUTE           ( "Attribute");
+
+        private final String  printValue;
+
+        ContextTag(String value) {
+            printValue = value;
+        }
+
+        public String printval() {
+            return printValue;
+        }
+    }
+
+    /*-------------------------------------------------------- */
+    /* ContextVal (marker) - Specific value on a context stack */
+    private class ContextVal {
+
+        public ContextTag tag;
+        int compCount;
+        ContextVal owner;
+
+        ContextVal(ContextTag tg) {
+            tag = tg;
+            compCount = 0;
+            owner = null;
+        }
+
+        ContextVal(ContextTag tg, ContextVal ownr) {
+            tag = tg;
+            compCount = 0;
+            owner = ownr;
+        }
+    }
+
+
+    /*-------------------------------------------------------- */
+    /* Context - Context stack */
+    public class Context {
+
+        Stack<ContextVal> stack;
+
+        private boolean hasCP;
+        private boolean hasMethods;
+        private boolean hasInterfaces;
+        private boolean hasFields;
+
+        Context() {
+            stack = new Stack<>();
+            init();
+        }
+
+        boolean isConstantPool() {
+          return !stack.empty() && (stack.peek().tag == ContextTag.CONSTANTPOOL);
+        }
+
+        public void init() {
+            stack.removeAllElements();
+            hasCP = false;
+            hasMethods = false;
+            hasInterfaces = false;
+            hasFields = false;
+        }
+
+        void update() {
+            if (stack.empty()) {
+                stack.push(new ContextVal(ContextTag.CLASS));
+                return;
+            }
+
+            ContextVal currentCtx = stack.peek();
+            switch (currentCtx.tag) {
+                case CLASS:
+                    if (!hasCP) {
+                        stack.push(new ContextVal(ContextTag.CONSTANTPOOL));
+                        hasCP = true;
+                    } else if (!hasInterfaces) {
+                        stack.push(new ContextVal(ContextTag.INTERFACES));
+                        hasInterfaces = true;
+                    } else if (!hasFields) {
+                        stack.push(new ContextVal(ContextTag.FIELDS));
+                        hasFields = true;
+                    } else if (!hasMethods) {
+                        stack.push(new ContextVal(ContextTag.METHODS));
+                        hasMethods = true;
+                    } else {
+                        // must be class attributes
+                        currentCtx.compCount += 1;
+                        stack.push(new ContextVal(ContextTag.ATTRIBUTE, currentCtx));
+                    }
+                    break;
+                case INTERFACES:
+                    currentCtx.compCount += 1;
+                    stack.push(new ContextVal(ContextTag.INTERFACE, currentCtx));
+                    break;
+                case FIELDS:
+                    currentCtx.compCount += 1;
+                    stack.push(new ContextVal(ContextTag.FIELD, currentCtx));
+                    break;
+                case METHODS:
+                    currentCtx.compCount += 1;
+                    stack.push(new ContextVal(ContextTag.METHOD, currentCtx));
+                    break;
+                case FIELD:
+                case METHOD:
+                case ATTRIBUTE:
+                    currentCtx.compCount += 1;
+                    stack.push(new ContextVal(ContextTag.ATTRIBUTE, currentCtx));
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        void exit() {
+            if (!stack.isEmpty()) {
+                stack.pop();
+            }
+        }
+
+        public String toString() {
+            if (stack.isEmpty()) {
+                return "";
+            }
+            ContextVal currentCtx = stack.peek();
+            String retval = currentCtx.tag.printval();
+            switch (currentCtx.tag) {
+                case INTERFACE:
+                case METHOD:
+                case FIELD:
+                case ATTRIBUTE:
+                    if (currentCtx.owner != null) {
+                        retval += "[" + currentCtx.owner.compCount + "]";
+                    }
+            }
+
+            return retval;
+        }
+    }
+
+
+    /*-------------------------------------------------------- */
+    /* Jcoder */
+    /**
+     * Create a parser
+     */
+    Jcoder(SourceFile sf, HashMap<String, String> macros) throws IOException {
+        scanner = new Scanner(sf, macros);
+        env = sf;
+        context = new Context();
+
+    }
+    /*-------------------------------------------------------- */
+
+    /**
+     * Expect a token, return its value, scan the next token or throw an exception.
+     */
+    private void expect(Token t) throws SyntaxError, IOException {
+        if (scanner.token != t) {
+            env.traceln("expect:" + t + " instead of " + scanner.token);
+            switch (t) {
+                case IDENT:
+                    env.error(scanner.pos, "identifier.expected");
+                    break;
+                default:
+                    env.error(scanner.pos, "token.expected", t.toString());
+                    break;
+            }
+            throw new SyntaxError();
+        }
+        scanner.scan();
+    }
+
+    private void recoverField() throws SyntaxError, IOException {
+        while (true) {
+            switch (scanner.token) {
+                case LBRACE:
+                    scanner.match(Token.LBRACE, Token.RBRACE);
+                    scanner.scan();
+                    break;
+
+                case LPAREN:
+                    scanner.match(Token.LPAREN, Token.RPAREN);
+                    scanner.scan();
+                    break;
+
+                case LSQBRACKET:
+                    scanner.match(Token.LSQBRACKET, Token.RSQBRACKET);
+                    scanner.scan();
+                    break;
+
+                case RBRACE:
+                case EOF:
+                case INTERFACE:
+                case CLASS:
+                    // begin of something outside a class, panic more
+                    throw new SyntaxError();
+
+                default:
+                    // don't know what to do, skip
+                    scanner.scan();
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Parse an array of struct.
+     */
+    private void parseArray() throws IOException {
+        scanner.scan();
+        int length0 = buf.length, pos0 = scanner.pos;
+        int num_expected;
+        if (scanner.token == Token.INTVAL) {
+            num_expected = scanner.intValue;
+            scanner.scan();
+        } else {
+            num_expected = -1;
+        }
+        expect(Token.RSQBRACKET);
+        int numSize;
+        switch (scanner.token) {
+            case BYTEINDEX:
+                scanner.scan();
+                numSize = 1;
+                break;
+            case SHORTINDEX:
+                scanner.scan();
+                numSize = 2;
+                break;
+            case ZEROINDEX:
+                scanner.scan();
+                numSize = 0;
+                break;
+            default:
+                numSize = 2;
+        }
+
+        // skip array size
+        if (numSize > 0) {
+            buf.append(num_expected, numSize);
+        }
+
+        int num_present = parseStruct();
+        if (num_expected == -1) {
+            env.trace(" buf.writeAt(" + length0 + ", " + num_present + ", " + numSize + ");  ");
+            // skip array size
+            if (numSize > 0) {
+                buf.writeAt(length0, num_present, numSize);
+            }
+        } else if ( num_expected != num_present) {
+            if (context.isConstantPool() && num_expected == num_present +1) return;
+            env.error(pos0, "warn.array.wronglength", num_expected, num_present);
+        }
+    }
+
+    /**
+     * Parse a byte array.
+     */
+    private void parseByteArray() throws IOException {
+        scanner.scan();
+        expect(Token.LSQBRACKET);
+        int length0 = buf.length, pos0 = scanner.pos;
+        int len_expected;
+        if (scanner.token == Token.INTVAL) {
+            len_expected = scanner.intValue;
+            scanner.scan();
+        } else {
+            len_expected = -1;
+        }
+        expect(Token.RSQBRACKET);
+        int lenSize;
+        switch (scanner.token) {
+            case BYTEINDEX:
+                scanner.scan();
+                lenSize = 1;
+                break;
+            case SHORTINDEX:
+                scanner.scan();
+                lenSize = 2;
+                break;
+            case ZEROINDEX:
+                scanner.scan();
+                lenSize = 0;
+                break;
+            default:
+                lenSize = 4;
+        }
+
+        // skip array size
+        if (lenSize > 0) {
+            buf.append(len_expected, lenSize);
+        }
+        int length1 = buf.length;
+        parseStruct();
+        int len_present = buf.length - length1;
+        if (len_expected == -1) {
+            env.trace(" buf.writeAt(" + length0 + ", " + len_present + ", " + lenSize + ");  ");
+            // skip array size
+            if (lenSize > 0) {
+                buf.writeAt(length0, len_present, lenSize);
+            }
+        } else if (len_expected != len_present) {
+            env.error(pos0, "warn.array.wronglength", len_expected, len_present);
+        }
+    }
+
+    /**
+     * Parse an Attribute.
+     */
+    private void parseAttr() throws IOException {
+        scanner.scan();
+        expect(Token.LPAREN);
+        int cpx; // index int const. pool
+        if (scanner.token == Token.INTVAL) {
+            cpx = scanner.intValue;
+            scanner.scan();
+
+            /*  } else if (token==STRINGVAL) {
+             Integer Val=(Integer)(CP_Strings.get(stringValue));
+             if (Val == null) {
+             env.error(pos, "attrname.notfound", stringValue);
+             throw new SyntaxError();
+             }
+             cpx=Val.intValue();
+             */        } else {
+            env.error(scanner.pos, "attrname.expected");
+            throw new SyntaxError();
+        }
+        buf.append(cpx, 2);
+        int pos0 = scanner.pos, length0 = buf.length;
+        int len_expected;
+        if (scanner.token == Token.COMMA) {
+            scanner.scan();
+            len_expected = scanner.intValue;
+            expect(Token.INTVAL);
+        } else {
+            len_expected = -1;
+        }
+        buf.append(len_expected, 4);
+        expect(Token.RPAREN);
+        parseStruct();
+        int len_present = buf.length - (length0 + 4);
+        if (len_expected == -1) {
+            buf.writeAt(length0, len_present, 4);
+        } else if (len_expected != len_present) {
+            env.error(pos0, "warn.attr.wronglength", len_expected, len_present);
+        }
+    } // end parseAttr
+
+    /**
+     * Parse a Component of JavaCard .cap file.
+     */
+    private void parseComp() throws IOException {
+        scanner.scan();
+        expect(Token.LPAREN);
+        int tag = scanner.intValue; // index int const. pool
+        expect(Token.INTVAL);
+        buf.append(tag, 1);
+        int pos0 = scanner.pos, length0 = buf.length;
+        int len_expected;
+        if (scanner.token == Token.COMMA) {
+            scanner.scan();
+            len_expected = scanner.intValue;
+            expect(Token.INTVAL);
+        } else {
+            len_expected = -1;
+        }
+        buf.append(len_expected, 2);
+        expect(Token.RPAREN);
+        parseStruct();
+        int len_present = buf.length - (length0 + 2);
+        if (len_expected == -1) {
+            buf.writeAt(length0, len_present, 2);
+        } else if (len_expected != len_present) {
+            env.error(pos0, "warn.attr.wronglength", len_expected, len_present);
+        }
+    } // end parseComp
+
+    private void adjustDepth(boolean up) {
+        if (up) {
+            depth += 1;
+            context.update();
+            scanner.setDebugCP(context.isConstantPool());
+        } else {
+            depth -= 1;
+            context.exit();
+        }
+        StringBuilder bldr = new StringBuilder();
+        int tabAmt = 4;
+        int len = depth * tabAmt;
+        for (int i = 0; i < len; i++) {
+            bldr.append(" ");
+        }
+        tabStr = bldr.toString();
+    }
+
+    /**
+     * Parse a structure.
+     */
+    private int parseStruct() throws IOException {
+        adjustDepth(true);
+        env.traceln(" ");
+        env.traceln(tabStr + "MapStruct { <" + context + "> ");
+        expect(Token.LBRACE);
+        int num = 0;
+        int addElem = 0;
+        while (true) {
+            try {
+                switch (scanner.token) {
+                    case COMMA: // ignored
+                        scanner.scan();
+                        break;
+                    case SEMICOLON:
+                        num++;
+                        addElem = 0;
+                        scanner.scan();
+                        break;
+                    case CLASS:
+                        scanner.addConstDebug(ConstType.CONSTANT_CLASS);
+                        env.trace("class ");
+                        scanner.longValue = ConstType.CONSTANT_CLASS.value();
+                        scanner.intSize = 1;
+                    case INTVAL:
+                        env.trace("int [" + scanner.longValue + "] ");
+                        buf.append(scanner.longValue, scanner.intSize);
+                        scanner.scan();
+                        addElem = 1;
+                        break;
+                    case STRINGVAL:
+                        scanner.scan();
+                        scanner.addConstDebug(ConstType.CONSTANT_UTF8);
+                        env.trace("UTF8 [\"" + scanner.stringValue + "\"] ");
+                        bufstream.writeUTF(scanner.stringValue);
+                        addElem = 1;
+                        break;
+                    case LONGSTRINGVAL:
+                        scanner.scan();
+                        env.traceln("LongString [\"" + Arrays.toString(scanner.longStringValue.data) + "\"] ");
+                        buf.write(scanner.longStringValue.data, 0, scanner.longStringValue.length);
+                        addElem = 1;
+                        break;
+                    case LBRACE:
+                        parseStruct();
+                        addElem = 1;
+                        break;
+                    case LSQBRACKET:
+                        parseArray();
+                        addElem = 1;
+                        break;
+                    case BYTES:
+                        env.trace("bytes ");
+                        parseByteArray();
+                        addElem = 1;
+                        break;
+                    case ATTR:
+                        env.trace("attr ");
+                        parseAttr();
+                        addElem = 1;
+                        break;
+                    case COMP:
+                        env.trace("comp ");
+                        parseComp();
+                        addElem = 1;
+                        break;
+                    case RBRACE:
+                        scanner.scan();
+                        env.traceln(" ");
+                        env.traceln(tabStr + "} // MapStruct  <" + context + "> [");
+                        adjustDepth(false);
+                        return num + addElem;
+                    default:
+                        env.traceln("unexp token=" + scanner.token);
+                        env.traceln("   scanner.stringval = \"" + scanner.stringValue + "\"");
+                        env.error(scanner.pos, "element.expected");
+                        throw new SyntaxError();
+                }
+            } catch (SyntaxError e) {
+                recoverField();
+            }
+        }
+    } // end parseStruct
+
+    /**
+     * Recover after a syntax error in the file. This involves discarding tokens until an
+     * EOF or a possible legal continuation is encountered.
+     */
+    private void recoverFile() throws IOException {
+        while (true) {
+            switch (scanner.token) {
+                case CLASS:
+                case INTERFACE:
+                    // Start of a new source file statement, continue
+                    return;
+
+                case LBRACE:
+                    scanner.match(Token.LBRACE, Token.RBRACE);
+                    scanner.scan();
+                    break;
+
+                case LPAREN:
+                    scanner.match(Token.LPAREN, Token.RPAREN);
+                    scanner.scan();
+                    break;
+
+                case LSQBRACKET:
+                    scanner.match(Token.LSQBRACKET, Token.RSQBRACKET);
+                    scanner.scan();
+                    break;
+
+                case EOF:
+                    return;
+
+                default:
+                    // Don't know what to do, skip
+                    scanner.scan();
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Parse module declaration
+     */
+    private void parseModule() throws IOException {
+        // skip module name as a redundant element
+        scanner.skipTill(Scanner.LBRACE);
+        buf = new ByteBuffer();
+        bufstream = new DataOutputStream(buf);
+        buf.myname = "module-info.class";
+        scanner.scan();
+        env.traceln("starting " + buf.myname);
+        // Parse the clause
+        parseClause();
+        env.traceln("ending " + buf.myname);
+    }
+
+    /**
+     * Parse a class or interface declaration.
+     */
+    private void parseClass(Token prev) throws IOException {
+        scanner.scan();
+        buf = new ByteBuffer();
+        bufstream = new DataOutputStream(buf);
+        // Parse the class name
+        switch (scanner.token) {
+            case STRINGVAL:
+                buf.myname = scanner.stringValue;
+                break;
+            case BYTEINDEX:
+            case SHORTINDEX:
+            case ATTR:
+            case BYTES:
+            case MACRO:
+            case COMP:
+            case FILE:
+            case IDENT:
+                if (prev == Token.FILE) {
+                    buf.myname = scanner.stringValue;
+                } else {
+                    buf.myname = scanner.stringValue + ".class";
+                }
+                break;
+            default:
+                env.error(scanner.prevPos, "name.expected");
+                throw new SyntaxError();
+        }
+        scanner.scan();
+        env.traceln("starting class " + buf.myname);
+        // Parse the clause
+        parseClause();
+        env.traceln("ending class " + buf.myname);
+
+    } // end parseClass
+
+    private void parseClause() throws IOException {
+        switch (scanner.token) {
+            case LBRACE:
+                parseStruct();
+                break;
+            case LSQBRACKET:
+                parseArray();
+                break;
+            case BYTES:
+                parseByteArray();
+                break;
+            case ATTR:
+                parseAttr();
+                break;
+            case COMP:
+                parseComp();
+                break;
+            default:
+                env.error(scanner.pos, "struct.expected");
+        }
+    }
+
+    /**
+     * Parse an Jcoder file.
+     */
+    void parseFile() {
+        env.traceln("PARSER");
+        context.init();
+        try {
+            while (scanner.token != Token.EOF) {
+                try {
+                    switch (scanner.token) {
+                        case CLASS:
+                        case MODULE:
+                        case INTERFACE:
+                        case FILE:
+                            Token t = scanner.token;
+                            if ( t == Token.MODULE) {
+                                parseModule();
+                            } else {
+                                parseClass(t);
+                            }
+                            // End of the class,interface or module
+                            env.flushErrors();
+                            Classes.add(buf);
+                            break;
+                        case SEMICOLON:
+                            // Bogus semi colon
+                            scanner.scan();
+                            break;
+
+                        case EOF:
+                            // The end
+                            return;
+
+                        default:
+                            env.traceln("unexpected token=" + scanner.token.toString());
+                            env.error(scanner.pos, "toplevel.expected");
+                            throw new SyntaxError();
+                    }
+                } catch (SyntaxError e) {
+                    String msg = e.getMessage();
+                    env.traceln("SyntaxError " + (msg == null ? "" : msg));
+                    if( env.debugInfoFlag ) {
+                        e.printStackTrace();
+                    }
+                    recoverFile();
+                }
+            }
+        } catch (IOException e) {
+            env.error(scanner.pos, "io.exception", env.getInputFileName());
+        }
+    } //end parseFile
+
+    /*---------------------------------------------*/
+    private static char fileSeparator; //=System.getProperty("file.separator");
+
+    /**
+     * write to the directory passed with -d option
+     */
+    public void write(ByteBuffer cls, File destdir) throws IOException {
+        String myname = cls.myname;
+        if (myname == null) {
+            env.error("cannot.write", null);
+            return;
+        }
+
+        env.traceln("writing " + myname);
+        File outfile;
+        if (destdir == null) {
+            int startofname = myname.lastIndexOf('/');
+            if (startofname != -1) {
+                myname = myname.substring(startofname + 1);
+            }
+            outfile = new File(myname);
+        } else {
+            env.traceln("writing -d " + destdir.getPath());
+            if (fileSeparator == 0) {
+                fileSeparator = System.getProperty("file.separator").charAt(0);
+            }
+            if (fileSeparator != '/') {
+                myname = myname.replace('/', fileSeparator);
+            }
+            outfile = new File(destdir, myname);
+            File outdir = new File(outfile.getParent());
+            if (!outdir.exists() && !outdir.mkdirs()) {
+                env.error("cannot.write", outdir.getPath());
+                return;
+            }
+        }
+
+        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(outfile));
+        out.write(cls.data, 0, cls.length);
+        try {
+            out.close();
+        } catch (IOException ignored) { }
+    }
+
+    /**
+     * Writes the classes
+     */
+    public void write(File destdir) throws IOException {
+        for (ByteBuffer cls : Classes) {
+            write(cls, destdir);
+        }
+    }  // end write()
+} // end Jcoder
diff a/test/lib/org/openjdk/asmtools/jcoder/Main.java b/test/lib/org/openjdk/asmtools/jcoder/Main.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jcoder/Main.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2009, 2020, 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 org.openjdk.asmtools.jcoder;
+
+import org.openjdk.asmtools.common.Tool;
+import org.openjdk.asmtools.util.I18NResourceBundle;
+import org.openjdk.asmtools.util.ProductInfo;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ *
+ *
+ */
+public class Main extends Tool {
+
+    public static final I18NResourceBundle i18n
+            = I18NResourceBundle.getBundleForClass(Main.class);
+
+    public Main(PrintWriter out, String programName) {
+        super(out, programName);
+        printCannotReadMsg = (fname) -> error(i18n.getString("jcoder.error.cannot_read", fname));
+    }
+
+    public Main(PrintStream out, String program) {
+        this(new PrintWriter(out), program);
+    }
+
+    @Override
+    public void usage() {
+        println(i18n.getString("jcoder.usage"));
+        println(i18n.getString("jcoder.opt.nowrite"));
+        println(i18n.getString("jcoder.opt.ignore"));
+        println(i18n.getString("jcoder.opt.d"));
+        println(i18n.getString("jcoder.opt.version"));
+    }
+
+    /**
+     * Run the compiler
+     */
+    public synchronized boolean compile(String argv[]) {
+        File destDir = null;
+        boolean traceFlag = false;
+        DebugFlag = () -> false;
+        long tm = System.currentTimeMillis();
+        ArrayList<String> v = new ArrayList<>();
+        boolean nowrite = false;
+        boolean ignore  = false;
+        int nwarnings = 0;
+        HashMap<String, String> macros = new HashMap<>();
+        macros.put("VERSION", "3;45");
+
+        // Parse arguments
+        for (int i = 0; i < argv.length; i++) {
+            String arg = argv[i];
+            if (!arg.startsWith("-")) {
+                v.add(arg);
+            } else if (arg.startsWith("-D")) {
+                int argLength = arg.length();
+                if (argLength == 2) {
+                    error(i18n.getString("jcoder.error.D_needs_macro"));
+                    return false;
+                }
+                int index = arg.indexOf('=');
+                if (index == -1) {
+                    error(i18n.getString("jcoder.error.D_needs_macro"));
+                    return false;
+                }
+                String macroId = arg.substring(2, index);
+                index++;
+                if (argLength == index) {
+                    error(i18n.getString("jcoder.error.D_needs_macro"));
+                    return false;
+                }
+                String macro;
+                if (arg.charAt(index) == '"') {
+                    index++;
+                    if (argLength == index || arg.charAt(argLength - 1) != '"') {
+                        error(i18n.getString("jcoder.error.no_closing_quota"));
+                        return false;
+                    }
+                    macro = arg.substring(index, argLength - 1);
+                } else {
+                    macro = arg.substring(index, argLength);
+                }
+                macros.put(macroId, macro);
+            } else if (arg.equals("-vv")) {
+                DebugFlag = () -> true;
+                traceFlag = true;
+            } else if (arg.equals("-v")) {
+                traceFlag = true;
+            } else if (arg.equals("-nowrite")) {
+                nowrite = true;
+            } else if (arg.equals("-ignore")) {
+                ignore = true;
+            } else if (arg.equals("-d")) {
+                if ((i + 1) == argv.length) {
+                    error(i18n.getString("jcoder.error.d_requires_argument"));
+                    usage();
+                    return false;
+                }
+                destDir = new File(argv[++i]);
+                if (!destDir.exists()) {
+                    error(i18n.getString("jcoder.error.does_not_exist", destDir));
+                    return false;
+                }
+            } else if (arg.equals("-version")) {
+                println(ProductInfo.FULL_VERSION);
+            } else {
+                error(i18n.getString("jcoder.error.invalid_option", arg));
+                usage();
+                return false;
+            }
+        }
+        if (v.isEmpty()) {
+            usage();
+            return false;
+        }
+        // compile all input files
+        try {
+            for (String inpname : v) {
+                SourceFile env;
+                Jcoder p;
+
+                DataInputStream dataInputStream = getDataInputStream(inpname);
+                if( dataInputStream == null ) {
+                    nerrors++;
+                    continue;
+                }
+                env = new SourceFile(this, dataInputStream, inpname, out);
+                env.traceFlag = traceFlag;
+                env.debugInfoFlag = DebugFlag.getAsBoolean();
+                p = new Jcoder(env, macros);
+                p.parseFile();
+                env.traceln("END PARSER");
+                env.closeInp();
+
+                nerrors += env.nerrors;
+                nwarnings += env.nwarnings;
+                if (nowrite || (nerrors > 0 & !ignore)) {
+                    continue;
+                }
+                try {
+                    env.traceln("WRITE");
+                    p.write(destDir);
+                } catch (FileNotFoundException ex) {
+                    error(i18n.getString("jcoder.error.cannot_write", ex.getMessage()));
+                }
+            }
+        } catch (Error ee) {
+            ee.printStackTrace();
+            error(i18n.getString("jcoder.error.fatal_error"));
+        } catch (Exception ee) {
+            ee.printStackTrace();
+            error(i18n.getString("jcoder.error.fatal_exception"));
+        }
+
+        boolean errs = nerrors > 0;
+        boolean warns = nwarnings > 0;
+        if (!errs && !warns) {
+            return true;
+        }
+        println(errs ? (nerrors > 1 ? (nerrors + " errors") : "1 error")
+                : "" + ((errs && warns) ? ", " : "") + (warns ? (nwarnings > 1 ? (nwarnings + " warnings") : "1 warning") : ""));
+        return !errs;
+    }
+
+    /**
+     * main program
+     */
+    public static void main(String[] argv) {
+        Main compiler = new Main(new PrintWriter(System.out), "jcoder");
+        System.exit(compiler.compile(argv) ? 0 : 1);
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jcoder/Scanner.java b/test/lib/org/openjdk/asmtools/jcoder/Scanner.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jcoder/Scanner.java
@@ -0,0 +1,905 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jcoder;
+
+import static org.openjdk.asmtools.jcoder.JcodTokens.*;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+/**
+ * A Scanner for Jcoder tokens. Errors are reported to the environment object.<p>
+ *
+ * The scanner keeps track of the current token, the value of the current token (if any),
+ * and the start position of the current token.<p>
+ *
+ * The scan() method advances the scanner to the next token in the input.<p>
+ *
+ * The match() method is used to quickly match opening brackets (ie: '(', '{', or '[')
+ * with their closing counter part. This is useful during error recovery.<p>
+ *
+ * The compiler treats either "\n", "\r" or "\r\n" as the end of a line.<p>
+ */
+public class Scanner {
+    /*-------------------------------------------------------- */
+    /* Scanner Fields */
+
+    /**
+     * End of input
+     */
+    public static final int EOF = -1;
+    public static final int LBRACE = 123; // "{"
+    private boolean debugCP = false;
+    private int numCPentrs = 0;
+
+    /**
+     * Where errors are reported
+     */
+    protected SourceFile env;
+
+    /**
+     * Input stream
+     */
+    protected SourceFile in;
+    HashMap<String, String> macros;
+
+    /**
+     * The current character
+     */
+    protected int ch, prevCh = -1;
+    protected String macro;
+    protected int indexMacro;
+
+    /**
+     * Current token
+     */
+    protected Token token;
+
+    /**
+     * The position of the current token
+     */
+    protected int pos;
+
+    /**
+     * The position of the previous token
+     */
+    protected int prevPos;
+
+    /*  Token values. */
+    protected long longValue;
+    protected int intValue;
+    protected int intSize;
+    protected String stringValue;
+    protected ByteBuffer longStringValue;
+    protected int sign; // sign, when reading number
+
+    /*  A doc comment preceding the most recent token */
+    protected String docComment;
+
+    /**
+     * A growable character buffer.
+     */
+    private int count;
+    private char[] buffer = new char[32];
+
+    /*-------------------------------------------------------- */
+    /**
+     * Create a scanner to scan an input stream.
+     */
+    protected Scanner(SourceFile sf, HashMap<String, String> macros)
+            throws IOException {
+        this.env = sf;
+        this.in = sf;
+        this.macros = macros;
+
+        ch = sf.read();
+        prevPos = sf.pos;
+
+        scan();
+    }
+
+    /**
+     * for use in jcfront.
+     */
+    protected Scanner(SourceFile sf)
+            throws IOException {
+        this.env = sf;
+        this.in = sf;
+        this.macros = new HashMap<>();
+
+        ch = sf.read();
+        prevPos = sf.pos;
+
+        scan();
+    }
+
+    /* *********************************************** */
+    void setDebugCP(boolean enable) {
+        if (enable) {
+            numCPentrs = 0;
+        }
+        debugCP = enable;
+
+    }
+
+    void addConstDebug(ConstType ct) {
+        numCPentrs += 1;
+        env.traceln("\n Const[" + numCPentrs + "] = " + ct.printval());
+    }
+
+    void setMacro(String macro) {
+        this.macro = macro;
+        indexMacro = 0;
+        prevCh = ch;
+    }
+
+    void readCh() throws IOException {
+        if (macro != null) {
+            if (indexMacro < macro.length()) {
+                ch = macro.charAt(indexMacro);
+            }
+            macro = null;
+        }
+        if (prevCh >= 0) {
+            ch = prevCh;
+            prevCh = -1;
+        } else {
+            ch = in.read();
+        }
+    }
+
+    private void putc(int ch) {
+        if (count == buffer.length) {
+            char[] newBuffer = new char[buffer.length * 2];
+            System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
+            buffer = newBuffer;
+        }
+        buffer[count++] = (char) ch;
+    }
+
+    private String bufferString() {
+        char[] buf = new char[count];
+        System.arraycopy(buffer, 0, buf, 0, count);
+        return new String(buf);
+    }
+
+    /**
+     * Scan a comment. This method should be called once the initial /, * and the next
+     * character have been read.
+     */
+    private void skipComment() throws IOException {
+        while (true) {
+            switch (ch) {
+                case EOF:
+                    env.error(pos, "eof.in.comment");
+                    return;
+
+                case '*':
+                    readCh();
+                    if (ch == '/') {
+                        readCh();
+                        return;
+                    }
+                    break;
+
+                default:
+                    readCh();
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Scan a doc comment. This method should be called once the initial /, * and * have
+     * been read. It gathers the content of the comment (witout leading spaces and '*'s)
+     * in the string buffer.
+     */
+    private String scanDocComment() throws IOException {
+        count = 0;
+
+        if (ch == '*') {
+            do {
+                readCh();
+            } while (ch == '*');
+            if (ch == '/') {
+                readCh();
+                return "";
+            }
+        }
+        switch (ch) {
+            case '\n':
+            case ' ':
+                readCh();
+                break;
+        }
+
+        boolean seenstar = false;
+        int c = count;
+        while (true) {
+            switch (ch) {
+                case EOF:
+                    env.error(pos, "eof.in.comment");
+                    return bufferString();
+
+                case '\n':
+                    putc('\n');
+                    readCh();
+                    seenstar = false;
+                    c = count;
+                    break;
+
+                case ' ':
+                case '\t':
+                    putc(ch);
+                    readCh();
+                    break;
+
+                case '*':
+                    if (seenstar) {
+                        readCh();
+                        if (ch == '/') {
+                            readCh();
+                            count = c;
+                            return bufferString();
+                        }
+                        putc('*');
+                    } else {
+                        seenstar = true;
+                        count = c;
+                        do {
+                            readCh();
+                        } while (ch == '*');
+                        switch (ch) {
+                            case ' ':
+                                readCh();
+                                break;
+
+                            case '/':
+                                readCh();
+                                count = c;
+                                return bufferString();
+                        }
+                    }
+                    break;
+
+                default:
+                    if (!seenstar) {
+                        seenstar = true;
+                    }
+                    putc(ch);
+                    readCh();
+                    c = count;
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Scan a decimal number
+     */
+    private void scanDecNumber() throws IOException {
+        boolean overflow = false;
+        long value = ch - '0';
+        count = 0;
+        token = Token.INTVAL;
+        intSize = 2; // default
+        putc(ch);    // save character in buffer
+numberLoop:
+        for (;;) {
+            readCh();
+            switch (ch) {
+                case '8':
+                case '9':
+                case '0':
+                case '1':
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7':
+                    putc(ch);
+                    overflow = overflow || (value * 10) / 10 != value;
+                    value = (value * 10) + (ch - '0');
+                    overflow = overflow || (value - 1 < -1);
+                    break;
+                case 'b':
+                    readCh();
+                    intSize = 1;
+                    break numberLoop;
+                case 's':
+                    readCh();
+                    intSize = 2;
+                    break numberLoop;
+                case 'i':
+                    readCh();
+                    intSize = 4;
+                    break numberLoop;
+                case 'l':
+                    readCh();
+                    intSize = 8;
+                    break numberLoop;
+                default:
+                    break numberLoop;
+            }
+        }
+        longValue = value;
+        intValue = (int) value;
+        // we have just finished reading the number.  The next thing better
+        // not be a letter or digit.
+        if (Character.isJavaIdentifierPart((char) ch) || ch == '.') {
+            env.error(in.pos, "invalid.number", Character.toString((char)ch));
+            do {
+                readCh();
+            } while (Character.isJavaIdentifierPart((char) ch) || ch == '.');
+            return;
+        }
+        if (overflow) {
+            env.error(pos, "overflow");
+        }
+    } // scanNumber()
+
+    /**
+     * Scan a hex number.
+     */
+    private void scanHexNumber() throws IOException {
+        boolean overflow = false;
+        long value = 0;
+        int cypher;
+        count = 0;
+        token = Token.INTVAL;
+        intSize = 2; // default
+        putc(ch);    // save character in buffer
+numberLoop:
+        for (int k = 0;; k++) {
+            readCh();
+            switch (ch) {
+                case '8':
+                case '9':
+                case '0':
+                case '1':
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7':
+                    cypher = (char) ch - '0';
+                    break;
+                case 'd':
+                case 'D':
+                case 'e':
+                case 'E':
+                case 'f':
+                case 'F':
+                case 'a':
+                case 'A':
+                case 'b':
+                case 'B':
+                case 'c':
+                case 'C':
+                    cypher = 10 + Character.toLowerCase((char) ch) - 'a';
+                    break;
+
+                default:
+                    break numberLoop;
+            }
+            putc(ch);
+            overflow = overflow || ((value >>> 60) != 0);
+            value = (value << 4) + cypher;
+            intSize = (k + 1) / 2;
+        }
+        longValue = value;
+        intValue = (int) value;
+        // we have just finished reading the number.  The next thing better
+        // not be a letter or digit.
+        if (Character.isJavaIdentifierPart((char) ch) || ch == '.') {
+            env.error(in.pos, "invalid.number", Character.toString((char)ch));
+            do {
+                readCh();
+            } while (Character.isJavaIdentifierPart((char) ch) || ch == '.');
+            intValue = 0;
+//        } else if ( overflow || (intValue - 1 < -1) ) {
+        } else if (overflow) {
+            intValue = 0;   // so we don't get second overflow in Parser
+            env.error(pos, "overflow");
+        }
+    } // scanNumber()
+
+    /**
+     * Scan an escape character.
+     *
+     * @return the character or -1 if it escaped an end-of-line.
+     */
+    private int scanEscapeChar() throws IOException {
+        int p = in.pos;
+
+        readCh();
+        switch (ch) {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7': {
+                int n = ch - '0';
+                for (int i = 2; i > 0; i--) {
+                    readCh();
+                    switch (ch) {
+                        case '0':
+                        case '1':
+                        case '2':
+                        case '3':
+                        case '4':
+                        case '5':
+                        case '6':
+                        case '7':
+                            n = (n << 3) + ch - '0';
+                            break;
+
+                        default:
+                            if (n > 0xFF) {
+                                env.error(p, "invalid.escape.char");
+                            }
+                            return n;
+                    }
+                }
+                readCh();
+                if (n > 0xFF) {
+                    env.error(p, "invalid.escape.char");
+                }
+                return n;
+            }
+            case 'r':
+                readCh();
+                return '\r';
+            case 'n':
+                readCh();
+                return '\n';
+            case 'f':
+                readCh();
+                return '\f';
+            case 'b':
+                readCh();
+                return '\b';
+            case 't':
+                readCh();
+                return '\t';
+            case '\\':
+                readCh();
+                return '\\';
+            case '\"':
+                readCh();
+                return '\"';
+            case '\'':
+                readCh();
+                return '\'';
+        }
+
+        env.error(p, "invalid.escape.char");
+        readCh();
+        return -1;
+    }
+
+    /**
+     * Scan a string. The current character should be the opening " of the string.
+     */
+    private void scanString() throws IOException {
+        token = Token.STRINGVAL;
+        count = 0;
+        readCh();
+
+loop:
+        for (;;) {
+            switch (ch) {
+                case EOF:
+                    env.error(pos, "eof.in.string");
+                    break loop;
+
+                case '\n':
+                    readCh();
+                    env.error(pos, "newline.in.string");
+                    break loop;
+
+                case '"':
+                    readCh();
+                    break loop;
+
+                case '\\': {
+                    int c = scanEscapeChar();
+                    if (c >= 0) {
+                        putc((char)c);
+                    }
+                    break;
+                }
+
+                default:
+                    putc(ch);
+                    readCh();
+                    break;
+            }
+        }
+        stringValue = bufferString();
+    }
+
+    /**
+     * Scan a character array. The current character should be the opening ' of the array.
+     */
+    private void scanCharArray() throws IOException {
+        token = Token.LONGSTRINGVAL;
+        ByteBuffer buf = new ByteBuffer();
+        count = 0;
+        readCh();
+
+loop:
+        for (;;) {
+            int c = ch;
+            switch (ch) {
+                case EOF:
+                    env.error(pos, "eof.in.string");
+                    break loop;
+
+                case '\n':
+                    readCh();
+                    env.error(pos, "newline.in.string");
+                    break loop;
+
+                case '\'':
+                    readCh();
+                    break loop;
+
+                case '\\':
+                    c = scanEscapeChar();
+                    if (c < 0) {
+                        break;
+                    }
+                // no break - continue
+                default:
+                    // see  description of java.io.DataOutput.writeUTF()
+                    if ((c > 0) && (c <= 0x7F)) {
+                        buf.write(c);
+                    } else if ((c == 0) || ((c >= 0x80) && (c <= 0x7FF))) {
+                        buf.write(0xC0 | (0x1F & (c >> 6)));
+                        buf.write(0x80 | (0x3f & c));
+                    } else {
+                        buf.write(0xc0 | (0x0f & (c >> 12)));
+                        buf.write(0x80 | (0x3f & (c >> 6)));
+                        buf.write(0x80 | (0x3f & c));
+                    }
+                    readCh();
+            }
+        }
+        longStringValue = buf;
+    }
+
+    /**
+     * Scan an Identifier. The current character should be the first character of the
+     * identifier.
+     */
+    private void scanIdentifier() throws IOException {
+        count = 0;
+        boolean compound = false;
+        for (;;) {
+            putc(ch);
+            readCh();
+            if ((ch == '/') || (ch == '.') || (ch == '-')) {
+                compound = true;
+            } else if (!Character.isJavaIdentifierPart((char) ch)) {
+                break;
+            }
+        }
+        stringValue = bufferString();
+        if (compound) {
+            token = Token.IDENT;
+        } else {
+            token = keyword_token_ident(stringValue);
+            if (token == Token.IDENT) {
+                intValue = constValue(stringValue);
+                if (intValue != -1) {
+                    // this is a constant
+                    if (debugCP) {
+                        ConstType ct = constType(stringValue);
+                        if (ct != null) {
+                            addConstDebug(ct);
+                        }
+                    }
+                    token = Token.INTVAL;
+                    intSize = 1;
+                    longValue = intValue;
+                }
+            }
+        }
+    } // end scanIdentifier
+
+    // skip till symbol
+    protected void skipTill(int sym) throws IOException {
+        while (true) {
+            if( ch == EOF ) {
+                env.error(pos, "eof.in.comment");
+                return;
+            } else if (ch == sym) {
+                return;
+            }
+            readCh();
+        }
+    }
+
+    protected int xscan() throws IOException {
+        int retPos = pos;
+        prevPos = in.pos;
+        docComment = null;
+        sign = 1;
+        for (;;) {
+            pos = in.pos;
+
+            switch (ch) {
+                case EOF:
+                    token = Token.EOF;
+                    return retPos;
+
+                case '\n':
+                case ' ':
+                case '\t':
+                case '\f':
+                    readCh();
+                    break;
+
+                case '/':
+                    readCh();
+                    switch (ch) {
+                        case '/':
+                            // Parse a // comment
+                            do {
+                                readCh();
+                            } while ((ch != EOF) && (ch != '\n'));
+                            break;
+
+                        case '*':
+                            readCh();
+                            if (ch == '*') {
+                                docComment = scanDocComment();
+                            } else {
+                                skipComment();
+                            }
+                            break;
+
+                        default:
+                            token = Token.DIV;
+                            return retPos;
+                    }
+                    break;
+
+                case '"':
+                    scanString();
+                    return retPos;
+
+                case '\'':
+                    scanCharArray();
+                    return retPos;
+
+                case '-':
+                    sign = -sign; // hack: no check that numbers only are allowed after
+                case '+':
+                    readCh();
+                    break;
+
+                case '0':
+                    readCh();
+                    token = Token.INTVAL;
+                    longValue = intValue = 0;
+                    switch (ch) {
+                        case 'x':
+                        case 'X':
+                            scanHexNumber();
+                            break;
+                        case '0':
+                        case '1':
+                        case '2':
+                        case '3':
+                        case '4':
+                        case '5':
+                        case '6':
+                        case '7':
+                        case '8':
+                        case '9':
+                            scanDecNumber();
+                            break;
+                        case 'b':
+                            readCh();
+                            intSize = 1;
+                            break;
+                        case 's':
+                            readCh();
+                            intSize = 2;
+                            break;
+                        case 'i':
+                            readCh();
+                            intSize = 4;
+                            break;
+                        case 'l':
+                            readCh();
+                            intSize = 8;
+                            break;
+                        default:
+                            intSize = 2;
+                    }
+                    return retPos;
+
+                case '1':
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7':
+                case '8':
+                case '9':
+                    scanDecNumber();
+                    return retPos;
+
+                case '{':
+                    readCh();
+                    token = Token.LBRACE;
+                    return retPos;
+
+                case '}':
+                    readCh();
+                    token = Token.RBRACE;
+                    return retPos;
+
+                case '(':
+                    readCh();
+                    token = Token.LPAREN;
+                    return retPos;
+
+                case ')':
+                    readCh();
+                    token = Token.RPAREN;
+                    return retPos;
+
+                case '[':
+                    readCh();
+                    token = Token.LSQBRACKET;
+                    return retPos;
+
+                case ']':
+                    readCh();
+                    token = Token.RSQBRACKET;
+                    return retPos;
+
+                case ',':
+                    readCh();
+                    token = Token.COMMA;
+                    return retPos;
+
+                case ';':
+                    readCh();
+                    token = Token.SEMICOLON;
+                    return retPos;
+
+                case ':':
+                    readCh();
+                    token = Token.COLON;
+                    return retPos;
+
+                case '=':
+                    readCh();
+                    if (ch == '=') {
+                        readCh();
+                        token = Token.EQ;
+                        return retPos;
+                    }
+                    token = Token.ASSIGN;
+                    return retPos;
+
+                case '\u001a':
+                    // Our one concession to DOS.
+                    readCh();
+                    if (ch == EOF) {
+                        token = Token.EOF;
+                        return retPos;
+                    }
+                    env.error(pos, "funny.char");
+                    readCh();
+                    break;
+
+                case '#':
+                    readCh();
+                    scanDecNumber();
+                    return retPos;
+
+                case '&': {
+                    readCh();
+                    retPos = pos;
+                    if (!Character.isJavaIdentifierStart((char) ch)) {
+                        env.error(pos, "identifier.expected");
+                    }
+                    scanIdentifier();
+                    String macroId = stringValue;
+                    String macro = (String) macros.get(macroId);
+                    if (macro == null) {
+                        env.error(pos, "macro.undecl", macroId);
+                        throw new SyntaxError();
+                    }
+                    setMacro(macro);
+                    readCh();
+                }
+                break;
+
+                default:
+                    if (Character.isJavaIdentifierStart((char) ch)) {
+                        scanIdentifier();
+                        return retPos;
+                    }
+                    env.error(pos, "funny.char");
+                    readCh();
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Scan to a matching '}', ']' or ')'. The current token must be a '{', '[' or '(';
+     */
+    protected void match(Token open, Token close) throws IOException {
+        int depth = 1;
+
+        while (true) {
+            scan();
+            if (token == open) {
+                depth++;
+            } else if (token == close) {
+                if (--depth == 0) {
+                    return;
+                }
+            } else if (token == Token.EOF) {
+                env.error(pos, "unbalanced.paren");
+                return;
+            }
+        }
+    }
+
+    /**
+     * Scan the next token.
+     *
+     * @return the position of the previous token.
+     */
+    protected int scan() throws IOException {
+        int retPos = xscan();
+//env.traceln("scanned:"+token+" ("+keywordName(token)+")");
+        return retPos;
+    }
+
+    /**
+     * Scan the next token.
+     *
+     * @return the position of the previous token.
+     */
+    protected int scanMacro() throws IOException {
+        int retPos = xscan();
+//env.traceln("scanned:"+token+" ("+keywordName(token)+")");
+        return retPos;
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jcoder/SourceFile.java b/test/lib/org/openjdk/asmtools/jcoder/SourceFile.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jcoder/SourceFile.java
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 2009, 2014, 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 org.openjdk.asmtools.jcoder;
+
+import java.io.*;
+import java.util.Hashtable;
+
+import org.openjdk.asmtools.common.Tool;
+import org.openjdk.asmtools.util.I18NResourceBundle;
+
+/**
+ * An input stream for java programs. The stream treats either "\n", "\r" or "\r\n" as the
+ * end of a line, it always returns \n. It also parses UNICODE characters expressed as
+ * \uffff. However, if it sees "\\", the second slash cannot begin a unicode sequence. It
+ * keeps track of the current position in the input stream.
+ *
+ * An position consists of: ((linenr &lt;&lt; OFFSETBITS) | offset) this means that both
+ * the line number and the exact offset into the file are encoded in each postion
+ * value.<p>
+ */
+public class SourceFile implements org.openjdk.asmtools.jasm.Constants {
+
+    Tool tool;
+
+    boolean traceFlag = false;
+    boolean debugInfoFlag = false;
+    /**
+     * The increment for each character.
+     */
+    static final int OFFSETINC = 1;
+    /**
+     * The increment for each line.
+     */
+    static final int LINEINC = 1 << OFFSETBITS;
+    String inputFileName;
+    InputStream in;
+    PrintWriter out;
+    int pos;
+    private int chpos;
+    private int pushBack = -1;
+
+    public SourceFile(Tool tool, DataInputStream dataInputStream, String inputFileName, PrintWriter out) {
+        this.tool = tool;
+        this.inputFileName = inputFileName;
+        this.in = new BufferedInputStream(dataInputStream);
+        chpos = LINEINC;
+        this.out = out;
+    }
+
+    public String getInputFileName() {
+        return inputFileName;
+    }
+
+    public void closeInp() {
+        try {
+            in.close();
+        } catch (IOException e) {
+        }
+        flushErrors();
+    }
+
+    public int read() throws IOException {
+        pos = chpos;
+        chpos += OFFSETINC;
+
+        int c = pushBack;
+        if (c == -1) {
+            c = in.read();
+        } else {
+            pushBack = -1;
+        }
+
+        // parse special characters
+        switch (c) {
+            case -2:
+                // -2 is a special code indicating a pushback of a backslash that
+                // definitely isn't the start of a unicode sequence.
+                return '\\';
+
+            case '\\':
+                if ((c = in.read()) != 'u') {
+                    pushBack = (c == '\\' ? -2 : c);
+                    return '\\';
+                }
+                // we have a unicode sequence
+                chpos += OFFSETINC;
+                while ((c = in.read()) == 'u') {
+                    chpos += OFFSETINC;
+                }
+
+                // unicode escape sequence
+                int d = 0;
+                for (int i = 0; i < 4; i++, chpos += OFFSETINC, c = in.read()) {
+                    switch (c) {
+                        case '0':
+                        case '1':
+                        case '2':
+                        case '3':
+                        case '4':
+                        case '5':
+                        case '6':
+                        case '7':
+                        case '8':
+                        case '9':
+                            d = (d << 4) + c - '0';
+                            break;
+
+                        case 'a':
+                        case 'b':
+                        case 'c':
+                        case 'd':
+                        case 'e':
+                        case 'f':
+                            d = (d << 4) + 10 + c - 'a';
+                            break;
+
+                        case 'A':
+                        case 'B':
+                        case 'C':
+                        case 'D':
+                        case 'E':
+                        case 'F':
+                            d = (d << 4) + 10 + c - 'A';
+                            break;
+
+                        default:
+                            error(pos, "invalid.escape.char");
+                            pushBack = c;
+                            return d;
+                    }
+                }
+                pushBack = c;
+                return d;
+
+            case '\n':
+                chpos += LINEINC;
+                return '\n';
+
+            case '\r':
+                if ((c = in.read()) != '\n') {
+                    pushBack = c;
+                } else {
+                    chpos += OFFSETINC;
+                }
+                chpos += LINEINC;
+                return '\n';
+
+            default:
+                return c;
+        }
+    }
+
+    public int lineNumber(int pos) {
+        return pos >>> OFFSETBITS;
+    }
+
+    public int lineNumber() {
+        return pos >>> OFFSETBITS;
+    }
+
+    /*==============================================================  Environment */
+    /**
+     * The number of errors and warnings
+     */
+    public int nerrors;
+    public int nwarnings;
+
+    public static final I18NResourceBundle i18n
+            = I18NResourceBundle.getBundleForClass(Main.class);
+
+    /*
+     *  Until place for jasm.properties is defind,
+     *  I have to keep them right here
+     *
+     static Hashtable properties = new Hashtable(40);
+
+     static {
+     // Scanner:
+     properties.put("err.eof.in.comment", "Comment not terminated at end of input.");
+     properties.put("err.invalid.number", "Invalid character \'%s\' in number.");
+     properties.put("err.invalid.octal.number", "Invalid character in octal number.");
+     properties.put("err.overflow", "Numeric overflow.");
+     properties.put("err.float.format", "Invalid floating point format.");
+     properties.put("err.eof.in.string", "String not terminated at end of input.");
+     properties.put("err.newline.in.string", "String not terminated at end of line.");
+     properties.put("err.funny.char", "Invalid character in input.");
+     properties.put("err.unbalanced.paren", "Unbalanced parentheses.");
+     // Parser:
+     properties.put("err.toplevel.expected", "Class or interface declaration expected.");
+     properties.put("err.token.expected", "'%s' expected.");
+     properties.put("err.identifier.expected", "Identifier expected.");
+     properties.put("err.name.expected", "Name expected.");
+     properties.put("err.io.exception", "I/O error in %s.");
+     properties.put("err.cannot.write", "Cannot write to %s.");
+     properties.put("warn.array.wronglength", "expected array length %s do not match real length %s; expected length written");
+     properties.put("warn.attr.wronglength", "expected attribute length %s do not match real length %s; expected length written");
+     properties.put("attrname.notfound", "Cannot find \"%s\" in constant pool");
+     properties.put("err.attrname.expected", "Attribute's name or index expected.");
+     properties.put("err.element.expected", "Primary data item expected.");
+     properties.put("err.struct.expected", "Structured data item expected.");
+     properties.put("err.macro.undecl", "Macro %s undefined.");
+     }
+     static String getProperty(String nm) {
+     return (String) properties.get(nm);
+     }
+     */
+    /**
+     * Error String
+     */
+    String errorString(String err, Object arg1, Object arg2, Object arg3) {
+        String str = null;
+
+        if (!err.startsWith("warn.")) {
+            err = "err." + err;
+        }
+        //str = getProperty(err);
+        str = i18n.getString(err);
+
+        if (str == null) {
+            return "error message '" + err + "' not found";
+        }
+
+        StringBuffer buf = new StringBuffer();
+        for (int i = 0; i < str.length(); i++) {
+            char c = str.charAt(i);
+            if ((c == '%') && (i + 1 < str.length())) {
+                switch (str.charAt(++i)) {
+                    case 's':
+                        String arg = arg1.toString();
+                        for (int j = 0; j < arg.length(); j++) {
+                            switch (c = arg.charAt(j)) {
+                                case ' ':
+                                case '\t':
+                                case '\n':
+                                case '\r':
+                                    buf.append((char) c);
+                                    break;
+
+                                default:
+                                    if ((c > ' ') && (c <= 255)) {
+                                        buf.append((char) c);
+                                    } else {
+                                        buf.append('\\');
+                                        buf.append('u');
+                                        buf.append(Integer.toString(c, 16));
+                                    }
+                            }
+                        }
+                        arg1 = arg2;
+                        arg2 = arg3;
+                        break;
+
+                    case '%':
+                        buf.append('%');
+                        break;
+
+                    default:
+                        buf.append('?');
+                        break;
+                }
+            } else {
+                buf.append((char) c);
+            }
+        }
+        return buf.toString();
+    }
+
+    /**
+     * List of outstanding error messages
+     */
+    ErrorMessage errors;
+
+    /**
+     * Insert an error message in the list of outstanding error messages. The list is
+     * sorted on input position.
+     */
+    void insertError(int where, String message) {
+        //output("ERR = " + message);
+        ErrorMessage msg = new ErrorMessage(where, message);
+        if (errors == null) {
+            errors = msg;
+        } else if (errors.where > where) {
+            msg.next = errors;
+            errors = msg;
+        } else {
+            ErrorMessage m = errors;
+            for (; (m.next != null) && (m.next.where <= where); m = m.next) {
+                ;
+            }
+            msg.next = m.next;
+            m.next = msg;
+        }
+    }
+
+    /**
+     * Flush outstanding errors
+     */
+    public void flushErrors() {
+        if (errors == null) {
+            return;
+        }
+
+        try {
+            // Read the file
+            DataInputStream dataInputStream = tool.getDataInputStream(inputFileName);
+            if (dataInputStream == null)
+                return;
+
+            byte data[] = new byte[dataInputStream.available()];
+            dataInputStream.read(data);
+            dataInputStream.close();
+
+            // Report the errors
+            for (ErrorMessage msg = errors; msg != null; msg = msg.next) {
+                int ln = msg.where >>> OFFSETBITS;
+                int off = msg.where & ((1 << OFFSETBITS) - 1);
+
+                int i, j;
+                for (i = off; (i > 0) && (data[i - 1] != '\n') && (data[i - 1] != '\r'); i--) {
+                    ;
+                }
+                for (j = off; (j < data.length) && (data[j] != '\n') && (data[j] != '\r'); j++) {
+                    ;
+                }
+
+                String prefix = inputFileName + ":" + ln + ":";
+                outputln(prefix + " " + msg.message);
+                outputln(new String(data, i, j - i));
+
+                char strdata[] = new char[(off - i) + 1];
+                for (j = i; j < off; j++) {
+                    strdata[j - i] = (data[j] == '\t') ? '\t' : ' ';
+                }
+                strdata[off - i] = '^';
+                outputln(new String(strdata));
+            }
+        } catch (IOException e) {
+            outputln("I/O exception");
+        }
+        errors = null;
+    }
+
+    /**
+     * Output a string. This can either be an error message or something for debugging.
+     * This should be used instead of print.
+     */
+    public void output(String msg) {
+        int len = msg.length();
+        for (int i = 0; i < len; i++) {
+            out.write(msg.charAt(i));
+        }
+        out.flush();
+    }
+
+    /**
+     * Output a string. This can either be an error message or something for debugging.
+     * This should be used instead of println.
+     */
+    public void outputln(String msg) {
+        output(msg);
+        out.write('\n');
+        out.flush();
+    }
+
+    /**
+     * Issue an error.
+     * @param where Offset in the source for the error
+     * @param err Error number (as defined in this interface)
+     * @param arg1 Optional argument to the error (null if not applicable)
+     * @param arg2 Optional argument to the error (null if not applicable)
+     * @param arg3 Optional argument to the error (null if not applicable)
+     */
+    /**
+     * Issue an error
+     */
+    public void error(int where, String err, Object arg1, Object arg2, Object arg3) {
+        String msg = errorString(err, arg1, arg2, arg3);
+        if (err.startsWith("warn.")) {
+            nwarnings++;
+        } else {
+            nerrors++;
+        }
+        traceln("error:" + msg);
+        insertError(where, msg);
+    }
+
+    public final void error(int where, String err, Object arg1, Object arg2) {
+        error(where, err, arg1, arg2, null);
+    }
+
+    public final void error(int where, String err, Object arg1) {
+        error(where, err, arg1, null, null);
+    }
+
+    public final void error(int where, String err) {
+        error(where, err, null, null, null);
+    }
+
+    public final void error(String err) {
+        error(pos, err, null, null, null);
+    }
+
+    public final void error(String err, Object arg1) {
+        error(pos, err, arg1, null, null);
+    }
+
+    /*==============================================================  trace */
+    public void trace(String message) {
+        if (traceFlag) {
+            output(message);
+        }
+    }
+
+    public void traceln(String message) {
+        if (traceFlag) {
+            outputln(message);
+        }
+    }
+} // end SourceFile
+
diff a/test/lib/org/openjdk/asmtools/jcoder/SyntaxError.java b/test/lib/org/openjdk/asmtools/jcoder/SyntaxError.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jcoder/SyntaxError.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2009, 2014, 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 org.openjdk.asmtools.jcoder;
+
+/**
+ * Syntax errors, should always be caught inside the parser for error recovery.
+ */
+class SyntaxError extends Error {
+}
diff a/test/lib/org/openjdk/asmtools/jcoder/i18n.properties b/test/lib/org/openjdk/asmtools/jcoder/i18n.properties
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jcoder/i18n.properties
@@ -0,0 +1,67 @@
+# Copyright (c) 2014, 2019, 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.
+
+jcoder.usage=\
+Usage: java -jar asmtools.jar jcoder [options] file.jcod...\n\
+where possible options include:
+
+jcoder.opt.nowrite=\
+\     -nowrite     do not write resulting .class files
+jcoder.opt.ignore=\
+\     -ignore      ingore non-fatal error(s) that suppress writing .class files
+jcoder.opt.d=\
+\     -d destdir   directory to place resulting .class files
+jcoder.opt.version=\
+\     -version     prints the program version
+
+jcoder.error.D_needs_macro=-D needs macro declaration
+jcoder.error.no_closing_quota=no closing quota in macro definition
+jcoder.error.d_requires_argument=-d requires argument
+jcoder.error.does_not_exist={0} does not exist
+jcoder.error.invalid_option=invalid option: {0}
+jcoder.error.cannot_read=cannot read {0}
+jcoder.error.cannot_write=cannot write {0}
+jcoder.error.fatal_error=fatal error
+jcoder.error.fatal_exception=fatal exception
+
+err.eof.in.comment=Comment not terminated at end of input.
+err.invalid.number=Invalid character '%s' in number.
+#err.invalid.octal.number=Invalid character in octal number.
+err.overflow=Numeric overflow.
+#err.float.format=Invalid floating point format.
+err.eof.in.string=String not terminated at end of input.
+err.newline.in.string=String not terminated at end of line.
+err.funny.char=Invalid character in input.
+err.unbalanced.paren=Unbalanced parentheses.
+err.toplevel.expected=Class or interface declaration expected.
+err.token.expected='{0}' expected.
+err.identifier.expected=Identifier expected.
+err.name.expected=Name expected.
+err.io.exception=I/O error in {0}.
+err.cannot.write=Cannot write to {0}.
+warn.array.wronglength=expected array length %s do not match real length %s; expected length written
+warn.attr.wronglength=expected attribute length %s do not match real length %s; expected length written
+#attrname.notfound=Cannot find "{0}" in constant pool
+err.attrname.expected=Attribute's name or index expected.
+err.element.expected=Primary data item expected.
+err.struct.expected=Structured data item expected.
+err.macro.undecl=Macro {0} undefined.
+err.invalid.escape.char=Invalid escape char
diff a/test/lib/org/openjdk/asmtools/jdec/ClassData.java b/test/lib/org/openjdk/asmtools/jdec/ClassData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdec/ClassData.java
@@ -0,0 +1,1292 @@
+/*
+ * Copyright (c) 2009, 2020, 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 org.openjdk.asmtools.jdec;
+
+import org.openjdk.asmtools.common.Module;
+import org.openjdk.asmtools.asmutils.StringUtils;
+import org.openjdk.asmtools.jasm.Modifiers;
+import org.openjdk.asmtools.jcoder.JcodTokens;
+import org.openjdk.asmtools.util.I18NResourceBundle;
+
+import java.awt.event.KeyEvent;
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import static java.lang.String.format;
+import static org.openjdk.asmtools.jasm.Tables.*;
+import static org.openjdk.asmtools.jasm.Tables.AnnotElemType.AE_UNKNOWN;
+import static org.openjdk.asmtools.jasm.TypeAnnotationTypes.*;
+
+/**
+ * Class data of the Java Decoder
+ */
+class ClassData {
+
+    private byte[] types;
+    private Object[] cpool;
+    private int CPlen;
+    private NestedByteArrayInputStream countedin;
+    private DataInputStream in;
+    private PrintWriter out;
+    private int[] cpe_pos;
+    private boolean printDetails;
+    private String entityType = "";
+    private String entityName = "";
+
+    public static I18NResourceBundle i18n
+            = I18NResourceBundle.getBundleForClass(Main.class);
+
+    ClassData(DataInputStream dis, int printFlags, PrintWriter out) throws IOException {
+        byte[] buf = new byte[dis.available()];
+        try {
+            if (dis.read(buf) <= 0)
+                throw new IOException("The file is empty");
+        } finally {
+            dis.close();
+        }
+        countedin = new NestedByteArrayInputStream(buf);
+        in = new DataInputStream(countedin);
+        this.out = out;
+        printDetails = ((printFlags & 1) == 1);
+    }
+
+    /*========================================================*/
+    private static final char[] hexTable = {
+            '0', '1', '2', '3', '4', '5', '6', '7',
+            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+    };
+
+    private String toHex(long val, int width) {
+        StringBuilder s = new StringBuilder();
+        for (int i = width * 2 - 1; i >= 0; i--) {
+            s.append(hexTable[((int) (val >> (4 * i))) & 0xF]);
+        }
+        return "0x" + s.toString();
+    }
+
+    private String toHex(long val) {
+        int width;
+        for (width = 8; width > 0; width--) {
+            if ((val >> (width - 1) * 8) != 0) {
+                break;
+            }
+        }
+        return toHex(val, width);
+    }
+
+    private void printByteHex(PrintWriter out, int b) {
+        out.print(hexTable[(b >> 4) & 0xF]);
+        out.print(hexTable[b & 0xF]);
+    }
+
+    private void printBytes(PrintWriter out, DataInputStream in, int len)
+            throws IOException {
+        try {
+            for (int i = 0; i < len; i++) {
+                if (i % 8 == 0) {
+                    out_print("0x");
+                }
+                printByteHex(out, in.readByte());
+                if (i % 8 == 7) {
+                    out.println(";");
+                }
+            }
+        } finally {
+            if (len % 8 != 0) {
+                out.println(";");
+            }
+        }
+    }
+
+    private void printRestOfBytes() {
+        for (int i = 0; ; i++) {
+            try {
+                byte b = in.readByte();
+                if (i % 8 == 0) {
+                    out_print("0x");
+                }
+                printByteHex(out, b);
+                if (i % 8 == 7) {
+                    out.print(";\n");
+                }
+            } catch (IOException e) {
+                return;
+            }
+        }
+    }
+
+    private void printUtf8InfoIndex(int index, String indexName) {
+        String name = (String) cpool[index];
+        out_print("#" + index + "; // ");
+        if (printDetails) {
+            out.println(String.format("%-16s",indexName) + " : " + name);
+        } else {
+            out.println(indexName);
+        }
+    }
+
+    /*========================================================*/
+    private int shift = 0;
+
+    private void out_begin(String s) {
+        for (int i = 0; i < shift; i++) {
+            out.print("  ");
+        }
+        out.println(s);
+        shift++;
+    }
+
+    private void out_print(String s) {
+        for (int i = 0; i < shift; i++) {
+            out.print("  ");
+        }
+        out.print(s);
+    }
+
+    private void out_println(String s) {
+        for (int i = 0; i < shift; i++) {
+            out.print("  ");
+        }
+        out.println(s);
+    }
+
+    private void out_end(String s) {
+        shift--;
+        for (int i = 0; i < shift; i++) {
+            out.print("  ");
+        }
+        out.println(s);
+    }
+
+    private String startArray(int length) {
+        return "[" + (printDetails ? Integer.toString(length) : "") + "]";
+    }
+
+    private void startArrayCmt(int length, String comment) {
+        out_begin(startArray(length) + format(" {%s", comment == null ? "" : " // " + comment));
+    }
+
+    private void startArrayCmtB(int length, String comment) {
+        out_begin(startArray(length) + format("b {%s", comment == null ? "" : " // " + comment));
+    }
+
+    /*========================================================*/
+    private void readCP(DataInputStream in) throws IOException {
+        int length = in.readUnsignedShort();
+        CPlen = length;
+        traceln(i18n.getString("jdec.trace.CP_len", length));
+        types = new byte[length];
+        cpool = new Object[length];
+        cpe_pos = new int[length];
+        for (int i = 1; i < length; i++) {
+            byte btag;
+            int v1;
+            long lv;
+            cpe_pos[i] = countedin.getPos();
+            btag = in.readByte();
+            traceln(i18n.getString("jdec.trace.CP_entry", i, btag));
+            types[i] = btag;
+            ConstType tg = tag(btag);
+            switch (tg) {
+                case CONSTANT_UTF8:
+                    cpool[i] = in.readUTF();
+                    break;
+                case CONSTANT_INTEGER:
+                    v1 = in.readInt();
+                    cpool[i] = v1;
+                    break;
+                case CONSTANT_FLOAT:
+                    v1 = Float.floatToIntBits(in.readFloat());
+                    cpool[i] = v1;
+                    break;
+                case CONSTANT_LONG:
+                    lv = in.readLong();
+                    cpool[i] = lv;
+                    i++;
+                    break;
+                case CONSTANT_DOUBLE:
+                    lv = Double.doubleToLongBits(in.readDouble());
+                    cpool[i] = lv;
+                    i++;
+                    break;
+                case CONSTANT_CLASS:
+                case CONSTANT_STRING:
+                case CONSTANT_MODULE:
+                case CONSTANT_PACKAGE:
+                    v1 = in.readUnsignedShort();
+                    cpool[i] = v1;
+                    break;
+                case CONSTANT_INTERFACEMETHOD:
+                case CONSTANT_FIELD:
+                case CONSTANT_METHOD:
+                case CONSTANT_NAMEANDTYPE:
+                    cpool[i] = "#" + in.readUnsignedShort() + " #" + in.readUnsignedShort();
+                    break;
+                case CONSTANT_DYNAMIC:
+                case CONSTANT_INVOKEDYNAMIC:
+                    cpool[i] = in.readUnsignedShort() + "s #" + in.readUnsignedShort();
+                    break;
+                case CONSTANT_METHODHANDLE:
+                    cpool[i] = in.readUnsignedByte() + "b #" + in.readUnsignedShort();
+                    break;
+                case CONSTANT_METHODTYPE:
+                    cpool[i] = "#" + in.readUnsignedShort();
+                    break;
+                default:
+                    CPlen = i;
+                    printCP(out);
+                    out_println(toHex(btag, 1) + "; // invalid constant type: " + (int) btag + " for element " + i);
+                    throw new ClassFormatError();
+            }
+        }
+    }
+
+    private void printCP(PrintWriter out) {
+        int length = CPlen;
+        startArrayCmt(length, "Constant Pool");
+        out_println("; // first element is empty");
+        try {
+            int size;
+            for (int i = 1; i < length; i = i + size) {
+                size = 1;
+                byte btag = types[i];
+                ConstType tg = tag(btag);
+                int pos = cpe_pos[i];
+                String tagstr;
+                String valstr;
+                int v1;
+                long lv;
+                if (tg != null) {
+                    tagstr = tg.parseKey();
+                } else {
+                    throw new Error("Can't get a tg representing the type of Constant in the Constant Pool at: " + i);
+                }
+                switch (tg) {
+                    case CONSTANT_UTF8: {
+                        tagstr = "Utf8";
+                        valstr = StringUtils.Utf8ToString((String) cpool[i]);
+                    }
+                    break;
+                    case CONSTANT_FLOAT:
+                    case CONSTANT_INTEGER:
+                        v1 = (Integer) cpool[i];
+                        valstr = toHex(v1, 4);
+                        break;
+                    case CONSTANT_DOUBLE:
+                    case CONSTANT_LONG:
+                        lv = (Long) cpool[i];
+                        valstr = toHex(lv, 8) + ";";
+                        size = 2;
+                        break;
+                    case CONSTANT_CLASS:
+                    case CONSTANT_MODULE:
+                    case CONSTANT_PACKAGE:
+                    case CONSTANT_STRING:
+                        v1 = (Integer) cpool[i];
+                        valstr = "#" + v1;
+                        break;
+                    case CONSTANT_INTERFACEMETHOD:
+                    case CONSTANT_FIELD:
+                    case CONSTANT_METHOD:
+                    case CONSTANT_NAMEANDTYPE:
+                    case CONSTANT_METHODHANDLE:
+                    case CONSTANT_METHODTYPE:
+                    case CONSTANT_DYNAMIC:
+                    case CONSTANT_INVOKEDYNAMIC:
+                        valstr = (String) cpool[i];
+                        break;
+                    default:
+                        throw new Error("invalid constant type: " + (int) btag);
+                }
+                out_print(tagstr + " " + valstr + "; // #" + i);
+                if (printDetails) {
+                    out_println(" at " + toHex(pos));
+                } else {
+                    out.println();
+                }
+            }
+        } finally {
+            out_end("} // Constant Pool");
+            out.println();
+        }
+    }
+
+    private String getStringPos() {
+        return " at " + toHex(countedin.getPos());
+    }
+
+    private String getCommentPosCond() {
+        if (printDetails) {
+            return " // " + getStringPos();
+        } else {
+            return "";
+        }
+    }
+
+    private void decodeCPXAttr(DataInputStream in, int len, String attrname, PrintWriter out) throws IOException {
+        decodeCPXAttrM(in, len, attrname, out, 1);
+    }
+
+    private void decodeCPXAttrM(DataInputStream in, int len, String attrname, PrintWriter out, int expectedIndices) throws IOException {
+        if (len != expectedIndices * 2) {
+            out_println("// invalid length of " + attrname + " attr: " + len + " (should be " + (expectedIndices * 2) + ") > ");
+            printBytes(out, in, len);
+        } else {
+            StringBuilder outputString = new StringBuilder();
+            for (int k = 1; k <= expectedIndices; k++) {
+                outputString.append("#").append(in.readUnsignedShort()).append("; ");
+                if (k % 16 == 0) {
+                    out_println(outputString.toString().replaceAll("\\s+$",""));
+                    outputString = new StringBuilder();
+                }
+            }
+            if (outputString.length() > 0) {
+                out_println(outputString.toString().replaceAll("\\s+$",""));
+            }
+        }
+    }
+
+    private void printStackMap(DataInputStream in, int elementsNum) throws IOException {
+        int num;
+        if (elementsNum > 0) {
+            num = elementsNum;
+        } else {
+            num = in.readUnsignedShort();
+        }
+        out.print(startArray(num) + (elementsNum > 0 ? "z" : "") + "{");
+        try {
+            for (int k = 0; k < num; k++) {
+                int maptype = in.readUnsignedByte();
+                StackMapType mptyp = stackMapType(maptype, out);
+                String maptypeImg;
+                if (printDetails) {
+                    maptypeImg = maptype + "b";
+                } else {
+                    try {
+                        maptypeImg = mptyp.parsekey();
+                    } catch (ArrayIndexOutOfBoundsException e) {
+                        maptypeImg = "/* BAD TYPE: */ " + maptype + "b";
+                    }
+                }
+                switch (mptyp) {
+                    case ITEM_Object:
+                    case ITEM_NewObject:
+                        maptypeImg = maptypeImg + "," + in.readUnsignedShort();
+                        break;
+                    case ITEM_UNKNOWN:
+                        maptypeImg = maptype + "b";
+                        break;
+                    default:
+                }
+                out.print(maptypeImg);
+                if (k < num - 1) {
+                    out.print("; ");
+                }
+            }
+        } finally {
+            out.print("}");
+        }
+    }
+
+    /**
+     * Processes 4.7.20 The RuntimeVisibleTypeAnnotations Attribute, 4.7.21 The RuntimeInvisibleTypeAnnotations Attribute
+     * <code>type_annotation</code> structure.
+     */
+    private void decodeTargetTypeAndRefInfo(DataInputStream in) throws IOException {
+        int tt = in.readUnsignedByte(); // [4.7.20] annotations[], type_annotation { u1 target_type; ...}
+        ETargetType targetType = ETargetType.getTargetType(tt);
+        if( targetType == null ) {
+            throw new Error("Type annotation: invalid target_type(u1) " + tt);
+        }
+        ETargetInfo targetInfo = targetType.targetInfo();
+        out_println(toHex(tt, 1) + ";  //  target_type: " + targetType.parseKey());
+        switch (targetInfo) {
+            case TYPEPARAM:          //[3.3.1] meth_type_param, class_type_param:
+                out_println(toHex(in.readUnsignedByte(), 1) + ";  //  param_index");
+                break;
+            case SUPERTYPE:         //[3.3.2]  class_exts_impls
+                out_println(toHex(in.readUnsignedShort(), 2) + ";  //  type_index");
+                break;
+            case TYPEPARAM_BOUND:   //[3.3.3]  class_type_param_bnds, meth_type_param_bnds
+                out_println(toHex(in.readUnsignedByte(), 1) + ";  //  param_index");
+                out_println(toHex(in.readUnsignedByte(), 1) + ";  //  bound_index");
+                break;
+            case EMPTY:             //[3.3.4]  meth_receiver, meth_ret_type, field
+                // NOTE: reference_info is empty for this annotation's target
+                break;
+            case METHODPARAM:       //[3.3.5]  meth_formal_param:
+                out_println(toHex(in.readUnsignedByte(), 1) + ";  //  parameter_index");
+                break;
+            case EXCEPTION:         //[3.3.61]  throws_type
+                //KTL:  Updated index to UShort for JSR308 change
+                out_println(in.readUnsignedShort() + ";  //  type_index");
+                break;
+            case LOCALVAR: //[3.3.7]  local_var, resource_var
+            {
+                int lv_num = in.readUnsignedShort();
+                startArrayCmt(lv_num, "local_variables");
+                try {
+                    for (int i = 0; i < lv_num; i++) {
+                        out_println(in.readUnsignedShort() + " " + in.readUnsignedShort()
+                                + " " + in.readUnsignedShort() + ";" + getCommentPosCond());
+                    }
+                } finally {
+                    out_end("}");
+                }
+            }
+            break;
+            case CATCH:             //[3.3.8]  exception_param
+                out_println(in.readUnsignedShort() + ";  //  exception_table_index");
+                break;
+            case OFFSET:            //[3.3.9]  type_test (instanceof), obj_creat (new)
+                // constr_ref_receiver, meth_ref_receiver
+                out_println(in.readUnsignedShort() + ";  //  offset");
+                break;
+            case TYPEARG:           //[3.3.10]  cast, constr_ref_typearg, meth_invoc_typearg
+                // constr_invoc_typearg, meth_ref_typearg
+                out_println(in.readUnsignedShort() + ";  //  offset");
+                out_println(toHex(in.readUnsignedByte(), 1) + ";  //  type_index");
+                break;
+            default:                // should never happen
+                out_println(toHex(tt, 1) + "; // invalid target_info: " + tt);
+                throw new ClassFormatError();
+        }
+        // [4.7.20.2]
+        int path_length = in.readUnsignedByte();  // type_path { u1 path_length; ...}
+        startArrayCmtB(path_length, "type_paths");
+        try {
+            for (int i = 0; i < path_length; i++) {
+                // print the type_path elements
+                out_println("{ " + toHex(in.readUnsignedByte(), 1)  // { u1 type_path_kind;
+                        + "; " + toHex(in.readUnsignedByte(), 1)    //   u1 type_argument_index; }
+                        + "; } // type_path[" + i + "]");           // path[i]
+            }
+        } finally {
+            out_end("}");
+        }
+    }
+
+    private void decodeElementValue(DataInputStream in, PrintWriter out) throws IOException {
+        out_begin("{  //  element_value");
+        try {
+            char tg = (char) in.readByte();
+            AnnotElemType tag = annotElemType(tg);
+            if (tag != AE_UNKNOWN) {
+                out_println("'" + tg + "';");
+            }
+            switch (tag) {
+                case AE_BYTE:
+                case AE_CHAR:
+                case AE_DOUBLE:
+                case AE_FLOAT:
+                case AE_INT:
+                case AE_LONG:
+                case AE_SHORT:
+                case AE_BOOLEAN:
+                case AE_STRING:
+                    decodeCPXAttr(in, 2, "const_value_index", out);
+                    break;
+                case AE_ENUM:
+                    out_begin("{  //  enum_const_value");
+                    decodeCPXAttr(in, 2, "type_name_index", out);
+                    decodeCPXAttr(in, 2, "const_name_index", out);
+                    out_end("}  //  enum_const_value");
+                    break;
+                case AE_CLASS:
+                    decodeCPXAttr(in, 2, "class_info_index", out);
+                    break;
+                case AE_ANNOTATION:
+                    decodeAnnotation(in, out);
+                    break;
+                case AE_ARRAY:
+                    int ev_num = in.readUnsignedShort();
+                    startArrayCmt(ev_num, "array_value");
+                    try {
+                        for (int i = 0; i < ev_num; i++) {
+                            decodeElementValue(in, out);
+                            if (i < ev_num - 1) {
+                                out_println(";");
+                            }
+                        }
+                    } finally {
+                        out_end("}  //  array_value");
+                    }
+                    break;
+                case AE_UNKNOWN:
+                default:
+                    String msg = "invalid element_value" + (isPrintableChar(tg) ? " tag type : " + tg : "");
+                    out_println(toHex(tg, 1) + "; // " + msg);
+                    throw new ClassFormatError(msg);
+            }
+        } finally {
+            out_end("}  //  element_value");
+        }
+    }
+
+    public boolean isPrintableChar(char c) {
+        Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
+        return (!Character.isISOControl(c)) &&
+                c != KeyEvent.CHAR_UNDEFINED &&
+                block != null &&
+                block != Character.UnicodeBlock.SPECIALS;
+    }
+
+    private void decodeAnnotation(DataInputStream in, PrintWriter out) throws IOException {
+        out_begin("{  //  annotation");
+        try {
+            decodeCPXAttr(in, 2, "field descriptor", out);
+            int evp_num = in.readUnsignedShort();
+            decodeElementValuePairs(evp_num, in, out);
+        } finally {
+            out_end("}  //  annotation");
+        }
+    }
+
+    private void decodeElementValuePairs(int count, DataInputStream in, PrintWriter out) throws IOException {
+        startArrayCmt(count, "element_value_pairs");
+        try {
+            for (int i = 0; i < count; i++) {
+                out_begin("{  //  element value pair");
+                try {
+                    decodeCPXAttr(in, 2, "name of the annotation type element", out);
+                    decodeElementValue(in, out);
+                } finally {
+                    out_end("}  //  element value pair");
+                    if (i < count - 1) {
+                        out_println(";");
+                    }
+                }
+            }
+        } finally {
+            out_end("}  //  element_value_pairs");
+        }
+    }
+
+    /**
+     * component_info {     JEP 359 Record(Preview): class file 58.65535
+     *     u2               name_index;
+     *     u2               descriptor_index;
+     *     u2               attributes_count;
+     *     attribute_info attributes[attributes_count];
+     * }
+     *
+     * or
+     * field_info {
+     *     u2             access_flags;
+     *     u2             name_index;
+     *     u2             descriptor_index;
+     *     u2             attributes_count;
+     *     attribute_info attributes[attributes_count];
+     * }
+     * or
+     * method_info {
+     *     u2             access_flags;
+     *     u2             name_index;
+     *     u2             descriptor_index;
+     *     u2             attributes_count;
+     *     attribute_info attributes[attributes_count];
+     * }
+     *
+     */
+    private void decodeInfo(DataInputStream in, PrintWriter out, String elementName, boolean hasAccessFlag) throws IOException {
+        out_begin("{  // " + elementName + (printDetails ? getStringPos() : ""));
+        try {
+            if(hasAccessFlag) {
+                //  u2 access_flags;
+                out_println(toHex(in.readShort(), 2) + "; // access");
+            }
+            // u2 name_index
+            printUtf8InfoIndex(in.readUnsignedShort(), "name_index");
+            // u2 descriptor_index
+            printUtf8InfoIndex(in.readUnsignedShort(), "descriptor_index");
+            // u2 attributes_count;
+            // attribute_info attributes[attributes_count]
+            decodeAttrs(in, out);
+        } finally {
+            out_end("}");
+        }
+    }
+
+    private void decodeTypeAnnotation(DataInputStream in, PrintWriter out) throws IOException {
+        out_begin("{  //  type_annotation");
+        try {
+            decodeTargetTypeAndRefInfo(in);
+            decodeCPXAttr(in, 2, "field descriptor", out);
+            int evp_num = in.readUnsignedShort();
+            decodeElementValuePairs(evp_num, in, out);
+        } finally {
+            out_end("}  //  type_annotation");
+        }
+    }
+
+    private void decodeBootstrapMethod(DataInputStream in) throws IOException {
+        out_begin("{  //  bootstrap_method");
+        try {
+            out_println("#" + in.readUnsignedShort() + "; // bootstrap_method_ref");
+            int bm_args_cnt = in.readUnsignedShort();
+            startArrayCmt(bm_args_cnt, "bootstrap_arguments");
+            try {
+                for (int i = 0; i < bm_args_cnt; i++) {
+                    out_println("#" + in.readUnsignedShort() + ";" + getCommentPosCond());
+                }
+            } finally {
+                out_end("}  //  bootstrap_arguments");
+            }
+        } finally {
+            out_end("}  //  bootstrap_method");
+        }
+    }
+
+    private void decodeAttr(DataInputStream in, PrintWriter out) throws IOException {
+        // Read one attribute
+        String posComment = getStringPos();
+        int name_cpx = in.readUnsignedShort(), btag, len;
+
+        String AttrName = "";
+        try {
+            btag = types[name_cpx];
+            ConstType tag = tag(btag);
+
+            if (tag == ConstType.CONSTANT_UTF8) {
+                AttrName = (String) cpool[name_cpx];
+            }
+        } catch (ArrayIndexOutOfBoundsException ignored) {
+        }
+        AttrTag tg = attrtag(AttrName);
+        String endingComment = AttrName;
+        len = in.readInt();
+        countedin.enter(len);
+        try {
+            if (printDetails) {
+                out_begin("Attr(#" + name_cpx + ", " + len + ") { // " + AttrName + posComment);
+            } else {
+                out_begin("Attr(#" + name_cpx + ") { // " + AttrName);
+            }
+
+            switch (tg) {
+                case ATT_Code:
+                    out_println(in.readUnsignedShort() + "; // max_stack");
+                    out_println(in.readUnsignedShort() + "; // max_locals");
+                    int code_len = in.readInt();
+                    out_begin("Bytes" + startArray(code_len) + "{");
+                    try {
+                        printBytes(out, in, code_len);
+                    } finally {
+                        out_end("}");
+                    }
+                    int trap_num = in.readUnsignedShort();
+                    startArrayCmt(trap_num, "Traps");
+                    try {
+                        for (int i = 0; i < trap_num; i++) {
+                            out_println(in.readUnsignedShort() + " " +
+                                    in.readUnsignedShort() + " " +
+                                    in.readUnsignedShort() + " " +
+                                    in.readUnsignedShort() + ";" +
+                                    getCommentPosCond());
+                        }
+                    } finally {
+                        out_end("} // end Traps");
+                    }
+                    // Read the attributes
+                    decodeAttrs(in, out);
+                    break;
+
+                case ATT_Exceptions:
+                    int count = in.readUnsignedShort();
+                    startArrayCmt(count, AttrName);
+                    try {
+                        for (int i = 0; i < count; i++) {
+                            out_println("#" + in.readUnsignedShort() + ";" +
+                                    getCommentPosCond());
+                        }
+                    } finally {
+                        out_end("}");
+                    }
+                    break;
+                case ATT_LineNumberTable:
+                    int ll_num = in.readUnsignedShort();
+                    startArrayCmt(ll_num, "line_number_table");
+                    try {
+                        for (int i = 0; i < ll_num; i++) {
+                            out_println(in.readUnsignedShort() + "  " +
+                                    in.readUnsignedShort() + ";" +
+                                    getCommentPosCond());
+                        }
+                    } finally {
+                        out_end("}");
+                    }
+                    break;
+                case ATT_LocalVariableTable:
+                case ATT_LocalVariableTypeTable:
+                    int lvt_num = in.readUnsignedShort();
+                    startArrayCmt(lvt_num, AttrName);
+                    try {
+                        for (int i = 0; i < lvt_num; i++) {
+                            out_println(in.readUnsignedShort() + " " +
+                                    in.readUnsignedShort() + " " +
+                                    in.readUnsignedShort() + " " +
+                                    in.readUnsignedShort() + " " +
+                                    in.readUnsignedShort() + ";" +
+                                    getCommentPosCond());
+                        }
+                    } finally {
+                        out_end("}");
+                    }
+                    break;
+                case ATT_InnerClasses:
+                    int ic_num = in.readUnsignedShort();
+                    startArrayCmt(ic_num, "classes");
+                    try {
+                        for (int i = 0; i < ic_num; i++) {
+                            out_println("#" + in.readUnsignedShort() + " #" +
+                                    in.readUnsignedShort() + " #" +
+                                    in.readUnsignedShort() + " " +
+                                    in.readUnsignedShort() + ";" + getCommentPosCond());
+                        }
+                    } finally {
+                        out_end("}");
+                    }
+                    break;
+                case ATT_StackMap:
+                    int e_num = in.readUnsignedShort();
+                    startArrayCmt(e_num, "");
+                    try {
+                        for (int k = 0; k < e_num; k++) {
+                            int start_pc = in.readUnsignedShort();
+                            out_print("" + start_pc + ", ");
+                            printStackMap(in, 0);
+                            out.print(", ");
+                            printStackMap(in, 0);
+                            out.println(";");
+                        }
+                    } finally {
+                        out_end("}");
+                    }
+                    break;
+                case ATT_StackMapTable:
+                    int et_num = in.readUnsignedShort();
+                    startArrayCmt(et_num, "");
+                    try {
+                        for (int k = 0; k < et_num; k++) {
+                            int frame_type = in.readUnsignedByte();
+                            StackMapFrameType ftype = stackMapFrameType(frame_type);
+                            switch (ftype) {
+                                case SAME_FRAME:
+                                    // type is same_frame;
+                                    out_print("" + frame_type + "b");
+                                    out.println("; // same_frame");
+                                    break;
+                                case SAME_LOCALS_1_STACK_ITEM_FRAME:
+                                    // type is same_locals_1_stack_item_frame
+                                    out_print("" + frame_type + "b, ");
+                                    // read additional single stack element
+                                    printStackMap(in, 1);
+                                    out.println("; // same_locals_1_stack_item_frame");
+                                    break;
+                                case SAME_LOCALS_1_STACK_ITEM_EXTENDED_FRAME:
+                                    // type is same_locals_1_stack_item_frame_extended
+                                    int noffset = in.readUnsignedShort();
+                                    out_print("" + frame_type + "b, " + noffset + ", ");
+                                    // read additional single stack element
+                                    printStackMap(in, 1);
+                                    out.println("; // same_locals_1_stack_item_frame_extended");
+                                    break;
+                                case CHOP_1_FRAME:
+                                case CHOP_2_FRAME:
+                                case CHOP_3_FRAME:
+                                    // type is chop_frame
+                                    int coffset = in.readUnsignedShort();
+                                    out_print("" + frame_type + "b, " + coffset);
+                                    out.println("; // chop_frame " + (251 - frame_type));
+                                    break;
+                                case SAME_FRAME_EX:
+                                    // type is same_frame_extended;
+                                    int xoffset = in.readUnsignedShort();
+                                    out_print("" + frame_type + "b, " + xoffset);
+                                    out.println("; // same_frame_extended");
+                                    break;
+                                case APPEND_FRAME:
+                                    // type is append_frame
+                                    int aoffset = in.readUnsignedShort();
+                                    out_print("" + frame_type + "b, " + aoffset + ", ");
+                                    // read additional locals
+                                    printStackMap(in, frame_type - 251);
+                                    out.println("; // append_frame " + (frame_type - 251));
+                                    break;
+                                case FULL_FRAME:
+                                    // type is full_frame
+                                    int foffset = in.readUnsignedShort();
+                                    out_print("" + frame_type + "b, " + foffset + ", ");
+                                    printStackMap(in, 0);
+                                    out.print(", ");
+                                    printStackMap(in, 0);
+                                    out.println("; // full_frame");
+                                    break;
+                            }
+                        }
+                    } finally {
+                        out_end("}");
+                    }
+                    break;
+                case ATT_EnclosingMethod:
+                    decodeCPXAttrM(in, len, AttrName, out, 2);
+                    break;
+                case ATT_AnnotationDefault:
+                    decodeElementValue(in, out);
+                    break;
+                case ATT_RuntimeInvisibleAnnotations:
+                case ATT_RuntimeVisibleAnnotations:
+                    int an_num = in.readUnsignedShort();
+                    startArrayCmt(an_num, "annotations");
+                    try {
+                        for (int i = 0; i < an_num; i++) {
+                            decodeAnnotation(in, out);
+                            if (i < an_num - 1) {
+                                out_println(";");
+                            }
+                        }
+                    } finally {
+                        out_end("}");
+                    }
+                    break;
+                // 4.7.20 The RuntimeVisibleTypeAnnotations Attribute
+                // 4.7.21 The RuntimeInvisibleTypeAnnotations Attribute
+                case ATT_RuntimeInvisibleTypeAnnotations:
+                case ATT_RuntimeVisibleTypeAnnotations:
+                    int ant_num = in.readUnsignedShort();
+                    startArrayCmt(ant_num, "annotations");
+                    try {
+                        for (int i = 0; i < ant_num; i++) {
+                            decodeTypeAnnotation(in, out);
+                            if (i < ant_num - 1) {
+                                out_println(";");
+                            }
+                        }
+                    } finally {
+                        out_end("}");
+                    }
+                    break;
+                case ATT_RuntimeInvisibleParameterAnnotations:
+                case ATT_RuntimeVisibleParameterAnnotations:
+                    int pm_num = in.readUnsignedByte();
+                    startArrayCmtB(pm_num, "parameters");
+                    try {
+                        for (int k = 0; k < pm_num; k++) {
+                            int anp_num = in.readUnsignedShort();
+                            startArrayCmt(anp_num, "annotations");
+                            try {
+                                for (int i = 0; i < anp_num; i++) {
+                                    decodeAnnotation(in, out);
+                                    if (k < anp_num - 1) {
+                                        out_println(";");
+                                    }
+                                }
+                            } finally {
+                                out_end("}");
+                            }
+                            if (k < pm_num - 1) {
+                                out_println(";");
+                            }
+                        }
+                    } finally {
+                        out_end("}");
+                    }
+                    break;
+                case ATT_BootstrapMethods:
+                    int bm_num = in.readUnsignedShort();
+                    startArrayCmt(bm_num, "bootstrap_methods");
+                    try {
+                        for (int i = 0; i < bm_num; i++) {
+                            decodeBootstrapMethod(in);
+                            if (i < bm_num - 1) {
+                                out_println(";");
+                            }
+                        }
+                    } finally {
+                        out_end("}");
+                    }
+                    break;
+                case ATT_Module:
+                    decodeModule(in);
+                    break;
+                case ATT_TargetPlatform:
+                    decodeCPXAttrM(in, len, AttrName, out, 3);
+                    break;
+                case ATT_ModulePackages:
+                    int p_num = in.readUnsignedShort();
+                    startArrayCmt(p_num, null);
+                    try {
+                        decodeCPXAttrM(in, len - 2, AttrName, out, p_num);
+                    } finally {
+                        out_end("}");
+                    }
+                    break;
+                //  MethodParameters_attribute {
+                //    u2 attribute_name_index;
+                //    u4 attribute_length;
+                //    u1 parameters_count;
+                //    {   u2 name_index;
+                //        u2 access_flags;
+                //    } parameters[parameters_count];
+                //  }
+                case ATT_MethodParameters:
+                    int pcount = in.readUnsignedByte();
+                    startArrayCmtB(pcount, AttrName);
+                    try {
+                        for (int i = 0; i < pcount; i++) {
+                            out_println("#" + in.readUnsignedShort() + "  " +
+                                    toHex(in.readUnsignedShort(), 2) + ";" +
+                                    getCommentPosCond());
+                        }
+                    } finally {
+                        out_end("}");
+                    }
+                    break;
+                //  JEP 359 Record(Preview): class file 58.65535
+                //  Record_attribute {
+                //      u2 attribute_name_index;
+                //      u4 attribute_length;
+                //      u2 components_count;
+                //      component_info components[components_count];
+                //  }
+                case ATT_Record:
+                    int ncomps = in.readUnsignedShort();
+                    startArrayCmt(ncomps, "components");
+                    try {
+                        for (int i = 0; i < ncomps; i++) {
+                            decodeInfo(in,out,"component",false);
+                            if (i < ncomps - 1) {
+                                out_println(";");
+                            }
+                        }
+                    } finally {
+                        out_end("}");
+                    }
+                    break;
+                case ATT_ConstantValue:
+                case ATT_Signature:
+                case ATT_SourceFile:
+                    decodeCPXAttr(in, len, AttrName, out);
+                    break;
+                    //  JEP 181 (Nest-based Access Control): class file 55.0
+                    //  NestHost_attribute {
+                    //    u2 attribute_name_index;
+                    //    u4 attribute_length;
+                    //    u2 host_class_index;
+                    //  }
+                case ATT_NestHost:
+                    decodeTypes(in, out, 1);
+                    break;
+                    //  JEP 181 (Nest-based Access Control): class file 55.0
+                    //  NestMembers_attribute {
+                    //    u2 attribute_name_index;
+                    //    u4 attribute_length;
+                    //    u2 number_of_classes;
+                    //    u2 classes[number_of_classes];
+                    //  }
+                case ATT_NestMembers:
+                    //  JEP 360 (Sealed types): class file 59.65535
+                    //  PermittedSubclasses_attribute {
+                    //    u2 attribute_name_index;
+                    //    u4 attribute_length;
+                    //    u2 number_of_classes;
+                    //    u2 classes[number_of_classes];
+                    //  }
+                case ATT_PermittedSubclasses:
+                    // Preload attribute has same format
+                case ATT_Preload:
+                    int nsubtypes = in.readUnsignedShort();
+                    startArrayCmt(nsubtypes, "classes");
+                    try {
+                        decodeTypes(in, out, nsubtypes);
+                    } finally {
+                        out_end("}");
+                    }
+                    break;
+                default:
+                    printBytes(out, in, len);
+                    if (AttrName == null) {
+                        endingComment = "Attr(#" + name_cpx + ")";
+                    }
+            }
+
+        } catch (EOFException e) {
+            out.println("// ======== unexpected end of attribute array");
+        } finally {
+            int rest = countedin.available();
+            if (rest > 0) {
+                out.println("// ======== attribute array started " + posComment + " has " + rest + " bytes more:");
+                printBytes(out, in, rest);
+            }
+            out_end("} // end " + endingComment);
+            countedin.leave();
+        }
+    }
+
+    private void decodeModuleStatement(String statementName, DataInputStream in) throws IOException {
+        // u2 {exports|opens}_count
+        int count = in.readUnsignedShort();
+        startArrayCmt(count, statementName);
+        try {
+            for (int i = 0; i < count; i++) {
+                // u2 {exports|opens}_index; u2 {exports|opens}_flags
+                int index = in.readUnsignedShort();
+                int nFlags = in.readUnsignedShort();
+                String sFlags = printDetails ? Module.Modifier.getStatementFlags(nFlags) : "";
+                out_println("#" + index + " " + toHex(nFlags, 2) + (sFlags.isEmpty() ? "" : " // [ " + sFlags + " ]"));
+                int exports_to_count = in.readUnsignedShort();
+                startArrayCmt(exports_to_count, null);
+                try {
+                    for (int j = 0; j < exports_to_count; j++) {
+                        out_println("#" + in.readUnsignedShort() + ";");
+                    }
+                } finally {
+                    out_end("};");
+                }
+            }
+        } finally {
+            out_end("} // " + statementName + "\n");
+        }
+    }
+
+    private void decodeModule(DataInputStream in) throws IOException {
+        //u2 module_name_index
+        int index = in.readUnsignedShort();
+        entityName = (String) cpool[(Integer) cpool[index]];
+        out_print("#" + index + "; // ");
+        if (printDetails) {
+            out.println(String.format("%-16s","name_index") + " : " + entityName);
+        } else {
+            out.println("name_index");
+        }
+
+        // u2 module_flags
+        int moduleFlags = in.readUnsignedShort();
+        out_print(toHex(moduleFlags, 2) + "; // flags");
+        if (printDetails) {
+            out_print(" " + Module.Modifier.getModuleFlags(moduleFlags));
+        }
+        out.println();
+
+        //u2 module_version
+        int versionIndex = in.readUnsignedShort();
+        out_println("#" + versionIndex + "; // version");
+
+        // u2 requires_count
+        int count = in.readUnsignedShort();
+        startArrayCmt(count, "requires");
+        try {
+            for (int i = 0; i < count; i++) {
+                // u2 requires_index; u2 requires_flags; u2 requires_version_index
+                index = in.readUnsignedShort();
+                int nFlags = in.readUnsignedShort();
+                versionIndex = in.readUnsignedShort();
+                String sFlags = printDetails ? Module.Modifier.getStatementFlags(nFlags) : "";
+                out_println("#" + index + " " + toHex(nFlags, 2) + " #" + versionIndex + ";" + (sFlags.isEmpty() ? "" : " // " + sFlags));
+            }
+        } finally {
+            out_end("} // requires\n");
+        }
+
+        decodeModuleStatement("exports", in);
+
+        decodeModuleStatement("opens", in);
+        // u2 uses_count
+        count = in.readUnsignedShort();
+        startArrayCmt(count, "uses");
+        try {
+            for (int i = 0; i < count; i++) {
+                // u2 uses_index
+                out_println("#" + in.readUnsignedShort() + ";");
+            }
+        } finally {
+            out_end("} // uses\n");
+        }
+        count = in.readUnsignedShort(); // u2 provides_count
+        startArrayCmt(count, "provides");
+        try {
+            for (int i = 0; i < count; i++) {
+                // u2 provides_index
+                out_println("#" + in.readUnsignedShort());
+                int provides_with_count = in.readUnsignedShort();
+                // u2 provides_with_count
+                startArrayCmt(provides_with_count, null);
+                try {
+                    for (int j = 0; j < provides_with_count; j++) {
+                        // u2 provides_with_index;
+                        out_println("#" + in.readUnsignedShort() + ";");
+                    }
+                } finally {
+                    out_end("};");
+                }
+            }
+        } finally {
+            out_end("} // provides\n");
+        }
+    }
+
+    private void decodeAttrs(DataInputStream in, PrintWriter out) throws IOException {
+        // Read the attributes
+        int attr_num = in.readUnsignedShort();
+        startArrayCmt(attr_num, "Attributes");
+        try {
+            for (int i = 0; i < attr_num; i++) {
+                decodeAttr(in, out);
+                if (i + 1 < attr_num) {
+                    out_println(";");
+                }
+            }
+        } finally {
+            out_end("} // Attributes");
+        }
+    }
+
+    private void decodeMembers(DataInputStream in, PrintWriter out, String groupName, String elementName) throws IOException {
+        int count = in.readUnsignedShort();
+        traceln(groupName + "=" + count);
+        startArrayCmt(count, groupName);
+        try {
+            for (int i = 0; i < count; i++) {
+                decodeInfo(in,out,elementName,true);
+                if (i + 1 < count) {
+                    out_println(";");
+                }
+            }
+        } finally {
+            out_end("} // " + groupName);
+            out.println();
+        }
+    }
+
+    void decodeClass(String fileName) throws IOException {
+        // Read the header
+        try {
+            int magic = in.readInt();
+            int min_version = in.readUnsignedShort();
+            int version = in.readUnsignedShort();
+
+            // Read the constant pool
+            readCP(in);
+            short access = in.readShort(); // don't care about sign
+            int this_cpx = in.readUnsignedShort();
+
+            try {
+                entityName = (String) cpool[(Integer) cpool[this_cpx]];
+                if (entityName.equals("module-info")) {
+                    entityType = "module";
+                    entityName = "";
+                } else {
+                    entityType = "class";
+                }
+                if (!entityName.isEmpty() && (JcodTokens.keyword_token_ident(entityName) != JcodTokens.Token.IDENT || JcodTokens.constValue(entityName) != -1)) {
+                    // JCod can't parse a entityName matching a keyword or a constant value,
+                    // then use the filename instead:
+                    out_begin(String.format("file \"%s.class\" {", entityName));
+                } else {
+                    out_begin(format("%s %s {", entityType, entityName));
+                }
+            } catch (Exception e) {
+                entityName = fileName;
+                out.println("// " + e.getMessage() + " while accessing entityName");
+                out_begin(format("%s %s { // source file name", entityType, entityName));
+            }
+
+            out_print(toHex(magic, 4) + ";");
+            if (magic != JAVA_MAGIC) {
+                out.print(" // wrong magic: 0x" + Integer.toString(JAVA_MAGIC, 16) + " expected");
+            }
+            out.println();
+            out_println(min_version + "; // minor version");
+            out_println(version + "; // version");
+
+            // Print the constant pool
+            printCP(out);
+            out_println(toHex(access, 2) + "; // access" +
+                    (printDetails ? " [" + (" " + Modifiers.accessString(access, CF_Context.CTX_CLASS).toUpperCase()).replaceAll(" (\\S)", " ACC_$1") + "]" : ""));
+            out_println("#" + this_cpx + ";// this_cpx");
+            int super_cpx = in.readUnsignedShort();
+            out_println("#" + super_cpx + ";// super_cpx");
+            traceln(i18n.getString("jdec.trace.access_thisCpx_superCpx", access, this_cpx, super_cpx));
+            out.println();
+
+            // Read the interfaces
+            int numinterfaces = in.readUnsignedShort();
+            traceln(i18n.getString("jdec.trace.numinterfaces", numinterfaces));
+            startArrayCmt(numinterfaces, "Interfaces");
+            try {
+                decodeTypes(in, out, numinterfaces);
+            } finally {
+                out_end("} // Interfaces\n");
+            }
+            // Read the fields
+            decodeMembers(in, out, "Fields", "field");
+
+            // Read the methods
+            decodeMembers(in, out, "Methods", "method");
+
+            // Read the attributes
+            decodeAttrs(in, out);
+        } catch (EOFException ignored) {
+        } catch (ClassFormatError err) {
+            String msg = err.getMessage();
+            out.println("//------- ClassFormatError" +
+                    (msg == null || msg.isEmpty() ? "" : ": " + msg));
+            printRestOfBytes();
+        } finally {
+            out_end(format("} // end %s %s", entityType, entityName));
+        }
+    } // end decodeClass()
+
+    private void decodeTypes(DataInputStream in, PrintWriter out, int count) throws IOException {
+        for (int i = 0; i < count; i++) {
+            int type_cpx = in.readUnsignedShort();
+            traceln(i18n.getString("jdec.trace.type", i, type_cpx));
+            out_print("#" + type_cpx + ";");
+            if (printDetails) {
+                String name = (String) cpool[(int)cpool[type_cpx]];
+                out.println(" // " + name + getStringPos());
+            } else {
+                out.println();
+            }
+        }
+    }
+
+    /* ====================================================== */
+    boolean DebugFlag = false;
+
+    public void trace(String s) {
+        if (!DebugFlag) {
+            return;
+        }
+        System.out.print(s);
+    }
+
+    public void traceln(String s) {
+        if (!DebugFlag) {
+            return;
+        }
+        System.out.println(s);
+    }
+}// end class ClassData
diff a/test/lib/org/openjdk/asmtools/jdec/Main.java b/test/lib/org/openjdk/asmtools/jdec/Main.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdec/Main.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2009, 2019, 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 org.openjdk.asmtools.jdec;
+
+import org.openjdk.asmtools.common.Tool;
+import org.openjdk.asmtools.jdis.uEscWriter;
+import org.openjdk.asmtools.util.I18NResourceBundle;
+import org.openjdk.asmtools.util.ProductInfo;
+
+import java.io.DataInputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Main program of the Java DECoder :: class to jcod
+ */
+public class Main extends Tool {
+
+    int     printFlags = 0;
+
+    public static final I18NResourceBundle i18n
+            = I18NResourceBundle.getBundleForClass(Main.class);
+
+    public Main(PrintWriter out, PrintWriter err, String programName) {
+        super(out, err, programName);
+        printCannotReadMsg = (fname) ->
+                error( i18n.getString("jdec.error.cannot_read", fname));
+    }
+
+    public Main(PrintStream out, String program) {
+        this(new PrintWriter(out), new PrintWriter(System.err), program);
+    }
+
+    @Override
+    public void usage() {
+        println(i18n.getString("jdec.usage"));
+        println(i18n.getString("jdec.opt.g"));
+        println(i18n.getString("jdec.opt.version"));
+    }
+
+    /**
+     * Run the decoder
+     */
+    public synchronized boolean decode(String argv[]) {
+        long tm = System.currentTimeMillis();
+        ArrayList<String> vargs = new ArrayList<>();
+        ArrayList<String> vj = new ArrayList<>();
+        boolean nowrite = false;
+        int addOptions = 0;
+
+        // Parse arguments
+        int i = 0;
+        for (String arg : argv) {
+            //
+            if (arg.equals("-g")) {
+                printFlags = printFlags | 1;
+                vargs.add(arg);
+            } else if (arg.equals("-v")) {
+                DebugFlag = () -> true;
+                vargs.add(arg);
+                out.println("arg[" + i + "]=" + argv[i] + "/verbose");
+            } else if (arg.equals("-version")) {
+                out.println(ProductInfo.FULL_VERSION);
+            } else if (arg.startsWith("-")) {
+                error(i18n.getString("jdec.error.invalid_flag", arg));
+                usage();
+                return false;
+            } else {
+                vargs.add(arg);
+                vj.add(arg);
+            }
+            i += 1;
+        }
+
+        if (vj.isEmpty()) {
+            usage();
+            return false;
+        }
+
+        String[] names = new String[0];
+        names = vj.toArray(names);
+        for (String inpname : names) {
+            try {
+                DataInputStream dataInputStream = getDataInputStream(inpname);
+                if( dataInputStream == null )
+                    return false;
+                ClassData cc = new ClassData(dataInputStream, printFlags, out);
+                cc.DebugFlag = DebugFlag.getAsBoolean();
+                cc.decodeClass(inpname);
+                out.flush();
+                continue;
+            } catch (Error ee) {
+                if (DebugFlag.getAsBoolean())
+                    ee.printStackTrace();
+                error(i18n.getString("jdec.error.fatal_error"));
+            } catch (Exception ee) {
+                if (DebugFlag.getAsBoolean())
+                    ee.printStackTrace();
+                error(i18n.getString("jdec.error.fatal_exception"));
+            }
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Main program
+     */
+    public static void main(String argv[]) {
+        Main decoder = new Main(new PrintWriter(new uEscWriter(System.out)), new PrintWriter(System.err), "jdec");
+        System.exit(decoder.decode(argv) ? 0 : 1);
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jdec/NestedByteArrayInputStream.java b/test/lib/org/openjdk/asmtools/jdec/NestedByteArrayInputStream.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdec/NestedByteArrayInputStream.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2009, 2019, 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 org.openjdk.asmtools.jdec;
+
+import java.io.ByteArrayInputStream;
+import java.util.Stack;
+
+/**
+ * this class provides functionality needed to read class files:
+ * <ul>
+ * <li>methods to read unsigned integers of various length
+ * <li>counts bytes read so far
+ * </ul>
+ */
+public class NestedByteArrayInputStream extends ByteArrayInputStream {
+
+    NestedByteArrayInputStream(byte buf[]) {
+        super(buf);
+    }
+
+    NestedByteArrayInputStream(byte buf[], int offset, int length) {
+        super(buf, offset, length);
+    }
+
+    public int getPos() {
+        return pos;
+    }
+    Stack savedStates = new Stack();
+
+    public void enter(int range) {
+        savedStates.push(count);
+        if (pos + range < count) {
+            count = pos + range;
+        }
+    }
+
+    public void leave() {
+        pos = count;
+        count = ((Integer) savedStates.pop()).intValue();
+    }
+} // end class NestedByteArrayInputStream
+
diff a/test/lib/org/openjdk/asmtools/jdec/i18n.properties b/test/lib/org/openjdk/asmtools/jdec/i18n.properties
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdec/i18n.properties
@@ -0,0 +1,41 @@
+# Copyright (c) 2014, 2020, 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.
+
+jdec.error.invalid_flag=invalid flag: " {0}
+jdec.error.fatal_error=fatal error
+jdec.error.fatal_exception=fatal exception
+jdec.error.cannot_read=cannot read {0}
+jdec.usage=\
+Usage: java -jar asmtools.jar jdec [options] FILE.class... > FILE.jcod\n\
+where possible options include:
+
+jdec.opt.g=\
+\     -g:  detailed output format
+jdec.opt.version=\
+\     -version:  print version number and date
+
+
+jdec.trace.CP_len=CP len= {0}
+jdec.trace.CP_entry=CP entry # {0} tag= {1}
+jdec.trace.access_thisCpx_superCpx=access={0} this_cpx={1} super_cpx={2}
+jdec.trace.numinterfaces=numinterfaces={0}
+jdec.trace.type=\
+\  type_cpx[{0}]={1}
diff a/test/lib/org/openjdk/asmtools/jdis/AnnotationData.java b/test/lib/org/openjdk/asmtools/jdis/AnnotationData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/AnnotationData.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jdis;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+import static java.lang.String.format;
+
+/**
+ *
+ */
+public class  AnnotationData {
+    /*-------------------------------------------------------- */
+    /* AnnotData Fields */
+
+    protected String visAnnotToken = "@+";
+    protected String invAnnotToken = "@-";
+    protected String dataName = "AnnotationData";
+    private boolean invisible = false;
+    private int type_cpx = 0;  //an index into the constant pool indicating the annotation type for this annotation.
+    private ArrayList<AnnotationElement> array = new ArrayList<>();
+    private ClassData cls;
+    /*-------------------------------------------------------- */
+
+    public AnnotationData(boolean invisible, ClassData cls) {
+        this.cls = cls;
+        this.invisible = invisible;
+    }
+
+    public void read(DataInputStream in) throws IOException {
+        type_cpx = in.readShort();
+        int elemValueLength = in.readShort();
+        TraceUtils.traceln(3, format(" %s: name[%d]=%s", dataName, type_cpx, cls.pool.getString(type_cpx)),
+                format(" %s: %s  num_elems: %d", dataName, cls.pool.getString(type_cpx), elemValueLength));
+        for (int evc = 0; evc < elemValueLength; evc++) {
+            AnnotationElement elem = new AnnotationElement(cls);
+            TraceUtils.traceln(3, format(" %s: %s reading [%d]", dataName, cls.pool.getString(type_cpx), evc));
+            elem.read(in, invisible);
+            array.add(elem);
+        }
+    }
+
+    public void print(PrintWriter out, String tab) {
+        printHeader(out, tab);
+        printBody(out, "");
+    }
+
+    protected void printHeader(PrintWriter out, String tab) {
+        //Print annotation Header, which consists of the
+        // Annotation Token ('@'), visibility ('+', '-'),
+        // and the annotation name (type index, CPX).
+
+        // Mark whether it is invisible or not.
+        if (invisible) {
+            out.print(tab + invAnnotToken);
+        } else {
+            out.print(tab + visAnnotToken);
+        }
+        String annoName = cls.pool.getString(type_cpx);
+
+        // converts class type to java class name
+        if (annoName.startsWith("L") && annoName.endsWith(";")) {
+            annoName = annoName.substring(1, annoName.length() - 1);
+        }
+
+        out.print(annoName);
+    }
+
+    protected void printBody(PrintWriter out, String tab) {
+        // For a standard annotation, print out brackets,
+        // and list the name/value pairs.
+        out.print(" { ");
+        int i = 0;
+        for (AnnotationElement elem : array) {
+            elem.print(out, tab);
+            if (i++ < array.size() - 1) {
+                out.print(", ");
+            }
+        }
+        out.print("  }");
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        String annoName = cls.pool.getString(type_cpx);
+
+        // converts class type to java class name
+        if (annoName.startsWith("L") && annoName.endsWith(";")) {
+            annoName = annoName.substring(1, annoName.length() - 1);
+        }
+
+        //Print annotation
+        // Mark whether it is invisible or not.
+        if (invisible) {
+            sb.append(invAnnotToken);
+        } else {
+            sb.append(visAnnotToken);
+        }
+
+        sb.append(annoName);
+        sb.append(" { ");
+
+        int i = 0;
+        for (AnnotationElement elem : array) {
+            sb.append(elem.toString());
+
+            if (i++ < array.size() - 1) {
+                sb.append(", ");
+            }
+        }
+
+        _toString(sb);
+
+        sb.append("}");
+        return sb.toString();
+    }
+
+    protected void _toString(StringBuilder sb) {
+        // sub-classes override this
+    }
+}
+
diff a/test/lib/org/openjdk/asmtools/jdis/AnnotationElement.java b/test/lib/org/openjdk/asmtools/jdis/AnnotationElement.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/AnnotationElement.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jdis;
+
+import static java.lang.String.format;
+import static org.openjdk.asmtools.jasm.Tables.*;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Base class of all AnnotationElement entries
+ */
+public class AnnotationElement {
+
+    /**
+     *
+     * CPX_AnnotElem
+     *
+     * base class for an annotation value.
+     *
+     */
+    public static class AnnotValue {
+
+        /**
+         * tag the descriptor for the constant
+         */
+        public AnnotElemType tag;
+
+        // internal references
+        protected ClassData cls;
+
+        public AnnotValue(AnnotElemType tagval, ClassData cls) {
+            tag = tagval;
+            this.cls = cls;
+        }
+
+        public String stringVal() {
+            return "";
+        }
+
+        public void print(PrintWriter out, String tab) {
+            out.print(tag.val() + "  ");
+        }
+
+        @Override
+        public String toString() {
+            return "<AnnotValue " + tag.printval() + " " + stringVal() + ">";
+        }
+    }
+
+    /**
+     *
+     * CPX_AnnotElem
+     *
+     * Annotation value which is described by a single CPX entry (ie. String, byte, char,
+     * int, short, boolean, float, long, double, class reference).
+     *
+     */
+    public static class CPX_AnnotValue extends AnnotValue {
+
+        /**
+         * tag the descriptor for the constant
+         */
+        public int cpx;
+
+        public CPX_AnnotValue(AnnotElemType tag, ClassData cls, int cpx) {
+            super(tag, cls);
+            this.cpx = cpx;
+        }
+
+        @Override
+        public String stringVal() {
+            StringBuilder sb = new StringBuilder();
+            switch (tag) {
+                case AE_STRING:    // String
+                    sb.append('"' + cls.pool.getString(cpx) + '"');
+                    break;
+                case AE_BYTE:    // Byte
+                    sb.append("byte " + cls.pool.getConst(cpx).stringVal());
+                    break;
+                case AE_CHAR:    // Char
+                    sb.append("char " + cls.pool.getConst(cpx).stringVal());
+                    break;
+                case AE_INT:    // Int  (no need to add keyword)
+                    sb.append(cls.pool.getConst(cpx).stringVal());
+                    break;
+                case AE_SHORT:    // Short
+                    sb.append("short " + cls.pool.getConst(cpx).stringVal());
+                    break;
+                case AE_BOOLEAN:    // Boolean
+                    ConstantPool.CP_Int cns = (ConstantPool.CP_Int) cls.pool.getConst(cpx);
+                    sb.append("boolean " + (cns.value == 0 ? "false" : "true"));
+                    break;
+                case AE_FLOAT:    // Float
+                    sb.append(cls.pool.getConst(cpx).stringVal()); // + "f");
+                    break;
+                case AE_DOUBLE:    // Double
+                    sb.append(cls.pool.getConst(cpx).stringVal()); // + "d");
+                    break;
+                case AE_LONG:    // Long
+                    sb.append(cls.pool.getConst(cpx).stringVal()); // + "l");
+                    break;
+                case AE_CLASS:    // Class
+                    sb.append("class " + cls.pool.decodeClassDescriptor(cpx));
+                    break;
+                default:
+                    break;
+            }
+            return sb.toString();
+        }
+
+        @Override
+        public void print(PrintWriter out, String tab) {
+            out.print(tab + stringVal());
+        }
+
+        @Override
+        public String toString() {
+            return "<CPX_AnnotValue tag: '" + tag + "' stringVal=" + this.stringVal() + ">";
+        }
+    }
+
+    /**
+     *
+     * CPX_AnnotElem
+     *
+     * AnnotElements that contain 2 cpx indices (ie. enums).
+     *
+     */
+    public static class CPX2_AnnotValue extends AnnotValue {
+
+        /**
+         * tag the descriptor for the constant
+         */
+        public int cpx1;
+        public int cpx2;
+
+        public CPX2_AnnotValue(AnnotElemType tag, ClassData cls, int cpx1, int cpx2) {
+            super(tag, cls);
+            this.cpx1 = cpx1;
+            this.cpx2 = cpx2;
+        }
+
+        @Override
+        public String stringVal() {
+            StringBuilder sb = new StringBuilder();
+            switch (tag) {
+
+                case AE_ENUM:    // Enum
+                    // print the enum type and constant name
+                    sb.append("enum " + cls.pool.decodeClassDescriptor(cpx1)
+                            + " " + cls.pool.getName(cpx2));
+                    break;
+                default:
+                    break;
+            }
+            return sb.toString();
+        }
+
+        @Override
+        public void print(PrintWriter out, String tab) {
+            out.print(tab + stringVal());
+        }
+
+        @Override
+        public String toString() {
+            return "<CPX2_AnnotValue tag: '" + tag + "' stringVal=" + this.stringVal() + ">";
+        }
+    }
+
+    /**
+     *
+     * Array_AnnotElem
+     *
+     * Annotation value that is an array of annotation elements.
+     *
+     */
+    public static class Array_AnnotValue extends AnnotValue {
+
+        /**
+         * tag the descriptor for the constant
+         */
+        public ArrayList<AnnotValue> array = new ArrayList<>();
+
+        public Array_AnnotValue(AnnotElemType tagval, ClassData cls) {
+            super(tagval, cls);
+        }
+
+        @Override
+        public String stringVal() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(super.stringVal() + " = ");
+            sb.append("{");
+            int i = 0;
+            int cnt = array.size();
+            for (AnnotValue arrayelem : array) {
+                sb.append(arrayelem.toString());
+                if (i < cnt - 1) {
+                    sb.append(",");
+                }
+            }
+            sb.append("}");
+            return sb.toString();
+        }
+
+        public void add(AnnotValue elem) {
+            array.add(elem);
+        }
+
+        @Override
+        public void print(PrintWriter out, String tab) {
+            out.println("{");
+            int i = 0;
+            int cnt = array.size();
+            for (AnnotValue arrayelem : array) {
+                arrayelem.print(out, tab + "  ");
+                if (i < cnt - 1) {
+                    out.println(",");
+                }
+                i += 1;
+            }
+            out.println("}");
+        }
+
+        @Override
+        public String toString() {
+            return "<Array_AnnotValue " + tag + " " + stringVal() + ">";
+        }
+    }
+
+    /**
+     *
+     * Annot_AnnotValue
+     *
+     * Annotation value that is a reference to an annotation.
+     *
+     */
+    public static class Annot_AnnotValue extends AnnotValue {
+
+        /**
+         * tag the descriptor for the constant
+         */
+        AnnotationData annot;
+
+        public Annot_AnnotValue(AnnotElemType tagval, ClassData cls, AnnotationData annot) {
+            super(tagval, cls);
+            this.annot = annot;
+        }
+
+        @Override
+        public String stringVal() {
+            return annot.toString();
+        }
+
+        @Override
+        public void print(PrintWriter out, String tab) {
+//            out.print(tag + "\t");
+            annot.print(out, tab);
+        }
+
+        @Override
+        public String toString() {
+            return "<Annot_AnnotValue " + tag + " " + stringVal() + ">";
+        }
+    }
+
+    /*========================================================*/
+    /* Factory Method */
+    /**
+     *
+     * read
+     *
+     * Static factory - creates Annotation Elements.
+     *
+     */
+    public static AnnotValue readValue(DataInputStream in, ClassData cls, boolean invisible) throws IOException {
+        AnnotValue val = null;
+        char tg = (char) in.readByte();
+        AnnotElemType tag = annotElemType(tg);
+
+        switch (tag) {
+            case AE_STRING:     // String
+            case AE_BYTE:       // Byte
+            case AE_CHAR:       // Char
+            case AE_INT:        // Int  (no need to add keyword)
+            case AE_SHORT:      // Short
+            case AE_BOOLEAN:    // Boolean
+            case AE_FLOAT:      // Float
+            case AE_DOUBLE:     // Double
+            case AE_LONG:       // Long
+            case AE_CLASS:      // Class
+                // CPX based Annotation
+                int CPX = in.readShort();
+                val = new CPX_AnnotValue(tag, cls, CPX);
+                break;
+            case AE_ENUM:    // Enum
+                // CPX2 based Annotation
+                int CPX1 = in.readShort();
+                int CPX2 = in.readShort();
+                val = new CPX2_AnnotValue(tag, cls, CPX1, CPX2);
+                break;
+            case AE_ANNOTATION:    // Annotation
+                AnnotationData ad = new AnnotationData(invisible, cls);
+                ad.read(in);
+                val = new Annot_AnnotValue(tag, cls, ad);
+                break;
+            case AE_ARRAY:    // Array
+                Array_AnnotValue aelem = new Array_AnnotValue(tag, cls);
+                val = aelem;
+                int cnt = in.readShort();
+                for (int i = 0; i < cnt; i++) {
+                    aelem.add(readValue(in, cls, invisible));
+                }
+                break;
+            default:
+                throw new IOException("Unknown tag in annotation '" + tg + "' [" + Integer.toHexString(tg) + "]");
+        }
+
+        return val;
+    }
+
+    /*========================================================*/
+
+    /*-------------------------------------------------------- */
+    /* AnnotElem Fields */
+    /**
+     * constant pool index for the name of the Annotation Element
+     */
+    public int name_cpx;
+
+    public AnnotValue value = null;
+
+    // internal references
+    protected ClassData cls;
+    /*-------------------------------------------------------- */
+
+    public AnnotationElement(ClassData cls) {
+        this.cls = cls;
+    }
+
+    /**
+     *
+     * read
+     *
+     * read and resolve the method data called from ClassData. precondition: NumFields has
+     * already been read from the stream.
+     *
+     */
+    public void read(DataInputStream in, boolean invisible) throws IOException {
+        name_cpx = in.readShort();
+        value = readValue(in, cls, invisible);
+        TraceUtils.traceln(format("                   AnnotElem: name[%d]=%s value=%s", name_cpx, cls.pool.getString(name_cpx), value.toString()));
+    }
+
+    public String stringVal() {
+        return cls.pool.getName(name_cpx);
+    }
+
+    public void print(PrintWriter out, String tab) {
+        out.print(stringVal() + " = ");
+        value.print(out, "");
+    }
+
+    @Override
+    public String toString() {
+        return "<AnnotElem " + stringVal() + " = " + value.toString() + ">";
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jdis/AttrData.java b/test/lib/org/openjdk/asmtools/jdis/AttrData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/AttrData.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jdis;
+
+import org.openjdk.asmtools.jasm.Tables;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+import static java.lang.String.format;
+
+/**
+ *
+ */
+public class AttrData {
+
+    int name_cpx;
+    byte data[];
+    ClassData cls;
+    public AttrData(ClassData cls) {
+        this.cls = cls;
+    }
+
+    /**
+     * attributeTag
+     * <p>
+     * returns either -1 (not found), or the hashed integer tag tag.
+     */
+    public static int attributeTag(String tagname) {
+        int intgr = Tables.attrtagValue(tagname);
+
+        if (intgr == 0) {
+            return -1;
+        }
+
+        return intgr;
+    }
+
+    public void read(int name_cpx, int attrlen, DataInputStream in) throws IOException {
+        this.name_cpx = name_cpx;
+        data = new byte[attrlen];
+        TraceUtils.traceln(format("AttrData:#%d len=%d", name_cpx, attrlen));
+        in.readFully(data);
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jdis/BootstrapMethodData.java b/test/lib/org/openjdk/asmtools/jdis/BootstrapMethodData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/BootstrapMethodData.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jdis;
+
+import org.openjdk.asmtools.jasm.JasmTokens;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ *
+ */
+public class BootstrapMethodData extends Indenter {
+
+    int bsm_index;
+    ArrayList<Integer> bsm_args_indexes;
+
+    // internal references
+    private Options options = Options.OptionObject();
+    private ClassData cls;
+    private PrintWriter out;
+
+    public BootstrapMethodData(ClassData cls) {
+        this.cls = cls;
+        out = cls.out;
+    }
+
+
+    /*========================================================*/
+    /* Read Methods */
+
+    /**
+     *
+     * read
+     *
+     * read and resolve the bootstrap method data called from ClassData. precondition:
+     * NumFields has already been read from the stream.
+     *
+     */
+    public void read(DataInputStream in) throws IOException {
+        // read the Methods CP indexes
+        bsm_index = in.readUnsignedShort();
+        int arg_num = in.readUnsignedShort();
+        bsm_args_indexes = new ArrayList<>(arg_num);
+        for (int i = 0; i < arg_num; i++) {
+            bsm_args_indexes.add(in.readUnsignedShort());
+        }
+    }
+
+
+    /*========================================================*/
+    /* Print Methods */
+    public void print() throws IOException {
+        out.print(getIndentString() + JasmTokens.Token.BOOTSTRAPMETHOD.parseKey() + " #" + bsm_index);
+        for (int i = 0; i < bsm_args_indexes.size(); i++) {
+            out.print(" #" + bsm_args_indexes.get(i));
+        }
+        out.println(";");
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jdis/ClassArrayData.java b/test/lib/org/openjdk/asmtools/jdis/ClassArrayData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/ClassArrayData.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2018, 2020, 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 org.openjdk.asmtools.jdis;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * Base class of the "classes[]" data of attributes
+ * <p>
+ * JEP 181 (Nest-based Access Control): class file 55.0
+ * NestMembers_attribute {
+ * u2 attribute_name_index;
+ * u4 attribute_length;
+ * u2 number_of_classes;
+ * u2 classes[number_of_classes];
+ * }
+ * <p>
+ * JEP 360 (Sealed types): class file 59.65535
+ * PermittedSubclasses_attribute {
+ * u2 attribute_name_index;
+ * u4 attribute_length;
+ * u2 number_of_classes;
+ * u2 classes[number_of_classes];
+ * }
+ * </p>
+ */
+public class ClassArrayData extends Indenter {
+    String name;
+    ClassData cls;
+    int[] classes;
+    private Options options = Options.OptionObject();
+
+    protected ClassArrayData(ClassData cls, String attrName) {
+        this.cls = cls;
+        this.name = attrName;
+    }
+
+    public ClassArrayData read(DataInputStream in, int attribute_length) throws IOException, ClassFormatError {
+        int number_of_classes = in.readUnsignedShort();
+        if (attribute_length != 2 + number_of_classes * 2) {
+            throw new ClassFormatError(name + "_attribute: Invalid attribute length");
+        }
+        classes = new int[number_of_classes];
+        for (int i = 0; i < number_of_classes; i++) {
+            classes[i] = in.readUnsignedShort();
+        }
+        return this;
+    }
+
+    public void print() {
+        String indexes = "";
+        String names = "";
+        boolean pr_cpx = options.contains(Options.PR.CPX);
+        cls.out.print(getIndentString() + name + " ");
+        for (int i = 0; i < classes.length; i++) {
+            if (pr_cpx) {
+                indexes += (indexes.isEmpty() ? "" : ", ") + "#" + classes[i];
+            }
+            names += (names.isEmpty() ? "" : ", ") + cls.pool.StringValue(classes[i]);
+        }
+        if (pr_cpx) {
+            cls.out.print(indexes + "; // ");
+        }
+        cls.out.print(names);
+        if (pr_cpx) {
+            cls.out.println();
+        } else {
+            cls.out.println(";");
+        }
+    }
+
+}
diff a/test/lib/org/openjdk/asmtools/jdis/ClassData.java b/test/lib/org/openjdk/asmtools/jdis/ClassData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/ClassData.java
@@ -0,0 +1,539 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jdis;
+
+import org.openjdk.asmtools.asmutils.HexUtils;
+import org.openjdk.asmtools.common.Tool;
+import org.openjdk.asmtools.jasm.Modifiers;
+
+import java.io.*;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static java.lang.String.format;
+import static org.openjdk.asmtools.jasm.RuntimeConstants.*;
+import static org.openjdk.asmtools.jasm.Tables.*;
+
+/**
+ * Central class data for of the Java Disassembler
+ */
+public class ClassData extends MemberData {
+
+    // Owner of this ClassData
+    protected Tool tool;
+
+    // -----------------------------
+    // Header Info
+    // -----------------------------
+    // Version info
+    protected int minor_version, major_version;
+
+    // Constant Pool index to this class
+    protected int this_cpx;
+
+    // Constant Pool index to this classes parent (super)
+    protected int super_cpx;
+
+    // Constant Pool index to a file reference to the Java source
+    protected int source_cpx = 0;
+
+    // -----------------------------
+    // The Constant Pool
+    // -----------------------------
+    protected ConstantPool pool;
+
+    // -----------------------------
+    // Interfaces,Fields,Methods && Attributes
+    // -----------------------------
+    // The interfaces this class implements
+    protected int[] interfaces;
+
+    // The fields of this class
+    protected ArrayList<FieldData> fields;
+
+    // The methods of this class
+    protected ArrayList<MethodData> methods;
+
+    // The record attribute of this class (since class file 58.65535)
+    protected RecordData record;
+
+    // The inner-classes of this class
+    protected ArrayList<InnerClassData> innerClasses;
+
+    // The bootstrapmethods this class implements
+    protected ArrayList<BootstrapMethodData> bootstrapMethods;
+
+    //The module this class file presents
+    protected ModuleData moduleData;
+
+    // The NestHost of this class (since class file: 55.0)
+    protected NestHostData nestHost;
+
+    // The NestMembers of this class (since class file: 55.0)
+    protected NestMembersData nestMembers;
+
+    // The PermittedSubclasses of this class (JEP 360 (Sealed types): class file 59.65535)
+    protected PermittedSubclassesData permittedSubclassesData;
+
+    protected PreloadData preloadData;
+
+    // other parsing fields
+    protected PrintWriter out;
+    protected String pkgPrefix = "";
+    // source file data
+    private TextLines sourceLines = null;
+    private Path classFile = null;
+
+    public ClassData(PrintWriter out, Tool tool) {
+        this.out  = out;
+        this.tool = tool;
+        memberType = "ClassData";
+        TraceUtils.traceln("printOptions=" + options.toString());
+        pool = new ConstantPool(this);
+        init(this);
+    }
+
+    public void read(File in) throws IOException {
+        try ( DataInputStream dis = new DataInputStream(new FileInputStream(in))){
+            read(dis);
+        }
+        classFile = in.toPath();
+    }
+
+    public void read(String in) throws IOException {
+        try ( DataInputStream dis = new DataInputStream(new FileInputStream(in))){
+            read(dis);
+        }
+        classFile = Paths.get(in);
+    }
+
+    /**
+     * Read and resolve the field data
+     */
+    protected void readFields(DataInputStream in) throws IOException {
+        int nfields = in.readUnsignedShort();
+        TraceUtils.traceln("nfields=" + nfields);
+        fields = new ArrayList<>(nfields);
+        for (int k = 0; k < nfields; k++) {
+            FieldData field = new FieldData(this);
+            TraceUtils.traceln("  FieldData: #" + k);
+            field.read(in);
+            fields.add(field);
+        }
+    }
+
+    /**
+     * Read and resolve the method data
+     */
+    protected void readMethods(DataInputStream in) throws IOException {
+        int nmethods = in.readUnsignedShort();
+        TraceUtils.traceln("nmethods=" + nmethods);
+        methods = new ArrayList<>(nmethods);
+        for (int k = 0; k < nmethods; k++) {
+            MethodData method = new MethodData(this);
+            TraceUtils.traceln("  MethodData: #" + k);
+            method.read(in);
+            methods.add(method);
+        }
+    }
+
+    /**
+     * Read and resolve the interface data
+     */
+    protected void readInterfaces(DataInputStream in) throws IOException {
+        // Read the interface names
+        int numinterfaces = in.readUnsignedShort();
+        TraceUtils.traceln("numinterfaces=" + numinterfaces);
+        interfaces = new int[numinterfaces];
+        for (int i = 0; i < numinterfaces; i++) {
+            int intrf_cpx = in.readShort();
+            TraceUtils.traceln("  intrf_cpx[" + i + "]=" + intrf_cpx);
+            interfaces[i] = intrf_cpx;
+        }
+    }
+
+    /**
+     * Read and resolve the attribute data
+     */
+    @Override
+    protected boolean handleAttributes(DataInputStream in, AttrTag attrtag, int attrlen) throws IOException {
+        // Read the Attributes
+        boolean handled = true;
+        switch (attrtag) {
+            case ATT_SourceFile:
+                // Read SourceFile Attr
+                if (attrlen != 2) {
+                    throw new ClassFormatError("ATT_SourceFile: Invalid attribute length");
+                }
+                source_cpx = in.readUnsignedShort();
+                break;
+            case ATT_InnerClasses:
+                // Read InnerClasses Attr
+                int num1 = in.readUnsignedShort();
+                if (2 + num1 * 8 != attrlen) {
+                    throw new ClassFormatError("ATT_InnerClasses: Invalid attribute length");
+                }
+                innerClasses = new ArrayList<>(num1);
+                for (int j = 0; j < num1; j++) {
+                    InnerClassData innerClass = new InnerClassData(this);
+                    innerClass.read(in);
+                    innerClasses.add(innerClass);
+                }
+                break;
+            case ATT_BootstrapMethods:
+                // Read BootstrapMethods Attr
+                int num2 = in.readUnsignedShort();
+                bootstrapMethods = new ArrayList<>(num2);
+                for (int j = 0; j < num2; j++) {
+                    BootstrapMethodData bsmData = new BootstrapMethodData(this);
+                    bsmData.read(in);
+                    bootstrapMethods.add(bsmData);
+                }
+                break;
+            case ATT_Module:
+                // Read Module Attribute
+                moduleData = new ModuleData(this);
+                moduleData.read(in);
+                break;
+            case ATT_NestHost:
+                // Read NestHost Attribute (since class file: 55.0)
+                nestHost = new NestHostData(this).read(in, attrlen);
+                break;
+            case ATT_NestMembers:
+                // Read NestMembers Attribute (since class file: 55.0)
+                nestMembers = new NestMembersData(this).read(in, attrlen);
+                break;
+            case ATT_Record:
+                record = new RecordData(this).read(in);
+                break;
+            case ATT_PermittedSubclasses:
+                // Read PermittedSubclasses Attribute (JEP 360 (Sealed types): class file 59.65535)
+                permittedSubclassesData = new PermittedSubclassesData(this).read(in, attrlen);
+                break;
+            case ATT_Preload:
+                preloadData = new PreloadData(this).read(in, attrlen);
+                break;
+            default:
+                handled = false;
+                break;
+        }
+        return handled;
+    }
+
+    /**
+     * Read and resolve the class data
+     */
+    private void read(DataInputStream in) throws IOException {
+        // Read the header
+        int magic = in.readInt();
+        if (magic != JAVA_MAGIC) {
+            throw new ClassFormatError("wrong magic: " + HexUtils.toHex(magic) + ", expected " + HexUtils.toHex(JAVA_MAGIC));
+        }
+        minor_version = in.readUnsignedShort();
+        major_version = in.readUnsignedShort();
+
+        // Read the constant pool
+        pool.read(in);
+        access = in.readUnsignedShort(); // & MM_CLASS; // Q
+        this_cpx = in.readUnsignedShort();
+        super_cpx = in.readUnsignedShort();
+        TraceUtils.traceln("access=" + access + " " + Modifiers.accessString(access, CF_Context.CTX_INNERCLASS) +
+                " this_cpx=" + this_cpx +
+                " super_cpx=" + super_cpx);
+
+        // Read the interfaces
+        readInterfaces(in);
+
+        // Read the fields
+        readFields(in);
+
+        // Read the methods
+        readMethods(in);
+
+        // Read the attributes
+        readAttributes(in);
+        //
+        TraceUtils.traceln("", "<< Reading is done >>", "");
+    }
+
+    /**
+     * Read and resolve the attribute data
+     */
+    public String getSrcLine(int lnum) {
+        if (sourceLines == null) {
+            return null;  // impossible call
+        }
+        String line;
+        try {
+            line = sourceLines.getLine(lnum);
+        } catch (ArrayIndexOutOfBoundsException e) {
+            line = "Line number " + lnum + " is out of bounds";
+        }
+        return line;
+    }
+
+    private <T extends AnnotationData> void printAnnotations(List<T> annotations) {
+        if (annotations != null) {
+            for (T ad : annotations) {
+                ad.print(out, "");
+                out.println();
+            }
+        }
+    }
+
+    @Override
+    public void print() throws IOException {
+        int k, l;
+        String className = "";
+        String sourceName = null;
+        if( isModuleUnit() ) {
+            // Print the Annotations
+            printAnnotations(visibleAnnotations);
+            printAnnotations(invisibleAnnotations);
+        } else {
+            className = pool.getClassName(this_cpx);
+            int pkgPrefixLen = className.lastIndexOf("/") + 1;
+            // Write the header
+            // package-info compilation unit
+            if (className.endsWith("package-info")) {
+                // Print the Annotations
+                printAnnotations(visibleAnnotations);
+                printAnnotations(invisibleAnnotations);
+                printAnnotations(visibleTypeAnnotations);
+                printAnnotations(invisibleTypeAnnotations);
+                if (pkgPrefixLen != 0) {
+                    pkgPrefix = className.substring(0, pkgPrefixLen);
+                    out.print("package  " + pkgPrefix.substring(0, pkgPrefixLen - 1) + " ");
+                    out.print("version " + major_version + ":" + minor_version + ";");
+                }
+                out.println();
+                return;
+            }
+            if (pkgPrefixLen != 0) {
+                pkgPrefix = className.substring(0, pkgPrefixLen);
+                out.println("package  " + pkgPrefix.substring(0, pkgPrefixLen - 1) + ";");
+                className = pool.getShortClassName(this_cpx, pkgPrefix);
+            }
+            out.println();
+            // Print the Annotations
+            printAnnotations(visibleAnnotations);
+            printAnnotations(invisibleAnnotations);
+            printAnnotations(visibleTypeAnnotations);
+            printAnnotations(invisibleTypeAnnotations);
+            if ((access & ACC_SUPER) != 0) {
+                out.print("super ");
+                access = access & ~ACC_SUPER;
+            }
+        }
+// see if we are going to print: abstract interface class
+// then replace it with just: interface
+printHeader:
+        {
+printSugar:
+            {
+                if ((access & ACC_ABSTRACT) == 0) {
+                    break printSugar;
+                }
+                if ((access & ACC_INTERFACE) == 0) {
+                    break printSugar;
+                }
+                if (options.contains(Options.PR.CPX)) {
+                    break printSugar;
+                }
+                if (this_cpx == 0) {
+                    break printSugar;
+                }
+
+                // make sure the this_class is a valid class ref
+                ConstantPool.Constant this_const = pool.getConst(this_cpx);
+                if (this_const == null || this_const.tag != ConstantPool.TAG.CONSTANT_CLASS) {
+                    break printSugar;
+                }
+
+                // all conditions met, print syntactic sugar:
+                out.print(Modifiers.accessString(access & ~ACC_ABSTRACT, CF_Context.CTX_CLASS));
+                if (isSynthetic) {
+                    out.print("synthetic ");
+                }
+                if (isDeprecated) {
+                    out.print("deprecated ");
+                }
+                out.print(" " + pool.getShortClassName(this_cpx, pkgPrefix));
+                break printHeader;
+            }
+
+            if(isModuleUnit()) {
+                out.print(moduleData.getModuleHeader());
+            } else {
+                // not all conditions met, print header in ordinary way:
+                out.print(Modifiers.accessString(access, CF_Context.CTX_CLASS));
+                if (isSynthetic) {
+                    out.print("synthetic ");
+                }
+                if (isDeprecated) {
+                    out.print("deprecated ");
+                }
+                if (options.contains(Options.PR.CPX)) {
+                    out.print("\t#" + this_cpx + " //");
+                }
+                pool.PrintConstant(out, this_cpx);
+            }
+        }
+        out.println();
+        if(!isModuleUnit()) {
+            if (!pool.getClassName(super_cpx).equals("java/lang/Object")) {
+                out.print("\textends ");
+                pool.printlnClassId(out, super_cpx);
+                out.println();
+            }
+        }
+        l = interfaces.length;
+
+        if (l > 0) {
+            for (k = 0; k < l; k++) {
+                if (k == 0) {
+                    out.print("\timplements ");
+                } else {
+                    out.print("\t\t ");
+                }
+                boolean printComma = (l > 1 && k < (l - 1));
+                pool.printlnClassId(out, interfaces[k], printComma);
+                out.println();
+            }
+        }
+        out.println("\tversion " + major_version + ":" + minor_version);
+        out.println("{");
+
+        if ((options.contains(Options.PR.SRC)) && (source_cpx != 0)) {
+            sourceName = pool.getString(source_cpx);
+            if (sourceName != null) {
+                sourceLines = new TextLines(classFile.getParent(), sourceName);
+            }
+        }
+
+        // Print the constant pool
+        if (options.contains(Options.PR.CP)) {
+            pool.print(out);
+        }
+        // Don't print fields, methods, inner classes and bootstrap methods if it is module-info entity
+        if ( !isModuleUnit() ) {
+
+            // Print the fields
+            printMemberDataList(fields);
+
+            // Print the methods
+            printMemberDataList(methods);
+
+            // Print the Record (since class file 58.65535 JEP 359)
+            if( record != null ) {
+                record.print();
+            }
+
+            // Print PermittedSubclasses Attribute (JEP 360 (Sealed types): class file 59.65535)
+            if( permittedSubclassesData != null) {
+                permittedSubclassesData.print();
+            }
+            // Print the NestHost (since class file: 55.0)
+            if(nestHost != null) {
+                nestHost.print();
+            }
+            // Print the NestMembers (since class file: 55.0)
+            if( nestMembers  != null) {
+                nestMembers.print();
+            }
+            // Print the inner classes
+            if (innerClasses != null && !innerClasses.isEmpty()) {
+                for (InnerClassData icd : innerClasses) {
+                    icd.print();
+                }
+                out.println();
+            }
+
+            // Print Preload attribute
+            if (preloadData != null) {
+                preloadData.print();
+            }
+
+            // Print the BootstrapMethods
+            //
+            // Only print these if printing extended constants
+            if ((options.contains(Options.PR.CPX)) && bootstrapMethods != null && !bootstrapMethods.isEmpty()) {
+                for (BootstrapMethodData bsmdd : bootstrapMethods) {
+                    bsmdd.print();
+                }
+                out.println();
+            }
+            out.println(format("} // end Class %s%s",
+                    className,
+                    sourceName != null ? " compiled from \"" + sourceName +"\"" : ""));
+        } else {
+            // Print module attributes
+            moduleData.print();
+            out.print("} // end Module ");
+            out.print( moduleData.getModuleName());
+            if(moduleData.getModuleVersion() != null)
+                out.print(" @" + moduleData.getModuleVersion());
+            out.println();
+        }
+
+        List<IOException> issues = getIssues();
+        if( !issues.isEmpty() ) {
+
+            throw issues.get(0);
+        }
+    } // end ClassData.print()
+
+    // Gets the type of processed binary
+    private boolean isModuleUnit() {
+        return moduleData != null;
+    }
+
+    private void printMemberDataList( List<? extends MemberData> list) throws IOException {
+        if( list != null ) {
+            int count = list.size();
+            if( count > 0 ) {
+                for( int i=0; i < count; i++ ) {
+                    MemberData md = list.get(i);
+                    md.setIndent(Options.BODY_INDENT);
+                    if( i !=0 && md.getAnnotationsCount() > 0 )
+                        out.println();
+                    md.print();
+                }
+                out.println();
+            }
+        }
+    }
+
+    private List<IOException> getIssues() {
+        return this.pool.pool.stream().
+                filter(Objects::nonNull).
+                filter(c->c.getIssue() != null).
+                map(ConstantPool.Constant::getIssue).
+                collect(Collectors.toList());
+    }
+
+}// end class ClassData
diff a/test/lib/org/openjdk/asmtools/jdis/CodeData.java b/test/lib/org/openjdk/asmtools/jdis/CodeData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/CodeData.java
@@ -0,0 +1,745 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jdis;
+
+import org.openjdk.asmtools.jasm.Tables;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import static org.openjdk.asmtools.jasm.OpcodeTables.Opcode;
+import static org.openjdk.asmtools.jasm.OpcodeTables.opcode;
+import static org.openjdk.asmtools.jasm.Tables.*;
+import static org.openjdk.asmtools.jasm.Tables.AttrTag.ATT_RuntimeInvisibleTypeAnnotations;
+import static org.openjdk.asmtools.jasm.Tables.AttrTag.ATT_RuntimeVisibleTypeAnnotations;
+import static org.openjdk.asmtools.jdis.Utils.commentString;
+
+/**
+ * Code data for a code attribute in method members in a class of the Java Disassembler
+ */
+public class CodeData extends Indenter {
+
+    /**
+     * Raw byte array for the byte codes
+     */
+    protected byte[] code;
+    /**
+     * Limit for the stack size
+     */
+    protected int max_stack;
+
+    /* CodeData Fields */
+    /**
+     * Limit for the number of local vars
+     */
+    protected int max_locals;
+    /**
+     * The remaining attributes of this class
+     */
+    protected ArrayList<AttrData> attrs = new ArrayList<>(0);        // AttrData
+
+    // internal references
+    protected ClassData cls;
+    protected MethodData meth;
+    /**
+     * (parsed) Trap table, describes exceptions caught
+     */
+    private ArrayList<TrapData> trap_table = new ArrayList<>(0);   // TrapData
+    /**
+     * (parsed) Line Number table, describes source lines associated with ByteCode indexes
+     */
+    private ArrayList<LineNumData> lin_num_tb = new ArrayList<>(0);   // LineNumData
+    /**
+     * (parsed) Local Variable table, describes variable scopes associated with ByteCode
+     * indexes
+     */
+    private ArrayList<LocVarData> loc_var_tb = new ArrayList<>(0);   // LocVarData
+    /**
+     * (parsed) stack map table, describes compiler hints for stack rep, associated with
+     * ByteCode indexes
+     */
+    private ArrayList<StackMapData> stack_map = null;
+    /**
+     * The visible type annotations for this method
+     */
+    private ArrayList<TypeAnnotationData> visibleTypeAnnotations;
+    /**
+     * The invisible type annotations for this method
+     */
+    private ArrayList<TypeAnnotationData> invisibleTypeAnnotations;
+
+    /**
+     * (parsed) reversed bytecode index hash, associates labels with ByteCode indexes
+     */
+    private HashMap<Integer, iAtt> iattrs = new HashMap<>();
+    private PrintWriter out;
+    public CodeData(MethodData meth) {
+        this.meth = meth;
+        this.cls = meth.cls;
+        this.out = cls.out;
+    }
+
+    private static int align(int n) {
+        return (n + 3) & ~3;
+    }
+    /*-------------------------------------------------------- */
+
+    private int getbyte(int pc) {
+        return code[pc];
+    }
+
+    private int getUbyte(int pc) {
+        return code[pc] & 0xFF;
+    }
+
+    private int getShort(int pc) {
+        return (code[pc] << 8) | (code[pc + 1] & 0xFF);
+    }
+
+    private int getUShort(int pc) {
+        return ((code[pc] << 8) | (code[pc + 1] & 0xFF)) & 0xFFFF;
+    }
+
+    private int getInt(int pc) {
+        return (getShort(pc) << 16) | (getShort(pc + 2) & 0xFFFF);
+    }
+
+    protected iAtt get_iAtt(int pc) {
+        Integer PC = pc;
+        iAtt res = iattrs.get(PC);
+        if (res == null) {
+            res = new iAtt(this);
+            iattrs.put(PC, res);
+        }
+        return res;
+    }
+
+    /*========================================================*/
+    /* Read Methods */
+    private void readLineNumTable(DataInputStream in) throws IOException {
+        int len = in.readInt(); // attr_length
+        int numlines = in.readUnsignedShort();
+        lin_num_tb = new ArrayList<>(numlines);
+        TraceUtils.traceln(3,  "CodeAttr:  LineNumTable[" + numlines + "] len=" + len);
+        for (int l = 0; l < numlines; l++) {
+            lin_num_tb.add(new LineNumData(in));
+        }
+    }
+
+    private void readLocVarTable(DataInputStream in) throws IOException {
+        int len = in.readInt(); // attr_length
+        int numlines = in.readUnsignedShort();
+        loc_var_tb = new ArrayList<>(numlines);
+        TraceUtils.traceln(3,  "CodeAttr:  LocalVariableTable[" + numlines + "] len=" + len);
+        for (int l = 0; l < numlines; l++) {
+            loc_var_tb.add(new LocVarData(in));
+        }
+    }
+
+    private void readTrapTable(DataInputStream in) throws IOException {
+        int trap_table_len = in.readUnsignedShort();
+        TraceUtils.traceln(3,  "CodeAttr:  TrapTable[" + trap_table_len + "]");
+        trap_table = new ArrayList<>(trap_table_len);
+        for (int l = 0; l < trap_table_len; l++) {
+            trap_table.add(new TrapData(in, l));
+        }
+    }
+
+    private void readStackMap(DataInputStream in) throws IOException {
+        int len = in.readInt(); // attr_length
+        int stack_map_len = in.readUnsignedShort();
+        TraceUtils.traceln(3,  "CodeAttr:  Stack_Map: attrlen=" + len + " num=" + stack_map_len);
+        stack_map = new ArrayList<>(stack_map_len);
+        StackMapData.prevFramePC = 0;
+        for (int k = 0; k < stack_map_len; k++) {
+            stack_map.add(new StackMapData(this, in));
+        }
+    }
+
+    private void readStackMapTable(DataInputStream in) throws IOException {
+        int len = in.readInt(); // attr_length
+        int stack_map_len = in.readUnsignedShort();
+        TraceUtils.traceln(3,  "CodeAttr:  Stack_Map_Table: attrlen=" + len + " num=" + stack_map_len);
+        stack_map = new ArrayList<>(stack_map_len);
+        StackMapData.prevFramePC = 0;
+        for (int k = 0; k < stack_map_len; k++) {
+            stack_map.add(new StackMapData(this, in, true));
+        }
+    }
+
+    private void readTypeAnnotations(DataInputStream in, boolean isInvisible) throws IOException  {
+        int attrLength = in.readInt();
+        // Read Type Annotations Attr
+        int count = in.readShort();
+        ArrayList<TypeAnnotationData> tannots = new ArrayList<>(count);
+        TraceUtils.traceln(3,  "CodeAttr:   Runtime" +
+                (isInvisible ? "Inv" : "V") +
+                "isibleTypeAnnotation: attrlen=" +
+                attrLength + " num=" + count);
+        for (int index = 0; index < count; index++) {
+            TraceUtils.traceln("\t\t\t[" + index +"]:");
+            TypeAnnotationData tannot = new TypeAnnotationData(isInvisible, cls);
+            tannot.read(in);
+            tannots.add(tannot);
+        }
+        if (isInvisible) {
+            invisibleTypeAnnotations = tannots;
+        } else {
+            visibleTypeAnnotations = tannots;
+        }
+    }
+
+    /**
+     * read
+     * <p>
+     * read and resolve the code attribute data called from MethodData. precondition:
+     * NumFields has already been read from the stream.
+     */
+    public void read(DataInputStream in, int codeattrlen) throws IOException {
+
+        // Read the code in the Code Attribute
+        max_stack = in.readUnsignedShort();
+        max_locals = in.readUnsignedShort();
+        int codelen = in.readInt();
+        TraceUtils.traceln(3,  "CodeAttr:  Codelen=" + codelen +
+                " fulllen=" + codeattrlen +
+                " max_stack=" + max_stack +
+                " max_locals=" + max_locals);
+
+        // read the raw code bytes
+        code = new byte[codelen];
+        in.read(code, 0, codelen);
+
+        //read the trap table
+        readTrapTable(in);
+
+        // Read any attributes of the Code Attribute
+        int nattr = in.readUnsignedShort();
+        TraceUtils.traceln(3,  "CodeAttr: add.attr:" + nattr);
+        for (int k = 0; k < nattr; k++) {
+            int name_cpx = in.readUnsignedShort();
+            // verify the Attrs name
+            ConstantPool.Constant name_const = cls.pool.getConst(name_cpx);
+            if (name_const != null && name_const.tag == ConstantPool.TAG.CONSTANT_UTF8) {
+                String attrname = cls.pool.getString(name_cpx);
+                TraceUtils.traceln(3,  "CodeAttr:  attr: " + attrname);
+                // process the attr
+                AttrTag attrtag = attrtag(attrname);
+                switch (attrtag) {
+                    case ATT_LineNumberTable:
+                        readLineNumTable(in);
+                        break;
+                    case ATT_LocalVariableTable:
+                        readLocVarTable(in);
+                        break;
+                    case ATT_StackMap:
+                        readStackMap(in);
+                        break;
+                    case ATT_StackMapTable:
+                        readStackMapTable(in);
+                        break;
+                    case ATT_RuntimeVisibleTypeAnnotations:
+                    case ATT_RuntimeInvisibleTypeAnnotations:
+                        readTypeAnnotations(in, attrtag == ATT_RuntimeInvisibleTypeAnnotations);
+                        break;
+                    default:
+                        AttrData attr = new AttrData(cls);
+                        int attrlen = in.readInt(); // attr_length
+                        attr.read(name_cpx, attrlen, in);
+                        attrs.add(attr);
+                        break;
+                }
+            }
+        }
+    }
+
+    /*========================================================*/
+    /* Code Resolution Methods */
+    private int checkForLabelRef(int pc) {
+        //         throws IOException {
+        int opc = getUbyte(pc);
+        Opcode opcode = opcode(opc);
+        switch (opcode) {
+            case opc_tableswitch: {
+                int tb = align(pc + 1);
+                int default_skip = getInt(tb); /* default skip pamount */
+
+                int low = getInt(tb + 4);
+                int high = getInt(tb + 8);
+                int count = high - low;
+                for (int i = 0; i <= count; i++) {
+                    get_iAtt(pc + getInt(tb + 12 + 4 * i)).referred = true;
+                }
+                get_iAtt(default_skip + pc).referred = true;
+                return tb - pc + 16 + count * 4;
+            }
+            case opc_lookupswitch: {
+                int tb = align(pc + 1);
+                int default_skip = getInt(tb); /* default skip pamount */
+
+                int npairs = getInt(tb + 4);
+                for (int i = 1; i <= npairs; i++) {
+                    get_iAtt(pc + getInt(tb + 4 + i * 8)).referred = true;
+                }
+                get_iAtt(default_skip + pc).referred = true;
+                return tb - pc + (npairs + 1) * 8;
+            }
+            case opc_jsr:
+            case opc_goto:
+            case opc_ifeq:
+            case opc_ifge:
+            case opc_ifgt:
+            case opc_ifle:
+            case opc_iflt:
+            case opc_ifne:
+            case opc_if_icmpeq:
+            case opc_if_icmpne:
+            case opc_if_icmpge:
+            case opc_if_icmpgt:
+            case opc_if_icmple:
+            case opc_if_icmplt:
+            case opc_if_acmpeq:
+            case opc_if_acmpne:
+            case opc_ifnull:
+            case opc_ifnonnull:
+                get_iAtt(pc + getShort(pc + 1)).referred = true;
+                return 3;
+            case opc_jsr_w:
+            case opc_goto_w:
+                get_iAtt(pc + getInt(pc + 1)).referred = true;
+                return 5;
+            case opc_wide:
+            case opc_nonpriv:
+            case opc_priv:
+                int opc2 = (opcode.value() << 8) + getUbyte(pc + 1);
+                opcode = opcode(opc2);
+        }
+        try {
+            int opclen = opcode.length();
+            return opclen == 0 ? 1 : opclen;  // bugfix for 4614404
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return 1;
+        }
+    } // end checkForLabelRef
+
+    private void loadLabelTable() {
+        for (int pc = 0; pc < code.length; ) {
+            pc = pc + checkForLabelRef(pc);
+        }
+    }
+
+    private void loadLineNumTable() {
+        for (LineNumData entry : lin_num_tb) {
+            get_iAtt(entry.start_pc).lnum = entry.line_number;
+        }
+    }
+
+    private void loadStackMap() {
+        for (StackMapData entry : stack_map) {
+            get_iAtt(entry.start_pc).stackMapEntry = entry;
+        }
+    }
+
+    private void loadLocVarTable() {
+        for (LocVarData entry : loc_var_tb) {
+            get_iAtt(entry.start_pc).add_var(entry);
+            get_iAtt(entry.start_pc + entry.length).add_endvar(entry);
+        }
+    }
+
+    private void loadTrapTable() {
+        for (TrapData entry : trap_table) {
+            get_iAtt(entry.start_pc).add_trap(entry);
+            get_iAtt(entry.end_pc).add_endtrap(entry);
+            get_iAtt(entry.handler_pc).add_handler(entry);
+        }
+    }
+
+    /*========================================================*/
+    /* Print Methods */
+    private void PrintConstant(int cpx) {
+        out.print("\t");
+        cls.pool.PrintConstant(out, cpx);
+    }
+
+    private void PrintCommentedConstant(int cpx) {
+        out.print(commentString(cls.pool.ConstantStrValue(cpx)));
+    }
+
+    private int printInstr(int pc) {
+        boolean pr_cpx = meth.options.contains(Options.PR.CPX);
+        int opc = getUbyte(pc);
+        int opc2;
+        Opcode opcode = opcode(opc);
+        Opcode opcode2;
+        String mnem;
+        switch (opcode) {
+            case opc_nonpriv:
+            case opc_priv:
+                opc2 = getUbyte(pc + 1);
+                int finalopc = (opc << 8) + opc2;
+                opcode2 = opcode(finalopc);
+                if (opcode2 == null) {
+// assume all (even nonexistent) priv and nonpriv instructions
+// are 2 bytes long
+                    mnem = opcode.parsekey() + " " + opc2;
+                } else {
+                    mnem = opcode2.parsekey();
+                }
+                out.print(mnem);
+                return 2;
+            case opc_wide: {
+                opc2 = getUbyte(pc + 1);
+                int finalopcwide = (opc << 8) + opc2;
+                opcode2 = opcode(finalopcwide);
+                if (opcode2 == null) {
+// nonexistent opcode - but we have to print something
+                    out.print("bytecode " + opcode);
+                    return 1;
+                } else {
+                    mnem = opcode2.parsekey();
+                }
+                out.print(mnem + " " + getUShort(pc + 2));
+                if (opcode2 == Opcode.opc_iinc_w) {
+                    out.print(", " + getShort(pc + 4));
+                    return 6;
+                }
+                return 4;
+            }
+        }
+        mnem = opcode.parsekey();
+        if (mnem == null) {
+// nonexistent opcode - but we have to print something
+            out.print("bytecode " + opcode);
+            return 1;
+        }
+        if (opcode.value() >= Opcode.opc_bytecode.value()) {
+// pseudo opcodes should be printed as bytecodes
+            out.print("bytecode " + opcode);
+            return 1;
+        }
+        out.print(opcode.parsekey());
+// TraceUtils.traceln("****** [CodeData.printInstr]: got an '" + opcode.parseKey() + "' [" + opc + "] instruction ****** ");
+        switch (opcode) {
+            case opc_aload:
+            case opc_astore:
+            case opc_fload:
+            case opc_fstore:
+            case opc_iload:
+            case opc_istore:
+            case opc_lload:
+            case opc_lstore:
+            case opc_dload:
+            case opc_dstore:
+            case opc_ret:
+                out.print("\t" + getUbyte(pc + 1));
+                return 2;
+            case opc_iinc:
+                out.print("\t" + getUbyte(pc + 1) + ", " + getbyte(pc + 2));
+                return 3;
+            case opc_tableswitch: {
+                int tb = align(pc + 1);
+                int default_skip = getInt(tb); /* default skip pamount */
+
+                int low = getInt(tb + 4);
+                int high = getInt(tb + 8);
+                int count = high - low;
+                out.print("{ //" + low + " to " + high);
+                for (int i = 0; i <= count; i++) {
+                    out.print("\n\t\t" + (i + low) + ": " + meth.lP + (pc + getInt(tb + 12 + 4 * i)) + ";");
+                }
+                out.print("\n\t\tdefault: " + meth.lP + (default_skip + pc) + " }");
+                return tb - pc + 16 + count * 4;
+            }
+            case opc_lookupswitch: {
+                int tb = align(pc + 1);
+                int default_skip = getInt(tb);
+                int npairs = getInt(tb + 4);
+                out.print("{ //" + npairs);
+                for (int i = 1; i <= npairs; i++) {
+                    out.print("\n\t\t" + getInt(tb + i * 8) + ": " + meth.lP + (pc + getInt(tb + 4 + i * 8)) + ";");
+                }
+                out.print("\n\t\tdefault: " + meth.lP + (default_skip + pc) + " }");
+                return tb - pc + (npairs + 1) * 8;
+            }
+            case opc_newarray:
+                int tp = getUbyte(pc + 1);
+                BasicType type = basictype(tp);
+                switch (type) {
+                    case T_BOOLEAN:
+                        out.print(" boolean");
+                        break;
+                    case T_BYTE:
+                        out.print(" byte");
+                        break;
+                    case T_CHAR:
+                        out.print(" char");
+                        break;
+                    case T_SHORT:
+                        out.print(" short");
+                        break;
+                    case T_INT:
+                        out.print(" int");
+                        break;
+                    case T_LONG:
+                        out.print(" long");
+                        break;
+                    case T_FLOAT:
+                        out.print(" float");
+                        break;
+                    case T_DOUBLE:
+                        out.print(" double");
+                        break;
+                    case T_CLASS:
+                        out.print(" class");
+                        break;
+                    default:
+                        out.print(" BOGUS TYPE:" + type);
+                }
+                return 2;
+            case opc_ldc_w:
+            case opc_ldc2_w: {
+                // added printing of the tag: Method/Interface to clarify
+                // interpreting CONSTANT_MethodHandle_info:reference_kind
+                // Example: ldc_w Dynamic REF_invokeStatic:Method CondyIndy.condy_bsm
+                cls.pool.setPrintTAG(true);
+                int index = getUShort(pc + 1);
+                if (pr_cpx) {
+                    out.print("\t#" + index + "; //");
+                }
+                PrintConstant(index);
+                cls.pool.setPrintTAG(false);
+                return 3;
+            }
+            case opc_anewarray:
+            case opc_instanceof:
+            case opc_checkcast:
+            case opc_new:
+            case opc_aconst_init:
+            case opc_putstatic:
+            case opc_getstatic:
+            case opc_putfield:
+            case opc_getfield:
+            case opc_withfield:
+            case opc_invokevirtual:
+            case opc_invokespecial:
+            case opc_invokestatic: {
+                int index = getUShort(pc + 1);
+                if (pr_cpx) {
+                    out.print("\t#" + index + "; //");
+                }
+                PrintConstant(index);
+                return 3;
+            }
+            case opc_sipush:
+                out.print("\t" + getShort(pc + 1));
+                return 3;
+            case opc_bipush:
+                out.print("\t" + getbyte(pc + 1));
+                return 2;
+            case opc_ldc: {
+                // added printing of the tag: Method/Interface to clarify
+                // interpreting CONSTANT_MethodHandle_info:reference_kind
+                // Example: ldc Dynamic REF_invokeStatic:Method CondyIndy.condy_bsm
+                cls.pool.setPrintTAG(true);
+                int index = getUbyte(pc + 1);
+                if (pr_cpx) {
+                    out.print("\t#" + index + "; //");
+                }
+                PrintConstant(index);
+                cls.pool.setPrintTAG(false);
+                return 2;
+            }
+            case opc_invokeinterface: {
+                int index = getUShort(pc + 1), nargs = getUbyte(pc + 3);
+                if (pr_cpx) {
+                    out.print("\t#" + index + ",  " + nargs + "; //");
+                    PrintConstant(index);
+                } else {
+                    PrintConstant(index);
+                    out.print(",  " + nargs); // args count
+                }
+                return 5;
+            }
+            case opc_invokedynamic: { // JSR-292
+                cls.pool.setPrintTAG(true);
+                int index = getUShort(pc + 1);
+                // getUbyte(pc + 3); // reserved byte
+                // getUbyte(pc + 4); // reserved byte
+                if (pr_cpx) {
+                    out.print("\t#" + index + ";\t");
+                    PrintCommentedConstant(index);
+                } else {
+                    PrintConstant(index);
+                }
+                cls.pool.setPrintTAG(false);
+                return 5;
+            }
+            case opc_multianewarray: {
+                int index = getUShort(pc + 1), dimensions = getUbyte(pc + 3);
+                if (pr_cpx) {
+                    out.print("\t#" + index + ",  " + dimensions + "; //");
+                    PrintConstant(index);
+                } else {
+                    PrintConstant(index);
+                    out.print(",  " + dimensions); // dimensions count
+                }
+                return 4;
+            }
+            case opc_jsr:
+            case opc_goto:
+            case opc_ifeq:
+            case opc_ifge:
+            case opc_ifgt:
+            case opc_ifle:
+            case opc_iflt:
+            case opc_ifne:
+            case opc_if_icmpeq:
+            case opc_if_icmpne:
+            case opc_if_icmpge:
+            case opc_if_icmpgt:
+            case opc_if_icmple:
+            case opc_if_icmplt:
+            case opc_if_acmpeq:
+            case opc_if_acmpne:
+            case opc_ifnull:
+            case opc_ifnonnull:
+                out.print("\t" + meth.lP + (pc + getShort(pc + 1)));
+                return 3;
+            case opc_jsr_w:
+            case opc_goto_w:
+                out.print("\t" + meth.lP + (pc + getInt(pc + 1)));
+                return 5;
+            default:
+                return 1;
+        }
+    } // end printInstr
+
+    /**
+     * print
+     * <p>
+     * prints the code data to the current output stream. called from MethodData.
+     */
+    public void print() throws IOException {
+        if (!lin_num_tb.isEmpty()) {
+            loadLineNumTable();
+        }
+        if (stack_map != null) {
+            loadStackMap();
+        }
+        if (!meth.options.contains(Options.PR.PC)) {
+            loadLabelTable();
+        }
+        loadTrapTable();
+        if (!loc_var_tb.isEmpty()) {
+            loadLocVarTable();
+        }
+
+        out.println();
+        out.println("\tstack " + max_stack + " locals " + max_locals);
+
+        // Need to print ParamAnnotations here.
+        meth.printPAnnotations();
+
+        out.println(getIndentString() + "{");
+
+        iAtt iatt = iattrs.get(0);
+        for (int pc = 0; pc < code.length; ) {
+            if (iatt != null) {
+                iatt.printBegins(); // equ. print("\t");
+            } else {
+                out.print("\t");
+            }
+            if (meth.options.contains(Options.PR.PC)) {
+                out.print(pc + ":\t");
+            } else if ((iatt != null) && iatt.referred) {
+                out.print(meth.lP + pc + ":\t");
+            } else {
+                out.print("\t");
+            }
+            if (iatt != null) {
+                iatt.printStackMap();
+            }
+            pc = pc + printInstr(pc);
+            out.println(";");
+            iatt = iattrs.get(pc);
+            if (iatt != null) {
+                iatt.printEnds();
+            }
+        }
+        // the right brace can be labelled:
+        if (iatt != null) {
+            iatt.printBegins(); // equ. print("\t");
+            if (iatt.referred) {
+                out.print(meth.lP + code.length + ":\t");
+            }
+            iatt.printStackMap();
+            out.println();
+        }
+        // print TypeAnnotations
+        if (visibleTypeAnnotations != null) {
+            out.println();
+            for (TypeAnnotationData visad : visibleTypeAnnotations) {
+                visad.print(out, getIndentString());
+                out.println();
+            }
+        }
+        if (invisibleTypeAnnotations != null) {
+            for (TypeAnnotationData invisad : invisibleTypeAnnotations) {
+                invisad.print(out, getIndentString());
+                out.println();
+            }
+        }
+        // end of code
+        out.println(getIndentString() + "}");
+    }
+
+
+    public static class LocVarData {
+
+        short start_pc, length, name_cpx, sig_cpx, slot;
+
+        public LocVarData(DataInputStream in) throws IOException {
+            start_pc = in.readShort();
+            length = in.readShort();
+            name_cpx = in.readShort();
+            sig_cpx = in.readShort();
+            slot = in.readShort();
+        }
+    }
+
+    /* Code Data inner classes */
+    class LineNumData {
+
+        short start_pc, line_number;
+
+        public LineNumData(DataInputStream in) throws IOException {
+            start_pc = in.readShort();
+            line_number = in.readShort();
+        }
+    }
+
+}
diff a/test/lib/org/openjdk/asmtools/jdis/ConstantPool.java b/test/lib/org/openjdk/asmtools/jdis/ConstantPool.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/ConstantPool.java
@@ -0,0 +1,1073 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jdis;
+
+import org.openjdk.asmtools.asmutils.HexUtils;
+import org.openjdk.asmtools.asmutils.StringUtils;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.stream.Collectors;
+
+import static java.lang.String.format;
+import static org.openjdk.asmtools.jdis.Utils.commentString;
+
+/**
+ *
+ * ConstantPool
+ *
+ * Class representing the ConstantPool
+ */
+public class ConstantPool {
+
+    private static final Hashtable<Byte, TAG> taghash = new Hashtable<>();
+    private static final Hashtable<Byte, SUBTAG> subtaghash = new Hashtable<>();
+
+    private boolean printTAG = false;
+
+    public void setPrintTAG(boolean value) {
+        this.printTAG = value;
+    }
+
+    public String getPrintedTAG(TAG tag) {
+        return (this.printTAG) ? tag.tagname + " " : "" ;
+    }
+
+    class Indent {
+        private int length, offset, step;
+
+        void inc() {  length+=step; }
+
+        void dec() { length-=step; }
+
+        Indent(int offset, int step) {
+            this.length = 0;
+            this.step = step;
+            this.offset = offset;
+        }
+
+        int size() { return  offset + length; }
+
+        /**
+         * Creates indent string based on current indent size.
+         */
+        private String get() {
+            return Collections.nCopies(size(), "\t").stream().collect(Collectors.joining());
+        }
+    }
+
+    private final Indent indent = new Indent(2, 1);
+
+    /**
+     * TAG
+     *
+     * A Tag descriptor of constants in the constant pool
+     *
+     */
+    public enum TAG {
+        CONSTANT_UTF8               ((byte) 1, "Asciz", "CONSTANT_UTF8"),
+        CONSTANT_UNICODE            ((byte) 2, "unicorn", "CONSTANT_UNICODE"),
+        CONSTANT_INTEGER            ((byte) 3, "int", "CONSTANT_INTEGER"),
+        CONSTANT_FLOAT              ((byte) 4, "float", "CONSTANT_FLOAT"),
+        CONSTANT_LONG               ((byte) 5, "long", "CONSTANT_LONG"),
+        CONSTANT_DOUBLE             ((byte) 6, "double", "CONSTANT_DOUBLE"),
+        CONSTANT_CLASS              ((byte) 7, "class", "CONSTANT_CLASS"),
+        CONSTANT_STRING             ((byte) 8, "String", "CONSTANT_STRING"),
+        CONSTANT_FIELD              ((byte) 9, "Field", "CONSTANT_FIELD"),
+        CONSTANT_METHOD             ((byte) 10, "Method", "CONSTANT_METHOD"),
+        CONSTANT_INTERFACEMETHOD    ((byte) 11, "InterfaceMethod", "CONSTANT_INTERFACEMETHOD"),
+        CONSTANT_NAMEANDTYPE        ((byte) 12, "NameAndType", "CONSTANT_NAMEANDTYPE"),
+        CONSTANT_METHODHANDLE       ((byte) 15, "MethodHandle", "CONSTANT_METHODHANDLE"),
+        CONSTANT_METHODTYPE         ((byte) 16, "MethodType", "CONSTANT_METHODTYPE"),
+        CONSTANT_DYNAMIC            ((byte) 17, "Dynamic", "CONSTANT_DYNAMIC"),
+        CONSTANT_INVOKEDYNAMIC      ((byte) 18, "InvokeDynamic", "CONSTANT_INVOKEDYNAMIC"),
+        CONSTANT_MODULE             ((byte) 19, "Module", "CONSTANT_MODULE"),
+        CONSTANT_PACKAGE            ((byte) 20, "Package", "CONSTANT_PACKAGE");
+
+        private final Byte value;
+        private final String tagname;
+        private final String printval;
+
+        TAG(byte val, String tgname, String print) {
+            value = val;
+            tagname = tgname;
+            printval = print;
+        }
+
+        public byte value() {
+            return value;
+        }
+
+        public String tagname() {
+            return tagname;
+        }
+
+        public String description() {
+            return printval;
+        }
+
+        @Override
+        public String toString() {
+            return "<" + tagname + "> ";
+        }
+
+    };
+
+
+    /**
+    * SUBTAG
+    *
+    * A Tag descriptor of form method-handle constants
+    *
+    */
+    static public enum SUBTAG {
+        REF_GETFIELD            ((byte) 1, "REF_getField", "REF_GETFIELD"),
+        REF_GETSTATIC           ((byte) 2, "REF_getStatic", "REF_GETSTATIC"),
+        REF_PUTFIELD            ((byte) 3, "REF_putField", "REF_PUTFIELD"),
+        REF_PUTSTATIC           ((byte) 4, "REF_putStatic", "REF_PUTSTATIC"),
+        REF_INVOKEVIRTUAL       ((byte) 5, "REF_invokeVirtual", "REF_INVOKEVIRTUAL"),
+        REF_INVOKESTATIC        ((byte) 6, "REF_invokeStatic", "REF_INVOKESTATIC"),
+        REF_INVOKESPECIAL       ((byte) 7, "REF_invokeSpecial", "REF_INVOKESPECIAL"),
+        REF_NEWINVOKESPECIAL    ((byte) 8, "REF_newInvokeSpecial", "REF_NEWINVOKESPECIAL"),
+        REF_INVOKEINTERFACE     ((byte) 9, "REF_invokeInterface", "REF_INVOKEINTERFACE");
+
+        private final Byte value;
+        private final String tagname;
+        private final String printval;
+
+        SUBTAG(byte val, String tgname, String print) {
+            value = val;
+            tagname = tgname;
+            printval = print;
+//            subtaghash.put(new Byte(val), this);
+        }
+
+        public byte value() {
+            return value;
+        }
+
+        public String tagname() {
+            return tagname;
+        }
+
+        public String description() {
+            return printval;
+        }
+
+        @Override
+        public String toString() {
+            return "<" + tagname + "> ";
+        }
+    };
+
+    static {
+
+        // Class initializer Code
+        //
+        // Make sure all of the tags get initialized before being used.
+        taghash.put(TAG.CONSTANT_UTF8.value(), TAG.CONSTANT_UTF8);
+        taghash.put(TAG.CONSTANT_UNICODE.value(), TAG.CONSTANT_UNICODE);
+        taghash.put(TAG.CONSTANT_INTEGER.value(), TAG.CONSTANT_INTEGER);
+        taghash.put(TAG.CONSTANT_FLOAT.value(), TAG.CONSTANT_FLOAT);
+        taghash.put(TAG.CONSTANT_LONG.value(), TAG.CONSTANT_LONG);
+        taghash.put(TAG.CONSTANT_DOUBLE.value(), TAG.CONSTANT_DOUBLE);
+        taghash.put(TAG.CONSTANT_CLASS.value(), TAG.CONSTANT_CLASS);
+        taghash.put(TAG.CONSTANT_STRING.value(), TAG.CONSTANT_STRING);
+        taghash.put(TAG.CONSTANT_FIELD.value(), TAG.CONSTANT_FIELD);
+        taghash.put(TAG.CONSTANT_METHOD.value(), TAG.CONSTANT_METHOD);
+        taghash.put(TAG.CONSTANT_INTERFACEMETHOD.value(), TAG.CONSTANT_INTERFACEMETHOD);
+        taghash.put(TAG.CONSTANT_NAMEANDTYPE.value(), TAG.CONSTANT_NAMEANDTYPE);
+        taghash.put(TAG.CONSTANT_METHODHANDLE.value(), TAG.CONSTANT_METHODHANDLE);
+        taghash.put(TAG.CONSTANT_METHODTYPE.value(), TAG.CONSTANT_METHODTYPE);
+        taghash.put(TAG.CONSTANT_DYNAMIC.value(), TAG.CONSTANT_DYNAMIC);
+        taghash.put(TAG.CONSTANT_INVOKEDYNAMIC.value(), TAG.CONSTANT_INVOKEDYNAMIC);
+        taghash.put(TAG.CONSTANT_MODULE.value(), TAG.CONSTANT_MODULE);
+        taghash.put(TAG.CONSTANT_PACKAGE.value(), TAG.CONSTANT_PACKAGE);
+
+        subtaghash.put(SUBTAG.REF_GETFIELD.value(), SUBTAG.REF_GETFIELD);
+        subtaghash.put(SUBTAG.REF_GETSTATIC.value(), SUBTAG.REF_GETSTATIC);
+        subtaghash.put(SUBTAG.REF_PUTFIELD.value(), SUBTAG.REF_PUTFIELD);
+        subtaghash.put(SUBTAG.REF_PUTSTATIC.value(), SUBTAG.REF_PUTSTATIC);
+        subtaghash.put(SUBTAG.REF_INVOKEVIRTUAL.value(), SUBTAG.REF_INVOKEVIRTUAL);
+        subtaghash.put(SUBTAG.REF_INVOKESTATIC.value(), SUBTAG.REF_INVOKESTATIC);
+        subtaghash.put(SUBTAG.REF_INVOKESPECIAL.value(), SUBTAG.REF_INVOKESPECIAL);
+        subtaghash.put(SUBTAG.REF_NEWINVOKESPECIAL.value(), SUBTAG.REF_NEWINVOKESPECIAL);
+        subtaghash.put(SUBTAG.REF_INVOKEINTERFACE.value(), SUBTAG.REF_INVOKEINTERFACE);
+
+    }
+
+    /**
+     *
+     * Constant
+     *
+     * Base class of all constant entries
+     *
+     */
+    public class Constant {
+
+        /**
+         * tag the descriptor for the constant
+         */
+        public TAG tag;
+
+        public Constant(TAG tagval) {
+            tag = tagval;
+        }
+
+        public String stringVal() {
+            return "";
+        }
+
+        public void print(PrintWriter out) {
+            out.print(tag.tagname + "\t");
+        }
+
+        public int size() {
+            return 1;
+        }
+
+        @Override
+        public String toString() {
+            return "<CONSTANT " + tag.toString() + " " + stringVal() + ">";
+        }
+
+        private IOException issue;
+
+        public IOException getIssue() {
+            return issue;
+        }
+
+        public void setIssue(IOException value) {
+            issue = value;
+        }
+
+    }
+
+    /* -------------------------------------------------------- */
+    /* Constant Sub-classes */
+    /**
+     *
+     * CP_Str
+     *
+     * Constant entries that contain String data. usually is a CONSTANT_UTF8
+     *
+     */
+    class CP_Str extends Constant {
+
+        String value;
+
+        CP_Str(TAG tagval, String str) {
+            super(tagval);
+            this.value = str;
+        }
+
+        @Override
+        public String stringVal() {
+            return StringUtils.Utf8ToString(value);
+        }
+
+        @Override
+        public void print(PrintWriter out) {
+            super.print(out);
+            out.println(stringVal() + ";");
+        }
+    }
+
+    /**
+     *
+     * CP_Int
+     *
+     * Constant entries that contain Integer data. usually is a CONSTANT_INTEGER
+     *
+     */
+    class CP_Int extends Constant {
+
+        Integer value;
+
+        CP_Int(TAG tagval, int intval) {
+            super(tagval);
+            this.value = intval;
+        }
+
+        @Override
+        public String stringVal() {
+            if (cd.options.contains(Options.PR.HEX)) {
+                return HexUtils.toHex(value.intValue());
+            }
+            return value.toString();
+        }
+
+        @Override
+        public void print(PrintWriter out) {
+            super.print(out);
+            out.println(stringVal() + ";");
+        }
+    }
+
+    /**
+     *
+     * CP_Long
+     *
+     * Constant entries that contain LongInteger data. usually is a CONSTANT_LONG
+     *
+     * These take up 2 slots in the constant pool.
+     *
+     */
+    class CP_Long extends Constant {
+
+        Long value;
+
+        CP_Long(TAG tagval, long intval) {
+            super(tagval);
+            this.value = intval;
+        }
+
+        @Override
+        public String stringVal() {
+            if (cd.options.contains(Options.PR.HEX)) {
+                return HexUtils.toHex(value.longValue()) + 'l';
+            }
+            return value.toString() + 'l';
+        }
+
+        @Override
+        public void print(PrintWriter out) {
+            super.print(out);
+            out.println(stringVal() + ";");
+        }
+
+        @Override
+        public int size() {
+            return 2;
+        }
+    }
+
+    /**
+     *
+     * CP_Float
+     *
+     * Constant entries that contain Float data. usually is a CONSTANT_FLOAT
+     *
+     */
+    class CP_Float extends Constant {
+
+        Float value;
+
+        CP_Float(TAG tagval, float fltvl) {
+            super(tagval);
+            this.value = fltvl;
+        }
+
+        @Override
+        public String stringVal() {
+            if (cd.options.contains(Options.PR.HEX)) {
+                return "bits " + HexUtils.toHex(Float.floatToIntBits(value.floatValue()));
+            }
+            String sf = (value).toString();
+            if (value.isNaN() || value.isInfinite()) {
+                return sf;
+            }
+            return sf + "f";
+        }
+
+        @Override
+        public void print(PrintWriter out) {
+            super.print(out);
+            out.println(stringVal() + ";");
+        }
+    }
+
+    /**
+     *
+     * CP_Double
+     *
+     * Constant entries that contain double-precision float data. usually is a
+     * CONSTANT_DOUBLE
+     *
+     * These take up 2 slots in the constant pool.
+     *
+     */
+    class CP_Double extends Constant {
+
+        Double value;
+
+        CP_Double(TAG tagval, double fltvl) {
+            super(tagval);
+            this.value = fltvl;
+        }
+
+        @Override
+        public String stringVal() {
+            if (cd.options.contains(Options.PR.HEX)) {
+                return "bits " + HexUtils.toHex(Double.doubleToLongBits(value.doubleValue())) + 'l';
+            }
+            String sd = value.toString();
+            if (value.isNaN() || value.isInfinite()) {
+                return sd;
+            }
+            return sd + "d";
+        }
+
+        @Override
+        public void print(PrintWriter out) {
+            super.print(out);
+            out.println(stringVal() + ";");
+        }
+
+        @Override
+        public int size() {
+            return 2;
+        }
+    }
+
+    /**
+     *
+     * CPX
+     *
+     * Constant entries that contain a single constant-pool index. Usually, this includes:
+     * CONSTANT_CLASS CONSTANT_METHODTYPE CONSTANT_STRING CONSTANT_MODULE CONSTANT_PACKAGE
+     *
+     */
+    class CPX extends Constant {
+
+        int value;
+
+        CPX(TAG tagval, int cpx) {
+            super(tagval);
+            this.value = cpx;
+        }
+
+        @Override
+        public String stringVal() {
+            String str = "UnknownTag";
+            switch (tag) {
+                case CONSTANT_CLASS:
+                    str = getShortClassName(getClassName(this), cd.pkgPrefix);
+                    break;
+                case CONSTANT_PACKAGE:
+                case CONSTANT_MODULE:
+                    str = getString(value);
+                    break;
+                case CONSTANT_METHODTYPE:
+                case CONSTANT_STRING:
+                    str = StringValue(value);
+                    break;
+                default:
+                    break;
+            }
+            return str;
+        }
+
+        @Override
+        public void print(PrintWriter out) {
+            super.print(out);
+            switch (tag) {
+                case CONSTANT_CLASS:
+                case CONSTANT_STRING:
+                case CONSTANT_METHODTYPE:
+                case CONSTANT_PACKAGE:
+                case CONSTANT_MODULE:
+                    out.println("#" + (value) + ";\t//  " + stringVal());
+                    break;
+            }
+        }
+    }
+
+    /**
+     *
+     * CPX2
+     *
+     * Constant entries that contain two constant-pool indices. Usually, this includes:
+     * CONSTANT_FIELD CONSTANT_METHOD CONSTANT_INTERFACEMETHOD CONSTANT_NAMEANDTYPE
+     * CONSTANT_METHODHANDLE CONSTANT_DYNAMIC CONSTANT_INVOKEDYNAMIC
+     *
+     */
+    class CPX2 extends Constant {
+
+        int value1, value2;
+
+        CPX2(TAG tagval, int cpx1, int cpx2) {
+            super(tagval);
+            this.value1 = cpx1;
+            this.value2 = cpx2;
+        }
+
+        @Override
+        public String stringVal() {
+
+            String str = "UnknownTag";
+            switch (tag) {
+                case CONSTANT_FIELD:
+                    // CODETOOLS-7902660: the tag Field is not necessary while printing static parameters of a bsm
+                    // Example: MethodHandle REF_getField:ClassName.FieldName:"I"
+                    str = getShortClassName(getClassName(value1), cd.pkgPrefix) + "." + StringValue(value2);
+                    break;
+                case CONSTANT_METHOD:
+                case CONSTANT_INTERFACEMETHOD:
+                    // CODETOOLS-7902648: added printing of the tag: Method/Interface to clarify
+                    // interpreting CONSTANT_MethodHandle_info:reference_kind
+                    // Example: invokedynamic InvokeDynamic REF_invokeStatic:Method java/lang/runtime/ObjectMethods.bootstrap
+                    str = getPrintedTAG(tag) + getShortClassName(getClassName(value1), cd.pkgPrefix) + "." + StringValue(value2);
+                    break;
+                case CONSTANT_NAMEANDTYPE:
+                    str = getName(value1) + ":" + StringValue(value2);
+                    break;
+                case CONSTANT_METHODHANDLE:
+                    str = subtagToString(value1) + ":" + StringValue(value2);
+                    break;
+                case CONSTANT_DYNAMIC:
+                case CONSTANT_INVOKEDYNAMIC:
+                    int bsm_attr_idx = value1;
+                    int nape_idx = value2;
+                    BootstrapMethodData bsmData;
+                    try {
+                        bsmData = cd.bootstrapMethods.get(bsm_attr_idx);
+                    } catch (NullPointerException npe) {
+                        return "<Missing BootstrapMethods attribute>";
+                    } catch (IndexOutOfBoundsException ioob) {
+                        return "<Invalid bootstrap method index:" + bsm_attr_idx + ">";
+                    }
+                    StringBuilder bsm_args_str = new StringBuilder();
+                    String offsetParm,offsetBrace;
+                    int bsm_ref = bsmData.bsm_index;
+                    int bsm_args_len = bsmData.bsm_args_indexes.size();
+                    if (bsm_args_len > 0) {
+                        bsm_args_str.append(" {\n");
+                        offsetBrace = indent.get();
+                        indent.inc();
+                        offsetParm = indent.get();
+                        for (int i = 0; i < bsm_args_len; i++) {
+                            int bsm_arg_idx = bsmData.bsm_args_indexes.get(i);
+                            Constant cnt = pool.get(bsm_arg_idx);
+                            if (cnt.equals(this)) {
+                                String s = "circular reference to " + cnt.tag.tagname() + " #" + bsm_arg_idx;
+                                bsm_args_str.append(offsetParm).append("  <").append(s).append(">");
+                                cnt.setIssue(new IOException(s));
+                            } else {
+                                bsm_args_str.append(offsetParm).append(ConstantStrValue(bsm_arg_idx));
+                                if (i + 1 < bsm_args_len) {
+                                    bsm_args_str.append(",");
+                                }
+                            }
+                            bsm_args_str.append('\n');
+                        }
+                        indent.dec();
+                        bsm_args_str.append(offsetBrace).append("}");
+                    }
+                    str = StringValue(bsm_ref) + ":" + StringValue(nape_idx) + bsm_args_str.toString();
+                default:
+                    break;
+            }
+            return str;
+        }
+
+
+
+        @Override
+        public void print(PrintWriter out) {
+            super.print(out);
+            switch (tag) {
+                case CONSTANT_FIELD:
+                case CONSTANT_METHOD:
+                case CONSTANT_INTERFACEMETHOD:
+                    out.println("#" + value1 + ".#" + value2 + ";\t//  " + stringVal());
+                    break;
+                case CONSTANT_METHODHANDLE:
+                    out.println(value1 + ":#" + value2 + ";\t//  " + stringVal());
+                    break;
+                case CONSTANT_NAMEANDTYPE:
+                    out.println("#" + value1 + ":#" + value2 + ";\t//  " + stringVal());
+                    break;
+                case CONSTANT_DYNAMIC:
+                case CONSTANT_INVOKEDYNAMIC:
+                    out.println(value1 + ":#" + value2 + ";\t" + commentString(stringVal()));
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        public boolean refersClassMember() {
+            return tag == TAG.CONSTANT_FIELD || tag == TAG.CONSTANT_METHOD || tag == TAG.CONSTANT_INTERFACEMETHOD;
+        }
+    }
+
+    /* -------------------------------------------------------- */
+    /* ConstantPool Fields */
+    /**
+     * The actual pool of Constants
+     */
+    public ArrayList<Constant> pool;
+    /**
+     * Reference to the class data
+     */
+    private ClassData cd;
+
+
+    /* -------------------------------------------------------- */
+    /* ConstantPool Methods */
+
+    /* ConstantPool Constructors */
+    public ConstantPool(ClassData cd) {
+        pool = null;
+        this.cd = cd;
+    }
+
+    public ConstantPool(ClassData cd, int size) {
+        pool = new ArrayList<>(size);
+        this.cd = cd;
+    }
+
+    /**
+     *
+     * read
+     *
+     * decodes a ConstantPool and it's constants from a data stream.
+     *
+     */
+    void read(DataInputStream in) throws IOException {
+        int length = in.readUnsignedShort();
+        pool = new ArrayList<>(length);
+        pool.add(0, null);
+        TraceUtils.traceln("CP len=" + length);
+        for (int i = 1; i < length; i++) {
+            byte tag = in.readByte();
+            TAG tagobj = taghash.get(tag);
+            TraceUtils.traceln("CP entry #" + i + " + tagindex=" + tag + " tag=" + tagobj);
+            switch (tagobj) {
+                case CONSTANT_UTF8:
+                    pool.add(i, new CP_Str(tagobj, in.readUTF()));
+                    break;
+                case CONSTANT_INTEGER:
+                    pool.add(i, new CP_Int(tagobj, in.readInt()));
+                    break;
+                case CONSTANT_LONG:
+                    pool.add(i, new CP_Long(tagobj, in.readLong()));
+                    // handle null entry to account for Longs taking up 2 CP slots
+                    i += 1;
+                    pool.add(null);
+                    break;
+                case CONSTANT_FLOAT:
+                    pool.add(i, new CP_Float(tagobj, in.readFloat()));
+                    break;
+                case CONSTANT_DOUBLE:
+                    pool.add(i, new CP_Double(tagobj, in.readDouble()));
+                    // handle null entry to account for Doubles taking up 2 CP slots
+                    i += 1;
+                    pool.add(null);
+                    break;
+                case CONSTANT_CLASS:
+                case CONSTANT_STRING:
+                case CONSTANT_METHODTYPE:
+                case CONSTANT_PACKAGE:
+                case CONSTANT_MODULE:
+                    pool.add(i, new CPX(tagobj, in.readUnsignedShort()));
+                    break;
+                case CONSTANT_FIELD:
+                case CONSTANT_METHOD:
+                case CONSTANT_INTERFACEMETHOD:
+                case CONSTANT_NAMEANDTYPE:
+                case CONSTANT_DYNAMIC:
+                case CONSTANT_INVOKEDYNAMIC:
+                    pool.add(i, new CPX2(tagobj, in.readUnsignedShort(), in.readUnsignedShort()));
+                    break;
+                case CONSTANT_METHODHANDLE:
+                    pool.add(i, new CPX2(tagobj, in.readUnsignedByte(), in.readUnsignedShort()));
+                    break;
+
+                default:
+                    throw new ClassFormatError("invalid constant type: " + (int) tag);
+            }
+        }
+    }
+
+    /**
+     *
+     * inbounds
+     *
+     * bounds-check a CP index.
+     *
+     */
+    private boolean inbounds(int cpx) {
+        return !(cpx == 0 || cpx >= pool.size());
+    }
+
+    /**
+     *
+     * getConst
+     *
+     * Public getter - Safely gets a Constant from the CP at a given index.
+     *
+     */
+    public Constant getConst(int cpx) {
+        if (inbounds(cpx)) {
+            return pool.get(cpx);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     *
+     * StringTag
+     *
+     * Public string val - Safely gets the string-rep of a Constant from the CP at a given
+     * index.
+     *
+     */
+    public String StringTag(int cpx) {
+        String str = "Incorrect CP index:" + cpx;
+        if (inbounds(cpx)) {
+            Constant cns = pool.get(cpx);
+            if (cns != null) {
+                str = cns.tag.tagname;
+            }
+        }
+        return str;
+    }
+
+    /**
+     *
+     * getString
+     *
+     * Public string val - Safely gets the string-rep of a ConstantUTF8 from the CP at a
+     * given index.
+     *
+     * Returns either null (if invalid), or the string value of the UTF8
+     *
+     */
+    public String getString(int cpx) {
+        String str = null;
+        if (inbounds(cpx)) {
+            Constant cns = pool.get(cpx);
+            if (cns != null && cns.tag == TAG.CONSTANT_UTF8) {
+                CP_Str cns1 = (CP_Str) cns;
+                str = cns1.value;
+            }
+        }
+        return str;
+    }
+
+    /**
+     *
+     * getModule
+     *
+     * Public string val - Safely gets the string-rep of a ConstantModule from the CP at a
+     * given index.
+     *
+     * Returns either null (if invalid), or the string value of the ConstantModule
+     *
+     */
+    public String getModule(int cpx) {
+        String str = null;
+        if (inbounds(cpx)) {
+            Constant cns = pool.get(cpx);
+            if (cns != null && cns.tag == TAG.CONSTANT_MODULE) {
+                str = cns.stringVal();
+            }
+        }
+        return str;
+    }
+
+    /**
+     *
+     * getPackage
+     *
+     * Public string val - Safely gets the string-rep of a ConstantPackage from the CP at a
+     * given index.
+     *
+     * Returns either null (if invalid), or the string value of the ConstantPackage
+     *
+     */
+    public String getPackage(int cpx) {
+        String str = null;
+        if (inbounds(cpx)) {
+            Constant cns = pool.get(cpx);
+            if (cns != null && cns.tag == TAG.CONSTANT_PACKAGE) {
+                str = cns.stringVal();
+            }
+        }
+        return str;
+    }
+
+    /**
+     *
+     * getTypeName
+     *
+     * Safely gets a Java name from a ConstantUTF8 from the CP at a given index.
+     *
+     * Returns either null (if invalid), or the Java name value of the UTF8
+     *
+     */
+    public String getName(int cpx) {
+        String str = getString(cpx);
+        if (str == null) {
+            return "<invalid constant pool index:" + cpx + ">";
+        }
+
+        return Utils.javaName(str);
+    }
+
+    /**
+     *
+     * getClassName
+     *
+     * Safely gets a Java class name from a ConstantClass from the CP at a given index.
+     *
+     * Returns either the Java class name, or a CP index reference string.
+     *
+     */
+    public String getClassName(int cpx) {
+        String res = "#" + cpx;
+        if (cpx == 0) {
+            return res;
+        }
+        if (!inbounds(cpx)) {
+            return res;
+        }
+        Constant cns = pool.get(cpx);
+        if (cns == null || cns.tag != TAG.CONSTANT_CLASS) {
+            return res;
+        }
+
+        return getClassName((CPX) cns);
+    }
+
+    /**
+     *
+     * getClassName
+     *
+     * Safely gets a Java class name from a ConstantClass from a CPX2 constant pool
+     * object. (eg. Method/Field/Interface Ref)
+     *
+     * Returns either the Java class name, or a CP index reference string.
+     *
+     */
+    public String getClassName(CPX2 classConst) {
+        return _getClassName(classConst.value1);
+    }
+
+    /**
+     *
+     * getClassName
+     *
+     * Safely gets a Java class name from a ConstantClass from a CPX constant pool object.
+     * (eg. Class Ref)
+     *
+     * Returns either the Java class name, or a CP index reference string.
+     *
+     */
+    public String getClassName(CPX classConst) {
+        return _getClassName(classConst.value);
+    }
+
+    /**
+     *
+     * _getClassName
+     *
+     * Helper for getting class name. Checks bounds, does name conversion.
+     *
+     */
+    private String _getClassName(int nameIndex) {
+        String res = "#" + nameIndex;
+        if (!inbounds(nameIndex)) {
+            return res;
+        }
+        Constant nameconst = pool.get(nameIndex);
+        if (nameconst == null || nameconst.tag != TAG.CONSTANT_UTF8) {
+            return res;
+        }
+        CP_Str name = (CP_Str) nameconst;
+
+        String classname = name.value;
+
+        if (Utils.isClassArrayDescriptor(classname)) {
+            classname = "\"" + classname + "\"";
+        }
+        return classname;
+    }
+
+    /**
+     *
+     * getShortClassName
+     *
+     * shortens a class name (if the class is in the given package). works with a
+     * string-encoded classname.
+     *
+     */
+    public String getShortClassName(String className, String pkgPrefix) {
+        if (className.startsWith(pkgPrefix)) {
+            return className.substring(pkgPrefix.length());
+        }
+        return className;
+    }
+
+    /**
+     *
+     * getShortClassName
+     *
+     * shortens a class name (if the class is in the given package). works with a CP index
+     * to a ConstantClass.
+     *
+     */
+    public String getShortClassName(int cpx, String pkgPrefix) {
+        String name = Utils.javaName(getClassName(cpx));
+        return getShortClassName(name, pkgPrefix);
+    }
+
+    /**
+     *
+     * decodeClassDescriptor
+     *
+     * Pulls the class name out of a string (at the CP index). (drops any array
+     * descriptors, and the class descriptors ("L" and ";")
+     *
+     */
+    public String decodeClassDescriptor(int cpx) {
+        // enum type is encoded as a descriptor
+        // need to remove '"'s and L (class descriptor)
+
+        // TODO: might have to count '['s at the beginning for Arrays
+        String rawEnumName = getName(cpx);
+        int len = rawEnumName.length();
+        int begin = (rawEnumName.startsWith("\"L")) ? 2 : 0;
+        int end = (begin > 0) ? len - 2 : len;
+        return rawEnumName.substring(begin, end);
+    }
+
+    /**
+     *
+     * subtagToString
+     *
+     * Getter that safely gets the string descriptor of a subtag
+     *
+     */
+    private String subtagToString(int subtag) {
+        SUBTAG st = subtaghash.get((byte) subtag);
+        if (st == null) {
+            return "BOGUS_SUBTAG:" + subtag;
+        }
+        return st.tagname;
+    }
+
+    /**
+     *
+     * StringValue
+     *
+     * Safely gets the string value of any Constant at any CP index.
+     *
+     */
+    public String StringValue(int cpx) {
+        if (cpx == 0) {
+            return "#0";
+        }
+        if (!inbounds(cpx)) {
+            return "<Incorrect CP index:" + cpx + ">";
+        }
+        Constant cnst = pool.get(cpx);
+        if (cnst == null) {
+            return "<NULL>";
+        }
+        return cnst.stringVal();
+    }
+
+    /**
+     * ConstantStrValue
+     *
+     * Safely gets the string value of any Constant at any CP index. This string is either
+     * a Constant's String value, or a CP index reference string. The Constant string has
+     * a tag descriptor in the beginning.
+     *
+     */
+    public String ConstantStrValue(int cpx) {
+        if (cpx == 0) {
+            return "#0";
+        }
+        if (!inbounds(cpx)) {
+            return "#" + cpx;
+        }
+        Constant cns = pool.get(cpx);
+        if (cns == null) {
+            return "#" + cpx;
+        }
+        if (cns instanceof CPX2) {
+            CPX2 cns2 = (CPX2) cns;
+            if (cns2.value1 == cd.this_cpx && cns2.refersClassMember()) {
+                cpx = cns2.value2;
+            }
+        }
+        return cns.tag.tagname + " " + StringValue(cpx);
+    }
+
+    /**
+     * prints the entire constant pool.
+     */
+    public void print(PrintWriter out) throws IOException {
+        int cpx = 0;
+        for (Constant cns : pool) {
+            if (cpx == 0) {
+                cpx += 1;
+                continue;
+            }
+
+            out.print("\tconst #" + cpx + " = ");
+
+            if (cns == null) {
+                // do something
+                out.println("null");
+                cpx += 1;
+            } else {
+                cns.print(out);
+                cpx += cns.size();
+            }
+        }
+    }
+
+    /**
+     * prints the Constant value at a given CP index.
+     */
+    void PrintConstant(PrintWriter out, int cpx) {
+        out.print(ConstantStrValue(cpx));
+    }
+
+    /**
+     * prints a constant value, with the print format based on the print options.
+     */
+    public void printlnClassId(PrintWriter out, int cpx) throws IOException {
+        printlnClassId(out, cpx, false);
+    }
+
+    public void printlnClassId(PrintWriter out, int cpx, boolean addComma) throws IOException {
+        if (!cd.options.contains(Options.PR.CPX)) {
+            out.print(getShortClassName(cpx, cd.pkgPrefix) + (addComma ? "," : ""));
+        } else {
+            out.print("\t#" + cpx + (addComma ? "," : "") + " //");
+            PrintConstant(out, cpx);
+        }
+    }
+
+}
diff a/test/lib/org/openjdk/asmtools/jdis/FieldData.java b/test/lib/org/openjdk/asmtools/jdis/FieldData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/FieldData.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jdis;
+
+import org.openjdk.asmtools.jasm.JasmTokens;
+import org.openjdk.asmtools.jasm.Modifiers;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+import static java.lang.String.format;
+import static org.openjdk.asmtools.jasm.Tables.AttrTag;
+import static org.openjdk.asmtools.jasm.Tables.CF_Context;
+import static org.openjdk.asmtools.jdis.TraceUtils.traceln;
+
+/**
+ * Field data for field members in a class of the Java Disassembler
+ */
+public class FieldData extends MemberData {
+
+    // CP index to the field name
+    protected int name_cpx;
+    // CP index to the field type
+    protected int type_cpx;
+    // CP index to the field value
+    protected int value_cpx = 0;
+
+    public FieldData(ClassData cls) {
+        super(cls);
+        memberType = "FieldData";
+    }
+
+    @Override
+    protected boolean handleAttributes(DataInputStream in, AttrTag attrtag, int attrlen) throws IOException {
+        // Read the Attributes
+        boolean handled = true;
+        switch (attrtag) {
+            case ATT_Signature:
+                if( signature != null ) {
+                    traceln("Record attribute:  more than one attribute Signature are in component.attribute_info_attributes[attribute_count]");
+                    traceln("Last one will be used.");
+                }
+                signature = new SignatureData(cls).read(in, attrlen);
+                break;
+            case ATT_ConstantValue:
+                if (attrlen != 2) {
+                    throw new ClassFormatError(format("%s: Invalid attribute length #%d", AttrTag.ATT_ConstantValue.printval(), attrlen));
+                }
+                value_cpx = in.readUnsignedShort();
+                break;
+            default:
+                handled = false;
+                break;
+        }
+        return handled;
+    }
+
+    /**
+     * Read and resolve the field data called from ClassData.
+     * Precondition: NumFields has already been read from the stream.
+     */
+    public void read(DataInputStream in) throws IOException {
+        // read the Fields CP indexes
+        access = in.readUnsignedShort();
+        name_cpx = in.readUnsignedShort();
+        type_cpx = in.readUnsignedShort();
+        // Read the attributes
+        readAttributes(in);
+        //
+        TraceUtils.traceln(2,
+                format("FieldData: name[%d]=%s type[%d]=%s%s",
+                        name_cpx, cls.pool.getString(name_cpx),
+                        type_cpx, cls.pool.getString(type_cpx),
+                        signature != null ? signature : ""));
+    }
+
+
+    /**
+     * Prints the field data to the current output stream. called from ClassData.
+     */
+    @Override
+    public void print() throws IOException {
+        // Print annotations first
+        super.printAnnotations(getIndentString());
+
+        StringBuilder bodyPrefix = new StringBuilder(getIndentString()).append(Modifiers.accessString(access, CF_Context.CTX_FIELD));
+        StringBuilder tailPrefix = new StringBuilder();
+
+        if (isSynthetic) {
+            bodyPrefix.append(JasmTokens.Token.SYNTHETIC.parseKey()).append(' ');
+        }
+        if (isDeprecated) {
+            bodyPrefix.append(JasmTokens.Token.DEPRECATED.parseKey()).append(' ');
+        }
+
+        // field
+        bodyPrefix.append(JasmTokens.Token.FIELDREF.parseKey()).append(' ');
+
+        if (value_cpx != 0) {
+            tailPrefix.append("\t= ").append(cls.pool.ConstantStrValue(value_cpx));
+        }
+
+        printVar(bodyPrefix, tailPrefix,name_cpx, type_cpx);
+    }
+} // end FieldData
+
diff a/test/lib/org/openjdk/asmtools/jdis/Indenter.java b/test/lib/org/openjdk/asmtools/jdis/Indenter.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/Indenter.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.asmtools.jdis;
+
+public class Indenter {
+
+    private int indentLength;
+
+    public Indenter(int indentLength) {
+        this.indentLength = indentLength;
+    }
+
+    public Indenter() {
+        this.indentLength = Options.BODY_INDENT;
+    }
+    /**
+     * Returns current indentation length.
+     *
+     * @return current indentation length.
+     */
+    public int indent() {
+        return indentLength;
+    }
+
+    /**
+     * Increases indentation length.
+     *
+     * @param indentLength new indent length
+     *
+     * @throws IllegalArgumentException if indentLength is negative.
+     */
+    public Indenter setIndent(int indentLength) {
+        if (indentLength < 0) {
+            throw new IllegalArgumentException("indent length can't be negative");
+        }
+        this.indentLength = indentLength;
+        return this;
+    }
+
+    /**
+     * Increases indentation length.
+     *
+     * @param increase length to increase by.
+     *
+     * @throws IllegalArgumentException if increase is negative.
+     */
+    public Indenter increaseIndent(int increase) {
+        if (increase < 0) {
+            throw new IllegalArgumentException("indent length can't be negative");
+        }
+        setIndent(indent() + increase);
+        return this;
+    }
+
+    /**
+     * Decreases indentation length.
+     *
+     * @param decrease length to decrease by
+     *
+     * @throws IllegalArgumentException if decrease is negative, or if decrease is greater than
+     *                                  {@link #indent() current indentation length}.
+     */
+    public Indenter decreaseIndent(int decrease) {
+        if (decrease < 0) {
+            throw new IllegalArgumentException("decrease can't be negative");
+        }
+        setIndent(indent() - decrease);
+        return this;
+    }
+
+    /**
+     * Creates indent string based on current indent size.
+     */
+    public String getIndentString() {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < indent(); i++) {
+            sb.append(' ');
+        }
+        return sb.toString();
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jdis/InnerClassData.java b/test/lib/org/openjdk/asmtools/jdis/InnerClassData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/InnerClassData.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jdis;
+
+import static org.openjdk.asmtools.jasm.Tables.*;
+import org.openjdk.asmtools.jasm.Modifiers;
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ *
+ */
+class InnerClassData extends Indenter {
+
+    ClassData cls;
+    int inner_class_info_index;
+    int outer_class_info_index;
+    int inner_name_index;
+    int access;
+    /*-------------------------------------------------------- */
+
+    public InnerClassData(ClassData cls) {
+        this.cls = cls;
+    }
+
+    public void read(DataInputStream in) throws IOException {
+        inner_class_info_index = in.readUnsignedShort();
+        outer_class_info_index = in.readUnsignedShort();
+        inner_name_index = in.readUnsignedShort();
+        access = in.readUnsignedShort();
+    }  // end read
+
+    public void print() throws IOException {
+        boolean pr_cpx = Options.OptionObject().contains(Options.PR.CPX);
+        cls.out.print(getIndentString() + Modifiers.accessString(access, CF_Context.CTX_INNERCLASS));
+        cls.out.print("InnerClass ");
+        if (pr_cpx) {
+            if (inner_name_index != 0) {
+                cls.out.print("#" + inner_name_index + "= ");
+            }
+            cls.out.print("#" + inner_class_info_index);
+            if (outer_class_info_index != 0) {
+                cls.out.print(" of #" + outer_class_info_index);
+            }
+            cls.out.print("; // ");
+        }
+        if (inner_name_index != 0) {
+            cls.out.print(cls.pool.getName(inner_name_index) + "=");
+        }
+        if (inner_class_info_index != 0) {
+            cls.pool.PrintConstant(cls.out, inner_class_info_index);
+        }
+        if (outer_class_info_index != 0) {
+            cls.out.print(" of ");
+            cls.pool.PrintConstant(cls.out, outer_class_info_index);
+        }
+        if (pr_cpx) {
+            cls.out.println();
+        } else {
+            cls.out.println(";");
+        }
+    }
+} // end InnerClassData
+
diff a/test/lib/org/openjdk/asmtools/jdis/Main.java b/test/lib/org/openjdk/asmtools/jdis/Main.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/Main.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jdis;
+
+import org.openjdk.asmtools.common.Tool;
+import org.openjdk.asmtools.util.I18NResourceBundle;
+import org.openjdk.asmtools.util.ProductInfo;
+
+import java.io.DataInputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Main program of the Java Disassembler :: class to jasm
+ */
+public class Main extends Tool {
+
+    private Options options;
+
+    public static final I18NResourceBundle i18n
+            = I18NResourceBundle.getBundleForClass(Main.class);
+
+    public  Main(PrintWriter out, PrintWriter err, String programName) {
+        super(out, err, programName);
+        // tool specific initialization
+        options = Options.OptionObject();
+        DebugFlag = () -> options.contains(Options.PR.DEBUG);
+        printCannotReadMsg = (fname) -> error( i18n.getString("jdis.error.cannot_read", fname));
+    }
+
+    public Main(PrintStream out, String program) {
+        this(new PrintWriter(out), new PrintWriter(System.err), program);
+    }
+
+    @Override
+    public void usage() {
+        println(i18n.getString("jdis.usage"));
+        println(i18n.getString("jdis.opt.g"));
+        println(i18n.getString("jdis.opt.sl"));
+        println(i18n.getString("jdis.opt.hx"));
+        println(i18n.getString("jdis.opt.v"));
+        println(i18n.getString("jdis.opt.version"));
+    }
+
+    /**
+     * Run the disassembler
+     */
+    public synchronized boolean  disasm(String argv[]) {
+        ArrayList<String> files = new ArrayList<>();
+
+        // Parse arguments
+        for (int i = 0; i < argv.length; i++) {
+            String arg = argv[i];
+            switch (arg) {
+                case "-g":
+                    options.setCodeOptions();
+                    break;
+                case "-v":
+                    options.set(Options.PR.DEBUG);
+                    break;
+                case "-sl":
+                    options.set(Options.PR.SRC);
+                    break;
+                case "-hx":
+                    options.set(Options.PR.HEX);
+                    break;
+                case "-version":
+                    out.println(ProductInfo.FULL_VERSION);
+                    break;
+                default:
+                    if (arg.startsWith("-")) {
+                        error(i18n.getString("jdis.error.invalid_option", arg));
+                        usage();
+                        return false;
+                    } else {
+                        files.add(arg);
+                    }
+                    break;
+            }
+        }
+
+        if (files.isEmpty()) {
+            usage();
+            return false;
+        }
+
+        for (String fname : files) {
+            if (fname == null) {
+                continue;
+            } // cross out by CompilerChoice.compile
+            try {
+                ClassData cc = new ClassData(out, this);
+                cc.read(fname);
+                cc.print();
+                out.flush();
+                continue;
+            } catch (Error ee) {
+                if (DebugFlag.getAsBoolean())
+                    ee.printStackTrace();
+                error(i18n.getString("jdis.error.fatal_error", fname));
+            } catch (Exception ee) {
+                if (DebugFlag.getAsBoolean())
+                    ee.printStackTrace();
+                error(i18n.getString("jdis.error.fatal_exception", fname));
+            }
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Main program
+     */
+    public static void main(String argv[]) {
+        Main disassembler = new Main(new PrintWriter(new uEscWriter(System.out)), new PrintWriter(System.err), "jdis");
+        boolean result = disassembler.disasm(argv);
+        System.exit(result ? 0 : 1);
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jdis/MemberData.java b/test/lib/org/openjdk/asmtools/jdis/MemberData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/MemberData.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jdis;
+
+import org.openjdk.asmtools.jasm.Tables;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+import static java.lang.String.format;
+
+/**
+ * Base class for ClassData, MethodData, FieldData and RecordData(JEP 360)
+ */
+public abstract class MemberData extends Indenter {
+
+    // access flags (modifiers)
+    protected int access;
+
+    // flags
+    protected boolean isSynthetic = false;
+    protected boolean isDeprecated = false;
+
+    // Signature can be located in ClassFile, field_info, method_info, and component_info
+    protected SignatureData signature;
+
+    /**
+     * The visible annotations for this class, member( field or method) or record component
+     */
+    protected ArrayList<AnnotationData> visibleAnnotations;
+
+    /**
+     * The invisible annotations for this class, member( field or method) or record component
+     */
+    protected ArrayList<AnnotationData> invisibleAnnotations;
+
+    /**
+     * The visible annotations for this class, member( field or method) or record component
+     */
+    protected ArrayList<TypeAnnotationData> visibleTypeAnnotations;
+
+    /**
+     * The invisible annotations for this class, member( field or method) or record component
+     */
+    protected ArrayList<TypeAnnotationData> invisibleTypeAnnotations;
+
+    /**
+     * The remaining attributes of this class, member( field or method) or record component
+     */
+    protected ArrayList<AttrData> attrs;
+
+    // internal references
+    protected final Options options = Options.OptionObject();
+    protected final  boolean pr_cpx = options.contains(Options.PR.CPX);;
+    protected ClassData cls;
+    protected PrintWriter out;
+    protected String memberType = "";
+
+    public MemberData(ClassData cls) {
+        this();
+        init(cls);
+    }
+
+    public MemberData() {
+    }
+
+    public void init(ClassData cls) {
+        this.out = cls.out;
+        this.cls = cls;
+    }
+
+    protected boolean handleAttributes(DataInputStream in, Tables.AttrTag attrtag, int attrlen) throws IOException {
+        // sub-classes override
+        return false;
+    }
+
+    protected abstract void print() throws IOException;
+
+    final protected int getAnnotationsCount() {
+        return  ((visibleAnnotations == null) ? 0 : visibleAnnotations.size()) +
+                ((invisibleAnnotations == null) ? 0 : invisibleAnnotations.size()) +
+                ((visibleTypeAnnotations == null) ? 0 : visibleTypeAnnotations.size()) +
+                ((invisibleTypeAnnotations == null) ? 0 : invisibleTypeAnnotations.size());
+
+    }
+
+    final protected void printAnnotations(String initialTab) {
+        if( getAnnotationsCount() > 0 ) {
+            if (visibleAnnotations != null) {
+                for (AnnotationData visad : visibleAnnotations) {
+                    // out.print(initialTab);
+                    visad.print(out, initialTab);
+                    out.println();
+                }
+            }
+            if (invisibleAnnotations != null) {
+                for (AnnotationData invisad : invisibleAnnotations) {
+                    invisad.print(out, initialTab);
+                    out.println();
+                }
+            }
+
+            if (visibleTypeAnnotations != null) {
+                for (TypeAnnotationData visad : visibleTypeAnnotations) {
+                    visad.print(out, initialTab);
+                    out.println();
+                }
+            }
+            if (invisibleTypeAnnotations != null) {
+                for (TypeAnnotationData invisad : invisibleTypeAnnotations) {
+                    invisad.print(out, initialTab);
+                    out.println();
+                }
+            }
+        }
+    }
+
+    protected void printVar(StringBuilder bodyPrefix, StringBuilder tailPrefix, int name_cpx, int type_cpx) {
+        if( pr_cpx ) {
+            bodyPrefix.append('#').append(name_cpx).append(":#").append(type_cpx);
+            tailPrefix.append(";\t // ").append(cls.pool.getName(name_cpx)).append(':').append(cls.pool.getName(type_cpx));
+
+        } else {
+            bodyPrefix.append(cls.pool.getName(name_cpx)).append(':').append(cls.pool.getName(type_cpx));
+            tailPrefix.append(';');
+        }
+
+        if (signature != null) {
+            signature.print(bodyPrefix.append(':').toString(), tailPrefix.append( pr_cpx ? ":" : "" ).toString());
+        } else {
+            out.print(bodyPrefix);
+            out.print(tailPrefix);
+        }
+        out.println();
+    }
+
+    protected void readAttributes(DataInputStream in) throws IOException {
+        // Read the Attributes
+        int natt = in.readUnsignedShort();
+        attrs = new ArrayList<>(natt);
+        TraceUtils.traceln(format("%s - Attributes[%d]", memberType , natt));
+        AttrData attr;
+        for (int k = 0; k < natt; k++) {
+            int name_cpx = in.readUnsignedShort();
+            attr = new AttrData(cls);
+            attrs.add(attr);
+            String attr_name = cls.pool.getString(name_cpx);
+            TraceUtils.traceln(format("   #%d name[%d]=\"%s\"", k, name_cpx, attr_name));
+            Tables.AttrTag tag = Tables.attrtag(attr_name);
+            int attrlen = in.readInt();
+            switch (tag) {
+                case ATT_Synthetic:
+                    // Read Synthetic Attr
+                    if (attrlen != 0) {
+                        throw new ClassFormatError("invalid Synthetic attr length");
+                    }
+                    isSynthetic = true;
+                    break;
+                case ATT_Deprecated:
+                    // Read Deprecated Attr
+                    if (attrlen != 0) {
+                        throw new ClassFormatError("invalid Deprecated attr length");
+                    }
+                    isDeprecated = true;
+                    break;
+                case ATT_RuntimeVisibleAnnotations:
+                case ATT_RuntimeInvisibleAnnotations:
+                    // Read Annotations Attr
+                    int cnt = in.readShort();
+                    ArrayList<AnnotationData> annots = new ArrayList<>(cnt);
+                    boolean invisible = (tag == Tables.AttrTag.ATT_RuntimeInvisibleAnnotations);
+                    for (int i = 0; i < cnt; i++) {
+                        TraceUtils.traceln("      AnnotationData: #" + i);
+                        AnnotationData annot = new AnnotationData(invisible, cls);
+                        annot.read(in);
+                        annots.add(annot);
+                    }
+
+                    if (invisible) {
+                        invisibleAnnotations = annots;
+                    } else {
+                        visibleAnnotations = annots;
+                    }
+                    break;
+                case ATT_RuntimeVisibleTypeAnnotations:
+                case ATT_RuntimeInvisibleTypeAnnotations:
+                    // Read Type Annotations Attr
+                    int tcnt = in.readShort();
+                    ArrayList<TypeAnnotationData> tannots = new ArrayList<>(tcnt);
+                    boolean tinvisible = (tag == Tables.AttrTag.ATT_RuntimeInvisibleTypeAnnotations);
+                    for (int tindex = 0; tindex < tcnt; tindex++) {
+                        TraceUtils.traceln("      TypeAnnotationData: #" + tindex);
+                        TypeAnnotationData tannot = new TypeAnnotationData(tinvisible, cls);
+                        tannot.read(in);
+                        tannots.add(tannot);
+                    }
+
+                    if (tinvisible) {
+                        invisibleTypeAnnotations = tannots;
+                    } else {
+                        visibleTypeAnnotations = tannots;
+                    }
+                    break;
+                default:
+                    boolean handled = handleAttributes(in, tag, attrlen);
+                    if (!handled) {
+                        attr.read(name_cpx, attrlen, in);
+                    }
+                    break;
+
+            }
+        }
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jdis/MethodData.java b/test/lib/org/openjdk/asmtools/jdis/MethodData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/MethodData.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jdis;
+
+import org.openjdk.asmtools.jasm.Modifiers;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import static org.openjdk.asmtools.jasm.JasmTokens.Token;
+import static org.openjdk.asmtools.jasm.Tables.AttrTag;
+import static org.openjdk.asmtools.jasm.Tables.CF_Context;
+
+/**
+ * Method data for method members in a class of the Java Disassembler
+ */
+public class MethodData extends MemberData {
+
+    /**
+     * CP index to the method name
+     */
+    protected int name_cpx;
+
+    /**
+     * CP index to the method type
+     */
+    protected int sig_cpx;
+    protected String lP;        // labelPrefix
+    /**
+     * The parameter names for this method
+     */
+    protected ArrayList<ParamNameData> paramNames;
+    /**
+     * The visible parameter annotations for this method
+     */
+    protected ParameterAnnotationData visibleParameterAnnotations;
+    /**
+     * The invisible parameter annotations for this method
+     */
+    protected ParameterAnnotationData invisibleParameterAnnotations;
+    /**
+     * The invisible parameter annotations for this method
+     */
+    protected AnnotationElement.AnnotValue defaultAnnotation;
+    /**
+     * The code data for this method. May be null
+     */
+    private CodeData code;
+    /**
+     * The exception table (thrown exceptions) for this method. May be null
+     */
+    private int[] exc_table = null;
+
+    public MethodData(ClassData cls) {
+        super(cls);
+        memberType = "MethodData";
+        lP = (options.contains(Options.PR.LABS)) ? "L" : "";
+        paramNames = null;
+    }
+
+    /*========================================================*/
+    /* Read Methods */
+    @Override
+    protected boolean handleAttributes(DataInputStream in, AttrTag attrtag, int attrlen) throws IOException {
+        // Read the Attributes
+        boolean handled = true;
+        switch (attrtag) {
+            case ATT_Code:
+                code = new CodeData(this);
+                code.read(in, attrlen);
+                break;
+            case ATT_Exceptions:
+                readExceptions(in);
+                break;
+            case ATT_MethodParameters:
+                readMethodParameters(in);
+                break;
+            case ATT_RuntimeVisibleParameterAnnotations:
+            case ATT_RuntimeInvisibleParameterAnnotations:
+                boolean invisible = (attrtag == AttrTag.ATT_RuntimeInvisibleParameterAnnotations);
+                ParameterAnnotationData pannots = new ParameterAnnotationData(cls, invisible);
+                pannots.read(in);
+                if (invisible) {
+                    invisibleParameterAnnotations = pannots;
+                } else {
+                    visibleParameterAnnotations = pannots;
+                }
+                break;
+            case ATT_AnnotationDefault:
+                defaultAnnotation = AnnotationElement.readValue(in, cls, false);
+                break;
+            default:
+                handled = false;
+                break;
+        }
+        return handled;
+    }
+
+    /**
+     * read
+     * read and resolve the method data called from ClassData.
+     * Precondition: NumFields has already been read from the stream.
+     */
+    public void read(DataInputStream in) throws IOException {
+        // read the Methods CP indexes
+        access = in.readUnsignedShort(); // & MM_METHOD; // Q
+        name_cpx = in.readUnsignedShort();
+        sig_cpx = in.readUnsignedShort();
+        TraceUtils.traceln(2,"MethodData: {modifiers}: " + Modifiers.toString(access, CF_Context.CTX_METHOD),
+                            "      MethodData: name[" + name_cpx + "]=" + cls.pool.getString(name_cpx) + " sig[" + sig_cpx + "]=" + cls.pool.getString(sig_cpx));
+        // Read the attributes
+        readAttributes(in);
+    }
+
+    private void readExceptions(DataInputStream in) throws IOException {
+        // this is not really a CodeAttr attribute, it's part of the CodeAttr
+        int exc_table_len = in.readUnsignedShort();
+        TraceUtils.traceln(3,"ExceptionsAttr[" + exc_table_len + "]");
+        exc_table = new int[exc_table_len];
+        for (int l = 0; l < exc_table_len; l++) {
+            int exc = in.readShort();
+            TraceUtils.traceln(4,"throws:#" + exc);
+            exc_table[l] = exc;
+        }
+    }
+
+    private void readMethodParameters(DataInputStream in) throws IOException {
+        // this is not really a CodeAttr attribute, it's part of the CodeAttr
+        int num_params = in.readUnsignedByte();
+        TraceUtils.traceln(3,"MethodParametersAttr[" + num_params + "]");
+        paramNames = new ArrayList<>(num_params);
+        for (int l = 0; l < num_params; l++) {
+            short pname_cpx = (short) in.readUnsignedShort();
+            int paccess = in.readUnsignedShort();
+            TraceUtils.traceln(4,"P[" + l + "] ={ name[" + pname_cpx + "]: " + cls.pool.getString(pname_cpx)
+                    + " modifiers [" + paccess + "]: " + Modifiers.toString(paccess, CF_Context.CTX_METHOD) + "}");
+            paramNames.add(l, new ParamNameData(pname_cpx, paccess));
+        }
+    }
+
+    /**
+     * printPAnnotations
+     * <p>
+     * prints the parameter annotations for this method. called from CodeAttr (since JASM
+     * code integrates the PAnnotation Syntax inside the method body).
+     */
+    // This is called from the CodeAttr
+    public void printPAnnotations() throws IOException {
+        int visSize = 0;
+        int invisSize = 0;
+        int pNumSize = 0;
+
+        if (visibleParameterAnnotations != null) {
+            visSize = visibleParameterAnnotations.numParams();
+        }
+        if (invisibleParameterAnnotations != null) {
+            invisSize = invisibleParameterAnnotations.numParams();
+        }
+        if (paramNames != null) {
+            pNumSize = paramNames.size();
+        }
+
+        int maxParams;
+        maxParams = (pNumSize > invisSize) ? pNumSize : invisSize;
+        maxParams = (visSize > maxParams) ? visSize : maxParams;
+
+        for (int paramNum = 0; paramNum < maxParams; paramNum++) {
+            ArrayList<AnnotationData> visAnnots = null;
+            if (visibleParameterAnnotations != null && paramNum < visSize) {
+                visAnnots = visibleParameterAnnotations.get(paramNum);
+            }
+            ArrayList<AnnotationData> invisAnnots = null;
+            if (invisibleParameterAnnotations != null && paramNum < invisSize) {
+                invisAnnots = invisibleParameterAnnotations.get(paramNum);
+            }
+            ParamNameData pname = (paramNames == null) ? null : paramNames.get(paramNum);
+
+            boolean nullAnnots = ((visAnnots == null) && (invisAnnots == null));
+            if (pname != null && pname.name_cpx == 0) {
+                pname = null;
+            }
+
+            // Print the Param number (header)
+            if ((pname != null) || !nullAnnots) {
+                out.print("\t" + paramNum + ": ");
+            } else {
+                continue;
+            }
+
+            boolean firstTime = true;
+
+            // Print the Parameter name
+            if (pname != null) {
+                out.print(Token.PARAM_NAME.parseKey());
+                out.print(Token.LBRACE.parseKey());
+                out.print(cls.pool.getString(pname.name_cpx));
+                out.print(" ");
+                out.print(Modifiers.toString(pname.access, CF_Context.CTX_METHOD));
+                out.print(Token.RBRACE.parseKey());
+                out.print(" ");
+            }
+
+            // Print any visible param annotations
+            if (visAnnots != null) {
+                for (AnnotationData annot : visAnnots) {
+                    if (!firstTime) {
+                        out.print("\t   ");
+                    }
+                    annot.print(out, getIndentString());
+//                    out.println();
+                    firstTime = false;
+                }
+            }
+
+            // Print any invisible param annotations
+            if (invisAnnots != null) {
+                for (AnnotationData annot : invisAnnots) {
+                    if (!firstTime) {
+                        out.print("\t   ");
+                    }
+                    annot.print(out, getIndentString());
+//                    out.println();
+                    firstTime = false;
+                }
+            }
+
+            // Reset the line, if there were parameters
+            if ((pname != null) || !nullAnnots) {
+                out.println();
+            }
+
+        }
+
+    }
+
+    /**
+     * Prints the method data to the current output stream. called from ClassData.
+     */
+    @Override
+    public void print() throws IOException {
+
+        printAnnotations(getIndentString());
+
+        out.print(getIndentString() + Modifiers.accessString(access, CF_Context.CTX_METHOD));
+
+        if (isSynthetic) {
+            out.print(Token.SYNTHETIC.parseKey() + " ");
+        }
+        if (isDeprecated) {
+            out.print(Token.DEPRECATED.parseKey() + " ");
+        }
+        out.print(Token.METHODREF.parseKey() + " ");
+
+        if (pr_cpx) {
+            // print the CPX method descriptor
+            out.print("#" + name_cpx + ":#" + sig_cpx +
+                    ((code == null && exc_table == null && defaultAnnotation == null) ? ";" : "") +
+                    "\t // " + cls.pool.getName(name_cpx) + ":" + cls.pool.getName(sig_cpx));
+        } else {
+            out.print(cls.pool.getName(name_cpx) + ":" + cls.pool.getName(sig_cpx) +
+                    ((code == null && exc_table == null && defaultAnnotation == null) ? ";" : ""));
+        }
+        // followed by default annotation
+        if (defaultAnnotation != null) {
+            out.print(" default { ");
+            defaultAnnotation.print(out, getIndentString());
+            out.print(" }" + ((code == null && exc_table == null) ? ";" : " "));
+        }
+        // followed by exception table
+        printExceptionTable();
+
+        if (code != null) {
+            code.print();
+        } else {
+            if( exc_table != null ) {
+                out.print(';');
+            }
+            out.println();
+        }
+    }
+
+    private void printExceptionTable() {
+        if (exc_table != null) {
+            out.print("\n\tthrows ");
+            int len = exc_table.length;
+            for (int exceptNum = 0; exceptNum < len; exceptNum++) {
+                out.print(cls.pool.getClassName(exc_table[exceptNum]));
+                if (exceptNum < len - 1) {
+                    out.print(", ");
+                }
+            }
+        }
+    }
+
+    /**
+     * MethodParamData
+     */
+    class ParamNameData {
+
+        public int access;
+        public int name_cpx;
+
+        public ParamNameData(int name, int access) {
+            this.access = access;
+            this.name_cpx = name;
+        }
+    }
+} // end MethodData
diff a/test/lib/org/openjdk/asmtools/jdis/ModuleData.java b/test/lib/org/openjdk/asmtools/jdis/ModuleData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/ModuleData.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2016, 2020, 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 org.openjdk.asmtools.jdis;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.*;
+
+import org.openjdk.asmtools.common.Module;
+import org.openjdk.asmtools.common.Tool;
+import org.openjdk.asmtools.jasm.JasmTokens;
+
+import static org.openjdk.asmtools.jdis.Main.i18n;
+
+/**
+ *  The module attribute data.
+ */
+public class ModuleData {
+
+  // internal references
+  private final Tool tool;
+
+  private ConstantPool pool;
+  private PrintWriter out;
+  private Module module;
+
+  public ModuleData(ClassData clsData) {
+    this.tool = clsData.tool;
+    this.pool = clsData.pool;
+    this.out = clsData.out;
+  }
+
+  public String getModuleName() {
+    return module == null ? "N/A" : module.getModuleName();
+  }
+
+  public String getModuleVersion() { return module.getModuleVersion();  }
+
+  public String getModuleHeader() {
+    if ( module == null ) {
+      return "N/A";
+    } else {
+      StringBuilder sb = new StringBuilder(module.getModuleFlags());
+      sb.append(JasmTokens.Token.MODULE.parseKey()).append(" ");
+      sb.append(module.getModuleName());
+      if (module.getModuleVersion() != null)
+        sb.append("// @").append(module.getModuleVersion());
+      return sb.toString();
+    }
+  }
+
+  /**
+   * Reads and resolve the method's attribute data called from ClassData.
+   */
+  public void read(DataInputStream in) throws IOException {
+    int index, moduleFlags, versionIndex;
+    String moduleName, version;
+    Module.Builder builder;
+    try {
+    // u2 module_name_index;
+    index = in.readUnsignedShort();
+    moduleName = pool.getModule(index);
+    // u2 module_flags;
+    moduleFlags = in.readUnsignedShort();
+    // u2 module_version_index;
+    versionIndex = in.readUnsignedShort();
+    version = pool.getString(versionIndex);
+    builder = new Module.Builder(moduleName, moduleFlags, version);
+    } catch (IOException ioe) {
+      tool.error(i18n.getString("jdis.error.invalid_header"));
+      throw ioe;
+    }
+
+    try {
+      int requires_count = in.readUnsignedShort();
+      for (int i = 0; i < requires_count; i++) {
+        index = in.readUnsignedShort();
+        int requiresFlags = in.readUnsignedShort();
+        versionIndex = in.readUnsignedShort();
+
+        moduleName = pool.getModule(index);
+        version = pool.getString(versionIndex);
+        builder.require(moduleName, requiresFlags, version);
+      }
+    } catch (IOException ioe) {
+      tool.error(i18n.getString("jdis.error.invalid_requires"));
+      throw ioe;
+    }
+
+    try {
+      int exports_count = in.readUnsignedShort();
+      if (exports_count > 0) {
+        for (int i = 0; i < exports_count; i++) {
+          index = in.readUnsignedShort();
+          String packageName = pool.getPackage(index);
+          int exportsFlags = in.readUnsignedShort();
+          int exports_to_count = in.readUnsignedShort();
+          if (exports_to_count > 0) {
+            Set<String> targets = new HashSet<>(exports_to_count);
+            for (int j = 0; j < exports_to_count; j++) {
+              int exports_to_index = in.readUnsignedShort();
+              targets.add(pool.getModule(exports_to_index));
+            }
+            builder.exports(packageName, exportsFlags, targets);
+          } else {
+            builder.exports(packageName, exportsFlags);
+          }
+        }
+      }
+    } catch (IOException ioe) {
+      tool.error(i18n.getString("jdis.error.invalid_exports"));
+      throw ioe;
+    }
+
+    try {
+      int opens_count = in.readUnsignedShort();
+      if (opens_count > 0) {
+        for (int i = 0; i < opens_count; i++) {
+          index = in.readUnsignedShort();
+          String packageName = pool.getPackage(index);
+          int opensFlags = in.readUnsignedShort();
+          int opens_to_count = in.readUnsignedShort();
+          if (opens_to_count > 0) {
+            Set<String> targets = new HashSet<>(opens_to_count);
+            for (int j = 0; j < opens_to_count; j++) {
+              int opens_to_index = in.readUnsignedShort();
+              targets.add(pool.getModule(opens_to_index));
+            }
+            builder.opens(packageName, opensFlags, targets);
+          } else {
+            builder.opens(packageName, opensFlags);
+          }
+        }
+      }
+    } catch (IOException ioe) {
+      tool.error(i18n.getString("jdis.error.invalid_opens"));
+      throw ioe;
+    }
+
+    try {
+      int uses_count = in.readUnsignedShort();
+      if (uses_count > 0) {
+        for (int i = 0; i < uses_count; i++) {
+          index = in.readUnsignedShort();
+          String serviceName = pool.getClassName(index);
+          builder.uses(serviceName);
+        }
+      }
+    } catch (IOException ioe) {
+      tool.error(i18n.getString("jdis.error.invalid_uses"));
+      throw ioe;
+    }
+
+    try {
+      int provides_count = in.readUnsignedShort();
+      if (provides_count > 0) {
+        for (int i = 0; i < provides_count; i++) {
+          index = in.readUnsignedShort();
+          String serviceName = pool.getClassName(index);
+          int provides_with_count = in.readUnsignedShort();
+          Set<String> implNames = new HashSet<>(provides_with_count);
+          for (int j = 0; j < provides_with_count; j++) {
+            int provides_with_index = in.readUnsignedShort();
+            implNames.add(pool.getClassName(provides_with_index));
+          }
+          builder.provides(serviceName, implNames);
+        }
+      }
+    } catch (IOException ioe) {
+      tool.error(i18n.getString("jdis.error.invalid_provides"));
+      throw ioe;
+    }
+    module = builder.build();
+  }
+
+  /* Print Methods */
+  public void print() {
+    if (module != null)
+      out.println(module.toString());
+  }
+}
diff a/test/lib/org/openjdk/asmtools/jdis/NestHostData.java b/test/lib/org/openjdk/asmtools/jdis/NestHostData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/NestHostData.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2018, 2020, 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 org.openjdk.asmtools.jdis;
+
+
+import org.openjdk.asmtools.jasm.JasmTokens;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * The NestHost attribute data
+ * <p>
+ * since class file 55.0 (JEP 181)
+ */
+public class NestHostData extends Indenter{
+    ClassData cls;
+    int host_class_index;
+    private Options options = Options.OptionObject();
+
+    public NestHostData(ClassData cls) {
+        this.cls = cls;
+    }
+
+    public NestHostData read(DataInputStream in, int attribute_length) throws IOException, ClassFormatError {
+        if (attribute_length != 2) {
+            throw new ClassFormatError("ATT_NestHost: Invalid attribute length");
+        }
+        host_class_index = in.readUnsignedShort();
+        return this;
+    }
+
+    public void print() {
+        boolean pr_cpx = options.contains(Options.PR.CPX);
+        cls.out.print(getIndentString() + JasmTokens.Token.NESTHOST.parseKey() + " ");
+        if (pr_cpx) {
+            cls.out.print("#" + host_class_index + "; //");
+        }
+        if (pr_cpx) {
+            cls.pool.PrintConstant(cls.out, host_class_index);
+            cls.out.println();
+        } else {
+            cls.out.println(cls.pool.StringValue(host_class_index) + ";");
+        }
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jdis/NestMembersData.java b/test/lib/org/openjdk/asmtools/jdis/NestMembersData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/NestMembersData.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2018, 2020, 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 org.openjdk.asmtools.jdis;
+
+import org.openjdk.asmtools.jasm.JasmTokens;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * The NestMembers attribute data
+ * <p>
+ * JEP 181 (Nest-based Access Control): class file 55.0
+ * NestMembers_attribute {
+ * u2 attribute_name_index;
+ * u4 attribute_length;
+ * u2 number_of_classes;
+ * u2 classes[number_of_classes];
+ * }
+ */
+public class NestMembersData extends ClassArrayData {
+    public NestMembersData(ClassData cls) {
+        super(cls, JasmTokens.Token.NESTMEMBERS.parseKey());
+    }
+
+    public NestMembersData read(DataInputStream in, int attribute_length) throws IOException, ClassFormatError {
+        return (NestMembersData) super.read(in, attribute_length);
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jdis/Options.java b/test/lib/org/openjdk/asmtools/jdis/Options.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/Options.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jdis;
+
+import java.util.EnumSet;
+
+/**
+ * The singleton class to share global options among jdis classes.
+ */
+public class Options {
+
+    public static final int BODY_INDENT = 2;
+
+    /* Options Fields */
+    private static Options ref;
+
+     public enum PR {
+
+        CP, // print Constant Pool
+        LNT, // print Line Number table
+        PC, // print Program Counter - for all instr
+        LABS, // print Labels (as identifiers)
+        CPX, // print CP index along with arguments
+        SRC, // print Source Line as comment
+        HEX, // print numbers as hexadecimals
+        VAR, // print local variables declarations
+        DEBUG;  // Debug flag
+    };
+
+    static private final EnumSet<PR> JASM = EnumSet.<PR>of(PR.LABS); // default options
+    static private final EnumSet<PR> CODE = EnumSet.<PR>of(
+            PR.CP,
+            PR.LNT,
+            PR.PC,
+            PR.CPX,
+            PR.VAR
+    );
+
+    static private EnumSet<PR> printOptions = JASM;
+    /*-------------------------------------------------------- */
+
+    private Options() {
+    }
+
+    public static Options OptionObject() {
+        if (ref == null) {
+            ref = new Options();
+        }
+        return ref;
+    }
+
+    public void set(PR val) {
+        printOptions.add(val);
+    }
+
+    public void setCodeOptions() {
+        printOptions.addAll(CODE);
+    }
+
+    public boolean contains(PR val) {
+        return printOptions.contains(val);
+    }
+
+    public boolean debug() {
+        return printOptions.contains(PR.DEBUG);
+    }
+
+    @Override
+    public String toString() {
+        return printOptions.toString();
+    }
+
+}
diff a/test/lib/org/openjdk/asmtools/jdis/ParameterAnnotationData.java b/test/lib/org/openjdk/asmtools/jdis/ParameterAnnotationData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/ParameterAnnotationData.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 1996, 2014, 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 org.openjdk.asmtools.jdis;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ *
+ */
+public class ParameterAnnotationData {
+    /*-------------------------------------------------------- */
+    /* AnnotData Fields */
+
+    private boolean invisible = false;
+    private static final String initialTab = "";
+    private ArrayList<ArrayList<AnnotationData>> array = null;
+
+    private ClassData cls;
+    /*-------------------------------------------------------- */
+
+    public ParameterAnnotationData(ClassData cls, boolean invisible) {
+        this.cls = cls;
+        this.invisible = invisible;
+    }
+
+    public int numParams() {
+        if (array == null) {
+            return 0;
+        }
+
+        return array.size();
+    }
+
+    public ArrayList<AnnotationData> get(int i) {
+        return array.get(i);
+    }
+
+    public void read(DataInputStream in) throws IOException {
+        int numParams = in.readByte();
+        TraceUtils.traceln("             ParameterAnnotationData[" + numParams + "]");
+        array = new ArrayList<>(numParams);
+        for (int paramNum = 0; paramNum < numParams; paramNum++) {
+
+            int numAnnots = in.readShort();
+            TraceUtils.traceln("             Param#[" + paramNum + "]: numAnnots=" + numAnnots);
+
+            if (numAnnots > 0) {
+                // read annotation
+                ArrayList<AnnotationData> p_annots = new ArrayList<>(numAnnots);
+                for (int annotIndex = 0; annotIndex < numAnnots; annotIndex++) {
+                    AnnotationData annot = new AnnotationData(invisible, cls);
+                    annot.read(in);
+                    p_annots.add(annot);
+                }
+                array.add(paramNum, p_annots);
+            } else {
+                array.add(paramNum, null);
+            }
+        }
+    }
+
+    // Don't need to do this --
+    // we need to print annotations (both vis and invisible) per each param number
+    public void print(PrintWriter out, String tab) {
+        if (array != null && array.size() > 0) {
+            out.println();
+            int paramNum = 0;
+            for (ArrayList<AnnotationData> p_annot : array) {
+                if (p_annot != null && p_annot.size() > 0) {
+                    out.print("\t" + paramNum + ": ");
+                    boolean firstTime = true;
+                    for (AnnotationData annot : p_annot) {
+                        if (!firstTime) {
+                            out.print("\t   ");
+                        }
+                        annot.print(out, initialTab);
+                        firstTime = false;
+                    }
+                }
+
+                paramNum += 1;
+                out.println();
+            }
+        }
+    }
+
+}
diff a/test/lib/org/openjdk/asmtools/jdis/PermittedSubclassesData.java b/test/lib/org/openjdk/asmtools/jdis/PermittedSubclassesData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/PermittedSubclassesData.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.asmtools.jdis;
+
+import org.openjdk.asmtools.jasm.JasmTokens;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * The PermittedSubclasses attribute data
+ * <p>
+ * JEP 360 (Sealed types): class file 59.65535
+ * PermittedSubclasses_attribute {
+ * u2 attribute_name_index;
+ * u4 attribute_length;
+ * u2 number_of_classes;
+ * u2 classes[number_of_classes];
+ * }
+ */
+public class PermittedSubclassesData extends ClassArrayData {
+    public PermittedSubclassesData(ClassData cls) {
+        super(cls, JasmTokens.Token.PERMITTEDSUBCLASSES.parseKey());
+    }
+
+    public PermittedSubclassesData read(DataInputStream in, int attribute_length) throws IOException, ClassFormatError {
+        return (PermittedSubclassesData) super.read(in, attribute_length);
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jdis/PreloadData.java b/test/lib/org/openjdk/asmtools/jdis/PreloadData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/PreloadData.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2022, 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 org.openjdk.asmtools.jdis;
+
+import org.openjdk.asmtools.jasm.JasmTokens;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+public class PreloadData extends ClassArrayData {
+    public PreloadData(ClassData cls) {
+        super(cls, JasmTokens.Token.PRELOAD.parseKey());
+    }
+
+    public PreloadData read(DataInputStream in, int attribute_length) throws IOException, ClassFormatError {
+        return (PreloadData) super.read(in, attribute_length);
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jdis/RecordData.java b/test/lib/org/openjdk/asmtools/jdis/RecordData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/RecordData.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2019, 2020, 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 org.openjdk.asmtools.jdis;
+
+import org.openjdk.asmtools.jasm.JasmTokens;
+import org.openjdk.asmtools.jasm.Tables;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.lang.String.format;
+import static org.openjdk.asmtools.jasm.JasmTokens.Token.*;
+import static org.openjdk.asmtools.jdis.TraceUtils.traceln;
+
+/**
+ * The Record attribute data
+ * <p>
+ * since class file 58.65535 (JEP 359)
+ */
+public class RecordData extends  Indenter {
+
+
+    private final ClassData cls;
+    private List<Component> components;
+
+    public RecordData(ClassData cls) {
+        this.cls = cls;
+    }
+
+    public RecordData read(DataInputStream in) throws IOException {
+        int count = in.readUnsignedShort();
+        traceln("components=" + count);
+        components = new ArrayList<>(count);
+        for (int i = 0; i < count; i++) {
+            components.add(new Component(cls).read(in));
+        }
+        return this;
+    }
+
+    /**
+     * Prints the record data to the current output stream. called from ClassData.
+     */
+    public void print() throws IOException {
+        int count = components.size();
+        if (count > 0) {
+            cls.out.println(getIndentString() + RECORD.parseKey() + getIndentString() + LBRACE.parseKey());
+            for (int i = 0; i < count; i++) {
+                Component cn = components.get(i);
+                cn.setIndent(indent() * 2);
+                if (i != 0 && cn.getAnnotationsCount() > 0)
+                    cn.out.println();
+                cn.print();
+            }
+            cls.out.println(getIndentString() + RBRACE.parseKey());
+            cls.out.println();
+        }
+    }
+
+    private class Component extends MemberData {
+        // CP index to the name
+        private int name_cpx;
+        // CP index to the type descriptor
+        private int type_cpx;
+
+        public Component(ClassData cls) {
+            super(cls);
+            memberType = "RecordData";
+        }
+
+        @Override
+        protected boolean handleAttributes(DataInputStream in, Tables.AttrTag attrtag, int attrlen) throws IOException {
+            // Read the Attributes
+            boolean handled = true;
+            switch (attrtag) {
+                case ATT_Signature:
+                    if( signature != null ) {
+                        traceln("Record attribute:  more than one attribute Signature are in component.attribute_info_attributes[attribute_count]");
+                        traceln("Last one will be used.");
+                    }
+                    signature = new SignatureData(cls).read(in, attrlen);
+                    break;
+                default:
+                    handled = false;
+                    break;
+            }
+            return handled;
+        }
+
+        /**
+         * Read and resolve the component data called from ClassData.
+         */
+        public Component read(DataInputStream in) throws IOException {
+            // read the Component CP indexes
+            name_cpx = in.readUnsignedShort();
+            type_cpx = in.readUnsignedShort();
+            traceln(2, "RecordComponent: name[" + name_cpx + "]=" + cls.pool.getString(name_cpx)
+                    + " descriptor[" + type_cpx + "]=" + cls.pool.getString(type_cpx));
+            // Read the attributes
+            readAttributes(in);
+            return this;
+        }
+
+        /**
+         * Prints the component data to the current output stream. called from RecordData.
+         */
+        public void print() throws IOException {
+            // print component's attributes
+                super.printAnnotations(getIndentString());
+            // print component
+            StringBuilder bodyPrefix = new StringBuilder(getIndentString());
+            StringBuilder tailPrefix = new StringBuilder();
+            if (isSynthetic) {
+                bodyPrefix.append(JasmTokens.Token.SYNTHETIC.parseKey()).append(' ');
+            }
+            if (isDeprecated) {
+                bodyPrefix.append(JasmTokens.Token.DEPRECATED.parseKey()).append(' ');
+            }
+            // component
+            bodyPrefix.append(JasmTokens.Token.COMPONENT.parseKey()).append(' ');
+
+            printVar(bodyPrefix, tailPrefix,name_cpx, type_cpx);
+        }
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jdis/SignatureData.java b/test/lib/org/openjdk/asmtools/jdis/SignatureData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/SignatureData.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2018, 2020, 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 org.openjdk.asmtools.jdis;
+
+
+import org.openjdk.asmtools.jasm.Tables;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+import static java.lang.String.format;
+
+/**
+ * The Signature attribute data
+ * <p>
+ * since class file 49.0
+ */
+public class SignatureData {
+    ClassData cls;
+    int signature_index;
+    private Options options = Options.OptionObject();
+
+    public SignatureData(ClassData cls) {
+        this.cls = cls;
+    }
+
+    public SignatureData read(DataInputStream in, int attribute_length) throws IOException, ClassFormatError {
+        if (attribute_length != 2) {
+            throw new ClassFormatError(format("%s: Invalid attribute length #%d", Tables.AttrTag.ATT_Signature.printval(), attribute_length));
+        }
+        signature_index = in.readUnsignedShort();
+        return this;
+    }
+
+    public void print(String bodyPrefix, String commentPrefix) {
+        boolean pr_cpx = options.contains(Options.PR.CPX);
+        if (pr_cpx) {
+            cls.out.print(format("%s#%d%s%s", bodyPrefix, signature_index, commentPrefix, cls.pool.StringValue(signature_index)));
+        } else {
+            cls.out.print(format("%s%s%s", bodyPrefix, cls.pool.getName(signature_index), commentPrefix));
+        }
+    }
+
+    @Override
+    public String toString() {
+        return format("signature[%d]=%s", signature_index, cls.pool.StringValue(signature_index));
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jdis/StackMapData.java b/test/lib/org/openjdk/asmtools/jdis/StackMapData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/StackMapData.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jdis;
+
+import static java.lang.String.format;
+import static org.openjdk.asmtools.jasm.Tables.*;
+import static org.openjdk.asmtools.jdis.TraceUtils.mapToHexString;
+import static org.openjdk.asmtools.jdis.TraceUtils.traceln;
+
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+
+/**
+ * represents one entry of StackMap attribute
+ */
+class StackMapData {
+    static int prevFramePC = 0;
+    boolean isStackMapTable = false;
+    StackMapFrameType stackFrameType = null;
+    int start_pc;
+    int[] lockMap;
+    int[] stackMap;
+
+    public StackMapData(CodeData code, DataInputStream in) throws IOException {
+        start_pc = in.readUnsignedShort();
+        lockMap = readMap(code, in);
+        stackMap = readMap(code, in);
+        traceln(2, format("stack_map_entry:pc=%d numloc=%s  numstack=%s",
+                start_pc, mapToHexString(lockMap), mapToHexString(stackMap)));
+    }
+
+    public StackMapData(CodeData code, DataInputStream in,
+            boolean isStackMapTable) throws IOException {
+        this.isStackMapTable = isStackMapTable;
+        int ft_val = in.readUnsignedByte();
+        StackMapFrameType frame_type = stackMapFrameType(ft_val);
+        int offset = 0;
+        switch (frame_type) {
+            case SAME_FRAME:
+                // type is same_frame;
+                offset = ft_val;
+                traceln(2, format("same_frame=%d", ft_val));
+                break;
+            case SAME_FRAME_EX:
+                // type is same_frame_extended;
+                offset = in.readUnsignedShort();
+                traceln(2, format("same_frame_extended=%d, offset=%d", ft_val, offset));
+                break;
+            case SAME_LOCALS_1_STACK_ITEM_FRAME:
+                // type is same_locals_1_stack_item_frame
+                offset = ft_val - 64;
+                stackMap = readMapElements(code, in, 1);
+                traceln(2, format("same_locals_1_stack_item_frame=%d, offset=%d, numstack=%s",
+                        ft_val, offset, mapToHexString(stackMap)));
+                break;
+            case SAME_LOCALS_1_STACK_ITEM_EXTENDED_FRAME:
+                // type is same_locals_1_stack_item_frame_extended
+                offset = in.readUnsignedShort();
+                stackMap = readMapElements(code, in, 1);
+                traceln(2, format("same_locals_1_stack_item_frame_extended=%d, offset=%d, numstack=%s",
+                        ft_val, offset, mapToHexString(stackMap)));
+                break;
+            case CHOP_1_FRAME:
+            case CHOP_2_FRAME:
+            case CHOP_3_FRAME:
+                // type is chop_frame
+                offset = in.readUnsignedShort();
+                traceln(2, format("chop_frame=%d offset=%d", ft_val, offset));
+                break;
+            case APPEND_FRAME:
+                // type is append_frame
+                offset = in.readUnsignedShort();
+                lockMap = readMapElements(code, in, ft_val - 251);
+                traceln(2, format("append_frame=%d offset=%d numlock=%s",
+                        ft_val, offset, mapToHexString(lockMap)));
+                break;
+            case FULL_FRAME:
+                // type is full_frame
+                offset = in.readUnsignedShort();
+                lockMap = readMap(code, in);
+                stackMap = readMap(code, in);
+                traceln(2, format("full_frame=%d offset=%d numloc=%s  numstack=%s",
+                        ft_val, offset, mapToHexString(lockMap), mapToHexString(stackMap)));
+                break;
+            default:
+                TraceUtils.traceln("incorrect frame_type argument");
+
+        }
+        stackFrameType = frame_type;
+        start_pc = prevFramePC == 0 ? offset : prevFramePC + offset + 1;
+        prevFramePC = start_pc;
+    }
+
+    private int[] readMap(CodeData code, DataInputStream in) throws IOException {
+        int num = in.readUnsignedShort();
+        return readMapElements(code, in, num);
+    }
+
+    private int[] readMapElements(CodeData code, DataInputStream in, int num) throws IOException {
+        int[] map = new int[num];
+        for (int k = 0; k < num; k++) {
+            int mt_val = 0;
+            try {
+                mt_val = in.readUnsignedByte();
+            } catch (EOFException eofe) {
+                throw eofe;
+            }
+            StackMapType maptype = stackMapType(mt_val, null);
+            switch (maptype) {
+                case ITEM_Object:
+                    mt_val = mt_val | (in.readUnsignedShort() << 8);
+                    break;
+                case ITEM_NewObject: {
+                    int pc = in.readUnsignedShort();
+                    code.get_iAtt(pc).referred = true;
+                    mt_val = mt_val | (pc << 8);
+                    break;
+                }
+            }
+            map[k] = mt_val;
+        }
+        return map;
+    }
+
+}
diff a/test/lib/org/openjdk/asmtools/jdis/TextLines.java b/test/lib/org/openjdk/asmtools/jdis/TextLines.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/TextLines.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jdis;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+
+/**
+ * A container for the java sources tied to an jasm output when -sl in on
+ */
+public class TextLines {
+
+    final Path file;
+    List<String> lines;
+
+    public TextLines(Path directory, String sourceFileName) {
+        file = directory == null ? Paths.get(sourceFileName) : directory.resolve(sourceFileName);
+        try {
+            lines = Files.readAllLines(file, StandardCharsets.UTF_8);
+        } catch (IOException ignore) {}
+    }
+
+    public String getLine(int index) {
+        if( lines != null ) {
+            if (index < 1 || index >= lines.size()) {
+                return String.format("Line number %d is out of range in \"%s\"", index, file);
+            }
+            return lines.get(index - 1);
+        }
+        return String.format("\"%s\" not found", file);
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jdis/TraceUtils.java b/test/lib/org/openjdk/asmtools/jdis/TraceUtils.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/TraceUtils.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jdis;
+
+import org.openjdk.asmtools.asmutils.HexUtils;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+import static java.lang.String.format;
+
+public class TraceUtils {
+
+    public static String prefixString = "\t";
+
+    public static void trace(String s) {
+        if (!(Options.OptionObject()).debug()) {
+            return;
+        }
+        System.out.print(s);
+    }
+
+    public static void trace(int prefixLength, String s) {
+        if (!(Options.OptionObject()).debug()) {
+            return;
+        }
+        System.out.print((prefixLength > 0) ? new String(new char[prefixLength]).replace("\0", prefixString) + s : s);
+    }
+
+    public static void traceln(String s) {
+        if (!(Options.OptionObject()).debug()) {
+            return;
+        }
+        System.out.println(s);
+    }
+
+    public static void traceln(String... lines) {
+        if (!(Options.OptionObject()).debug()) {
+            return;
+        }
+
+        if (lines.length == 0) {
+            System.out.println();
+        } else {
+            for (String s : lines) {
+                System.out.println(s);
+            }
+        }
+    }
+
+    public static void traceln(int prefixLength, String s) {
+        if (!(Options.OptionObject()).debug()) {
+            return;
+        }
+        System.out.println((prefixLength > 0) ? new String(new char[prefixLength]).replace("\0", prefixString) + s : s);
+    }
+
+    public static void traceln(int prefixLength, String... lines) {
+        if (!(Options.OptionObject()).debug()) {
+            return;
+        }
+        if (lines.length == 0) {
+            System.out.println();
+        } else {
+            String prefix = (prefixLength > 0) ? new String(new char[prefixLength]).replace("\0", prefixString) : "";
+            for (String s : lines) {
+                System.out.println(prefix + s);
+            }
+        }
+    }
+
+    public static String mapToHexString(int[] array) {
+        return format("%d %s",
+                array.length,
+                Arrays.stream(array).
+                        mapToObj(val -> HexUtils.toHex(val)).
+                        collect(Collectors.joining(" ")));
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jdis/TrapData.java b/test/lib/org/openjdk/asmtools/jdis/TrapData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/TrapData.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1996, 2018, 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 org.openjdk.asmtools.jdis;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * Exception table entry (JVMS 4.7.3 The Code Attribute) describes one exception handler
+ * in the code array {@link CodeData}.
+ */
+class TrapData {
+
+    int num;
+
+    // exception_table
+    int start_pc,       // u2
+        end_pc,         // u2
+        handler_pc,     // u2
+        catch_cpx;      // u2
+
+    public TrapData(DataInputStream in, int num) throws IOException {
+        this.num = num;
+        start_pc = in.readUnsignedShort();
+        end_pc = in.readUnsignedShort();
+        handler_pc = in.readUnsignedShort();
+        catch_cpx = in.readUnsignedShort();
+    }
+
+    /* returns recommended identifier
+     */
+    public String ident() {
+        return "t" + num;
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jdis/TypeAnnotationData.java b/test/lib/org/openjdk/asmtools/jdis/TypeAnnotationData.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/TypeAnnotationData.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 1996, 2020, 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 org.openjdk.asmtools.jdis;
+
+import org.openjdk.asmtools.jasm.TypeAnnotationTargetInfoData;
+import org.openjdk.asmtools.jasm.TypeAnnotationTypePathData;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import static org.openjdk.asmtools.jasm.TypeAnnotationTargetInfoData.*;
+import static org.openjdk.asmtools.jasm.TypeAnnotationTypes.*;
+
+/**
+ * Type Annotation data is a specific kind of AnnotationData. As well as the normal data
+ * items needed to present an annotation, Type annotations require a TargetInfo
+ * descriptor. This descriptor is based on a TargetType, and it optionally may contain a
+ * location descriptor (when the Type is embedded in a collection).
+ * <p>
+ * The TypeAnnotationData class is based on JDis's AnnotationData class, and contains the
+ * (jasm) class for representing TargetInfo.
+ */
+public class TypeAnnotationData extends AnnotationData {
+
+    private static TTVis TT_Visitor = new TTVis();
+    private TypeAnnotationTargetInfoData targetInfo;
+    private TypeAnnotationTypePathData typePath;
+
+    public TypeAnnotationData(boolean invisible, ClassData cls) {
+        super(invisible, cls);
+        targetInfo = null;
+        typePath  = new TypeAnnotationTypePathData();
+        visAnnotToken = "@T+";
+        invAnnotToken = "@T-";
+        dataName = "TypeAnnotationData";
+    }
+
+    @Override
+    public void read(DataInputStream in) throws IOException {
+
+        int ttype = in.readUnsignedByte();
+        ETargetType targetType = ETargetType.getTargetType(ttype);
+
+        if (targetType == null) {
+            // Throw some kind of error for bad target type index
+            throw new IOException("Bad target type: " + ttype + " in TypeAnnotationData");
+        }
+
+        // read the target info
+        TT_Visitor.init(in);
+        TT_Visitor.visitExcept(targetType);
+        targetInfo = TT_Visitor.getTargetInfo();
+
+        // read the target path info
+        int len = in.readUnsignedByte();
+        TraceUtils.traceln(4,"[TypeAnnotationData.read]: Reading Location (length = " + len + ").");
+        TraceUtils.trace(4,"[TypeAnnotationData.read]: [ ");
+        for (int i = 0; i < len; i++) {
+            int pathType = in.readUnsignedByte();
+            String pk = (getPathKind(pathType)).parseKey();
+            char pathArgIndex = (char) in.readUnsignedByte();
+            typePath.addTypePathEntry(new TypePathEntry(pathType, pathArgIndex));
+            TraceUtils.trace(" " + pk + "(" + pathType + "," + pathArgIndex + "), ");
+        }
+        TraceUtils.traceln("] ");
+        super.read(in);
+    }
+
+    @Override
+    protected void printBody(PrintWriter out, String tab) {
+        // For a type annotation, print out brackets,
+        // print out the (regular) annotation name/value pairs,
+        // then print out the target types.
+        out.print(" {");
+        super.printBody(out, "");
+        targetInfo.print(out, tab);
+        typePath.print(out, tab);
+        out.print(tab + "}");
+    }
+
+    /**
+     * TTVis
+     * <p>
+     * Target Type visitor, used for constructing the target-info within a type
+     * annotation. visitExcept() is the entry point. ti is the constructed target info.
+     */
+    private static class TTVis extends TypeAnnotationTargetVisitor {
+
+        private TypeAnnotationTargetInfoData targetInfo = null;
+        private IOException IOProb = null;
+        private DataInputStream in;
+
+        public void init(DataInputStream in) {
+            this.in = in;
+        }
+
+        public int scanByteVal() {
+            int val = 0;
+            try {
+                val = in.readUnsignedByte();
+            } catch (IOException e) {
+                IOProb = e;
+            }
+            return val;
+        }
+
+        public int scanShortVal() {
+            int val = 0;
+            try {
+                val = in.readUnsignedShort();
+            } catch (IOException e) {
+                IOProb = e;
+            }
+            return val;
+        }
+
+        //This is the entry point for a visitor that tunnels exceptions
+        public void visitExcept(ETargetType tt) throws IOException {
+            IOProb = null;
+            targetInfo = null;
+
+            TraceUtils.traceln(4,"Target Type: " + tt.parseKey());
+            visit(tt);
+
+            if (IOProb != null) {
+                throw IOProb;
+            }
+        }
+
+        public TypeAnnotationTargetInfoData getTargetInfo() {
+            return targetInfo;
+        }
+
+        private boolean error() {
+            return IOProb != null;
+        }
+
+        @Override
+        public void visit_type_param_target(ETargetType tt) {
+            TraceUtils.trace(4,"Type Param Target: ");
+            int byteval = scanByteVal(); // param index
+            TraceUtils.traceln("{ param_index: " + byteval + "}");
+            if (!error()) {
+                targetInfo = new type_parameter_target(tt, byteval);
+            }
+        }
+
+        @Override
+        public void visit_supertype_target(ETargetType tt) {
+            TraceUtils.trace(4,"SuperType Target: ");
+            int shortval = scanShortVal(); // type index
+            TraceUtils.traceln("{ type_index: " + shortval + "}");
+            if (!error()) {
+                targetInfo = new supertype_target(tt, shortval);
+            }
+        }
+
+        @Override
+        public void visit_typeparam_bound_target(ETargetType tt) {
+            TraceUtils.trace(4,"TypeParam Bound Target: ");
+            int byteval1 = scanByteVal(); // param index
+            if (error()) {
+                return;
+            }
+            int byteval2 = scanByteVal(); // bound index
+            if (error()) {
+                return;
+            }
+            TraceUtils.traceln("{ param_index: " + byteval1 + " bound_index: " + byteval2 + "}");
+            targetInfo = new type_parameter_bound_target(tt, byteval1, byteval2);
+        }
+
+        @Override
+        public void visit_empty_target(ETargetType tt) {
+            TraceUtils.traceln(4,"Empty Target: ");
+            if (!error()) {
+                targetInfo = new empty_target(tt);
+            }
+        }
+
+        @Override
+        public void visit_methodformalparam_target(ETargetType tt) {
+            TraceUtils.trace(4,"MethodFormalParam Target: ");
+            int byteval = scanByteVal(); // param index
+            TraceUtils.traceln("{ param_index: " + byteval + "}");
+            if (!error()) {
+                targetInfo = new formal_parameter_target(tt, byteval);
+            }
+        }
+
+        @Override
+        public void visit_throws_target(ETargetType tt) {
+            TraceUtils.trace(4,"Throws Target: ");
+            int shortval = scanShortVal(); // exception index
+            TraceUtils.traceln("{ exception_index: " + shortval + "}");
+            if (!error()) {
+                targetInfo = new throws_target(tt, shortval);
+            }
+        }
+
+        @Override
+        public void visit_localvar_target(ETargetType tt) {
+            TraceUtils.traceln(4,"LocalVar Target: ");
+            int tblsize = scanShortVal(); // table length (short)
+            if (error()) {
+                return;
+            }
+            localvar_target locvartab = new localvar_target(tt, tblsize);
+            targetInfo = locvartab;
+
+            for (int i = 0; i < tblsize; i++) {
+                int shortval1 = scanShortVal(); // startPC
+                if (error()) {
+                    return;
+                }
+                int shortval2 = scanShortVal(); // length
+                if (error()) {
+                    return;
+                }
+                int shortval3 = scanShortVal(); // CPX
+                TraceUtils.trace(4,"LocalVar[" + i + "]: ");
+                TraceUtils.traceln("{ startPC: " + shortval1 + ", length: " + shortval2 + ", CPX: " + shortval3 + "}");
+                locvartab.addEntry(shortval1, shortval2, shortval3);
+            }
+        }
+
+        @Override
+        public void visit_catch_target(ETargetType tt) {
+            TraceUtils.trace(4,"Catch Target: ");
+            int shortval = scanShortVal(); // catch index
+            TraceUtils.traceln("{ catch_index: " + shortval + "}");
+            if (!error()) {
+                targetInfo = new catch_target(tt, shortval);
+            }
+        }
+
+        @Override
+        public void visit_offset_target(ETargetType tt) {
+            TraceUtils.trace(4,"Offset Target: ");
+            int shortval = scanShortVal(); // offset index
+            TraceUtils.traceln("{ offset_index: " + shortval + "}");
+            if (!error()) {
+                targetInfo = new offset_target(tt, shortval);
+            }
+        }
+
+        @Override
+        public void visit_typearg_target(ETargetType tt) {
+            TraceUtils.trace(4,"TypeArg Target: ");
+            int shortval = scanShortVal(); // offset
+            if (error()) {
+                return;
+            }
+            int byteval = scanByteVal(); // type index
+            if (error()) {
+                return;
+            }
+            TraceUtils.traceln("{ offset: " + shortval + " type_index: " + byteval + "}");
+            targetInfo = new type_argument_target(tt, shortval, byteval);
+        }
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jdis/Utils.java b/test/lib/org/openjdk/asmtools/jdis/Utils.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/Utils.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 1996, 2014, 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 org.openjdk.asmtools.jdis;
+
+/**
+ *
+ */
+public class Utils {
+
+    static public String javaName(String name) {
+        if (name == null) {
+            return "null";
+        }
+        int len = name.length();
+        if (len == 0) {
+            return "\"\"";
+        }
+        char cc = '/';
+fullname:
+        { // xxx/yyy/zzz
+            for (int k = 0; k < len; k++) {
+                char c = name.charAt(k);
+                if (cc == '/') {
+                    if (!Character.isJavaIdentifierStart(c) && c != '-') {
+                        break fullname;
+                    }
+                } else if (c != '/') {
+                    if (!Character.isJavaIdentifierPart(c) && c != '-') {
+                        break fullname;
+                    }
+                }
+                cc = c;
+            }
+            return name;
+        }
+        return "\"" + name + "\"";
+    }
+
+    static public boolean isClassArrayDescriptor(String name) {
+        boolean retval = false;
+        if (name != null) {
+            if (name.startsWith("[")) {
+                retval = true;
+            }
+        }
+
+        return retval;
+    }
+
+    static public String commentString(String  str) {
+        return commentString(str,"// ");
+    }
+
+    static public String commentString(String  str, String prefix) {
+        return prefix + str.replace("\n", "\n" + prefix);
+    }
+
+}
diff a/test/lib/org/openjdk/asmtools/jdis/i18n.properties b/test/lib/org/openjdk/asmtools/jdis/i18n.properties
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/i18n.properties
@@ -0,0 +1,47 @@
+# Copyright (c) 2014, 2019, 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.
+
+jdis.usage=\
+Usage: java -jar asmtools.jar jdis [options] FILE.class... > FILE.jasm\n\
+where possible options include:
+
+jdis.opt.g=\
+\     -g   detailed output format
+jdis.opt.sl=\
+\     -sl  source lines in comments
+jdis.opt.v=\
+\     -v   debug mode
+jdis.opt.hx=\
+\     -hx  hexadecimal numbers
+jdis.opt.version=\
+\     -version   prints the version info
+
+jdis.error.invalid_option=invalid option: {0}
+jdis.error.cannot_read=cannot read {0}
+jdis.error.fatal_error=fatal error in file: {0}
+jdis.error.fatal_exception=fatal exception in file: {0}
+
+jdis.error.invalid_header=Invalid Module's attributes table
+jdis.error.invalid_requires=Invalid requires table
+jdis.error.invalid_exports=Invalid exports table
+jdis.error.invalid_opens=Invalid opens table
+jdis.error.invalid_uses=Invalid uses table
+jdis.error.invalid_provides=Invalid provides table
diff a/test/lib/org/openjdk/asmtools/jdis/iAtt.java b/test/lib/org/openjdk/asmtools/jdis/iAtt.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/iAtt.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 1996, 2017, 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 org.openjdk.asmtools.jdis;
+
+import org.openjdk.asmtools.jasm.RuntimeConstants;
+import static org.openjdk.asmtools.jasm.Tables.*;
+import static org.openjdk.asmtools.jasm.OpcodeTables.*;
+import org.openjdk.asmtools.jasm.Tables;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * instruction attributes
+ */
+class iAtt {
+
+    /*-------------------------------------------------------- */
+    /* iAtt Fields */
+    private Options options;
+
+    short lnum = 0;
+    boolean referred = false; // from some other instruction
+    ArrayList<CodeData.LocVarData> vars;
+    ArrayList<CodeData.LocVarData> endvars;
+    ArrayList<TrapData> handlers;
+    ArrayList<TrapData> traps;
+    ArrayList<TrapData> endtraps;
+    StackMapData stackMapEntry;
+    CodeData code;
+    ClassData cls;
+    PrintWriter out; // =cls.out;
+ /*-------------------------------------------------------- */
+
+    public iAtt(CodeData code) {
+        this.code = code;
+        this.cls = code.meth.cls;
+        out = cls.out;
+        options = cls.options;
+    }
+
+    void add_var(CodeData.LocVarData var) {
+        if (vars == null) {
+            vars = new ArrayList<>(4);
+        }
+        vars.add(var);
+    }
+
+    void add_endvar(CodeData.LocVarData endvar) {
+        if (endvars == null) {
+            endvars = new ArrayList<>(4);
+        }
+        endvars.add(endvar);
+    }
+
+    void add_trap(TrapData trap) {
+        if (traps == null) {
+            traps = new ArrayList<>(4);
+        }
+        traps.add(trap);
+    }
+
+    void add_endtrap(TrapData endtrap) {
+        if (endtraps == null) {
+            endtraps = new ArrayList<>(4);
+        }
+        endtraps.add(endtrap);
+    }
+
+    void add_handler(TrapData endtrap) {
+        if (handlers == null) {
+            handlers = new ArrayList<>(4);
+        }
+        handlers.add(endtrap);
+    }
+
+    public void printEnds() throws IOException {
+// prints additional information for instruction:
+//  end of local variable and trap scopes;
+        int len;
+        if ((endvars != null) && (options.contains(Options.PR.VAR))) {
+            len = endvars.size() - 1;
+            out.print("\t\tendvar");
+            for (CodeData.LocVarData line : endvars) {
+                out.print(" " + line.slot);
+                if (len-- > 0) {
+                    out.print(",");
+                }
+            }
+            out.println(";");
+        }
+
+        if (endtraps != null) {
+            len = endtraps.size() - 1;
+            out.print("\t\tendtry");
+            for (TrapData line : endtraps) {
+                out.print(" " + line.ident());
+                if (len-- > 0) {
+                    out.print(",");
+                }
+            }
+            out.println(";");
+        }
+    }
+
+    public void printBegins()
+            throws IOException {
+// prints additional information for instruction:
+// source line number;
+// start of exception handler;
+// begin of locvar and trap scopes;
+        boolean eitherOpt = options.contains(Options.PR.LNT) || options.contains(Options.PR.SRC);
+        boolean bothOpt = options.contains(Options.PR.LNT) && options.contains(Options.PR.SRC);
+        int k;
+
+        if ((lnum != 0) && eitherOpt) {
+            if (bothOpt) {
+                out.println("// " + lnum + ": " + cls.getSrcLine(lnum));
+            } else if (options.contains(Options.PR.LNT)) {
+                out.print(lnum);
+            } else if (options.contains(Options.PR.SRC)) {
+                out.println("// " + cls.getSrcLine(lnum));
+            }
+        }
+        out.print("\t");
+        if (handlers != null) {
+            for (TrapData line : handlers) {
+                out.print("\tcatch " + line.ident());
+                out.print(" " + cls.pool.getClassName(line.catch_cpx) + ";\n\t");
+            }
+        }
+        if (traps != null) {
+            int len = traps.size() - 1;
+            out.print("\ttry");
+            for (TrapData line : traps) {
+                out.print(" " + line.ident());
+                if (len-- > 0) {
+                    out.print(",");
+                }
+            }
+            out.print(";\n\t");
+        }
+        if ((vars != null) && options.contains(Options.PR.VAR)) {
+            for (CodeData.LocVarData line : vars) {
+                out.println("\tvar " + line.slot + "; // " + cls.pool.getName(line.name_cpx) + ":" + cls.pool.getName(line.sig_cpx));
+                out.print("\t");
+            }
+        }
+    }
+
+    public void printMapList(int[] map) throws IOException {
+        boolean pr_cpx = options.contains(Options.PR.CPX);
+
+        for (int k = 0; k < map.length; k++) {
+            int fullmaptype = map[k];
+            int mt_val = fullmaptype & 0xFF;
+            StackMapType maptype = stackMapType(mt_val, out);
+            int argument = fullmaptype >> 8;
+            switch (maptype) {
+                case ITEM_Object:
+                    if (pr_cpx) {
+                        out.print(" #" + argument);
+                    } else {
+                        out.print(" ");
+                        cls.pool.PrintConstant(out, argument);
+                    }
+                    break;
+                case ITEM_NewObject:
+                    if (pr_cpx) {
+                        out.print(" " + mt_val);
+                    } else {
+                        out.print(" " + maptype.printval());
+                    }
+                    out.print(" " + code.meth.lP + argument);
+                    break;
+                default:
+                    if (pr_cpx) {
+                        out.print(" " + mt_val);
+                    } else {
+                        out.print(" " + maptype.printval());
+                    }
+            }
+            out.print((k == (map.length - 1) ? ';' : ','));
+        }
+    }
+
+    public void printStackMap() throws IOException {
+        if (stackMapEntry == null) {
+            return;
+        }
+        boolean printed = false;
+        if (stackMapEntry.stackFrameType != null) {
+            out.print(Opcode.opc_stack_frame_type.parsekey()); //    opcNamesTab[opc_stackframetype]);
+            out.print(" " + stackMapEntry.stackFrameType.parsekey() + ';');
+            out.print("\n\t\t");
+            printed = true;
+        }
+        int[] map = stackMapEntry.lockMap;
+        if ((map != null) && (map.length > 0)) {
+            out.print(Opcode.opc_locals_map.parsekey());
+            printMapList(map);
+            out.print("\n\t\t");
+            printed = true;
+        }
+        map = stackMapEntry.stackMap;
+        if ((map != null) && (map.length > 0)) {
+            out.print(Opcode.opc_stack_map.parsekey());
+            printMapList(map);
+            out.print("\n\t\t");
+            printed = true;
+        }
+        if (!printed) {
+// empty attribute should be printed anyway - it should not
+// be eliminated after jdis/jasm cycle
+            out.print(Opcode.opc_locals_map.parsekey() + " ;\n\t\t");
+        }
+    }
+}
diff a/test/lib/org/openjdk/asmtools/jdis/uEscWriter.java b/test/lib/org/openjdk/asmtools/jdis/uEscWriter.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/jdis/uEscWriter.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 1996, 2014, 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 org.openjdk.asmtools.jdis;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+
+/**
+ *
+ */
+public class uEscWriter extends Writer {
+    /*-------------------------------------------------------- */
+    /* uEscWriter Fields */
+
+    static final char[] hexTable = "0123456789ABCDEF".toCharArray();
+    OutputStream out;
+    byte[] tmpl;
+    /*-------------------------------------------------------- */
+
+    public uEscWriter(OutputStream out) {
+        this.out = out;
+        tmpl = new byte[6];
+        tmpl[0] = (byte) '\\';
+        tmpl[1] = (byte) 'u';
+    }
+
+    @Override
+    public synchronized void write(int c) throws IOException {
+        if (c < 128) {
+            out.write(c);
+            return;
+        }
+        // write \udddd
+        byte[] tmpll = tmpl;
+        for (int k = 3; k >= 0; k--) {
+            tmpll[5 - k] = (byte) hexTable[(c >> 4 * k) & 0xF];
+        }
+        out.write(tmpll, 0, 6);
+    }
+
+    @Override
+    public synchronized void write(char[] cc, int ofs, int len) throws IOException {
+        for (int k = ofs; k < len; k++) {
+            write(cc[k]);
+        }
+    }
+
+    @Override
+    public void flush() {
+    }
+
+    @Override
+    public void close() {
+    }
+} // end uEscWriter
+
diff a/test/lib/org/openjdk/asmtools/util/I18NResourceBundle.java b/test/lib/org/openjdk/asmtools/util/I18NResourceBundle.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/util/I18NResourceBundle.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2014, 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 org.openjdk.asmtools.util;
+
+import java.text.MessageFormat;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * A class that lazily opens a package-specific resource bundle containing localization
+ * data for a class.
+ */
+public class I18NResourceBundle extends ResourceBundle {
+
+    /**
+     * Get a package-specific resource bundle for a class containing localization data.
+     * The bundle is named i18n.properties in the same package as the given class.
+     *
+     * @param c the class for which to obtain the resource bundle
+     * @return the appropriate resource bundle for the class
+     */
+    public static I18NResourceBundle getBundleForClass(Class c) {
+        String cn = c.getName();
+        int dot = cn.lastIndexOf('.');
+        String rn = (dot == -1 ? "i18n" : cn.substring(0, dot) + ".i18n");
+        return new I18NResourceBundle(rn, c.getClassLoader());
+    }
+
+    /**
+     * Create a resource bundle for the given name. The actual resource bundle will not be
+     * loaded until it is needed.
+     *
+     * @param name The name of the actual resource bundle to use.
+     */
+    private I18NResourceBundle(String name, ClassLoader cl) {
+        this.name = name;
+        this.classLoader = cl;
+    }
+
+    /**
+     * Get an entry from the resource bundle. If the resource cannot be found, a message
+     * is printed to the console and the result will be a string containing the method
+     * parameters.
+     *
+     * @param key the name of the entry to be returned
+     * @param arg an argument to be formatted into the result using
+     * {@link java.text.MessageFormat#format}
+     * @return the formatted string
+     */
+    public String getString(String key, Object arg) {
+        return getString(key, new Object[]{arg});
+    }
+
+    /**
+     * Get an entry from the resource bundle. If the resource cannot be found, a message
+     * is printed to the console and the result will be a string containing the method
+     * parameters.
+     *
+     * @param key the name of the entry to be returned
+     * @param args an array of arguments to be formatted into the result using
+     * {@link java.text.MessageFormat#format}
+     * @return the formatted string
+     */
+    public String getString(String key, Object... args) {
+        try {
+            return MessageFormat.format(getString(key), args);
+        } catch (MissingResourceException e) {
+            System.err.println("WARNING: missing resource: " + key + " for " + name);
+            StringBuffer sb = new StringBuffer(key);
+            for (int i = 0; i < args.length; i++) {
+                sb.append('\n');
+                sb.append(args.toString());
+            }
+            return sb.toString();
+        }
+    }
+
+    /**
+     * Get an entry from the bundle, returning null if it is not found.
+     *
+     * @param key the name of the entry to be returned
+     * @return the value of the entry, or null if it is not found.
+     */
+    public String getOptionalString(String key) {
+        if (delegate == null) {
+            delegate = ResourceBundle.getBundle(name, Locale.getDefault(), classLoader);
+        }
+        try {
+            String s = (String) (delegate.getObject(key));
+            if (s != null) {
+                System.out.println("i18n: " + key);
+            }
+            return s;
+        } catch (MissingResourceException e) {
+            return null;
+        }
+    }
+
+    /**
+     * A required internal method for ResourceBundle. Load the actual resource bundle, if
+     * it has not yet been loaded, then hand the request off to that bundle. If the
+     * resource cannot be found, a message is printed to the console and the result will
+     * be the original tag.
+     */
+    protected Object handleGetObject(String key) throws MissingResourceException {
+        try {
+            if (delegate == null) {
+                delegate = ResourceBundle.getBundle(name, Locale.getDefault(), classLoader);
+            }
+            return delegate.getObject(key);
+        } catch (MissingResourceException e) {
+            System.err.println("WARNING: missing resource: " + key + " for " + name);
+            return key;
+        }
+    }
+
+    /**
+     * A required internal method for ResourceBundle. Load the actual resource bundle, if
+     * it has not yet been loaded, then hand the request off to that bundle.
+     */
+    public Enumeration getKeys() {
+        if (delegate == null) {
+            delegate = ResourceBundle.getBundle(name);
+        }
+        return delegate.getKeys();
+    }
+
+    /**
+     * Returns the name of this bundle (useful for methods using bundle name instead of
+     * instance, such as <code>Logger</code> creation,
+     *
+     * @return the name of this resource bundle
+     */
+    public String getName() {
+        return name;
+    }
+
+    private String name;
+    private ResourceBundle delegate;
+    private ClassLoader classLoader;
+}
diff a/test/lib/org/openjdk/asmtools/util/ProductInfo.java b/test/lib/org/openjdk/asmtools/util/ProductInfo.java
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/util/ProductInfo.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2009, 2014, 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 org.openjdk.asmtools.util;
+
+import java.util.ResourceBundle;
+
+/**
+ * Class providing an access to the product info.
+ * <i>productinfo.properties</i> will be generated during the build
+ */
+public class ProductInfo {
+
+    static {
+        init();
+    }
+
+    ;
+
+    /**
+     * Returns the value of the specified property
+     */
+    public static String getProperty(String propName) {
+        try {
+            return bundle.getString(propName);
+        } catch (java.util.MissingResourceException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Version of the product in the short format, like 5.0
+     */
+    public static final String VERSION = version();
+
+    /**
+     * Full version of the product, including build number and date of creation
+     */
+    public static final String FULL_VERSION = fullVersion();
+
+    private static final String BUNDLE_NAME = "org.openjdk.asmtools.util.productinfo";
+
+    private static ResourceBundle bundle;
+
+    /**
+     * Initializes the bundle
+     */
+    private static void init() {
+        bundle = ResourceBundle.getBundle(BUNDLE_NAME);
+    }
+
+    private static String version() {
+        return getProperty("PRODUCT_VERSION");
+    }
+
+    private static String fullVersion() {
+        return getProperty("PRODUCT_NAME_LONG") + ", version " + version()
+                + " " + getProperty("PRODUCT_MILESTONE")
+                + " " + getProperty("PRODUCT_BUILDNUMBER")
+                + " (" + getProperty("PRODUCT_DATE") + ")";
+
+    }
+
+}
diff a/test/lib/org/openjdk/asmtools/util/productinfo.properties b/test/lib/org/openjdk/asmtools/util/productinfo.properties
--- /dev/null
+++ b/test/lib/org/openjdk/asmtools/util/productinfo.properties
@@ -0,0 +1,27 @@
+# Copyright (c) 2014, 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.
+
+PRODUCT_NAME        = %%PRODUCT_NAME%%
+PRODUCT_VERSION     = %%PRODUCT_VERSION%%
+PRODUCT_MILESTONE   = %%PRODUCT_MILESTONE%%
+PRODUCT_BUILDNUMBER = %%PRODUCT_BUILDNUMBER%%
+PRODUCT_NAME_LONG   = %%PRODUCT_NAME_LONG%%
+PRODUCT_DATE        = %%BUILD_DATE%%
diff a/test/micro/org/openjdk/bench/valhalla/ackermann/AckermannBase.java b/test/micro/org/openjdk/bench/valhalla/ackermann/AckermannBase.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/ackermann/AckermannBase.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2016, 2020, 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 org.openjdk.bench.valhalla.ackermann;
+
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public abstract class AckermannBase {
+
+    // ackermann(1,1748)+ ackermann(2,1897)+ ackermann(3,8); == 9999999 calls
+    // max depth - 3798
+    public static final int X1 = 1;
+    public static final int Y1 = 1748;
+    public static final int X2 = 2;
+    public static final int Y2 = 1897;
+    public static final int X3 = 3;
+    public static final int Y3 = 8;
+
+    public static final int OPI = 9999999;
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/ackermann/Identity64byte.java b/test/micro/org/openjdk/bench/valhalla/ackermann/Identity64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/ackermann/Identity64byte.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.ackermann;
+
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.R64byte;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+
+public class Identity64byte extends AckermannBase {
+
+    private static R64byte ack_ref(R64byte x, R64byte y) {
+        return x.longValue() == 0 ?
+                new R64byte(y.longValue() + 1) :
+                (y.longValue() == 0 ?
+                        ack_ref(new R64byte(x.longValue()-1) , new R64byte(1)) :
+                        ack_ref(new R64byte(x.longValue()-1), ack_ref(x, new R64byte(y.longValue()-1))));
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(OPI)
+    public long ack_Ref() {
+        return ack_ref(new R64byte(X1), new R64byte(Y1)).longValue()
+                + ack_ref(new R64byte(X2), new R64byte(Y2)).longValue()
+                + ack_ref(new R64byte(X3), new R64byte(Y3)).longValue();
+    }
+
+    private static Int64 ack_inter(Int64 x, Int64 y) {
+        return x.longValue() == 0 ?
+                new R64byte(y.longValue() + 1) :
+                (y.longValue() == 0 ?
+                        ack_inter(new R64byte(x.longValue()-1) , new R64byte(1)) :
+                        ack_inter(new R64byte(x.longValue()-1), ack_inter(x, new R64byte(y.longValue()-1))));
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(OPI)
+    public long ack_Int() {
+        return ack_inter(new R64byte(X1), new R64byte(Y1)).longValue()
+                + ack_inter(new R64byte(X2), new R64byte(Y2)).longValue()
+                + ack_inter(new R64byte(X3), new R64byte(Y3)).longValue();
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/ackermann/Identity64int.java b/test/micro/org/openjdk/bench/valhalla/ackermann/Identity64int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/ackermann/Identity64int.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.ackermann;
+
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.R64int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+
+public class Identity64int extends AckermannBase {
+
+    private static R64int ack_ref(R64int x, R64int y) {
+        return x.longValue() == 0 ?
+                new R64int(y.longValue() + 1) :
+                (y.longValue() == 0 ?
+                        ack_ref(new R64int(x.longValue()-1) , new R64int(1)) :
+                        ack_ref(new R64int(x.longValue()-1), ack_ref(x, new R64int(y.longValue()-1))));
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(OPI)
+    public long ack_Ref() {
+        return ack_ref(new R64int(X1), new R64int(Y1)).longValue()
+                + ack_ref(new R64int(X2), new R64int(Y2)).longValue()
+                + ack_ref(new R64int(X3), new R64int(Y3)).longValue();
+    }
+
+    private static Int64 ack_inter(Int64 x, Int64 y) {
+        return x.longValue() == 0 ?
+                new R64int(y.longValue() + 1) :
+                (y.longValue() == 0 ?
+                        ack_inter(new R64int(x.longValue()-1) , new R64int(1)) :
+                        ack_inter(new R64int(x.longValue()-1), ack_inter(x, new R64int(y.longValue()-1))));
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(OPI)
+    public long ack_Int() {
+        return ack_inter(new R64int(X1), new R64int(Y1)).longValue()
+                + ack_inter(new R64int(X2), new R64int(Y2)).longValue()
+                + ack_inter(new R64int(X3), new R64int(Y3)).longValue();
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/ackermann/Identity64long.java b/test/micro/org/openjdk/bench/valhalla/ackermann/Identity64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/ackermann/Identity64long.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.ackermann;
+
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.R64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+
+public class Identity64long extends AckermannBase {
+
+    private static R64long ack_ref(R64long x, R64long y) {
+        return x.longValue() == 0 ?
+                new R64long(y.longValue() + 1) :
+                (y.longValue() == 0 ?
+                        ack_ref(new R64long(x.longValue()-1) , new R64long(1)) :
+                        ack_ref(new R64long(x.longValue()-1), ack_ref(x, new R64long(y.longValue()-1))));
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(OPI)
+    public long ack_Ref() {
+        return ack_ref(new R64long(X1), new R64long(Y1)).longValue()
+                + ack_ref(new R64long(X2), new R64long(Y2)).longValue()
+                + ack_ref(new R64long(X3), new R64long(Y3)).longValue();
+    }
+
+    private static Int64 ack_inter(Int64 x, Int64 y) {
+        return x.longValue() == 0 ?
+                new R64long(y.longValue() + 1) :
+                (y.longValue() == 0 ?
+                        ack_inter(new R64long(x.longValue()-1) , new R64long(1)) :
+                        ack_inter(new R64long(x.longValue()-1), ack_inter(x, new R64long(y.longValue()-1))));
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(OPI)
+    public long ack_Int() {
+        return ack_inter(new R64long(X1), new R64long(Y1)).longValue()
+                + ack_inter(new R64long(X2), new R64long(Y2)).longValue()
+                + ack_inter(new R64long(X3), new R64long(Y3)).longValue();
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/ackermann/IdentityLong.java b/test/micro/org/openjdk/bench/valhalla/ackermann/IdentityLong.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/ackermann/IdentityLong.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.ackermann;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+
+public class IdentityLong extends AckermannBase {
+
+    private static Long ack_Long(Long x, Long y) {
+        return x == 0 ?
+                y + 1 :
+                (y == 0 ?
+                        ack_Long(x - 1, 1L) :
+                        ack_Long(x - 1, ack_Long(x, y - 1)));
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(OPI)
+    public long ref_Long() {
+        return ack_Long(Long.valueOf(X1), Long.valueOf(Y1))
+                + ack_Long(Long.valueOf(X2), Long.valueOf(Y2))
+                + ack_Long(Long.valueOf(X3), Long.valueOf(Y3));
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/ackermann/Inline64byte.java b/test/micro/org/openjdk/bench/valhalla/ackermann/Inline64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/ackermann/Inline64byte.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.ackermann;
+
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64byte;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+
+public class Inline64byte extends AckermannBase {
+
+    private static Q64byte ack_value(Q64byte x, Q64byte y) {
+        return x.longValue() == 0 ?
+                new Q64byte(y.longValue() + 1) :
+                (y.longValue() == 0 ?
+                        ack_value(new Q64byte(x.longValue()-1) , new Q64byte(1)) :
+                        ack_value(new Q64byte(x.longValue()-1), ack_value(x, new Q64byte(y.longValue()-1))));
+    }
+
+//    @Benchmark
+//    @OperationsPerInvocation(OPI)
+// TODO fix StackOverflowException
+    public long ack_Val() {
+        return ack_value(new Q64byte(X1), new Q64byte(Y1)).longValue()
+                + ack_value(new Q64byte(X2), new Q64byte(Y2)).longValue()
+                + ack_value(new Q64byte(X3), new Q64byte(Y3)).longValue();
+    }
+
+    private static Q64byte ack_ref(Q64byte x, Q64byte y) {
+        return x.longValue() == 0 ?
+                new Q64byte(y.longValue() + 1) :
+                (y.longValue() == 0 ?
+                        ack_ref(new Q64byte(x.longValue()-1) , new Q64byte(1)) :
+                        ack_ref(new Q64byte(x.longValue()-1), ack_ref(x, new Q64byte(y.longValue()-1))));
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(OPI)
+    public long ack_Ref() {
+        return ack_ref(new Q64byte(X1), new Q64byte(Y1)).longValue()
+                + ack_ref(new Q64byte(X2), new Q64byte(Y2)).longValue()
+                + ack_ref(new Q64byte(X3), new Q64byte(Y3)).longValue();
+    }
+
+    private static Int64 ack_inter(Int64 x, Int64 y) {
+        return x.longValue() == 0 ?
+                new Q64byte(y.longValue() + 1) :
+                (y.longValue() == 0 ?
+                        ack_inter(new Q64byte(x.longValue()-1) , new Q64byte(1)) :
+                        ack_inter(new Q64byte(x.longValue()-1), ack_inter(x, new Q64byte(y.longValue()-1))));
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(OPI)
+    public long ack_Int() {
+        return ack_inter(new Q64byte(X1), new Q64byte(Y1)).longValue()
+                + ack_inter(new Q64byte(X2), new Q64byte(Y2)).longValue()
+                + ack_inter(new Q64byte(X3), new Q64byte(Y3)).longValue();
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/ackermann/Inline64int.java b/test/micro/org/openjdk/bench/valhalla/ackermann/Inline64int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/ackermann/Inline64int.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.ackermann;
+
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+
+public class Inline64int extends AckermannBase {
+
+    private static Q64int ack_value(Q64int x, Q64int y) {
+        return x.longValue() == 0 ?
+                new Q64int(y.longValue() + 1) :
+                (y.longValue() == 0 ?
+                        ack_value(new Q64int(x.longValue()-1) , new Q64int(1)) :
+                        ack_value(new Q64int(x.longValue()-1), ack_value(x, new Q64int(y.longValue()-1))));
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(OPI)
+    public long ack_Val() {
+        return ack_value(new Q64int(X1), new Q64int(Y1)).longValue()
+                + ack_value(new Q64int(X2), new Q64int(Y2)).longValue()
+                + ack_value(new Q64int(X3), new Q64int(Y3)).longValue();
+    }
+
+    private static Q64int ack_ref(Q64int x, Q64int y) {
+        return x.longValue() == 0 ?
+                new Q64int(y.longValue() + 1) :
+                (y.longValue() == 0 ?
+                        ack_ref(new Q64int(x.longValue()-1) , new Q64int(1)) :
+                        ack_ref(new Q64int(x.longValue()-1), ack_ref(x, new Q64int(y.longValue()-1))));
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(OPI)
+    public long ack_Ref() {
+        return ack_ref(new Q64int(X1), new Q64int(Y1)).longValue()
+                + ack_ref(new Q64int(X2), new Q64int(Y2)).longValue()
+                + ack_ref(new Q64int(X3), new Q64int(Y3)).longValue();
+    }
+
+    private static Int64 ack_inter(Int64 x, Int64 y) {
+        return x.longValue() == 0 ?
+                new Q64int(y.longValue() + 1) :
+                (y.longValue() == 0 ?
+                        ack_inter(new Q64int(x.longValue()-1) , new Q64int(1)) :
+                        ack_inter(new Q64int(x.longValue()-1), ack_inter(x, new Q64int(y.longValue()-1))));
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(OPI)
+    public long ack_Int() {
+        return ack_inter(new Q64int(X1), new Q64int(Y1)).longValue()
+                + ack_inter(new Q64int(X2), new Q64int(Y2)).longValue()
+                + ack_inter(new Q64int(X3), new Q64int(Y3)).longValue();
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/ackermann/Inline64long.java b/test/micro/org/openjdk/bench/valhalla/ackermann/Inline64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/ackermann/Inline64long.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.ackermann;
+
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+
+public class Inline64long extends AckermannBase {
+
+    private static Q64long ack_value(Q64long x, Q64long y) {
+        return x.longValue() == 0 ?
+                new Q64long(y.longValue() + 1) :
+                (y.longValue() == 0 ?
+                        ack_value(new Q64long(x.longValue()-1) , new Q64long(1)) :
+                        ack_value(new Q64long(x.longValue()-1), ack_value(x, new Q64long(y.longValue()-1))));
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(OPI)
+    public long ack_Val() {
+        return ack_value(new Q64long(X1), new Q64long(Y1)).longValue()
+                + ack_value(new Q64long(X2), new Q64long(Y2)).longValue()
+                + ack_value(new Q64long(X3), new Q64long(Y3)).longValue();
+    }
+
+    private static Q64long ack_ref(Q64long x, Q64long y) {
+        return x.longValue() == 0 ?
+                new Q64long(y.longValue() + 1) :
+                (y.longValue() == 0 ?
+                        ack_ref(new Q64long(x.longValue()-1) , new Q64long(1)) :
+                        ack_ref(new Q64long(x.longValue()-1), ack_ref(x, new Q64long(y.longValue()-1))));
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(OPI)
+    public long ack_Ref() {
+        return ack_ref(new Q64long(X1), new Q64long(Y1)).longValue()
+                + ack_ref(new Q64long(X2), new Q64long(Y2)).longValue()
+                + ack_ref(new Q64long(X3), new Q64long(Y3)).longValue();
+    }
+
+    private static Int64 ack_inter(Int64 x, Int64 y) {
+        return x.longValue() == 0 ?
+                new Q64long(y.longValue() + 1) :
+                (y.longValue() == 0 ?
+                        ack_inter(new Q64long(x.longValue()-1) , new Q64long(1)) :
+                        ack_inter(new Q64long(x.longValue()-1), ack_inter(x, new Q64long(y.longValue()-1))));
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(OPI)
+    public long ack_Int() {
+        return ack_inter(new Q64long(X1), new Q64long(Y1)).longValue()
+                + ack_inter(new Q64long(X2), new Q64long(Y2)).longValue()
+                + ack_inter(new Q64long(X3), new Q64long(Y3)).longValue();
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/ackermann/Primitive64.java b/test/micro/org/openjdk/bench/valhalla/ackermann/Primitive64.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/ackermann/Primitive64.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.ackermann;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+
+public class Primitive64 extends AckermannBase {
+
+    private static long ack_primitive(long x, long y) {
+        return x == 0 ?
+                y + 1 :
+                (y == 0 ?
+                        ack_primitive(x - 1, 1) :
+                        ack_primitive(x - 1, ack_primitive(x, y - 1)));
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(OPI)
+    public long primitive() {
+        return ack_primitive(X1, Y1)
+                + ack_primitive(X2, Y2)
+                + ack_primitive(X3, Y3);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/acmp/array/IdentityIsCmpBranch.java b/test/micro/org/openjdk/bench/valhalla/acmp/array/IdentityIsCmpBranch.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/acmp/array/IdentityIsCmpBranch.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.acmp.array;
+
+import org.openjdk.bench.valhalla.types.R64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+/*
+ *  For proper results it should be executed:
+ *  java -jar target/benchmarks.jar IdentityIsCmpBranch  -wmb IdentityIsCmpBranch.equals050
+ */
+
+@Fork(3)
+@Warmup(iterations = 3, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class IdentityIsCmpBranch extends StatesR64long {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static int cmp_Obj(Object[] objects1, Object[] objects2) {
+        int s = 0;
+        for (int i = 0; i < SIZE; i++) {
+            if (objects1[i] == objects2[i]) {
+                s += 1;
+            } else {
+                s -= 1;
+            }
+        }
+        return s;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static int cmp_Ref(R64long[] objects1, R64long[] objects2) {
+        int s = 0;
+        for (int i = 0; i < SIZE; i++) {
+            if (objects1[i] == objects2[i]) {
+                s += 1;
+            } else {
+                s -= 1;
+            }
+        }
+        return s;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals000(ObjState00 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals025(ObjState25 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals050(ObjState50 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals075(ObjState75 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals100(ObjState100 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals000(RefState00 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals025(RefState25 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals050(RefState50 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals075(RefState75 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals100(RefState100 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/acmp/array/IdentityIsCmpResult.java b/test/micro/org/openjdk/bench/valhalla/acmp/array/IdentityIsCmpResult.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/acmp/array/IdentityIsCmpResult.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.acmp.array;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+/*
+ *  For proper results it should be executed:
+ *  java -jar target/benchmarks.jar IdentityIsCmpResult  -wmb IdentityIsCmpResult.Obj_equals050
+ */
+
+@Fork(3)
+@Warmup(iterations = 3, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class IdentityIsCmpResult extends StatesR64long {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static boolean cmp_Obj(Object[] objects1, Object[] objects2) {
+        boolean s = false;
+        for (int i = 0; i < SIZE; i++) {
+            s ^= objects1[i] == objects2[i];
+        }
+        return s;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public boolean Obj_equals000(ObjState00 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public boolean Obj_equals025(ObjState25 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public boolean Obj_equals050(ObjState50 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public boolean Obj_equals075(ObjState75 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public boolean Obj_equals100(ObjState100 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/acmp/array/IdentityIsNotCmpBranch.java b/test/micro/org/openjdk/bench/valhalla/acmp/array/IdentityIsNotCmpBranch.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/acmp/array/IdentityIsNotCmpBranch.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.acmp.array;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+/*
+ *  For proper results it should be executed:
+ *  java -jar target/benchmarks.jar IdentityIsNotCmpBranch  -wmb IdentityIsNotCmpBranch.Obj_equals050
+ */
+
+@Fork(3)
+@Warmup(iterations = 3, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class IdentityIsNotCmpBranch extends StatesR64long {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static int cmp_Obj(Object[] objects1, Object[] objects2) {
+        int s = 0;
+        for (int i = 0; i < SIZE; i++) {
+            if (objects1[i] != objects2[i]) {
+                s += 1;
+            } else {
+                s -= 1;
+            }
+        }
+        return s;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals000(ObjState00 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals025(ObjState25 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals050(ObjState50 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals075(ObjState75 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals100(ObjState100 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/acmp/array/IdentityIsNotCmpResult.java b/test/micro/org/openjdk/bench/valhalla/acmp/array/IdentityIsNotCmpResult.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/acmp/array/IdentityIsNotCmpResult.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.acmp.array;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 3, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class IdentityIsNotCmpResult extends StatesR64long {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static boolean cmp_Obj(Object[] objects1, Object[] objects2) {
+        boolean s = false;
+        for (int i = 0; i < SIZE; i++) {
+            s ^= objects1[i] != objects2[i];
+        }
+        return s;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public boolean Obj_equals000(ObjState00 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public boolean Obj_equals025(ObjState25 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public boolean Obj_equals050(ObjState50 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public boolean Obj_equals075(ObjState75 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public boolean Obj_equals100(ObjState100 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/acmp/array/InlineIsCmpBranch64byte.java b/test/micro/org/openjdk/bench/valhalla/acmp/array/InlineIsCmpBranch64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/acmp/array/InlineIsCmpBranch64byte.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.acmp.array;
+
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64byte;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 3, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class InlineIsCmpBranch64byte extends StatesQ64byte {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static int cmp_Obj(Object[] objects1, Object[] objects2) {
+        int s = 0;
+        for (int i = 0; i < SIZE; i++) {
+            if (objects1[i] == objects2[i]) {
+                s += 1;
+            } else {
+                s -= 1;
+            }
+        }
+        return s;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static int cmp_Int(Int64[] objects1, Int64[] objects2) {
+        int s = 0;
+        for (int i = 0; i < SIZE; i++) {
+            if (objects1[i] == objects2[i]) {
+                s += 1;
+            } else {
+                s -= 1;
+            }
+        }
+        return s;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static int cmp_Ref(Q64byte[] objects1, Q64byte[] objects2) {
+        int s = 0;
+        for (int i = 0; i < SIZE; i++) {
+            if (objects1[i] == objects2[i]) {
+                s += 1;
+            } else {
+                s -= 1;
+            }
+        }
+        return s;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static int cmp_Val(Q64byte[] objects1, Q64byte[] objects2) {
+        int s = 0;
+        for (int i = 0; i < SIZE; i++) {
+            if (objects1[i] == objects2[i]) {
+                s += 1;
+            } else {
+                s -= 1;
+            }
+        }
+        return s;
+    }
+
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals000(ObjState00 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals025(ObjState25 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals050(ObjState50 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals075(ObjState75 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals100(ObjState100 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Int_equals000(IntState00 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Int_equals025(IntState25 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Int_equals050(IntState50 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Int_equals075(IntState75 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Int_equals100(IntState100 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals000(RefState00 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals025(RefState25 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals050(RefState50 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals075(RefState75 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals100(RefState100 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_equals000(ValState00 st) {
+        return cmp_Val(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_equals025(ValState25 st) {
+        return cmp_Val(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_equals050(ValState50 st) {
+        return cmp_Val(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_equals075(ValState75 st) {
+        return cmp_Val(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_equals100(ValState100 st) {
+        return cmp_Val(st.arr1, st.arr2);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/acmp/array/InlineIsCmpBranch64long.java b/test/micro/org/openjdk/bench/valhalla/acmp/array/InlineIsCmpBranch64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/acmp/array/InlineIsCmpBranch64long.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.acmp.array;
+
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 3, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class InlineIsCmpBranch64long extends StatesQ64long {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static int cmp_Obj(Object[] objects1, Object[] objects2) {
+        int s = 0;
+        for (int i = 0; i < SIZE; i++) {
+            if (objects1[i] == objects2[i]) {
+                s += 1;
+            } else {
+                s -= 1;
+            }
+        }
+        return s;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static int cmp_Int(Int64[] objects1, Int64[] objects2) {
+        int s = 0;
+        for (int i = 0; i < SIZE; i++) {
+            if (objects1[i] == objects2[i]) {
+                s += 1;
+            } else {
+                s -= 1;
+            }
+        }
+        return s;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static int cmp_Ref(Q64long[] objects1, Q64long[] objects2) {
+        int s = 0;
+        for (int i = 0; i < SIZE; i++) {
+            if (objects1[i] == objects2[i]) {
+                s += 1;
+            } else {
+                s -= 1;
+            }
+        }
+        return s;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static int cmp_Val(Q64long[] objects1, Q64long[] objects2) {
+        int s = 0;
+        for (int i = 0; i < SIZE; i++) {
+            if (objects1[i] == objects2[i]) {
+                s += 1;
+            } else {
+                s -= 1;
+            }
+        }
+        return s;
+    }
+
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals000(ObjState00 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals025(ObjState25 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals050(ObjState50 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals075(ObjState75 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals100(ObjState100 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Int_equals000(IntState00 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Int_equals025(IntState25 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Int_equals050(IntState50 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Int_equals075(IntState75 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Int_equals100(IntState100 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals000(RefState00 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals025(RefState25 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals050(RefState50 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals075(RefState75 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals100(RefState100 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_equals000(ValState00 st) {
+        return cmp_Val(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_equals025(ValState25 st) {
+        return cmp_Val(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_equals050(ValState50 st) {
+        return cmp_Val(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_equals075(ValState75 st) {
+        return cmp_Val(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_equals100(ValState100 st) {
+        return cmp_Val(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_as_Ref_equals000(ValState00 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_as_Ref_equals025(ValState25 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_as_Ref_equals050(ValState50 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_as_Ref_equals075(ValState75 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_as_Ref_equals100(ValState100 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_as_Int_equals000(ValState00 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_as_Int_equals025(ValState25 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_as_Int_equals050(ValState50 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_as_Int_equals075(ValState75 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_as_Int_equals100(ValState100 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_as_Obj_equals000(ValState00 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_as_Obj_equals025(ValState25 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_as_Obj_equals050(ValState50 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_as_Obj_equals075(ValState75 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_as_Obj_equals100(ValState100 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/acmp/array/StatesQ64byte.java b/test/micro/org/openjdk/bench/valhalla/acmp/array/StatesQ64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/acmp/array/StatesQ64byte.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.acmp.array;
+
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64byte;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+
+import java.util.BitSet;
+import java.util.Random;
+
+public class StatesQ64byte {
+
+    public static final int SIZE = 100;
+
+    private static BitSet indices(Random rnd, int bound, int size) {
+        return rnd.ints(0, bound)
+                .distinct()
+                .limit(size)
+                .collect(BitSet::new, BitSet::set, BitSet::or);
+    }
+
+    private static void populate(Object[] arr1, Object[] arr2, int eq) {
+        BitSet eqset = (eq > 0 && eq < 100) ? indices(new Random(42), SIZE, (eq * SIZE) / 100) : null;
+        for (int i = 0; i < SIZE; i++) {
+            if (eq > 0 && (eq >= 100 || eqset.get(i))) {
+                arr2[i] = arr1[i] = new Q64byte(i);
+            } else {
+                arr1[i] = new Q64byte(2 * i);
+                arr2[i] = new Q64byte(2 * i + 1);
+            }
+        }
+    }
+
+    @State(Scope.Thread)
+    public abstract static class ObjState {
+        Object[] arr1, arr2;
+    }
+
+    @State(Scope.Thread)
+    public abstract static class IntState {
+        Int64[] arr1, arr2;
+    }
+
+    @State(Scope.Thread)
+    public abstract static class RefState {
+        Q64byte[] arr1, arr2;
+    }
+
+    @State(Scope.Thread)
+    public abstract static class ValState {
+        Q64byte[] arr1, arr2;
+    }
+
+    public static class ObjState00 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new Object[SIZE];
+            arr2 = new Object[SIZE];
+            populate(arr1, arr2, 0);
+        }
+
+    }
+
+    public static class ObjState25 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new Object[SIZE];
+            arr2 = new Object[SIZE];
+            populate(arr1, arr2, 25);
+        }
+
+    }
+
+    public static class ObjState50 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new Object[SIZE];
+            arr2 = new Object[SIZE];
+            populate(arr1, arr2, 50);
+        }
+
+    }
+
+    public static class ObjState75 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new Object[SIZE];
+            arr2 = new Object[SIZE];
+            populate(arr1, arr2, 75);
+        }
+
+    }
+
+    public static class ObjState100 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new Object[SIZE];
+            arr2 = new Object[SIZE];
+            populate(arr1, arr2, 100);
+        }
+    }
+
+    public static class IntState00 extends IntState {
+        @Setup
+        public void setup() {
+            arr1 = new Int64[SIZE];
+            arr2 = new Int64[SIZE];
+            populate(arr1, arr2, 0);
+        }
+    }
+
+    public static class IntState25 extends IntState {
+        @Setup
+        public void setup() {
+            arr1 = new Int64[SIZE];
+            arr2 = new Int64[SIZE];
+            populate(arr1, arr2, 25);
+        }
+    }
+
+    public static class IntState50 extends IntState {
+        @Setup
+        public void setup() {
+            arr1 = new Int64[SIZE];
+            arr2 = new Int64[SIZE];
+            populate(arr1, arr2, 50);
+        }
+    }
+
+    public static class IntState75 extends IntState {
+        @Setup
+        public void setup() {
+            arr1 = new Int64[SIZE];
+            arr2 = new Int64[SIZE];
+            populate(arr1, arr2, 75);
+        }
+    }
+
+    public static class IntState100 extends IntState {
+        @Setup
+        public void setup() {
+            arr1 = new Int64[SIZE];
+            arr2 = new Int64[SIZE];
+            populate(arr1, arr2, 100);
+        }
+    }
+
+    public static class RefState00 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64byte[SIZE];
+            arr2 = new Q64byte[SIZE];
+            populate(arr1, arr2, 0);
+        }
+    }
+
+    public static class RefState25 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64byte[SIZE];
+            arr2 = new Q64byte[SIZE];
+            populate(arr1, arr2, 25);
+        }
+    }
+
+    public static class RefState50 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64byte[SIZE];
+            arr2 = new Q64byte[SIZE];
+            populate(arr1, arr2, 50);
+        }
+    }
+
+    public static class RefState75 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64byte[SIZE];
+            arr2 = new Q64byte[SIZE];
+            populate(arr1, arr2, 75);
+        }
+    }
+
+    public static class RefState100 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64byte[SIZE];
+            arr2 = new Q64byte[SIZE];
+            populate(arr1, arr2, 100);
+        }
+    }
+
+    public static class ValState00 extends ValState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64byte[SIZE];
+            arr2 = new Q64byte[SIZE];
+            populate(arr1, arr2, 0);
+        }
+    }
+
+    public static class ValState25 extends ValState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64byte[SIZE];
+            arr2 = new Q64byte[SIZE];
+            populate(arr1, arr2, 25);
+        }
+    }
+
+    public static class ValState50 extends ValState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64byte[SIZE];
+            arr2 = new Q64byte[SIZE];
+            populate(arr1, arr2, 50);
+        }
+    }
+
+    public static class ValState75 extends ValState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64byte[SIZE];
+            arr2 = new Q64byte[SIZE];
+            populate(arr1, arr2, 75);
+        }
+    }
+
+    public static class ValState100 extends ValState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64byte[SIZE];
+            arr2 = new Q64byte[SIZE];
+            populate(arr1, arr2, 100);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/acmp/array/StatesQ64long.java b/test/micro/org/openjdk/bench/valhalla/acmp/array/StatesQ64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/acmp/array/StatesQ64long.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.acmp.array;
+
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+
+import java.util.BitSet;
+import java.util.Random;
+
+public class StatesQ64long {
+
+    public static final int SIZE = 100;
+
+    private static BitSet indices(Random rnd, int bound, int size) {
+        return rnd.ints(0, bound)
+                .distinct()
+                .limit(size)
+                .collect(BitSet::new, BitSet::set, BitSet::or);
+    }
+
+    private static void populate(Object[] arr1, Object[] arr2, int eq) {
+        BitSet eqset = (eq > 0 && eq < 100) ? indices(new Random(42), SIZE, (eq * SIZE) / 100) : null;
+        for (int i = 0; i < SIZE; i++) {
+            if (eq > 0 && (eq >= 100 || eqset.get(i))) {
+                arr2[i] = arr1[i] = new Q64long(i);
+            } else {
+                arr1[i] = new Q64long(2 * i);
+                arr2[i] = new Q64long(2 * i + 1);
+            }
+        }
+    }
+
+    @State(Scope.Thread)
+    public abstract static class ObjState {
+        Object[] arr1, arr2;
+    }
+
+    @State(Scope.Thread)
+    public abstract static class IntState {
+        Int64[] arr1, arr2;
+    }
+
+    @State(Scope.Thread)
+    public abstract static class RefState {
+        Q64long[] arr1, arr2;
+    }
+
+    @State(Scope.Thread)
+    public abstract static class ValState {
+        Q64long[] arr1, arr2;
+    }
+
+    public static class ObjState00 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new Object[SIZE];
+            arr2 = new Object[SIZE];
+            populate(arr1, arr2, 0);
+        }
+
+    }
+
+    public static class ObjState25 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new Object[SIZE];
+            arr2 = new Object[SIZE];
+            populate(arr1, arr2, 25);
+        }
+
+    }
+
+    public static class ObjState50 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new Object[SIZE];
+            arr2 = new Object[SIZE];
+            populate(arr1, arr2, 50);
+        }
+
+    }
+
+    public static class ObjState75 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new Object[SIZE];
+            arr2 = new Object[SIZE];
+            populate(arr1, arr2, 75);
+        }
+
+    }
+
+    public static class ObjState100 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new Object[SIZE];
+            arr2 = new Object[SIZE];
+            populate(arr1, arr2, 100);
+        }
+    }
+
+    public static class IntState00 extends IntState {
+        @Setup
+        public void setup() {
+            arr1 = new Int64[SIZE];
+            arr2 = new Int64[SIZE];
+            populate(arr1, arr2, 0);
+        }
+    }
+
+    public static class IntState25 extends IntState {
+        @Setup
+        public void setup() {
+            arr1 = new Int64[SIZE];
+            arr2 = new Int64[SIZE];
+            populate(arr1, arr2, 25);
+        }
+    }
+
+    public static class IntState50 extends IntState {
+        @Setup
+        public void setup() {
+            arr1 = new Int64[SIZE];
+            arr2 = new Int64[SIZE];
+            populate(arr1, arr2, 50);
+        }
+    }
+
+    public static class IntState75 extends IntState {
+        @Setup
+        public void setup() {
+            arr1 = new Int64[SIZE];
+            arr2 = new Int64[SIZE];
+            populate(arr1, arr2, 75);
+        }
+    }
+
+    public static class IntState100 extends IntState {
+        @Setup
+        public void setup() {
+            arr1 = new Int64[SIZE];
+            arr2 = new Int64[SIZE];
+            populate(arr1, arr2, 100);
+        }
+    }
+
+    public static class RefState00 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64long[SIZE];
+            arr2 = new Q64long[SIZE];
+            populate(arr1, arr2, 0);
+        }
+    }
+
+    public static class RefState25 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64long[SIZE];
+            arr2 = new Q64long[SIZE];
+            populate(arr1, arr2, 25);
+        }
+    }
+
+    public static class RefState50 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64long[SIZE];
+            arr2 = new Q64long[SIZE];
+            populate(arr1, arr2, 50);
+        }
+    }
+
+    public static class RefState75 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64long[SIZE];
+            arr2 = new Q64long[SIZE];
+            populate(arr1, arr2, 75);
+        }
+    }
+
+    public static class RefState100 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64long[SIZE];
+            arr2 = new Q64long[SIZE];
+            populate(arr1, arr2, 100);
+        }
+    }
+
+    public static class ValState00 extends ValState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64long[SIZE];
+            arr2 = new Q64long[SIZE];
+            populate(arr1, arr2, 0);
+        }
+    }
+
+    public static class ValState25 extends ValState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64long[SIZE];
+            arr2 = new Q64long[SIZE];
+            populate(arr1, arr2, 25);
+        }
+    }
+
+    public static class ValState50 extends ValState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64long[SIZE];
+            arr2 = new Q64long[SIZE];
+            populate(arr1, arr2, 50);
+        }
+    }
+
+    public static class ValState75 extends ValState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64long[SIZE];
+            arr2 = new Q64long[SIZE];
+            populate(arr1, arr2, 75);
+        }
+    }
+
+    public static class ValState100 extends ValState {
+        @Setup
+        public void setup() {
+            arr1 = new Q64long[SIZE];
+            arr2 = new Q64long[SIZE];
+            populate(arr1, arr2, 100);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/acmp/array/StatesR64long.java b/test/micro/org/openjdk/bench/valhalla/acmp/array/StatesR64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/acmp/array/StatesR64long.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.acmp.array;
+
+import org.openjdk.bench.valhalla.types.R64long;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+
+import java.util.BitSet;
+import java.util.Random;
+
+public class StatesR64long {
+
+    public static final int SIZE = 100;
+
+    private static BitSet indices(Random rnd, int bound, int size) {
+        return rnd.ints(0, bound)
+                .distinct()
+                .limit(size)
+                .collect(BitSet::new, BitSet::set, BitSet::or);
+    }
+
+    private static void populate(Object[] arr1, Object[] arr2, int eq) {
+        BitSet eqset = (eq > 0 && eq < 100) ? indices(new Random(42), SIZE, (eq * SIZE) / 100) : null;
+        int samenulls = 0;
+        int distinctnulls = 0;
+        for (int i = 0; i < SIZE; i++) {
+            if (eq > 0 && (eq >= 100 || eqset.get(i))) {
+                if (samenulls > 0) {
+                    arr2[i] = arr1[i] = new R64long(i);
+                } else {
+                    arr2[i] = arr1[i] = null;
+                    samenulls++;
+                }
+            } else {
+                if (distinctnulls > 1) {
+                    arr1[i] = new R64long(2 * i);
+                    arr2[i] = new R64long(2 * i + 1);
+                } else {
+                    if (distinctnulls == 0) {
+                        arr1[i] = null;
+                        arr2[i] = new R64long(2 * i + 1);
+                    } else {
+                        arr1[i] = new R64long(2 * i);
+                        arr2[i] = null;
+                    }
+                    distinctnulls++;
+                }
+            }
+        }
+    }
+
+    @State(Scope.Thread)
+    public abstract static class ObjState {
+        Object[] arr1, arr2;
+    }
+
+    @State(Scope.Thread)
+    public abstract static class RefState {
+        R64long[] arr1, arr2;
+    }
+
+    public static class ObjState00 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new Object[SIZE];
+            arr2 = new Object[SIZE];
+            populate(arr1, arr2, 0);
+        }
+
+    }
+
+    public static class ObjState25 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new Object[SIZE];
+            arr2 = new Object[SIZE];
+            populate(arr1, arr2, 25);
+        }
+
+    }
+
+    public static class ObjState50 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new Object[SIZE];
+            arr2 = new Object[SIZE];
+            populate(arr1, arr2, 50);
+        }
+
+    }
+
+    public static class ObjState75 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new Object[SIZE];
+            arr2 = new Object[SIZE];
+            populate(arr1, arr2, 75);
+        }
+
+    }
+
+    public static class ObjState100 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new Object[SIZE];
+            arr2 = new Object[SIZE];
+            populate(arr1, arr2, 100);
+        }
+    }
+
+    public static class RefState00 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new R64long[SIZE];
+            arr2 = new R64long[SIZE];
+            populate(arr1, arr2, 0);
+        }
+    }
+
+    public static class RefState25 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new R64long[SIZE];
+            arr2 = new R64long[SIZE];
+            populate(arr1, arr2, 25);
+        }
+    }
+
+    public static class RefState50 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new R64long[SIZE];
+            arr2 = new R64long[SIZE];
+            populate(arr1, arr2, 50);
+        }
+    }
+
+    public static class RefState75 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new R64long[SIZE];
+            arr2 = new R64long[SIZE];
+            populate(arr1, arr2, 75);
+        }
+    }
+
+    public static class RefState100 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new R64long[SIZE];
+            arr2 = new R64long[SIZE];
+            populate(arr1, arr2, 100);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/acmp/field/InlineIsCmpBranch64long.java b/test/micro/org/openjdk/bench/valhalla/acmp/field/InlineIsCmpBranch64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/acmp/field/InlineIsCmpBranch64long.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.acmp.field;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+/*
+ *  For proper results it should be executed:
+ *  java -jar target/benchmarks.jar baseline.acmp.IsCmpBranch  -wmb baseline.acmp.IsCmpBranch.equals050
+ */
+
+@Fork(3)
+@Warmup(iterations = 3, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class InlineIsCmpBranch64long extends StatesQ64long {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static int cmp_Obj(ObjWrapper[] objects1, ObjWrapper[] objects2) {
+        int s = 0;
+        for (int i = 0; i < SIZE; i++) {
+            if (objects1[i].f == objects2[i].f) {
+                s += 1;
+            } else {
+                s -= 1;
+            }
+        }
+        return s;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static int cmp_Int(IntWrapper[] objects1, IntWrapper[] objects2) {
+        int s = 0;
+        for (int i = 0; i < SIZE; i++) {
+            if (objects1[i].f == objects2[i].f) {
+                s += 1;
+            } else {
+                s -= 1;
+            }
+        }
+        return s;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static int cmp_Ref(RefWrapper[] objects1, RefWrapper[] objects2) {
+        int s = 0;
+        for (int i = 0; i < SIZE; i++) {
+            if (objects1[i].f == objects2[i].f) {
+                s += 1;
+            } else {
+                s -= 1;
+            }
+        }
+        return s;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static int cmp_Val(ValWrapper[] objects1, ValWrapper[] objects2) {
+        int s = 0;
+        for (int i = 0; i < SIZE; i++) {
+            if (objects1[i].f == objects2[i].f) {
+                s += 1;
+            } else {
+                s -= 1;
+            }
+        }
+        return s;
+    }
+
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals000(ObjState00 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals025(ObjState25 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals050(ObjState50 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals075(ObjState75 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Obj_equals100(ObjState100 st) {
+        return cmp_Obj(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Int_equals000(IntState00 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Int_equals025(IntState25 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Int_equals050(IntState50 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Int_equals075(IntState75 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Int_equals100(IntState100 st) {
+        return cmp_Int(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals000(RefState00 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals025(RefState25 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals050(RefState50 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals075(RefState75 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Ref_equals100(RefState100 st) {
+        return cmp_Ref(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_equals000(ValState00 st) {
+        return cmp_Val(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_equals025(ValState25 st) {
+        return cmp_Val(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_equals050(ValState50 st) {
+        return cmp_Val(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_equals075(ValState75 st) {
+        return cmp_Val(st.arr1, st.arr2);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int Val_equals100(ValState100 st) {
+        return cmp_Val(st.arr1, st.arr2);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/acmp/field/StatesQ64long.java b/test/micro/org/openjdk/bench/valhalla/acmp/field/StatesQ64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/acmp/field/StatesQ64long.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.acmp.field;
+
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+
+import java.util.BitSet;
+import java.util.Random;
+
+public class StatesQ64long {
+
+    public static final int SIZE = 100;
+
+    private static BitSet indices(Random rnd, int bound, int size) {
+        return rnd.ints(0, bound)
+                .distinct()
+                .limit(size)
+                .collect(BitSet::new, BitSet::set, BitSet::or);
+    }
+
+    private static void populate(ObjWrapper[] arr1, ObjWrapper[] arr2, int eq) {
+        BitSet eqset = (eq > 0 && eq < 100) ? indices(new Random(42), SIZE, (eq * SIZE) / 100) : null;
+        for (int i = 0; i < SIZE; i++) {
+            if (eq > 0 && (eq >= 100 || eqset.get(i))) {
+                arr2[i] = arr1[i] = new ObjWrapper(new Q64long(i));
+            } else {
+                arr1[i] = new ObjWrapper(new Q64long(2 * i));
+                arr2[i] = new ObjWrapper(new Q64long(2 * i + 1));
+            }
+        }
+    }
+
+    private static void populate(IntWrapper[] arr1, IntWrapper[] arr2, int eq) {
+        BitSet eqset = (eq > 0 && eq < 100) ? indices(new Random(42), SIZE, (eq * SIZE) / 100) : null;
+        for (int i = 0; i < SIZE; i++) {
+            if (eq > 0 && (eq >= 100 || eqset.get(i))) {
+                arr2[i] = arr1[i] = new IntWrapper(new Q64long(i));
+            } else {
+                arr1[i] = new IntWrapper(new Q64long(2 * i));
+                arr2[i] = new IntWrapper(new Q64long(2 * i + 1));
+            }
+        }
+    }
+
+    private static void populate(RefWrapper[] arr1, RefWrapper[] arr2, int eq) {
+        BitSet eqset = (eq > 0 && eq < 100) ? indices(new Random(42), SIZE, (eq * SIZE) / 100) : null;
+        for (int i = 0; i < SIZE; i++) {
+            if (eq > 0 && (eq >= 100 || eqset.get(i))) {
+                arr2[i] = arr1[i] = new RefWrapper(new Q64long(i));
+            } else {
+                arr1[i] = new RefWrapper(new Q64long(2 * i));
+                arr2[i] = new RefWrapper(new Q64long(2 * i + 1));
+            }
+        }
+    }
+
+    private static void populate(ValWrapper[] arr1, ValWrapper[] arr2, int eq) {
+        BitSet eqset = (eq > 0 && eq < 100) ? indices(new Random(42), SIZE, (eq * SIZE) / 100) : null;
+        for (int i = 0; i < SIZE; i++) {
+            if (eq > 0 && (eq >= 100 || eqset.get(i))) {
+                arr2[i] = arr1[i] = new ValWrapper(new Q64long(i));
+            } else {
+                arr1[i] = new ValWrapper(new Q64long(2 * i));
+                arr2[i] = new ValWrapper(new Q64long(2 * i + 1));
+            }
+        }
+    }
+
+    public static class ObjWrapper {
+        public Object f;
+
+        public ObjWrapper(Object f) {
+            this.f = f;
+        }
+    }
+
+
+    @State(Scope.Thread)
+    public abstract static class ObjState {
+        ObjWrapper[] arr1, arr2;
+    }
+
+    public static class IntWrapper {
+        public Int64 f;
+
+        public IntWrapper(Int64 f) {
+            this.f = f;
+        }
+    }
+
+    @State(Scope.Thread)
+    public abstract static class IntState {
+        IntWrapper[] arr1, arr2;
+    }
+
+    public static class RefWrapper {
+        public Q64long f;
+
+        public RefWrapper(Q64long f) {
+            this.f = f;
+        }
+    }
+
+
+    @State(Scope.Thread)
+    public abstract static class RefState {
+        RefWrapper[] arr1, arr2;
+    }
+
+    public static class ValWrapper {
+        public Q64long f;
+
+        public ValWrapper(Q64long f) {
+            this.f = f;
+        }
+    }
+
+
+    @State(Scope.Thread)
+    public abstract static class ValState {
+        ValWrapper[] arr1, arr2;
+    }
+
+    public static class ObjState00 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new ObjWrapper[SIZE];
+            arr2 = new ObjWrapper[SIZE];
+            populate(arr1, arr2, 0);
+        }
+
+    }
+
+    public static class ObjState25 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new ObjWrapper[SIZE];
+            arr2 = new ObjWrapper[SIZE];
+            populate(arr1, arr2, 25);
+        }
+
+    }
+
+    public static class ObjState50 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new ObjWrapper[SIZE];
+            arr2 = new ObjWrapper[SIZE];
+            populate(arr1, arr2, 50);
+        }
+
+    }
+
+    public static class ObjState75 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new ObjWrapper[SIZE];
+            arr2 = new ObjWrapper[SIZE];
+            populate(arr1, arr2, 75);
+        }
+
+    }
+
+    public static class ObjState100 extends ObjState {
+        @Setup
+        public void setup() {
+            arr1 = new ObjWrapper[SIZE];
+            arr2 = new ObjWrapper[SIZE];
+            populate(arr1, arr2, 100);
+        }
+    }
+
+    public static class IntState00 extends IntState {
+        @Setup
+        public void setup() {
+            arr1 = new IntWrapper[SIZE];
+            arr2 = new IntWrapper[SIZE];
+            populate(arr1, arr2, 0);
+        }
+    }
+
+    public static class IntState25 extends IntState {
+        @Setup
+        public void setup() {
+            arr1 = new IntWrapper[SIZE];
+            arr2 = new IntWrapper[SIZE];
+            populate(arr1, arr2, 25);
+        }
+    }
+
+    public static class IntState50 extends IntState {
+        @Setup
+        public void setup() {
+            arr1 = new IntWrapper[SIZE];
+            arr2 = new IntWrapper[SIZE];
+            populate(arr1, arr2, 50);
+        }
+    }
+
+    public static class IntState75 extends IntState {
+        @Setup
+        public void setup() {
+            arr1 = new IntWrapper[SIZE];
+            arr2 = new IntWrapper[SIZE];
+            populate(arr1, arr2, 75);
+        }
+    }
+
+    public static class IntState100 extends IntState {
+        @Setup
+        public void setup() {
+            arr1 = new IntWrapper[SIZE];
+            arr2 = new IntWrapper[SIZE];
+            populate(arr1, arr2, 100);
+        }
+    }
+
+    public static class RefState00 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new RefWrapper[SIZE];
+            arr2 = new RefWrapper[SIZE];
+            populate(arr1, arr2, 0);
+        }
+    }
+
+    public static class RefState25 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new RefWrapper[SIZE];
+            arr2 = new RefWrapper[SIZE];
+            populate(arr1, arr2, 25);
+        }
+    }
+
+    public static class RefState50 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new RefWrapper[SIZE];
+            arr2 = new RefWrapper[SIZE];
+            populate(arr1, arr2, 50);
+        }
+    }
+
+    public static class RefState75 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new RefWrapper[SIZE];
+            arr2 = new RefWrapper[SIZE];
+            populate(arr1, arr2, 75);
+        }
+    }
+
+    public static class RefState100 extends RefState {
+        @Setup
+        public void setup() {
+            arr1 = new RefWrapper[SIZE];
+            arr2 = new RefWrapper[SIZE];
+            populate(arr1, arr2, 100);
+        }
+    }
+
+    public static class ValState00 extends ValState {
+        @Setup
+        public void setup() {
+            arr1 = new ValWrapper[SIZE];
+            arr2 = new ValWrapper[SIZE];
+            populate(arr1, arr2, 0);
+        }
+    }
+
+    public static class ValState25 extends ValState {
+        @Setup
+        public void setup() {
+            arr1 = new ValWrapper[SIZE];
+            arr2 = new ValWrapper[SIZE];
+            populate(arr1, arr2, 25);
+        }
+    }
+
+    public static class ValState50 extends ValState {
+        @Setup
+        public void setup() {
+            arr1 = new ValWrapper[SIZE];
+            arr2 = new ValWrapper[SIZE];
+            populate(arr1, arr2, 50);
+        }
+    }
+
+    public static class ValState75 extends ValState {
+        @Setup
+        public void setup() {
+            arr1 = new ValWrapper[SIZE];
+            arr2 = new ValWrapper[SIZE];
+            populate(arr1, arr2, 75);
+        }
+    }
+
+    public static class ValState100 extends ValState {
+        @Setup
+        public void setup() {
+            arr1 = new ValWrapper[SIZE];
+            arr2 = new ValWrapper[SIZE];
+            populate(arr1, arr2, 100);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/acmp/trivial/IdentityTrivialEquals.java b/test/micro/org/openjdk/bench/valhalla/acmp/trivial/IdentityTrivialEquals.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/acmp/trivial/IdentityTrivialEquals.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.acmp.trivial;
+
+import org.openjdk.bench.valhalla.types.R64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+/*
+ * to provide proper measurement the benchmark have to be executed in two modes:
+ *  -wm INDI
+ *  -wm BULK
+ */
+@Fork(1)
+@Warmup(iterations = 3, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class IdentityTrivialEquals {
+
+    Object o1 = new R64long(1);
+    Object o2 = new R64long(2);
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static boolean cmpEquals(Object a, Object b) {
+        return a == b;     }
+
+    @Benchmark
+    public boolean isCmp_null_null() {
+        return cmpEquals(null, null);
+    }
+
+    @Benchmark
+    public boolean isCmp_o1_null() {
+        return cmpEquals(o1, null);
+    }
+
+    @Benchmark
+    public boolean isCmp_null_o1() {
+        return cmpEquals(null, o1);
+    }
+
+    @Benchmark
+    public boolean isCmp_o1_o1() {
+        return cmpEquals(o1, o1);
+    }
+
+    @Benchmark
+    public boolean isCmp_o1_o2() {
+        return cmpEquals(o1, o2);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/acmp/trivial/IdentityTrivialNotEquals.java b/test/micro/org/openjdk/bench/valhalla/acmp/trivial/IdentityTrivialNotEquals.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/acmp/trivial/IdentityTrivialNotEquals.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.acmp.trivial;
+
+import org.openjdk.bench.valhalla.types.R64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+/*
+ * to provide proper measurement the benchmark have to be executed in two modes:
+ *  -wm INDI
+ *  -wm BULK
+ */
+
+@Fork(1)
+@Warmup(iterations = 3, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class IdentityTrivialNotEquals {
+
+    Object o1 = new R64long(1);
+    Object o2 = new R64long(2);
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static boolean cmpNotEquals(Object a, Object b) {
+        return a != b; // new acmp
+    }
+
+    @Benchmark
+    public boolean isNotCmp_null_null() {
+        return cmpNotEquals(null, null);
+    }
+
+    @Benchmark
+    public boolean isNotCmp_o1_null() {
+        return cmpNotEquals(o1, null);
+    }
+
+    @Benchmark
+    public boolean isNotCmp_null_o1() {
+        return cmpNotEquals(null, o1);
+    }
+
+    @Benchmark
+    public boolean isNotCmp_o1_o1() {
+        return cmpNotEquals(o1, o1);
+    }
+
+    @Benchmark
+    public boolean isNotCmp_o1_o2() {
+        return cmpNotEquals(o1, o2);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/copy/Identity32int.java b/test/micro/org/openjdk/bench/valhalla/array/copy/Identity32int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/copy/Identity32int.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.copy;
+
+import org.openjdk.bench.valhalla.array.util.StatesR32int;
+import org.openjdk.bench.valhalla.types.R32int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Identity32int extends StatesR32int {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void ref_copy(Ref_as_Ref s, Ref_as_Ref d) {
+        R32int[] src = s.arr;
+        R32int[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void ref_arraycopy(Ref_as_Ref s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/copy/Inline128int.java b/test/micro/org/openjdk/bench/valhalla/array/copy/Inline128int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/copy/Inline128int.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.copy;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ128int;
+import org.openjdk.bench.valhalla.types.Q128int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline128int extends StatesQ128int {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_copy(Val_as_Val s, Val_as_Val d) {
+        Q128int[] src = s.arr;
+        Q128int[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_arraycopy(Val_as_Val s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_copy(Ref_as_Ref s, Val_as_Val d) {
+        Q128int[] src = s.arr;
+        Q128int[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_arraycopy(Ref_as_Ref s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_copy(Val_as_Val s, Ref_as_Ref d) {
+        Q128int[] src = s.arr;
+        Q128int[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_arraycopy(Val_as_Val s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/copy/Inline32int.java b/test/micro/org/openjdk/bench/valhalla/array/copy/Inline32int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/copy/Inline32int.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.copy;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ32int;
+import org.openjdk.bench.valhalla.types.Q32int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline32int extends StatesQ32int {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_copy(Val_as_Val s, Val_as_Val d) {
+        Q32int[] src = s.arr;
+        Q32int[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_arraycopy(Val_as_Val s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_copy(Ref_as_Ref s, Val_as_Val d) {
+        Q32int[] src = s.arr;
+        Q32int[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_arraycopy(Ref_as_Ref s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_copy(Val_as_Val s, Ref_as_Ref d) {
+        Q32int[] src = s.arr;
+        Q32int[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_arraycopy(Val_as_Val s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/copy/Inline64byte.java b/test/micro/org/openjdk/bench/valhalla/array/copy/Inline64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/copy/Inline64byte.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.copy;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ64byte;
+import org.openjdk.bench.valhalla.types.Q64byte;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64byte extends StatesQ64byte {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_copy(Val_as_Val s, Val_as_Val d) {
+        Q64byte[] src = s.arr;
+        Q64byte[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_arraycopy(Val_as_Val s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_copy(Ref_as_Ref s, Val_as_Val d) {
+        Q64byte[] src = s.arr;
+        Q64byte[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_arraycopy(Ref_as_Ref s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_copy(Val_as_Val s, Ref_as_Ref d) {
+        Q64byte[] src = s.arr;
+        Q64byte[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_arraycopy(Val_as_Val s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/copy/Inline64int.java b/test/micro/org/openjdk/bench/valhalla/array/copy/Inline64int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/copy/Inline64int.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.copy;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ64int;
+import org.openjdk.bench.valhalla.types.Q64int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64int extends StatesQ64int {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_copy(Val_as_Val s, Val_as_Val d) {
+        Q64int[] src = s.arr;
+        Q64int[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_arraycopy(Val_as_Val s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_copy(Ref_as_Ref s, Val_as_Val d) {
+        Q64int[] src = s.arr;
+        Q64int[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_arraycopy(Ref_as_Ref s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_copy(Val_as_Val s, Ref_as_Ref d) {
+        Q64int[] src = s.arr;
+        Q64int[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_arraycopy(Val_as_Val s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/copy/InlineOpt.java b/test/micro/org/openjdk/bench/valhalla/array/copy/InlineOpt.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/copy/InlineOpt.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.copy;
+
+import org.openjdk.bench.valhalla.array.util.StatesQOpt;
+import org.openjdk.bench.valhalla.types.Int32;
+import org.openjdk.bench.valhalla.types.QOpt;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class InlineOpt extends StatesQOpt {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_copy(Val_as_Val s, Val_as_Val d) {
+        QOpt<Int32>[] src = s.arr;
+        QOpt<Int32>[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_arraycopy(Val_as_Val s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_copy(Ref_as_Ref s, Val_as_Val d) {
+        QOpt<Int32>[] src = s.arr;
+        QOpt<Int32>[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_arraycopy(Ref_as_Ref s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_copy(Val_as_Val s, Ref_as_Ref d) {
+        QOpt<Int32>[] src = s.arr;
+        QOpt<Int32>[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_arraycopy(Val_as_Val s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/copy/Primitive128.java b/test/micro/org/openjdk/bench/valhalla/array/copy/Primitive128.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/copy/Primitive128.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.copy;
+
+import org.openjdk.bench.valhalla.array.util.PrimitiveStates;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Primitive128 extends PrimitiveStates {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void p128int_copy(Primitive128int s, Primitive128int d) {
+        int[] src = s.arr;
+        int[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void p128int_arraycopy(Primitive128int s, Primitive128int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/copy/Primitive32.java b/test/micro/org/openjdk/bench/valhalla/array/copy/Primitive32.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/copy/Primitive32.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.copy;
+
+import org.openjdk.bench.valhalla.array.util.PrimitiveStates;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Primitive32 extends PrimitiveStates {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void p32int_copy(Primitive32int s, Primitive32int d) {
+        int[] src = s.arr;
+        int[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void p32int_arraycopy(Primitive32int s, Primitive32int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/copy/Primitive64.java b/test/micro/org/openjdk/bench/valhalla/array/copy/Primitive64.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/copy/Primitive64.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.copy;
+
+import org.openjdk.bench.valhalla.array.util.PrimitiveStates;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Primitive64 extends PrimitiveStates {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void p64byte_copy(Primitive64byte s, Primitive64byte d) {
+        byte[] src = s.arr;
+        byte[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void p64byte_arraycopy(Primitive64byte s, Primitive64byte d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void p64int_copy(Primitive64int s, Primitive64int d) {
+        int[] src = s.arr;
+        int[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void p64int_arraycopy(Primitive64int s, Primitive64int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void p64long_copy(Primitive64long s, Primitive64long d) {
+        long[] src = s.arr;
+        long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void p64long_arraycopy(Primitive64long s, Primitive64long d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/fill/Identity32int.java b/test/micro/org/openjdk/bench/valhalla/array/fill/Identity32int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/fill/Identity32int.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.fill;
+
+import org.openjdk.bench.valhalla.array.util.StatesR32int;
+import org.openjdk.bench.valhalla.types.R32int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+import java.util.Arrays;
+
+public class Identity32int extends StatesR32int {
+
+    public static class RefStaticField {
+        static R32int f = new R32int(42);
+    }
+
+    @State(Scope.Thread)
+    public static class RefInstanceField {
+        R32int f = new R32int(42);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Ref_fill(Ref_as_Ref st) {
+        R32int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new R32int();
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_fill(Ref_as_Ref st) {
+        R32int[] arr = st.arr;
+        R32int v = new R32int(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fill_fillstat(Ref_as_Ref st) {
+        R32int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = RefStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fillinst(Ref_as_Ref st, RefInstanceField f) {
+        R32int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, new R32int()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, new R32int(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_arrayfillstat(Ref_as_Ref st) {
+        Arrays.fill(st.arr, RefStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_arrayfillinst(Ref_as_Ref st, RefInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/fill/IdentityOpt.java b/test/micro/org/openjdk/bench/valhalla/array/fill/IdentityOpt.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/fill/IdentityOpt.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.fill;
+
+import org.openjdk.bench.valhalla.array.util.StatesROpt;
+import org.openjdk.bench.valhalla.types.Int32;
+import org.openjdk.bench.valhalla.types.ROpt;
+import org.openjdk.bench.valhalla.types.R32int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+import java.util.Arrays;
+
+public class IdentityOpt extends StatesROpt {
+
+    public static class RefStaticField {
+        static ROpt<Int32> f = ROpt.of(new R32int(42));
+    }
+
+    @State(Scope.Thread)
+    public static class RefInstanceField {
+        ROpt<Int32> f = ROpt.of(new R32int(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Ref_fill(Ref_as_Ref st) {
+        ROpt<Int32>[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = ROpt.of();
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_fill(Ref_as_Ref st) {
+        ROpt<Int32>[] arr = st.arr;
+        ROpt<Int32> v = ROpt.of(new R32int(42));
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fill_fillstat(Ref_as_Ref st) {
+        ROpt<Int32>[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = RefStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fillinst(Ref_as_Ref st, RefInstanceField f) {
+        ROpt<Int32>[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, ROpt.of()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, ROpt.of(new R32int(42)));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_arrayfillstat(Ref_as_Ref st) {
+        Arrays.fill(st.arr, RefStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_arrayfillinst(Ref_as_Ref st, RefInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/fill/Inline128int.java b/test/micro/org/openjdk/bench/valhalla/array/fill/Inline128int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/fill/Inline128int.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.fill;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ128int;
+import org.openjdk.bench.valhalla.types.Q128int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+import java.util.Arrays;
+
+public class Inline128int extends StatesQ128int {
+
+    public static class RefStaticField {
+        static Q128int f = new Q128int(42);
+    }
+
+    public static class ValStaticField {
+        static Q128int f = new Q128int(42);
+    }
+
+    @State(Scope.Thread)
+    public static class RefInstanceField {
+        Q128int f = new Q128int(42);
+    }
+
+    @State(Scope.Thread)
+    public static class ValInstanceField {
+        Q128int f = new Q128int(42);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Val_fill(Val_as_Val st) {
+        Q128int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q128int();
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Val_fill(Val_as_Val st) {
+        Q128int[] arr = st.arr;
+        Q128int v = new Q128int(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_fillstat(Val_as_Val st) {
+        Q128int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = RefStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_fillstat(Val_as_Val st) {
+        Q128int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = ValStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_fillinst(Val_as_Val st, RefInstanceField f) {
+        Q128int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_fillinst(Val_as_Val st, ValInstanceField f) {
+        Q128int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Ref_fill(Ref_as_Ref st) {
+        Q128int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q128int();
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_fill(Ref_as_Ref st) {
+        Q128int[] arr = st.arr;
+        Q128int v = new Q128int(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fillstat(Ref_as_Ref st) {
+        Q128int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = RefStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_fillstat(Ref_as_Ref st) {
+        Q128int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = ValStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fillinst(Ref_as_Ref st, RefInstanceField f) {
+        Q128int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_fillinst(Ref_as_Ref st, ValInstanceField f) {
+        Q128int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Val_arrayfill(Val_as_Val st) {
+        Arrays.fill(st.arr, new Q128int()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Val_arrayfill(Val_as_Val st) {
+        Arrays.fill(st.arr, new Q128int(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_arrayfillstat(Val_as_Val st) {
+        Arrays.fill(st.arr, RefStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_arrayfillstat(Val_as_Val st) {
+        Arrays.fill(st.arr, ValStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_arrayfillinst(Val_as_Val st, RefInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_arrayfillinst(Val_as_Val st, ValInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, new Q128int()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, new Q128int(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_arrayfillstat(Ref_as_Ref st) {
+        Arrays.fill(st.arr, RefStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_arrayfillstat(Ref_as_Ref st) {
+        Arrays.fill(st.arr, ValStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_arrayfillinst(Ref_as_Ref st, RefInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_arrayfillinst(Ref_as_Ref st, ValInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/fill/Inline32int.java b/test/micro/org/openjdk/bench/valhalla/array/fill/Inline32int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/fill/Inline32int.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.fill;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ32int;
+import org.openjdk.bench.valhalla.types.Q32int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+import java.util.Arrays;
+
+public class Inline32int extends StatesQ32int {
+
+    public static class RefStaticField {
+        static Q32int f = new Q32int(42);
+    }
+
+    public static class ValStaticField {
+        static Q32int f = new Q32int(42);
+    }
+
+    @State(Scope.Thread)
+    public static class RefInstanceField {
+        Q32int f = new Q32int(42);
+    }
+
+    @State(Scope.Thread)
+    public static class ValInstanceField {
+        Q32int f = new Q32int(42);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Val_fill(Val_as_Val st) {
+        Q32int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q32int();
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Val_fill(Val_as_Val st) {
+        Q32int[] arr = st.arr;
+        Q32int v = new Q32int(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_fillstat(Val_as_Val st) {
+        Q32int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = RefStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_fillstat(Val_as_Val st) {
+        Q32int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = ValStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_fillinst(Val_as_Val st, RefInstanceField f) {
+        Q32int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_fillinst(Val_as_Val st, ValInstanceField f) {
+        Q32int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Ref_fill(Ref_as_Ref st) {
+        Q32int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q32int()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_fill(Ref_as_Ref st) {
+        Q32int[] arr = st.arr;
+        Q32int v = new Q32int(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fillstat(Ref_as_Ref st) {
+        Q32int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = RefStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_fillstat(Ref_as_Ref st) {
+        Q32int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = ValStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fillinst(Ref_as_Ref st, RefInstanceField f) {
+        Q32int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_fillinst(Ref_as_Ref st, ValInstanceField f) {
+        Q32int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Val_arrayfill(Val_as_Val st) {
+        Arrays.fill(st.arr, new Q32int()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Val_arrayfill(Val_as_Val st) {
+        Arrays.fill(st.arr, new Q32int(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_arrayfillstat(Val_as_Val st) {
+        Arrays.fill(st.arr, RefStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_arrayfillstat(Val_as_Val st) {
+        Arrays.fill(st.arr, ValStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_arrayfillinst(Val_as_Val st, RefInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_arrayfillinst(Val_as_Val st, ValInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, new Q32int()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, new Q32int(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_arrayfillstat(Ref_as_Ref st) {
+        Arrays.fill(st.arr, RefStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_arrayfillstat(Ref_as_Ref st) {
+        Arrays.fill(st.arr, ValStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_arrayfillinst(Ref_as_Ref st, RefInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_arrayfillinst(Ref_as_Ref st, ValInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/fill/Inline64byte.java b/test/micro/org/openjdk/bench/valhalla/array/fill/Inline64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/fill/Inline64byte.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.fill;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ64byte;
+import org.openjdk.bench.valhalla.types.Q64byte;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+import java.util.Arrays;
+
+public class Inline64byte extends StatesQ64byte {
+
+    public static class RefStaticField {
+        static Q64byte f = new Q64byte(42);
+    }
+
+    public static class ValStaticField {
+        static Q64byte f = new Q64byte(42);
+    }
+
+    @State(Scope.Thread)
+    public static class RefInstanceField {
+        Q64byte f = new Q64byte(42);
+    }
+
+    @State(Scope.Thread)
+    public static class ValInstanceField {
+        Q64byte f = new Q64byte(42);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Val_fill(Val_as_Val st) {
+        Q64byte[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64byte()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Val_fill(Val_as_Val st) {
+        Q64byte[] arr = st.arr;
+        Q64byte v = new Q64byte(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_fillstat(Val_as_Val st) {
+        Q64byte[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = RefStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_fillstat(Val_as_Val st) {
+        Q64byte[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = ValStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_fillinst(Val_as_Val st, RefInstanceField f) {
+        Q64byte[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_fillinst(Val_as_Val st, ValInstanceField f) {
+        Q64byte[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Ref_fill(Ref_as_Ref st) {
+        Q64byte[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64byte()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_fill(Ref_as_Ref st) {
+        Q64byte[] arr = st.arr;
+        Q64byte v = new Q64byte(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fillstat(Ref_as_Ref st) {
+        Q64byte[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = RefStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_fillstat(Ref_as_Ref st) {
+        Q64byte[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = ValStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fillinst(Ref_as_Ref st, RefInstanceField f) {
+        Q64byte[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_fillinst(Ref_as_Ref st, ValInstanceField f) {
+        Q64byte[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Val_arrayfill(Val_as_Val st) {
+        Arrays.fill(st.arr, new Q64byte()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Val_arrayfill(Val_as_Val st) {
+        Arrays.fill(st.arr, new Q64byte(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_arrayfillstat(Val_as_Val st) {
+        Arrays.fill(st.arr, RefStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_arrayfillstat(Val_as_Val st) {
+        Arrays.fill(st.arr, ValStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_arrayfillinst(Val_as_Val st, RefInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_arrayfillinst(Val_as_Val st, ValInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, new Q64byte()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, new Q64byte(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_arrayfillstat(Ref_as_Ref st) {
+        Arrays.fill(st.arr, RefStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_arrayfillstat(Ref_as_Ref st) {
+        Arrays.fill(st.arr, ValStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_arrayfillinst(Ref_as_Ref st, RefInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_arrayfillinst(Ref_as_Ref st, ValInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/fill/Inline64int.java b/test/micro/org/openjdk/bench/valhalla/array/fill/Inline64int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/fill/Inline64int.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.fill;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ64int;
+import org.openjdk.bench.valhalla.types.Q64int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+import java.util.Arrays;
+
+public class Inline64int extends StatesQ64int {
+
+    public static class RefStaticField {
+        static Q64int f = new Q64int(42);
+    }
+
+    public static class ValStaticField {
+        static Q64int f = new Q64int(42);
+    }
+
+    @State(Scope.Thread)
+    public static class RefInstanceField {
+        Q64int f = new Q64int(42);
+    }
+
+    @State(Scope.Thread)
+    public static class ValInstanceField {
+        Q64int f = new Q64int(42);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Val_fill(Val_as_Val st) {
+        Q64int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64int()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Val_fill(Val_as_Val st) {
+        Q64int[] arr = st.arr;
+        Q64int v = new Q64int(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_fillstat(Val_as_Val st) {
+        Q64int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = RefStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_fillstat(Val_as_Val st) {
+        Q64int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = ValStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_fillinst(Val_as_Val st, RefInstanceField f) {
+        Q64int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_fillinst(Val_as_Val st, ValInstanceField f) {
+        Q64int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Ref_fill(Ref_as_Ref st) {
+        Q64int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64int()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_fill(Ref_as_Ref st) {
+        Q64int[] arr = st.arr;
+        Q64int v = new Q64int(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fillstat(Ref_as_Ref st) {
+        Q64int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = RefStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_fillstat(Ref_as_Ref st) {
+        Q64int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = ValStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fillinst(Ref_as_Ref st, RefInstanceField f) {
+        Q64int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_fillinst(Ref_as_Ref st, ValInstanceField f) {
+        Q64int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Val_arrayfill(Val_as_Val st) {
+        Arrays.fill(st.arr, new Q64int()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Val_arrayfill(Val_as_Val st) {
+        Arrays.fill(st.arr, new Q64int(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_arrayfillstat(Val_as_Val st) {
+        Arrays.fill(st.arr, RefStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_arrayfillstat(Val_as_Val st) {
+        Arrays.fill(st.arr, ValStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_arrayfillinst(Val_as_Val st, RefInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_arrayfillinst(Val_as_Val st, ValInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, new Q64int()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, new Q64int(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_arrayfillstat(Ref_as_Ref st) {
+        Arrays.fill(st.arr, RefStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_arrayfillstat(Ref_as_Ref st) {
+        Arrays.fill(st.arr, ValStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_arrayfillinst(Ref_as_Ref st, RefInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_arrayfillinst(Ref_as_Ref st, ValInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/fill/InlineOpt.java b/test/micro/org/openjdk/bench/valhalla/array/fill/InlineOpt.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/fill/InlineOpt.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.fill;
+
+import org.openjdk.bench.valhalla.array.util.StatesQOpt;
+import org.openjdk.bench.valhalla.types.Int32;
+import org.openjdk.bench.valhalla.types.QOpt;
+import org.openjdk.bench.valhalla.types.R32int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+import java.util.Arrays;
+
+public class InlineOpt extends StatesQOpt {
+
+    public static class RefStaticField {
+        static QOpt<Int32> f = QOpt.of(new R32int(42));
+    }
+
+    public static class ValStaticField {
+        static QOpt<Int32> f = QOpt.of(new R32int(42));
+    }
+
+    @State(Scope.Thread)
+    public static class RefInstanceField {
+        QOpt<Int32> f = QOpt.of(new R32int(42));
+    }
+
+    @State(Scope.Thread)
+    public static class ValInstanceField {
+        QOpt<Int32> f = QOpt.of(new R32int(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Val_fill(Val_as_Val st) {
+        QOpt<Int32>[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = QOpt.of();
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Val_fill(Val_as_Val st) {
+        QOpt<Int32>[] arr = st.arr;
+        QOpt<Int32> v = QOpt.of(new R32int(42));
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_fillstat(Val_as_Val st) {
+        QOpt<Int32>[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = RefStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_fillstat(Val_as_Val st) {
+        QOpt<Int32>[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = ValStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_fillinst(Val_as_Val st, RefInstanceField f) {
+        QOpt<Int32>[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_fillinst(Val_as_Val st, ValInstanceField f) {
+        QOpt<Int32>[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Ref_fill(Ref_as_Ref st) {
+        QOpt<Int32>[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = QOpt.of();
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_fill(Ref_as_Ref st) {
+        QOpt<Int32>[] arr = st.arr;
+        QOpt<Int32> v = QOpt.of(new R32int(42));
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fillstat(Ref_as_Ref st) {
+        QOpt<Int32>[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = RefStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_fillstat(Ref_as_Ref st) {
+        QOpt<Int32>[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = ValStaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fillinst(Ref_as_Ref st, RefInstanceField f) {
+        QOpt<Int32>[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_fillinst(Ref_as_Ref st, ValInstanceField f) {
+        QOpt<Int32>[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Val_arrayfill(Val_as_Val st) {
+        Arrays.fill(st.arr, QOpt.of() );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Val_arrayfill(Val_as_Val st) {
+        Arrays.fill(st.arr, QOpt.of(new R32int(42)));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_arrayfillstat(Val_as_Val st) {
+        Arrays.fill(st.arr, RefStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_arrayfillstat(Val_as_Val st) {
+        Arrays.fill(st.arr, ValStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_arrayfillinst(Val_as_Val st, RefInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_arrayfillinst(Val_as_Val st, ValInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, QOpt.of()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, QOpt.of(new R32int(42)));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_arrayfillstat(Ref_as_Ref st) {
+        Arrays.fill(st.arr, RefStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_arrayfillstat(Ref_as_Ref st) {
+        Arrays.fill(st.arr, ValStaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_arrayfillinst(Ref_as_Ref st, RefInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_arrayfillinst(Ref_as_Ref st, ValInstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/fill/Primitive64.java b/test/micro/org/openjdk/bench/valhalla/array/fill/Primitive64.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/fill/Primitive64.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.fill1;
+
+import org.openjdk.bench.valhalla.array.util.PrimitiveStates;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+import java.util.Arrays;
+
+public class Primitive64 extends PrimitiveStates {
+
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void fill0(Primitive64long st) {
+        long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = 42;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void fill1(Primitive64long st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = 42;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void arrayfill(Primitive64long st) {
+        Arrays.fill(st.arr, 42);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/read/Inline128int.java b/test/micro/org/openjdk/bench/valhalla/array/read/Inline128int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/read/Inline128int.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.read;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ128int;
+import org.openjdk.bench.valhalla.types.Int128;
+import org.openjdk.bench.valhalla.types.Q128int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline128int extends StatesQ128int {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void val_consume(Q128int v) {
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void ref_consume(Q128int v) {
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void int_consume(Int128 v) {
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Val_to_Val_read(Val_as_Val st) {
+        Q128int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Val_to_Ref_read(Val_as_Val st) {
+        Q128int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            ref_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Val_to_Int_read(Val_as_Val st) {
+        Q128int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            int_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Ref_to_Val_read(Val_as_Ref st) {
+        Q128int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Ref_to_Ref_read(Val_as_Ref st) {
+        Q128int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            ref_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Ref_to_Int_read(Val_as_Ref st) {
+        Q128int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            int_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Int_to_Int_read(Val_as_Int st) {
+        Int128[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            int_consume(arr[i]);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/read/Inline32int.java b/test/micro/org/openjdk/bench/valhalla/array/read/Inline32int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/read/Inline32int.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.read;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ32int;
+import org.openjdk.bench.valhalla.types.Int32;
+import org.openjdk.bench.valhalla.types.Q32int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline32int extends StatesQ32int {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void val_consume(Q32int v) {
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void ref_consume(Q32int v) {
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void int_consume(Int32 v) {
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Val_to_Val_read(Val_as_Val st) {
+        Q32int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Val_to_Ref_read(Val_as_Val st) {
+        Q32int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            ref_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Val_to_Int_read(Val_as_Val st) {
+        Q32int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            int_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Ref_to_Val_read(Val_as_Ref st) {
+        Q32int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Ref_to_Ref_read(Val_as_Ref st) {
+        Q32int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            ref_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Ref_to_Int_read(Val_as_Ref st) {
+        Q32int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            int_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Int_to_Int_read(Val_as_Int st) {
+        Int32[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            int_consume(arr[i]);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/read/Inline64byte.java b/test/micro/org/openjdk/bench/valhalla/array/read/Inline64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/read/Inline64byte.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.read;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ64byte;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64byte;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64byte extends StatesQ64byte {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void val_consume(Q64byte v) {
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void ref_consume(Q64byte v) {
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void int_consume(Int64 v) {
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Val_to_Val_read(Val_as_Val st) {
+        Q64byte[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Val_to_Ref_read(Val_as_Val st) {
+        Q64byte[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            ref_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Val_to_Int_read(Val_as_Val st) {
+        Q64byte[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            int_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Ref_to_Val_read(Val_as_Ref st) {
+        Q64byte[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Ref_to_Ref_read(Val_as_Ref st) {
+        Q64byte[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            ref_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Ref_to_Int_read(Val_as_Ref st) {
+        Q64byte[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            int_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Int_to_Int_read(Val_as_Int st) {
+        Int64[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            int_consume(arr[i]);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/read/Inline64int.java b/test/micro/org/openjdk/bench/valhalla/array/read/Inline64int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/read/Inline64int.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.read;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ64int;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64int extends StatesQ64int {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void val_consume(Q64int v) {
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void ref_consume(Q64int v) {
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void int_consume(Int64 v) {
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Val_to_Val_read(Val_as_Val st) {
+        Q64int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Val_to_Ref_read(Val_as_Val st) {
+        Q64int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            ref_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Val_to_Int_read(Val_as_Val st) {
+        Q64int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            int_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Ref_to_Val_read(Val_as_Ref st) {
+        Q64int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Ref_to_Ref_read(Val_as_Ref st) {
+        Q64int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            ref_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Ref_to_Int_read(Val_as_Ref st) {
+        Q64int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            int_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Int_to_Int_read(Val_as_Int st) {
+        Int64[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            int_consume(arr[i]);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/read/Primitive128int.java b/test/micro/org/openjdk/bench/valhalla/array/read/Primitive128int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/read/Primitive128int.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.read;
+
+import org.openjdk.bench.valhalla.array.util.PrimitiveStates;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Primitive128int extends PrimitiveStates {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void consume_int(int v0, int v1, int v2, int v3) {
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void p128int_read(Primitive128int st) {
+        int[] values = st.arr;
+        for (int i = 0; i < values.length; i+=4) {
+            consume_int(values[i], values[i+1], values[i+2], values[i+3]);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/read/Primitive32int.java b/test/micro/org/openjdk/bench/valhalla/array/read/Primitive32int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/read/Primitive32int.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.read;
+
+import org.openjdk.bench.valhalla.array.util.PrimitiveStates;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Primitive32int extends PrimitiveStates {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void consume_int(int v) {
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void p32int_read(Primitive32int st) {
+        int[] values = st.arr;
+        for (int i = 0; i < values.length; i++) {
+            consume_int(values[i]);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/read/Primitive64byte.java b/test/micro/org/openjdk/bench/valhalla/array/read/Primitive64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/read/Primitive64byte.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.read;
+
+import org.openjdk.bench.valhalla.array.util.PrimitiveStates;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Primitive64byte extends PrimitiveStates {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void consume_byte(byte v0, byte v1, byte v2, byte v3, byte v4, byte v5, byte v6, byte v7) {
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void p64byte_read(Primitive64byte st) {
+        byte[] values = st.arr;
+        for (int i = 0; i < values.length; i+=8) {
+            consume_byte(values[i], values[i+1], values[i+2], values[i+3], values[i+4], values[i+5], values[i+6], values[i+7]);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/read/Primitive64int.java b/test/micro/org/openjdk/bench/valhalla/array/read/Primitive64int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/read/Primitive64int.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.read;
+
+import org.openjdk.bench.valhalla.array.util.PrimitiveStates;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Primitive64int extends PrimitiveStates {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void consume_int(int v0, int v1) {
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void read(Primitive64int st) {
+        int[] values = st.arr;
+        for (int i = 0; i < values.length; i+=2) {
+            consume_int(values[i], values[i+1]);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/read/Primitive64long.java b/test/micro/org/openjdk/bench/valhalla/array/read/Primitive64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/read/Primitive64long.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.read;
+
+import org.openjdk.bench.valhalla.array.util.PrimitiveStates;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Primitive64long extends PrimitiveStates {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void consume_long(long v) {
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void p64long_read(Primitive64long st) {
+        long[] values = st.arr;
+        for (int i = 0; i < values.length; i++) {
+            consume_long(values[i]);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/set/Inline128int.java b/test/micro/org/openjdk/bench/valhalla/array/set/Inline128int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/set/Inline128int.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.set;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ128int;
+import org.openjdk.bench.valhalla.types.Q128int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline128int extends StatesQ128int {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Val_set(Val_as_Val st) {
+        Q128int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q128int(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_set(Ref_as_Ref st) {
+        Q128int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q128int(i);
+        }
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q128int getRef(int i) {
+        return new Q128int(i);
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q128int getVal(int i) {
+        return new Q128int(i);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_set(Val_as_Val st) {
+        Q128int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = getRef(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_set(Ref_as_Ref st) {
+        Q128int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = getRef(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_set(Val_as_Val st) {
+        Q128int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = getVal(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_set(Ref_as_Ref st) {
+        Q128int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = getVal(i);
+        }
+    }
+
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/set/Inline32int.java b/test/micro/org/openjdk/bench/valhalla/array/set/Inline32int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/set/Inline32int.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.set;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ32int;
+import org.openjdk.bench.valhalla.types.Q32int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline32int extends StatesQ32int {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Val_set(Val_as_Val st) {
+        Q32int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q32int(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_set(Ref_as_Ref st) {
+        Q32int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q32int(i);
+        }
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q32int getRef(int i) {
+        return new Q32int(i);
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q32int getVal(int i) {
+        return new Q32int(i);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_set(Val_as_Val st) {
+        Q32int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = getRef(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_set(Ref_as_Ref st) {
+        Q32int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = getRef(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_set(Val_as_Val st) {
+        Q32int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = getVal(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_set(Ref_as_Ref st) {
+        Q32int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = getVal(i);
+        }
+    }
+
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/set/Inline64byte.java b/test/micro/org/openjdk/bench/valhalla/array/set/Inline64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/set/Inline64byte.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.set;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ64byte;
+import org.openjdk.bench.valhalla.types.Q64byte;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64byte extends StatesQ64byte {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Val_set(Val_as_Val st) {
+        Q64byte[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64byte(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_set(Ref_as_Ref st) {
+        Q64byte[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64byte(i);
+        }
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q64byte getRef(int i) {
+        return new Q64byte(i);
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q64byte getVal(int i) {
+        return new Q64byte(i);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_set(Val_as_Val st) {
+        Q64byte[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = getRef(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_set(Ref_as_Ref st) {
+        Q64byte[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = getRef(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_set(Val_as_Val st) {
+        Q64byte[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = getVal(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_set(Ref_as_Ref st) {
+        Q64byte[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = getVal(i);
+        }
+    }
+
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/set/Inline64int.java b/test/micro/org/openjdk/bench/valhalla/array/set/Inline64int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/set/Inline64int.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.set;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ64int;
+import org.openjdk.bench.valhalla.types.Q64int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64int extends StatesQ64int {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Val_set(Val_as_Val st) {
+        Q64int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64int(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_set(Ref_as_Ref st) {
+        Q64int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64int(i);
+        }
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q64int getRef(int i) {
+        return new Q64int(i);
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q64int getVal(int i) {
+        return new Q64int(i);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_set(Val_as_Val st) {
+        Q64int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = getRef(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_set(Ref_as_Ref st) {
+        Q64int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = getRef(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_set(Val_as_Val st) {
+        Q64int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = getVal(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_set(Ref_as_Ref st) {
+        Q64int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = getVal(i);
+        }
+    }
+
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/set/Primitive32.java b/test/micro/org/openjdk/bench/valhalla/array/set/Primitive32.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/set/Primitive32.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.set;
+
+import org.openjdk.bench.valhalla.array.util.PrimitiveStates;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Primitive32 extends PrimitiveStates {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void set(Primitive32int st) {
+        int[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = i;
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/set/Primitive64.java b/test/micro/org/openjdk/bench/valhalla/array/set/Primitive64.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/set/Primitive64.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.set;
+
+import org.openjdk.bench.valhalla.array.util.PrimitiveStates;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Primitive64 extends PrimitiveStates {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void set0(Primitive64long st) {
+        long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = i;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void set1(Primitive64long st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = i;
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/sum/IdentityOpt.java b/test/micro/org/openjdk/bench/valhalla/array/sum/IdentityOpt.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/sum/IdentityOpt.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.sum;
+
+import org.openjdk.bench.valhalla.array.util.StatesROpt;
+import org.openjdk.bench.valhalla.types.Int32;
+import org.openjdk.bench.valhalla.types.Opt;
+import org.openjdk.bench.valhalla.types.ROpt;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class IdentityOpt extends StatesROpt {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Ref_as_Ref_fields(Ref_as_Ref st) {
+        int s = 0;
+        ROpt<Int32>[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].value.intValue();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Ref_as_Ref_sum(Ref_as_Ref st) {
+        int s = 0;
+        ROpt<Int32>[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].get().intValue();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Int_as_Int_sum(Int_as_Int st) {
+        int s = 0;
+        Opt<Int32>[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].get().intValue();
+        }
+        return s;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/sum/Inline128int.java b/test/micro/org/openjdk/bench/valhalla/array/sum/Inline128int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/sum/Inline128int.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.sum;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ128int;
+import org.openjdk.bench.valhalla.types.ByInt;
+import org.openjdk.bench.valhalla.types.Q128int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline128int extends StatesQ128int {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Val_fields(Val_as_Val st) {
+        int s = 0;
+        Q128int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].v0.v0.v0;
+            s += arr[i].v0.v1.v0;
+            s += arr[i].v1.v0.v0;
+            s += arr[i].v1.v1.v0;
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Ref_fields(Val_as_Ref st) {
+        int s = 0;
+        Q128int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].v0.v0.v0;
+            s += arr[i].v0.v1.v0;
+            s += arr[i].v1.v0.v0;
+            s += arr[i].v1.v1.v0;
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Ref_as_Ref_fields(Ref_as_Ref st) {
+        int s = 0;
+        Q128int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].v0.v0.v0;
+            s += arr[i].v0.v1.v0;
+            s += arr[i].v1.v0.v0;
+            s += arr[i].v1.v1.v0;
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Val_sum(Val_as_Val st) {
+        int s = 0;
+        Q128int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].intSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Ref_sum(Val_as_Ref st) {
+        int s = 0;
+        Q128int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].intSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Ref_as_Ref_sum(Ref_as_Ref st) {
+        int s = 0;
+        Q128int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].intSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Int_sum(Val_as_By st) {
+        int s = 0;
+        ByInt[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].intSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Ref_as_Int_sum(Ref_as_By st) {
+        int s = 0;
+        ByInt[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].intSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Int_as_Int_sum(By_as_By st) {
+        int s = 0;
+        ByInt[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].intSum();
+        }
+        return s;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/sum/Inline32int.java b/test/micro/org/openjdk/bench/valhalla/array/sum/Inline32int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/sum/Inline32int.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.sum;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ32int;
+import org.openjdk.bench.valhalla.types.ByInt;
+import org.openjdk.bench.valhalla.types.Q32int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline32int extends StatesQ32int {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Val_fields(Val_as_Val st) {
+        int s = 0;
+        Q32int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].v0;
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Ref_fields(Val_as_Ref st) {
+        int s = 0;
+        Q32int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].v0;
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Ref_as_Ref_fields(Ref_as_Ref st) {
+        int s = 0;
+        Q32int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].v0;
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Val_sum(Val_as_Val st) {
+        int s = 0;
+        Q32int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].intSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Ref_sum(Val_as_Ref st) {
+        int s = 0;
+        Q32int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].intSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Ref_as_Ref_sum(Ref_as_Ref st) {
+        int s = 0;
+        Q32int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].intSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Int_sum(Val_as_By st) {
+        int s = 0;
+        ByInt[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].intSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Int_as_Int_sum(By_as_By st) {
+        int s = 0;
+        ByInt[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].intSum();
+        }
+        return s;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/sum/Inline64byte.java b/test/micro/org/openjdk/bench/valhalla/array/sum/Inline64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/sum/Inline64byte.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.sum;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ64byte;
+import org.openjdk.bench.valhalla.types.ByByte;
+import org.openjdk.bench.valhalla.types.Q64byte;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64byte extends StatesQ64byte {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte Val_as_Val_fields(Val_as_Val st) {
+        byte s = 0;
+        Q64byte[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].v0.v0;
+            s += arr[i].v0.v1;
+            s += arr[i].v0.v2;
+            s += arr[i].v0.v3;
+            s += arr[i].v1.v0;
+            s += arr[i].v1.v1;
+            s += arr[i].v1.v2;
+            s += arr[i].v1.v3;
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte Val_as_Ref_fields(Val_as_Ref st) {
+        byte s = 0;
+        Q64byte[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].v0.v0;
+            s += arr[i].v0.v1;
+            s += arr[i].v0.v2;
+            s += arr[i].v0.v3;
+            s += arr[i].v1.v0;
+            s += arr[i].v1.v1;
+            s += arr[i].v1.v2;
+            s += arr[i].v1.v3;
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte Ref_as_Ref_fields(Ref_as_Ref st) {
+        byte s = 0;
+        Q64byte[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].v0.v0;
+            s += arr[i].v0.v1;
+            s += arr[i].v0.v2;
+            s += arr[i].v0.v3;
+            s += arr[i].v1.v0;
+            s += arr[i].v1.v1;
+            s += arr[i].v1.v2;
+            s += arr[i].v1.v3;
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte Val_as_Val_sum(Val_as_Val st) {
+        byte s = 0;
+        Q64byte[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].byteSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte Val_as_Ref_sum(Val_as_Ref st) {
+        byte s = 0;
+        Q64byte[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].byteSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte Ref_as_Ref_sum(Ref_as_Ref st) {
+        byte s = 0;
+        Q64byte[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].byteSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Int_sum(Val_as_By st) {
+        int s = 0;
+        ByByte[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].byteSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Int_as_Int_sum(By_as_By st) {
+        int s = 0;
+        ByByte[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].byteSum();
+        }
+        return s;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/sum/Inline64int.java b/test/micro/org/openjdk/bench/valhalla/array/sum/Inline64int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/sum/Inline64int.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.sum;
+
+import org.openjdk.bench.valhalla.array.util.StatesQ64int;
+import org.openjdk.bench.valhalla.types.ByInt;
+import org.openjdk.bench.valhalla.types.Q64int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64int extends StatesQ64int {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Val_fields(Val_as_Val st) {
+        int s = 0;
+        Q64int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].v0.v0;
+            s += arr[i].v1.v0;
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Ref_fields(Val_as_Ref st) {
+        int s = 0;
+        Q64int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].v0.v0;
+            s += arr[i].v1.v0;
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Ref_as_Ref_fields(Ref_as_Ref st) {
+        int s = 0;
+        Q64int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].v0.v0;
+            s += arr[i].v1.v0;
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Val_sum(Val_as_Val st) {
+        int s = 0;
+        Q64int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].intSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Ref_sum(Val_as_Ref st) {
+        int s = 0;
+        Q64int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].intSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Ref_as_Ref_sum(Ref_as_Ref st) {
+        int s = 0;
+        Q64int[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].intSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Int_sum(Val_as_By st) {
+        int s = 0;
+        ByInt[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].intSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Ref_as_Int_sum(Ref_as_By st) {
+        int s = 0;
+        ByInt[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].intSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Int_as_Int_sum(By_as_By st) {
+        int s = 0;
+        ByInt[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].intSum();
+        }
+        return s;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/sum/InlineOpt.java b/test/micro/org/openjdk/bench/valhalla/array/sum/InlineOpt.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/sum/InlineOpt.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.sum;
+
+import org.openjdk.bench.valhalla.array.util.StatesQOpt;
+import org.openjdk.bench.valhalla.types.Int32;
+import org.openjdk.bench.valhalla.types.Opt;
+import org.openjdk.bench.valhalla.types.QOpt;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class InlineOpt extends StatesQOpt {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Val_fields(Val_as_Val st) {
+        int s = 0;
+        QOpt<Int32>[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].value.intValue();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Ref_fields(Val_as_Ref st) {
+        int s = 0;
+        QOpt<Int32>[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].value.intValue();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Ref_as_Ref_fields(Ref_as_Ref st) {
+        int s = 0;
+        QOpt<Int32>[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].value.intValue();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Val_sum(Val_as_Val st) {
+        int s = 0;
+        QOpt<Int32>[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].get().intValue();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Ref_sum(Val_as_Ref st) {
+        int s = 0;
+        QOpt<Int32>[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].get().intValue();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Ref_as_Ref_sum(Ref_as_Ref st) {
+        int s = 0;
+        QOpt<Int32>[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].get().intValue();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Int_sum(Val_as_Int st) {
+        int s = 0;
+        Opt<Int32>[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].get().intValue();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Int_as_Int_sum(Int_as_Int st) {
+        int s = 0;
+        Opt<Int32>[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].get().intValue();
+        }
+        return s;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/sum/Primitive128int.java b/test/micro/org/openjdk/bench/valhalla/array/sum/Primitive128int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/sum/Primitive128int.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.sum;
+
+import org.openjdk.bench.valhalla.array.util.PrimitiveStates;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Primitive128int extends PrimitiveStates {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int sum(Primitive128int st) {
+        int s = 0;
+        int[] values = st.arr;
+        for (int i = 0; i < values.length; i++) {
+            s += values[i];
+        }
+        return s;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/sum/Primitive32int.java b/test/micro/org/openjdk/bench/valhalla/array/sum/Primitive32int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/sum/Primitive32int.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.sum;
+
+import org.openjdk.bench.valhalla.array.util.PrimitiveStates;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Primitive32int extends PrimitiveStates {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int sum(Primitive32int st) {
+        int s = 0;
+        int[] values = st.arr;
+        for (int i = 0; i < values.length; i++) {
+            s += values[i];
+        }
+        return s;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/sum/Primitive64byte.java b/test/micro/org/openjdk/bench/valhalla/array/sum/Primitive64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/sum/Primitive64byte.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.sum;
+
+import org.openjdk.bench.valhalla.array.util.PrimitiveStates;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Primitive64byte extends PrimitiveStates {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte sum(Primitive64byte st) {
+        byte s = 0;
+        byte[] values = st.arr;
+        for (int i = 0; i < values.length; i++) {
+            s += values[i];
+        }
+        return s;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/sum/Primitive64int.java b/test/micro/org/openjdk/bench/valhalla/array/sum/Primitive64int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/sum/Primitive64int.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.sum;
+
+import org.openjdk.bench.valhalla.array.util.PrimitiveStates;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Primitive64int extends PrimitiveStates {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int sum(Primitive64int st) {
+        int s = 0;
+        int[] values = st.arr;
+        for (int i = 0; i < values.length; i++) {
+            s += values[i];
+        }
+        return s;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/sum/Primitive64long.java b/test/micro/org/openjdk/bench/valhalla/array/sum/Primitive64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/sum/Primitive64long.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.sum;
+
+import org.openjdk.bench.valhalla.array.util.PrimitiveStates;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Primitive64long extends PrimitiveStates {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long sum0(Primitive64long st) {
+        long s = 0;
+        long[] values = st.arr;
+        for (int i = 0; i < values.length; i++) {
+            s += values[i];
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long sum1(Primitive64long st) {
+        long s = 0;
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            s += st.arr[i];
+        }
+        return s;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/util/PrimitiveStates.java b/test/micro/org/openjdk/bench/valhalla/array/util/PrimitiveStates.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/util/PrimitiveStates.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.util;
+
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class PrimitiveStates extends SizeBase {
+
+    public static abstract class ByteState extends SizeState {
+        public byte[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = (byte) i;
+            }
+        }
+    }
+
+    public static abstract class IntState extends SizeState {
+        public int[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = i;
+            }
+        }
+    }
+
+    public static abstract class LongState extends SizeState {
+        public long[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = i;
+            }
+        }
+    }
+
+    public static class Primitive32int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new int[size];
+            fill();
+        }
+    }
+
+    public static class Primitive64byte extends ByteState {
+        @Setup
+        public void setup() {
+            arr = new byte[size * 8];
+            fill();
+        }
+    }
+
+    public static class Primitive64int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new int[size * 2];
+            fill();
+        }
+    }
+
+    public static class Primitive64long extends LongState {
+        @Setup
+        public void setup() {
+            arr = new long[size];
+            fill();
+        }
+    }
+
+    public static class Primitive128int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new int[size * 4];
+            fill();
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/util/StatesQ128byte.java b/test/micro/org/openjdk/bench/valhalla/array/util/StatesQ128byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/util/StatesQ128byte.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.util;
+
+import org.openjdk.bench.valhalla.types.Int128;
+import org.openjdk.bench.valhalla.types.Q128byte;
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class StatesQ128byte extends SizeBase {
+
+    public static abstract class ObjState extends SizeState {
+        public Object[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q128byte(i);
+            }
+        }
+    }
+
+    public static abstract class IntState extends SizeState {
+        public Int128[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q128byte(i);
+            }
+        }
+    }
+
+    public static abstract class RefState extends SizeState {
+        public Q128byte[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q128byte(i);
+            }
+        }
+    }
+
+    public static abstract class ValState extends SizeState {
+        public Q128byte[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q128byte(i);
+            }
+        }
+    }
+
+    // naming convention: <runtime array type>_as_<static array type>
+
+    public static class Obj_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Int128[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Q128byte[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Q128byte[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Int128[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Q128byte[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Q128byte[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new Q128byte[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new Q128byte[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Val extends ValState {
+        @Setup
+        public void setup() {
+            arr = new Q128byte[size];
+            fill();
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/util/StatesQ128int.java b/test/micro/org/openjdk/bench/valhalla/array/util/StatesQ128int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/util/StatesQ128int.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.util;
+
+import org.openjdk.bench.valhalla.types.ByInt;
+import org.openjdk.bench.valhalla.types.Int128;
+import org.openjdk.bench.valhalla.types.Q128int;
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class StatesQ128int extends SizeBase {
+
+    public static abstract class ObjState extends SizeState {
+        public Object[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q128int(i);
+            }
+        }
+    }
+
+    public static abstract class IntState extends SizeState {
+        public Int128[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q128int(i);
+            }
+        }
+    }
+
+    public static abstract class RefState extends SizeState {
+        public Q128int[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q128int(i);
+            }
+        }
+    }
+
+    public static abstract class ValState extends SizeState {
+        public Q128int[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q128int(i);
+            }
+        }
+    }
+
+    // naming convention: <runtime array type>_as_<static array type>
+
+    public static class Obj_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Int128[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Q128int[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Q128int[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Int128[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Q128int[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Q128int[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new Q128int[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new Q128int[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Val extends ValState {
+        @Setup
+        public void setup() {
+            arr = new Q128int[size];
+            fill();
+        }
+    }
+
+    public static abstract class ByState extends SizeState {
+        public ByInt[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q128int(i);
+            }
+        }
+    }
+
+    public static class Val_as_By extends StatesQ32int.ByState {
+        @Setup
+        public void setup() {
+            arr = new Q128int[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_By extends StatesQ32int.ByState {
+        @Setup
+        public void setup() {
+            arr = new Q128int[size];
+            fill();
+        }
+    }
+
+    public static class By_as_By extends StatesQ32int.ByState {
+        @Setup
+        public void setup() {
+            arr = new ByInt[size];
+            fill();
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/util/StatesQ128long.java b/test/micro/org/openjdk/bench/valhalla/array/util/StatesQ128long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/util/StatesQ128long.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.util;
+
+import org.openjdk.bench.valhalla.types.Int128;
+import org.openjdk.bench.valhalla.types.Q128long;
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class StatesQ128long extends SizeBase {
+
+    public static abstract class ObjState extends SizeState {
+        public Object[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q128long(i);
+            }
+        }
+    }
+
+    public static abstract class IntState extends SizeState {
+        public Int128[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q128long(i);
+            }
+        }
+    }
+
+    public static abstract class RefState extends SizeState {
+        public Q128long[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q128long(i);
+            }
+        }
+    }
+
+    public static abstract class ValState extends SizeState {
+        public Q128long[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q128long(i);
+            }
+        }
+    }
+
+    // naming convention: <runtime array type>_as_<static array type>
+
+    public static class Obj_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Int128[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Q128long[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Q128long[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Int128[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Q128long[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Q128long[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new Q128long[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new Q128long[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Val extends ValState {
+        @Setup
+        public void setup() {
+            arr = new Q128long[size];
+            fill();
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/util/StatesQ32int.java b/test/micro/org/openjdk/bench/valhalla/array/util/StatesQ32int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/util/StatesQ32int.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.util;
+
+import org.openjdk.bench.valhalla.types.ByInt;
+import org.openjdk.bench.valhalla.types.Int32;
+import org.openjdk.bench.valhalla.types.Q32int;
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class StatesQ32int extends SizeBase {
+
+    public static abstract class ObjState extends SizeState {
+        public Object[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q32int(i);
+            }
+        }
+    }
+
+    public static abstract class IntState extends SizeState {
+        public Int32[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q32int(i);
+            }
+        }
+    }
+
+    public static abstract class RefState extends SizeState {
+        public Q32int[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q32int(i);
+            }
+        }
+    }
+
+    public static abstract class ValState extends SizeState {
+        public Q32int[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q32int(i);
+            }
+        }
+    }
+
+    // naming convention: <runtime array type>_as_<static array type>
+
+    public static class Obj_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Int32[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Q32int[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Q32int[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Int32[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Q32int[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Q32int[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new Q32int[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new Q32int[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Val extends ValState {
+        @Setup
+        public void setup() {
+            arr = new Q32int[size];
+            fill();
+        }
+    }
+
+    public static abstract class ByState extends SizeState {
+        public ByInt[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q32int(i);
+            }
+        }
+    }
+
+    public static class Val_as_By extends ByState {
+        @Setup
+        public void setup() {
+            arr = new Q32int[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_By extends ByState {
+        @Setup
+        public void setup() {
+            arr = new Q32int[size];
+            fill();
+        }
+    }
+
+    public static class By_as_By extends ByState {
+        @Setup
+        public void setup() {
+            arr = new ByInt[size];
+            fill();
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/util/StatesQ64byte.java b/test/micro/org/openjdk/bench/valhalla/array/util/StatesQ64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/util/StatesQ64byte.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.util;
+
+import org.openjdk.bench.valhalla.types.ByByte;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64byte;
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class StatesQ64byte extends SizeBase {
+
+    public static abstract class ObjState extends SizeState {
+        public Object[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q64byte(i);
+            }
+        }
+    }
+
+    public static abstract class IntState extends SizeState {
+        public Int64[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q64byte(i);
+            }
+        }
+    }
+
+    public static abstract class RefState extends SizeState {
+        public Q64byte[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q64byte(i);
+            }
+        }
+    }
+
+    public static abstract class ValState extends SizeState {
+        public Q64byte[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q64byte(i);
+            }
+        }
+    }
+
+    // naming convention: <runtime array type>_as_<static array type>
+
+    public static class Obj_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Int64[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Q64byte[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Q64byte[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Int64[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Q64byte[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Q64byte[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new Q64byte[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new Q64byte[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Val extends ValState {
+        @Setup
+        public void setup() {
+            arr = new Q64byte[size];
+            fill();
+        }
+    }
+
+    public static abstract class ByState extends SizeState {
+        public ByByte[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q64byte(i);
+            }
+        }
+    }
+
+    public static class Val_as_By extends ByState {
+        @Setup
+        public void setup() {
+            arr = new Q64byte[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_By extends ByState {
+        @Setup
+        public void setup() {
+            arr = new Q64byte[size];
+            fill();
+        }
+    }
+
+    public static class By_as_By extends ByState {
+        @Setup
+        public void setup() {
+            arr = new ByByte[size];
+            fill();
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/util/StatesQ64int.java b/test/micro/org/openjdk/bench/valhalla/array/util/StatesQ64int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/util/StatesQ64int.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.util;
+
+import org.openjdk.bench.valhalla.types.ByInt;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64int;
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class StatesQ64int extends SizeBase {
+
+    public static abstract class ObjState extends SizeState {
+        public Object[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q64int(i);
+            }
+        }
+    }
+
+    public static abstract class IntState extends SizeState {
+        public Int64[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q64int(i);
+            }
+        }
+    }
+
+    public static abstract class RefState extends SizeState {
+        public Q64int[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q64int(i);
+            }
+        }
+    }
+
+    public static abstract class ValState extends SizeState {
+        public Q64int[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q64int(i);
+            }
+        }
+    }
+
+    // naming convention: <runtime array type>_as_<static array type>
+
+    public static class Obj_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Int64[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Q64int[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Q64int[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Int64[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Q64int[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Q64int[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new Q64int[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new Q64int[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Val extends ValState {
+        @Setup
+        public void setup() {
+            arr = new Q64int[size];
+            fill();
+        }
+    }
+
+    public static abstract class ByState extends SizeState {
+        public ByInt[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q64int(i);
+            }
+        }
+    }
+
+    public static class Val_as_By extends StatesQ32int.ByState {
+        @Setup
+        public void setup() {
+            arr = new Q64int[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_By extends StatesQ32int.ByState {
+        @Setup
+        public void setup() {
+            arr = new Q64int[size];
+            fill();
+        }
+    }
+
+    public static class By_as_By extends StatesQ32int.ByState {
+        @Setup
+        public void setup() {
+            arr = new ByInt[size];
+            fill();
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/util/StatesQOpt.java b/test/micro/org/openjdk/bench/valhalla/array/util/StatesQOpt.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/util/StatesQOpt.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.util;
+
+import org.openjdk.bench.valhalla.types.Int32;
+import org.openjdk.bench.valhalla.types.Opt;
+import org.openjdk.bench.valhalla.types.QOpt;
+import org.openjdk.bench.valhalla.types.R32int;
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class StatesQOpt extends SizeBase {
+
+    public static abstract class ObjState extends SizeState {
+        public Object[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] =  QOpt.of(new R32int(i));
+            }
+        }
+    }
+
+    public static abstract class IntState extends SizeState {
+        public Opt<Int32>[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] =  QOpt.of(new R32int(i));
+            }
+        }
+    }
+
+    public static abstract class RefState extends SizeState {
+        public QOpt<Int32>[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] =  QOpt.of(new R32int(i));
+            }
+        }
+    }
+
+    public static abstract class ValState extends SizeState {
+        public QOpt<Int32>[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] =  QOpt.of(new R32int(i));
+            }
+        }
+    }
+
+    // naming convention: <runtime array type>_as_<static array type>
+
+    public static class Obj_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Opt[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new QOpt[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new QOpt[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Opt[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new QOpt[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new QOpt[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new QOpt[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new QOpt[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Val extends ValState {
+        @Setup
+        public void setup() {
+            arr = new QOpt[size];
+            fill();
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/util/StatesR128int.java b/test/micro/org/openjdk/bench/valhalla/array/util/StatesR128int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/util/StatesR128int.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.util;
+
+import org.openjdk.bench.valhalla.types.Int128;
+import org.openjdk.bench.valhalla.types.R128int;
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class StatesR128int extends SizeBase {
+
+    public static abstract class ObjState extends SizeState {
+        public Object[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new R128int(i);
+            }
+        }
+    }
+
+    public static abstract class IntState extends SizeState {
+        public Int128[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new R128int(i);
+            }
+        }
+    }
+
+    public static abstract class RefState extends SizeState {
+        public R128int[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new R128int(i);
+            }
+        }
+    }
+
+    // naming convention: <runtime array type>_as_<static array type>
+
+    public static class Obj_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Int128[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new R128int[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Int128[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new R128int[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new R128int[size];
+            fill();
+        }
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/util/StatesR32int.java b/test/micro/org/openjdk/bench/valhalla/array/util/StatesR32int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/util/StatesR32int.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.util;
+
+import org.openjdk.bench.valhalla.types.Int32;
+import org.openjdk.bench.valhalla.types.R32int;
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class StatesR32int extends SizeBase {
+
+    public static abstract class ObjState extends SizeState {
+        public Object[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new R32int(i);
+            }
+        }
+    }
+
+    public static abstract class IntState extends SizeState {
+        public Int32[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new R32int(i);
+            }
+        }
+    }
+
+    public static abstract class RefState extends SizeState {
+        public R32int[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new R32int(i);
+            }
+        }
+    }
+
+    // naming convention: <runtime array type>_as_<static array type>
+
+    public static class Obj_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Int32[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new R32int[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Int32[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new R32int[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new R32int[size];
+            fill();
+        }
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/util/StatesR64byte.java b/test/micro/org/openjdk/bench/valhalla/array/util/StatesR64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/util/StatesR64byte.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.util;
+
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.R64byte;
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class StatesR64byte extends SizeBase {
+
+    public static abstract class ObjState extends SizeState {
+        public Object[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new R64byte(i);
+            }
+        }
+    }
+
+    public static abstract class IntState extends SizeState {
+        public Int64[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new R64byte(i);
+            }
+        }
+    }
+
+    public static abstract class RefState extends SizeState {
+        public R64byte[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new R64byte(i);
+            }
+        }
+    }
+
+    // naming convention: <runtime array type>_as_<static array type>
+
+    public static class Obj_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Int64[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new R64byte[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Int64[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new R64byte[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new R64byte[size];
+            fill();
+        }
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/util/StatesR64int.java b/test/micro/org/openjdk/bench/valhalla/array/util/StatesR64int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/util/StatesR64int.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.util;
+
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.R64int;
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class StatesR64int extends SizeBase {
+
+    public static abstract class ObjState extends SizeState {
+        public Object[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new R64int(i);
+            }
+        }
+    }
+
+    public static abstract class IntState extends SizeState {
+        public Int64[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new R64int(i);
+            }
+        }
+    }
+
+    public static abstract class RefState extends SizeState {
+        public R64int[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new R64int(i);
+            }
+        }
+    }
+
+    // naming convention: <runtime array type>_as_<static array type>
+
+    public static class Obj_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Int64[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new R64int[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Int64[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new R64int[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new R64int[size];
+            fill();
+        }
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/array/util/StatesROpt.java b/test/micro/org/openjdk/bench/valhalla/array/util/StatesROpt.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/array/util/StatesROpt.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.array.util;
+
+import org.openjdk.bench.valhalla.types.Int32;
+import org.openjdk.bench.valhalla.types.Opt;
+import org.openjdk.bench.valhalla.types.R32int;
+import org.openjdk.bench.valhalla.types.ROpt;
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class StatesROpt extends SizeBase {
+
+    public static abstract class ObjState extends SizeState {
+        public Object[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] =  ROpt.of(new R32int(i));
+            }
+        }
+    }
+
+    public static abstract class IntState extends SizeState {
+        public Opt<Int32>[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] =  ROpt.of(new R32int(i));
+            }
+        }
+    }
+
+    public static abstract class RefState extends SizeState {
+        public ROpt<Int32>[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] =  ROpt.of(new R32int(i));
+            }
+        }
+    }
+
+    // naming convention: <runtime array type>_as_<static array type>
+
+    public static class Obj_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Opt[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new ROpt[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Opt[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new ROpt[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new ROpt[size];
+            fill();
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/IdentityArrayCopy.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/IdentityArrayCopy.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/IdentityArrayCopy.java
@@ -0,0 +1,631 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.copy;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesR64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class IdentityArrayCopy extends StatesR64long {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_to_Obj_copy(Obj_as_Obj s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Obj_copy(Obj_as_Obj s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Abs_as_Obj_to_Obj_copy(Obj_as_Obj s, Abs_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Obj_copy(Obj_as_Obj s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Int_copy(Obj_as_Obj s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Abs_as_Obj_to_Int_copy(Obj_as_Obj s, Abs_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Int_copy(Obj_as_Obj s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Abs_as_Obj_to_Abs_copy(Obj_as_Obj s, Abs_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Abs_copy(Obj_as_Obj s, Ref_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Ref_copy(Obj_as_Obj s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_to_Obj_copy(Int_as_Obj s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Obj_copy(Int_as_Obj s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Obj_to_Obj_copy(Int_as_Obj s, Abs_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Obj_copy(Int_as_Obj s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Int_copy(Int_as_Obj s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Obj_to_Int_copy(Int_as_Obj s, Abs_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Int_copy(Int_as_Obj s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Obj_to_Abs_copy(Int_as_Obj s, Abs_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Abs_copy(Int_as_Obj s, Ref_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Ref_copy(Int_as_Obj s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Obj_as_Obj_to_Obj_copy(Abs_as_Obj s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Obj_to_Obj_copy(Abs_as_Obj s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Obj_to_Obj_copy(Abs_as_Obj s, Abs_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Obj_copy(Abs_as_Obj s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Obj_to_Int_copy(Abs_as_Obj s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Obj_to_Int_copy(Abs_as_Obj s, Abs_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Int_copy(Abs_as_Obj s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Obj_to_Abs_copy(Abs_as_Obj s, Abs_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Abs_copy(Abs_as_Obj s, Ref_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Ref_copy(Abs_as_Obj s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_to_Obj_copy(Ref_as_Obj s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Obj_copy(Ref_as_Obj s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Obj_to_Obj_copy(Ref_as_Obj s, Abs_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Obj_copy(Ref_as_Obj s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Int_copy(Ref_as_Obj s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Obj_to_Int_copy(Ref_as_Obj s, Abs_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Int_copy(Ref_as_Obj s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Obj_to_Abs_copy(Ref_as_Obj s, Abs_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Abs_copy(Ref_as_Obj s, Ref_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Ref_copy(Ref_as_Obj s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Int_to_Obj_copy(Int_as_Int s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Obj_copy(Int_as_Int s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Int_to_Obj_copy(Int_as_Int s, Abs_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Obj_copy(Int_as_Int s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Int_copy(Int_as_Int s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Int_to_Int_copy(Int_as_Int s, Abs_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Int_copy(Int_as_Int s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Int_to_Abs_copy(Int_as_Int s, Abs_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Abs_copy(Int_as_Int s, Ref_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Ref_copy(Int_as_Int s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Obj_as_Int_to_Obj_copy(Abs_as_Int s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Int_to_Obj_copy(Abs_as_Int s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Int_to_Obj_copy(Abs_as_Int s, Abs_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Obj_copy(Abs_as_Int s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Int_to_Int_copy(Abs_as_Int s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Int_to_Int_copy(Abs_as_Int s, Abs_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Int_copy(Abs_as_Int s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Int_to_Abs_copy(Abs_as_Int s, Abs_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Abs_copy(Abs_as_Int s, Ref_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Ref_copy(Abs_as_Int s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Int_to_Obj_copy(Ref_as_Int s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Obj_copy(Ref_as_Int s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Int_to_Obj_copy(Ref_as_Int s, Abs_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Obj_copy(Ref_as_Int s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Int_copy(Ref_as_Int s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Int_to_Int_copy(Ref_as_Int s, Abs_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Int_copy(Ref_as_Int s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Int_to_Abs_copy(Ref_as_Int s, Abs_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Abs_copy(Ref_as_Int s, Ref_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Ref_copy(Ref_as_Int s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Obj_as_Abs_to_Obj_copy(Abs_as_Abs s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Abs_to_Obj_copy(Abs_as_Abs s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Abs_to_Obj_copy(Abs_as_Abs s, Abs_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Obj_copy(Abs_as_Abs s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Abs_to_Int_copy(Abs_as_Abs s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Abs_to_Int_copy(Abs_as_Abs s, Abs_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Int_copy(Abs_as_Abs s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Abs_to_Abs_copy(Abs_as_Abs s, Abs_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Abs_copy(Abs_as_Abs s, Ref_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Ref_copy(Abs_as_Abs s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Abs_to_Obj_copy(Ref_as_Abs s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Abs_to_Obj_copy(Ref_as_Abs s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Abs_to_Obj_copy(Ref_as_Abs s, Abs_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Obj_copy(Ref_as_Abs s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Abs_to_Int_copy(Ref_as_Abs s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Abs_to_Int_copy(Ref_as_Abs s, Abs_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Int_copy(Ref_as_Abs s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Abs_to_Abs_copy(Ref_as_Abs s, Abs_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Abs_copy(Ref_as_Abs s, Ref_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Ref_copy(Ref_as_Abs s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Ref_to_Obj_copy(Ref_as_Ref s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Obj_copy(Ref_as_Ref s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Ref_to_Obj_copy(Ref_as_Ref s, Abs_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Obj_copy(Ref_as_Ref s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Int_copy(Ref_as_Ref s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Ref_to_Int_copy(Ref_as_Ref s, Abs_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Int_copy(Ref_as_Ref s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Ref_to_Abs_copy(Ref_as_Ref s, Abs_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Abs_copy(Ref_as_Ref s, Ref_as_Abs d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Ref_copy(Ref_as_Ref s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/IdentityCopy0.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/IdentityCopy0.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/IdentityCopy0.java
@@ -0,0 +1,1034 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.copy;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesR64long;
+import org.openjdk.bench.valhalla.types.A64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.R64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class IdentityCopy0 extends StatesR64long {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_to_Obj_copy(Obj_as_Obj s, Obj_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Obj_copy(Obj_as_Obj s, Int_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Abs_as_Obj_to_Obj_copy(Obj_as_Obj s, Abs_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Obj_copy(Obj_as_Obj s, Ref_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Int_copy(Obj_as_Obj s, Int_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Abs_as_Obj_to_Int_copy(Obj_as_Obj s, Abs_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Int_copy(Obj_as_Obj s, Ref_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Abs_as_Obj_to_Abs_copy(Obj_as_Obj s, Abs_as_Abs d) {
+        Object[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Abs_copy(Obj_as_Obj s, Ref_as_Abs d) {
+        Object[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Ref_copy(Obj_as_Obj s, Ref_as_Ref d) {
+        Object[] src = s.arr;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (R64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_to_Obj_copy(Int_as_Obj s, Obj_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Obj_copy(Int_as_Obj s, Int_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Obj_to_Obj_copy(Int_as_Obj s, Abs_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Obj_copy(Int_as_Obj s, Ref_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Int_copy(Int_as_Obj s, Int_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Obj_to_Int_copy(Int_as_Obj s, Abs_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Int_copy(Int_as_Obj s, Ref_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Obj_to_Abs_copy(Int_as_Obj s, Abs_as_Abs d) {
+        Object[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Abs_copy(Int_as_Obj s, Ref_as_Abs d) {
+        Object[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Ref_copy(Int_as_Obj s, Ref_as_Ref d) {
+        Object[] src = s.arr;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (R64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Obj_as_Obj_to_Obj_copy(Abs_as_Obj s, Obj_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Obj_to_Obj_copy(Abs_as_Obj s, Int_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Obj_to_Obj_copy(Abs_as_Obj s, Abs_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Obj_copy(Abs_as_Obj s, Ref_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Obj_to_Int_copy(Abs_as_Obj s, Int_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Obj_to_Int_copy(Abs_as_Obj s, Abs_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Int_copy(Abs_as_Obj s, Ref_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Obj_to_Abs_copy(Abs_as_Obj s, Abs_as_Abs d) {
+        Object[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Abs_copy(Abs_as_Obj s, Ref_as_Abs d) {
+        Object[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Ref_copy(Abs_as_Obj s, Ref_as_Ref d) {
+        Object[] src = s.arr;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (R64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_to_Obj_copy(Ref_as_Obj s, Obj_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Obj_copy(Ref_as_Obj s, Int_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Obj_to_Obj_copy(Ref_as_Obj s, Abs_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Obj_copy(Ref_as_Obj s, Ref_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Int_copy(Ref_as_Obj s, Int_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Obj_to_Int_copy(Ref_as_Obj s, Abs_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Int_copy(Ref_as_Obj s, Ref_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Obj_to_Abs_copy(Ref_as_Obj s, Abs_as_Abs d) {
+        Object[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Abs_copy(Ref_as_Obj s, Ref_as_Abs d) {
+        Object[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Ref_copy(Ref_as_Obj s, Ref_as_Ref d) {
+        Object[] src = s.arr;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (R64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Int_to_Obj_copy(Int_as_Int s, Obj_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Obj_copy(Int_as_Int s, Int_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Int_to_Obj_copy(Int_as_Int s, Abs_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Obj_copy(Int_as_Int s, Ref_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Int_copy(Int_as_Int s, Int_as_Int d) {
+        Int64[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Int_to_Int_copy(Int_as_Int s, Abs_as_Int d) {
+        Int64[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Int_copy(Int_as_Int s, Ref_as_Int d) {
+        Int64[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Int_to_Abs_copy(Int_as_Int s, Abs_as_Abs d) {
+        Int64[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Abs_copy(Int_as_Int s, Ref_as_Abs d) {
+        Int64[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Ref_copy(Int_as_Int s, Ref_as_Ref d) {
+        Int64[] src = s.arr;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (R64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Obj_as_Int_to_Obj_copy(Abs_as_Int s, Obj_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Int_to_Obj_copy(Abs_as_Int s, Int_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Int_to_Obj_copy(Abs_as_Int s, Abs_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Obj_copy(Abs_as_Int s, Ref_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Int_to_Int_copy(Abs_as_Int s, Int_as_Int d) {
+        Int64[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Int_to_Int_copy(Abs_as_Int s, Abs_as_Int d) {
+        Int64[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Int_copy(Abs_as_Int s, Ref_as_Int d) {
+        Int64[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Int_to_Abs_copy(Abs_as_Int s, Abs_as_Abs d) {
+        Int64[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Abs_copy(Abs_as_Int s, Ref_as_Abs d) {
+        Int64[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Ref_copy(Abs_as_Int s, Ref_as_Ref d) {
+        Int64[] src = s.arr;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (R64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Int_to_Obj_copy(Ref_as_Int s, Obj_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Obj_copy(Ref_as_Int s, Int_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Int_to_Obj_copy(Ref_as_Int s, Abs_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Obj_copy(Ref_as_Int s, Ref_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Int_copy(Ref_as_Int s, Int_as_Int d) {
+        Int64[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Int_to_Int_copy(Ref_as_Int s, Abs_as_Int d) {
+        Int64[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Int_copy(Ref_as_Int s, Ref_as_Int d) {
+        Int64[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Int_to_Abs_copy(Ref_as_Int s, Abs_as_Abs d) {
+        Int64[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Abs_copy(Ref_as_Int s, Ref_as_Abs d) {
+        Int64[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Ref_copy(Ref_as_Int s, Ref_as_Ref d) {
+        Int64[] src = s.arr;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (R64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Obj_as_Abs_to_Obj_copy(Abs_as_Abs s, Obj_as_Obj d) {
+        A64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Abs_to_Obj_copy(Abs_as_Abs s, Int_as_Obj d) {
+        A64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Abs_to_Obj_copy(Abs_as_Abs s, Abs_as_Obj d) {
+        A64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Obj_copy(Abs_as_Abs s, Ref_as_Obj d) {
+        A64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Abs_to_Int_copy(Abs_as_Abs s, Int_as_Int d) {
+        A64long[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Abs_to_Int_copy(Abs_as_Abs s, Abs_as_Int d) {
+        A64long[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Int_copy(Abs_as_Abs s, Ref_as_Int d) {
+        A64long[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Abs_to_Abs_copy(Abs_as_Abs s, Abs_as_Abs d) {
+        A64long[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Abs_copy(Abs_as_Abs s, Ref_as_Abs d) {
+        A64long[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Ref_copy(Abs_as_Abs s, Ref_as_Ref d) {
+        A64long[] src = s.arr;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (R64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Abs_to_Obj_copy(Ref_as_Abs s, Obj_as_Obj d) {
+        A64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Abs_to_Obj_copy(Ref_as_Abs s, Int_as_Obj d) {
+        A64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Abs_to_Obj_copy(Ref_as_Abs s, Abs_as_Obj d) {
+        A64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Obj_copy(Ref_as_Abs s, Ref_as_Obj d) {
+        A64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Abs_to_Int_copy(Ref_as_Abs s, Int_as_Int d) {
+        A64long[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Abs_to_Int_copy(Ref_as_Abs s, Abs_as_Int d) {
+        A64long[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Int_copy(Ref_as_Abs s, Ref_as_Int d) {
+        A64long[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Abs_to_Abs_copy(Ref_as_Abs s, Abs_as_Abs d) {
+        A64long[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Abs_copy(Ref_as_Abs s, Ref_as_Abs d) {
+        A64long[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Ref_copy(Ref_as_Abs s, Ref_as_Ref d) {
+        A64long[] src = s.arr;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (R64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Ref_to_Obj_copy(Ref_as_Ref s, Obj_as_Obj d) {
+        R64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Obj_copy(Ref_as_Ref s, Int_as_Obj d) {
+        R64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Ref_to_Obj_copy(Ref_as_Ref s, Abs_as_Obj d) {
+        R64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Obj_copy(Ref_as_Ref s, Ref_as_Obj d) {
+        R64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Int_copy(Ref_as_Ref s, Int_as_Int d) {
+        R64long[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Ref_to_Int_copy(Ref_as_Ref s, Abs_as_Int d) {
+        R64long[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Int_copy(Ref_as_Ref s, Ref_as_Int d) {
+        R64long[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Ref_to_Abs_copy(Ref_as_Ref s, Abs_as_Abs d) {
+        R64long[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Abs_copy(Ref_as_Ref s, Ref_as_Abs d) {
+        R64long[] src = s.arr;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Ref_copy(Ref_as_Ref s, Ref_as_Ref d) {
+        R64long[] src = s.arr;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/IdentityCopy1.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/IdentityCopy1.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/IdentityCopy1.java
@@ -0,0 +1,934 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.copy;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesR64long;
+import org.openjdk.bench.valhalla.types.A64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.R64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class IdentityCopy1 extends StatesR64long {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_to_Obj_copy(Obj_as_Obj s, Obj_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Obj_copy(Obj_as_Obj s, Int_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Abs_as_Obj_to_Obj_copy(Obj_as_Obj s, Abs_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Obj_copy(Obj_as_Obj s, Ref_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Int_copy(Obj_as_Obj s, Int_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Abs_as_Obj_to_Int_copy(Obj_as_Obj s, Abs_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Int_copy(Obj_as_Obj s, Ref_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Abs_as_Obj_to_Abs_copy(Obj_as_Obj s, Abs_as_Abs d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Abs_copy(Obj_as_Obj s, Ref_as_Abs d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Ref_copy(Obj_as_Obj s, Ref_as_Ref d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (R64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_to_Obj_copy(Int_as_Obj s, Obj_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Obj_copy(Int_as_Obj s, Int_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Obj_to_Obj_copy(Int_as_Obj s, Abs_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Obj_copy(Int_as_Obj s, Ref_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Int_copy(Int_as_Obj s, Int_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Obj_to_Int_copy(Int_as_Obj s, Abs_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Int_copy(Int_as_Obj s, Ref_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Obj_to_Abs_copy(Int_as_Obj s, Abs_as_Abs d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Abs_copy(Int_as_Obj s, Ref_as_Abs d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Ref_copy(Int_as_Obj s, Ref_as_Ref d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (R64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Obj_as_Obj_to_Obj_copy(Abs_as_Obj s, Obj_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Obj_to_Obj_copy(Abs_as_Obj s, Int_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Obj_to_Obj_copy(Abs_as_Obj s, Abs_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Obj_copy(Abs_as_Obj s, Ref_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Obj_to_Int_copy(Abs_as_Obj s, Int_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Obj_to_Int_copy(Abs_as_Obj s, Abs_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Int_copy(Abs_as_Obj s, Ref_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Obj_to_Abs_copy(Abs_as_Obj s, Abs_as_Abs d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Abs_copy(Abs_as_Obj s, Ref_as_Abs d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Ref_copy(Abs_as_Obj s, Ref_as_Ref d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (R64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_to_Obj_copy(Ref_as_Obj s, Obj_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Obj_copy(Ref_as_Obj s, Int_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Obj_to_Obj_copy(Ref_as_Obj s, Abs_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Obj_copy(Ref_as_Obj s, Ref_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Int_copy(Ref_as_Obj s, Int_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Obj_to_Int_copy(Ref_as_Obj s, Abs_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Int_copy(Ref_as_Obj s, Ref_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Obj_to_Abs_copy(Ref_as_Obj s, Abs_as_Abs d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Abs_copy(Ref_as_Obj s, Ref_as_Abs d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Ref_copy(Ref_as_Obj s, Ref_as_Ref d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (R64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Int_to_Obj_copy(Int_as_Int s, Obj_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Obj_copy(Int_as_Int s, Int_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Int_to_Obj_copy(Int_as_Int s, Abs_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Obj_copy(Int_as_Int s, Ref_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Int_copy(Int_as_Int s, Int_as_Int d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Int_to_Int_copy(Int_as_Int s, Abs_as_Int d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Int_copy(Int_as_Int s, Ref_as_Int d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Int_to_Abs_copy(Int_as_Int s, Abs_as_Abs d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Abs_copy(Int_as_Int s, Ref_as_Abs d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Ref_copy(Int_as_Int s, Ref_as_Ref d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (R64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Obj_as_Int_to_Obj_copy(Abs_as_Int s, Obj_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Int_to_Obj_copy(Abs_as_Int s, Int_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Int_to_Obj_copy(Abs_as_Int s, Abs_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Obj_copy(Abs_as_Int s, Ref_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Int_to_Int_copy(Abs_as_Int s, Int_as_Int d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Int_to_Int_copy(Abs_as_Int s, Abs_as_Int d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Int_copy(Abs_as_Int s, Ref_as_Int d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Int_to_Abs_copy(Abs_as_Int s, Abs_as_Abs d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Abs_copy(Abs_as_Int s, Ref_as_Abs d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Ref_copy(Abs_as_Int s, Ref_as_Ref d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (R64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Int_to_Obj_copy(Ref_as_Int s, Obj_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Obj_copy(Ref_as_Int s, Int_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Int_to_Obj_copy(Ref_as_Int s, Abs_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Obj_copy(Ref_as_Int s, Ref_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Int_copy(Ref_as_Int s, Int_as_Int d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Int_to_Int_copy(Ref_as_Int s, Abs_as_Int d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Int_copy(Ref_as_Int s, Ref_as_Int d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Int_to_Abs_copy(Ref_as_Int s, Abs_as_Abs d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Abs_copy(Ref_as_Int s, Ref_as_Abs d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (A64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Ref_copy(Ref_as_Int s, Ref_as_Ref d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (R64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Obj_as_Abs_to_Obj_copy(Abs_as_Abs s, Obj_as_Obj d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Abs_to_Obj_copy(Abs_as_Abs s, Int_as_Obj d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Abs_to_Obj_copy(Abs_as_Abs s, Abs_as_Obj d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Obj_copy(Abs_as_Abs s, Ref_as_Obj d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Abs_to_Int_copy(Abs_as_Abs s, Int_as_Int d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Abs_to_Int_copy(Abs_as_Abs s, Abs_as_Int d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Int_copy(Abs_as_Abs s, Ref_as_Int d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Abs_to_Abs_copy(Abs_as_Abs s, Abs_as_Abs d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Abs_copy(Abs_as_Abs s, Ref_as_Abs d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Ref_copy(Abs_as_Abs s, Ref_as_Ref d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (R64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Abs_to_Obj_copy(Ref_as_Abs s, Obj_as_Obj d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Abs_to_Obj_copy(Ref_as_Abs s, Int_as_Obj d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Abs_to_Obj_copy(Ref_as_Abs s, Abs_as_Obj d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Obj_copy(Ref_as_Abs s, Ref_as_Obj d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Abs_to_Int_copy(Ref_as_Abs s, Int_as_Int d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Abs_to_Int_copy(Ref_as_Abs s, Abs_as_Int d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Int_copy(Ref_as_Abs s, Ref_as_Int d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Abs_to_Abs_copy(Ref_as_Abs s, Abs_as_Abs d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Abs_copy(Ref_as_Abs s, Ref_as_Abs d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Ref_copy(Ref_as_Abs s, Ref_as_Ref d) {
+        A64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (R64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Ref_to_Obj_copy(Ref_as_Ref s, Obj_as_Obj d) {
+        R64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Obj_copy(Ref_as_Ref s, Int_as_Obj d) {
+        R64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Ref_to_Obj_copy(Ref_as_Ref s, Abs_as_Obj d) {
+        R64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Obj_copy(Ref_as_Ref s, Ref_as_Obj d) {
+        R64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Int_copy(Ref_as_Ref s, Int_as_Int d) {
+        R64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Ref_to_Int_copy(Ref_as_Ref s, Abs_as_Int d) {
+        R64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Int_copy(Ref_as_Ref s, Ref_as_Int d) {
+        R64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Ref_to_Abs_copy(Ref_as_Ref s, Abs_as_Abs d) {
+        R64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Abs_copy(Ref_as_Ref s, Ref_as_Abs d) {
+        R64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Ref_copy(Ref_as_Ref s, Ref_as_Ref d) {
+        R64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/IdentityCopy2.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/IdentityCopy2.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/IdentityCopy2.java
@@ -0,0 +1,1034 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.copy;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesR64long;
+import org.openjdk.bench.valhalla.types.A64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.R64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class IdentityCopy2 extends StatesR64long {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_to_Obj_copy(Obj_as_Obj s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Obj_copy(Obj_as_Obj s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Abs_as_Obj_to_Obj_copy(Obj_as_Obj s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Obj_copy(Obj_as_Obj s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Int_copy(Obj_as_Obj s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Abs_as_Obj_to_Int_copy(Obj_as_Obj s, Abs_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Int_copy(Obj_as_Obj s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Abs_as_Obj_to_Abs_copy(Obj_as_Obj s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Abs_copy(Obj_as_Obj s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Ref_copy(Obj_as_Obj s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (R64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_to_Obj_copy(Int_as_Obj s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Obj_copy(Int_as_Obj s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Obj_to_Obj_copy(Int_as_Obj s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Obj_copy(Int_as_Obj s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Int_copy(Int_as_Obj s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Obj_to_Int_copy(Int_as_Obj s, Abs_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Int_copy(Int_as_Obj s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Obj_to_Abs_copy(Int_as_Obj s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Abs_copy(Int_as_Obj s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Ref_copy(Int_as_Obj s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (R64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Obj_as_Obj_to_Obj_copy(Abs_as_Obj s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Obj_to_Obj_copy(Abs_as_Obj s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Obj_to_Obj_copy(Abs_as_Obj s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Obj_copy(Abs_as_Obj s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Obj_to_Int_copy(Abs_as_Obj s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Obj_to_Int_copy(Abs_as_Obj s, Abs_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Int_copy(Abs_as_Obj s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Obj_to_Abs_copy(Abs_as_Obj s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Abs_copy(Abs_as_Obj s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Ref_copy(Abs_as_Obj s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (R64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_to_Obj_copy(Ref_as_Obj s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Obj_copy(Ref_as_Obj s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Obj_to_Obj_copy(Ref_as_Obj s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Obj_copy(Ref_as_Obj s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Int_copy(Ref_as_Obj s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Obj_to_Int_copy(Ref_as_Obj s, Abs_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Int_copy(Ref_as_Obj s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Obj_to_Abs_copy(Ref_as_Obj s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Abs_copy(Ref_as_Obj s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Ref_copy(Ref_as_Obj s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (R64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Int_to_Obj_copy(Int_as_Int s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Obj_copy(Int_as_Int s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Int_to_Obj_copy(Int_as_Int s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Obj_copy(Int_as_Int s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Int_copy(Int_as_Int s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Int_to_Int_copy(Int_as_Int s, Abs_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Int_copy(Int_as_Int s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Int_to_Abs_copy(Int_as_Int s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Abs_copy(Int_as_Int s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Ref_copy(Int_as_Int s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (R64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Obj_as_Int_to_Obj_copy(Abs_as_Int s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Int_to_Obj_copy(Abs_as_Int s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Int_to_Obj_copy(Abs_as_Int s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Obj_copy(Abs_as_Int s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Int_to_Int_copy(Abs_as_Int s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Int_to_Int_copy(Abs_as_Int s, Abs_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Int_copy(Abs_as_Int s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Int_to_Abs_copy(Abs_as_Int s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Abs_copy(Abs_as_Int s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Ref_copy(Abs_as_Int s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (R64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Int_to_Obj_copy(Ref_as_Int s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Obj_copy(Ref_as_Int s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Int_to_Obj_copy(Ref_as_Int s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Obj_copy(Ref_as_Int s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Int_copy(Ref_as_Int s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Int_to_Int_copy(Ref_as_Int s, Abs_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Int_copy(Ref_as_Int s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Int_to_Abs_copy(Ref_as_Int s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Abs_copy(Ref_as_Int s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Ref_copy(Ref_as_Int s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (R64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Obj_as_Abs_to_Obj_copy(Abs_as_Abs s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Abs_to_Obj_copy(Abs_as_Abs s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Abs_to_Obj_copy(Abs_as_Abs s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Obj_copy(Abs_as_Abs s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Abs_to_Int_copy(Abs_as_Abs s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Abs_to_Int_copy(Abs_as_Abs s, Abs_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Int_copy(Abs_as_Abs s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Abs_to_Abs_copy(Abs_as_Abs s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Abs_copy(Abs_as_Abs s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Ref_copy(Abs_as_Abs s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (R64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Abs_to_Obj_copy(Ref_as_Abs s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Abs_to_Obj_copy(Ref_as_Abs s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Abs_to_Obj_copy(Ref_as_Abs s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Obj_copy(Ref_as_Abs s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Abs_to_Int_copy(Ref_as_Abs s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Abs_to_Int_copy(Ref_as_Abs s, Abs_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Int_copy(Ref_as_Abs s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Abs_to_Abs_copy(Ref_as_Abs s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Abs_copy(Ref_as_Abs s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Ref_copy(Ref_as_Abs s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (R64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Ref_to_Obj_copy(Ref_as_Ref s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Obj_copy(Ref_as_Ref s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Ref_to_Obj_copy(Ref_as_Ref s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Obj_copy(Ref_as_Ref s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Int_copy(Ref_as_Ref s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Ref_to_Int_copy(Ref_as_Ref s, Abs_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Int_copy(Ref_as_Ref s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Ref_to_Abs_copy(Ref_as_Ref s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Abs_copy(Ref_as_Ref s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        A64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Ref_copy(Ref_as_Ref s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        R64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/IdentityCopy3.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/IdentityCopy3.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/IdentityCopy3.java
@@ -0,0 +1,934 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.copy;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesR64long;
+import org.openjdk.bench.valhalla.types.A64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.R64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class IdentityCopy3 extends StatesR64long {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_to_Obj_copy(Obj_as_Obj s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Obj_copy(Obj_as_Obj s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Abs_as_Obj_to_Obj_copy(Obj_as_Obj s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Obj_copy(Obj_as_Obj s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Int_copy(Obj_as_Obj s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Abs_as_Obj_to_Int_copy(Obj_as_Obj s, Abs_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Int_copy(Obj_as_Obj s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Abs_as_Obj_to_Abs_copy(Obj_as_Obj s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Abs_copy(Obj_as_Obj s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Ref_copy(Obj_as_Obj s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (R64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_to_Obj_copy(Int_as_Obj s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Obj_copy(Int_as_Obj s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Obj_to_Obj_copy(Int_as_Obj s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Obj_copy(Int_as_Obj s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Int_copy(Int_as_Obj s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Obj_to_Int_copy(Int_as_Obj s, Abs_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Int_copy(Int_as_Obj s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Obj_to_Abs_copy(Int_as_Obj s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Abs_copy(Int_as_Obj s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Ref_copy(Int_as_Obj s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (R64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Obj_as_Obj_to_Obj_copy(Abs_as_Obj s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Obj_to_Obj_copy(Abs_as_Obj s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Obj_to_Obj_copy(Abs_as_Obj s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Obj_copy(Abs_as_Obj s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Obj_to_Int_copy(Abs_as_Obj s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Obj_to_Int_copy(Abs_as_Obj s, Abs_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Int_copy(Abs_as_Obj s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Obj_to_Abs_copy(Abs_as_Obj s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Abs_copy(Abs_as_Obj s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Obj_to_Ref_copy(Abs_as_Obj s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (R64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_to_Obj_copy(Ref_as_Obj s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Obj_copy(Ref_as_Obj s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Obj_to_Obj_copy(Ref_as_Obj s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Obj_copy(Ref_as_Obj s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Int_copy(Ref_as_Obj s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Obj_to_Int_copy(Ref_as_Obj s, Abs_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Int_copy(Ref_as_Obj s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Obj_to_Abs_copy(Ref_as_Obj s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Abs_copy(Ref_as_Obj s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Ref_copy(Ref_as_Obj s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (R64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Int_to_Obj_copy(Int_as_Int s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Obj_copy(Int_as_Int s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Int_to_Obj_copy(Int_as_Int s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Obj_copy(Int_as_Int s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Int_copy(Int_as_Int s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Int_to_Int_copy(Int_as_Int s, Abs_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Int_copy(Int_as_Int s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Abs_as_Int_to_Abs_copy(Int_as_Int s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Abs_copy(Int_as_Int s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Ref_copy(Int_as_Int s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (R64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Obj_as_Int_to_Obj_copy(Abs_as_Int s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Int_to_Obj_copy(Abs_as_Int s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Int_to_Obj_copy(Abs_as_Int s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Obj_copy(Abs_as_Int s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Int_to_Int_copy(Abs_as_Int s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Int_to_Int_copy(Abs_as_Int s, Abs_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Int_copy(Abs_as_Int s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Int_to_Abs_copy(Abs_as_Int s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Abs_copy(Abs_as_Int s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Int_to_Ref_copy(Abs_as_Int s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (R64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Int_to_Obj_copy(Ref_as_Int s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Obj_copy(Ref_as_Int s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Int_to_Obj_copy(Ref_as_Int s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Obj_copy(Ref_as_Int s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Int_copy(Ref_as_Int s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Int_to_Int_copy(Ref_as_Int s, Abs_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Int_copy(Ref_as_Int s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Int_to_Abs_copy(Ref_as_Int s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Abs_copy(Ref_as_Int s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (A64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Ref_copy(Ref_as_Int s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (R64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Obj_as_Abs_to_Obj_copy(Abs_as_Abs s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Abs_to_Obj_copy(Abs_as_Abs s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Abs_to_Obj_copy(Abs_as_Abs s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Obj_copy(Abs_as_Abs s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Int_as_Abs_to_Int_copy(Abs_as_Abs s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Abs_to_Int_copy(Abs_as_Abs s, Abs_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Int_copy(Abs_as_Abs s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Abs_as_Abs_to_Abs_copy(Abs_as_Abs s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Abs_copy(Abs_as_Abs s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Abs_to_Ref_as_Abs_to_Ref_copy(Abs_as_Abs s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (R64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Abs_to_Obj_copy(Ref_as_Abs s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Abs_to_Obj_copy(Ref_as_Abs s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Abs_to_Obj_copy(Ref_as_Abs s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Obj_copy(Ref_as_Abs s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Abs_to_Int_copy(Ref_as_Abs s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Abs_to_Int_copy(Ref_as_Abs s, Abs_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Int_copy(Ref_as_Abs s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Abs_to_Abs_copy(Ref_as_Abs s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Abs_copy(Ref_as_Abs s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Abs_to_Ref_copy(Ref_as_Abs s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (R64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Ref_to_Obj_copy(Ref_as_Ref s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Obj_copy(Ref_as_Ref s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Ref_to_Obj_copy(Ref_as_Ref s, Abs_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Obj_copy(Ref_as_Ref s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Int_copy(Ref_as_Ref s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Ref_to_Int_copy(Ref_as_Ref s, Abs_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Int_copy(Ref_as_Ref s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Abs_as_Ref_to_Abs_copy(Ref_as_Ref s, Abs_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Abs_copy(Ref_as_Ref s, Ref_as_Abs d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Ref_copy(Ref_as_Ref s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/InlineArrayCopy.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/InlineArrayCopy.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/InlineArrayCopy.java
@@ -0,0 +1,632 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.copy;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class InlineArrayCopy extends StatesQ64long {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_to_Obj_copy(Obj_as_Obj s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Obj_copy(Obj_as_Obj s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Obj_copy(Obj_as_Obj s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Obj_copy(Obj_as_Obj s, Val_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Int_copy(Obj_as_Obj s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Int_copy(Obj_as_Obj s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Int_copy(Obj_as_Obj s, Val_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Ref_copy(Obj_as_Obj s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Ref_copy(Obj_as_Obj s, Val_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Val_copy(Obj_as_Obj s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_to_Obj_copy(Int_as_Obj s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Obj_copy(Int_as_Obj s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Obj_copy(Int_as_Obj s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Obj_copy(Int_as_Obj s, Val_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Int_copy(Int_as_Obj s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Int_copy(Int_as_Obj s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Int_copy(Int_as_Obj s, Val_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Ref_copy(Int_as_Obj s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Ref_copy(Int_as_Obj s, Val_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Val_copy(Int_as_Obj s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_to_Obj_copy(Ref_as_Obj s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Obj_copy(Ref_as_Obj s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Obj_copy(Ref_as_Obj s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Obj_copy(Ref_as_Obj s, Val_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Int_copy(Ref_as_Obj s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Int_copy(Ref_as_Obj s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Int_copy(Ref_as_Obj s, Val_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Ref_copy(Ref_as_Obj s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Ref_copy(Ref_as_Obj s, Val_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Val_copy(Ref_as_Obj s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Obj_to_Obj_copy(Val_as_Obj s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_to_Obj_copy(Val_as_Obj s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_to_Obj_copy(Val_as_Obj s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Obj_copy(Val_as_Obj s, Val_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_to_Int_copy(Val_as_Obj s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_to_Int_copy(Val_as_Obj s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Int_copy(Val_as_Obj s, Val_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_to_Ref_copy(Val_as_Obj s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Ref_copy(Val_as_Obj s, Val_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Val_copy(Val_as_Obj s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Int_to_Obj_copy(Int_as_Int s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Obj_copy(Int_as_Int s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Obj_copy(Int_as_Int s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Obj_copy(Int_as_Int s, Val_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Int_copy(Int_as_Int s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Int_copy(Int_as_Int s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Int_copy(Int_as_Int s, Val_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Ref_copy(Int_as_Int s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Ref_copy(Int_as_Int s, Val_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Val_copy(Int_as_Int s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Int_to_Obj_copy(Ref_as_Int s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Obj_copy(Ref_as_Int s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Obj_copy(Ref_as_Int s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Obj_copy(Ref_as_Int s, Val_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Int_copy(Ref_as_Int s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Int_copy(Ref_as_Int s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Int_copy(Ref_as_Int s, Val_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Ref_copy(Ref_as_Int s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Ref_copy(Ref_as_Int s, Val_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Val_copy(Ref_as_Int s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Int_to_Obj_copy(Val_as_Int s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_to_Obj_copy(Val_as_Int s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_to_Obj_copy(Val_as_Int s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Obj_copy(Val_as_Int s, Val_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_to_Int_copy(Val_as_Int s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_to_Int_copy(Val_as_Int s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Int_copy(Val_as_Int s, Val_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_to_Ref_copy(Val_as_Int s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Ref_copy(Val_as_Int s, Val_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Val_copy(Val_as_Int s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Ref_to_Obj_copy(Ref_as_Ref s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Obj_copy(Ref_as_Ref s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Obj_copy(Ref_as_Ref s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Obj_copy(Ref_as_Ref s, Val_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Int_copy(Ref_as_Ref s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Int_copy(Ref_as_Ref s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Int_copy(Ref_as_Ref s, Val_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Ref_copy(Ref_as_Ref s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Ref_copy(Ref_as_Ref s, Val_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Val_copy(Ref_as_Ref s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Ref_to_Obj_copy(Val_as_Ref s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Ref_to_Obj_copy(Val_as_Ref s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_to_Obj_copy(Val_as_Ref s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Obj_copy(Val_as_Ref s, Val_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Ref_to_Int_copy(Val_as_Ref s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_to_Int_copy(Val_as_Ref s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Int_copy(Val_as_Ref s, Val_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_to_Ref_copy(Val_as_Ref s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Ref_copy(Val_as_Ref s, Val_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Val_copy(Val_as_Ref s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Val_to_Obj_copy(Val_as_Val s, Obj_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Val_to_Obj_copy(Val_as_Val s, Int_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Val_to_Obj_copy(Val_as_Val s, Ref_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Obj_copy(Val_as_Val s, Val_as_Obj d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Val_to_Int_copy(Val_as_Val s, Int_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Val_to_Int_copy(Val_as_Val s, Ref_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Int_copy(Val_as_Val s, Val_as_Int d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Val_to_Ref_copy(Val_as_Val s, Ref_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Ref_copy(Val_as_Val s, Val_as_Ref d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Val_copy(Val_as_Val s, Val_as_Val d) {
+        System.arraycopy(s.arr, 0, d.arr, 0, s.arr.length);
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/InlineCopy0.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/InlineCopy0.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/InlineCopy0.java
@@ -0,0 +1,1034 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.copy;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class InlineCopy0 extends StatesQ64long {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_to_Obj_copy(Obj_as_Obj s, Obj_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Obj_copy(Obj_as_Obj s, Int_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Obj_copy(Obj_as_Obj s, Ref_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Obj_copy(Obj_as_Obj s, Val_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Int_copy(Obj_as_Obj s, Int_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Int_copy(Obj_as_Obj s, Ref_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Int_copy(Obj_as_Obj s, Val_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Ref_copy(Obj_as_Obj s, Ref_as_Ref d) {
+        Object[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Ref_copy(Obj_as_Obj s, Val_as_Ref d) {
+        Object[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Val_copy(Obj_as_Obj s, Val_as_Val d) {
+        Object[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_to_Obj_copy(Int_as_Obj s, Obj_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Obj_copy(Int_as_Obj s, Int_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Obj_copy(Int_as_Obj s, Ref_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Obj_copy(Int_as_Obj s, Val_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Int_copy(Int_as_Obj s, Int_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Int_copy(Int_as_Obj s, Ref_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Int_copy(Int_as_Obj s, Val_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Ref_copy(Int_as_Obj s, Ref_as_Ref d) {
+        Object[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Ref_copy(Int_as_Obj s, Val_as_Ref d) {
+        Object[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Val_copy(Int_as_Obj s, Val_as_Val d) {
+        Object[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_to_Obj_copy(Ref_as_Obj s, Obj_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Obj_copy(Ref_as_Obj s, Int_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Obj_copy(Ref_as_Obj s, Ref_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Obj_copy(Ref_as_Obj s, Val_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Int_copy(Ref_as_Obj s, Int_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Int_copy(Ref_as_Obj s, Ref_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Int_copy(Ref_as_Obj s, Val_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Ref_copy(Ref_as_Obj s, Ref_as_Ref d) {
+        Object[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Ref_copy(Ref_as_Obj s, Val_as_Ref d) {
+        Object[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Val_copy(Ref_as_Obj s, Val_as_Val d) {
+        Object[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Obj_to_Obj_copy(Val_as_Obj s, Obj_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_to_Obj_copy(Val_as_Obj s, Int_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_to_Obj_copy(Val_as_Obj s, Ref_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Obj_copy(Val_as_Obj s, Val_as_Obj d) {
+        Object[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_to_Int_copy(Val_as_Obj s, Int_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_to_Int_copy(Val_as_Obj s, Ref_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Int_copy(Val_as_Obj s, Val_as_Int d) {
+        Object[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_to_Ref_copy(Val_as_Obj s, Ref_as_Ref d) {
+        Object[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Ref_copy(Val_as_Obj s, Val_as_Ref d) {
+        Object[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Val_copy(Val_as_Obj s, Val_as_Val d) {
+        Object[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Int_to_Obj_copy(Int_as_Int s, Obj_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Obj_copy(Int_as_Int s, Int_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Obj_copy(Int_as_Int s, Ref_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Obj_copy(Int_as_Int s, Val_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Int_copy(Int_as_Int s, Int_as_Int d) {
+        Int64[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Int_copy(Int_as_Int s, Ref_as_Int d) {
+        Int64[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Int_copy(Int_as_Int s, Val_as_Int d) {
+        Int64[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Ref_copy(Int_as_Int s, Ref_as_Ref d) {
+        Int64[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Ref_copy(Int_as_Int s, Val_as_Ref d) {
+        Int64[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Val_copy(Int_as_Int s, Val_as_Val d) {
+        Int64[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Int_to_Obj_copy(Ref_as_Int s, Obj_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Obj_copy(Ref_as_Int s, Int_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Obj_copy(Ref_as_Int s, Ref_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Obj_copy(Ref_as_Int s, Val_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Int_copy(Ref_as_Int s, Int_as_Int d) {
+        Int64[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Int_copy(Ref_as_Int s, Ref_as_Int d) {
+        Int64[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Int_copy(Ref_as_Int s, Val_as_Int d) {
+        Int64[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Ref_copy(Ref_as_Int s, Ref_as_Ref d) {
+        Int64[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Ref_copy(Ref_as_Int s, Val_as_Ref d) {
+        Int64[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Val_copy(Ref_as_Int s, Val_as_Val d) {
+        Int64[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Int_to_Obj_copy(Val_as_Int s, Obj_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_to_Obj_copy(Val_as_Int s, Int_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_to_Obj_copy(Val_as_Int s, Ref_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Obj_copy(Val_as_Int s, Val_as_Obj d) {
+        Int64[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_to_Int_copy(Val_as_Int s, Int_as_Int d) {
+        Int64[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_to_Int_copy(Val_as_Int s, Ref_as_Int d) {
+        Int64[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Int_copy(Val_as_Int s, Val_as_Int d) {
+        Int64[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_to_Ref_copy(Val_as_Int s, Ref_as_Ref d) {
+        Int64[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Ref_copy(Val_as_Int s, Val_as_Ref d) {
+        Int64[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Val_copy(Val_as_Int s, Val_as_Val d) {
+        Int64[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Ref_to_Obj_copy(Ref_as_Ref s, Obj_as_Obj d) {
+        Q64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Obj_copy(Ref_as_Ref s, Int_as_Obj d) {
+        Q64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Obj_copy(Ref_as_Ref s, Ref_as_Obj d) {
+        Q64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Obj_copy(Ref_as_Ref s, Val_as_Obj d) {
+        Q64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Int_copy(Ref_as_Ref s, Int_as_Int d) {
+        Q64long[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Int_copy(Ref_as_Ref s, Ref_as_Int d) {
+        Q64long[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Int_copy(Ref_as_Ref s, Val_as_Int d) {
+        Q64long[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Ref_copy(Ref_as_Ref s, Ref_as_Ref d) {
+        Q64long[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Ref_copy(Ref_as_Ref s, Val_as_Ref d) {
+        Q64long[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Val_copy(Ref_as_Ref s, Val_as_Val d) {
+        Q64long[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Ref_to_Obj_copy(Val_as_Ref s, Obj_as_Obj d) {
+        Q64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Ref_to_Obj_copy(Val_as_Ref s, Int_as_Obj d) {
+        Q64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_to_Obj_copy(Val_as_Ref s, Ref_as_Obj d) {
+        Q64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Obj_copy(Val_as_Ref s, Val_as_Obj d) {
+        Q64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Ref_to_Int_copy(Val_as_Ref s, Int_as_Int d) {
+        Q64long[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_to_Int_copy(Val_as_Ref s, Ref_as_Int d) {
+        Q64long[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Int_copy(Val_as_Ref s, Val_as_Int d) {
+        Q64long[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_to_Ref_copy(Val_as_Ref s, Ref_as_Ref d) {
+        Q64long[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Ref_copy(Val_as_Ref s, Val_as_Ref d) {
+        Q64long[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Val_copy(Val_as_Ref s, Val_as_Val d) {
+        Q64long[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Val_to_Obj_copy(Val_as_Val s, Obj_as_Obj d) {
+        Q64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Val_to_Obj_copy(Val_as_Val s, Int_as_Obj d) {
+        Q64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Val_to_Obj_copy(Val_as_Val s, Ref_as_Obj d) {
+        Q64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Obj_copy(Val_as_Val s, Val_as_Obj d) {
+        Q64long[] src = s.arr;
+        Object[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Val_to_Int_copy(Val_as_Val s, Int_as_Int d) {
+        Q64long[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Val_to_Int_copy(Val_as_Val s, Ref_as_Int d) {
+        Q64long[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Int_copy(Val_as_Val s, Val_as_Int d) {
+        Q64long[] src = s.arr;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Val_to_Ref_copy(Val_as_Val s, Ref_as_Ref d) {
+        Q64long[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Ref_copy(Val_as_Val s, Val_as_Ref d) {
+        Q64long[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Val_copy(Val_as_Val s, Val_as_Val d) {
+        Q64long[] src = s.arr;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i] = src[i];
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/InlineCopy1.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/InlineCopy1.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/InlineCopy1.java
@@ -0,0 +1,933 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.copy;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class InlineCopy1 extends StatesQ64long {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_to_Obj_copy(Obj_as_Obj s, Obj_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Obj_copy(Obj_as_Obj s, Int_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Obj_copy(Obj_as_Obj s, Ref_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Obj_copy(Obj_as_Obj s, Val_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Int_copy(Obj_as_Obj s, Int_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Int_copy(Obj_as_Obj s, Ref_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Int_copy(Obj_as_Obj s, Val_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Ref_copy(Obj_as_Obj s, Ref_as_Ref d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Ref_copy(Obj_as_Obj s, Val_as_Ref d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Val_copy(Obj_as_Obj s, Val_as_Val d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_to_Obj_copy(Int_as_Obj s, Obj_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Obj_copy(Int_as_Obj s, Int_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Obj_copy(Int_as_Obj s, Ref_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Obj_copy(Int_as_Obj s, Val_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Int_copy(Int_as_Obj s, Int_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Int_copy(Int_as_Obj s, Ref_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Int_copy(Int_as_Obj s, Val_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Ref_copy(Int_as_Obj s, Ref_as_Ref d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Ref_copy(Int_as_Obj s, Val_as_Ref d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Val_copy(Int_as_Obj s, Val_as_Val d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_to_Obj_copy(Ref_as_Obj s, Obj_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Obj_copy(Ref_as_Obj s, Int_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Obj_copy(Ref_as_Obj s, Ref_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Obj_copy(Ref_as_Obj s, Val_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Int_copy(Ref_as_Obj s, Int_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Int_copy(Ref_as_Obj s, Ref_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Int_copy(Ref_as_Obj s, Val_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Ref_copy(Ref_as_Obj s, Ref_as_Ref d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Ref_copy(Ref_as_Obj s, Val_as_Ref d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Val_copy(Ref_as_Obj s, Val_as_Val d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Obj_to_Obj_copy(Val_as_Obj s, Obj_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_to_Obj_copy(Val_as_Obj s, Int_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_to_Obj_copy(Val_as_Obj s, Ref_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Obj_copy(Val_as_Obj s, Val_as_Obj d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_to_Int_copy(Val_as_Obj s, Int_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_to_Int_copy(Val_as_Obj s, Ref_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Int_copy(Val_as_Obj s, Val_as_Int d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Int64)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_to_Ref_copy(Val_as_Obj s, Ref_as_Ref d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Ref_copy(Val_as_Obj s, Val_as_Ref d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Val_copy(Val_as_Obj s, Val_as_Val d) {
+        Object[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Int_to_Obj_copy(Int_as_Int s, Obj_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Obj_copy(Int_as_Int s, Int_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Obj_copy(Int_as_Int s, Ref_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Obj_copy(Int_as_Int s, Val_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Int_copy(Int_as_Int s, Int_as_Int d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Int_copy(Int_as_Int s, Ref_as_Int d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Int_copy(Int_as_Int s, Val_as_Int d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Ref_copy(Int_as_Int s, Ref_as_Ref d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Ref_copy(Int_as_Int s, Val_as_Ref d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Val_copy(Int_as_Int s, Val_as_Val d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Int_to_Obj_copy(Ref_as_Int s, Obj_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Obj_copy(Ref_as_Int s, Int_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Obj_copy(Ref_as_Int s, Ref_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Obj_copy(Ref_as_Int s, Val_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Int_copy(Ref_as_Int s, Int_as_Int d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Int_copy(Ref_as_Int s, Ref_as_Int d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Int_copy(Ref_as_Int s, Val_as_Int d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Ref_copy(Ref_as_Int s, Ref_as_Ref d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Ref_copy(Ref_as_Int s, Val_as_Ref d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Val_copy(Ref_as_Int s, Val_as_Val d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Int_to_Obj_copy(Val_as_Int s, Obj_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_to_Obj_copy(Val_as_Int s, Int_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_to_Obj_copy(Val_as_Int s, Ref_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Obj_copy(Val_as_Int s, Val_as_Obj d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_to_Int_copy(Val_as_Int s, Int_as_Int d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_to_Int_copy(Val_as_Int s, Ref_as_Int d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Int_copy(Val_as_Int s, Val_as_Int d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_to_Ref_copy(Val_as_Int s, Ref_as_Ref d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Ref_copy(Val_as_Int s, Val_as_Ref d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Val_copy(Val_as_Int s, Val_as_Val d) {
+        Int64[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = (Q64long)src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Ref_to_Obj_copy(Ref_as_Ref s, Obj_as_Obj d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Obj_copy(Ref_as_Ref s, Int_as_Obj d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Obj_copy(Ref_as_Ref s, Ref_as_Obj d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Obj_copy(Ref_as_Ref s, Val_as_Obj d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Int_copy(Ref_as_Ref s, Int_as_Int d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Int_copy(Ref_as_Ref s, Ref_as_Int d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Int_copy(Ref_as_Ref s, Val_as_Int d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Ref_copy(Ref_as_Ref s, Ref_as_Ref d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Ref_copy(Ref_as_Ref s, Val_as_Ref d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Val_copy(Ref_as_Ref s, Val_as_Val d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Ref_to_Obj_copy(Val_as_Ref s, Obj_as_Obj d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Ref_to_Obj_copy(Val_as_Ref s, Int_as_Obj d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_to_Obj_copy(Val_as_Ref s, Ref_as_Obj d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Obj_copy(Val_as_Ref s, Val_as_Obj d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Ref_to_Int_copy(Val_as_Ref s, Int_as_Int d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_to_Int_copy(Val_as_Ref s, Ref_as_Int d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Int_copy(Val_as_Ref s, Val_as_Int d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_to_Ref_copy(Val_as_Ref s, Ref_as_Ref d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Ref_copy(Val_as_Ref s, Val_as_Ref d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Val_copy(Val_as_Ref s, Val_as_Val d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Val_to_Obj_copy(Val_as_Val s, Obj_as_Obj d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Val_to_Obj_copy(Val_as_Val s, Int_as_Obj d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Val_to_Obj_copy(Val_as_Val s, Ref_as_Obj d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Obj_copy(Val_as_Val s, Val_as_Obj d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Val_to_Int_copy(Val_as_Val s, Int_as_Int d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Val_to_Int_copy(Val_as_Val s, Ref_as_Int d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Int_copy(Val_as_Val s, Val_as_Int d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Val_to_Ref_copy(Val_as_Val s, Ref_as_Ref d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Ref_copy(Val_as_Val s, Val_as_Ref d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Val_copy(Val_as_Val s, Val_as_Val d) {
+        Q64long[] src = s.arr;
+        for (int i = 0; i < src.length; i++) {
+            d.arr[i] = src[i];
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/InlineCopy2.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/InlineCopy2.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/InlineCopy2.java
@@ -0,0 +1,1035 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.copy;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class InlineCopy2 extends StatesQ64long {
+
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_to_Obj_copy(Obj_as_Obj s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Obj_copy(Obj_as_Obj s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Obj_copy(Obj_as_Obj s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Obj_copy(Obj_as_Obj s, Val_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Int_copy(Obj_as_Obj s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Int_copy(Obj_as_Obj s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Int_copy(Obj_as_Obj s, Val_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Ref_copy(Obj_as_Obj s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Ref_copy(Obj_as_Obj s, Val_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Val_copy(Obj_as_Obj s, Val_as_Val d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_to_Obj_copy(Int_as_Obj s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Obj_copy(Int_as_Obj s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Obj_copy(Int_as_Obj s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Obj_copy(Int_as_Obj s, Val_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Int_copy(Int_as_Obj s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Int_copy(Int_as_Obj s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Int_copy(Int_as_Obj s, Val_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Ref_copy(Int_as_Obj s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Ref_copy(Int_as_Obj s, Val_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Val_copy(Int_as_Obj s, Val_as_Val d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_to_Obj_copy(Ref_as_Obj s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Obj_copy(Ref_as_Obj s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Obj_copy(Ref_as_Obj s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Obj_copy(Ref_as_Obj s, Val_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Int_copy(Ref_as_Obj s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Int_copy(Ref_as_Obj s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Int_copy(Ref_as_Obj s, Val_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Ref_copy(Ref_as_Obj s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Ref_copy(Ref_as_Obj s, Val_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Val_copy(Ref_as_Obj s, Val_as_Val d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Obj_to_Obj_copy(Val_as_Obj s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_to_Obj_copy(Val_as_Obj s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_to_Obj_copy(Val_as_Obj s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Obj_copy(Val_as_Obj s, Val_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_to_Int_copy(Val_as_Obj s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_to_Int_copy(Val_as_Obj s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Int_copy(Val_as_Obj s, Val_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_to_Ref_copy(Val_as_Obj s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Ref_copy(Val_as_Obj s, Val_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Val_copy(Val_as_Obj s, Val_as_Val d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Int_to_Obj_copy(Int_as_Int s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Obj_copy(Int_as_Int s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Obj_copy(Int_as_Int s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Obj_copy(Int_as_Int s, Val_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Int_copy(Int_as_Int s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Int_copy(Int_as_Int s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Int_copy(Int_as_Int s, Val_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Ref_copy(Int_as_Int s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Ref_copy(Int_as_Int s, Val_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Val_copy(Int_as_Int s, Val_as_Val d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Int_to_Obj_copy(Ref_as_Int s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Obj_copy(Ref_as_Int s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Obj_copy(Ref_as_Int s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Obj_copy(Ref_as_Int s, Val_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Int_copy(Ref_as_Int s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Int_copy(Ref_as_Int s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Int_copy(Ref_as_Int s, Val_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Ref_copy(Ref_as_Int s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Ref_copy(Ref_as_Int s, Val_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Val_copy(Ref_as_Int s, Val_as_Val d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Int_to_Obj_copy(Val_as_Int s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_to_Obj_copy(Val_as_Int s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_to_Obj_copy(Val_as_Int s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Obj_copy(Val_as_Int s, Val_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_to_Int_copy(Val_as_Int s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_to_Int_copy(Val_as_Int s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Int_copy(Val_as_Int s, Val_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_to_Ref_copy(Val_as_Int s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Ref_copy(Val_as_Int s, Val_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Val_copy(Val_as_Int s, Val_as_Val d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Ref_to_Obj_copy(Ref_as_Ref s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Obj_copy(Ref_as_Ref s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Obj_copy(Ref_as_Ref s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Obj_copy(Ref_as_Ref s, Val_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Int_copy(Ref_as_Ref s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Int_copy(Ref_as_Ref s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Int_copy(Ref_as_Ref s, Val_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Ref_copy(Ref_as_Ref s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Ref_copy(Ref_as_Ref s, Val_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Val_copy(Ref_as_Ref s, Val_as_Val d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Ref_to_Obj_copy(Val_as_Ref s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Ref_to_Obj_copy(Val_as_Ref s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_to_Obj_copy(Val_as_Ref s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Obj_copy(Val_as_Ref s, Val_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Ref_to_Int_copy(Val_as_Ref s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_to_Int_copy(Val_as_Ref s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Int_copy(Val_as_Ref s, Val_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_to_Ref_copy(Val_as_Ref s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Ref_copy(Val_as_Ref s, Val_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Val_copy(Val_as_Ref s, Val_as_Val d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Val_to_Obj_copy(Val_as_Val s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Val_to_Obj_copy(Val_as_Val s, Int_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Val_to_Obj_copy(Val_as_Val s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Obj_copy(Val_as_Val s, Val_as_Obj d) {
+        int len = s.arr.length;
+        Object[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Val_to_Int_copy(Val_as_Val s, Int_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Val_to_Int_copy(Val_as_Val s, Ref_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Int_copy(Val_as_Val s, Val_as_Int d) {
+        int len = s.arr.length;
+        Int64[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Val_to_Ref_copy(Val_as_Val s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Ref_copy(Val_as_Val s, Val_as_Ref d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Val_copy(Val_as_Val s, Val_as_Val d) {
+        int len = s.arr.length;
+        Q64long[] dst = d.arr;
+        for (int i = 0; i < len; i++) {
+            dst[i] = s.arr[i];
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/InlineCopy3.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/InlineCopy3.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/copy/InlineCopy3.java
@@ -0,0 +1,935 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.copy;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class InlineCopy3 extends StatesQ64long {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_to_Obj_copy(Obj_as_Obj s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Obj_copy(Obj_as_Obj s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Obj_copy(Obj_as_Obj s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Obj_copy(Obj_as_Obj s, Val_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_to_Int_copy(Obj_as_Obj s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Int_copy(Obj_as_Obj s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Int_copy(Obj_as_Obj s, Val_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_to_Ref_copy(Obj_as_Obj s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Ref_copy(Obj_as_Obj s, Val_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_to_Val_copy(Obj_as_Obj s, Val_as_Val d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_to_Obj_copy(Int_as_Obj s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Obj_copy(Int_as_Obj s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Obj_copy(Int_as_Obj s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Obj_copy(Int_as_Obj s, Val_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_to_Int_copy(Int_as_Obj s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Int_copy(Int_as_Obj s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Int_copy(Int_as_Obj s, Val_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_to_Ref_copy(Int_as_Obj s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Ref_copy(Int_as_Obj s, Val_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_to_Val_copy(Int_as_Obj s, Val_as_Val d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_to_Obj_copy(Ref_as_Obj s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Obj_copy(Ref_as_Obj s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Obj_copy(Ref_as_Obj s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Obj_copy(Ref_as_Obj s, Val_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_to_Int_copy(Ref_as_Obj s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Int_copy(Ref_as_Obj s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Int_copy(Ref_as_Obj s, Val_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_to_Ref_copy(Ref_as_Obj s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Ref_copy(Ref_as_Obj s, Val_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_to_Val_copy(Ref_as_Obj s, Val_as_Val d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Obj_to_Obj_copy(Val_as_Obj s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_to_Obj_copy(Val_as_Obj s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_to_Obj_copy(Val_as_Obj s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Obj_copy(Val_as_Obj s, Val_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_to_Int_copy(Val_as_Obj s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_to_Int_copy(Val_as_Obj s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Int_copy(Val_as_Obj s, Val_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Int64)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_to_Ref_copy(Val_as_Obj s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Ref_copy(Val_as_Obj s, Val_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_to_Val_copy(Val_as_Obj s, Val_as_Val d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Int_to_Obj_copy(Int_as_Int s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Obj_copy(Int_as_Int s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Obj_copy(Int_as_Int s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Obj_copy(Int_as_Int s, Val_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_to_Int_copy(Int_as_Int s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Int_copy(Int_as_Int s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Int_copy(Int_as_Int s, Val_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_to_Ref_copy(Int_as_Int s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Ref_copy(Int_as_Int s, Val_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_to_Val_copy(Int_as_Int s, Val_as_Val d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Int_to_Obj_copy(Ref_as_Int s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Obj_copy(Ref_as_Int s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Obj_copy(Ref_as_Int s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Obj_copy(Ref_as_Int s, Val_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_to_Int_copy(Ref_as_Int s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Int_copy(Ref_as_Int s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Int_copy(Ref_as_Int s, Val_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_to_Ref_copy(Ref_as_Int s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Ref_copy(Ref_as_Int s, Val_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_to_Val_copy(Ref_as_Int s, Val_as_Val d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Int_to_Obj_copy(Val_as_Int s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_to_Obj_copy(Val_as_Int s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_to_Obj_copy(Val_as_Int s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Obj_copy(Val_as_Int s, Val_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_to_Int_copy(Val_as_Int s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_to_Int_copy(Val_as_Int s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Int_copy(Val_as_Int s, Val_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_to_Ref_copy(Val_as_Int s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Ref_copy(Val_as_Int s, Val_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_to_Val_copy(Val_as_Int s, Val_as_Val d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = (Q64long)s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Ref_to_Obj_copy(Ref_as_Ref s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Obj_copy(Ref_as_Ref s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Obj_copy(Ref_as_Ref s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Obj_copy(Ref_as_Ref s, Val_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Ref_to_Int_copy(Ref_as_Ref s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Int_copy(Ref_as_Ref s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Int_copy(Ref_as_Ref s, Val_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_to_Ref_copy(Ref_as_Ref s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Ref_copy(Ref_as_Ref s, Val_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_to_Val_copy(Ref_as_Ref s, Val_as_Val d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Ref_to_Obj_copy(Val_as_Ref s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Ref_to_Obj_copy(Val_as_Ref s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_to_Obj_copy(Val_as_Ref s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Obj_copy(Val_as_Ref s, Val_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Ref_to_Int_copy(Val_as_Ref s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_to_Int_copy(Val_as_Ref s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Int_copy(Val_as_Ref s, Val_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_to_Ref_copy(Val_as_Ref s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Ref_copy(Val_as_Ref s, Val_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_to_Val_copy(Val_as_Ref s, Val_as_Val d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Val_to_Obj_copy(Val_as_Val s, Obj_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Val_to_Obj_copy(Val_as_Val s, Int_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Val_to_Obj_copy(Val_as_Val s, Ref_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Obj_copy(Val_as_Val s, Val_as_Obj d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Val_to_Int_copy(Val_as_Val s, Int_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Val_to_Int_copy(Val_as_Val s, Ref_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Int_copy(Val_as_Val s, Val_as_Int d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Val_to_Ref_copy(Val_as_Val s, Ref_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Ref_copy(Val_as_Val s, Val_as_Ref d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_to_Val_copy(Val_as_Val s, Val_as_Val d) {
+        int len = s.arr.length;
+        for (int i = 0; i < len; i++) {
+            d.arr[i] = s.arr[i];
+        }
+    }
+
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillDef.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillDef.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillDef.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.fill;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import java.util.Arrays;
+
+public class Inline64longFillDef extends StatesQ64long {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Val_fill0(Val_as_Val st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Val_fill1(Val_as_Val st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Ref_fill0(Val_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Ref_fill1(Val_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Ref_fill0(Ref_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Ref_fill1(Ref_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Int_fill0(Val_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Int_fill1(Val_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Int_fill0(Ref_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Int_fill1(Ref_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Int_as_Int_fill0(Int_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Int_as_Int_fill1(Int_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Obj_fill0(Val_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Obj_fill1(Val_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Obj_fill0(Ref_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Obj_fill1(Ref_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Int_as_Obj_fill0(Int_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Int_as_Obj_fill1(Int_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Obj_as_Obj_fill0(Obj_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Obj_as_Obj_fill1(Obj_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Val_arrayfill(Val_as_Val st) {
+        Arrays.fill(st.arr, new Q64long()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Ref_arrayfill(Val_as_Ref st) {
+        Arrays.fill(st.arr, new Q64long()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, new Q64long()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Int_arrayfill(Val_as_Int st) {
+        Arrays.fill(st.arr, new Q64long()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Int_arrayfill(Ref_as_Int st) {
+        Arrays.fill(st.arr, new Q64long()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Int_as_Int_arrayfill(Int_as_Int st) {
+        Arrays.fill(st.arr, new Q64long()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_as_Obj_arrayfill(Val_as_Obj st) {
+        Arrays.fill(st.arr, new Q64long()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_as_Obj_arrayfill(Ref_as_Obj st) {
+        Arrays.fill(st.arr, new Q64long()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Int_as_Obj_arrayfill(Int_as_Obj st) {
+        Arrays.fill(st.arr, new Q64long()  );
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Obj_as_Obj_arrayfill(Obj_as_Obj st) {
+        Arrays.fill(st.arr, new Q64long()  );
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillInstInt.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillInstInt.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillInstInt.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.fill;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+import java.util.Arrays;
+
+public class Inline64longFillInstInt extends StatesQ64long {
+
+
+    @State(Scope.Thread)
+    public static class InstanceField {
+        Q64long f = new Q64long(42);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Val_fillinst0( Val_as_Val st, InstanceField f) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long) f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Val_fillinst1( Val_as_Val st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long) f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Ref_fillinst0( Val_as_Ref st, InstanceField f) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long) f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Ref_fillinst1( Val_as_Ref st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long) f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Ref_fillinst0( Ref_as_Ref st, InstanceField f) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long) f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Ref_fillinst1( Ref_as_Ref st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long) f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_fillinst0( Val_as_Int st, InstanceField f) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_fillinst1( Val_as_Int st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_fillinst0( Ref_as_Int st, InstanceField f) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_fillinst1( Ref_as_Int st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_fillinst0( Int_as_Int st, InstanceField f) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_fillinst1( Int_as_Int st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_fillinst0( Val_as_Obj st, InstanceField f) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_fillinst1( Val_as_Obj st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_fillinst0( Ref_as_Obj st, InstanceField f) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_fillinst1( Ref_as_Obj st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_fillinst0( Int_as_Obj st, InstanceField f) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_fillinst1( Int_as_Obj st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_fillinst0( Obj_as_Obj st, InstanceField f) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_fillinst1( Obj_as_Obj st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Val_arrayfill_arrayfillinst(Val_as_Val st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Ref_arrayfillinst(Val_as_Ref st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Ref_arrayfillinst(Ref_as_Ref st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_arrayfillinst(Val_as_Int st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_arrayfillinst(Ref_as_Int st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_arrayfillinst(Int_as_Int st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_arrayfillinst(Val_as_Obj st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_arrayfillinst(Ref_as_Obj st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_arrayfillinst(Int_as_Obj st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_arrayfillinst(Obj_as_Obj st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillInstObj.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillInstObj.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillInstObj.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.fill;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+import java.util.Arrays;
+
+public class Inline64longFillInstObj extends StatesQ64long {
+
+
+    @State(Scope.Thread)
+    public static class InstanceField {
+        Object f = new Q64long(42);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Val_fillinst0(Val_as_Val st, InstanceField f) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long) f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Val_fillinst1(Val_as_Val st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long) f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Ref_fillinst0(Val_as_Ref st, InstanceField f) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long) f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Ref_fillinst1(Val_as_Ref st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long) f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Ref_fillinst0(Ref_as_Ref st, InstanceField f) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long) f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Ref_fillinst1(Ref_as_Ref st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long) f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Int_fillinst0(Val_as_Int st, InstanceField f) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Int64) f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Int_fillinst1(Val_as_Int st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Int64) f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Int_fillinst0(Ref_as_Int st, InstanceField f) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Int64) f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Int_fillinst1(Ref_as_Int st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Int64) f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Int_fillinst0(Int_as_Int st, InstanceField f) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Int64) f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Int_fillinst1(Int_as_Int st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Int64) f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_fillinst0(Val_as_Obj st, InstanceField f) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_fillinst1(Val_as_Obj st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_fillinst0(Ref_as_Obj st, InstanceField f) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_fillinst1(Ref_as_Obj st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_fillinst0(Int_as_Obj st, InstanceField f) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_fillinst1(Int_as_Obj st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_fillinst0(Obj_as_Obj st, InstanceField f) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_fillinst1(Obj_as_Obj st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Val_arrayfillinst(Val_as_Val st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Ref_arrayfillinst(Val_as_Ref st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Ref_arrayfillinst(Ref_as_Ref st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Int_arrayfillinst(Val_as_Int st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Int_arrayfillinst(Ref_as_Int st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Int_arrayfillinst(Int_as_Int st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_arrayfillinst(Val_as_Obj st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_arrayfillinst(Ref_as_Obj st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_arrayfillinst(Int_as_Obj st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_arrayfillinst(Obj_as_Obj st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillInstRef.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillInstRef.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillInstRef.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.fill;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+import java.util.Arrays;
+
+public class Inline64longFillInstRef extends StatesQ64long {
+
+
+    @State(Scope.Thread)
+    public static class InstanceField {
+        Q64long f = new Q64long(42);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_fillinst0(Val_as_Val st, InstanceField f) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_fillinst1(Val_as_Val st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_fillinst0(Val_as_Ref st, InstanceField f) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_fillinst1(Val_as_Ref st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fillinst0(Ref_as_Ref st, InstanceField f) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fillinst1(Ref_as_Ref st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_fillinst0(Val_as_Int st, InstanceField f) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_fillinst1(Val_as_Int st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_fillinst0(Ref_as_Int st, InstanceField f) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_fillinst1(Ref_as_Int st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_fillinst0(Int_as_Int st, InstanceField f) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_fillinst1(Int_as_Int st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_fillinst0(Val_as_Obj st, InstanceField f) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_fillinst1(Val_as_Obj st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_fillinst0(Ref_as_Obj st, InstanceField f) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_fillinst1(Ref_as_Obj st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_fillinst0(Int_as_Obj st, InstanceField f) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_fillinst1(Int_as_Obj st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_fillinst0(Obj_as_Obj st, InstanceField f) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_fillinst1(Obj_as_Obj st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_arrayfillinst(Val_as_Val st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_arrayfillinst(Val_as_Ref st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_arrayfillinst(Ref_as_Ref st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_arrayfillinst(Val_as_Int st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_arrayfillinst(Ref_as_Int st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_arrayfillinst(Int_as_Int st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_arrayfillinst(Val_as_Obj st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_arrayfillinst(Ref_as_Obj st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_arrayfillinst(Int_as_Obj st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_arrayfillinst(Obj_as_Obj st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillInstVal.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillInstVal.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillInstVal.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.fill;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+import java.util.Arrays;
+
+public class Inline64longFillInstVal extends StatesQ64long {
+
+
+    @State(Scope.Thread)
+    public static class InstanceField {
+        Q64long f = new Q64long(42);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_fillinst0(Val_as_Val st, InstanceField f) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_fillinst1(Val_as_Val st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_fillinst0(Val_as_Ref st, InstanceField f) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_fillinst1(Val_as_Ref st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_fillinst0(Ref_as_Ref st, InstanceField f) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_fillinst1(Ref_as_Ref st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_fillinst0(Val_as_Int st, InstanceField f) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_fillinst1(Val_as_Int st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_fillinst0(Ref_as_Int st, InstanceField f) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_fillinst1(Ref_as_Int st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_fillinst0(Int_as_Int st, InstanceField f) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_fillinst1(Int_as_Int st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_fillinst0(Val_as_Obj st, InstanceField f) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_fillinst1(Val_as_Obj st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_fillinst0(Ref_as_Obj st, InstanceField f) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_fillinst1(Ref_as_Obj st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_fillinst0(Int_as_Obj st, InstanceField f) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_fillinst1(Int_as_Obj st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Obj_fillinst0(Obj_as_Obj st, InstanceField f) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Obj_fillinst1(Obj_as_Obj st, InstanceField f) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = f.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_arrayfillinst(Val_as_Val st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_arrayfillinst(Val_as_Ref st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_arrayfillinst(Ref_as_Ref st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_arrayfillinst(Val_as_Int st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_arrayfillinst(Ref_as_Int st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_arrayfillinst(Int_as_Int st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_arrayfillinst(Val_as_Obj st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_arrayfillinst(Ref_as_Obj st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_arrayfillinst(Int_as_Obj st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Obj_arrayfillinst(Obj_as_Obj st, InstanceField f) {
+        Arrays.fill(st.arr, f.f);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillInt.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillInt.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillInt.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.fill;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+import java.util.Arrays;
+
+public class Inline64longFillInt extends StatesQ64long {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Int64 get(int i) {
+        return new Q64long(i);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Val_fill0(Val_as_Val st) {
+        Q64long[] arr = st.arr;
+        Int64 v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long)v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Val_fill1(Val_as_Val st) {
+        int len = st.arr.length;
+        Int64 v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long)v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Ref_fill0(Val_as_Ref st) {
+        Q64long[] arr = st.arr;
+        Int64 v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long)v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Ref_fill1(Val_as_Ref st) {
+        int len = st.arr.length;
+        Int64 v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long)v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Ref_fill0(Ref_as_Ref st) {
+        Q64long[] arr = st.arr;
+        Int64 v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long)v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Ref_fill1(Ref_as_Ref st) {
+        int len = st.arr.length;
+        Int64 v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long)v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_fill0(Val_as_Int st) {
+        Int64[] arr = st.arr;
+        Int64 v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_fill1(Val_as_Int st) {
+        int len = st.arr.length;
+        Int64 v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_fill0(Ref_as_Int st) {
+        Int64[] arr = st.arr;
+        Int64 v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_fill1(Ref_as_Int st) {
+        int len = st.arr.length;
+        Int64 v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_fill0(Int_as_Int st) {
+        Int64[] arr = st.arr;
+        Int64 v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_fill1(Int_as_Int st) {
+        int len = st.arr.length;
+        Int64 v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_fill0(Val_as_Obj st) {
+        Object[] arr = st.arr;
+        Int64 v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_fill1(Val_as_Obj st) {
+        int len = st.arr.length;
+        Int64 v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_fill0(Ref_as_Obj st) {
+        Object[] arr = st.arr;
+        Int64 v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_fill1(Ref_as_Obj st) {
+        int len = st.arr.length;
+        Int64 v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_fill0(Int_as_Obj st) {
+        Object[] arr = st.arr;
+        Int64 v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_fill1(Int_as_Obj st) {
+        int len = st.arr.length;
+        Int64 v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_fill0(Obj_as_Obj st) {
+        Object[] arr = st.arr;
+        Int64 v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_fill1(Obj_as_Obj st) {
+        int len = st.arr.length;
+        Int64 v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Val_arrayfill(Val_as_Val st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Ref_arrayfill(Val_as_Ref st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_arrayfill(Val_as_Int st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_arrayfill(Ref_as_Int st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_arrayfill(Int_as_Int st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_arrayfill(Val_as_Obj st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_arrayfill(Ref_as_Obj st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_arrayfill(Int_as_Obj st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_arrayfill(Obj_as_Obj st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillNew.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillNew.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillNew.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.fill;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+import java.util.Arrays;
+
+public class Inline64longFillNew extends StatesQ64long {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Val_fill0(Val_as_Val st) {
+        Q64long[] arr = st.arr;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Val_fill1(Val_as_Val st) {
+        int len = st.arr.length;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Ref_fill0(Val_as_Ref st) {
+        Q64long[] arr = st.arr;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Ref_fill1(Val_as_Ref st) {
+        int len = st.arr.length;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_fill0(Ref_as_Ref st) {
+        Q64long[] arr = st.arr;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_fill1(Ref_as_Ref st) {
+        int len = st.arr.length;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Int_fill0(Val_as_Int st) {
+        Int64[] arr = st.arr;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Int_fill1(Val_as_Int st) {
+        int len = st.arr.length;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Int_fill0(Ref_as_Int st) {
+        Int64[] arr = st.arr;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Int_fill1(Ref_as_Int st) {
+        int len = st.arr.length;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Int_as_Int_fill0(Int_as_Int st) {
+        Int64[] arr = st.arr;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Int_as_Int_fill1(Int_as_Int st) {
+        int len = st.arr.length;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Obj_fill0(Val_as_Obj st) {
+        Object[] arr = st.arr;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Obj_fill1(Val_as_Obj st) {
+        int len = st.arr.length;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Obj_fill0(Ref_as_Obj st) {
+        Object[] arr = st.arr;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Obj_fill1(Ref_as_Obj st) {
+        int len = st.arr.length;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Int_as_Obj_fill0(Int_as_Obj st) {
+        Object[] arr = st.arr;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Int_as_Obj_fill1(Int_as_Obj st) {
+        int len = st.arr.length;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Obj_as_Obj_fill0(Obj_as_Obj st) {
+        Object[] arr = st.arr;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Obj_as_Obj_fill1(Obj_as_Obj st) {
+        int len = st.arr.length;
+        Q64long v = new Q64long(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Val_arrayfill(Val_as_Val st) {
+        Arrays.fill(st.arr, new Q64long(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Ref_arrayfill(Val_as_Ref st) {
+        Arrays.fill(st.arr, new Q64long(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, new Q64long(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Int_arrayfill(Val_as_Int st) {
+        Arrays.fill(st.arr, new Q64long(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Int_arrayfill(Ref_as_Int st) {
+        Arrays.fill(st.arr, new Q64long(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Int_as_Int_arrayfill(Int_as_Int st) {
+        Arrays.fill(st.arr, new Q64long(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Obj_arrayfill(Val_as_Obj st) {
+        Arrays.fill(st.arr, new Q64long(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Obj_arrayfill(Ref_as_Obj st) {
+        Arrays.fill(st.arr, new Q64long(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Int_as_Obj_arrayfill(Int_as_Obj st) {
+        Arrays.fill(st.arr, new Q64long(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Obj_as_Obj_arrayfill(Obj_as_Obj st) {
+        Arrays.fill(st.arr, new Q64long(42));
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillObj.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillObj.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillObj.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.fill;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+import java.util.Arrays;
+
+public class Inline64longFillObj extends StatesQ64long {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Object get(int i) {
+        return new Q64long(i);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Val_fill0(Val_as_Val st) {
+        Q64long[] arr = st.arr;
+        Object v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long)v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Val_fill1(Val_as_Val st) {
+        int len = st.arr.length;
+        Object v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long)v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Ref_fill0(Val_as_Ref st) {
+        Q64long[] arr = st.arr;
+        Object v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long)v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Ref_fill1(Val_as_Ref st) {
+        int len = st.arr.length;
+        Object v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long)v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Ref_fill0(Ref_as_Ref st) {
+        Q64long[] arr = st.arr;
+        Object v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long)v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Ref_fill1(Ref_as_Ref st) {
+        int len = st.arr.length;
+        Object v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long)v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Int_fill0(Val_as_Int st) {
+        Int64[] arr = st.arr;
+        Object v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Int64)v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Int_fill1(Val_as_Int st) {
+        int len = st.arr.length;
+        Object v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Int64)v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Int_fill0(Ref_as_Int st) {
+        Int64[] arr = st.arr;
+        Object v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Int64)v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Int_fill1(Ref_as_Int st) {
+        int len = st.arr.length;
+        Object v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Int64)v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Int_fill0(Int_as_Int st) {
+        Int64[] arr = st.arr;
+        Object v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Int64)v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Int_fill1(Int_as_Int st) {
+        int len = st.arr.length;
+        Object v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Int64)v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_fill0(Val_as_Obj st) {
+        Object[] arr = st.arr;
+        Object v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_fill1(Val_as_Obj st) {
+        int len = st.arr.length;
+        Object v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_fill0(Ref_as_Obj st) {
+        Object[] arr = st.arr;
+        Object v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_fill1(Ref_as_Obj st) {
+        int len = st.arr.length;
+        Object v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_fill0(Int_as_Obj st) {
+        Object[] arr = st.arr;
+        Object v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_fill1(Int_as_Obj st) {
+        int len = st.arr.length;
+        Object v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_fill0(Obj_as_Obj st) {
+        Object[] arr = st.arr;
+        Object v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_fill1(Obj_as_Obj st) {
+        int len = st.arr.length;
+        Object v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Val_arrayfill(Val_as_Val st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Ref_arrayfill(Val_as_Ref st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Int_arrayfill(Val_as_Int st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Int_arrayfill(Ref_as_Int st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Int_arrayfill(Int_as_Int st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_arrayfill(Val_as_Obj st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_arrayfill(Ref_as_Obj st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_arrayfill(Int_as_Obj st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_arrayfill(Obj_as_Obj st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillRef.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillRef.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillRef.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.fill;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+import java.util.Arrays;
+
+public class Inline64longFillRef extends StatesQ64long {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q64long get(int i) {
+        return new Q64long(i);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_fill0(Val_as_Val st) {
+        Q64long[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_fill1(Val_as_Val st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_fill0(Val_as_Ref st) {
+        Q64long[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_fill1(Val_as_Ref st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fill0(Ref_as_Ref st) {
+        Q64long[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fill1(Ref_as_Ref st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_fill0(Val_as_Int st) {
+        Int64[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_fill1(Val_as_Int st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_fill0(Ref_as_Int st) {
+        Int64[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_fill1(Ref_as_Int st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_fill0(Int_as_Int st) {
+        Int64[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_fill1(Int_as_Int st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_fill0(Val_as_Obj st) {
+        Object[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_fill1(Val_as_Obj st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_fill0(Ref_as_Obj st) {
+        Object[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_fill1(Ref_as_Obj st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_fill0(Int_as_Obj st) {
+        Object[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_fill1(Int_as_Obj st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_fill0(Obj_as_Obj st) {
+        Object[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_fill1(Obj_as_Obj st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_arrayfill(Val_as_Val st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_arrayfill(Val_as_Ref st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_arrayfill(Val_as_Int st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_arrayfill(Ref_as_Int st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_arrayfill(Int_as_Int st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_arrayfill(Val_as_Obj st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_arrayfill(Ref_as_Obj st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_arrayfill(Int_as_Obj st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_arrayfill(Obj_as_Obj st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillStatInt.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillStatInt.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillStatInt.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.fill;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+import java.util.Arrays;
+
+public class Inline64longFillStatInt extends StatesQ64long {
+
+    public static class StaticField {
+        static Int64 f = new Q64long(42);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Val_fillstat0(Val_as_Val st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long)StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Val_fillstat1(Val_as_Val st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long)StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Ref_fillstat0(Val_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long)StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Ref_fillstat1(Val_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long)StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Ref_fillstat0(Ref_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long)StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Ref_fillstat1(Ref_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long)StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_fillstat0(Val_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_fillstat1(Val_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_fillstat0(Ref_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_fillstat1(Ref_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_fillstat0(Int_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_fillstat1(Int_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_fillstat0(Val_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_fillstat1(Val_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_fillstat0(Ref_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_fillstat1(Ref_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_fillstat0(Int_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_fillstat1(Int_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_fillstat0(Obj_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_fillstat1(Obj_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Val_arrayfillstat(Val_as_Val st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Ref_arrayfillstat(Val_as_Ref st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Ref_arrayfillstat(Ref_as_Ref st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_arrayfillstat(Val_as_Int st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_arrayfillstat(Ref_as_Int st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_arrayfillstat(Int_as_Int st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_arrayfillstat(Val_as_Obj st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_arrayfillstat(Ref_as_Obj st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_arrayfillstat(Int_as_Obj st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_arrayfillstat(Obj_as_Obj st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillStatObj.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillStatObj.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillStatObj.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.fill;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+import java.util.Arrays;
+
+public class Inline64longFillStatObj extends StatesQ64long {
+
+    public static class StaticField {
+        static Object f = new Q64long(42);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Val_fillstat0(Val_as_Val st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long)StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Val_fillstat1(Val_as_Val st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long)StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Ref_fillstat0(Val_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long)StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Ref_fillstat1(Val_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long)StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Ref_fillstat0(Ref_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long)StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Ref_fillstat1(Ref_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long)StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Int_fillstat0(Val_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Int64) StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Int_fillstat1(Val_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Int64) StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Int_fillstat0(Ref_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Int64) StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Int_fillstat1(Ref_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Int64) StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Int_fillstat0(Int_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Int64) StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Int_fillstat1(Int_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Int64) StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_fillstat0(Val_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_fillstat1(Val_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_fillstat0(Ref_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_fillstat1(Ref_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_fillstat0(Int_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_fillstat1(Int_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_fillstat0(Obj_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_fillstat1(Obj_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Val_arrayfillstat(Val_as_Val st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Ref_arrayfillstat(Val_as_Ref st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Ref_arrayfillstat(Ref_as_Ref st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Int_arrayfillstat(Val_as_Int st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Int_arrayfillstat(Ref_as_Int st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Int_arrayfillstat(Int_as_Int st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_arrayfillstat(Val_as_Obj st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_arrayfillstat(Ref_as_Obj st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_arrayfillstat(Int_as_Obj st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_arrayfillstat(Obj_as_Obj st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillStatRef.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillStatRef.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillStatRef.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.fill;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+import java.util.Arrays;
+
+public class Inline64longFillStatRef extends StatesQ64long {
+
+    public static class StaticField {
+        static Q64long f = new Q64long(42);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_fillstat0(Val_as_Val st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_fillstat1(Val_as_Val st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_fillstat0(Val_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_fillstat1(Val_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fillstat0(Ref_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_fillstat1(Ref_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_fillstat0(Val_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_fillstat1(Val_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_fillstat0(Ref_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_fillstat1(Ref_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_fillstat0(Int_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_fillstat1(Int_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_fillstat0(Val_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_fillstat1(Val_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_fillstat0(Ref_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_fillstat1(Ref_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_fillstat0(Int_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_fillstat1(Int_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_fillstat0(Obj_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_fillstat1(Obj_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_arrayfillstat(Val_as_Val st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_arrayfillstat(Val_as_Ref st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_arrayfillstat(Ref_as_Ref st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_arrayfillstat(Val_as_Int st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_arrayfillstat(Ref_as_Int st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_arrayfillstat(Int_as_Int st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_arrayfillstat(Val_as_Obj st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_arrayfillstat(Ref_as_Obj st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_arrayfillstat(Int_as_Obj st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_arrayfillstat(Obj_as_Obj st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillStatVal.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillStatVal.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillStatVal.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.fill;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+import java.util.Arrays;
+
+public class Inline64longFillStatVal extends StatesQ64long {
+
+    public static class StaticField {
+        static Q64long f = new Q64long(42);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_fillstat0(Val_as_Val st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_fillstat1(Val_as_Val st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_fillstat0(Val_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_fillstat1(Val_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_fillstat0(Ref_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_fillstat1(Ref_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_fillstat0(Val_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_fillstat1(Val_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_fillstat0(Ref_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_fillstat1(Ref_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_fillstat0(Int_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_fillstat1(Int_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_fillstat0(Val_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_fillstat1(Val_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_fillstat0(Ref_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_fillstat1(Ref_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_fillstat0(Int_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_fillstat1(Int_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Obj_fillstat0(Obj_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Obj_fillstat1(Obj_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = StaticField.f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_arrayfillstat(Val_as_Val st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_arrayfillstat(Val_as_Ref st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_arrayfillstat(Ref_as_Ref st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_arrayfillstat(Val_as_Int st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_arrayfillstat(Ref_as_Int st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_arrayfillstat(Int_as_Int st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_arrayfillstat(Val_as_Obj st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_arrayfillstat(Ref_as_Obj st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_arrayfillstat(Int_as_Obj st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Obj_arrayfillstat(Obj_as_Obj st) {
+        Arrays.fill(st.arr, StaticField.f);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillVal.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillVal.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/fill/Inline64longFillVal.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.fill;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+import java.util.Arrays;
+
+public class Inline64longFillVal extends StatesQ64long {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q64long get(int i) {
+        return new Q64long(i);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_fill0(Val_as_Val st) {
+        Q64long[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_fill1(Val_as_Val st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_fill0(Val_as_Ref st) {
+        Q64long[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_fill1(Val_as_Ref st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_fill0(Ref_as_Ref st) {
+        Q64long[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_fill1(Ref_as_Ref st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_fill0(Val_as_Int st) {
+        Int64[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_fill1(Val_as_Int st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_fill0(Ref_as_Int st) {
+        Int64[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_fill1(Ref_as_Int st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_fill0(Int_as_Int st) {
+        Int64[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_fill1(Int_as_Int st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_fill0(Val_as_Obj st) {
+        Object[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_fill1(Val_as_Obj st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_fill0(Ref_as_Obj st) {
+        Object[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_fill1(Ref_as_Obj st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_fill0(Int_as_Obj st) {
+        Object[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_fill1(Int_as_Obj st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Obj_fill0(Obj_as_Obj st) {
+        Object[] arr = st.arr;
+        Q64long v = get(42);
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Obj_fill1(Obj_as_Obj st) {
+        int len = st.arr.length;
+        Q64long v = get(42);
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = v;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_arrayfill(Val_as_Val st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_arrayfill(Val_as_Ref st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_arrayfill(Ref_as_Ref st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_arrayfill(Val_as_Int st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_arrayfill(Ref_as_Int st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_arrayfill(Int_as_Int st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_arrayfill(Val_as_Obj st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_arrayfill(Ref_as_Obj st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_arrayfill(Int_as_Obj st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Obj_arrayfill(Obj_as_Obj st) {
+        Arrays.fill(st.arr, get(42));
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/read/Inline64long.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/read/Inline64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/read/Inline64long.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.read;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64long extends StatesQ64long {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void val_consume(Q64long v) {
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void ref_consume(Q64long v) {
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void int_consume(Int64 v) {
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void obj_consume(Object v) {
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Val_to_Val_read(Val_as_Val st) {
+        Q64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Val_to_Ref_read(Val_as_Val st) {
+        Q64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            ref_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Val_to_Int_read(Val_as_Val st) {
+        Q64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            int_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Val_to_Obj_read(Val_as_Val st) {
+        Q64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            obj_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Ref_to_Val_read(Val_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Ref_to_Ref_read(Val_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            ref_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Ref_to_Int_read(Val_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            int_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Ref_to_Obj_read(Val_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            obj_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Int_to_Int_read(Val_as_Int st) {
+        Int64[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            int_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Int_to_Obj_read(Val_as_Int st) {
+        Int64[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            obj_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_as_Obj_to_Obj_read(Val_as_Obj st) {
+        Object[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            obj_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_as_Ref_to_Val_read(Ref_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_as_Ref_to_Ref_read(Ref_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            ref_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_as_Ref_to_Int_read(Ref_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            int_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_as_Ref_to_Obj_read(Ref_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            obj_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_as_Int_to_Int_read(Ref_as_Int st) {
+        Int64[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            int_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_as_Int_to_Obj_read(Ref_as_Int st) {
+        Int64[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            obj_consume(arr[i]);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_as_Obj_to_Obj_read(Ref_as_Obj st) {
+        Object[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            obj_consume(arr[i]);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/set/Inline64longSetInt.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/set/Inline64longSetInt.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/set/Inline64longSetInt.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.set;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64longSetInt extends StatesQ64long {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Int64 get(int i) {
+        return new Q64long(i);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Val_set0(Val_as_Val st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long)get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Val_set1(Val_as_Val st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long)get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Ref_set0(Val_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long)get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Ref_set1(Val_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long)get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Ref_set0(Ref_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long)get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Ref_set1(Ref_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long)get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_set0(Val_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Int_set1(Val_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_set0(Ref_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Int_set1(Ref_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_set0(Int_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Int_set1(Int_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_set0(Val_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_as_Obj_set1(Val_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_set0(Ref_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_as_Obj_set1(Ref_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_set0(Int_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_as_Obj_set1(Int_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_set0(Obj_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_as_Obj_set1(Obj_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/set/Inline64longSetNew.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/set/Inline64longSetNew.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/set/Inline64longSetNew.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.set;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64longSetNew extends StatesQ64long {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Val_set0(Val_as_Val st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Val_set1(Val_as_Val st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Ref_set0(Val_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Ref_set1(Val_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_set0(Ref_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Ref_set1(Ref_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Int_set0(Val_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Int_set1(Val_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Int_set0(Ref_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Int_set1(Ref_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Int_as_Int_set0(Int_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Int_as_Int_set1(Int_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Obj_set0(Val_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_as_Obj_set1(Val_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Obj_set0(Ref_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_as_Obj_set1(Ref_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Int_as_Obj_set0(Int_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Int_as_Obj_set1(Int_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Obj_as_Obj_set0(Obj_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Obj_as_Obj_set1(Obj_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = new Q64long(i);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/set/Inline64longSetObj.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/set/Inline64longSetObj.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/set/Inline64longSetObj.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.set;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64longSetObj extends StatesQ64long {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Object get(int i) {
+        return new Q64long(i);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Val_set0(Val_as_Val st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long)get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Val_set1(Val_as_Val st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long)get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Ref_set0(Val_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long)get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Ref_set1(Val_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long)get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Ref_set0(Ref_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Q64long)get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Ref_set1(Ref_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Q64long)get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Int_set0(Val_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Int64)get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Int_set1(Val_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Int64)get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Int_set0(Ref_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Int64)get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Int_set1(Ref_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Int64)get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Int_set0(Int_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = (Int64)get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Int_set1(Int_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = (Int64)get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_set0(Val_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_as_Obj_set1(Val_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_set0(Ref_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_as_Obj_set1(Ref_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_set0(Int_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_as_Obj_set1(Int_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_set0(Obj_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_as_Obj_set1(Obj_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/set/Inline64longSetRef.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/set/Inline64longSetRef.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/set/Inline64longSetRef.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.set;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64longSetRef extends StatesQ64long {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q64long get(int i) {
+        return new Q64long(i);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_set0(Val_as_Val st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Val_set1(Val_as_Val st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_set0(Val_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Ref_set1(Val_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_set0(Ref_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Ref_set1(Ref_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_set0(Val_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Int_set1(Val_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_set0(Ref_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Int_set1(Ref_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_set0(Int_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Int_set1(Int_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_set0(Val_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_as_Obj_set1(Val_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_set0(Ref_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_as_Obj_set1(Ref_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_set0(Int_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_as_Obj_set1(Int_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_set0(Obj_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_as_Obj_set1(Obj_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/set/Inline64longSetVal.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/set/Inline64longSetVal.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/set/Inline64longSetVal.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.set;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64longSetVal extends StatesQ64long {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q64long get(int i) {
+        return new Q64long(i);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_set0(Val_as_Val st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Val_set1(Val_as_Val st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_set0(Val_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Ref_set1(Val_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_set0(Ref_as_Ref st) {
+        Q64long[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Ref_set1(Ref_as_Ref st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_set0(Val_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Int_set1(Val_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_set0(Ref_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Int_set1(Ref_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_set0(Int_as_Int st) {
+        Int64[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Int_set1(Int_as_Int st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_set0(Val_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_as_Obj_set1(Val_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_set0(Ref_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_as_Obj_set1(Ref_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_set0(Int_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_as_Obj_set1(Int_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Obj_set0(Obj_as_Obj st) {
+        Object[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i] = get(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_as_Obj_set1(Obj_as_Obj st) {
+        int len = st.arr.length;
+        for (int i = 0; i < len; i++) {
+            st.arr[i] = get(i);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/sum/Identity64long.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/sum/Identity64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/sum/Identity64long.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.sum;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesR64long;
+import org.openjdk.bench.valhalla.types.A64long;
+import org.openjdk.bench.valhalla.types.ByLong;
+import org.openjdk.bench.valhalla.types.R64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Identity64long extends StatesR64long {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Ref_as_Ref_fields(Ref_as_Ref st) {
+        long s = 0;
+        R64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].v0;
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Ref_as_Abs_sum(Ref_as_Abs st) {
+        long s = 0;
+        A64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].longSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Ref_as_Int_sum(Ref_as_By st) {
+        long s = 0;
+        ByLong[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].longSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Abs_as_Abs_sum(Abs_as_Abs st) {
+        long s = 0;
+        A64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].longSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Abs_as_Int_sum(Abs_as_By st) {
+        long s = 0;
+        ByLong[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].longSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Int_as_Int_sum(By_as_By st) {
+        long s = 0;
+        ByLong[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].longSum();
+        }
+        return s;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/sum/Inline64long.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/sum/Inline64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/sum/Inline64long.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.sum;
+
+import org.openjdk.bench.valhalla.arraytotal.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.ByLong;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64long extends StatesQ64long {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Val_as_Val_fields0(Val_as_Val st) {
+        long s = 0;
+        Q64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].v0;
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Val_as_Val_fields1(Val_as_Val st) {
+        long s = 0;
+        int len = st.arr.length;
+        for(int i=0; i < len; i++) {
+            s += st.arr[i].v0;
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Val_as_Ref_fields0(Val_as_Ref st) {
+        long s = 0;
+        Q64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].v0;
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Val_as_Ref_fields1(Val_as_Ref st) {
+        long s = 0;
+        int len = st.arr.length;
+        for(int i=0; i < len; i++) {
+            s += st.arr[i].v0;
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Ref_as_Ref_fields0(Ref_as_Ref st) {
+        long s = 0;
+        Q64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].v0;
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Ref_as_Ref_fields1(Ref_as_Ref st) {
+        long s = 0;
+        int len = st.arr.length;
+        for(int i=0; i < len; i++) {
+            s += st.arr[i].v0;
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Val_as_Val_sum0(Val_as_Val st) {
+        long s = 0;
+        Q64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].longSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Val_as_Val_sum1(Val_as_Val st) {
+        long s = 0;
+        int len = st.arr.length;
+        for(int i=0; i < len; i++) {
+            s += st.arr[i].longSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Val_as_Ref_sum0(Val_as_Ref st) {
+        long s = 0;
+        Q64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].longSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Val_as_Ref_sum1(Val_as_Ref st) {
+        long s = 0;
+        int len = st.arr.length;
+        for(int i=0; i < len; i++) {
+            s += st.arr[i].longSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Val_as_Int_sum0(Val_as_By st) {
+        long s = 0;
+        ByLong[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].longSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Val_as_Int_sum1(Val_as_By st) {
+        long s = 0;
+        int len = st.arr.length;
+        for(int i=0; i < len; i++) {
+            s += st.arr[i].longSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Ref_as_Ref_sum0(Ref_as_Ref st) {
+        long s = 0;
+        Q64long[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].longSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Ref_as_Ref_sum1(Ref_as_Ref st) {
+        long s = 0;
+        int len = st.arr.length;
+        for(int i=0; i < len; i++) {
+            s += st.arr[i].longSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Ref_as_Int_sum0(Ref_as_By st) {
+        long s = 0;
+        ByLong[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].longSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Ref_as_Int_sum1(Ref_as_By st) {
+        long s = 0;
+        int len = st.arr.length;
+        for(int i=0; i < len; i++) {
+            s += st.arr[i].longSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Int_as_Int_sum0(By_as_By st) {
+        long s = 0;
+        ByLong[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            s += arr[i].longSum();
+        }
+        return s;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Int_as_Int_sum1(By_as_By st) {
+        long s = 0;
+        int len = st.arr.length;
+        for(int i=0; i < len; i++) {
+            s += st.arr[i].longSum();
+        }
+        return s;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/util/StatesQ64long.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/util/StatesQ64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/util/StatesQ64long.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.util;
+
+import org.openjdk.bench.valhalla.types.ByLong;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class StatesQ64long extends SizeBase {
+
+    public static abstract class ObjState extends SizeState {
+        public Object[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q64long(i);
+            }
+        }
+    }
+
+    public static abstract class IntState extends SizeState {
+        public Int64[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q64long(i);
+            }
+        }
+    }
+
+    public static abstract class RefState extends SizeState {
+        public Q64long[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q64long(i);
+            }
+        }
+    }
+
+    public static abstract class ValState extends SizeState {
+        public Q64long[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q64long(i);
+            }
+        }
+    }
+
+    // naming convention: <runtime array type>_as_<static array type>
+
+    public static class Obj_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Int64[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Q64long[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Q64long[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Int64[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Q64long[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Q64long[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new Q64long[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new Q64long[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Val extends ValState {
+        @Setup
+        public void setup() {
+            arr = new Q64long[size];
+            fill();
+        }
+    }
+
+    public static abstract class ByState extends SizeState {
+        public ByLong[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Q64long(i);
+            }
+        }
+    }
+
+    public static class Val_as_By extends ByState {
+        @Setup
+        public void setup() {
+            arr = new Q64long[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_By extends ByState {
+        @Setup
+        public void setup() {
+            arr = new Q64long[size];
+            fill();
+        }
+    }
+
+    public static class By_as_By extends ByState {
+        @Setup
+        public void setup() {
+            arr = new ByLong[size];
+            fill();
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/arraytotal/util/StatesR64long.java b/test/micro/org/openjdk/bench/valhalla/arraytotal/util/StatesR64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/arraytotal/util/StatesR64long.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.arraytotal.util;
+
+import org.openjdk.bench.valhalla.types.ByLong;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.R64long;
+import org.openjdk.bench.valhalla.types.A64long;
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class StatesR64long extends SizeBase {
+
+    public static abstract class ObjState extends SizeState {
+        public Object[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new R64long(i);
+            }
+        }
+    }
+
+    public static abstract class IntState extends SizeState {
+        public Int64[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new R64long(i);
+            }
+        }
+    }
+
+    public static abstract class AbsState extends SizeState {
+        public A64long[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new R64long(i);
+            }
+        }
+    }
+
+    public static abstract class RefState extends SizeState {
+        public R64long[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new R64long(i);
+            }
+        }
+    }
+
+    // naming convention: <runtime array type>_as_<static array type>
+
+    public static class Obj_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Int64[size];
+            fill();
+        }
+    }
+
+    public static class Abs_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new A64long[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new R64long[size];
+            fill();
+        }
+    }
+
+    public static class Int_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Int64[size];
+            fill();
+        }
+    }
+
+    public static class Abs_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new A64long[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new R64long[size];
+            fill();
+        }
+    }
+
+    public static class Abs_as_Abs extends AbsState {
+        @Setup
+        public void setup() {
+            arr = new A64long[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Abs extends AbsState {
+        @Setup
+        public void setup() {
+            arr = new R64long[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new R64long[size];
+            fill();
+        }
+    }
+
+    public static abstract class ByState extends SizeState {
+        public ByLong[] arr;
+        void fill() {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new R64long(i);
+            }
+        }
+    }
+
+    public static class Ref_as_By extends ByState {
+        @Setup
+        public void setup() {
+            arr = new R64long[size];
+            fill();
+        }
+    }
+
+    public static class Abs_as_By extends ByState {
+        @Setup
+        public void setup() {
+            arr = new A64long[size];
+            fill();
+        }
+    }
+
+    public static class By_as_By extends ByState {
+        @Setup
+        public void setup() {
+            arr = new ByLong[size];
+            fill();
+        }
+    }
+
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/callconv/Inline64byte.java b/test/micro/org/openjdk/bench/valhalla/callconv/Inline64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/callconv/Inline64byte.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.callconv;
+
+import org.openjdk.bench.valhalla.types.Q64byte;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class Inline64byte {
+
+    public static final int SIZE = 96;  // must be divisible by 2 and 3 and around 100
+
+    public abstract static class InvocationLogic {
+        public abstract Q64byte compute(Q64byte v1);
+        public abstract Q64byte compute(Q64byte v1, Q64byte v2);
+        public abstract Q64byte compute(Q64byte v1, Q64byte v2, Q64byte v3, Q64byte v4);
+        public abstract Q64byte compute(Q64byte v1, Q64byte v2, Q64byte v3, Q64byte v4, Q64byte v5, Q64byte v6, Q64byte v7, Q64byte v8);
+    }
+
+    public static class InvokeImpl1 extends InvocationLogic {
+
+        @Override
+        public Q64byte compute(Q64byte v1) {
+            return v1;
+        }
+
+        @Override
+        public Q64byte compute(Q64byte v1, Q64byte v2) {
+            return v1;
+        }
+
+        @Override
+        public Q64byte compute(Q64byte v1, Q64byte v2, Q64byte v3, Q64byte v4) {
+            return v1;
+        }
+
+        @Override
+        public Q64byte compute(Q64byte v1, Q64byte v2, Q64byte v3, Q64byte v4, Q64byte v5, Q64byte v6, Q64byte v7, Q64byte v8) {
+            return v1;
+        }
+
+    }
+
+    public static class InvokeImpl2 extends InvocationLogic {
+
+        @Override
+        public Q64byte compute(Q64byte v1) {
+            return v1;
+        }
+
+        @Override
+        public Q64byte compute(Q64byte v1, Q64byte v2) {
+            return v1;
+        }
+
+        @Override
+        public Q64byte compute(Q64byte v1, Q64byte v2, Q64byte v3, Q64byte v4) {
+            return v1;
+        }
+
+        @Override
+        public Q64byte compute(Q64byte v1, Q64byte v2, Q64byte v3, Q64byte v4, Q64byte v5, Q64byte v6, Q64byte v7, Q64byte v8) {
+            return v1;
+        }
+
+    }
+
+    public static class InvokeImpl3 extends InvocationLogic {
+
+        @Override
+        public Q64byte compute(Q64byte v1) {
+            return v1;
+        }
+
+        @Override
+        public Q64byte compute(Q64byte v1, Q64byte v2) {
+            return v1;
+        }
+
+        @Override
+        public Q64byte compute(Q64byte v1, Q64byte v2, Q64byte v3, Q64byte v4) {
+            return v1;
+        }
+
+        @Override
+        public Q64byte compute(Q64byte v1, Q64byte v2, Q64byte v3, Q64byte v4, Q64byte v5, Q64byte v6, Q64byte v7, Q64byte v8) {
+            return v1;
+        }
+
+    }
+
+
+    private static InvocationLogic getImpl(int i, int targets) {
+        switch (i % targets) {
+            case 0:
+                return new InvokeImpl1();
+            case 1:
+                return new InvokeImpl2();
+            case 2:
+                return new InvokeImpl3();
+        }
+        return null;
+    }
+
+    @State(Scope.Thread)
+    public static class StateTargets0 {
+        InvokeImpl1[] arr;
+        @Setup
+        public void setup() {
+            arr = new InvokeImpl1[SIZE];
+            for(int i=0; i < arr.length; i++) {
+                arr[i] = new InvokeImpl1();
+            }
+        }
+    }
+
+    @State(Scope.Thread)
+    public static abstract class StateTargets {
+        InvocationLogic[] arr;
+
+        public void init(int targets) {
+            arr = new InvocationLogic[SIZE];
+            for(int i=0; i < arr.length; i++) {
+                arr[i] = getImpl(i, targets);
+            }
+        }
+    }
+
+    public static class StateTargets1 extends StateTargets {
+        @Setup
+        public void setup() {
+            init(1);
+        }
+    }
+
+    public static class StateTargets2 extends StateTargets {
+        @Setup
+        public void setup() {
+            init(2);
+        }
+    }
+
+    public static class StateTargets3 extends StateTargets {
+        @Setup
+        public void setup() {
+            init(3);
+        }
+    }
+
+    Q64byte a0 = new Q64byte(42);
+    Q64byte a1 = new Q64byte(43);
+    Q64byte a2 = new Q64byte(44);
+    Q64byte a3 = new Q64byte(45);
+    Q64byte a4 = new Q64byte(46);
+    Q64byte a5 = new Q64byte(47);
+    Q64byte a6 = new Q64byte(48);
+    Q64byte a7 = new Q64byte(49);
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public long args1(InvocationLogic[] logic) {
+        long r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0).longValue();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public long args2(InvocationLogic[] logic) {
+        long r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0, a1).longValue();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public long args4(InvocationLogic[] logic) {
+        long r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0, a1, a2, a3).longValue();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public long args8(InvocationLogic[] logic) {
+        long r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0, a1, a2, a3, a4, a5, a6, a7).longValue();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64byte_args1_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        long r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0).longValue();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64byte_args2_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        long r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0, a1).longValue();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64byte_args4_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        long r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0, a1, a2, a3).longValue();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64byte_args8_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        long r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0, a1, a2, a3, a4, a5, a6, a7).longValue();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64byte_args1_targets1(StateTargets1 st) {
+        return args1(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64byte_args2_targets1(StateTargets1 st) {
+        return args2(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64byte_args4_targets1(StateTargets1 st) {
+        return args4(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64byte_args8_targets1(StateTargets1 st) {
+        return args8(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64byte_args1_targets2(StateTargets2 st) {
+        return args1(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64byte_args2_targets2(StateTargets2 st) {
+        return args2(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64byte_args4_targets2(StateTargets2 st) {
+        return args4(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64byte_args8_targets2(StateTargets2 st) {
+        return args8(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64byte_args1_targets3(StateTargets3 st) {
+        return args1(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64byte_args2_targets3(StateTargets3 st) {
+        return args2(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64byte_args4_targets3(StateTargets3 st) {
+        return args4(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64byte_args8_targets3(StateTargets3 st) {
+        return args8(st.arr);
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/callconv/Inline64int.java b/test/micro/org/openjdk/bench/valhalla/callconv/Inline64int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/callconv/Inline64int.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.callconv;
+
+import org.openjdk.bench.valhalla.types.Q64int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class Inline64int {
+
+    public static final int SIZE = 96;  // must be divisible by 2 and 3 and around 100
+
+    public abstract static class InvocationLogic {
+        public abstract Q64int compute(Q64int v1);
+        public abstract Q64int compute(Q64int v1, Q64int v2);
+        public abstract Q64int compute(Q64int v1, Q64int v2, Q64int v3, Q64int v4);
+        public abstract Q64int compute(Q64int v1, Q64int v2, Q64int v3, Q64int v4, Q64int v5, Q64int v6, Q64int v7, Q64int v8);
+    }
+
+    public static class InvokeImpl1 extends InvocationLogic {
+
+        @Override
+        public Q64int compute(Q64int v1) {
+            return v1;
+        }
+
+        @Override
+        public Q64int compute(Q64int v1, Q64int v2) {
+            return v1;
+        }
+
+        @Override
+        public Q64int compute(Q64int v1, Q64int v2, Q64int v3, Q64int v4) {
+            return v1;
+        }
+
+        @Override
+        public Q64int compute(Q64int v1, Q64int v2, Q64int v3, Q64int v4, Q64int v5, Q64int v6, Q64int v7, Q64int v8) {
+            return v1;
+        }
+
+    }
+
+    public static class InvokeImpl2 extends InvocationLogic {
+
+        @Override
+        public Q64int compute(Q64int v1) {
+            return v1;
+        }
+
+        @Override
+        public Q64int compute(Q64int v1, Q64int v2) {
+            return v1;
+        }
+
+        @Override
+        public Q64int compute(Q64int v1, Q64int v2, Q64int v3, Q64int v4) {
+            return v1;
+        }
+
+        @Override
+        public Q64int compute(Q64int v1, Q64int v2, Q64int v3, Q64int v4, Q64int v5, Q64int v6, Q64int v7, Q64int v8) {
+            return v1;
+        }
+
+    }
+
+    public static class InvokeImpl3 extends InvocationLogic {
+
+        @Override
+        public Q64int compute(Q64int v1) {
+            return v1;
+        }
+
+        @Override
+        public Q64int compute(Q64int v1, Q64int v2) {
+            return v1;
+        }
+
+        @Override
+        public Q64int compute(Q64int v1, Q64int v2, Q64int v3, Q64int v4) {
+            return v1;
+        }
+
+        @Override
+        public Q64int compute(Q64int v1, Q64int v2, Q64int v3, Q64int v4, Q64int v5, Q64int v6, Q64int v7, Q64int v8) {
+            return v1;
+        }
+
+    }
+
+
+    private static InvocationLogic getImpl(int i, int targets) {
+        switch (i % targets) {
+            case 0:
+                return new InvokeImpl1();
+            case 1:
+                return new InvokeImpl2();
+            case 2:
+                return new InvokeImpl3();
+        }
+        return null;
+    }
+
+    @State(Scope.Thread)
+    public static class StateTargets0 {
+        InvokeImpl1[] arr;
+        @Setup
+        public void setup() {
+            arr = new InvokeImpl1[SIZE];
+            for(int i=0; i < arr.length; i++) {
+                arr[i] = new InvokeImpl1();
+            }
+        }
+    }
+
+    @State(Scope.Thread)
+    public static abstract class StateTargets {
+        InvocationLogic[] arr;
+
+        public void init(int targets) {
+            arr = new InvocationLogic[SIZE];
+            for(int i=0; i < arr.length; i++) {
+                arr[i] = getImpl(i, targets);
+            }
+        }
+    }
+
+    public static class StateTargets1 extends StateTargets {
+        @Setup
+        public void setup() {
+            init(1);
+        }
+    }
+
+    public static class StateTargets2 extends StateTargets {
+        @Setup
+        public void setup() {
+            init(2);
+        }
+    }
+
+    public static class StateTargets3 extends StateTargets {
+        @Setup
+        public void setup() {
+            init(3);
+        }
+    }
+
+    Q64int a0 = new Q64int(42);
+    Q64int a1 = new Q64int(43);
+    Q64int a2 = new Q64int(44);
+    Q64int a3 = new Q64int(45);
+    Q64int a4 = new Q64int(46);
+    Q64int a5 = new Q64int(47);
+    Q64int a6 = new Q64int(48);
+    Q64int a7 = new Q64int(49);
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public long args1(InvocationLogic[] logic) {
+        long r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0).longValue();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public long args2(InvocationLogic[] logic) {
+        long r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0, a1).longValue();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public long args4(InvocationLogic[] logic) {
+        long r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0, a1, a2, a3).longValue();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public long args8(InvocationLogic[] logic) {
+        long r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0, a1, a2, a3, a4, a5, a6, a7).longValue();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64int_args1_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        long r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0).longValue();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64int_args2_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        long r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0, a1).longValue();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64int_args4_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        long r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0, a1, a2, a3).longValue();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64int_args8_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        long r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0, a1, a2, a3, a4, a5, a6, a7).longValue();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64int_args1_targets1(StateTargets1 st) {
+        return args1(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64int_args2_targets1(StateTargets1 st) {
+        return args2(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64int_args4_targets1(StateTargets1 st) {
+        return args4(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64int_args8_targets1(StateTargets1 st) {
+        return args8(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64int_args1_targets2(StateTargets2 st) {
+        return args1(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64int_args2_targets2(StateTargets2 st) {
+        return args2(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64int_args4_targets2(StateTargets2 st) {
+        return args4(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64int_args8_targets2(StateTargets2 st) {
+        return args8(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64int_args1_targets3(StateTargets3 st) {
+        return args1(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64int_args2_targets3(StateTargets3 st) {
+        return args2(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64int_args4_targets3(StateTargets3 st) {
+        return args4(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64int_args8_targets3(StateTargets3 st) {
+        return args8(st.arr);
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/callconv/Inline64long.java b/test/micro/org/openjdk/bench/valhalla/callconv/Inline64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/callconv/Inline64long.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.callconv;
+
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class Inline64long {
+
+    public static final int SIZE = 96;  // must be divisible by 2 and 3 and around 100
+
+    public abstract static class InvocationLogic {
+        public abstract Q64long compute(Q64long v1);
+        public abstract Q64long compute(Q64long v1, Q64long v2);
+        public abstract Q64long compute(Q64long v1, Q64long v2, Q64long v3, Q64long v4);
+        public abstract Q64long compute(Q64long v1, Q64long v2, Q64long v3, Q64long v4, Q64long v5, Q64long v6, Q64long v7, Q64long v8);
+    }
+
+    public static class InvokeImpl1 extends InvocationLogic {
+
+        @Override
+        public Q64long compute(Q64long v1) {
+            return v1;
+        }
+
+        @Override
+        public Q64long compute(Q64long v1, Q64long v2) {
+            return v1;
+        }
+
+        @Override
+        public Q64long compute(Q64long v1, Q64long v2, Q64long v3, Q64long v4) {
+            return v1;
+        }
+
+        @Override
+        public Q64long compute(Q64long v1, Q64long v2, Q64long v3, Q64long v4, Q64long v5, Q64long v6, Q64long v7, Q64long v8) {
+            return v1;
+        }
+
+    }
+
+    public static class InvokeImpl2 extends InvocationLogic {
+
+        @Override
+        public Q64long compute(Q64long v1) {
+            return v1;
+        }
+
+        @Override
+        public Q64long compute(Q64long v1, Q64long v2) {
+            return v1;
+        }
+
+        @Override
+        public Q64long compute(Q64long v1, Q64long v2, Q64long v3, Q64long v4) {
+            return v1;
+        }
+
+        @Override
+        public Q64long compute(Q64long v1, Q64long v2, Q64long v3, Q64long v4, Q64long v5, Q64long v6, Q64long v7, Q64long v8) {
+            return v1;
+        }
+
+    }
+
+    public static class InvokeImpl3 extends InvocationLogic {
+
+        @Override
+        public Q64long compute(Q64long v1) {
+            return v1;
+        }
+
+        @Override
+        public Q64long compute(Q64long v1, Q64long v2) {
+            return v1;
+        }
+
+        @Override
+        public Q64long compute(Q64long v1, Q64long v2, Q64long v3, Q64long v4) {
+            return v1;
+        }
+
+        @Override
+        public Q64long compute(Q64long v1, Q64long v2, Q64long v3, Q64long v4, Q64long v5, Q64long v6, Q64long v7, Q64long v8) {
+            return v1;
+        }
+
+    }
+
+
+    private static InvocationLogic getImpl(int i, int targets) {
+        switch (i % targets) {
+            case 0:
+                return new InvokeImpl1();
+            case 1:
+                return new InvokeImpl2();
+            case 2:
+                return new InvokeImpl3();
+        }
+        return null;
+    }
+
+    @State(Scope.Thread)
+    public static class StateTargets0 {
+        InvokeImpl1[] arr;
+        @Setup
+        public void setup() {
+            arr = new InvokeImpl1[SIZE];
+            for(int i=0; i < arr.length; i++) {
+                arr[i] = new InvokeImpl1();
+            }
+        }
+    }
+
+    @State(Scope.Thread)
+    public static abstract class StateTargets {
+        InvocationLogic[] arr;
+
+        public void init(int targets) {
+            arr = new InvocationLogic[SIZE];
+            for(int i=0; i < arr.length; i++) {
+                arr[i] = getImpl(i, targets);
+            }
+        }
+    }
+
+    public static class StateTargets1 extends StateTargets {
+        @Setup
+        public void setup() {
+            init(1);
+        }
+    }
+
+    public static class StateTargets2 extends StateTargets {
+        @Setup
+        public void setup() {
+            init(2);
+        }
+    }
+
+    public static class StateTargets3 extends StateTargets {
+        @Setup
+        public void setup() {
+            init(3);
+        }
+    }
+
+    Q64long a0 = new Q64long(42);
+    Q64long a1 = new Q64long(43);
+    Q64long a2 = new Q64long(44);
+    Q64long a3 = new Q64long(45);
+    Q64long a4 = new Q64long(46);
+    Q64long a5 = new Q64long(47);
+    Q64long a6 = new Q64long(48);
+    Q64long a7 = new Q64long(49);
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public long args1(InvocationLogic[] logic) {
+        long r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0).longValue();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public long args2(InvocationLogic[] logic) {
+        long r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0, a1).longValue();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public long args4(InvocationLogic[] logic) {
+        long r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0, a1, a2, a3).longValue();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public long args8(InvocationLogic[] logic) {
+        long r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0, a1, a2, a3, a4, a5, a6, a7).longValue();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64long_args1_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        long r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0).longValue();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64long_args2_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        long r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0, a1).longValue();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64long_args4_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        long r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0, a1, a2, a3).longValue();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64long_args8_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        long r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0, a1, a2, a3, a4, a5, a6, a7).longValue();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64long_args1_targets1(StateTargets1 st) {
+        return args1(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64long_args2_targets1(StateTargets1 st) {
+        return args2(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64long_args4_targets1(StateTargets1 st) {
+        return args4(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64long_args8_targets1(StateTargets1 st) {
+        return args8(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64long_args1_targets2(StateTargets2 st) {
+        return args1(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64long_args2_targets2(StateTargets2 st) {
+        return args2(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64long_args4_targets2(StateTargets2 st) {
+        return args4(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64long_args8_targets2(StateTargets2 st) {
+        return args8(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64long_args1_targets3(StateTargets3 st) {
+        return args1(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64long_args2_targets3(StateTargets3 st) {
+        return args2(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64long_args4_targets3(StateTargets3 st) {
+        return args4(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long Q64long_args8_targets3(StateTargets3 st) {
+        return args8(st.arr);
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/callconv/PrimitiveByte.java b/test/micro/org/openjdk/bench/valhalla/callconv/PrimitiveByte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/callconv/PrimitiveByte.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.callconv;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class PrimitiveByte {
+
+    public static final int SIZE = 96;  // must be divisible by 2 and 3 and around 100
+
+    public abstract static class InvocationLogic {
+        public abstract byte compute(byte v1);
+        public abstract byte compute(byte v1, byte v2);
+        public abstract byte compute(byte v1, byte v2, byte v3, byte v4);
+        public abstract byte compute(byte v1, byte v2, byte v3, byte v4, byte v5, byte v6, byte v7, byte v8);
+    }
+
+    public static class InvokeImpl1 extends InvocationLogic {
+
+        @Override
+        public byte compute(byte v1) {
+            return v1;
+        }
+
+        @Override
+        public byte compute(byte v1, byte v2) {
+            return v1;
+        }
+
+        @Override
+        public byte compute(byte v1, byte v2, byte v3, byte v4) {
+            return v1;
+        }
+
+        @Override
+        public byte compute(byte v1, byte v2, byte v3, byte v4, byte v5, byte v6, byte v7, byte v8) {
+            return v1;
+        }
+
+    }
+
+    public static class InvokeImpl2 extends InvocationLogic {
+
+        @Override
+        public byte compute(byte v1) {
+            return v1;
+        }
+
+        @Override
+        public byte compute(byte v1, byte v2) {
+            return v1;
+        }
+
+        @Override
+        public byte compute(byte v1, byte v2, byte v3, byte v4) {
+            return v1;
+        }
+
+        @Override
+        public byte compute(byte v1, byte v2, byte v3, byte v4, byte v5, byte v6, byte v7, byte v8) {
+            return v1;
+        }
+
+    }
+
+    public static class InvokeImpl3 extends InvocationLogic {
+
+        @Override
+        public byte compute(byte v1) {
+            return v1;
+        }
+
+        @Override
+        public byte compute(byte v1, byte v2) {
+            return v1;
+        }
+
+        @Override
+        public byte compute(byte v1, byte v2, byte v3, byte v4) {
+            return v1;
+        }
+
+        @Override
+        public byte compute(byte v1, byte v2, byte v3, byte v4, byte v5, byte v6, byte v7, byte v8) {
+            return v1;
+        }
+
+    }
+
+
+    private static InvocationLogic getImpl(int i, int targets) {
+        switch (i % targets) {
+            case 0:
+                return new InvokeImpl1();
+            case 1:
+                return new InvokeImpl2();
+            case 2:
+                return new InvokeImpl3();
+        }
+        return null;
+    }
+
+    @State(Scope.Thread)
+    public static class StateTargets0 {
+        InvokeImpl1[] arr;
+        @Setup
+        public void setup() {
+            arr = new InvokeImpl1[SIZE];
+            for(int i=0; i < arr.length; i++) {
+                arr[i] = new InvokeImpl1();
+            }
+        }
+    }
+
+    @State(Scope.Thread)
+    public static abstract class StateTargets {
+        InvocationLogic[] arr;
+
+        public void init(int targets) {
+            arr = new InvocationLogic[SIZE];
+            for(int i=0; i < arr.length; i++) {
+                arr[i] = getImpl(i, targets);
+            }
+        }
+    }
+
+    public static class StateTargets1 extends StateTargets {
+        @Setup
+        public void setup() {
+            init(1);
+        }
+    }
+
+    public static class StateTargets2 extends StateTargets {
+        @Setup
+        public void setup() {
+            init(2);
+        }
+    }
+
+    public static class StateTargets3 extends StateTargets {
+        @Setup
+        public void setup() {
+            init(3);
+        }
+    }
+
+    byte a0 = 42;
+    byte a1 = 43;
+    byte a2 = 44;
+    byte a3 = 45;
+    byte a4 = 46;
+    byte a5 = 47;
+    byte a6 = 48;
+    byte a7 = 49;
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public byte args1(InvocationLogic[] logic) {
+        byte r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0);
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public byte args2(InvocationLogic[] logic) {
+        byte r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0, a1);
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public byte args4(InvocationLogic[] logic) {
+        byte r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0, a1, a2, a3);
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public byte args8(InvocationLogic[] logic) {
+        byte r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0, a1, a2, a3, a4, a5, a6, a7);
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte byte_args1_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        byte r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0);
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte byte_args2_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        byte r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0, a1);
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte byte_args4_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        byte r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0, a1, a2, a3);
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte byte_args8_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        byte r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0, a1, a2, a3, a4, a5, a6, a7);
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte byte_args1_targets1(StateTargets1 st) {
+        return args1(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte byte_args2_targets1(StateTargets1 st) {
+        return args2(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte byte_args4_targets1(StateTargets1 st) {
+        return args4(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte byte_args8_targets1(StateTargets1 st) {
+        return args8(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte byte_args1_targets2(StateTargets2 st) {
+        return args1(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte byte_args2_targets2(StateTargets2 st) {
+        return args2(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte byte_args4_targets2(StateTargets2 st) {
+        return args4(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte byte_args8_targets2(StateTargets2 st) {
+        return args8(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte byte_args1_targets3(StateTargets3 st) {
+        return args1(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte byte_args2_targets3(StateTargets3 st) {
+        return args2(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte byte_args4_targets3(StateTargets3 st) {
+        return args4(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public byte byte_args8_targets3(StateTargets3 st) {
+        return args8(st.arr);
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/callconv/PrimitiveInt.java b/test/micro/org/openjdk/bench/valhalla/callconv/PrimitiveInt.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/callconv/PrimitiveInt.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.callconv;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class PrimitiveInt {
+
+    public static final int SIZE = 96;  // must be divisible by 2 and 3 and around 100
+
+    public abstract static class InvocationLogic {
+        public abstract int compute(int v1);
+        public abstract int compute(int v1, int v2);
+        public abstract int compute(int v1, int v2, int v3, int v4);
+        public abstract int compute(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8);
+    }
+
+    public static class InvokeImpl1 extends InvocationLogic {
+
+        @Override
+        public int compute(int v1) {
+            return v1;
+        }
+
+        @Override
+        public int compute(int v1, int v2) {
+            return v1;
+        }
+
+        @Override
+        public int compute(int v1, int v2, int v3, int v4) {
+            return v1;
+        }
+
+        @Override
+        public int compute(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8) {
+            return v1;
+        }
+
+    }
+
+    public static class InvokeImpl2 extends InvocationLogic {
+
+        @Override
+        public int compute(int v1) {
+            return v1;
+        }
+
+        @Override
+        public int compute(int v1, int v2) {
+            return v1;
+        }
+
+        @Override
+        public int compute(int v1, int v2, int v3, int v4) {
+            return v1;
+        }
+
+        @Override
+        public int compute(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8) {
+            return v1;
+        }
+    }
+
+    public static class InvokeImpl3 extends InvocationLogic {
+
+        @Override
+        public int compute(int v1) {
+            return v1;
+        }
+
+        @Override
+        public int compute(int v1, int v2) {
+            return v1;
+        }
+
+        @Override
+        public int compute(int v1, int v2, int v3, int v4) {
+            return v1;
+        }
+
+        @Override
+        public int compute(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8) {
+            return v1;
+        }
+    }
+
+    private static InvocationLogic getImpl(int i, int targets) {
+        switch (i % targets) {
+            case 0:
+                return new InvokeImpl1();
+            case 1:
+                return new InvokeImpl2();
+            case 2:
+                return new InvokeImpl3();
+        }
+        return null;
+    }
+
+    @State(Scope.Thread)
+    public static class StateTargets0 {
+        InvokeImpl1[] arr;
+        @Setup
+        public void setup() {
+            arr = new InvokeImpl1[SIZE];
+            for(int i=0; i < arr.length; i++) {
+                arr[i] = new InvokeImpl1();
+            }
+        }
+    }
+
+    @State(Scope.Thread)
+    public static abstract class StateTargets {
+        InvocationLogic[] arr;
+
+        public void init(int targets) {
+            arr = new InvocationLogic[SIZE];
+            for(int i=0; i < arr.length; i++) {
+                arr[i] = getImpl(i, targets);
+            }
+        }
+    }
+
+    public static class StateTargets1 extends StateTargets {
+        @Setup
+        public void setup() {
+            init(1);
+        }
+    }
+
+    public static class StateTargets2 extends StateTargets {
+        @Setup
+        public void setup() {
+            init(2);
+        }
+    }
+
+    public static class StateTargets3 extends StateTargets {
+        @Setup
+        public void setup() {
+            init(3);
+        }
+    }
+
+    int a0 = 42;
+    int a1 = 43;
+    int a2 = 44;
+    int a3 = 45;
+    int a4 = 46;
+    int a5 = 47;
+    int a6 = 48;
+    int a7 = 49;
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int args1(InvocationLogic[] logic) {
+        int r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0);
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int args2(InvocationLogic[] logic) {
+        int r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0, a1);
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int args4(InvocationLogic[] logic) {
+        int r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0, a1, a2, a3);
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int args8(InvocationLogic[] logic) {
+        int r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0, a1, a2, a3, a4, a5, a6, a7);
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int int_args1_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        int r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0);
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int int_args2_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        int r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0, a1);
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int int_args4_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        int r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0, a1, a2, a3);
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int int_args8_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        int r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0, a1, a2, a3, a4, a5, a6, a7);
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int int_args1_targets1(StateTargets1 st) {
+        return args1(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int int_args2_targets1(StateTargets1 st) {
+        return args2(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int int_args4_targets1(StateTargets1 st) {
+        return args4(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int int_args8_targets1(StateTargets1 st) {
+        return args8(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int int_args1_targets2(StateTargets2 st) {
+        return args1(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int int_args2_targets2(StateTargets2 st) {
+        return args2(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int int_args4_targets2(StateTargets2 st) {
+        return args4(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int int_args8_targets2(StateTargets2 st) {
+        return args8(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int int_args1_targets3(StateTargets3 st) {
+        return args1(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int int_args2_targets3(StateTargets3 st) {
+        return args2(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int int_args4_targets3(StateTargets3 st) {
+        return args4(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int int_args8_targets3(StateTargets3 st) {
+        return args8(st.arr);
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/callconv/PrimitiveLong.java b/test/micro/org/openjdk/bench/valhalla/callconv/PrimitiveLong.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/callconv/PrimitiveLong.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.callconv;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class PrimitiveLong {
+
+    public static final int SIZE = 96;  // must be divisible by 2 and 3 and around 100
+
+    public abstract static class InvocationLogic {
+        public abstract long compute(long v1);
+        public abstract long compute(long v1, long v2);
+        public abstract long compute(long v1, long v2, long v3, long v4);
+        public abstract long compute(long v1, long v2, long v3, long v4, long v5, long v6, long v7, long v8);
+    }
+
+    public static class InvokeImpl1 extends InvocationLogic {
+
+        @Override
+        public long compute(long v1) {
+            return v1;
+        }
+
+        @Override
+        public long compute(long v1, long v2) {
+            return v1;
+        }
+
+        @Override
+        public long compute(long v1, long v2, long v3, long v4) {
+            return v1;
+        }
+
+        @Override
+        public long compute(long v1, long v2, long v3, long v4, long v5, long v6, long v7, long v8) {
+            return v1;
+        }
+
+    }
+
+    public static class InvokeImpl2 extends InvocationLogic {
+
+        @Override
+        public long compute(long v1) {
+            return v1;
+        }
+
+        @Override
+        public long compute(long v1, long v2) {
+            return v1;
+        }
+
+        @Override
+        public long compute(long v1, long v2, long v3, long v4) {
+            return v1;
+        }
+
+        @Override
+        public long compute(long v1, long v2, long v3, long v4, long v5, long v6, long v7, long v8) {
+            return v1;
+        }
+
+    }
+
+    public static class InvokeImpl3 extends InvocationLogic {
+
+        @Override
+        public long compute(long v1) {
+            return v1;
+        }
+
+        @Override
+        public long compute(long v1, long v2) {
+            return v1;
+        }
+
+        @Override
+        public long compute(long v1, long v2, long v3, long v4) {
+            return v1;
+        }
+
+        @Override
+        public long compute(long v1, long v2, long v3, long v4, long v5, long v6, long v7, long v8) {
+            return v1;
+        }
+
+    }
+
+
+    private static InvocationLogic getImpl(int i, int targets) {
+        switch (i % targets) {
+            case 0:
+                return new InvokeImpl1();
+            case 1:
+                return new InvokeImpl2();
+            case 2:
+                return new InvokeImpl3();
+        }
+        return null;
+    }
+
+    @State(Scope.Thread)
+    public static class StateTargets0 {
+        InvokeImpl1[] arr;
+        @Setup
+        public void setup() {
+            arr = new InvokeImpl1[SIZE];
+            for(int i=0; i < arr.length; i++) {
+                arr[i] = new InvokeImpl1();
+            }
+        }
+    }
+
+    @State(Scope.Thread)
+    public static abstract class StateTargets {
+        InvocationLogic[] arr;
+
+        public void init(int targets) {
+            arr = new InvocationLogic[SIZE];
+            for(int i=0; i < arr.length; i++) {
+                arr[i] = getImpl(i, targets);
+            }
+        }
+    }
+
+    public static class StateTargets1 extends StateTargets {
+        @Setup
+        public void setup() {
+            init(1);
+        }
+    }
+
+    public static class StateTargets2 extends StateTargets {
+        @Setup
+        public void setup() {
+            init(2);
+        }
+    }
+
+    public static class StateTargets3 extends StateTargets {
+        @Setup
+        public void setup() {
+            init(3);
+        }
+    }
+
+    long a0 = 42;
+    long a1 = 43;
+    long a2 = 44;
+    long a3 = 45;
+    long a4 = 46;
+    long a5 = 47;
+    long a6 = 48;
+    long a7 = 49;
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public long args1(InvocationLogic[] logic) {
+        long r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0);
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public long args2(InvocationLogic[] logic) {
+        long r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0, a1);
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public long args4(InvocationLogic[] logic) {
+        long r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0, a1, a2, a3);
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public long args8(InvocationLogic[] logic) {
+        long r = 0;
+        for(InvocationLogic t : logic) {
+            r += t.compute(a0, a1, a2, a3, a4, a5, a6, a7);
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long long_args1_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        long r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0);
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long long_args2_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        long r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0, a1);
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long long_args4_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        long r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0, a1, a2, a3);
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long long_args8_targets0(StateTargets0 st) {
+        InvokeImpl1[] arr = st.arr;
+        long r = 0;
+        for(InvocationLogic t : arr) {
+            r += t.compute(a0, a1, a2, a3, a4, a5, a6, a7);
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long long_args1_targets1(StateTargets1 st) {
+        return args1(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long long_args2_targets1(StateTargets1 st) {
+        return args2(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long long_args4_targets1(StateTargets1 st) {
+        return args4(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long long_args8_targets1(StateTargets1 st) {
+        return args8(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long long_args1_targets2(StateTargets2 st) {
+        return args1(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long long_args2_targets2(StateTargets2 st) {
+        return args2(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long long_args4_targets2(StateTargets2 st) {
+        return args4(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long long_args8_targets2(StateTargets2 st) {
+        return args8(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long long_args1_targets3(StateTargets3 st) {
+        return args1(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long long_args2_targets3(StateTargets3 st) {
+        return args2(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long long_args4_targets3(StateTargets3 st) {
+        return args4(st.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE)
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public long long_args8_targets3(StateTargets3 st) {
+        return args8(st.arr);
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/copy/Inline128int.java b/test/micro/org/openjdk/bench/valhalla/field/copy/Inline128int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/copy/Inline128int.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.copy;
+
+import org.openjdk.bench.valhalla.field.util.StatesQ128int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline128int extends StatesQ128int {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_copy(RefState s, ValState d) {
+        RefWrapper[] src = s.arr;
+        ValWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_copy(ValState s, RefState d) {
+        ValWrapper[] src = s.arr;
+        RefWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_copy(ValState s, ValState d) {
+        ValWrapper[] src = s.arr;
+        ValWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/copy/Inline32int.java b/test/micro/org/openjdk/bench/valhalla/field/copy/Inline32int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/copy/Inline32int.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.copy;
+
+import org.openjdk.bench.valhalla.field.util.StatesQ32int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline32int extends StatesQ32int {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_copy(RefState s, ValState d) {
+        RefWrapper[] src = s.arr;
+        ValWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_copy(ValState s, RefState d) {
+        ValWrapper[] src = s.arr;
+        RefWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_copy(ValState s, ValState d) {
+        ValWrapper[] src = s.arr;
+        ValWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/copy/Inline64byte.java b/test/micro/org/openjdk/bench/valhalla/field/copy/Inline64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/copy/Inline64byte.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.copy;
+
+import org.openjdk.bench.valhalla.field.util.StatesQ64byte;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64byte extends StatesQ64byte {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_copy(RefState s, ValState d) {
+        RefWrapper[] src = s.arr;
+        ValWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_copy(ValState s, RefState d) {
+        ValWrapper[] src = s.arr;
+        RefWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_copy(ValState s, ValState d) {
+        ValWrapper[] src = s.arr;
+        ValWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/copy/Inline64int.java b/test/micro/org/openjdk/bench/valhalla/field/copy/Inline64int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/copy/Inline64int.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.copy;
+
+import org.openjdk.bench.valhalla.field.util.StatesQ64int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64int extends StatesQ64int {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_copy(RefState s, ValState d) {
+        RefWrapper[] src = s.arr;
+        ValWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_copy(ValState s, RefState d) {
+        ValWrapper[] src = s.arr;
+        RefWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_copy(ValState s, ValState d) {
+        ValWrapper[] src = s.arr;
+        ValWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/copy/Inline64long.java b/test/micro/org/openjdk/bench/valhalla/field/copy/Inline64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/copy/Inline64long.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.copy;
+
+import org.openjdk.bench.valhalla.field.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64long extends StatesQ64long {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Obj_copy(ObjState s, ObjState d) {
+        ObjWrapper[] src = s.arr;
+        ObjWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Int_copy(ObjState s, IntState d) {
+        ObjWrapper[] src = s.arr;
+        IntWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = (Int64) src[i].f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_copy(ObjState s, RefState d) {
+        ObjWrapper[] src = s.arr;
+        RefWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = (Q64long)src[i].f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_copy(ObjState s, ValState d) {
+        ObjWrapper[] src = s.arr;
+        ValWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = (Q64long) src[i].f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Obj_copy(IntState s, ObjState d) {
+        IntWrapper[] src = s.arr;
+        ObjWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Int_copy(IntState s, IntState d) {
+        IntWrapper[] src = s.arr;
+        IntWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_copy(IntState s, RefState d) {
+        IntWrapper[] src = s.arr;
+        RefWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = (Q64long)src[i].f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_copy(IntState s, ValState d) {
+        IntWrapper[] src = s.arr;
+        ValWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = (Q64long) src[i].f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_copy(RefState s, ObjState d) {
+        RefWrapper[] src = s.arr;
+        ObjWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_copy(RefState s, IntState d) {
+        RefWrapper[] src = s.arr;
+        IntWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_copy(RefState s, RefState d) {
+        RefWrapper[] src = s.arr;
+        RefWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_copy(RefState s, ValState d) {
+        RefWrapper[] src = s.arr;
+        ValWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_copy(ValState s, ObjState d) {
+        ValWrapper[] src = s.arr;
+        ObjWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_copy(ValState s, IntState d) {
+        ValWrapper[] src = s.arr;
+        IntWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_copy(ValState s, RefState d) {
+        ValWrapper[] src = s.arr;
+        RefWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_copy(ValState s, ValState d) {
+        ValWrapper[] src = s.arr;
+        ValWrapper[] dst = d.arr;
+        for (int i = 0; i < src.length; i++) {
+            dst[i].f = src[i].f;
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/copy/Primitive128.java b/test/micro/org/openjdk/bench/valhalla/field/copy/Primitive128.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/copy/Primitive128.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.copy;
+
+import org.openjdk.bench.valhalla.field.util.PrimitiveStates;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Primitive128 extends PrimitiveStates {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void p128int_copy(Primitive128int s, Primitive128int d) {
+        P128int[] src = s.arr;
+        P128int[] dst = d.arr;
+        for(int i=0; i<src.length; i++) {
+            dst[i].f0 = src[i].f0;
+            dst[i].f1 = src[i].f1;
+            dst[i].f2 = src[i].f2;
+            dst[i].f3 = src[i].f3;
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/copy/Primitive32.java b/test/micro/org/openjdk/bench/valhalla/field/copy/Primitive32.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/copy/Primitive32.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.copy;
+
+import org.openjdk.bench.valhalla.field.util.PrimitiveStates;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Primitive32 extends PrimitiveStates {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void p32int_copy(Primitive32int s, Primitive32int d) {
+        P32int[] src = s.arr;
+        P32int[] dst = d.arr;
+        for(int i=0; i<src.length; i++) {
+            dst[i].f0 = src[i].f0;
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/copy/Primitive64.java b/test/micro/org/openjdk/bench/valhalla/field/copy/Primitive64.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/copy/Primitive64.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.copy;
+
+import org.openjdk.bench.valhalla.field.util.PrimitiveStates;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Primitive64 extends PrimitiveStates {
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void p64byte_copy(Primitive64byte s, Primitive64byte d) {
+        P64byte[] src = s.arr;
+        P64byte[] dst = d.arr;
+        for(int i=0; i<src.length; i++) {
+            dst[i].f0 = src[i].f0;
+            dst[i].f1 = src[i].f1;
+            dst[i].f2 = src[i].f2;
+            dst[i].f3 = src[i].f3;
+            dst[i].f4 = src[i].f4;
+            dst[i].f5 = src[i].f5;
+            dst[i].f6 = src[i].f6;
+            dst[i].f7 = src[i].f7;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void p64int_copy(Primitive64int s, Primitive64int d) {
+        P64int[] src = s.arr;
+        P64int[] dst = d.arr;
+        for(int i=0; i<src.length; i++) {
+            dst[i].f0 = src[i].f0;
+            dst[i].f1 = src[i].f1;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void p64long_copy(Primitive64long s, Primitive64long d) {
+        P64long[] src = s.arr;
+        P64long[] dst = d.arr;
+        for(int i=0; i<src.length; i++) {
+            dst[i].f0 = src[i].f0;
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/read/Inline128int.java b/test/micro/org/openjdk/bench/valhalla/field/read/Inline128int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/read/Inline128int.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.read;
+
+import org.openjdk.bench.valhalla.field.util.StatesQ128int;
+import org.openjdk.bench.valhalla.types.Q128int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline128int extends StatesQ128int {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void val_consume(Q128int v) {
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void ref_consume(Q128int v) {
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_read(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i].f);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_read(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            ref_consume(arr[i].f);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_read(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i].f);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/read/Inline32int.java b/test/micro/org/openjdk/bench/valhalla/field/read/Inline32int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/read/Inline32int.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.read;
+
+import org.openjdk.bench.valhalla.field.util.StatesQ32int;
+import org.openjdk.bench.valhalla.types.Q32int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline32int extends StatesQ32int {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void val_consume(Q32int v) {
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void ref_consume(Q32int v) {
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_read(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i].f);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_read(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            ref_consume(arr[i].f);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_read(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i].f);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/read/Inline64byte.java b/test/micro/org/openjdk/bench/valhalla/field/read/Inline64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/read/Inline64byte.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.read;
+
+import org.openjdk.bench.valhalla.field.util.StatesQ64byte;
+import org.openjdk.bench.valhalla.types.Q64byte;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64byte extends StatesQ64byte {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void val_consume(Q64byte v) {
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void ref_consume(Q64byte v) {
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_read(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i].f);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_read(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            ref_consume(arr[i].f);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_read(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i].f);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/read/Inline64int.java b/test/micro/org/openjdk/bench/valhalla/field/read/Inline64int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/read/Inline64int.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.read;
+
+import org.openjdk.bench.valhalla.field.util.StatesQ64int;
+import org.openjdk.bench.valhalla.types.Q64int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64int extends StatesQ64int {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void val_consume(Q64int v) {
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void ref_consume(Q64int v) {
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_read(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i].f);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_read(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            ref_consume(arr[i].f);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_read(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i].f);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/read/Inline64long.java b/test/micro/org/openjdk/bench/valhalla/field/read/Inline64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/read/Inline64long.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.read;
+
+import org.openjdk.bench.valhalla.field.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64long extends StatesQ64long {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void val_consume(Q64long v) {
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void ref_consume(Q64long v) {
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void int_consume(Int64 v) {
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void obj_consume(Object v) {
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_read(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i].f);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_read(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            ref_consume(arr[i].f);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Int_read(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            int_consume(arr[i].f);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Obj_read(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            obj_consume(arr[i].f);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_read(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            val_consume(arr[i].f);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_read(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            ref_consume(arr[i].f);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Int_read(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            int_consume(arr[i].f);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Obj_read(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for(int i=0; i < arr.length; i++) {
+            obj_consume(arr[i].f);
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/set/Inline128int.java b/test/micro/org/openjdk/bench/valhalla/field/set/Inline128int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/set/Inline128int.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.set;
+
+import org.openjdk.bench.valhalla.field.util.StatesQ128int;
+import org.openjdk.bench.valhalla.types.Q128int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline128int extends StatesQ128int {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q128int getRef(int i) {
+        return new Q128int(i);
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q128int getVal(int i) {
+        return new Q128int(i);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q128int(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getRef(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getVal(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q128int()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q128int(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getRef(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getVal(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q128int()  ;
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/set/Inline32int.java b/test/micro/org/openjdk/bench/valhalla/field/set/Inline32int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/set/Inline32int.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.set;
+
+import org.openjdk.bench.valhalla.field.util.StatesQ32int;
+import org.openjdk.bench.valhalla.types.Q32int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline32int extends StatesQ32int {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q32int getRef(int i) {
+        return new Q32int(i);
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q32int getVal(int i) {
+        return new Q32int(i);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q32int(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getRef(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getVal(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q32int()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q32int(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getRef(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getVal(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q32int()  ;
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/set/Inline64byte.java b/test/micro/org/openjdk/bench/valhalla/field/set/Inline64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/set/Inline64byte.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.set;
+
+import org.openjdk.bench.valhalla.field.util.StatesQ64byte;
+import org.openjdk.bench.valhalla.types.Q64byte;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64byte extends StatesQ64byte {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q64byte getRef(int i) {
+        return new Q64byte(i);
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q64byte getVal(int i) {
+        return new Q64byte(i);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q64byte(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getRef(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getVal(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q64byte()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q64byte(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getRef(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getVal(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q64byte()  ;
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/set/Inline64int.java b/test/micro/org/openjdk/bench/valhalla/field/set/Inline64int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/set/Inline64int.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.set;
+
+import org.openjdk.bench.valhalla.field.util.StatesQ64int;
+import org.openjdk.bench.valhalla.types.Q64int;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64int extends StatesQ64int {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q64int getRef(int i) {
+        return new Q64int(i);
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q64int getVal(int i) {
+        return new Q64int(i);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q64int(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getRef(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getVal(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q64int()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q64int(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getRef(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getVal(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q64int()  ;
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/set/Inline64long.java b/test/micro/org/openjdk/bench/valhalla/field/set/Inline64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/set/Inline64long.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.set;
+
+import org.openjdk.bench.valhalla.field.util.StatesQ64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+
+public class Inline64long extends StatesQ64long {
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Object getObj(int i) {
+        return new Q64long(i);
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Int64 getInt(int i) {
+        return new Q64long(i);
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q64long getRef(int i) {
+        return new Q64long(i);
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public static Q64long getVal(int i) {
+        return new Q64long(i);
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = (Q64long) getObj(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = (Q64long) getInt(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getRef(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getVal(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Val_set(ValState st) {
+        ValWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q64long()  ;
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void New_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q64long(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Obj_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = (Q64long) getObj(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Int_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = (Q64long) getInt(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Ref_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getRef(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Val_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = getVal(i);
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public void Def_to_Ref_set(RefState st) {
+        RefWrapper[] arr = st.arr;
+        for (int i = 0; i < arr.length; i++) {
+            arr[i].f = new Q64long()  ;
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/util/PrimitiveStates.java b/test/micro/org/openjdk/bench/valhalla/field/util/PrimitiveStates.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/util/PrimitiveStates.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.util;
+
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class PrimitiveStates extends SizeBase {
+
+    public static class P64byte {
+
+        public byte f0;
+        public byte f1;
+        public byte f2;
+        public byte f3;
+        public byte f4;
+        public byte f5;
+        public byte f6;
+        public byte f7;
+
+        public P64byte(long v) {
+            this((byte) (v >>> 56), (byte) (v >>> 48), (byte) (v >>> 40), (byte) (v >>> 32), (byte) (v >>> 24), (byte) (v >>> 16), (byte) (v >>> 8), (byte) (v));
+        }
+
+        public P64byte(byte v0, byte v1, byte v2, byte v3, byte v4, byte v5, byte v6, byte v7) {
+            this.f0 = v0;
+            this.f1 = v1;
+            this.f2 = v2;
+            this.f3 = v3;
+            this.f4 = v4;
+            this.f5 = v5;
+            this.f6 = v6;
+            this.f7 = v7;
+        }
+
+    }
+
+    public static class Primitive64byte extends SizeState {
+        public P64byte[] arr;
+        @Setup
+        public void setup() {
+            arr = new P64byte[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new P64byte(i);
+            }
+        }
+    }
+
+
+    public static class P64int {
+
+        public int f0;
+        public int f1;
+
+        public P64int(long v) {
+            this((int) (v >>> 32), (int) v);
+        }
+
+        public P64int(int hi, int lo) {
+            this.f0 = hi;
+            this.f1 = lo;
+        }
+
+    }
+
+    public static class Primitive64int extends SizeState {
+        public P64int[] arr;
+        @Setup
+        public void setup() {
+            arr = new P64int[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new P64int(i);
+            }
+        }
+    }
+
+    public static class P64long {
+
+        public long f0;
+
+        public P64long(long v0) {
+            this.f0 = v0;
+        }
+
+    }
+
+    public static class Primitive64long extends SizeState {
+        public P64long[] arr;
+        @Setup
+        public void setup() {
+            arr = new P64long[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new P64long(i);
+            }
+        }
+    }
+
+
+    public static class P32int {
+
+        public int f0;
+
+        public P32int(int val) {
+            this.f0 = val;
+        }
+    }
+
+    public static class Primitive32int extends SizeState {
+        public P32int[] arr;
+        @Setup
+        public void setup() {
+            arr = new P32int[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new P32int(i);
+            }
+        }
+    }
+
+    public static class P128int {
+
+        public int f0;
+        public int f1;
+        public int f2;
+        public int f3;
+
+        public P128int(long v) {
+            this(0, 0, (int) (v >>> 32), (int) v);
+        }
+
+        public P128int(int v0, int v1, int v2, int v3) {
+            this.f0 = v0;
+            this.f1 = v1;
+            this.f2 = v2;
+            this.f3 = v3;
+        }
+    }
+
+    public static class Primitive128int extends SizeState {
+        public P128int[] arr;
+        @Setup
+        public void setup() {
+            arr = new P128int[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new P128int(i);
+            }
+        }
+    }
+
+}
+
diff a/test/micro/org/openjdk/bench/valhalla/field/util/StatesQ128int.java b/test/micro/org/openjdk/bench/valhalla/field/util/StatesQ128int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/util/StatesQ128int.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.util;
+
+import org.openjdk.bench.valhalla.types.Int128;
+import org.openjdk.bench.valhalla.types.Q128int;
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class StatesQ128int extends SizeBase {
+
+    public static class ObjWrapper {
+        public Object f;
+
+        public ObjWrapper(Object f) {
+            this.f = f;
+        }
+    }
+
+    public static class ObjState extends SizeState {
+        public ObjWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new ObjWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new ObjWrapper(new Q128int(i));
+            }
+        }
+    }
+
+    public static class IntWrapper {
+        public Int128 f;
+
+        public IntWrapper(Int128 f) {
+            this.f = f;
+        }
+    }
+
+    public static class IntState extends SizeState {
+        public IntWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new IntWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new IntWrapper(new Q128int(i));
+            }
+        }
+    }
+
+
+    public static class RefWrapper {
+        public Q128int f;
+
+        public RefWrapper(Q128int f) {
+            this.f = f;
+        }
+    }
+
+    public static class RefState extends SizeState {
+        public RefWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new RefWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new RefWrapper(new Q128int(i));
+            }
+        }
+    }
+
+
+    public static class ValWrapper {
+        public Q128int f;
+
+        public ValWrapper(Q128int f) {
+            this.f = f;
+        }
+    }
+
+    public static class ValState extends SizeState {
+        public ValWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new ValWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new ValWrapper(new Q128int(i));
+            }
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/util/StatesQ32int.java b/test/micro/org/openjdk/bench/valhalla/field/util/StatesQ32int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/util/StatesQ32int.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.util;
+
+import org.openjdk.bench.valhalla.types.Int32;
+import org.openjdk.bench.valhalla.types.Q32int;
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class StatesQ32int extends SizeBase {
+
+    public static class ObjWrapper {
+        public Object f;
+
+        public ObjWrapper(Object f) {
+            this.f = f;
+        }
+    }
+
+    public static class ObjState extends SizeState {
+        public ObjWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new ObjWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new ObjWrapper(new Q32int(i));
+            }
+        }
+    }
+
+    public static class IntWrapper {
+        public Int32 f;
+
+        public IntWrapper(Int32 f) {
+            this.f = f;
+        }
+    }
+
+    public static class IntState extends SizeState {
+        public IntWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new IntWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new IntWrapper(new Q32int(i));
+            }
+        }
+    }
+
+
+    public static class RefWrapper {
+        public Q32int f;
+
+        public RefWrapper(Q32int f) {
+            this.f = f;
+        }
+    }
+
+    public static class RefState extends SizeState {
+        public RefWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new RefWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new RefWrapper(new Q32int(i));
+            }
+        }
+    }
+
+
+    public static class ValWrapper {
+        public Q32int f;
+
+        public ValWrapper(Q32int f) {
+            this.f = f;
+        }
+    }
+
+    public static class ValState extends SizeState {
+        public ValWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new ValWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new ValWrapper(new Q32int(i));
+            }
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/util/StatesQ64byte.java b/test/micro/org/openjdk/bench/valhalla/field/util/StatesQ64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/util/StatesQ64byte.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.util;
+
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64byte;
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class StatesQ64byte extends SizeBase {
+
+    public static class ObjWrapper {
+        public Object f;
+
+        public ObjWrapper(Object f) {
+            this.f = f;
+        }
+    }
+
+    public static class ObjState extends SizeState {
+        public ObjWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new ObjWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new ObjWrapper(new Q64byte(i));
+            }
+        }
+    }
+
+    public static class IntWrapper {
+        public Int64 f;
+
+        public IntWrapper(Int64 f) {
+            this.f = f;
+        }
+    }
+
+    public static class IntState extends SizeState {
+        public IntWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new IntWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new IntWrapper(new Q64byte(i));
+            }
+        }
+    }
+
+
+    public static class RefWrapper {
+        public Q64byte f;
+
+        public RefWrapper(Q64byte f) {
+            this.f = f;
+        }
+    }
+
+    public static class RefState extends SizeState {
+        public RefWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new RefWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new RefWrapper(new Q64byte(i));
+            }
+        }
+    }
+
+
+    public static class ValWrapper {
+        public Q64byte f;
+
+        public ValWrapper(Q64byte f) {
+            this.f = f;
+        }
+    }
+
+    public static class ValState extends SizeState {
+        public ValWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new ValWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new ValWrapper(new Q64byte(i));
+            }
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/util/StatesQ64int.java b/test/micro/org/openjdk/bench/valhalla/field/util/StatesQ64int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/util/StatesQ64int.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.util;
+
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64int;
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class StatesQ64int extends SizeBase {
+
+    public static class ObjWrapper {
+        public Object f;
+
+        public ObjWrapper(Object f) {
+            this.f = f;
+        }
+    }
+
+    public static class ObjState extends SizeState {
+        public ObjWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new ObjWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new ObjWrapper(new Q64int(i));
+            }
+        }
+    }
+
+    public static class IntWrapper {
+        public Int64 f;
+
+        public IntWrapper(Int64 f) {
+            this.f = f;
+        }
+    }
+
+    public static class IntState extends SizeState {
+        public IntWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new IntWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new IntWrapper(new Q64int(i));
+            }
+        }
+    }
+
+
+    public static class RefWrapper {
+        public Q64int f;
+
+        public RefWrapper(Q64int f) {
+            this.f = f;
+        }
+    }
+
+    public static class RefState extends SizeState {
+        public RefWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new RefWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new RefWrapper(new Q64int(i));
+            }
+        }
+    }
+
+
+    public static class ValWrapper {
+        public Q64int f;
+
+        public ValWrapper(Q64int f) {
+            this.f = f;
+        }
+    }
+
+    public static class ValState extends SizeState {
+        public ValWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new ValWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new ValWrapper(new Q64int(i));
+            }
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/field/util/StatesQ64long.java b/test/micro/org/openjdk/bench/valhalla/field/util/StatesQ64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/field/util/StatesQ64long.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.field.util;
+
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.jmh.annotations.Setup;
+
+public class StatesQ64long extends SizeBase {
+
+    public static class ObjWrapper {
+        public Object f;
+
+        public ObjWrapper(Object f) {
+            this.f = f;
+        }
+    }
+
+    public static class ObjState extends SizeState {
+        public ObjWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new ObjWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new ObjWrapper(new Q64long(i));
+            }
+        }
+    }
+
+    public static class IntWrapper {
+        public Int64 f;
+
+        public IntWrapper(Int64 f) {
+            this.f = f;
+        }
+    }
+
+    public static class IntState extends SizeState {
+        public IntWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new IntWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new IntWrapper(new Q64long(i));
+            }
+        }
+    }
+
+
+    public static class RefWrapper {
+        public Q64long f;
+
+        public RefWrapper(Q64long f) {
+            this.f = f;
+        }
+    }
+
+    public static class RefState extends SizeState {
+        public RefWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new RefWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new RefWrapper(new Q64long(i));
+            }
+        }
+    }
+
+
+    public static class ValWrapper {
+        public Q64long f;
+
+        public ValWrapper(Q64long f) {
+            this.f = f;
+        }
+    }
+
+    public static class ValState extends SizeState {
+        public ValWrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new ValWrapper[size];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new ValWrapper(new Q64long(i));
+            }
+        }
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/intrinsics/IsFlatArray.java b/test/micro/org/openjdk/bench/valhalla/intrinsics/IsFlatArray.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/intrinsics/IsFlatArray.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package org.openjdk.bench.valhalla.intrinsics;
+
+import org.openjdk.jmh.annotations.*;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.util.concurrent.TimeUnit;
+import jdk.internal.misc.Unsafe;
+
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.MICROSECONDS)
+@Fork(value = 1,
+      jvmArgsAppend = {"--add-opens", "java.base/jdk.internal.misc=ALL-UNNAMED",
+                       "--enable-preview"})
+@Warmup(iterations = 3, time = 1)
+@Measurement(iterations = 5, time = 1)
+public class IsFlatArray {
+
+    private static final Unsafe U = Unsafe.getUnsafe();
+    private static final VarHandle objectArrayVarHandle =
+        MethodHandles.arrayElementVarHandle(Object[].class);
+
+    @State(Scope.Benchmark)
+    public static class ClassState {
+        public Class flatArrayClass = Point[].class;
+        public Class nonFlatArrayClass = String[].class;
+
+        public Object[] objectArray = new Object[10];
+        public Object objectElement = new Object();
+        public int arrayIndex = 0;
+    }
+
+    @Benchmark
+    public boolean testKnownFlatClass() {
+        return U.isFlatArray(Point[].class);
+    }
+
+    @Benchmark
+    public boolean testKnownNonFlatClass() {
+        return U.isFlatArray(String[].class);
+    }
+
+    @Benchmark
+    public boolean testUnknownFlatClass(ClassState state) {
+        return U.isFlatArray(state.flatArrayClass);
+    }
+
+    @Benchmark
+    public boolean testUnknownNonFlatClass(ClassState state) {
+        return U.isFlatArray(state.nonFlatArrayClass);
+    }
+
+    @Benchmark
+    public void setArrayElement(ClassState state) {
+        objectArrayVarHandle.set(state.objectArray, state.arrayIndex, state.objectElement);
+    }
+
+    @Benchmark
+    public VarHandle makeArrayVarHandle() {
+        return MethodHandles.arrayElementVarHandle(Object[].class);
+    }
+
+}
+
+value class Point {
+    int x;
+    int y;
+    public Point(int x, int y) {
+        this.x = x;
+        this.y = y;
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/invoke/IdentityArray.java b/test/micro/org/openjdk/bench/valhalla/invoke/IdentityArray.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/invoke/IdentityArray.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.invoke;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class IdentityArray {
+
+    public static final int SIZE = 128;
+
+    public interface MyInterface {
+        public int my_method();
+    }
+
+    public static class Ref1 implements MyInterface {
+        public final int f0;
+        public Ref1(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        public int my_method() {
+            return f0;
+        }
+    }
+
+    public static class Ref2 implements MyInterface {
+        public final int f0;
+        public Ref2(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        public int my_method() {
+            return f0;
+        }
+    }
+
+    public static class Ref3 implements MyInterface {
+        public final int f0;
+        public Ref3(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        public int my_method() {
+            return f0;
+        }
+    }
+
+    @State(Scope.Thread)
+    public static abstract class IntState {
+        public MyInterface[] arr;
+    }
+
+    @State(Scope.Thread)
+    public static abstract class Ref1State {
+        public Ref1[] arr;
+    }
+
+    public static class Ref1_as_Ref extends Ref1State {
+        @Setup
+        public void setup() {
+            arr = new Ref1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Ref1(i);
+            }
+        }
+    }
+
+    public static class Ref1_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Ref1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Ref1(i);
+            }
+        }
+    }
+
+    public static class Int1_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new MyInterface[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Ref1(i);
+            }
+        }
+    }
+
+    public static class Ref2_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Ref2[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Ref2(i);
+            }
+        }
+    }
+
+    public static class Int2_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new MyInterface[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Ref2(i);
+            }
+        }
+    }
+
+    public static class Ref3_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Ref3[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Ref3(i);
+            }
+        }
+    }
+
+    public static class Int3_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new MyInterface[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Ref3(i);
+            }
+        }
+    }
+
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceInt(MyInterface[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].my_method();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceRef(Ref1[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].my_method();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Ref_r(Ref1_as_Ref st0, Ref1_as_Ref st1, Ref1_as_Ref st2, Ref1_as_Ref st3, Ref1_as_Ref st4, Ref1_as_Ref st5) {
+        return reduceRef(st0.arr) +
+               reduceRef(st1.arr) +
+               reduceRef(st2.arr) +
+               reduceRef(st3.arr) +
+               reduceRef(st4.arr) +
+               reduceRef(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int_r(Ref1_as_Int st0, Ref1_as_Int st1, Ref1_as_Int st2, Ref1_as_Int st3, Ref1_as_Int st4, Ref1_as_Int st5) {
+        return reduceInt(st0.arr) +
+               reduceInt(st1.arr) +
+               reduceInt(st2.arr) +
+               reduceInt(st3.arr) +
+               reduceInt(st4.arr) +
+               reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int_i(Int1_as_Int st0, Int1_as_Int st1, Int1_as_Int st2, Int1_as_Int st3, Int1_as_Int st4, Int1_as_Int st5) {
+        return reduceInt(st0.arr) +
+               reduceInt(st1.arr) +
+               reduceInt(st2.arr) +
+               reduceInt(st3.arr) +
+               reduceInt(st4.arr) +
+               reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int_ri(Ref1_as_Int st0, Int1_as_Int st1, Ref1_as_Int st2, Int1_as_Int st3, Ref1_as_Int st4, Int1_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int_r(Ref1_as_Int st0, Ref2_as_Int st1, Ref1_as_Int st2, Ref2_as_Int st3, Ref1_as_Int st4, Ref2_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int_i(Int1_as_Int st0, Int2_as_Int st1, Int1_as_Int st2, Int2_as_Int st3, Int1_as_Int st4, Int2_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int_ri(Ref1_as_Int st0, Int1_as_Int st1, Ref2_as_Int st2, Int1_as_Int st3, Ref1_as_Int st4, Int2_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int_r(Ref1_as_Int st0, Ref2_as_Int st1, Ref3_as_Int st2, Ref1_as_Int st3, Ref2_as_Int st4, Ref3_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int_i(Int1_as_Int st0, Int2_as_Int st1, Int3_as_Int st2, Int1_as_Int st3, Int2_as_Int st4, Int3_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int_ri(Ref1_as_Int st0, Int2_as_Int st1, Ref3_as_Int st2, Int1_as_Int st3, Ref2_as_Int st4, Int3_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/invoke/IdentityField.java b/test/micro/org/openjdk/bench/valhalla/invoke/IdentityField.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/invoke/IdentityField.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.invoke;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class IdentityField {
+
+    public static final int SIZE = 128;
+
+    public interface MyInterface {
+        public int my_method();
+    }
+
+    public static class Ref1 implements MyInterface {
+        public final int f0;
+        public Ref1(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        public int my_method() {
+            return f0;
+        }
+    }
+
+    public static class Ref2 implements MyInterface {
+        public final int f0;
+        public Ref2(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        public int my_method() {
+            return f0;
+        }
+    }
+
+    public static class Ref3 implements MyInterface {
+        public final int f0;
+        public Ref3(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        public int my_method() {
+            return f0;
+        }
+    }
+
+    public static class Ref1Wrapper {
+        public Ref1 f;
+
+        public Ref1Wrapper(Ref1 f) {
+            this.f = f;
+        }
+    }
+
+    public static class IntWrapper {
+        public MyInterface f;
+
+        public IntWrapper(MyInterface f) {
+            this.f = f;
+        }
+    }
+
+    @State(Scope.Thread)
+    public static class Ref1State {
+        public Ref1Wrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new Ref1Wrapper[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Ref1Wrapper(new Ref1(i));
+            }
+        }
+    }
+
+    @State(Scope.Thread)
+    public static class IntStateBase {
+        public IntWrapper[] arr;
+    }
+
+    public static class Int1State extends IntStateBase {
+        @Setup
+        public void setup() {
+            arr = new IntWrapper[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new IntWrapper(new Ref1(i));
+            }
+        }
+    }
+
+    public static class Int2State extends IntStateBase {
+        @Setup
+        public void setup() {
+            arr = new IntWrapper[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new IntWrapper(new Ref2(i));
+            }
+        }
+    }
+
+    public static class Int3State extends IntStateBase {
+        @Setup
+        public void setup() {
+            arr = new IntWrapper[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new IntWrapper(new Ref3(i));
+            }
+        }
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceRef(Ref1Wrapper[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].f.my_method();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceInt(IntWrapper[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].f.my_method();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Ref(Ref1State st0, Ref1State st1, Ref1State st2, Ref1State st3, Ref1State st4, Ref1State st5) {
+        return reduceRef(st0.arr) +
+                reduceRef(st1.arr) +
+                reduceRef(st2.arr) +
+                reduceRef(st3.arr) +
+                reduceRef(st4.arr) +
+                reduceRef(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int(Int1State st0, Int1State st1, Int1State st2, Int1State st3, Int1State st4, Int1State st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int(Int1State st0, Int2State st1, Int1State st2, Int2State st3, Int1State st4, Int2State st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int(Int1State st0, Int2State st1, Int3State st2, Int1State st3, Int2State st4, Int3State st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/invoke/InlineArray0.java b/test/micro/org/openjdk/bench/valhalla/invoke/InlineArray0.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/invoke/InlineArray0.java
@@ -0,0 +1,552 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.invoke;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class InlineArray0 {
+
+    public static final int SIZE = 128;
+
+    public interface MyInterface {
+        public int my_method();
+    }
+
+    public static value class Val1 implements MyInterface {
+        public final int f0;
+        public Val1(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        public int my_method() {
+            return f0;
+        }
+    }
+
+    public static value class Val2 implements MyInterface {
+        public final int f0;
+        public Val2(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        public int my_method() {
+            return f0;
+        }
+    }
+
+    public static value class Val3 implements MyInterface {
+        public final int f0;
+        public Val3(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        public int my_method() {
+            return f0;
+        }
+    }
+
+    @State(Scope.Thread)
+    public static abstract class IntState {
+        public MyInterface[] arr;
+    }
+
+    @State(Scope.Thread)
+    public static abstract class Ref1State {
+        public Val1[] arr;
+    }
+
+    @State(Scope.Thread)
+    public static abstract class Val1State {
+        public Val1[] arr;
+    }
+
+    public static class Val1_as_Val extends Val1State {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Val1_as_Ref extends Ref1State {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Val1_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Ref1_as_Ref extends Ref1State {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Ref1_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Int1_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new MyInterface[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Val2_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Val2[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val2(i);
+            }
+        }
+    }
+
+    public static class Ref2_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Val2[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val2(i);
+            }
+        }
+    }
+
+    public static class Int2_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new MyInterface[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val2(i);
+            }
+        }
+    }
+
+    public static class Val3_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Val3[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val3(i);
+            }
+        }
+    }
+
+    public static class Ref3_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Val3[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val3(i);
+            }
+        }
+    }
+
+    public static class Int3_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new MyInterface[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val3(i);
+            }
+        }
+    }
+
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceInt(MyInterface[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].my_method();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceRef(Val1[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].my_method();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceVal(Val1[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].my_method();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Val_v(Val1_as_Val st0, Val1_as_Val st1, Val1_as_Val st2, Val1_as_Val st3, Val1_as_Val st4, Val1_as_Val st5) {
+        return reduceVal(st0.arr) +
+               reduceVal(st1.arr) +
+               reduceVal(st2.arr) +
+               reduceVal(st3.arr) +
+               reduceVal(st4.arr) +
+               reduceVal(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Ref_v(Val1_as_Ref st0, Val1_as_Ref st1, Val1_as_Ref st2, Val1_as_Ref st3, Val1_as_Ref st4, Val1_as_Ref st5) {
+        return reduceRef(st0.arr) +
+               reduceRef(st1.arr) +
+               reduceRef(st2.arr) +
+               reduceRef(st3.arr) +
+               reduceRef(st4.arr) +
+               reduceRef(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Ref_r(Ref1_as_Ref st0, Ref1_as_Ref st1, Ref1_as_Ref st2, Ref1_as_Ref st3, Ref1_as_Ref st4, Ref1_as_Ref st5) {
+        return reduceRef(st0.arr) +
+               reduceRef(st1.arr) +
+               reduceRef(st2.arr) +
+               reduceRef(st3.arr) +
+               reduceRef(st4.arr) +
+               reduceRef(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Ref_vr(Val1_as_Ref st0, Ref1_as_Ref st1, Val1_as_Ref st2, Ref1_as_Ref st3, Val1_as_Ref st4, Ref1_as_Ref st5) {
+        return reduceRef(st0.arr) +
+               reduceRef(st1.arr) +
+               reduceRef(st2.arr) +
+               reduceRef(st3.arr) +
+               reduceRef(st4.arr) +
+               reduceRef(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int_v(Val1_as_Int st0, Val1_as_Int st1, Val1_as_Int st2, Val1_as_Int st3, Val1_as_Int st4, Val1_as_Int st5) {
+        return reduceInt(st0.arr) +
+               reduceInt(st1.arr) +
+               reduceInt(st2.arr) +
+               reduceInt(st3.arr) +
+               reduceInt(st4.arr) +
+               reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int_r(Ref1_as_Int st0, Ref1_as_Int st1, Ref1_as_Int st2, Ref1_as_Int st3, Ref1_as_Int st4, Ref1_as_Int st5) {
+        return reduceInt(st0.arr) +
+               reduceInt(st1.arr) +
+               reduceInt(st2.arr) +
+               reduceInt(st3.arr) +
+               reduceInt(st4.arr) +
+               reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int_i(Int1_as_Int st0, Int1_as_Int st1, Int1_as_Int st2, Int1_as_Int st3, Int1_as_Int st4, Int1_as_Int st5) {
+        return reduceInt(st0.arr) +
+               reduceInt(st1.arr) +
+               reduceInt(st2.arr) +
+               reduceInt(st3.arr) +
+               reduceInt(st4.arr) +
+               reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int_vr(Val1_as_Int st0, Ref1_as_Int st1, Val1_as_Int st2, Ref1_as_Int st3, Val1_as_Int st4, Ref1_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int_vi(Val1_as_Int st0, Int1_as_Int st1, Val1_as_Int st2, Int1_as_Int st3, Val1_as_Int st4, Int1_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int_ri(Ref1_as_Int st0, Int1_as_Int st1, Ref1_as_Int st2, Int1_as_Int st3, Ref1_as_Int st4, Int1_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int_vri(Val1_as_Int st0, Ref1_as_Int st1, Int1_as_Int st2, Val1_as_Int st3, Ref1_as_Int st4, Int1_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int_v(Val1_as_Int st0, Val2_as_Int st1, Val1_as_Int st2, Val2_as_Int st3, Val1_as_Int st4, Val2_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int_r(Ref1_as_Int st0, Ref2_as_Int st1, Ref1_as_Int st2, Ref2_as_Int st3, Ref1_as_Int st4, Ref2_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int_i(Int1_as_Int st0, Int2_as_Int st1, Int1_as_Int st2, Int2_as_Int st3, Int1_as_Int st4, Int2_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int_vr(Val1_as_Int st0, Ref2_as_Int st1, Val2_as_Int st2, Ref1_as_Int st3, Val1_as_Int st4, Ref2_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int_vi(Val1_as_Int st0, Int2_as_Int st1, Val2_as_Int st2, Int1_as_Int st3, Val1_as_Int st4, Int2_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int_ri(Ref1_as_Int st0, Int1_as_Int st1, Ref2_as_Int st2, Int1_as_Int st3, Ref1_as_Int st4, Int2_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int_vri(Val1_as_Int st0, Ref1_as_Int st1, Int1_as_Int st2, Val2_as_Int st3, Ref2_as_Int st4, Int2_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int_v(Val1_as_Int st0, Val2_as_Int st1, Val3_as_Int st2, Val1_as_Int st3, Val2_as_Int st4, Val3_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int_r(Ref1_as_Int st0, Ref2_as_Int st1, Ref3_as_Int st2, Ref1_as_Int st3, Ref2_as_Int st4, Ref3_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int_i(Int1_as_Int st0, Int2_as_Int st1, Int3_as_Int st2, Int1_as_Int st3, Int2_as_Int st4, Int3_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int_vr(Val1_as_Int st0, Ref2_as_Int st1, Val3_as_Int st2, Ref1_as_Int st3, Val2_as_Int st4, Ref3_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int_vi(Val1_as_Int st0, Int2_as_Int st1, Val3_as_Int st2, Int1_as_Int st3, Val3_as_Int st4, Int3_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int_ri(Ref1_as_Int st0, Int2_as_Int st1, Ref3_as_Int st2, Int1_as_Int st3, Ref2_as_Int st4, Int3_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int_vri(Val1_as_Int st0, Ref2_as_Int st1, Int3_as_Int st2, Val2_as_Int st3, Ref3_as_Int st4, Int1_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/invoke/InlineArray1.java b/test/micro/org/openjdk/bench/valhalla/invoke/InlineArray1.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/invoke/InlineArray1.java
@@ -0,0 +1,555 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.invoke;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class InlineArray1 {
+
+    public static final int SIZE = 128;
+
+    public interface MyInterface {
+        public int my_method();
+    }
+
+    public static value class Val1 implements MyInterface {
+        public final int f0;
+        public Val1(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+        public int my_method() {
+            return f0;
+        }
+    }
+
+    public static value class Val2 implements MyInterface {
+        public final int f0;
+        public Val2(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+        public int my_method() {
+            return f0;
+        }
+    }
+
+    public static value class Val3 implements MyInterface {
+        public final int f0;
+        public Val3(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+        public int my_method() {
+            return f0;
+        }
+    }
+
+    @State(Scope.Thread)
+    public static abstract class IntState {
+        public MyInterface[] arr;
+    }
+
+    @State(Scope.Thread)
+    public static abstract class Ref1State {
+        public Val1[] arr;
+    }
+
+    @State(Scope.Thread)
+    public static abstract class Val1State {
+        public Val1[] arr;
+    }
+
+    public static class Val1_as_Val extends Val1State {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Val1_as_Ref extends Ref1State {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Val1_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Ref1_as_Ref extends Ref1State {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Ref1_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Int1_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new MyInterface[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Val2_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Val2[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val2(i);
+            }
+        }
+    }
+
+    public static class Ref2_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Val2[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val2(i);
+            }
+        }
+    }
+
+    public static class Int2_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new MyInterface[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val2(i);
+            }
+        }
+    }
+
+    public static class Val3_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Val3[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val3(i);
+            }
+        }
+    }
+
+    public static class Ref3_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Val3[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val3(i);
+            }
+        }
+    }
+
+    public static class Int3_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new MyInterface[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val3(i);
+            }
+        }
+    }
+
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceInt(MyInterface[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].my_method();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceRef(Val1[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].my_method();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceVal(Val1[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].my_method();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Val_v(Val1_as_Val st0, Val1_as_Val st1, Val1_as_Val st2, Val1_as_Val st3, Val1_as_Val st4, Val1_as_Val st5) {
+        return reduceVal(st0.arr) +
+               reduceVal(st1.arr) +
+               reduceVal(st2.arr) +
+               reduceVal(st3.arr) +
+               reduceVal(st4.arr) +
+               reduceVal(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Ref_v(Val1_as_Ref st0, Val1_as_Ref st1, Val1_as_Ref st2, Val1_as_Ref st3, Val1_as_Ref st4, Val1_as_Ref st5) {
+        return reduceRef(st0.arr) +
+               reduceRef(st1.arr) +
+               reduceRef(st2.arr) +
+               reduceRef(st3.arr) +
+               reduceRef(st4.arr) +
+               reduceRef(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Ref_r(Ref1_as_Ref st0, Ref1_as_Ref st1, Ref1_as_Ref st2, Ref1_as_Ref st3, Ref1_as_Ref st4, Ref1_as_Ref st5) {
+        return reduceRef(st0.arr) +
+               reduceRef(st1.arr) +
+               reduceRef(st2.arr) +
+               reduceRef(st3.arr) +
+               reduceRef(st4.arr) +
+               reduceRef(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Ref_vr(Val1_as_Ref st0, Ref1_as_Ref st1, Val1_as_Ref st2, Ref1_as_Ref st3, Val1_as_Ref st4, Ref1_as_Ref st5) {
+        return reduceRef(st0.arr) +
+               reduceRef(st1.arr) +
+               reduceRef(st2.arr) +
+               reduceRef(st3.arr) +
+               reduceRef(st4.arr) +
+               reduceRef(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int_v(Val1_as_Int st0, Val1_as_Int st1, Val1_as_Int st2, Val1_as_Int st3, Val1_as_Int st4, Val1_as_Int st5) {
+        return reduceInt(st0.arr) +
+               reduceInt(st1.arr) +
+               reduceInt(st2.arr) +
+               reduceInt(st3.arr) +
+               reduceInt(st4.arr) +
+               reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int_r(Ref1_as_Int st0, Ref1_as_Int st1, Ref1_as_Int st2, Ref1_as_Int st3, Ref1_as_Int st4, Ref1_as_Int st5) {
+        return reduceInt(st0.arr) +
+               reduceInt(st1.arr) +
+               reduceInt(st2.arr) +
+               reduceInt(st3.arr) +
+               reduceInt(st4.arr) +
+               reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int_i(Int1_as_Int st0, Int1_as_Int st1, Int1_as_Int st2, Int1_as_Int st3, Int1_as_Int st4, Int1_as_Int st5) {
+        return reduceInt(st0.arr) +
+               reduceInt(st1.arr) +
+               reduceInt(st2.arr) +
+               reduceInt(st3.arr) +
+               reduceInt(st4.arr) +
+               reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int_vr(Val1_as_Int st0, Ref1_as_Int st1, Val1_as_Int st2, Ref1_as_Int st3, Val1_as_Int st4, Ref1_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int_vi(Val1_as_Int st0, Int1_as_Int st1, Val1_as_Int st2, Int1_as_Int st3, Val1_as_Int st4, Int1_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int_ri(Ref1_as_Int st0, Int1_as_Int st1, Ref1_as_Int st2, Int1_as_Int st3, Ref1_as_Int st4, Int1_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int_vri(Val1_as_Int st0, Ref1_as_Int st1, Int1_as_Int st2, Val1_as_Int st3, Ref1_as_Int st4, Int1_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int_v(Val1_as_Int st0, Val2_as_Int st1, Val1_as_Int st2, Val2_as_Int st3, Val1_as_Int st4, Val2_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int_r(Ref1_as_Int st0, Ref2_as_Int st1, Ref1_as_Int st2, Ref2_as_Int st3, Ref1_as_Int st4, Ref2_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int_i(Int1_as_Int st0, Int2_as_Int st1, Int1_as_Int st2, Int2_as_Int st3, Int1_as_Int st4, Int2_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int_vr(Val1_as_Int st0, Ref2_as_Int st1, Val2_as_Int st2, Ref1_as_Int st3, Val1_as_Int st4, Ref2_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int_vi(Val1_as_Int st0, Int2_as_Int st1, Val2_as_Int st2, Int1_as_Int st3, Val1_as_Int st4, Int2_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int_ri(Ref1_as_Int st0, Int1_as_Int st1, Ref2_as_Int st2, Int1_as_Int st3, Ref1_as_Int st4, Int2_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int_vri(Val1_as_Int st0, Ref1_as_Int st1, Int1_as_Int st2, Val2_as_Int st3, Ref2_as_Int st4, Int2_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int_v(Val1_as_Int st0, Val2_as_Int st1, Val3_as_Int st2, Val1_as_Int st3, Val2_as_Int st4, Val3_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int_r(Ref1_as_Int st0, Ref2_as_Int st1, Ref3_as_Int st2, Ref1_as_Int st3, Ref2_as_Int st4, Ref3_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int_i(Int1_as_Int st0, Int2_as_Int st1, Int3_as_Int st2, Int1_as_Int st3, Int2_as_Int st4, Int3_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int_vr(Val1_as_Int st0, Ref2_as_Int st1, Val3_as_Int st2, Ref1_as_Int st3, Val2_as_Int st4, Ref3_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int_vi(Val1_as_Int st0, Int2_as_Int st1, Val3_as_Int st2, Int1_as_Int st3, Val3_as_Int st4, Int3_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int_ri(Ref1_as_Int st0, Int2_as_Int st1, Ref3_as_Int st2, Int1_as_Int st3, Ref2_as_Int st4, Int3_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int_vri(Val1_as_Int st0, Ref2_as_Int st1, Int3_as_Int st2, Val2_as_Int st3, Ref3_as_Int st4, Int1_as_Int st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/invoke/InlineArrayHashExplicit.java b/test/micro/org/openjdk/bench/valhalla/invoke/InlineArrayHashExplicit.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/invoke/InlineArrayHashExplicit.java
@@ -0,0 +1,547 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.invoke;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class InlineArrayHashExplicit {
+
+    public static final int SIZE = 128;
+
+    public static value class Val1  {
+        public final int f0;
+        public Val1(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        public int hashCode() {
+            return f0;
+        }
+    }
+
+    public static value class Val2  {
+        public final int f0;
+        public Val2(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        public int hashCode() {
+            return f0;
+        }
+    }
+
+    public static value class Val3  {
+        public final int f0;
+        public Val3(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        public int hashCode() {
+            return f0;
+        }
+    }
+
+    @State(Scope.Thread)
+    public static abstract class ObjState {
+        public Object[] arr;
+    }
+
+    @State(Scope.Thread)
+    public static abstract class Ref1State {
+        public Val1[] arr;
+    }
+
+    @State(Scope.Thread)
+    public static abstract class Val1State {
+        public Val1[] arr;
+    }
+
+    public static class Val1_as_Val extends Val1State {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Val1_as_Ref extends Ref1State {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Val1_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Ref1_as_Ref extends Ref1State {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Ref1_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Obj1_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Val2_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Val2[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val2(i);
+            }
+        }
+    }
+
+    public static class Ref2_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Val2[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val2(i);
+            }
+        }
+    }
+
+    public static class Obj2_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val2(i);
+            }
+        }
+    }
+
+    public static class Val3_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Val3[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val3(i);
+            }
+        }
+    }
+
+    public static class Ref3_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Val3[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val3(i);
+            }
+        }
+    }
+
+    public static class Obj3_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val3(i);
+            }
+        }
+    }
+
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceObj(Object[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].hashCode();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceRef(Val1[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].hashCode();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceVal(Val1[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].hashCode();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Val_v(Val1_as_Val st0, Val1_as_Val st1, Val1_as_Val st2, Val1_as_Val st3, Val1_as_Val st4, Val1_as_Val st5) {
+        return reduceVal(st0.arr) +
+               reduceVal(st1.arr) +
+               reduceVal(st2.arr) +
+               reduceVal(st3.arr) +
+               reduceVal(st4.arr) +
+               reduceVal(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Ref_v(Val1_as_Ref st0, Val1_as_Ref st1, Val1_as_Ref st2, Val1_as_Ref st3, Val1_as_Ref st4, Val1_as_Ref st5) {
+        return reduceRef(st0.arr) +
+               reduceRef(st1.arr) +
+               reduceRef(st2.arr) +
+               reduceRef(st3.arr) +
+               reduceRef(st4.arr) +
+               reduceRef(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Ref_r(Ref1_as_Ref st0, Ref1_as_Ref st1, Ref1_as_Ref st2, Ref1_as_Ref st3, Ref1_as_Ref st4, Ref1_as_Ref st5) {
+        return reduceRef(st0.arr) +
+               reduceRef(st1.arr) +
+               reduceRef(st2.arr) +
+               reduceRef(st3.arr) +
+               reduceRef(st4.arr) +
+               reduceRef(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Ref_vr(Val1_as_Ref st0, Ref1_as_Ref st1, Val1_as_Ref st2, Ref1_as_Ref st3, Val1_as_Ref st4, Ref1_as_Ref st5) {
+        return reduceRef(st0.arr) +
+               reduceRef(st1.arr) +
+               reduceRef(st2.arr) +
+               reduceRef(st3.arr) +
+               reduceRef(st4.arr) +
+               reduceRef(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Obj_v(Val1_as_Obj st0, Val1_as_Obj st1, Val1_as_Obj st2, Val1_as_Obj st3, Val1_as_Obj st4, Val1_as_Obj st5) {
+        return reduceObj(st0.arr) +
+               reduceObj(st1.arr) +
+               reduceObj(st2.arr) +
+               reduceObj(st3.arr) +
+               reduceObj(st4.arr) +
+               reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Obj_r(Ref1_as_Obj st0, Ref1_as_Obj st1, Ref1_as_Obj st2, Ref1_as_Obj st3, Ref1_as_Obj st4, Ref1_as_Obj st5) {
+        return reduceObj(st0.arr) +
+               reduceObj(st1.arr) +
+               reduceObj(st2.arr) +
+               reduceObj(st3.arr) +
+               reduceObj(st4.arr) +
+               reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Obj_o(Obj1_as_Obj st0, Obj1_as_Obj st1, Obj1_as_Obj st2, Obj1_as_Obj st3, Obj1_as_Obj st4, Obj1_as_Obj st5) {
+        return reduceObj(st0.arr) +
+               reduceObj(st1.arr) +
+               reduceObj(st2.arr) +
+               reduceObj(st3.arr) +
+               reduceObj(st4.arr) +
+               reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Obj_vr(Val1_as_Obj st0, Ref1_as_Obj st1, Val1_as_Obj st2, Ref1_as_Obj st3, Val1_as_Obj st4, Ref1_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Obj_vo(Val1_as_Obj st0, Obj1_as_Obj st1, Val1_as_Obj st2, Obj1_as_Obj st3, Val1_as_Obj st4, Obj1_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Obj_ro(Ref1_as_Obj st0, Obj1_as_Obj st1, Ref1_as_Obj st2, Obj1_as_Obj st3, Ref1_as_Obj st4, Obj1_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Obj_vro(Val1_as_Obj st0, Ref1_as_Obj st1, Obj1_as_Obj st2, Val1_as_Obj st3, Ref1_as_Obj st4, Obj1_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Obj_v(Val1_as_Obj st0, Val2_as_Obj st1, Val1_as_Obj st2, Val2_as_Obj st3, Val1_as_Obj st4, Val2_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Obj_r(Ref1_as_Obj st0, Ref2_as_Obj st1, Ref1_as_Obj st2, Ref2_as_Obj st3, Ref1_as_Obj st4, Ref2_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Obj_o(Obj1_as_Obj st0, Obj2_as_Obj st1, Obj1_as_Obj st2, Obj2_as_Obj st3, Obj1_as_Obj st4, Obj2_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Obj_vr(Val1_as_Obj st0, Ref2_as_Obj st1, Val2_as_Obj st2, Ref1_as_Obj st3, Val1_as_Obj st4, Ref2_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Obj_vo(Val1_as_Obj st0, Obj2_as_Obj st1, Val2_as_Obj st2, Obj1_as_Obj st3, Val1_as_Obj st4, Obj2_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Obj_ro(Ref1_as_Obj st0, Obj1_as_Obj st1, Ref2_as_Obj st2, Obj1_as_Obj st3, Ref1_as_Obj st4, Obj2_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Obj_vro(Val1_as_Obj st0, Ref1_as_Obj st1, Obj1_as_Obj st2, Val2_as_Obj st3, Ref2_as_Obj st4, Obj2_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Obj_v(Val1_as_Obj st0, Val2_as_Obj st1, Val3_as_Obj st2, Val1_as_Obj st3, Val2_as_Obj st4, Val3_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Obj_r(Ref1_as_Obj st0, Ref2_as_Obj st1, Ref3_as_Obj st2, Ref1_as_Obj st3, Ref2_as_Obj st4, Ref3_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Obj_o(Obj1_as_Obj st0, Obj2_as_Obj st1, Obj3_as_Obj st2, Obj1_as_Obj st3, Obj2_as_Obj st4, Obj3_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Obj_vr(Val1_as_Obj st0, Ref2_as_Obj st1, Val3_as_Obj st2, Ref1_as_Obj st3, Val2_as_Obj st4, Ref3_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Obj_vo(Val1_as_Obj st0, Obj2_as_Obj st1, Val3_as_Obj st2, Obj1_as_Obj st3, Val3_as_Obj st4, Obj3_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Obj_ro(Ref1_as_Obj st0, Obj2_as_Obj st1, Ref3_as_Obj st2, Obj1_as_Obj st3, Ref2_as_Obj st4, Obj3_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Obj_vro(Val1_as_Obj st0, Ref2_as_Obj st1, Obj3_as_Obj st2, Val2_as_Obj st3, Ref3_as_Obj st4, Obj1_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/invoke/InlineArrayHashImplicit.java b/test/micro/org/openjdk/bench/valhalla/invoke/InlineArrayHashImplicit.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/invoke/InlineArrayHashImplicit.java
@@ -0,0 +1,535 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.invoke;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class InlineArrayHashImplicit {
+
+    public static final int SIZE = 128;
+
+    public static value class Val1  {
+        public final int f0;
+        public Val1(int f0) {
+            this.f0 = f0;
+        }
+    }
+
+    public static value class Val2  {
+        public final int f0;
+        public Val2(int f0) {
+            this.f0 = f0;
+        }
+    }
+
+    public static value class Val3  {
+        public final int f0;
+        public Val3(int f0) {
+            this.f0 = f0;
+        }
+    }
+
+    @State(Scope.Thread)
+    public static abstract class ObjState {
+        public Object[] arr;
+    }
+
+    @State(Scope.Thread)
+    public static abstract class Ref1State {
+        public Val1[] arr;
+    }
+
+    @State(Scope.Thread)
+    public static abstract class Val1State {
+        public Val1[] arr;
+    }
+
+    public static class Val1_as_Val extends Val1State {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Val1_as_Ref extends Ref1State {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Val1_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Ref1_as_Ref extends Ref1State {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Ref1_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Val1[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Obj1_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1(i);
+            }
+        }
+    }
+
+    public static class Val2_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Val2[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val2(i);
+            }
+        }
+    }
+
+    public static class Ref2_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Val2[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val2(i);
+            }
+        }
+    }
+
+    public static class Obj2_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val2(i);
+            }
+        }
+    }
+
+    public static class Val3_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Val3[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val3(i);
+            }
+        }
+    }
+
+    public static class Ref3_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Val3[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val3(i);
+            }
+        }
+    }
+
+    public static class Obj3_as_Obj extends ObjState {
+        @Setup
+        public void setup() {
+            arr = new Object[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val3(i);
+            }
+        }
+    }
+
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceObj(Object[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].hashCode();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceRef(Val1[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].hashCode();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceVal(Val1[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].hashCode();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Val_v(Val1_as_Val st0, Val1_as_Val st1, Val1_as_Val st2, Val1_as_Val st3, Val1_as_Val st4, Val1_as_Val st5) {
+        return reduceVal(st0.arr) +
+               reduceVal(st1.arr) +
+               reduceVal(st2.arr) +
+               reduceVal(st3.arr) +
+               reduceVal(st4.arr) +
+               reduceVal(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Ref_v(Val1_as_Ref st0, Val1_as_Ref st1, Val1_as_Ref st2, Val1_as_Ref st3, Val1_as_Ref st4, Val1_as_Ref st5) {
+        return reduceRef(st0.arr) +
+               reduceRef(st1.arr) +
+               reduceRef(st2.arr) +
+               reduceRef(st3.arr) +
+               reduceRef(st4.arr) +
+               reduceRef(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Ref_r(Ref1_as_Ref st0, Ref1_as_Ref st1, Ref1_as_Ref st2, Ref1_as_Ref st3, Ref1_as_Ref st4, Ref1_as_Ref st5) {
+        return reduceRef(st0.arr) +
+               reduceRef(st1.arr) +
+               reduceRef(st2.arr) +
+               reduceRef(st3.arr) +
+               reduceRef(st4.arr) +
+               reduceRef(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Ref_vr(Val1_as_Ref st0, Ref1_as_Ref st1, Val1_as_Ref st2, Ref1_as_Ref st3, Val1_as_Ref st4, Ref1_as_Ref st5) {
+        return reduceRef(st0.arr) +
+               reduceRef(st1.arr) +
+               reduceRef(st2.arr) +
+               reduceRef(st3.arr) +
+               reduceRef(st4.arr) +
+               reduceRef(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Obj_v(Val1_as_Obj st0, Val1_as_Obj st1, Val1_as_Obj st2, Val1_as_Obj st3, Val1_as_Obj st4, Val1_as_Obj st5) {
+        return reduceObj(st0.arr) +
+               reduceObj(st1.arr) +
+               reduceObj(st2.arr) +
+               reduceObj(st3.arr) +
+               reduceObj(st4.arr) +
+               reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Obj_r(Ref1_as_Obj st0, Ref1_as_Obj st1, Ref1_as_Obj st2, Ref1_as_Obj st3, Ref1_as_Obj st4, Ref1_as_Obj st5) {
+        return reduceObj(st0.arr) +
+               reduceObj(st1.arr) +
+               reduceObj(st2.arr) +
+               reduceObj(st3.arr) +
+               reduceObj(st4.arr) +
+               reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Obj_o(Obj1_as_Obj st0, Obj1_as_Obj st1, Obj1_as_Obj st2, Obj1_as_Obj st3, Obj1_as_Obj st4, Obj1_as_Obj st5) {
+        return reduceObj(st0.arr) +
+               reduceObj(st1.arr) +
+               reduceObj(st2.arr) +
+               reduceObj(st3.arr) +
+               reduceObj(st4.arr) +
+               reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Obj_vr(Val1_as_Obj st0, Ref1_as_Obj st1, Val1_as_Obj st2, Ref1_as_Obj st3, Val1_as_Obj st4, Ref1_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Obj_vo(Val1_as_Obj st0, Obj1_as_Obj st1, Val1_as_Obj st2, Obj1_as_Obj st3, Val1_as_Obj st4, Obj1_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Obj_ro(Ref1_as_Obj st0, Obj1_as_Obj st1, Ref1_as_Obj st2, Obj1_as_Obj st3, Ref1_as_Obj st4, Obj1_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Obj_vro(Val1_as_Obj st0, Ref1_as_Obj st1, Obj1_as_Obj st2, Val1_as_Obj st3, Ref1_as_Obj st4, Obj1_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Obj_v(Val1_as_Obj st0, Val2_as_Obj st1, Val1_as_Obj st2, Val2_as_Obj st3, Val1_as_Obj st4, Val2_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Obj_r(Ref1_as_Obj st0, Ref2_as_Obj st1, Ref1_as_Obj st2, Ref2_as_Obj st3, Ref1_as_Obj st4, Ref2_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Obj_o(Obj1_as_Obj st0, Obj2_as_Obj st1, Obj1_as_Obj st2, Obj2_as_Obj st3, Obj1_as_Obj st4, Obj2_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Obj_vr(Val1_as_Obj st0, Ref2_as_Obj st1, Val2_as_Obj st2, Ref1_as_Obj st3, Val1_as_Obj st4, Ref2_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Obj_vo(Val1_as_Obj st0, Obj2_as_Obj st1, Val2_as_Obj st2, Obj1_as_Obj st3, Val1_as_Obj st4, Obj2_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Obj_ro(Ref1_as_Obj st0, Obj1_as_Obj st1, Ref2_as_Obj st2, Obj1_as_Obj st3, Ref1_as_Obj st4, Obj2_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Obj_vro(Val1_as_Obj st0, Ref1_as_Obj st1, Obj1_as_Obj st2, Val2_as_Obj st3, Ref2_as_Obj st4, Obj2_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Obj_v(Val1_as_Obj st0, Val2_as_Obj st1, Val3_as_Obj st2, Val1_as_Obj st3, Val2_as_Obj st4, Val3_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Obj_r(Ref1_as_Obj st0, Ref2_as_Obj st1, Ref3_as_Obj st2, Ref1_as_Obj st3, Ref2_as_Obj st4, Ref3_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Obj_o(Obj1_as_Obj st0, Obj2_as_Obj st1, Obj3_as_Obj st2, Obj1_as_Obj st3, Obj2_as_Obj st4, Obj3_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Obj_vr(Val1_as_Obj st0, Ref2_as_Obj st1, Val3_as_Obj st2, Ref1_as_Obj st3, Val2_as_Obj st4, Ref3_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Obj_vo(Val1_as_Obj st0, Obj2_as_Obj st1, Val3_as_Obj st2, Obj1_as_Obj st3, Val3_as_Obj st4, Obj3_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Obj_ro(Ref1_as_Obj st0, Obj2_as_Obj st1, Ref3_as_Obj st2, Obj1_as_Obj st3, Ref2_as_Obj st4, Obj3_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Obj_vro(Val1_as_Obj st0, Ref2_as_Obj st1, Obj3_as_Obj st2, Val2_as_Obj st3, Ref3_as_Obj st4, Obj1_as_Obj st5) {
+        return reduceObj(st0.arr) +
+                reduceObj(st1.arr) +
+                reduceObj(st2.arr) +
+                reduceObj(st3.arr) +
+                reduceObj(st4.arr) +
+                reduceObj(st5.arr);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/invoke/InlineField.java b/test/micro/org/openjdk/bench/valhalla/invoke/InlineField.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/invoke/InlineField.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.invoke;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class InlineField {
+
+    public static final int SIZE = 128;
+
+    public interface MyInterface {
+        public int my_method();
+    }
+
+    public static value class Val1 implements MyInterface {
+        public final int f0;
+        public Val1(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        public int my_method() {
+            return f0;
+        }
+    }
+
+    public static value class Val2 implements MyInterface {
+        public final int f0;
+        public Val2(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        public int my_method() {
+            return f0;
+        }
+    }
+
+    public static value class Val3 implements MyInterface {
+        public final int f0;
+        public Val3(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        public int my_method() {
+            return f0;
+        }
+    }
+
+    public static class Val1Wrapper {
+        public Val1 f;
+
+        public Val1Wrapper(Val1 f) {
+            this.f = f;
+        }
+    }
+
+    public static class Ref1Wrapper {
+        public Val1 f;
+
+        public Ref1Wrapper(Val1 f) {
+            this.f = f;
+        }
+    }
+
+    public static class IntWrapper {
+        public MyInterface f;
+
+        public IntWrapper(MyInterface f) {
+            this.f = f;
+        }
+    }
+
+    @State(Scope.Thread)
+    public static class Val1State {
+        public Val1Wrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new Val1Wrapper[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1Wrapper(new Val1(i));
+            }
+        }
+    }
+
+    @State(Scope.Thread)
+    public static class Ref1State {
+        public Ref1Wrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new Ref1Wrapper[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Ref1Wrapper(new Val1(i));
+            }
+        }
+    }
+
+    @State(Scope.Thread)
+    public static class IntStateBase {
+        public IntWrapper[] arr;
+    }
+
+    public static class Int1State extends IntStateBase {
+        @Setup
+        public void setup() {
+            arr = new IntWrapper[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new IntWrapper(new Val1(i));
+            }
+        }
+    }
+
+    public static class Int2State extends IntStateBase {
+        @Setup
+        public void setup() {
+            arr = new IntWrapper[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new IntWrapper(new Val2(i));
+            }
+        }
+    }
+
+    public static class Int3State extends IntStateBase {
+        @Setup
+        public void setup() {
+            arr = new IntWrapper[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new IntWrapper(new Val3(i));
+            }
+        }
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceVal(Val1Wrapper[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].f.my_method();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceRef(Ref1Wrapper[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].f.my_method();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceInt(IntWrapper[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].f.my_method();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Val(Val1State st0, Val1State st1, Val1State st2, Val1State st3, Val1State st4, Val1State st5) {
+        return reduceVal(st0.arr) +
+                reduceVal(st1.arr) +
+                reduceVal(st2.arr) +
+                reduceVal(st3.arr) +
+                reduceVal(st4.arr) +
+                reduceVal(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Ref(Ref1State st0, Ref1State st1, Ref1State st2, Ref1State st3, Ref1State st4, Ref1State st5) {
+        return reduceRef(st0.arr) +
+                reduceRef(st1.arr) +
+                reduceRef(st2.arr) +
+                reduceRef(st3.arr) +
+                reduceRef(st4.arr) +
+                reduceRef(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int(Int1State st0, Int1State st1, Int1State st2, Int1State st3, Int1State st4, Int1State st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int(Int1State st0, Int2State st1, Int1State st2, Int2State st3, Int1State st4, Int2State st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int(Int1State st0, Int2State st1, Int3State st2, Int1State st3, Int2State st4, Int3State st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/invoke/InlineField1.java b/test/micro/org/openjdk/bench/valhalla/invoke/InlineField1.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/invoke/InlineField1.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.invoke;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class InlineField1 {
+
+    public static final int SIZE = 128;
+
+    public interface MyInterface {
+        public int my_method();
+    }
+
+    public static value class Val1 implements MyInterface {
+        public final int f0;
+        public Val1(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+        public int my_method() {
+            return f0;
+        }
+    }
+
+    public static value class Val2 implements MyInterface {
+        public final int f0;
+        public Val2(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+        public int my_method() {
+            return f0;
+        }
+    }
+
+    public static value class Val3 implements MyInterface {
+        public final int f0;
+        public Val3(int f0) {
+            this.f0 = f0;
+        }
+        @Override
+        @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+        public int my_method() {
+            return f0;
+        }
+    }
+
+    public static class Val1Wrapper {
+        public Val1 f;
+
+        public Val1Wrapper(Val1 f) {
+            this.f = f;
+        }
+    }
+
+    public static class Ref1Wrapper {
+        public Val1 f;
+
+        public Ref1Wrapper(Val1 f) {
+            this.f = f;
+        }
+    }
+
+    public static class IntWrapper {
+        public MyInterface f;
+
+        public IntWrapper(MyInterface f) {
+            this.f = f;
+        }
+    }
+
+    @State(Scope.Thread)
+    public static class Val1State {
+        public Val1Wrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new Val1Wrapper[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Val1Wrapper(new Val1(i));
+            }
+        }
+    }
+
+    @State(Scope.Thread)
+    public static class Ref1State {
+        public Ref1Wrapper[] arr;
+        @Setup
+        public void setup() {
+            arr = new Ref1Wrapper[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new Ref1Wrapper(new Val1(i));
+            }
+        }
+    }
+
+    @State(Scope.Thread)
+    public static class IntStateBase {
+        public IntWrapper[] arr;
+    }
+
+    public static class Int1State extends IntStateBase {
+        @Setup
+        public void setup() {
+            arr = new IntWrapper[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new IntWrapper(new Val1(i));
+            }
+        }
+    }
+
+    public static class Int2State extends IntStateBase {
+        @Setup
+        public void setup() {
+            arr = new IntWrapper[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new IntWrapper(new Val2(i));
+            }
+        }
+    }
+
+    public static class Int3State extends IntStateBase {
+        @Setup
+        public void setup() {
+            arr = new IntWrapper[SIZE];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new IntWrapper(new Val3(i));
+            }
+        }
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceVal(Val1Wrapper[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].f.my_method();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceRef(Ref1Wrapper[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].f.my_method();
+        }
+        return r;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int reduceInt(IntWrapper[] arr) {
+        int r = 0;
+        for (int i = 0; i < arr.length; i++) {
+            r += arr[i].f.my_method();
+        }
+        return r;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Val(Val1State st0, Val1State st1, Val1State st2, Val1State st3, Val1State st4, Val1State st5) {
+        return reduceVal(st0.arr) +
+                reduceVal(st1.arr) +
+                reduceVal(st2.arr) +
+                reduceVal(st3.arr) +
+                reduceVal(st4.arr) +
+                reduceVal(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Ref(Ref1State st0, Ref1State st1, Ref1State st2, Ref1State st3, Ref1State st4, Ref1State st5) {
+        return reduceRef(st0.arr) +
+                reduceRef(st1.arr) +
+                reduceRef(st2.arr) +
+                reduceRef(st3.arr) +
+                reduceRef(st4.arr) +
+                reduceRef(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target1_Int(Int1State st0, Int1State st1, Int1State st2, Int1State st3, Int1State st4, Int1State st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target2_Int(Int1State st0, Int2State st1, Int1State st2, Int2State st3, Int1State st4, Int2State st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(SIZE * 6)
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    public int target3_Int(Int1State st0, Int2State st1, Int3State st2, Int1State st3, Int2State st4, Int3State st5) {
+        return reduceInt(st0.arr) +
+                reduceInt(st1.arr) +
+                reduceInt(st2.arr) +
+                reduceInt(st3.arr) +
+                reduceInt(st4.arr) +
+                reduceInt(st5.arr);
+    }
+
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/invoke/InlineRec.java b/test/micro/org/openjdk/bench/valhalla/invoke/InlineRec.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/invoke/InlineRec.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.invoke;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class InlineRec {
+
+    @Param("100")
+    public int depth;
+
+    public static value class V {
+        final int v;
+
+        public V(int v) {
+            this.v = v;
+        }
+
+        public V addL(V y) {
+            return this.v == 0 ? y : new V(this.v - 1).addL(new V(y.v + 1));
+        }
+
+        public V addR(V y) {
+            return y.v == 0 ? this : new V(this.v + 1).addR(new V(y.v - 1));
+        }
+    }
+
+    public static V s_addL(V x, V y) {
+        return x.v == 0 ? y : s_addL(new V(x.v - 1), new V(y.v + 1));
+    }
+
+    public static V s_addR(V x, V y) {
+        return y.v == 0 ? x : s_addR(new V(x.v + 1), new V(y.v - 1));
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int statL() {
+        return s_addL(new V(depth), new V(depth)).v;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int statR() {
+        return s_addR(new V(depth), new V(depth)).v;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int instL() {
+        return new V(depth).addL(new V(depth)).v;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int instR() {
+        return new V(depth).addR(new V(depth)).v;
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/matrix/Base.java b/test/micro/org/openjdk/bench/valhalla/matrix/Base.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/matrix/Base.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2016, 2020, 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 org.openjdk.bench.valhalla.matrix;
+
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@OutputTimeUnit(TimeUnit.MICROSECONDS)
+@Warmup(iterations = 3, time = 3)
+@Measurement(iterations = 3, time = 8)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class Base {
+
+
+    @Param({
+            "20",
+            "100",
+            "360",
+    })
+    public int size;
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/matrix/Identity.java b/test/micro/org/openjdk/bench/valhalla/matrix/Identity.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/matrix/Identity.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.matrix;
+
+import org.openjdk.bench.valhalla.types.Complex;
+import org.openjdk.bench.valhalla.types.RComplex;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Setup;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public abstract class Identity extends Base {
+
+    public static final Complex IZERO = new RComplex(0,0);
+    public static final RComplex RZERO = new RComplex(0,0);
+
+    private static void populate(Complex[][] m) {
+        int size = m.length;
+        for (int i = 0; i < size; i++) {
+            for (int j = 0; j < size; j++) {
+                m[i][j] = new RComplex(ThreadLocalRandom.current().nextDouble(), ThreadLocalRandom.current().nextDouble());
+            }
+        }
+    }
+
+    public static class Ref extends Identity {
+        RComplex[][] A;
+        RComplex[][] B;
+
+        @Setup
+        public void setup() {
+            A = new RComplex[size][size];
+            populate(A);
+            B = new RComplex[size][size];
+            populate(B);
+        }
+
+        @Benchmark
+        @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+        public RComplex[][] multiply() {
+            int size = A.length;
+            RComplex[][] R = new RComplex[size][size];
+            for (int i = 0; i < size; i++) {
+                for (int j = 0; j < size; j++) {
+                    RComplex s = RZERO;
+                    for (int k = 0; k < size; k++) {
+                        s = s.add(A[i][k].mul(B[k][j]));
+                    }
+                    R[i][j] = s;
+                }
+            }
+            return R;
+        }
+    }
+
+    public static class Int extends Identity {
+        Complex[][] A;
+        Complex[][] B;
+
+        @Setup
+        public void setup() {
+            A = new Complex[size][size];
+            populate(A);
+            B = new Complex[size][size];
+            populate(B);
+        }
+
+        @Benchmark
+        @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+        public Complex[][] multiply() {
+            int size = A.length;
+            Complex[][] R = new Complex[size][size];
+            for (int i = 0; i < size; i++) {
+                for (int j = 0; j < size; j++) {
+                    Complex s = IZERO;
+                    for (int k = 0; k < size; k++) {
+                        s = s.add(A[i][k].mul(B[k][j]));
+                    }
+                    R[i][j] = s;
+                }
+            }
+            return R;
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/matrix/Inline.java b/test/micro/org/openjdk/bench/valhalla/matrix/Inline.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/matrix/Inline.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.matrix;
+
+import org.openjdk.bench.valhalla.types.Complex;
+import org.openjdk.bench.valhalla.types.QComplex;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Setup;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public abstract class Inline extends Base {
+
+    public static final Complex IZERO = new QComplex(0,0);
+    public static final QComplex VZERO = new QComplex(0,0);
+    public static final QComplex RZERO = new QComplex(0,0);
+
+    private static void populate(Complex[][] m) {
+        int size = m.length;
+        for (int i = 0; i < size; i++) {
+            for (int j = 0; j < size; j++) {
+                m[i][j] = new QComplex(ThreadLocalRandom.current().nextDouble(), ThreadLocalRandom.current().nextDouble());
+            }
+        }
+    }
+
+//    public static class Ref extends Inline {
+//
+//    }
+
+    public static class Ref extends Inline {
+        QComplex[][] A;
+        QComplex[][] B;
+
+        @Setup
+        public void setup() {
+            A = new QComplex[size][size];
+            populate(A);
+            B = new QComplex[size][size];
+            populate(B);
+        }
+
+        @Benchmark
+        @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+        public QComplex[][] multiply() {
+            int size = A.length;
+            QComplex[][] R = new QComplex[size][size];
+            for (int i = 0; i < size; i++) {
+                for (int j = 0; j < size; j++) {
+                    QComplex s = RZERO;
+                    for (int k = 0; k < size; k++) {
+                        s = s.add(A[i][k].mul(B[k][j]));
+                    }
+                    R[i][j] = s;
+                }
+            }
+            return R;
+        }
+
+//        @Benchmark
+//        public QComplex[][] multiplyCacheFriendly() {
+//            int size = A.length;
+//            QComplex[][] R = new QComplex[size][size];
+//            for (int i = 0; i < size; i++) {
+//                for (int k = 0; k < size; k++) {
+//                    QComplex aik = A[i][k];
+//                    for (int j = 0; j < size; j++) {
+//                        R[i][j] = R[i][j].add(aik.mul(B[k][j]));
+//                    }
+//                }
+//            }
+//            return R;
+//        }
+
+    }
+
+    public static class Val extends Inline {
+        QComplex[][] A;
+        QComplex[][] B;
+
+        @Setup
+        public void setup() {
+            A = new QComplex[size][size];
+            populate(A);
+            B = new QComplex[size][size];
+            populate(B);
+        }
+
+        @Benchmark
+        @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+        public QComplex[][] multiply() {
+            int size = A.length;
+            QComplex[][] R = new QComplex[size][size];
+            for (int i = 0; i < size; i++) {
+                for (int j = 0; j < size; j++) {
+                    QComplex s = VZERO;
+                    for (int k = 0; k < size; k++) {
+                        s = s.add(A[i][k].mul(B[k][j]));
+                    }
+                    R[i][j] = s;
+                }
+            }
+            return R;
+        }
+
+//        @Benchmark
+//        public QComplex[][] multiplyCacheFriendly() {
+//            int size = A.length;
+//            QComplex[][] R = new QComplex[size][size];
+//            for (int i = 0; i < size; i++) {
+//                for (int k = 0; k < size; k++) {
+//                    QComplex aik = A[i][k];
+//                    for (int j = 0; j < size; j++) {
+//                        R[i][j] = R[i][j].add(aik.mul(B[k][j]));
+//                    }
+//                }
+//            }
+//            return R;
+//        }
+
+    }
+
+    public static class Int extends Inline {
+        Complex[][] A;
+        Complex[][] B;
+
+        @Setup
+        public void setup() {
+            A = new Complex[size][size];
+            populate(A);
+            B = new Complex[size][size];
+            populate(B);
+        }
+
+        @Benchmark
+        @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+        public Complex[][] multiply() {
+            int size = A.length;
+            Complex[][] R = new Complex[size][size];
+            for (int i = 0; i < size; i++) {
+                for (int j = 0; j < size; j++) {
+                    Complex s = IZERO;
+                    for (int k = 0; k < size; k++) {
+                        s = s.add(A[i][k].mul(B[k][j]));
+                    }
+                    R[i][j] = s;
+                }
+            }
+            return R;
+        }
+    }
+
+    public static class ICov extends Inline {
+        Complex[][] A;
+        Complex[][] B;
+
+        @Setup
+        public void setup() {
+            A = new QComplex[size][size];
+            populate(A);
+            B = new QComplex[size][size];
+            populate(B);
+        }
+
+        @Benchmark
+        @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+        public Complex[][] multiply() {
+            int size = A.length;
+            Complex[][] R = new QComplex[size][size];
+            for (int i = 0; i < size; i++) {
+                for (int j = 0; j < size; j++) {
+                    Complex s = IZERO;
+                    for (int k = 0; k < size; k++) {
+                        s = s.add(A[i][k].mul(B[k][j]));
+                    }
+                    R[i][j] = s;
+                }
+            }
+            return R;
+        }
+
+//        @Benchmark
+//        public Complex[][] multiplyCacheFriendly() {
+//            int size = A.length;
+//            Complex[][] R = new QComplex[size][size];
+//            for (int i = 0; i < size; i++) {
+//                for (int k = 0; k < size; k++) {
+//                    Complex aik = A[i][k];
+//                    for (int j = 0; j < size; j++) {
+//                        R[i][j] = R[i][j].add(aik.mul(B[k][j]));
+//                    }
+//                }
+//            }
+//            return R;
+//        }
+
+    }
+
+    public static class RCov extends Inline {
+        QComplex[][] A;
+        QComplex[][] B;
+
+        @Setup
+        public void setup() {
+            A = new QComplex[size][size];
+            populate(A);
+            B = new QComplex[size][size];
+            populate(B);
+        }
+
+        @Benchmark
+        @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+        public QComplex[][] multiply() {
+            int size = A.length;
+            QComplex[][] R = new QComplex[size][size];
+            for (int i = 0; i < size; i++) {
+                for (int j = 0; j < size; j++) {
+                    QComplex s = RZERO;
+                    for (int k = 0; k < size; k++) {
+                        s = s.add(A[i][k].mul(B[k][j]));
+                    }
+                    R[i][j] = s;
+                }
+            }
+            return R;
+        }
+
+//        @Benchmark
+//        public Complex[][] multiplyCacheFriendly() {
+//            int size = A.length;
+//            Complex[][] R = new QComplex[size][size];
+//            for (int i = 0; i < size; i++) {
+//                for (int k = 0; k < size; k++) {
+//                    Complex aik = A[i][k];
+//                    for (int j = 0; j < size; j++) {
+//                        R[i][j] = R[i][j].add(aik.mul(B[k][j]));
+//                    }
+//                }
+//            }
+//            return R;
+//        }
+
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/matrix/Primitive.java b/test/micro/org/openjdk/bench/valhalla/matrix/Primitive.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/matrix/Primitive.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2016, 2020, 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 org.openjdk.bench.valhalla.matrix;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Setup;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class Primitive extends Base {
+
+    double[][] A;
+    double[][] B;
+
+    @Setup
+    public void setup() {
+        A = populate(new double[size][size*2]);
+        B = populate(new double[size][size*2]);
+    }
+
+    private double[][] populate(double[][] m) {
+        int size = m.length;
+        for (int i = 0; i < size; i++) {
+            for (int j = 0; j < size; j++) {
+                m[i][j*2+0] = ThreadLocalRandom.current().nextDouble();
+                m[i][j*2+1] = ThreadLocalRandom.current().nextDouble();
+            }
+        }
+        return m;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public double[][] multiply() {
+        int size = A.length;
+        double[][] R = new double[size][size * 2];
+        for (int i = 0; i < size; i++) {
+            for (int j = 0; j < size; j++) {
+                double s_re = 0;
+                double s_im = 0;
+                for (int k = 0; k < size; k++) {
+                    double are = A[i][k * 2 + 0];
+                    double aim = A[i][k * 2 + 1];
+                    double bre = B[k][j * 2 + 0];
+                    double bim = B[k][j * 2 + 1];
+                    s_re += are * bre - aim * bim;
+                    s_im += are * bim + bre * aim;
+                }
+                R[i][j * 2 + 0] = s_re;
+                R[i][j * 2 + 1] = s_im;
+            }
+        }
+        return R;
+    }
+
+//    @Benchmark
+//    public double[][] multiplyCacheFriendly() {
+//        int size = A.length;
+//        double[][] R = new double[size][size * 2];
+//        for (int i = 0; i < size; i++) {
+//            for (int k = 0; k < size; k++) {
+//                double are = A[i][k * 2 + 0];
+//                double aim = A[i][k * 2 + 1];
+//                for (int j = 0; j < size; j++) {
+//                    double bre = B[k][j * 2 + 0];
+//                    double bim = B[k][j * 2 + 1];
+//                    R[i][j * 2 + 0] += are * bre - aim * bim;
+//                    R[i][j * 2 + 1] += are * bim + bre * aim;
+//                }
+//            }
+//        }
+//        return R;
+//    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/ArrayListInt.java b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/ArrayListInt.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/ArrayListInt.java
@@ -0,0 +1,640 @@
+/*
+ * 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.  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.
+ */
+
+package org.openjdk.bench.valhalla.sandbox.corelibs;
+
+import java.util.Arrays;
+import java.util.ConcurrentModificationException;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.function.IntConsumer;
+import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
+
+/**
+ * Resizable-array implementation like {@code ArrayList<int>}.
+ */
+public class ArrayListInt
+//        extends AbstractList<E>
+//        implements List<int>, RandomAccess, Cloneable, java.io.Serializable
+{
+    @java.io.Serial
+    private static final long serialVersionUID = 8683452581122892189L;
+
+    /**
+     *new ()   initial capacity.
+     */
+    private static final int DEFAULT_CAPACITY = 10;
+
+    /**
+     * Shared empty array instance used for empty instances.
+     */
+    private static final int[] EMPTY_ELEMENTDATA = {};
+
+    /**
+     * Shared empty array instance used for default sized empty instances. We
+     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
+     * first element is added.
+     */
+    private static final int[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new int[0];
+
+    /**
+     * The array buffer into which the elements of the ArrayList are stored.
+     * The capacity of the ArrayList is the length of this array buffer. Any
+     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
+     * will be expanded to DEFAULT_CAPACITY when the first element is added.
+     */
+    transient int[] elementData; // non-private to simplify nested class access
+
+    protected transient int modCount = 0;
+
+    /**
+     * The size of the ArrayList (the number of elements it contains).
+     *
+     * @serial
+     */
+    private int size;
+
+    /**
+     * Constructs an empty list with the specified initial capacity.
+     *
+     * @param  initialCapacity  the initial capacity of the list
+     * @throws IllegalArgumentException if the specified initial capacity
+     *         is negative
+     */
+    public ArrayListInt(int initialCapacity) {
+        if (initialCapacity > 0) {
+            this.elementData = new int[initialCapacity];
+        } else if (initialCapacity == 0) {
+            this.elementData = EMPTY_ELEMENTDATA;
+        } else {
+            throw new IllegalArgumentException("Illegal Capacity: "+
+                                               initialCapacity);
+        }
+    }
+
+    /**
+     * Constructs an empty list with an initial capacity of ten.
+     */
+    public ArrayListInt() {
+        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
+    }
+
+    /**
+     * Trims the capacity of this {@code ArrayList} instance to be the
+     * list's current size.  An application can use this operation to minimize
+     * the storage of an {@code ArrayList} instance.
+     */
+    public void trimToSize() {
+        modCount++;
+        if (size < elementData.length) {
+            elementData = (size == 0)
+              ? EMPTY_ELEMENTDATA
+              : Arrays.copyOf(elementData, size);
+        }
+    }
+
+    /**
+     * Increases the capacity of this {@code ArrayList} instance, if
+     * necessary, to ensure that it can hold at least the number of elements
+     * specified by the minimum capacity argument.
+     *
+     * @param minCapacity the desired minimum capacity
+     */
+    public void ensureCapacity(int minCapacity) {
+        if (minCapacity > elementData.length
+            && !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
+                 && minCapacity <= DEFAULT_CAPACITY)) {
+            modCount++;
+            grow(minCapacity);
+        }
+    }
+
+    /**
+     * Increases the capacity to ensure that it can hold at least the
+     * number of elements specified by the minimum capacity argument.
+     *
+     * @param minCapacity the desired minimum capacity
+     * @throws OutOfMemoryError if minCapacity is less than zero
+     */
+    private int[] grow(int minCapacity) {
+        int oldCapacity = elementData.length;
+        if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
+            int newCapacity = newLength(oldCapacity,
+                    minCapacity - oldCapacity, /* minimum growth */
+                    oldCapacity >> 1           /* preferred growth */);
+            return elementData = Arrays.copyOf(elementData, newCapacity);
+        } else {
+            return elementData = new int[Math.max(DEFAULT_CAPACITY, minCapacity)];
+        }
+    }
+
+    private int[] grow() {
+        return grow(size + 1);
+    }
+
+    /**
+     * Returns the number of elements in this list.
+     *
+     * @return the number of elements in this list
+     */
+    public int size() {
+        return size;
+    }
+
+    /**
+     * Returns {@code true} if this list contains no elements.
+     *
+     * @return {@code true} if this list contains no elements
+     */
+    public boolean isEmpty() {
+        return size == 0;
+    }
+
+    /**
+     * Returns {@code true} if this list contains the specified element.
+     * More formally, returns {@code true} if and only if this list contains
+     * at least one element {@code e} such that
+     * {@code Objects.equals(o, e)}.
+     *
+     * @param o element whose presence in this list is to be tested
+     * @return {@code true} if this list contains the specified element
+     */
+    public boolean contains(int o) {
+        return indexOf(o) >= 0;
+    }
+
+    /**
+     * Returns the index of the first occurrence of the specified element
+     * in this list, or -1 if this list does not contain the element.
+     * More formally, returns the lowest index {@code i} such that
+     * {@code Objects.equals(o, get(i))},
+     * or -1 if there is no such index.
+     */
+    public int indexOf(int o) {
+        return indexOfRange(o, 0, size);
+    }
+
+    int indexOfRange(int o, int start, int end) {
+        int[] es = elementData;
+        for (int i = start; i < end; i++) {
+            if (o == es[i]) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Returns the index of the last occurrence of the specified element
+     * in this list, or -1 if this list does not contain the element.
+     * More formally, returns the highest index {@code i} such that
+     * {@code Objects.equals(o, get(i))},
+     * or -1 if there is no such index.
+     */
+    public int lastIndexOf(int o) {
+        return lastIndexOfRange(o, 0, size);
+    }
+
+    int lastIndexOfRange(int o, int start, int end) {
+        int[] es = elementData;
+            {
+            for (int i = end - 1; i >= start; i--) {
+                if (o == es[i]) {
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Returns an array containing all of the elements in this list
+     * in proper sequence (from first to last element).
+     *
+     * <p>The returned array will be "safe" in that no references to it are
+     * maintained by this list.  (In other words, this method must allocate
+     * a new array).  The caller is thus free to modify the returned array.
+     *
+     * <p>This method acts as bridge between array-based and collection-based
+     * APIs.
+     *
+     * @return an array containing all of the elements in this list in
+     *         proper sequence
+     */
+    public int[] toArray() {
+        return Arrays.copyOf(elementData, size);
+    }
+
+    // Positional Access Operations
+
+    @SuppressWarnings("unchecked")
+    int elementData(int index) {
+        return (int) elementData[index];
+    }
+
+    @SuppressWarnings("unchecked")
+    static int elementAt(int[] es, int index) {
+        return es[index];
+    }
+
+    /**
+     * Returns the element at the specified position in this list.
+     *
+     * @param  index index of the element to return
+     * @return the element at the specified position in this list
+     * @throws IndexOutOfBoundsException {@inheritDoc}
+     */
+    public int get(int index) {
+        Objects.checkIndex(index, size);
+        return elementData(index);
+    }
+
+    /**
+     * Replaces the element at the specified position in this list with
+     * the specified element.
+     *
+     * @param index index of the element to replace
+     * @param element element to be stored at the specified position
+     * @return the element previously at the specified position
+     * @throws IndexOutOfBoundsException {@inheritDoc}
+     */
+    public int set(int index, int element) {
+        Objects.checkIndex(index, size);
+        int oldValue = elementData(index);
+        elementData[index] = element;
+        return oldValue;
+    }
+
+    /**
+     * This helper method split out from add(int) to keep method
+     * bytecode size under 35 (the -XX:MaxInlineSize default value),
+     * which helps when add(int) is called in a C1-compiled loop.
+     */
+    private void add(int e, int[] elementData, int s) {
+        if (s == elementData.length)
+            elementData = grow();
+        elementData[s] = e;
+        size = s + 1;
+    }
+
+    /**
+     * Appends the specified element to the end of this list.
+     *
+     * @param e element to be appended to this list
+     * @return {@code true} (as specified by {@link Collection#add})
+     */
+    public boolean add(int e) {
+        modCount++;
+        add(e, elementData, size);
+        return true;
+    }
+
+    /**
+     * Inserts the specified element at the specified position in this
+     * list. Shifts the element currently at that position (if any) and
+     * any subsequent elements to the right (adds one to their indices).
+     *
+     * @param index index at which the specified element is to be inserted
+     * @param element element to be inserted
+     * @throws IndexOutOfBoundsException {@inheritDoc}
+     */
+    public void add(int index, int element) {
+        rangeCheckForAdd(index);
+        modCount++;
+        final int s;
+        int[] elementData;
+        if ((s = size) == (elementData = this.elementData).length)
+            elementData = grow();
+        System.arraycopy(elementData, index,
+                         elementData, index + 1,
+                         s - index);
+        elementData[index] = element;
+        size = s + 1;
+    }
+
+    /**
+     * Removes the element at the specified position in this list.
+     * Shifts any subsequent elements to the left (subtracts one from their
+     * indices).
+     *
+     * @param index the index of the element to be removed
+     * @return the element that was removed from the list
+     * @throws IndexOutOfBoundsException {@inheritDoc}
+     */
+    public int remove(int index) {
+        Objects.checkIndex(index, size);
+        final int[] es = elementData;
+
+        int oldValue = (int) es[index];
+        fastRemove(es, index);
+
+        return oldValue;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (!(o instanceof ArrayListInt)) {
+            return false;
+        }
+
+        final int expectedModCount = modCount;
+        // ArrayList can be subclassed and given arbitrary behavior, but we can
+        // still deal with the common case where o is ArrayList precisely
+        boolean equal = equalsArrayList((ArrayListInt) o);
+
+        checkForComodification(expectedModCount);
+        return equal;
+    }
+
+    private boolean equalsArrayList(ArrayListInt other) {
+        final int otherModCount = other.modCount;
+        final int s = size;
+        boolean equal;
+        if (equal = (s == other.size)) {
+            final int[] otherEs = other.elementData;
+            final int[] es = elementData;
+            if (s > es.length || s > otherEs.length) {
+                throw new ConcurrentModificationException();
+            }
+            for (int i = 0; i < s; i++) {
+                if (es[i] != otherEs[i]) {
+                    equal = false;
+                    break;
+                }
+            }
+        }
+        other.checkForComodification(otherModCount);
+        return equal;
+    }
+
+    private void checkForComodification(final int expectedModCount) {
+        if (modCount != expectedModCount) {
+            throw new ConcurrentModificationException();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int hashCode() {
+        int expectedModCount = modCount;
+        int hash = hashCodeRange(0, size);
+        checkForComodification(expectedModCount);
+        return hash;
+    }
+
+    int hashCodeRange(int from, int to) {
+        final int[] es = elementData;
+        if (to > es.length) {
+            throw new ConcurrentModificationException();
+        }
+        int hashCode = 1;
+        for (int i = from; i < to; i++) {
+            int e = es[i];
+            hashCode = 31 * hashCode + e;
+        }
+        return hashCode;
+    }
+
+    /**
+     * Removes the first occurrence of the specified element from this list,
+     * if it is present.  If the list does not contain the element, it is
+     * unchanged.  More formally, removes the element with the lowest index
+     * {@code i} such that
+     * {@code Objects.equals(o, get(i))}
+     * (if such an element exists).  Returns {@code true} if this list
+     * contained the specified element (or equivalently, if this list
+     * changed as a result of the call).
+     *
+     * @param o element to be removed from this list, if present
+     * @return {@code true} if this list contained the specified element
+     */
+    public boolean removeC(int o) {
+        final int[] es = elementData;
+        final int size = this.size;
+        int i = 0;
+        found:
+        {
+//            if (o == null) {
+//                for (; i < size; i++)
+//                    if (es[i] == null)
+//                        break found;
+//            } else
+                {
+                for (; i < size; i++)
+                    if (o == es[i])
+                        break found;
+            }
+            return false;
+        }
+        fastRemove(es, i);
+        return true;
+    }
+
+    /**
+     * Private remove method that skips bounds checking and does not
+     * return the value removed.
+     */
+    private void fastRemove(int[] es, int i) {
+        modCount++;
+        final int newSize;
+        if ((newSize = size - 1) > i)
+            System.arraycopy(es, i + 1, es, i, newSize - i);
+        es[size = newSize] = 0;
+    }
+
+    /**
+     * Removes all of the elements from this list.  The list will
+     * be empty after this call returns.
+     */
+    public void clear() {
+        modCount++;
+        final int[] es = elementData;
+        for (int to = size, i = size = 0; i < to; i++)
+            es[i] = 0;
+    }
+
+    /**
+     * Removes from this list all of the elements whose index is between
+     * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
+     * Shifts any succeeding elements to the left (reduces their index).
+     * This call shortens the list by {@code (toIndex - fromIndex)} elements.
+     * (If {@code toIndex==fromIndex}, this operation has no effect.)
+     *
+     * @throws IndexOutOfBoundsException if {@code fromIndex} or
+     *         {@code toIndex} is out of range
+     *         ({@code fromIndex < 0 ||
+     *          toIndex > size() ||
+     *          toIndex < fromIndex})
+     */
+    protected void removeRange(int fromIndex, int toIndex) {
+        if (fromIndex > toIndex) {
+            throw new IndexOutOfBoundsException(
+                    outOfBoundsMsg(fromIndex, toIndex));
+        }
+        modCount++;
+        shiftTailOverGap(elementData, fromIndex, toIndex);
+    }
+
+    /** Erases the gap from lo to hi, by sliding down following elements. */
+    private void shiftTailOverGap(int[] es, int lo, int hi) {
+        System.arraycopy(es, hi, es, lo, size - hi);
+        for (int to = size, i = (size -= hi - lo); i < to; i++)
+            es[i] = 0;
+    }
+
+    /**
+     * A version of rangeCheck used by add and addAll.
+     */
+    private void rangeCheckForAdd(int index) {
+        if (index > size || index < 0)
+            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
+    }
+
+    /**
+     * Constructs an IndexOutOfBoundsException detail message.
+     * Of the many possible refactorings of the error handling code,
+     * this "outlining" performs best with both server and client VMs.
+     */
+    private String outOfBoundsMsg(int index) {
+        return "Index: "+index+", Size: "+size;
+    }
+
+    /**
+     * A version used in checking (fromIndex > toIndex) condition
+     */
+    private static String outOfBoundsMsg(int fromIndex, int toIndex) {
+        return "From Index: " + fromIndex + " > To Index: " + toIndex;
+    }
+
+    /**
+     * Returns an iterator over the elements in this list in proper sequence.
+     *
+     * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
+     *
+     * @return an iterator over the elements in this list in proper sequence
+     */
+    public IntItr iterator() {
+        return new IntItr();
+    }
+
+    /**
+     * An optimized version of AbstractList.Itr
+     */
+    private class IntItr {
+        int cursor;       // index of next element to return
+        int lastRet = -1; // index of last element returned; -1 if no such
+        int expectedModCount = modCount;
+
+        // prevent creating a synthetic constructor
+        IntItr() {}
+
+        public boolean hasNext() {
+            return cursor != size;
+        }
+
+        @SuppressWarnings("unchecked")
+        public int next() {
+            checkForComodification();
+            int i = cursor;
+            if (i >= size)
+                throw new NoSuchElementException();
+            int[] elementData = ArrayListInt.this.elementData;
+            if (i >= elementData.length)
+                throw new ConcurrentModificationException();
+            cursor = i + 1;
+            return (int) elementData[lastRet = i];
+        }
+
+        public void remove() {
+            if (lastRet < 0)
+                throw new IllegalStateException();
+            checkForComodification();
+
+            try {
+                ArrayListInt.this.remove(lastRet);
+                cursor = lastRet;
+                lastRet = -1;
+                expectedModCount = modCount;
+            } catch (IndexOutOfBoundsException ex) {
+                throw new ConcurrentModificationException();
+            }
+        }
+
+        public void forEachRemaining(IntConsumer action) {
+            Objects.requireNonNull(action);
+            final int size = ArrayListInt.this.size;
+            int i = cursor;
+            if (i < size) {
+                final int[] es = elementData;
+                if (i >= es.length)
+                    throw new ConcurrentModificationException();
+                for (; i < size && modCount == expectedModCount; i++)
+                    action.accept(elementAt(es, i));
+                // update once at end to reduce heap write traffic
+                cursor = i;
+                lastRet = i - 1;
+                checkForComodification();
+            }
+        }
+
+        final void checkForComodification() {
+            if (modCount != expectedModCount)
+                throw new ConcurrentModificationException();
+        }
+    }
+
+    static final int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
+
+    static int newLength(int oldLength, int minGrowth, int prefGrowth) {
+        // assert oldLength >= 0
+        // assert minGrowth > 0
+
+        int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
+        if (newLength - MAX_ARRAY_LENGTH <= 0) {
+            return newLength;
+        }
+        return hugeLength(oldLength, minGrowth);
+    }
+
+    private static int hugeLength(int oldLength, int minGrowth) {
+        int minLength = oldLength + minGrowth;
+        if (minLength < 0) { // overflow
+            throw new OutOfMemoryError("Required array length too large");
+        }
+        if (minLength <= MAX_ARRAY_LENGTH) {
+            return MAX_ARRAY_LENGTH;
+        }
+        return Integer.MAX_VALUE;
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/ArrayListOfIntBench.java b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/ArrayListOfIntBench.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/ArrayListOfIntBench.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.sandbox.corelibs;
+
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.TearDown;
+
+import org.openjdk.jmh.infra.Blackhole;
+
+import java.util.ArrayList;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Measure performance of List of Integer operations.
+ * - Set all of int from a set of random numbers (with a seed)
+ * - Get all
+ * - Shuffle the array
+ * - Sort the array
+ */
+
+
+@Fork(value = 3, jvmArgsAppend = "--enable-preview")
+@Warmup(iterations = 5, time = 2)
+@Measurement(iterations = 5, time = 3)
+@OutputTimeUnit(TimeUnit.MICROSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class ArrayListOfIntBench {
+
+    @Param({
+            "100",
+            "1000000",
+    })
+    public int size;
+
+    ArrayListInt arrayListInt;
+    ArrayListPrimitiveInt arrayListPrimitiveInt;
+    ArrayList<Integer> arrayListOfInteger;
+    ArrayList<PrimitiveInt> arrayListOfPrimitiveInt;
+    Random random;
+
+    @Setup
+    public void setup() {
+        arrayListInt = new ArrayListInt(size);
+        for (int i = 0; i < size; i++) {
+            arrayListInt.add(i, i);
+        }
+
+        arrayListPrimitiveInt = new ArrayListPrimitiveInt(size);
+        for (int i = 0; i < size; i++) {
+            arrayListPrimitiveInt.add(i, new PrimitiveInt(i));
+        }
+
+        arrayListOfInteger = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
+            arrayListOfInteger.add(i, i);
+        }
+
+        arrayListOfPrimitiveInt = new ArrayList<PrimitiveInt>(size);
+        for (int i = 0; i < size; i++) {
+            arrayListOfPrimitiveInt.add(i, new PrimitiveInt(i));
+        }
+
+        random = new Random(42);
+    }
+
+    @Benchmark
+    public Object appendListInt() {
+        ArrayListInt list = new ArrayListInt(size);
+        for (int i = 0; i < size; i++) {
+            list.add(i);
+        }
+        return list;
+    }
+
+    @Benchmark
+    public Object appendListPrimitiveInt() {
+        ArrayListPrimitiveInt list = new ArrayListPrimitiveInt(size);
+        for (int i = 0; i < size; i++) {
+            list.add(new PrimitiveInt(i));
+        }
+        return list;
+    }
+
+    @Benchmark
+    public Object appendListOfInteger() {
+        ArrayList<Integer> list = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
+            list.add(i);
+        }
+        return list;
+    }
+
+    @Benchmark
+    public Object appendListOfPrimitiveInt() {
+        ArrayList<PrimitiveInt> list = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
+            list.add(new PrimitiveInt(i));
+        }
+        return list;
+    }
+
+
+    @Benchmark
+    public int sumListInt() {
+        int sum = 0;
+        for (int i = 0; i < size; i++) {
+            sum += arrayListInt.get(i);
+        }
+        return sum;
+    }
+
+    @Benchmark
+    public int sumListOfInteger() {
+        int sum = 0;
+        for (int i = 0; i < size; i++) {
+            sum += arrayListOfInteger.get(i);
+        }
+        return sum;
+    }
+
+
+    @Benchmark
+    public int sumListPrimitiveInt() {
+        int sum = 0;
+        for (int i = 0; i < size; i++) {
+            sum += arrayListPrimitiveInt.get(i).value();
+        }
+        return sum;
+    }
+
+    @Benchmark
+    public int sumListOfPrimitiveInt() {
+        int sum = 0;
+        for (int i = 0; i < size; i++) {
+            sum += arrayListOfPrimitiveInt.get(i).value();
+        }
+        return sum;
+    }
+
+    @Benchmark
+    public int thrashListInt() {
+        final ArrayListInt list = arrayListInt;
+
+        int sum = 0;
+        for (int i = 0; i < 1000; i++) {
+            int ndx = (random.nextInt() & 0x7fffffff) % list.size();    // positive
+            if (list.size() == size) {
+                list.remove(ndx);
+            } else {
+                list.add(ndx);
+            }
+            sum += ndx;
+        }
+        return sum;
+    }
+
+    @Benchmark
+    public int thrashListPrimitiveInt() {
+        final ArrayListPrimitiveInt list = arrayListPrimitiveInt;
+        int sum = 0;
+        for (int i = 0; i < 1000; i++) {
+            int ndx = (random.nextInt() & 0x7fffffff) % list.size();    // positive
+            if (list.size() == size) {
+                list.remove(ndx);
+            } else {
+                list.add(ndx, new PrimitiveInt(ndx));
+            }
+            sum += ndx;
+        }
+        return sum;
+    }
+
+    @Benchmark
+    public int thrashListOfInteger() {
+        final ArrayList<Integer> list = arrayListOfInteger;
+        int sum = 0;
+        for (int i = 0; i < 1000; i++) {
+            int ndx = (random.nextInt() & 0x7fffffff) % list.size();    // positive
+            if (list.size() == size) {
+                list.remove(ndx);
+            } else {
+                list.add(ndx);
+            }
+            sum += ndx;
+        }
+        return sum;
+    }
+
+
+    @Benchmark
+    public int thrashListOfPrimitiveInt() {
+        final ArrayList<PrimitiveInt> list = arrayListOfPrimitiveInt;
+        int sum = 0;
+
+        for (int i = 0; i < 1000; i++) {
+            int ndx = (random.nextInt() & 0x7fffffff) % list.size();    // positive
+            if (list.size() == size) {
+                list.remove(ndx);
+            } else {
+                list.add(ndx, new PrimitiveInt(ndx));
+            }
+            sum += ndx;
+        }
+        return sum;
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/ArrayListPrimitiveInt.java b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/ArrayListPrimitiveInt.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/ArrayListPrimitiveInt.java
@@ -0,0 +1,678 @@
+/*
+ * 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.  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.
+ */
+
+package org.openjdk.bench.valhalla.sandbox.corelibs;
+
+import java.util.Arrays;
+import java.util.ConcurrentModificationException;
+import java.util.Objects;
+import java.util.NoSuchElementException;
+import java.util.function.Consumer;
+import java.util.function.IntConsumer;
+import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
+
+/**
+ * Resizable-array implementation like {@code ArrayList<PrimitiveInt>}.
+ */
+public class ArrayListPrimitiveInt
+//        extends AbstractList<E>
+//        implements List<int>, RandomAccess, Cloneable, java.io.Serializable
+{
+    @java.io.Serial
+    private static final long serialVersionUID = 8683452581122892189L;
+
+    /**
+     * Default initial capacity.
+     */
+    private static final int DEFAULT_CAPACITY = 10;
+
+    /**
+     * Shared empty array instance used for empty instances.
+     */
+    private static final PrimitiveInt[] EMPTY_ELEMENTDATA = {};
+
+    /**
+     * Shared empty array instance used for default sized empty instances. We
+     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
+     * first element is added.
+     */
+    private static final PrimitiveInt[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new PrimitiveInt[0];
+
+    /**
+     * The array buffer into which the elements of the ArrayList are stored.
+     * The capacity of the ArrayList is the length of this array buffer. Any
+     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
+     * will be expanded to DEFAULT_CAPACITY when the first element is added.
+     */
+    transient PrimitiveInt[] elementData; // non-private to simplify nested class access
+
+    protected transient int modCount = 0;
+
+    /**
+     * The size of the ArrayList (the number of elements it contains).
+     *
+     * @serial
+     */
+    private int size;
+
+    /**
+     * Constructs an empty list with the specified initial capacity.
+     *
+     * @param  initialCapacity  the initial capacity of the list
+     * @throws IllegalArgumentException if the specified initial capacity
+     *         is negative
+     */
+    public ArrayListPrimitiveInt(int initialCapacity) {
+        if (initialCapacity > 0) {
+            this.elementData = new PrimitiveInt[initialCapacity];
+        } else if (initialCapacity == 0) {
+            this.elementData = EMPTY_ELEMENTDATA;
+        } else {
+            throw new IllegalArgumentException("Illegal Capacity: "+
+                    initialCapacity);
+        }
+    }
+
+    /**
+     * Constructs an empty list with an initial capacity of ten.
+     */
+    public ArrayListPrimitiveInt() {
+        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
+    }
+
+    /**
+     * Trims the capacity of this {@code ArrayList} instance to be the
+     * list's current size.  An application can use this operation to minimize
+     * the storage of an {@code ArrayList} instance.
+     */
+    public void trimToSize() {
+        modCount++;
+        if (size < elementData.length) {
+            elementData = (size == 0)
+                    ? EMPTY_ELEMENTDATA
+                    : (PrimitiveInt[])Arrays.copyOf(elementData, size);
+        }
+    }
+
+    /**
+     * Increases the capacity of this {@code ArrayList} instance, if
+     * necessary, to ensure that it can hold at least the number of elements
+     * specified by the minimum capacity argument.
+     *
+     * @param minCapacity the desired minimum capacity
+     */
+    public void ensureCapacity(int minCapacity) {
+        if (minCapacity > elementData.length
+                && !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
+                && minCapacity <= DEFAULT_CAPACITY)) {
+            modCount++;
+            grow(minCapacity);
+        }
+    }
+
+    /**
+     * Increases the capacity to ensure that it can hold at least the
+     * number of elements specified by the minimum capacity argument.
+     *
+     * @param minCapacity the desired minimum capacity
+     * @throws OutOfMemoryError if minCapacity is less than zero
+     */
+    private PrimitiveInt[] grow(int minCapacity) {
+        int oldCapacity = elementData.length;
+        if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
+            int newCapacity = newLength(oldCapacity,
+                    minCapacity - oldCapacity, /* minimum growth */
+                    oldCapacity >> 1           /* preferred growth */);
+            return elementData = (PrimitiveInt[])Arrays.copyOf(elementData, newCapacity);
+        } else {
+            return elementData = new PrimitiveInt[Math.max(DEFAULT_CAPACITY, minCapacity)];
+        }
+    }
+
+    private PrimitiveInt[] grow() {
+        return grow(size + 1);
+    }
+
+    /**
+     * Returns the number of elements in this list.
+     *
+     * @return the number of elements in this list
+     */
+    public int size() {
+        return size;
+    }
+
+    /**
+     * Returns {@code true} if this list contains no elements.
+     *
+     * @return {@code true} if this list contains no elements
+     */
+    public boolean isEmpty() {
+        return size == 0;
+    }
+
+    /**
+     * Returns {@code true} if this list contains the specified element.
+     * More formally, returns {@code true} if and only if this list contains
+     * at least one element {@code e} such that
+     * {@code Objects.equals(o, e)}.
+     *
+     * @param o element whose presence in this list is to be tested
+     * @return {@code true} if this list contains the specified element
+     */
+    public boolean contains(PrimitiveInt o) {
+        return indexOf(o) >= 0;
+    }
+
+    /**
+     * Returns the index of the first occurrence of the specified element
+     * in this list, or -1 if this list does not contain the element.
+     * More formally, returns the lowest index {@code i} such that
+     * {@code Objects.equals(o, get(i))},
+     * or -1 if there is no such index.
+     */
+    public int indexOf(PrimitiveInt o) {
+        return indexOfRange(o, 0, size);
+    }
+
+    int indexOfRange(PrimitiveInt o, int start, int end) {
+        PrimitiveInt[] es = elementData;
+        {
+            for (int i = start; i < end; i++) {
+                if (o == es[i]) {
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Returns the index of the last occurrence of the specified element
+     * in this list, or -1 if this list does not contain the element.
+     * More formally, returns the highest index {@code i} such that
+     * {@code Objects.equals(o, get(i))},
+     * or -1 if there is no such index.
+     */
+    public int lastIndexOf(PrimitiveInt o) {
+        return lastIndexOfRange(o, 0, size);
+    }
+
+    int lastIndexOfRange(PrimitiveInt o, int start, int end) {
+        PrimitiveInt[] es = elementData;
+        {
+            for (int i = end - 1; i >= start; i--) {
+                if (o == es[i]) {
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Returns an array containing all of the elements in this list
+     * in proper sequence (from first to last element).
+     *
+     * <p>The returned array will be "safe" in that no references to it are
+     * maintained by this list.  (In other words, this method must allocate
+     * a new array).  The caller is thus free to modify the returned array.
+     *
+     * <p>This method acts as bridge between array-based and collection-based
+     * APIs.
+     *
+     * @return an array containing all of the elements in this list in
+     *         proper sequence
+     */
+    public PrimitiveInt[] toArray() {
+        return (PrimitiveInt[])Arrays.copyOf(elementData, size);
+    }
+
+    /**
+     * Returns an array containing all of the elements in this list in proper
+     * sequence (from first to last element); the runtime type of the returned
+     * array is that of the specified array.  If the list fits in the
+     * specified array, it is returned therein.  Otherwise, a new array is
+     * allocated with the runtime type of the specified array and the size of
+     * this list.
+     *
+     * <p>If the list fits in the specified array with room to spare
+     * (i.e., the array has more elements than the list), the element in
+     * the array immediately following the end of the collection is set to
+     * {@code null}.  (This is useful in determining the length of the
+     * list <i>only</i> if the caller knows that the list does not contain
+     * any null elements.)
+     *
+     * @param a the array into which the elements of the list are to
+     *          be stored, if it is big enough; otherwise, a new array of the
+     *          same runtime type is allocated for this purpose.
+     * @return an array containing the elements of the list
+     * @throws ArrayStoreException if the runtime type of the specified array
+     *         is not a supertype of the runtime type of every element in
+     *         this list
+     * @throws NullPointerException if the specified array is null
+     */
+//    @SuppressWarnings("unchecked")
+//    public <T> T[] toArray(T[] a) {
+//        if (a.length < size)
+//            // Make a new array of a's runtime type, but my contents:
+//            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
+//        System.arraycopy(elementData, 0, a, 0, size);
+//        if (a.length > size)
+//            a[size] = null;
+//        return a;
+//    }
+
+    // Positional Access Operations
+
+    @SuppressWarnings("unchecked")
+    PrimitiveInt elementData(int index) {
+        return (PrimitiveInt) elementData[index];
+    }
+
+    @SuppressWarnings("unchecked")
+    static PrimitiveInt elementAt(PrimitiveInt[] es, int index) {
+        return es[index];
+    }
+
+    /**
+     * Returns the element at the specified position in this list.
+     *
+     * @param  index index of the element to return
+     * @return the element at the specified position in this list
+     * @throws IndexOutOfBoundsException {@inheritDoc}
+     */
+    public PrimitiveInt get(int index) {
+        Objects.checkIndex(index, size);
+        return elementData(index);
+    }
+
+    /**
+     * Replaces the element at the specified position in this list with
+     * the specified element.
+     *
+     * @param index index of the element to replace
+     * @param element element to be stored at the specified position
+     * @return the element previously at the specified position
+     * @throws IndexOutOfBoundsException {@inheritDoc}
+     */
+    public PrimitiveInt set(int index, PrimitiveInt element) {
+        Objects.checkIndex(index, size);
+        PrimitiveInt oldValue = elementData(index);
+        elementData[index] = element;
+        return oldValue;
+    }
+
+    /**
+     * This helper method split out from add(int) to keep method
+     * bytecode size under 35 (the -XX:MaxInlineSize default value),
+     * which helps when add(int) is called in a C1-compiled loop.
+     */
+    private void add(PrimitiveInt e, PrimitiveInt[] elementData, int s) {
+        if (s == elementData.length)
+            elementData = grow();
+        elementData[s] = e;
+        size = s + 1;
+    }
+
+    /**
+     * Appends the specified element to the end of this list.
+     *
+     * @param e element to be appended to this list
+     * @return {@code true} (as specified by {@link Collection#add})
+     */
+    public boolean add(PrimitiveInt e) {
+        modCount++;
+        add(e, elementData, size);
+        return true;
+    }
+
+    /**
+     * Inserts the specified element at the specified position in this
+     * list. Shifts the element currently at that position (if any) and
+     * any subsequent elements to the right (adds one to their indices).
+     *
+     * @param index index at which the specified element is to be inserted
+     * @param element element to be inserted
+     * @throws IndexOutOfBoundsException {@inheritDoc}
+     */
+    public void add(int index, PrimitiveInt element) {
+        rangeCheckForAdd(index);
+        modCount++;
+        final int s;
+        PrimitiveInt[] elementData;
+        if ((s = size) == (elementData = this.elementData).length)
+            elementData = grow();
+        System.arraycopy(elementData, index,
+                elementData, index + 1,
+                s - index);
+        elementData[index] = element;
+        size = s + 1;
+    }
+
+    /**
+     * Removes the element at the specified position in this list.
+     * Shifts any subsequent elements to the left (subtracts one from their
+     * indices).
+     *
+     * @param index the index of the element to be removed
+     * @return the element that was removed from the list
+     * @throws IndexOutOfBoundsException {@inheritDoc}
+     */
+    public PrimitiveInt remove(int index) {
+        Objects.checkIndex(index, size);
+        final PrimitiveInt[] es = elementData;
+
+        PrimitiveInt oldValue = (PrimitiveInt) es[index];
+        fastRemove(es, index);
+
+        return oldValue;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (!(o instanceof ArrayListPrimitiveInt)) {
+            return false;
+        }
+
+        final int expectedModCount = modCount;
+        // ArrayList can be subclassed and given arbitrary behavior, but we can
+        // still deal with the common case where o is ArrayList precisely
+        boolean equal = equalsArrayList((ArrayListPrimitiveInt) o);
+
+        checkForComodification(expectedModCount);
+        return equal;
+    }
+
+    private boolean equalsArrayList(ArrayListPrimitiveInt other) {
+        final int otherModCount = other.modCount;
+        final int s = size;
+        boolean equal;
+        if (equal = (s == other.size)) {
+            final PrimitiveInt[] otherEs = other.elementData;
+            final PrimitiveInt[] es = elementData;
+            if (s > es.length || s > otherEs.length) {
+                throw new ConcurrentModificationException();
+            }
+            for (int i = 0; i < s; i++) {
+                if (es[i] != otherEs[i]) {
+                    equal = false;
+                    break;
+                }
+            }
+        }
+        other.checkForComodification(otherModCount);
+        return equal;
+    }
+
+    private void checkForComodification(final int expectedModCount) {
+        if (modCount != expectedModCount) {
+            throw new ConcurrentModificationException();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int hashCode() {
+        int expectedModCount = modCount;
+        int hash = hashCodeRange(0, size);
+        checkForComodification(expectedModCount);
+        return hash;
+    }
+
+    int hashCodeRange(int from, int to) {
+        final PrimitiveInt[] es = elementData;
+        if (to > es.length) {
+            throw new ConcurrentModificationException();
+        }
+        int hashCode = 1;
+        for (int i = from; i < to; i++) {
+            PrimitiveInt e = es[i];
+            hashCode = 31 * hashCode + e.hashCode();
+        }
+        return hashCode;
+    }
+
+    /**
+     * Removes the first occurrence of the specified element from this list,
+     * if it is present.  If the list does not contain the element, it is
+     * unchanged.  More formally, removes the element with the lowest index
+     * {@code i} such that
+     * {@code Objects.equals(o, get(i))}
+     * (if such an element exists).  Returns {@code true} if this list
+     * contained the specified element (or equivalently, if this list
+     * changed as a result of the call).
+     *
+     * @param o element to be removed from this list, if present
+     * @return {@code true} if this list contained the specified element
+     */
+    public boolean removeC(PrimitiveInt o) {
+        final PrimitiveInt[] es = elementData;
+        final int size = this.size;
+        int i = 0;
+        found:
+        {
+//            if (o == null) {
+//                for (; i < size; i++)
+//                    if (es[i] == null)
+//                        break found;
+//            } else
+            {
+                for (; i < size; i++)
+                    if (o == es[i])
+                        break found;
+            }
+            return false;
+        }
+        fastRemove(es, i);
+        return true;
+    }
+
+    /**
+     * Private remove method that skips bounds checking and does not
+     * return the value removed.
+     */
+    private void fastRemove(PrimitiveInt[] es, int i) {
+        modCount++;
+        final int newSize;
+        if ((newSize = size - 1) > i)
+            System.arraycopy(es, i + 1, es, i, newSize - i);
+        es[size = newSize] = new PrimitiveInt();
+    }
+
+    /**
+     * Removes all of the elements from this list.  The list will
+     * be empty after this call returns.
+     */
+    public void clear() {
+        modCount++;
+        final PrimitiveInt[] es = elementData;
+        for (int to = size, i = size = 0; i < to; i++)
+            es[i] = new PrimitiveInt();
+    }
+
+    /**
+     * Removes from this list all of the elements whose index is between
+     * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
+     * Shifts any succeeding elements to the left (reduces their index).
+     * This call shortens the list by {@code (toIndex - fromIndex)} elements.
+     * (If {@code toIndex==fromIndex}, this operation has no effect.)
+     *
+     * @throws IndexOutOfBoundsException if {@code fromIndex} or
+     *         {@code toIndex} is out of range
+     *         ({@code fromIndex < 0 ||
+     *          toIndex > size() ||
+     *          toIndex < fromIndex})
+     */
+    protected void removeRange(int fromIndex, int toIndex) {
+        if (fromIndex > toIndex) {
+            throw new IndexOutOfBoundsException(
+                    outOfBoundsMsg(fromIndex, toIndex));
+        }
+        modCount++;
+        shiftTailOverGap(elementData, fromIndex, toIndex);
+    }
+
+    /** Erases the gap from lo to hi, by sliding down following elements. */
+    private void shiftTailOverGap(PrimitiveInt[] es, int lo, int hi) {
+        System.arraycopy(es, hi, es, lo, size - hi);
+        for (int to = size, i = (size -= hi - lo); i < to; i++)
+            es[i] = new PrimitiveInt();
+    }
+
+    /**
+     * A version of rangeCheck used by add and addAll.
+     */
+    private void rangeCheckForAdd(int index) {
+        if (index > size || index < 0)
+            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
+    }
+
+    /**
+     * Constructs an IndexOutOfBoundsException detail message.
+     * Of the many possible refactorings of the error handling code,
+     * this "outlining" performs best with both server and client VMs.
+     */
+    private String outOfBoundsMsg(int index) {
+        return "Index: "+index+", Size: "+size;
+    }
+
+    /**
+     * A version used in checking (fromIndex > toIndex) condition
+     */
+    private static String outOfBoundsMsg(int fromIndex, int toIndex) {
+        return "From Index: " + fromIndex + " > To Index: " + toIndex;
+    }
+
+    /**
+     * Returns an iterator over the elements in this list in proper sequence.
+     *
+     * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
+     *
+     * @return an iterator over the elements in this list in proper sequence
+     */
+    public PrimitiveIntItr iterator() {
+        return new PrimitiveIntItr();
+    }
+
+    /**
+     * An optimized version of AbstractList.Itr
+     */
+    private class PrimitiveIntItr {
+        int cursor;       // index of next element to return
+        int lastRet = -1; // index of last element returned; -1 if no such
+        int expectedModCount = modCount;
+
+        // prevent creating a synthetic constructor
+        PrimitiveIntItr() {}
+
+        public boolean hasNext() {
+            return cursor != size;
+        }
+
+        @SuppressWarnings("unchecked")
+        public PrimitiveInt next() {
+            checkForComodification();
+            int i = cursor;
+            if (i >= size)
+                throw new NoSuchElementException();
+            PrimitiveInt[] elementData = ArrayListPrimitiveInt.this.elementData;
+            if (i >= elementData.length)
+                throw new ConcurrentModificationException();
+            cursor = i + 1;
+            return (PrimitiveInt) elementData[lastRet = i];
+        }
+
+        public void remove() {
+            if (lastRet < 0)
+                throw new IllegalStateException();
+            checkForComodification();
+
+            try {
+                ArrayListPrimitiveInt.this.remove(lastRet);
+                cursor = lastRet;
+                lastRet = -1;
+                expectedModCount = modCount;
+            } catch (IndexOutOfBoundsException ex) {
+                throw new ConcurrentModificationException();
+            }
+        }
+
+        public void forEachRemaining(Consumer<? super PrimitiveInt> action) {
+            Objects.requireNonNull(action);
+            final int size = ArrayListPrimitiveInt.this.size;
+            int i = cursor;
+            if (i < size) {
+                final PrimitiveInt[] es = elementData;
+                if (i >= es.length)
+                    throw new ConcurrentModificationException();
+                for (; i < size && modCount == expectedModCount; i++)
+                    action.accept(elementAt(es, i));
+                // update once at end to reduce heap write traffic
+                cursor = i;
+                lastRet = i - 1;
+                checkForComodification();
+            }
+        }
+
+        final void checkForComodification() {
+            if (modCount != expectedModCount)
+                throw new ConcurrentModificationException();
+        }
+    }
+
+    static final int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
+
+    static int newLength(int oldLength, int minGrowth, int prefGrowth) {
+        // assert oldLength >= 0
+        // assert minGrowth > 0
+
+        int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
+        if (newLength - MAX_ARRAY_LENGTH <= 0) {
+            return newLength;
+        }
+        return hugeLength(oldLength, minGrowth);
+    }
+
+    private static int hugeLength(int oldLength, int minGrowth) {
+        int minLength = oldLength + minGrowth;
+        if (minLength < 0) { // overflow
+            throw new OutOfMemoryError("Required array length too large");
+        }
+        if (minLength <= MAX_ARRAY_LENGTH) {
+            return MAX_ARRAY_LENGTH;
+        }
+        return Integer.MAX_VALUE;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/InlineCursor.java b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/InlineCursor.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/InlineCursor.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
+ */
+
+package org.openjdk.bench.valhalla.sandbox.corelibs.corelibs;
+
+import java.util.ConcurrentModificationException;
+import java.util.NoSuchElementException;
+
+/**
+ * An inline cursor is a reference to an existing or non-existent element
+ * of a collection.
+ * <p>
+ * Cursor values are immutable, the reference to an element
+ * does not change but the state of the collection can change
+ * so the element is no longer accessible.
+ * Calling {@link #get()} throws a {@link ConcurrentModificationException}.
+ * Iterating through a Collection proceeds by creating new Cursor
+ * from the Collection or advancing to the next or retreating to previous elements.
+ * Advancing past the end of the Collection or retreating before the beginning
+ * results in Cursor values that are non-existent.
+ * A Cursor for an empty Collection does not refer to an element and
+ * throws {@link NoSuchElementException}.
+ * Modifications to the Collection invalidate every Cursor that was created
+ * before the modification.
+ * The typical traversal pattern is:
+ * <pre>{@code
+ *  Collection<T> c = ...;
+ *  for (var cursor = c.cursor(); cursor.exists(); cursor = cursor.advance()) {
+ *      var el = cursor.get();
+ *  }
+ * }
+ * </pre>
+ * <p>
+ * Cursors can be used to {@link #remove()} remove an element from the collection.
+ * Removing an element modifies the collection making that cursor invalid.
+ * The cursor returned from the {@link #remove()} method is a placeholder
+ * for the position, the element occupied, between the next and previous elements.
+ * It can be moved to the next or previous element to continue the iteration.
+ * <p>
+ * The typical traversal and remove pattern follows; when an element is
+ * removed, the cursor returned from the remove is used to continue the iteration:
+ * <pre>{@code
+ *  Collection<T> c = ...;
+ *  for (var cursor = c.cursor(); cursor.exists(); cursor = cursor.advance()) {
+ *      var el = cursor.get();
+ *      if (el.equals(...)) {
+ *          cursor = cursor.remove();
+ *      }
+ *  }
+ * }
+ * </pre>
+ * <p>
+ * @param <T> the type of the element.
+ */
+public interface InlineCursor<T> {
+    /**
+     * Return true if the Cursor refers to an element.
+     *
+     * If the collection has been modified since the Cursor was created
+     * the element can not be known to exist.
+     * This method does not throw {@link ConcurrentModificationException}
+     * if the collection has been modified but returns false.
+     *
+     * @return  true if this Cursor refers to an element in the collection and
+     *          the collection has not been modified since the cursor was created;
+     *          false otherwise
+     */
+    boolean exists();
+
+    /**
+     * Return a Cursor for the next element after the current element.
+     * If there is no element following this element the returned
+     * Cursor will be non-existent. To wit: {@code Cursor.exists() == false}.
+     *
+     * @return return a cursor for the next element after this element
+     * @throws ConcurrentModificationException if the collection
+     *         has been modified since this Cursor was created
+     */
+    InlineCursor<T> advance();
+
+    /**
+     * Return the current element referred to by the Cursor.
+     *
+     * The behavior must be consistent with {@link #exists()}
+     * as long as the collection has not been modified.
+     *
+     * @return  return the element in the collection if the collection
+     *          has not been modified since the cursor was created
+     * @throws NoSuchElementException if the referenced element does not exist
+     *         or no longer exists
+     * @throws ConcurrentModificationException if the collection
+     *         has been modified since this Cursor was created
+     */
+    T get();
+}
diff a/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/PrimitiveInt.java b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/PrimitiveInt.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/PrimitiveInt.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.sandbox.corelibs;
+
+public value class PrimitiveInt {
+    int value;
+
+    PrimitiveInt() {
+        this.value = 0;
+    }
+
+    PrimitiveInt(int value) {
+        this.value = value;
+    }
+
+    int value() {
+        return value;
+    }
+
+    public String toString() {
+        return Integer.toString(value);
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/XArrayList.java b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/XArrayList.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/XArrayList.java
@@ -0,0 +1,1906 @@
+/*
+ * 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.  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.
+ */
+
+package org.openjdk.bench.valhalla.sandbox.corelibs.corelibs;
+
+import java.util.AbstractList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.RandomAccess;
+import java.util.Spliterator;
+
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
+//import jdk.internal.access.SharedSecrets;
+//import jdk.internal.util.ArraysSupport;
+
+/**
+ * Resizable-array implementation of the {@code List} interface.  Implements
+ * all optional list operations, and permits all elements, including
+ * {@code null}.  In addition to implementing the {@code List} interface,
+ * this class provides methods to manipulate the size of the array that is
+ * used internally to store the list.  (This class is roughly equivalent to
+ * {@code Vector}, except that it is unsynchronized.)
+ *
+ * <p>The {@code size}, {@code isEmpty}, {@code get}, {@code set},
+ * {@code iterator}, and {@code listIterator} operations run in constant
+ * time.  The {@code add} operation runs in <i>amortized constant time</i>,
+ * that is, adding n elements requires O(n) time.  All of the other operations
+ * run in linear time (roughly speaking).  The constant factor is low compared
+ * to that for the {@code LinkedList} implementation.
+ *
+ * <p>Each {@code XArrayList} instance has a <i>capacity</i>.  The capacity is
+ * the size of the array used to store the elements in the list.  It is always
+ * at least as large as the list size.  As elements are added to an XArrayList,
+ * its capacity grows automatically.  The details of the growth policy are not
+ * specified beyond the fact that adding an element has constant amortized
+ * time cost.
+ *
+ * <p>An application can increase the capacity of an {@code XArrayList} instance
+ * before adding a large number of elements using the {@code ensureCapacity}
+ * operation.  This may reduce the amount of incremental reallocation.
+ *
+ * <p><strong>Note that this implementation is not synchronized.</strong>
+ * If multiple threads access an {@code XArrayList} instance concurrently,
+ * and at least one of the threads modifies the list structurally, it
+ * <i>must</i> be synchronized externally.  (A structural modification is
+ * any operation that adds or deletes one or more elements, or explicitly
+ * resizes the backing array; merely setting the value of an element is not
+ * a structural modification.)  This is typically accomplished by
+ * synchronizing on some object that naturally encapsulates the list.
+ *
+ * If no such object exists, the list should be "wrapped" using the
+ * {@link Collections#synchronizedList Collections.synchronizedList}
+ * method.  This is best done at creation time, to prevent accidental
+ * unsynchronized access to the list:<pre>
+ *   List list = Collections.synchronizedList(new XArrayList(...));</pre>
+ *
+ * <p id="fail-fast">
+ * The iterators returned by this class's {@link #iterator() iterator} and
+ * {@link #listIterator(int) listIterator} methods are <em>fail-fast</em>:
+ * if the list is structurally modified at any time after the iterator is
+ * created, in any way except through the iterator's own
+ * {@link ListIterator#remove() remove} or
+ * {@link ListIterator#add(Object) add} methods, the iterator will throw a
+ * {@link ConcurrentModificationException}.  Thus, in the face of
+ * concurrent modification, the iterator fails quickly and cleanly, rather
+ * than risking arbitrary, non-deterministic behavior at an undetermined
+ * time in the future.
+ *
+ * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
+ * as it is, generally speaking, impossible to make any hard guarantees in the
+ * presence of unsynchronized concurrent modification.  Fail-fast iterators
+ * throw {@code ConcurrentModificationException} on a best-effort basis.
+ * Therefore, it would be wrong to write a program that depended on this
+ * exception for its correctness:  <i>the fail-fast behavior of iterators
+ * should be used only to detect bugs.</i>
+ *
+ * <p>This class is a member of the
+ * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
+ * Java Collections Framework</a>.
+ *
+ * @param <E> the type of elements in this list
+ *
+ * @author  Josh Bloch
+ * @author  Neal Gafter
+ * @see     Collection
+ * @see     List
+ * @see     LinkedList
+ * @see     Vector
+ * @since   1.2
+ */
+public class XArrayList<E> extends AbstractList<E>
+        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
+{
+    private static final long serialVersionUID = 8683452581122892189L;
+
+    /**
+     * Default initial capacity.
+     */
+    private static final int DEFAULT_CAPACITY = 10;
+
+    /**
+     * Shared empty array instance used for empty instances.
+     */
+    private static final Object[] EMPTY_ELEMENTDATA = {};
+
+    /**
+     * Shared empty array instance used for default sized empty instances. We
+     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
+     * first element is added.
+     */
+    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
+
+    /**
+     * The array buffer into which the elements of the ArrayList are stored.
+     * The capacity of the ArrayList is the length of this array buffer. Any
+     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
+     * will be expanded to DEFAULT_CAPACITY when the first element is added.
+     */
+    transient Object[] elementData; // non-private to simplify nested class access
+
+    /**
+     * The size of the ArrayList (the number of elements it contains).
+     *
+     * @serial
+     */
+    int size;
+
+    /**
+     * Constructs an empty list with the specified initial capacity.
+     *
+     * @param  initialCapacity  the initial capacity of the list
+     * @throws IllegalArgumentException if the specified initial capacity
+     *         is negative
+     */
+    public XArrayList(int initialCapacity) {
+        if (initialCapacity > 0) {
+            this.elementData = new Object[initialCapacity];
+        } else if (initialCapacity == 0) {
+            this.elementData = EMPTY_ELEMENTDATA;
+        } else {
+            throw new IllegalArgumentException("Illegal Capacity: "+
+                                               initialCapacity);
+        }
+    }
+
+    /**
+     * Constructs an empty list with an initial capacity of ten.
+     */
+    public XArrayList() {
+        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
+    }
+
+    /**
+     * Constructs a list containing the elements of the specified
+     * collection, in the order they are returned by the collection's
+     * iterator.
+     *
+     * @param c the collection whose elements are to be placed into this list
+     * @throws NullPointerException if the specified collection is null
+     */
+    public XArrayList(Collection<? extends E> c) {
+        elementData = c.toArray();
+        if ((size = elementData.length) != 0) {
+            // defend against c.toArray (incorrectly) not returning Object[]
+            // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
+            if (elementData.getClass() != Object[].class)
+                elementData = Arrays.copyOf(elementData, size, Object[].class);
+        } else {
+            // replace with empty array.
+            this.elementData = EMPTY_ELEMENTDATA;
+        }
+    }
+
+    /**
+     * Trims the capacity of this {@code XArrayList} instance to be the
+     * list's current size.  An application can use this operation to minimize
+     * the storage of an {@code XArrayList} instance.
+     */
+    public void trimToSize() {
+        modCount++;
+        if (size < elementData.length) {
+            elementData = (size == 0)
+              ? EMPTY_ELEMENTDATA
+              : Arrays.copyOf(elementData, size);
+        }
+    }
+
+    /**
+     * Increases the capacity of this {@code XArrayList} instance, if
+     * necessary, to ensure that it can hold at least the number of elements
+     * specified by the minimum capacity argument.
+     *
+     * @param minCapacity the desired minimum capacity
+     */
+    public void ensureCapacity(int minCapacity) {
+        if (minCapacity > elementData.length
+            && !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
+                 && minCapacity <= DEFAULT_CAPACITY)) {
+            modCount++;
+            grow(minCapacity);
+        }
+    }
+
+    /**
+     * Increases the capacity to ensure that it can hold at least the
+     * number of elements specified by the minimum capacity argument.
+     *
+     * @param minCapacity the desired minimum capacity
+     * @throws OutOfMemoryError if minCapacity is less than zero
+     */
+    private Object[] grow(int minCapacity) {
+        int oldCapacity = elementData.length;
+        if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
+            int newCapacity = newLength(oldCapacity,
+                    minCapacity - oldCapacity, /* minimum growth */
+                    oldCapacity >> 1           /* preferred growth */);
+            return elementData = Arrays.copyOf(elementData, newCapacity);
+        } else {
+            return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
+        }
+    }
+
+    private Object[] grow() {
+        return grow(size + 1);
+    }
+
+    /**
+     * Returns the number of elements in this list.
+     *
+     * @return the number of elements in this list
+     */
+    public int size() {
+        return size;
+    }
+
+    /**
+     * Returns {@code true} if this list contains no elements.
+     *
+     * @return {@code true} if this list contains no elements
+     */
+    public boolean isEmpty() {
+        return size == 0;
+    }
+
+    /**
+     * Returns {@code true} if this list contains the specified element.
+     * More formally, returns {@code true} if and only if this list contains
+     * at least one element {@code e} such that
+     * {@code Objects.equals(o, e)}.
+     *
+     * @param o element whose presence in this list is to be tested
+     * @return {@code true} if this list contains the specified element
+     */
+    public boolean contains(Object o) {
+        return indexOf(o) >= 0;
+    }
+
+    /**
+     * Returns the index of the first occurrence of the specified element
+     * in this list, or -1 if this list does not contain the element.
+     * More formally, returns the lowest index {@code i} such that
+     * {@code Objects.equals(o, get(i))},
+     * or -1 if there is no such index.
+     */
+    public int indexOf(Object o) {
+        return indexOfRange(o, 0, size);
+    }
+
+    int indexOfRange(Object o, int start, int end) {
+        Object[] es = elementData;
+        if (o == null) {
+            for (int i = start; i < end; i++) {
+                if (es[i] == null) {
+                    return i;
+                }
+            }
+        } else {
+            for (int i = start; i < end; i++) {
+                if (o.equals(es[i])) {
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Returns the index of the last occurrence of the specified element
+     * in this list, or -1 if this list does not contain the element.
+     * More formally, returns the highest index {@code i} such that
+     * {@code Objects.equals(o, get(i))},
+     * or -1 if there is no such index.
+     */
+    public int lastIndexOf(Object o) {
+        return lastIndexOfRange(o, 0, size);
+    }
+
+    int lastIndexOfRange(Object o, int start, int end) {
+        Object[] es = elementData;
+        if (o == null) {
+            for (int i = end - 1; i >= start; i--) {
+                if (es[i] == null) {
+                    return i;
+                }
+            }
+        } else {
+            for (int i = end - 1; i >= start; i--) {
+                if (o.equals(es[i])) {
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Returns a shallow copy of this {@code XArrayList} instance.  (The
+     * elements themselves are not copied.)
+     *
+     * @return a clone of this {@code XArrayList} instance
+     */
+    public Object clone() {
+        try {
+            XArrayList<?> v = (XArrayList<?>) super.clone();
+            v.elementData = Arrays.copyOf(elementData, size);
+            v.modCount = 0;
+            return v;
+        } catch (CloneNotSupportedException e) {
+            // this shouldn't happen, since we are Cloneable
+            throw new InternalError(e);
+        }
+    }
+
+    /**
+     * Returns an array containing all of the elements in this list
+     * in proper sequence (from first to last element).
+     *
+     * <p>The returned array will be "safe" in that no references to it are
+     * maintained by this list.  (In other words, this method must allocate
+     * a new array).  The caller is thus free to modify the returned array.
+     *
+     * <p>This method acts as bridge between array-based and collection-based
+     * APIs.
+     *
+     * @return an array containing all of the elements in this list in
+     *         proper sequence
+     */
+    public Object[] toArray() {
+        return Arrays.copyOf(elementData, size);
+    }
+
+    /**
+     * Returns an array containing all of the elements in this list in proper
+     * sequence (from first to last element); the runtime type of the returned
+     * array is that of the specified array.  If the list fits in the
+     * specified array, it is returned therein.  Otherwise, a new array is
+     * allocated with the runtime type of the specified array and the size of
+     * this list.
+     *
+     * <p>If the list fits in the specified array with room to spare
+     * (i.e., the array has more elements than the list), the element in
+     * the array immediately following the end of the collection is set to
+     * {@code null}.  (This is useful in determining the length of the
+     * list <i>only</i> if the caller knows that the list does not contain
+     * any null elements.)
+     *
+     * @param a the array into which the elements of the list are to
+     *          be stored, if it is big enough; otherwise, a new array of the
+     *          same runtime type is allocated for this purpose.
+     * @return an array containing the elements of the list
+     * @throws ArrayStoreException if the runtime type of the specified array
+     *         is not a supertype of the runtime type of every element in
+     *         this list
+     * @throws NullPointerException if the specified array is null
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T[] toArray(T[] a) {
+        if (a.length < size)
+            // Make a new array of a's runtime type, but my contents:
+            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
+        System.arraycopy(elementData, 0, a, 0, size);
+        if (a.length > size)
+            a[size] = null;
+        return a;
+    }
+
+    // Positional Access Operations
+
+    @SuppressWarnings("unchecked")
+    E elementData(int index) {
+        return (E) elementData[index];
+    }
+
+    @SuppressWarnings("unchecked")
+    static <E> E elementAt(Object[] es, int index) {
+        return (E) es[index];
+    }
+
+    /**
+     * Returns the element at the specified position in this list.
+     *
+     * @param  index index of the element to return
+     * @return the element at the specified position in this list
+     * @throws IndexOutOfBoundsException {@inheritDoc}
+     */
+    public E get(int index) {
+        Objects.checkIndex(index, size);
+        return elementData(index);
+    }
+
+    /**
+     * Replaces the element at the specified position in this list with
+     * the specified element.
+     *
+     * @param index index of the element to replace
+     * @param element element to be stored at the specified position
+     * @return the element previously at the specified position
+     * @throws IndexOutOfBoundsException {@inheritDoc}
+     */
+    public E set(int index, E element) {
+        Objects.checkIndex(index, size);
+        E oldValue = elementData(index);
+        elementData[index] = element;
+        return oldValue;
+    }
+
+    /**
+     * This helper method split out from add(E) to keep method
+     * bytecode size under 35 (the -XX:MaxInlineSize default value),
+     * which helps when add(E) is called in a C1-compiled loop.
+     */
+    private void add(E e, Object[] elementData, int s) {
+        if (s == elementData.length)
+            elementData = grow();
+        elementData[s] = e;
+        size = s + 1;
+    }
+
+    /**
+     * Appends the specified element to the end of this list.
+     *
+     * @param e element to be appended to this list
+     * @return {@code true} (as specified by {@link Collection#add})
+     */
+    public boolean add(E e) {
+        modCount++;
+        add(e, elementData, size);
+        return true;
+    }
+
+    /**
+     * Inserts the specified element at the specified position in this
+     * list. Shifts the element currently at that position (if any) and
+     * any subsequent elements to the right (adds one to their indices).
+     *
+     * @param index index at which the specified element is to be inserted
+     * @param element element to be inserted
+     * @throws IndexOutOfBoundsException {@inheritDoc}
+     */
+    public void add(int index, E element) {
+        rangeCheckForAdd(index);
+        modCount++;
+        final int s;
+        Object[] elementData;
+        if ((s = size) == (elementData = this.elementData).length)
+            elementData = grow();
+        System.arraycopy(elementData, index,
+                         elementData, index + 1,
+                         s - index);
+        elementData[index] = element;
+        size = s + 1;
+    }
+
+    /**
+     * Removes the element at the specified position in this list.
+     * Shifts any subsequent elements to the left (subtracts one from their
+     * indices).
+     *
+     * @param index the index of the element to be removed
+     * @return the element that was removed from the list
+     * @throws IndexOutOfBoundsException {@inheritDoc}
+     */
+    public E remove(int index) {
+        Objects.checkIndex(index, size);
+        final Object[] es = elementData;
+
+        @SuppressWarnings("unchecked") E oldValue = (E) es[index];
+        fastRemove(es, index);
+
+        return oldValue;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (!(o instanceof List)) {
+            return false;
+        }
+
+        final int expectedModCount = modCount;
+        // XArrayList can be subclassed and given arbitrary behavior, but we can
+        // still deal with the common case where o is XArrayList precisely
+        boolean equal = (o.getClass() == XArrayList.class)
+            ? equalsArrayList((XArrayList<?>) o)
+            : equalsRange((List<?>) o, 0, size);
+
+        checkForComodification(expectedModCount);
+        return equal;
+    }
+
+    boolean equalsRange(List<?> other, int from, int to) {
+        final Object[] es = elementData;
+        if (to > es.length) {
+            throw new ConcurrentModificationException();
+        }
+        var oit = other.iterator();
+        for (; from < to; from++) {
+            if (!oit.hasNext() || !Objects.equals(es[from], oit.next())) {
+                return false;
+            }
+        }
+        return !oit.hasNext();
+    }
+
+    private boolean equalsArrayList(XArrayList<?> other) {
+        final int otherModCount = other.modCount;
+        final int s = size;
+        boolean equal;
+        if (equal = (s == other.size)) {
+            final Object[] otherEs = other.elementData;
+            final Object[] es = elementData;
+            if (s > es.length || s > otherEs.length) {
+                throw new ConcurrentModificationException();
+            }
+            for (int i = 0; i < s; i++) {
+                if (!Objects.equals(es[i], otherEs[i])) {
+                    equal = false;
+                    break;
+                }
+            }
+        }
+        other.checkForComodification(otherModCount);
+        return equal;
+    }
+
+    private void checkForComodification(final int expectedModCount) {
+        if (modCount != expectedModCount) {
+            throw new ConcurrentModificationException();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int hashCode() {
+        int expectedModCount = modCount;
+        int hash = hashCodeRange(0, size);
+        checkForComodification(expectedModCount);
+        return hash;
+    }
+
+    int hashCodeRange(int from, int to) {
+        final Object[] es = elementData;
+        if (to > es.length) {
+            throw new ConcurrentModificationException();
+        }
+        int hashCode = 1;
+        for (int i = from; i < to; i++) {
+            Object e = es[i];
+            hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode());
+        }
+        return hashCode;
+    }
+
+    /**
+     * Removes the first occurrence of the specified element from this list,
+     * if it is present.  If the list does not contain the element, it is
+     * unchanged.  More formally, removes the element with the lowest index
+     * {@code i} such that
+     * {@code Objects.equals(o, get(i))}
+     * (if such an element exists).  Returns {@code true} if this list
+     * contained the specified element (or equivalently, if this list
+     * changed as a result of the call).
+     *
+     * @param o element to be removed from this list, if present
+     * @return {@code true} if this list contained the specified element
+     */
+    public boolean remove(Object o) {
+        final Object[] es = elementData;
+        final int size = this.size;
+        int i = 0;
+        found: {
+            if (o == null) {
+                for (; i < size; i++)
+                    if (es[i] == null)
+                        break found;
+            } else {
+                for (; i < size; i++)
+                    if (o.equals(es[i]))
+                        break found;
+            }
+            return false;
+        }
+        fastRemove(es, i);
+        return true;
+    }
+
+    /**
+     * Private remove method that skips bounds checking and does not
+     * return the value removed.
+     */
+    private void fastRemove(Object[] es, int i) {
+        modCount++;
+        final int newSize;
+        if ((newSize = size - 1) > i)
+            System.arraycopy(es, i + 1, es, i, newSize - i);
+        es[size = newSize] = null;
+    }
+
+    /**
+     * Removes all of the elements from this list.  The list will
+     * be empty after this call returns.
+     */
+    public void clear() {
+        modCount++;
+        final Object[] es = elementData;
+        for (int to = size, i = size = 0; i < to; i++)
+            es[i] = null;
+    }
+
+    /**
+     * Appends all of the elements in the specified collection to the end of
+     * this list, in the order that they are returned by the
+     * specified collection's Iterator.  The behavior of this operation is
+     * undefined if the specified collection is modified while the operation
+     * is in progress.  (This implies that the behavior of this call is
+     * undefined if the specified collection is this list, and this
+     * list is nonempty.)
+     *
+     * @param c collection containing elements to be added to this list
+     * @return {@code true} if this list changed as a result of the call
+     * @throws NullPointerException if the specified collection is null
+     */
+    public boolean addAll(Collection<? extends E> c) {
+        Object[] a = c.toArray();
+        modCount++;
+        int numNew = a.length;
+        if (numNew == 0)
+            return false;
+        Object[] elementData;
+        final int s;
+        if (numNew > (elementData = this.elementData).length - (s = size))
+            elementData = grow(s + numNew);
+        System.arraycopy(a, 0, elementData, s, numNew);
+        size = s + numNew;
+        return true;
+    }
+
+    /**
+     * Inserts all of the elements in the specified collection into this
+     * list, starting at the specified position.  Shifts the element
+     * currently at that position (if any) and any subsequent elements to
+     * the right (increases their indices).  The new elements will appear
+     * in the list in the order that they are returned by the
+     * specified collection's iterator.
+     *
+     * @param index index at which to insert the first element from the
+     *              specified collection
+     * @param c collection containing elements to be added to this list
+     * @return {@code true} if this list changed as a result of the call
+     * @throws IndexOutOfBoundsException {@inheritDoc}
+     * @throws NullPointerException if the specified collection is null
+     */
+    public boolean addAll(int index, Collection<? extends E> c) {
+        rangeCheckForAdd(index);
+
+        Object[] a = c.toArray();
+        modCount++;
+        int numNew = a.length;
+        if (numNew == 0)
+            return false;
+        Object[] elementData;
+        final int s;
+        if (numNew > (elementData = this.elementData).length - (s = size))
+            elementData = grow(s + numNew);
+
+        int numMoved = s - index;
+        if (numMoved > 0)
+            System.arraycopy(elementData, index,
+                             elementData, index + numNew,
+                             numMoved);
+        System.arraycopy(a, 0, elementData, index, numNew);
+        size = s + numNew;
+        return true;
+    }
+
+    /**
+     * Removes from this list all of the elements whose index is between
+     * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
+     * Shifts any succeeding elements to the left (reduces their index).
+     * This call shortens the list by {@code (toIndex - fromIndex)} elements.
+     * (If {@code toIndex==fromIndex}, this operation has no effect.)
+     *
+     * @throws IndexOutOfBoundsException if {@code fromIndex} or
+     *         {@code toIndex} is out of range
+     *         ({@code fromIndex < 0 ||
+     *          toIndex > size() ||
+     *          toIndex < fromIndex})
+     */
+    protected void removeRange(int fromIndex, int toIndex) {
+        if (fromIndex > toIndex) {
+            throw new IndexOutOfBoundsException(
+                    outOfBoundsMsg(fromIndex, toIndex));
+        }
+        modCount++;
+        shiftTailOverGap(elementData, fromIndex, toIndex);
+    }
+
+    /** Erases the gap from lo to hi, by sliding down following elements. */
+    private void shiftTailOverGap(Object[] es, int lo, int hi) {
+        System.arraycopy(es, hi, es, lo, size - hi);
+        for (int to = size, i = (size -= hi - lo); i < to; i++)
+            es[i] = null;
+    }
+
+    /**
+     * A version of rangeCheck used by add and addAll.
+     */
+    private void rangeCheckForAdd(int index) {
+        if (index > size || index < 0)
+            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
+    }
+
+    /**
+     * Constructs an IndexOutOfBoundsException detail message.
+     * Of the many possible refactorings of the error handling code,
+     * this "outlining" performs best with both server and client VMs.
+     */
+    private String outOfBoundsMsg(int index) {
+        return "Index: "+index+", Size: "+size;
+    }
+
+    /**
+     * A version used in checking (fromIndex > toIndex) condition
+     */
+    private static String outOfBoundsMsg(int fromIndex, int toIndex) {
+        return "From Index: " + fromIndex + " > To Index: " + toIndex;
+    }
+
+    /**
+     * Removes from this list all of its elements that are contained in the
+     * specified collection.
+     *
+     * @param c collection containing elements to be removed from this list
+     * @return {@code true} if this list changed as a result of the call
+     * @throws ClassCastException if the class of an element of this list
+     *         is incompatible with the specified collection
+     * (<a href="Collection.html#optional-restrictions">optional</a>)
+     * @throws NullPointerException if this list contains a null element and the
+     *         specified collection does not permit null elements
+     * (<a href="Collection.html#optional-restrictions">optional</a>),
+     *         or if the specified collection is null
+     * @see Collection#contains(Object)
+     */
+    public boolean removeAll(Collection<?> c) {
+        return batchRemove(c, false, 0, size);
+    }
+
+    /**
+     * Retains only the elements in this list that are contained in the
+     * specified collection.  In other words, removes from this list all
+     * of its elements that are not contained in the specified collection.
+     *
+     * @param c collection containing elements to be retained in this list
+     * @return {@code true} if this list changed as a result of the call
+     * @throws ClassCastException if the class of an element of this list
+     *         is incompatible with the specified collection
+     * (<a href="Collection.html#optional-restrictions">optional</a>)
+     * @throws NullPointerException if this list contains a null element and the
+     *         specified collection does not permit null elements
+     * (<a href="Collection.html#optional-restrictions">optional</a>),
+     *         or if the specified collection is null
+     * @see Collection#contains(Object)
+     */
+    public boolean retainAll(Collection<?> c) {
+        return batchRemove(c, true, 0, size);
+    }
+
+    boolean batchRemove(Collection<?> c, boolean complement,
+                        final int from, final int end) {
+        Objects.requireNonNull(c);
+        final Object[] es = elementData;
+        int r;
+        // Optimize for initial run of survivors
+        for (r = from;; r++) {
+            if (r == end)
+                return false;
+            if (c.contains(es[r]) != complement)
+                break;
+        }
+        int w = r++;
+        try {
+            for (Object e; r < end; r++)
+                if (c.contains(e = es[r]) == complement)
+                    es[w++] = e;
+        } catch (Throwable ex) {
+            // Preserve behavioral compatibility with AbstractCollection,
+            // even if c.contains() throws.
+            System.arraycopy(es, r, es, w, end - r);
+            w += end - r;
+            throw ex;
+        } finally {
+            modCount += end - w;
+            shiftTailOverGap(es, w, end);
+        }
+        return true;
+    }
+
+    /**
+     * Saves the state of the {@code XArrayList} instance to a stream
+     * (that is, serializes it).
+     *
+     * @param s the stream
+     * @throws java.io.IOException if an I/O error occurs
+     * @serialData The length of the array backing the {@code XArrayList}
+     *             instance is emitted (int), followed by all of its elements
+     *             (each an {@code Object}) in the proper order.
+     */
+    private void writeObject(java.io.ObjectOutputStream s)
+        throws java.io.IOException {
+        // Write out element count, and any hidden stuff
+        int expectedModCount = modCount;
+        s.defaultWriteObject();
+
+        // Write out size as capacity for behavioral compatibility with clone()
+        s.writeInt(size);
+
+        // Write out all elements in the proper order.
+        for (int i=0; i<size; i++) {
+            s.writeObject(elementData[i]);
+        }
+
+        if (modCount != expectedModCount) {
+            throw new ConcurrentModificationException();
+        }
+    }
+
+    /**
+     * Reconstitutes the {@code XArrayList} instance from a stream (that is,
+     * deserializes it).
+     * @param s the stream
+     * @throws ClassNotFoundException if the class of a serialized object
+     *         could not be found
+     * @throws java.io.IOException if an I/O error occurs
+     */
+    private void readObject(java.io.ObjectInputStream s)
+        throws java.io.IOException, ClassNotFoundException {
+
+        // Read in size, and any hidden stuff
+        s.defaultReadObject();
+
+        // Read in capacity
+        s.readInt(); // ignored
+
+        if (size > 0) {
+            // like clone(), allocate array based upon size not capacity
+            //SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, size);
+            Object[] elements = new Object[size];
+
+            // Read in all elements in the proper order.
+            for (int i = 0; i < size; i++) {
+                elements[i] = s.readObject();
+            }
+
+            elementData = elements;
+        } else if (size == 0) {
+            elementData = EMPTY_ELEMENTDATA;
+        } else {
+            throw new java.io.InvalidObjectException("Invalid size: " + size);
+        }
+    }
+
+    /**
+     * Returns a list iterator over the elements in this list (in proper
+     * sequence), starting at the specified position in the list.
+     * The specified index indicates the first element that would be
+     * returned by an initial call to {@link ListIterator#next next}.
+     * An initial call to {@link ListIterator#previous previous} would
+     * return the element with the specified index minus one.
+     *
+     * <p>The returned list iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
+     *
+     * @throws IndexOutOfBoundsException {@inheritDoc}
+     */
+    public ListIterator<E> listIterator(int index) {
+        rangeCheckForAdd(index);
+        return new ListItr(index);
+    }
+
+    /**
+     * Returns a list iterator over the elements in this list (in proper
+     * sequence).
+     *
+     * <p>The returned list iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
+     *
+     * @see #listIterator(int)
+     */
+    public ListIterator<E> listIterator() {
+        return new ListItr(0);
+    }
+
+    /**
+     * Returns an iterator over the elements in this list in proper sequence.
+     *
+     * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
+     *
+     * @return an iterator over the elements in this list in proper sequence
+     */
+    public Iterator<E> iterator() {
+        return new Itr();
+    }
+
+    /**
+     * An optimized version of AbstractList.Itr
+     */
+    private class Itr implements Iterator<E> {
+        int cursor;       // index of next element to return
+        int lastRet = -1; // index of last element returned; -1 if no such
+        int expectedModCount = modCount;
+
+        // prevent creating a synthetic constructor
+        Itr() {}
+
+        public boolean hasNext() {
+            return cursor != size;
+        }
+
+        @SuppressWarnings("unchecked")
+        public E next() {
+            checkForComodification();
+            int i = cursor;
+            if (i >= size)
+                throw new NoSuchElementException();
+            Object[] elementData = XArrayList.this.elementData;
+            if (i >= elementData.length)
+                throw new ConcurrentModificationException();
+            cursor = i + 1;
+            return (E) elementData[lastRet = i];
+        }
+
+        public void remove() {
+            if (lastRet < 0)
+                throw new IllegalStateException();
+            checkForComodification();
+
+            try {
+                XArrayList.this.remove(lastRet);
+                cursor = lastRet;
+                lastRet = -1;
+                expectedModCount = modCount;
+            } catch (IndexOutOfBoundsException ex) {
+                throw new ConcurrentModificationException();
+            }
+        }
+
+        @Override
+        public void forEachRemaining(Consumer<? super E> action) {
+            Objects.requireNonNull(action);
+            final int size = XArrayList.this.size;
+            int i = cursor;
+            if (i < size) {
+                final Object[] es = elementData;
+                if (i >= es.length)
+                    throw new ConcurrentModificationException();
+                for (; i < size && modCount == expectedModCount; i++)
+                    action.accept(elementAt(es, i));
+                // update once at end to reduce heap write traffic
+                cursor = i;
+                lastRet = i - 1;
+                checkForComodification();
+            }
+        }
+
+        final void checkForComodification() {
+            if (modCount != expectedModCount)
+                throw new ConcurrentModificationException();
+        }
+    }
+
+    /**
+     * An optimized version of AbstractList.ListItr
+     */
+    private class ListItr extends Itr implements ListIterator<E> {
+        ListItr(int index) {
+            super();
+            cursor = index;
+        }
+
+        public boolean hasPrevious() {
+            return cursor != 0;
+        }
+
+        public int nextIndex() {
+            return cursor;
+        }
+
+        public int previousIndex() {
+            return cursor - 1;
+        }
+
+        @SuppressWarnings("unchecked")
+        public E previous() {
+            checkForComodification();
+            int i = cursor - 1;
+            if (i < 0)
+                throw new NoSuchElementException();
+            Object[] elementData = XArrayList.this.elementData;
+            if (i >= elementData.length)
+                throw new ConcurrentModificationException();
+            cursor = i;
+            return (E) elementData[lastRet = i];
+        }
+
+        public void set(E e) {
+            if (lastRet < 0)
+                throw new IllegalStateException();
+            checkForComodification();
+
+            try {
+                XArrayList.this.set(lastRet, e);
+            } catch (IndexOutOfBoundsException ex) {
+                throw new ConcurrentModificationException();
+            }
+        }
+
+        public void add(E e) {
+            checkForComodification();
+
+            try {
+                int i = cursor;
+                XArrayList.this.add(i, e);
+                cursor = i + 1;
+                lastRet = -1;
+                expectedModCount = modCount;
+            } catch (IndexOutOfBoundsException ex) {
+                throw new ConcurrentModificationException();
+            }
+        }
+    }
+
+    /**
+     * Return a new cursor for this XArrayList.
+     * @return a cursor
+     */
+    public InlineCursor<E> cursor() {
+        return new AListCursor<>(0);
+    }
+
+    /**
+     * Create an inline cursor for this XArrayList.
+     */
+    private value class AListCursor<E> implements InlineCursor<E> {
+        // Inner class field 'this' is initialized
+        int index;
+        int expectedModCount;
+
+        /**
+         * Create a new Cursor for this XArrayList.
+         *
+         * @param cursor index
+         */
+        public AListCursor(int cursor) {
+            this.index = cursor;
+            this.expectedModCount = XArrayList.this.modCount;
+        }
+
+        @Override
+        public boolean exists() {
+            return index < XArrayList.this.size;
+        }
+
+        @SuppressWarnings("unchecked")
+        public E get() {
+            if (exists()) {
+                checkForComodification();
+                try {
+                    return (E) XArrayList.this.elementData[index];
+                } catch (ArrayIndexOutOfBoundsException aioobe) {
+                    throw new ConcurrentModificationException();
+                }
+            }
+            throw new NoSuchElementException();
+        }
+
+        @Override
+        public AListCursor<E> advance() {
+            // new Cursor will have a current expectedModCount
+            // TBD: Saturate index?  So calling adv, adv, adv, prev == last
+            return new AListCursor<>(Math.min(index + 1, size));
+        }
+
+        final void checkForComodification() {
+            if (XArrayList.this.modCount != expectedModCount)
+                throw new ConcurrentModificationException();
+        }
+    }
+
+    /**
+     * Returns a iterator (Using an InlineCursor) over the elements in this list in proper sequence.
+     *
+     * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
+     *
+     * @return an iterator over the elements in this list in proper sequence
+     */
+    public Iterator<E> iteratorCurs() {
+        return new CurItr();
+    }
+
+    /**
+     * Iterate using a Cursor.
+     */
+    private class CurItr implements Iterator<E> {
+        AListCursor<E> cursor;
+        AListCursor<E> lastRet;
+
+        // prevent creating a synthetic constructor
+        CurItr() {
+            this.cursor = new AListCursor<E>(0);
+            this.lastRet = this.cursor;
+        }
+
+        public boolean hasNext() {
+            return cursor.advance().exists();
+        }
+
+        @SuppressWarnings("unchecked")
+        public E next() {
+            E val = cursor.get();
+            lastRet = cursor;
+            cursor = cursor.advance();
+            return val;
+        }
+
+        @Override
+        public void forEachRemaining(Consumer<? super E> action) {
+            Objects.requireNonNull(action);
+
+            AListCursor<E> cur = cursor;
+            while (cur.exists()) {
+                E val = cur.get();
+                action.accept(val);
+                cur = cur.advance();
+            }
+            cursor = cur;
+        }
+
+        public String toString() {
+            return "cur: " + cursor;
+        }
+    }
+
+
+    /**
+     * Returns a view of the portion of this list between the specified
+     * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.  (If
+     * {@code fromIndex} and {@code toIndex} are equal, the returned list is
+     * empty.)  The returned list is backed by this list, so non-structural
+     * changes in the returned list are reflected in this list, and vice-versa.
+     * The returned list supports all of the optional list operations.
+     *
+     * <p>This method eliminates the need for explicit range operations (of
+     * the sort that commonly exist for arrays).  Any operation that expects
+     * a list can be used as a range operation by passing a subList view
+     * instead of a whole list.  For example, the following idiom
+     * removes a range of elements from a list:
+     * <pre>
+     *      list.subList(from, to).clear();
+     * </pre>
+     * Similar idioms may be constructed for {@link #indexOf(Object)} and
+     * {@link #lastIndexOf(Object)}, and all of the algorithms in the
+     * {@link Collections} class can be applied to a subList.
+     *
+     * <p>The semantics of the list returned by this method become undefined if
+     * the backing list (i.e., this list) is <i>structurally modified</i> in
+     * any way other than via the returned list.  (Structural modifications are
+     * those that change the size of this list, or otherwise perturb it in such
+     * a fashion that iterations in progress may yield incorrect results.)
+     *
+     * @throws IndexOutOfBoundsException {@inheritDoc}
+     * @throws IllegalArgumentException {@inheritDoc}
+     */
+    public List<E> subList(int fromIndex, int toIndex) {
+        subListRangeCheck(fromIndex, toIndex, size);
+        return new SubList<>(this, fromIndex, toIndex);
+    }
+
+    private static class SubList<E> extends AbstractList<E> implements RandomAccess {
+        private final XArrayList<E> root;
+        private final SubList<E> parent;
+        private final int offset;
+        private int size;
+
+        /**
+         * Constructs a sublist of an arbitrary XArrayList.
+         */
+        public SubList(XArrayList<E> root, int fromIndex, int toIndex) {
+            this.root = root;
+            this.parent = null;
+            this.offset = fromIndex;
+            this.size = toIndex - fromIndex;
+            this.modCount = root.modCount;
+        }
+
+        /**
+         * Constructs a sublist of another SubList.
+         */
+        private SubList(SubList<E> parent, int fromIndex, int toIndex) {
+            this.root = parent.root;
+            this.parent = parent;
+            this.offset = parent.offset + fromIndex;
+            this.size = toIndex - fromIndex;
+            this.modCount = root.modCount;
+        }
+
+        public E set(int index, E element) {
+            Objects.checkIndex(index, size);
+            checkForComodification();
+            E oldValue = root.elementData(offset + index);
+            root.elementData[offset + index] = element;
+            return oldValue;
+        }
+
+        public E get(int index) {
+            Objects.checkIndex(index, size);
+            checkForComodification();
+            return root.elementData(offset + index);
+        }
+
+        public int size() {
+            checkForComodification();
+            return size;
+        }
+
+        public void add(int index, E element) {
+            rangeCheckForAdd(index);
+            checkForComodification();
+            root.add(offset + index, element);
+            updateSizeAndModCount(1);
+        }
+
+        public E remove(int index) {
+            Objects.checkIndex(index, size);
+            checkForComodification();
+            E result = root.remove(offset + index);
+            updateSizeAndModCount(-1);
+            return result;
+        }
+
+        protected void removeRange(int fromIndex, int toIndex) {
+            checkForComodification();
+            root.removeRange(offset + fromIndex, offset + toIndex);
+            updateSizeAndModCount(fromIndex - toIndex);
+        }
+
+        public boolean addAll(Collection<? extends E> c) {
+            return addAll(this.size, c);
+        }
+
+        public boolean addAll(int index, Collection<? extends E> c) {
+            rangeCheckForAdd(index);
+            int cSize = c.size();
+            if (cSize==0)
+                return false;
+            checkForComodification();
+            root.addAll(offset + index, c);
+            updateSizeAndModCount(cSize);
+            return true;
+        }
+
+        public void replaceAll(UnaryOperator<E> operator) {
+            root.replaceAllRange(operator, offset, offset + size);
+        }
+
+        public boolean removeAll(Collection<?> c) {
+            return batchRemove(c, false);
+        }
+
+        public boolean retainAll(Collection<?> c) {
+            return batchRemove(c, true);
+        }
+
+        private boolean batchRemove(Collection<?> c, boolean complement) {
+            checkForComodification();
+            int oldSize = root.size;
+            boolean modified =
+                root.batchRemove(c, complement, offset, offset + size);
+            if (modified)
+                updateSizeAndModCount(root.size - oldSize);
+            return modified;
+        }
+
+        public boolean removeIf(Predicate<? super E> filter) {
+            checkForComodification();
+            int oldSize = root.size;
+            boolean modified = root.removeIf(filter, offset, offset + size);
+            if (modified)
+                updateSizeAndModCount(root.size - oldSize);
+            return modified;
+        }
+
+        public Object[] toArray() {
+            checkForComodification();
+            return Arrays.copyOfRange(root.elementData, offset, offset + size);
+        }
+
+        @SuppressWarnings("unchecked")
+        public <T> T[] toArray(T[] a) {
+            checkForComodification();
+            if (a.length < size)
+                return (T[]) Arrays.copyOfRange(
+                        root.elementData, offset, offset + size, a.getClass());
+            System.arraycopy(root.elementData, offset, a, 0, size);
+            if (a.length > size)
+                a[size] = null;
+            return a;
+        }
+
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+
+            if (!(o instanceof List)) {
+                return false;
+            }
+
+            boolean equal = root.equalsRange((List<?>)o, offset, offset + size);
+            checkForComodification();
+            return equal;
+        }
+
+        public int hashCode() {
+            int hash = root.hashCodeRange(offset, offset + size);
+            checkForComodification();
+            return hash;
+        }
+
+        public int indexOf(Object o) {
+            int index = root.indexOfRange(o, offset, offset + size);
+            checkForComodification();
+            return index >= 0 ? index - offset : -1;
+        }
+
+        public int lastIndexOf(Object o) {
+            int index = root.lastIndexOfRange(o, offset, offset + size);
+            checkForComodification();
+            return index >= 0 ? index - offset : -1;
+        }
+
+        public boolean contains(Object o) {
+            return indexOf(o) >= 0;
+        }
+
+        public Iterator<E> iterator() {
+            return listIterator();
+        }
+
+        public ListIterator<E> listIterator(int index) {
+            checkForComodification();
+            rangeCheckForAdd(index);
+
+            return new ListIterator<E>() {
+                int cursor = index;
+                int lastRet = -1;
+                int expectedModCount = root.modCount;
+
+                public boolean hasNext() {
+                    return cursor != SubList.this.size;
+                }
+
+                @SuppressWarnings("unchecked")
+                public E next() {
+                    checkForComodification();
+                    int i = cursor;
+                    if (i >= SubList.this.size)
+                        throw new NoSuchElementException();
+                    Object[] elementData = root.elementData;
+                    if (offset + i >= elementData.length)
+                        throw new ConcurrentModificationException();
+                    cursor = i + 1;
+                    return (E) elementData[offset + (lastRet = i)];
+                }
+
+                public boolean hasPrevious() {
+                    return cursor != 0;
+                }
+
+                @SuppressWarnings("unchecked")
+                public E previous() {
+                    checkForComodification();
+                    int i = cursor - 1;
+                    if (i < 0)
+                        throw new NoSuchElementException();
+                    Object[] elementData = root.elementData;
+                    if (offset + i >= elementData.length)
+                        throw new ConcurrentModificationException();
+                    cursor = i;
+                    return (E) elementData[offset + (lastRet = i)];
+                }
+
+                public void forEachRemaining(Consumer<? super E> action) {
+                    Objects.requireNonNull(action);
+                    final int size = SubList.this.size;
+                    int i = cursor;
+                    if (i < size) {
+                        final Object[] es = root.elementData;
+                        if (offset + i >= es.length)
+                            throw new ConcurrentModificationException();
+                        for (; i < size && modCount == expectedModCount; i++)
+                            action.accept(elementAt(es, offset + i));
+                        // update once at end to reduce heap write traffic
+                        cursor = i;
+                        lastRet = i - 1;
+                        checkForComodification();
+                    }
+                }
+
+                public int nextIndex() {
+                    return cursor;
+                }
+
+                public int previousIndex() {
+                    return cursor - 1;
+                }
+
+                public void remove() {
+                    if (lastRet < 0)
+                        throw new IllegalStateException();
+                    checkForComodification();
+
+                    try {
+                        SubList.this.remove(lastRet);
+                        cursor = lastRet;
+                        lastRet = -1;
+                        expectedModCount = root.modCount;
+                    } catch (IndexOutOfBoundsException ex) {
+                        throw new ConcurrentModificationException();
+                    }
+                }
+
+                public void set(E e) {
+                    if (lastRet < 0)
+                        throw new IllegalStateException();
+                    checkForComodification();
+
+                    try {
+                        root.set(offset + lastRet, e);
+                    } catch (IndexOutOfBoundsException ex) {
+                        throw new ConcurrentModificationException();
+                    }
+                }
+
+                public void add(E e) {
+                    checkForComodification();
+
+                    try {
+                        int i = cursor;
+                        SubList.this.add(i, e);
+                        cursor = i + 1;
+                        lastRet = -1;
+                        expectedModCount = root.modCount;
+                    } catch (IndexOutOfBoundsException ex) {
+                        throw new ConcurrentModificationException();
+                    }
+                }
+
+                final void checkForComodification() {
+                    if (root.modCount != expectedModCount)
+                        throw new ConcurrentModificationException();
+                }
+            };
+        }
+
+        public List<E> subList(int fromIndex, int toIndex) {
+            subListRangeCheck(fromIndex, toIndex, size);
+            return new SubList<>(this, fromIndex, toIndex);
+        }
+
+        private void rangeCheckForAdd(int index) {
+            if (index < 0 || index > this.size)
+                throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
+        }
+
+        private String outOfBoundsMsg(int index) {
+            return "Index: "+index+", Size: "+this.size;
+        }
+
+        private void checkForComodification() {
+            if (root.modCount != modCount)
+                throw new ConcurrentModificationException();
+        }
+
+        private void updateSizeAndModCount(int sizeChange) {
+            SubList<E> slist = this;
+            do {
+                slist.size += sizeChange;
+                slist.modCount = root.modCount;
+                slist = slist.parent;
+            } while (slist != null);
+        }
+
+        public Spliterator<E> spliterator() {
+            checkForComodification();
+
+            // ArrayListSpliterator not used here due to late-binding
+            return new Spliterator<E>() {
+                private int index = offset; // current index, modified on advance/split
+                private int fence = -1; // -1 until used; then one past last index
+                private int expectedModCount; // initialized when fence set
+
+                private int getFence() { // initialize fence to size on first use
+                    int hi; // (a specialized variant appears in method forEach)
+                    if ((hi = fence) < 0) {
+                        expectedModCount = modCount;
+                        hi = fence = offset + size;
+                    }
+                    return hi;
+                }
+
+                public XArrayList<E>.ArrayListSpliterator trySplit() {
+                    int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
+                    // ArrayListSpliterator can be used here as the source is already bound
+                    return (lo >= mid) ? null : // divide range in half unless too small
+                        root.new ArrayListSpliterator(lo, index = mid, expectedModCount);
+                }
+
+                public boolean tryAdvance(Consumer<? super E> action) {
+                    Objects.requireNonNull(action);
+                    int hi = getFence(), i = index;
+                    if (i < hi) {
+                        index = i + 1;
+                        @SuppressWarnings("unchecked") E e = (E)root.elementData[i];
+                        action.accept(e);
+                        if (root.modCount != expectedModCount)
+                            throw new ConcurrentModificationException();
+                        return true;
+                    }
+                    return false;
+                }
+
+                public void forEachRemaining(Consumer<? super E> action) {
+                    Objects.requireNonNull(action);
+                    int i, hi, mc; // hoist accesses and checks from loop
+                    XArrayList<E> lst = root;
+                    Object[] a;
+                    if ((a = lst.elementData) != null) {
+                        if ((hi = fence) < 0) {
+                            mc = modCount;
+                            hi = offset + size;
+                        }
+                        else
+                            mc = expectedModCount;
+                        if ((i = index) >= 0 && (index = hi) <= a.length) {
+                            for (; i < hi; ++i) {
+                                @SuppressWarnings("unchecked") E e = (E) a[i];
+                                action.accept(e);
+                            }
+                            if (lst.modCount == mc)
+                                return;
+                        }
+                    }
+                    throw new ConcurrentModificationException();
+                }
+
+                public long estimateSize() {
+                    return getFence() - index;
+                }
+
+                public int characteristics() {
+                    return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
+                }
+            };
+        }
+    }
+
+    /**
+     * @throws NullPointerException {@inheritDoc}
+     */
+    @Override
+    public void forEach(Consumer<? super E> action) {
+        Objects.requireNonNull(action);
+        final int expectedModCount = modCount;
+        final Object[] es = elementData;
+        final int size = this.size;
+        for (int i = 0; modCount == expectedModCount && i < size; i++)
+            action.accept(elementAt(es, i));
+        if (modCount != expectedModCount)
+            throw new ConcurrentModificationException();
+    }
+
+    /**
+     * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>
+     * and <em>fail-fast</em> {@link Spliterator} over the elements in this
+     * list.
+     *
+     * <p>The {@code Spliterator} reports {@link Spliterator#SIZED},
+     * {@link Spliterator#SUBSIZED}, and {@link Spliterator#ORDERED}.
+     * Overriding implementations should document the reporting of additional
+     * characteristic values.
+     *
+     * @return a {@code Spliterator} over the elements in this list
+     * @since 1.8
+     */
+    @Override
+    public Spliterator<E> spliterator() {
+        return new ArrayListSpliterator(0, -1, 0);
+    }
+
+    /** Index-based split-by-two, lazily initialized Spliterator */
+    final class ArrayListSpliterator implements Spliterator<E> {
+
+        /*
+         * If XArrayLists were immutable, or structurally immutable (no
+         * adds, removes, etc), we could implement their spliterators
+         * with Arrays.spliterator. Instead we detect as much
+         * interference during traversal as practical without
+         * sacrificing much performance. We rely primarily on
+         * modCounts. These are not guaranteed to detect concurrency
+         * violations, and are sometimes overly conservative about
+         * within-thread interference, but detect enough problems to
+         * be worthwhile in practice. To carry this out, we (1) lazily
+         * initialize fence and expectedModCount until the latest
+         * point that we need to commit to the state we are checking
+         * against; thus improving precision.  (This doesn't apply to
+         * SubLists, that create spliterators with current non-lazy
+         * values).  (2) We perform only a single
+         * ConcurrentModificationException check at the end of forEach
+         * (the most performance-sensitive method). When using forEach
+         * (as opposed to iterators), we can normally only detect
+         * interference after actions, not before. Further
+         * CME-triggering checks apply to all other possible
+         * violations of assumptions for example null or too-small
+         * elementData array given its size(), that could only have
+         * occurred due to interference.  This allows the inner loop
+         * of forEach to run without any further checks, and
+         * simplifies lambda-resolution. While this does entail a
+         * number of checks, note that in the common case of
+         * list.stream().forEach(a), no checks or other computation
+         * occur anywhere other than inside forEach itself.  The other
+         * less-often-used methods cannot take advantage of most of
+         * these streamlinings.
+         */
+
+        private int index; // current index, modified on advance/split
+        private int fence; // -1 until used; then one past last index
+        private int expectedModCount; // initialized when fence set
+
+        /** Creates new spliterator covering the given range. */
+        ArrayListSpliterator(int origin, int fence, int expectedModCount) {
+            this.index = origin;
+            this.fence = fence;
+            this.expectedModCount = expectedModCount;
+        }
+
+        private int getFence() { // initialize fence to size on first use
+            int hi; // (a specialized variant appears in method forEach)
+            if ((hi = fence) < 0) {
+                expectedModCount = modCount;
+                hi = fence = size;
+            }
+            return hi;
+        }
+
+        public ArrayListSpliterator trySplit() {
+            int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
+            return (lo >= mid) ? null : // divide range in half unless too small
+                new ArrayListSpliterator(lo, index = mid, expectedModCount);
+        }
+
+        public boolean tryAdvance(Consumer<? super E> action) {
+            if (action == null)
+                throw new NullPointerException();
+            int hi = getFence(), i = index;
+            if (i < hi) {
+                index = i + 1;
+                @SuppressWarnings("unchecked") E e = (E)elementData[i];
+                action.accept(e);
+                if (modCount != expectedModCount)
+                    throw new ConcurrentModificationException();
+                return true;
+            }
+            return false;
+        }
+
+        public void forEachRemaining(Consumer<? super E> action) {
+            int i, hi, mc; // hoist accesses and checks from loop
+            Object[] a;
+            if (action == null)
+                throw new NullPointerException();
+            if ((a = elementData) != null) {
+                if ((hi = fence) < 0) {
+                    mc = modCount;
+                    hi = size;
+                }
+                else
+                    mc = expectedModCount;
+                if ((i = index) >= 0 && (index = hi) <= a.length) {
+                    for (; i < hi; ++i) {
+                        @SuppressWarnings("unchecked") E e = (E) a[i];
+                        action.accept(e);
+                    }
+                    if (modCount == mc)
+                        return;
+                }
+            }
+            throw new ConcurrentModificationException();
+        }
+
+        public long estimateSize() {
+            return getFence() - index;
+        }
+
+        public int characteristics() {
+            return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
+        }
+    }
+
+    // A tiny bit set implementation
+
+    private static long[] nBits(int n) {
+        return new long[((n - 1) >> 6) + 1];
+    }
+    private static void setBit(long[] bits, int i) {
+        bits[i >> 6] |= 1L << i;
+    }
+    private static boolean isClear(long[] bits, int i) {
+        return (bits[i >> 6] & (1L << i)) == 0;
+    }
+
+    /**
+     * @throws NullPointerException {@inheritDoc}
+     */
+    @Override
+    public boolean removeIf(Predicate<? super E> filter) {
+        return removeIf(filter, 0, size);
+    }
+
+    /**
+     * Removes all elements satisfying the given predicate, from index
+     * i (inclusive) to index end (exclusive).
+     */
+    boolean removeIf(Predicate<? super E> filter, int i, final int end) {
+        Objects.requireNonNull(filter);
+        int expectedModCount = modCount;
+        final Object[] es = elementData;
+        // Optimize for initial run of survivors
+        for (; i < end && !filter.test(elementAt(es, i)); i++)
+            ;
+        // Tolerate predicates that reentrantly access the collection for
+        // read (but writers still get CME), so traverse once to find
+        // elements to delete, a second pass to physically expunge.
+        if (i < end) {
+            final int beg = i;
+            final long[] deathRow = nBits(end - beg);
+            deathRow[0] = 1L;   // set bit 0
+            for (i = beg + 1; i < end; i++)
+                if (filter.test(elementAt(es, i)))
+                    setBit(deathRow, i - beg);
+            if (modCount != expectedModCount)
+                throw new ConcurrentModificationException();
+            modCount++;
+            int w = beg;
+            for (i = beg; i < end; i++)
+                if (isClear(deathRow, i - beg))
+                    es[w++] = es[i];
+            shiftTailOverGap(es, w, end);
+            return true;
+        } else {
+            if (modCount != expectedModCount)
+                throw new ConcurrentModificationException();
+            return false;
+        }
+    }
+
+    @Override
+    public void replaceAll(UnaryOperator<E> operator) {
+        replaceAllRange(operator, 0, size);
+        // TODO(8203662): remove increment of modCount from ...
+        modCount++;
+    }
+
+    private void replaceAllRange(UnaryOperator<E> operator, int i, int end) {
+        Objects.requireNonNull(operator);
+        final int expectedModCount = modCount;
+        final Object[] es = elementData;
+        for (; modCount == expectedModCount && i < end; i++)
+            es[i] = operator.apply(elementAt(es, i));
+        if (modCount != expectedModCount)
+            throw new ConcurrentModificationException();
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void sort(Comparator<? super E> c) {
+        final int expectedModCount = modCount;
+        Arrays.sort((E[]) elementData, 0, size, c);
+        if (modCount != expectedModCount)
+            throw new ConcurrentModificationException();
+        modCount++;
+    }
+
+    void checkInvariants() {
+        // assert size >= 0;
+        // assert size == elementData.length || elementData[size] == null;
+    }
+
+    /**
+     * Calculates a new array length given an array's current length, a preferred
+     * growth value, and a minimum growth value.  If the preferred growth value
+     * is less than the minimum growth value, the minimum growth value is used in
+     * its place.  If the sum of the current length and the preferred growth
+     * value does not exceed {@link #MAX_ARRAY_LENGTH}, that sum is returned.
+     * If the sum of the current length and the minimum growth value does not
+     * exceed {@code MAX_ARRAY_LENGTH}, then {@code MAX_ARRAY_LENGTH} is returned.
+     * If the sum does not overflow an int, then {@code Integer.MAX_VALUE} is
+     * returned.  Otherwise, {@code OutOfMemoryError} is thrown.
+     *
+     * @param oldLength   current length of the array (must be non negative)
+     * @param minGrowth   minimum required growth of the array length (must be
+     *                    positive)
+     * @param prefGrowth  preferred growth of the array length (ignored, if less
+     *                    then {@code minGrowth})
+     * @return the new length of the array
+     * @throws OutOfMemoryError if increasing {@code oldLength} by
+     *                    {@code minGrowth} overflows.
+     */
+    private static int newLength(int oldLength, int minGrowth, int prefGrowth) {
+        // assert oldLength >= 0
+        // assert minGrowth > 0
+
+        int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
+        if (newLength - MAX_ARRAY_LENGTH <= 0) {
+            return newLength;
+        }
+        return hugeLength(oldLength, minGrowth);
+    }
+
+    private static int hugeLength(int oldLength, int minGrowth) {
+        int minLength = oldLength + minGrowth;
+        if (minLength < 0) { // overflow
+            throw new OutOfMemoryError("Required array length too large");
+        }
+        if (minLength <= MAX_ARRAY_LENGTH) {
+            return MAX_ARRAY_LENGTH;
+        }
+        return Integer.MAX_VALUE;
+    }
+
+    private static final int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
+
+    private static void subListRangeCheck(int fromIndex, int toIndex, int size) {
+        if (fromIndex < 0)
+            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
+        if (toIndex > size)
+            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
+        if (fromIndex > toIndex)
+            throw new IllegalArgumentException("fromIndex(" + fromIndex +
+                    ") > toIndex(" + toIndex + ")");
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/XArrayListCursorTest.java b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/XArrayListCursorTest.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/XArrayListCursorTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ */
+package org.openjdk.bench.valhalla.sandbox.corelibs.corelibs;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.concurrent.TimeUnit;
+
+import org.openjdk.jmh.annotations.*;
+import org.openjdk.jmh.infra.Blackhole;
+
+@Fork(1)
+@Warmup(iterations = 3, time = 1)
+@Measurement(iterations = 5, time = 3)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class XArrayListCursorTest {
+    @Param({"100000"})
+    public static int size;
+
+    private static final String constantString = "abc";
+
+    private static XArrayList<String> list;
+
+    @Setup
+    public void setup() {
+        list = new XArrayList<>();
+        for (int i = 0; i < size; i++) {
+            list.add(constantString);
+        }
+    }
+
+    @Benchmark
+    public void getViaCursorWhileLoop(Blackhole blackhole) {
+        InlineCursor<String> cur = list.cursor();
+        while (cur.exists()) {
+            blackhole.consume(cur.get());
+            cur = cur.advance();
+        }
+    }
+
+    @Benchmark
+    public void getViaCursorForLoop(Blackhole blackhole) {
+        for (InlineCursor<String> cur = list.cursor();
+             cur.exists();
+             cur = cur.advance()) {
+            blackhole.consume(cur.get());
+        }
+    }
+
+    @Benchmark
+    public void getViaIterator(Blackhole blackhole) {
+        Iterator<String> it = list.iterator();
+        while (it.hasNext()) {
+            blackhole.consume(it.next());
+        }
+    }
+
+    @Benchmark
+    public void getViaIteratorCurs(Blackhole blackhole) {
+        Iterator<String> it = list.iteratorCurs();
+        while (it.hasNext()) {
+            blackhole.consume(it.next());
+        }
+    }
+
+    @Benchmark
+    public void getViaArray(Blackhole blackhole) {
+        for (int i = 0; i < list.size(); i++) {
+            blackhole.consume(list.get(i));
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/GetX.java b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/GetX.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/GetX.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2016, 2019, 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 org.openjdk.bench.valhalla.sandbox.corelibs.corelibs.mapprotos;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.TearDown;
+
+import org.openjdk.jmh.infra.Blackhole;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.IntFunction;
+
+public class GetX extends MapBase {
+
+    IntFunction<Map<Integer, Integer>> mapSupplier;
+    Map<Integer, Integer> map;
+    Integer[] mixed;
+
+    @Setup
+    public void setup() {
+        super.init(size);
+        try {
+            Class<?> mapClass = Class.forName(mapType);
+            mapSupplier =  (s) -> newInstance(mapClass, s);
+        } catch (Exception ex) {
+            System.out.printf("%s: %s%n", mapType, ex.getMessage());
+            return;
+        }
+
+        map = mapSupplier.apply(size);
+        for (Integer k : keys) {
+            map.put(k, k);
+        }
+
+        mixed = new Integer[size];
+        System.arraycopy(keys, 0, mixed, 0, size / 2);
+        System.arraycopy(nonKeys, 0, mixed, size / 2, size / 2);
+        Collections.shuffle(Arrays.asList(mixed), rnd);
+    }
+
+    @TearDown
+    public void teardown() {
+        super.TearDown(map);
+    }
+
+    Map<Integer, Integer> newInstance(Class<?> mapClass, int size) {
+        try {
+            return (Map<Integer, Integer>)mapClass.getConstructor(int.class).newInstance(size);
+        } catch (Exception ex) {
+            throw new RuntimeException("failed", ex);
+        }
+    }
+
+    @Benchmark
+    public void getHit(Blackhole bh) {
+        Integer[] keys = this.keys;
+        Map<Integer, Integer> map = this.map;
+        for (Integer k : keys) {
+            bh.consume(map.get(k));
+        }
+    }
+
+    @Benchmark
+    public void getMix(Blackhole bh) {
+        Integer[] keys = this.mixed;
+        Map<Integer, Integer> map = this.map;
+        for (Integer k : keys) {
+            bh.consume(map.get(k));
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/HashMap.java b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/HashMap.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/HashMap.java
@@ -0,0 +1,1889 @@
+/*
+ * 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.  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.
+ */
+
+package org.openjdk.bench.valhalla.sandbox.corelibs.corelibs.mapprotos;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.PrintStream;
+import java.io.Serializable;
+import java.lang.Boolean;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.AbstractCollection;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.TreeMap;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * HashMap using hashing and "open addressing".
+ * Hash entries are inline class instances.
+ * As described in <em>Introduction to Algorithms, 3rd Edition (The MIT Press)</em>,
+ * Section 11 Hash tables and Section 11.4 Open addressing.
+ *
+ * Open addressing is used to locate other entries for keys with the same hash.
+ * If multiple keys have the same hashcode, a rehashing mechanism
+ * is used to place the 2nd and subsequent
+ * key/value pairs at a non-optimal index in the table. Therefore,
+ * finding the entry for a desired key must rehash and examine subsequent
+ * entries until the value is value or it encounters an empty entry.
+ * When an entry is removed, the entry is marked as deleted, (not empty)
+ * to allow the search algorithm to keep looking; otherwise it would terminate
+ * the scan on the deleted entry, when it might be the case for some (other) key
+ * would have that same entry as part of its chain of possible locations for its hash.
+ * The default load factor (.75) should be re-evaluated in light of the open addressing
+ * computations.  A higher number would reduce unused (wasted) space at the cost of
+ * increased search times, a lower number would increase unused (wasted) space but
+ * improve search times (assuming even hashcode distributions).
+ * Badly distributed hash values will result in incremental table growth and
+ * linear search performance.
+ * <p>
+ * During insertion the Robin Hood hash algorithm does a small optimization
+ * to reduce worst case rehash lengths.
+ * Removal of entries, does a compaction of the following entries to fill
+ * in free entries and reduce entry rehashling lengths based on
+ * "On Deletions in Open Addressing Hashing", by Rosa M. Jimenez and Conrado Martinz.
+ *
+ * <p>
+ * The only allocation that occurs during put operations is for the resizing of the entry table.
+ *
+ * <P>
+ * Hash table based implementation of the {@code Map} interface.  This
+ * implementation provides all of the optional map operations, and permits
+ * {@code null} values and the {@code null} key.  (The {@code HashMap}
+ * class is roughly equivalent to {@code Hashtable}, except that it is
+ * unsynchronized and permits nulls.)  This class makes no guarantees as to
+ * the order of the map; in particular, it does not guarantee that the order
+ * will remain constant over time.
+ *
+ * <p>This implementation provides constant-time performance for the basic
+ * operations ({@code get} and {@code put}), assuming the hash function
+ * disperses the elements properly among the buckets.  Iteration over
+ * collection views requires time proportional to the "capacity" of the
+ * {@code HashMap} instance (the number of buckets) plus its size (the number
+ * of key-value mappings).  Thus, it's very important not to set the initial
+ * capacity too high (or the load factor too low) if iteration performance is
+ * important.
+ *
+ * <p>An instance of {@code HashMap} has two parameters that affect its
+ * performance: <i>initial capacity</i> and <i>load factor</i>.  The
+ * <i>capacity</i> is the number of buckets in the hash table, and the initial
+ * capacity is simply the capacity at the time the hash table is created.  The
+ * <i>load factor</i> is a measure of how full the hash table is allowed to
+ * get before its capacity is automatically increased.  When the number of
+ * entries in the hash table exceeds the product of the load factor and the
+ * current capacity, the hash table is <i>rehashed</i> (that is, internal data
+ * structures are rebuilt) so that the hash table has approximately twice the
+ * number of buckets.
+ *
+ * <p>As a general rule, the default load factor (.75) offers a good
+ * tradeoff between time and space costs.  Higher values decrease the
+ * space overhead but increase the lookup cost (reflected in most of
+ * the operations of the {@code HashMap} class, including
+ * {@code get} and {@code put}).  The expected number of entries in
+ * the map and its load factor should be taken into account when
+ * setting its initial capacity, so as to minimize the number of
+ * rehash operations.  If the initial capacity is greater than the
+ * maximum number of entries divided by the load factor, no rehash
+ * operations will ever occur.
+ *
+ * <p>If many mappings are to be stored in a {@code HashMap}
+ * instance, creating it with a sufficiently large capacity will allow
+ * the mappings to be stored more efficiently than letting it perform
+ * automatic rehashing as needed to grow the table.  Note that using
+ * many keys with the same {@code hashCode()} is a sure way to slow
+ * down performance of any hash table.
+ * TBD: To ameliorate impact, when keys
+ * are {@link Comparable}, this class may use comparison order among
+ * keys to help break ties.
+ *
+ * <p><strong>Note that this implementation is not synchronized.</strong>
+ * If multiple threads access a hash map concurrently, and at least one of
+ * the threads modifies the map structurally, it <i>must</i> be
+ * synchronized externally.  (A structural modification is any operation
+ * that adds or deletes one or more mappings; merely changing the value
+ * associated with a key that an instance already contains is not a
+ * structural modification.)  This is typically accomplished by
+ * synchronizing on some object that naturally encapsulates the map.
+ *
+ * If no such object exists, the map should be "wrapped" using the
+ * {@link Collections#synchronizedMap Collections.synchronizedMap}
+ * method.  This is best done at creation time, to prevent accidental
+ * unsynchronized access to the map:<pre>
+ *   Map m = Collections.synchronizedMap(new HashMap(...));</pre>
+ *
+ * <p>The iterators returned by all of this class's "collection view methods"
+ * are <i>fail-fast</i>: if the map is structurally modified at any time after
+ * the iterator is created, in any way except through the iterator's own
+ * {@code remove} method, the iterator will throw a
+ * {@link ConcurrentModificationException}.  Thus, in the face of concurrent
+ * modification, the iterator fails quickly and cleanly, rather than risking
+ * arbitrary, non-deterministic behavior at an undetermined time in the
+ * future.
+ *
+ * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
+ * as it is, generally speaking, impossible to make any hard guarantees in the
+ * presence of unsynchronized concurrent modification.  Fail-fast iterators
+ * throw {@code ConcurrentModificationException} on a best-effort basis.
+ * Therefore, it would be wrong to write a program that depended on this
+ * exception for its correctness: <i>the fail-fast behavior of iterators
+ * should be used only to detect bugs.</i>
+ *
+ * <p>This class is a member of the
+ * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
+ * Java Collections Framework</a>.
+ *
+ * @param <K> the type of keys maintained by this map
+ * @param <V> the type of mapped values
+ *
+ * @author  Doug Lea
+ * @author  Josh Bloch
+ * @author  Arthur van Hoff
+ * @author  Neal Gafter
+ * @see     Object#hashCode()
+ * @see     Collection
+ * @see     Map
+ * @see     TreeMap
+ * @see     Hashtable
+ * @since   1.2
+ */
+public class HashMap<K,V> extends XAbstractMap<K,V>
+        implements Map<K,V>, Cloneable, Serializable {
+
+    private static final long serialVersionUID = 362498820763181265L;
+
+    private static final boolean DEBUG = Boolean.getBoolean("DEBUG");
+    private static final boolean VERIFYTABLEOK = Boolean.getBoolean("VERIFYTABLEOK");
+
+    /*
+     * Implementation notes.
+     *
+     * This map usually acts as a binned (bucketed) hash table.
+     * The concurrent-programming-like SSA-based coding style helps
+     * avoid aliasing errors amid all of the twisty pointer operations.
+     */
+
+    /**
+     * The default initial capacity - MUST be a power of two.
+     */
+    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
+
+    /**
+     * The maximum capacity, used if a higher value is implicitly specified
+     * by either of the constructors with arguments.
+     * MUST be a power of two <= 1<<30.
+     */
+    static final int MAXIMUM_CAPACITY = 1 << 30;
+
+    /**
+     * The load factor used when none specified in constructor.
+     */
+    static final float DEFAULT_LOAD_FACTOR = 0.75f;
+
+    private final int REHASH_HASH = 2003; // Odd and small (a medium-small prime)
+
+    /**
+     * Basic hash bin node, used for most entries.
+     */
+    static value class YNode<K,V> implements Map.Entry<K,V> {
+        final int hash;
+        final short probes; // maybe only a byte
+        final K key;
+        final V value;
+
+        YNode() {
+            this.hash = 0;
+            this.probes = 0;
+            this.key = null;
+            this.value = null;
+        }
+
+        YNode(int hash, K key, V value, int probes) {
+            this.hash = hash;
+            this.key = key;
+            this.value = value;
+            this.probes = (short)probes;
+        }
+
+        boolean isEmpty() {
+            return probes == 0;
+        }
+
+        boolean isValue() {
+            return probes > 0;
+        }
+
+        public final K getKey()        { return key; }
+        public final V getValue()      { return value; }
+        public final String toString() { return key + "=" + value + ", probes: " + probes
+                + ", hash: " + Integer.toString(hash, 16)
+                + ", hashCode: " + hashCode(); }
+        public final int hashCode() {
+            return Objects.hashCode(key) ^ Objects.hashCode(value);
+        }
+
+        public final V setValue(V newValue) {
+            throw new IllegalStateException("YNode cannot set a value");
+        }
+
+        public final boolean equals(Object o) {
+            if (o == this)
+                return true;
+            if (o instanceof Map.Entry) {
+                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
+                if (Objects.equals(key, e.getKey()) &&
+                        Objects.equals(value, e.getValue()))
+                    return true;
+            }
+            return false;
+        }
+    }
+
+    value class YNodeWrapper implements Map.Entry<K,V> {
+        final int index;
+        final YNode<K,V> entry;
+
+        YNodeWrapper(int index) {
+            this.index = index;
+            this.entry = table[index];
+        }
+
+        public K getKey() {
+            return entry.key;
+        }
+
+        public V getValue() {
+            return entry.value;
+        }
+
+        public String toString() {
+            return entry.toString();
+        }
+
+        public int hashCode() {
+            return entry.hashCode();
+        }
+
+        public boolean equals(Object o) {
+            return entry.equals(o);
+        }
+        /**
+         * Replaces the value corresponding to this entry with the specified
+         * value (optional operation).  (Writes through to the map.)  The
+         * behavior of this call is undefined if the mapping has already been
+         * removed from the map (by the iterator's {@code remove} operation).
+         *
+         * @param value new value to be stored in this entry
+         * @return old value corresponding to the entry
+         * @throws UnsupportedOperationException if the {@code put} operation
+         *         is not supported by the backing map
+         * @throws ClassCastException if the class of the specified value
+         *         prevents it from being stored in the backing map
+         * @throws NullPointerException if the backing map does not permit
+         *         null values, and the specified value is null
+         * @throws IllegalArgumentException if some property of this value
+         *         prevents it from being stored in the backing map
+         * @throws IllegalStateException implementations may, but are not
+         *         required to, throw this exception if the entry has been
+         *         removed from the backing map.
+         */
+        public V setValue(V value) {
+            table[index] = new YNode<>(entry.hash, entry.key, value, entry.probes);
+            return entry.value;
+        }
+    }
+    /* ---------------- Static utilities -------------- */
+
+    /**
+     * Computes key.hashCode() and spreads (XORs) higher bits of hash
+     * to lower.  Because the table uses power-of-two masking, sets of
+     * hashes that vary only in bits above the current mask will
+     * always collide. (Among known examples are sets of Float keys
+     * holding consecutive whole numbers in small tables.)  So we
+     * apply a transform that spreads the impact of higher bits
+     * downward. There is a tradeoff between speed, utility, and
+     * quality of bit-spreading. Because many common sets of hashes
+     * are already reasonably distributed (so don't benefit from
+     * spreading), and because we use trees to handle large sets of
+     * collisions in bins, we just XOR some shifted bits in the
+     * cheapest possible way to reduce systematic lossage, as well as
+     * to incorporate impact of the highest bits that would otherwise
+     * never be used in index calculations because of table bounds.
+     */
+    static final int hash(Object key) {
+        int h;
+        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
+    }
+
+    /**
+     * Returns a power of two size for the given target capacity.
+     */
+    static final int tableSizeFor(int cap) {
+        int n = -1 >>> Integer.numberOfLeadingZeros(cap - 1);
+        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+    }
+
+    /* ---------------- Fields -------------- */
+
+    /**
+     * The table, initialized on first use, and resized as
+     * necessary. When allocated, length is always a power of two.
+     * (We also tolerate length zero in some operations to allow
+     * bootstrapping mechanics that are currently not needed.)
+     */
+    transient YNode<K,V>[] table;
+
+    /**
+     * Holds cached entrySet(). Note that AbstractMap fields are used
+     * for keySet() and values().
+     */
+    transient Set<Map.Entry<K,V>> entrySet;
+
+    /**
+     * The number of key-value mappings contained in this map.
+     */
+    transient int size;
+
+    /**
+     * The number of times this HashMap has been structurally modified
+     * Structural modifications are those that change the number of mappings in
+     * the HashMap or otherwise modify its internal structure (e.g.,
+     * rehash).  This field is used to make iterators on Collection-views of
+     * the HashMap fail-fast.  (See ConcurrentModificationException).
+     */
+    transient int modCount;
+
+    /**
+     * The next size value at which to resize (capacity * load factor).
+     *
+     * @serial
+     */
+    // (The javadoc description is true upon serialization.
+    // Additionally, if the table array has not been allocated, this
+    // field holds the initial array capacity, or zero signifying
+    // DEFAULT_INITIAL_CAPACITY.)
+    int threshold;
+
+    /**
+     * The load factor for the hash table.
+     *
+     * @serial
+     */
+    final float loadFactor;
+
+    private transient int[] getProbes = new int[16];
+    private transient int[] putProbes = new int[16];
+    private transient int[] notFoundProbes = new int[16];
+
+
+    /* ---------------- Public operations -------------- */
+
+    /**
+     * Constructs an empty {@code HashMap} with the specified initial
+     * capacity and load factor.
+     *
+     * @param  initialCapacity the initial capacity
+     * @param  loadFactor      the load factor
+     * @throws IllegalArgumentException if the initial capacity is negative
+     *         or the load factor is nonpositive
+     */
+    public HashMap(int initialCapacity, float loadFactor) {
+        if (initialCapacity < 0)
+            throw new IllegalArgumentException("Illegal initial capacity: " +
+                    initialCapacity);
+        if (initialCapacity > MAXIMUM_CAPACITY)
+            initialCapacity = MAXIMUM_CAPACITY;
+        if (loadFactor <= 0 || Float.isNaN(loadFactor))
+            throw new IllegalArgumentException("Illegal load factor: " +
+                    loadFactor);
+        this.loadFactor = loadFactor;
+        this.threshold = tableSizeFor(initialCapacity);
+    }
+
+    /**
+     * Constructs an empty {@code HashMap} with the specified initial
+     * capacity and the default load factor (0.75).
+     *
+     * @param  initialCapacity the initial capacity.
+     * @throws IllegalArgumentException if the initial capacity is negative.
+     */
+    public HashMap(int initialCapacity) {
+        this(initialCapacity, DEFAULT_LOAD_FACTOR);
+    }
+
+    /**
+     * Constructs an empty {@code HashMap} with the default initial capacity
+     * (16) and the default load factor (0.75).
+     */
+    public HashMap() {
+        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
+    }
+
+    /**
+     * Constructs a new {@code HashMap} with the same mappings as the
+     * specified {@code Map}.  The {@code HashMap} is created with
+     * default load factor (0.75) and an initial capacity sufficient to
+     * hold the mappings in the specified {@code Map}.
+     *
+     * @param   m the map whose mappings are to be placed in this map
+     * @throws  NullPointerException if the specified map is null
+     */
+    public HashMap(Map<? extends K, ? extends V> m) {
+        this.loadFactor = DEFAULT_LOAD_FACTOR;
+        putMapEntries(m, false);
+    }
+
+    /**
+     * Implements Map.putAll and Map constructor.
+     *
+     * @param m the map
+     * @param evict false when initially constructing this map, else true.
+     */
+    final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
+        int s = m.size();
+        if (s > 0) {
+            if (table == null) { // pre-size
+                float ft = ((float)s / loadFactor) + 1.0F;
+                int t = ((ft < (float)MAXIMUM_CAPACITY) ?
+                        (int)ft : MAXIMUM_CAPACITY);
+                if (t > threshold)
+                    threshold = tableSizeFor(t);
+            } else {
+                // Because of linked-list bucket constraints, we cannot
+                // expand all at once, but can reduce total resize
+                // effort by repeated doubling now vs later
+                while (s > threshold && table.length < MAXIMUM_CAPACITY)
+                    resize();
+            }
+
+            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
+                K key = e.getKey();
+                V value = e.getValue();
+                putVal(hash(key), key, value, false);
+            }
+        }
+    }
+
+    /**
+     * Returns the number of key-value mappings in this map.
+     *
+     * @return the number of key-value mappings in this map
+     */
+    public int size() {
+        return size;
+    }
+
+    /**
+     * Returns {@code true} if this map contains no key-value mappings.
+     *
+     * @return {@code true} if this map contains no key-value mappings
+     */
+    public boolean isEmpty() {
+        return size == 0;
+    }
+
+    /**
+     * Returns the value to which the specified key is mapped,
+     * or {@code null} if this map contains no mapping for the key.
+     *
+     * <p>More formally, if this map contains a mapping from a key
+     * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
+     * key.equals(k))}, then this method returns {@code v}; otherwise
+     * it returns {@code null}.  (There can be at most one such mapping.)
+     *
+     * <p>A return value of {@code null} does not <i>necessarily</i>
+     * indicate that the map contains no mapping for the key; it's also
+     * possible that the map explicitly maps the key to {@code null}.
+     * The {@link #containsKey containsKey} operation may be used to
+     * distinguish these two cases.
+     *
+     * @see #put(Object, Object)
+     */
+    public V get(Object key) {
+        final YNode<K, V>[] tab;
+        final int mask;
+//        int probes = 0;
+        if ((tab = table) != null && (mask = tab.length - 1) >= 0) {
+            final int hash = hash(key);
+            int h = hash;
+            YNode<K, V> entry;
+            while ((entry = tab[(mask & h)]).isValue()) {
+//                probes++;
+                K k;
+                if (entry.hash == hash &&
+                        ((k = entry.key) == key || (key != null && key.equals(k)))) {
+//                    getProbes = incProbeCount(getProbes, probes);
+                    return entry.value;
+                } else {
+                    h += REHASH_HASH;
+                }
+            }
+        }
+//        notFoundProbes = incProbeCount(notFoundProbes, 0);
+        return null;      // not found; empty table
+    }
+
+    /**
+     * Same as Get caching the entry.
+     * @param key the key
+     * @return a value, or null
+     */
+    public V get1(Object key) {
+        final int hash = hash(key);
+        final YNode<K, V>[] tab;
+        int n;
+        if ((tab = table) != null && (n = tab.length) > 0) {
+            int h = hash;
+            int index = (n - 1) & h;
+            YNode<K, V> entry = tab[index];
+            for (; //int probes = 1
+                 entry.isValue();
+                 h += REHASH_HASH, index = (n - 1) & h, entry = tab[index]) {  //, probes++
+                K k;
+                if (entry.hash == hash &&
+                        ((k = entry.key) == key || (key != null && key.equals(k)))) {
+//                    getProbes = incProbeCount(getProbes, probes);
+                    return entry.value;
+                }
+            }
+        }
+//        notFoundProbes = incProbeCount(notFoundProbes, 0);
+        return null;      // not found; empty table
+    }
+
+    /**
+     * Implements Map.get and related methods.
+     *
+     * @param hash hash for key
+     * @param key the key
+     * @return the index of a matching node or -1
+     */
+    private final int getNode(final int hash, Object key) {
+        YNode<K, V>[] tab;
+        int n;
+        if ((tab = table) != null && (n = tab.length) > 0) {
+            final YNode<K, V> first;
+            final int i = (n - 1) & hash;
+            K k;
+            if ((first = tab[i]).isValue() &&
+                    first.hash == hash &&
+                    ((k = first.key) == key || (key != null && key.equals(k)))) {
+//                getProbes = incProbeCount(getProbes, 1);
+                return i;
+            }
+            // non-empty table and not first entry
+            int h = hash;
+            for (int probes = 1; probes <= tab.length; probes++, h += REHASH_HASH) {
+                final int index = (n - 1) & h;
+                final YNode<K, V> entry = tab[index];
+                if (!entry.isValue()) {
+//                    notFoundProbes = incProbeCount(notFoundProbes, probes);
+                    return -1;  // search ended without finding the key
+                } else if (entry.hash == hash &&
+                        ((k = entry.key) == key || (key != null && key.equals(k)))) {
+//                    getProbes = incProbeCount(getProbes, probes);
+                    return index;
+                }
+            }
+        }
+//        notFoundProbes = incProbeCount(notFoundProbes, 0);
+        return -1;      // not found; empty table
+    }
+
+    /**
+     * Returns {@code true} if this map contains a mapping for the
+     * specified key.
+     *
+     * @param   key   The key whose presence in this map is to be tested
+     * @return {@code true} if this map contains a mapping for the specified
+     * key.
+     */
+    public boolean containsKey(Object key) {
+        return getNode(hash(key), key) >= 0;
+    }
+
+    /**
+     * Associates the specified value with the specified key in this map.
+     * If the map previously contained a mapping for the key, the old
+     * value is replaced.
+     *
+     * @param key key with which the specified value is to be associated
+     * @param value value to be associated with the specified key
+     * @return the previous value associated with {@code key}, or
+     *         {@code null} if there was no mapping for {@code key}.
+     *         (A {@code null} return can also indicate that the map
+     *         previously associated {@code null} with {@code key}.)
+     */
+    public V put(K key, V value) {
+        return putVal(hash(key), key, value, false);
+    }
+
+    /**
+     * Implements Map.put and related methods.
+     *
+     * @param hash hash for key
+     * @param key the key
+     * @param value the value to put
+     * @param onlyIfAbsent if true, don't change existing value
+     * @return previous value, or null if none
+     */
+    private final V putVal(final int hash, final K key, final V value, boolean onlyIfAbsent) {
+        YNode<K, V>[] tab;
+        YNode<K, V> tp;
+        int n, i;
+        if ((tab = table) == null || (n = tab.length) == 0)
+            n = (tab = resize()).length;
+        debug("  putval", -1, new YNode<K,V>(hash, key, value, -1));
+
+        int h = hash;
+        int insert = -1;    // insertion point if not already present
+        int insertProbes = -1;
+        for (int probes = 1; ; probes++, h += REHASH_HASH) {
+            if (probes > tab.length)
+                throw new IllegalStateException("No empty entries in table");
+            final int index;
+            final YNode<K, V> entry = tab[(index = ((n - 1) & h))];
+//            debug("    looking at", index, entry);
+            if (entry.isEmpty()) {
+                // Absent; insert in the first place it could be added
+                // TBD: should it check onlyIfAbsent?
+                if (insert < 0) {
+                    // no better place to insert than here
+                    tab[index] = new YNode<>(hash, key, value, probes);
+                    debug("    setting", index, tab[index]);
+//                    putProbes = incProbeCount(putProbes, probes);
+                } else {
+                    // The new entry is more needy than the current one
+                    final YNode<K,V> tmp = tab[insert];
+                    tab[insert] = new YNode<>(hash, key, value, insertProbes);
+                    debug("    robin-hood inserted", index, tab[index]);
+//                    putProbes = incProbeCount(putProbes, insertProbes);
+                    putReinsert(insert, tmp);
+                }
+                break;  // break to update modCount and size
+            }
+
+            if (entry.isValue() && entry.hash == hash &&
+                    (entry.key == key || (key != null && key.equals(entry.key)))) {
+                // TBD: consider if updated entry should be moved closer to the front
+                if (!onlyIfAbsent || entry.value == null) {
+                    tab[index] = new YNode<>(hash, key, value, entry.probes);
+                }
+                debug("    oldvalue", index, entry);
+                debug("    updating", index, tab[index]);
+//                putProbes = incProbeCount(putProbes, probes);
+                return entry.value;
+            }
+
+            // Save first possible insert index
+            if (insert < 0 && probes > entry.probes) {
+                insert = index;
+                insertProbes = probes;
+            }
+        }
+        ++modCount;
+        ++size;
+        isTableOk(tab, "table not ok, putval");
+        if (size >= threshold)
+            resize();       // Ensure there is at least 1 empty available
+        return null;
+    }
+
+    /**
+     * Re-insert the entry in the table starting at the entry beyond the index.
+     * Insert it at an empty slot.
+     * Replace an entry with a lower probe count and repeat to reinsert that entry.
+     * @param oldIndex the index just replaced
+     * @param rEntry the entry to be replaced
+     */
+    private void putReinsert(final int oldIndex, YNode<K,V> rEntry) {
+        final YNode<K, V>[] tab = table;
+        final int n = tab.length;
+
+        int h = oldIndex + REHASH_HASH;
+        for (int probes = rEntry.probes + 1; probes <= n; probes++, h += REHASH_HASH) {
+            isTableOk(tab, "bubble down loop");
+            final int index = (n - 1) & h;
+            final YNode<K,V> entry = tab[index];
+            if (entry.isEmpty()) {
+                // Insert in the empty slot
+                tab[index] = new YNode<>(rEntry.hash, rEntry.key, rEntry.value, probes);
+                debug("    reinserted", index, tab[index]);
+                return;
+            } else if (probes > entry.probes) {
+                // Replace a less deserving entry
+                tab[index] = new YNode<>(rEntry.hash, rEntry.key, rEntry.value, probes);
+                debug("    robin-hood bubble down", index, tab[index]);
+                rEntry = entry;
+                probes = rEntry.probes;
+                debug("    robin-hood moving", index, rEntry);
+            } else {
+                debug("    robin-hood skipping", index, entry);
+            }
+        }
+        throw new RuntimeException("bubble down failed");
+    }
+
+    private void debug(String msg, int index, YNode<K,V> entry) {
+        if (DEBUG && System.out != null) {
+            System.out.println(System.identityHashCode(this) + ": " + msg + ": index: " + index + ", node: " + Objects.toString(entry));
+        }
+    }
+   private void debug2(String msg, int index, YNode<K,V> entry) {
+        if (System.out != null) {
+            System.out.println(System.identityHashCode(this) + ": " + msg + ": index: " + index +
+                    ", " + "node: " + entry);
+        }
+    }
+
+    /**
+     * Initializes or doubles table size.  If null, allocates in
+     * accord with initial capacity target held in field threshold.
+     * Otherwise, because we are using power-of-two expansion, the
+     * elements from each bin must either stay at same index, or move
+     * with a power of two offset in the new table.
+     *
+     * @return the table
+     */
+    final YNode<K,V>[] resize() {
+        YNode<K,V>[] oldTab = table;
+        int oldCap = (oldTab == null) ? 0 : oldTab.length;
+        int oldThr = threshold;
+        int newCap, newThr = 0;
+        if (oldCap > 0) {
+            if (oldCap >= MAXIMUM_CAPACITY) {
+                threshold = Integer.MAX_VALUE;
+                return oldTab;
+            }
+            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
+                     oldCap >= DEFAULT_INITIAL_CAPACITY)
+                newThr = oldThr << 1; // double threshold
+        }
+        else if (oldThr > 0) // initial capacity was placed in threshold
+            newCap = oldThr;
+        else {               // zero initial threshold signifies using defaults
+            newCap = DEFAULT_INITIAL_CAPACITY;
+            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
+        }
+        if (newThr == 0) {
+            float ft = (float)newCap * loadFactor;
+            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
+                    (int)ft : Integer.MAX_VALUE);
+        }
+        isTableOk(oldTab, "oldTab bad before resize");
+        if (getProbes != null)
+            Arrays.fill(getProbes, 0);
+        if (putProbes != null)
+            Arrays.fill(putProbes, 0);
+        if (notFoundProbes != null)
+            Arrays.fill(notFoundProbes, 0);
+
+        // There must always be an empty entry, resize when it gets to capacity.
+        threshold = (newThr > newCap) ? newCap : newThr;
+        @SuppressWarnings({"rawtypes","unchecked"})
+        YNode<K,V>[] newTab = (YNode<K,V>[])new YNode[newCap];
+        table = newTab;
+        if (oldTab != null) {
+            for (int i = 0; i < oldCap; ++i) {
+                YNode<K,V> e;
+                if ((e = oldTab[i]).isValue()) {
+                    final int ii;
+                    if (newTab[ii = (newCap - 1) & e.hash].isEmpty()) {
+                        newTab[ii] = new YNode<>(e.hash, e.key, e.value, 1);
+                        putProbes = incProbeCount(putProbes, 1);
+                    } else {
+                        int h = e.hash + REHASH_HASH;
+                        for (int probes = 2; ; probes++, h += REHASH_HASH) {
+                            final int index;
+                            if (newTab[(index = ((newCap - 1) & h))].isEmpty()) {
+                                newTab[index] = new YNode<>(e.hash, e.key, e.value, probes);
+                                putProbes = incProbeCount(putProbes, probes);
+                                break;
+                            }
+                            // TBD: Consider Robin-hood insert
+                            if (probes > newTab.length)
+                                throw new IllegalStateException("NYI resize: no support for overflow");
+                        }
+                    }
+                }
+            }
+        }
+
+        debug("resized", newTab.length, new YNode<K,V>());
+        isTableOk(newTab, "newTab bad after resize");
+        return newTab;
+    }
+
+    /**
+     * Dump the hashtable.
+     */
+    public void dumpTable() {
+        dumpTable(table, "dumpTable");
+    }
+
+    /**
+     * Dump the hashtable
+     * @param table the table
+     * @param msg a message
+     */
+    private void dumpTable(YNode<K, V>[] table, String msg) {
+        if (System.out == null || table == null)
+            return;
+        System.out.println(msg + ", size: " + size + ", len: " + table.length);
+        for (int i = 0; i < table.length; ++i) {
+            if (table[i].isValue())
+                System.out.println("   [" + i + "] " + table[i]);
+        }
+    }
+
+    /**
+     * Copies all of the mappings from the specified map to this map.
+     * These mappings will replace any mappings that this map had for
+     * any of the keys currently in the specified map.
+     *
+     * @param m mappings to be stored in this map
+     * @throws NullPointerException if the specified map is null
+     */
+    public void putAll(Map<? extends K, ? extends V> m) {
+        putMapEntries(m, true);
+    }
+
+    /**
+     * Removes the mapping for the specified key from this map if present.
+     *
+     * @param  key key whose mapping is to be removed from the map
+     * @return the previous value associated with {@code key}, or
+     *         {@code null} if there was no mapping for {@code key}.
+     *         (A {@code null} return can also indicate that the map
+     *         previously associated {@code null} with {@code key}.)
+     */
+    public V remove(Object key) {
+        YNode<K,V> entry = removeNode(hash(key), key, null, false, true);
+        return entry.isValue() ? entry.value : null;
+    }
+
+    /**
+     * Implements Map.remove and related methods.
+     *
+     * @param hash hash for key
+     * @param key the key
+     * @param value the value to match if matchValue, else ignored
+     * @param matchValue if true only remove if value is equal
+     * @param movable if false do not move other nodes while removing
+     * @return the node, or null if none
+     */
+    @SuppressWarnings("unchecked")
+    private final YNode<K,V> removeNode(final int hash, final Object key, final Object value,
+                                         boolean matchValue, boolean movable) {
+        YNode<K, V>[] tab;
+        YNode<K, V> entry;
+        V v = null;
+        int curr;
+        int n;
+        debug("  removeNode", -2, new YNode<K,V>(hash, (K)key, (V)value, -2));
+
+        if ((tab = table) != null && (n = tab.length) > 0 &&
+                (curr = getNode(hash, key)) >= 0 &&
+                (entry = tab[curr]).isValue() &&
+                ((!matchValue || (v = entry.value) == value ||
+                        (value != null && value.equals(v))))) {
+            // found entry; free and compress
+            removeNode(curr);
+            return entry;
+        }
+        return new YNode();
+    }
+
+    @SuppressWarnings("unchecked")
+    private void removeNode(final int curr) {
+        final YNode<K, V>[] tab = table;;
+        final int n = tab.length;
+
+        ++modCount;
+        --size;
+        int free = curr;        // The entry to be cleared, if not replaced
+        int h = curr;
+        int probes = 0;
+        do {
+            probes++;
+            h += REHASH_HASH;
+            final int index = (n - 1) & h;
+            final YNode<K, V> entry = tab[index];
+            if (entry.probes == 0) {
+                // Search ended at empty entry, clear the free entry
+                debug("    clearing", index, entry);
+                tab[free] = new YNode<>();
+                return;
+            }
+            if (entry.probes > probes) {
+                // move up the entry, skip if it is already in the best spot
+                debug("    replacing", free, entry);
+                tab[free] = new YNode<>(entry.hash, entry.key, entry.value, entry.probes - probes);
+                debug("         with", free, tab[free]);
+                free = index;
+                probes = 0;
+            }
+        } while (((n - 1) & h) != curr);
+        isTableOk(tab, "table not ok, not found");
+        throw new RuntimeException("removeNode too many probes");
+    }
+
+    /**
+     * Removes all of the mappings from this map.
+     * The map will be empty after this call returns.
+     */
+    @SuppressWarnings({"rawtypes","unchecked"})
+    public void clear() {
+        YNode<K,V>[] tab;
+        modCount++;
+        if ((tab = table) != null && size > 0) {
+            size = 0;
+            for (int i = 0; i < tab.length; i++)
+                tab[i] = new YNode();
+        }
+    }
+
+    /**
+     * Returns {@code true} if this map maps one or more keys to the
+     * specified value.
+     *
+     * @param value value whose presence in this map is to be tested
+     * @return {@code true} if this map maps one or more keys to the
+     *         specified value
+     */
+    public boolean containsValue(Object value) {
+        YNode<K,V>[] tab; V v;
+        if ((tab = table) != null && size > 0) {
+            for (YNode<K,V> te : tab) {
+                if (te.isValue()) {
+                    if ((v = te.value) == value ||
+                            (value != null && value.equals(v)))
+                        return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns a {@link Set} view of the keys contained in this map.
+     * The set is backed by the map, so changes to the map are
+     * reflected in the set, and vice-versa.  If the map is modified
+     * while an iteration over the set is in progress (except through
+     * the iterator's own {@code remove} operation), the results of
+     * the iteration are undefined.  The set supports element removal,
+     * which removes the corresponding mapping from the map, via the
+     * {@code Iterator.remove}, {@code Set.remove},
+     * {@code removeAll}, {@code retainAll}, and {@code clear}
+     * operations.  It does not support the {@code add} or {@code addAll}
+     * operations.
+     *
+     * @return a set view of the keys contained in this map
+     */
+    public Set<K> keySet() {
+        Set<K> ks = keySet;
+        if (ks == null) {
+            ks = new KeySet();
+            keySet = ks;
+        }
+        return ks;
+    }
+
+    /**
+     * Prepares the array for {@link Collection#toArray(Object[])} implementation.
+     * If supplied array is smaller than this map size, a new array is allocated.
+     * If supplied array is bigger than this map size, a null is written at size index.
+     *
+     * @param a an original array passed to {@code toArray()} method
+     * @param <T> type of array elements
+     * @return an array ready to be filled and returned from {@code toArray()} method.
+     */
+    @SuppressWarnings("unchecked")
+    final <T> T[] prepareArray(T[] a) {
+        int size = this.size;
+        if (a.length < size) {
+            return (T[]) java.lang.reflect.Array
+                    .newInstance(a.getClass().getComponentType(), size);
+        }
+        if (a.length > size) {
+            a[size] = null;
+        }
+        return a;
+    }
+
+    /**
+     * Fills an array with this map keys and returns it. This method assumes
+     * that input array is big enough to fit all the keys. Use
+     * {@link #prepareArray(Object[])} to ensure this.
+     *
+     * @param a an array to fill
+     * @param <T> type of array elements
+     * @return supplied array
+     */
+    <T> T[] keysToArray(T[] a) {
+        Object[] r = a;
+        YNode<K,V>[] tab;
+        int idx = 0;
+        if (size > 0 && (tab = table) != null) {
+            for (YNode<K,V> te : tab) {
+                if (te.isValue()) {
+                    r[idx++] = te.key;
+                }
+            }
+        }
+        return a;
+    }
+
+    /**
+     * Fills an array with this map values and returns it. This method assumes
+     * that input array is big enough to fit all the values. Use
+     * {@link #prepareArray(Object[])} to ensure this.
+     *
+     * @param a an array to fill
+     * @param <T> type of array elements
+     * @return supplied array
+     */
+    <T> T[] valuesToArray(T[] a) {
+        Object[] r = a;
+        YNode<K,V>[] tab;
+        int idx = 0;
+        if (size > 0 && (tab = table) != null) {
+            for (YNode<K,V> te : tab) {
+                if (te.isValue()) {
+                    r[idx++] = te.value;
+                }
+            }
+        }
+        return a;
+    }
+
+    final class KeySet extends AbstractSet<K> {
+        public final int size()                 { return size; }
+        public final void clear()               { HashMap.this.clear(); }
+        public final Iterator<K> iterator()     { return new KeyIterator(); }
+        public final boolean contains(Object o) { return containsKey(o); }
+        public final boolean remove(Object key) {
+            return removeNode(hash(key), key, null, false, true).isValue();
+        }
+
+        public Object[] toArray() {
+            return keysToArray(new Object[size]);
+        }
+
+        public <T> T[] toArray(T[] a) {
+            return keysToArray(prepareArray(a));
+        }
+
+        public final void forEach(Consumer<? super K> action) {
+            YNode<K,V>[] tab;
+            if (action == null)
+                throw new NullPointerException();
+            if (size > 0 && (tab = table) != null) {
+                int mc = modCount;
+                for (YNode<K,V> te : tab) {
+                    if (te.isValue()) {
+                        action.accept(te.key);
+                    }
+                }
+                if (modCount != mc)
+                    throw new ConcurrentModificationException();
+            }
+        }
+    }
+
+    /**
+     * Returns a {@link Collection} view of the values contained in this map.
+     * The collection is backed by the map, so changes to the map are
+     * reflected in the collection, and vice-versa.  If the map is
+     * modified while an iteration over the collection is in progress
+     * (except through the iterator's own {@code remove} operation),
+     * the results of the iteration are undefined.  The collection
+     * supports element removal, which removes the corresponding
+     * mapping from the map, via the {@code Iterator.remove},
+     * {@code Collection.remove}, {@code removeAll},
+     * {@code retainAll} and {@code clear} operations.  It does not
+     * support the {@code add} or {@code addAll} operations.
+     *
+     * @return a view of the values contained in this map
+     */
+    public Collection<V> values() {
+        Collection<V> vs = values;
+        if (vs == null) {
+            vs = new Values();
+            values = vs;
+        }
+        return vs;
+    }
+
+    final class Values extends AbstractCollection<V> {
+        public final int size()                 { return size; }
+        public final void clear()               { HashMap.this.clear(); }
+        public final Iterator<V> iterator()     { return new ValueIterator(); }
+        public final boolean contains(Object o) { return containsValue(o); }
+
+        public Object[] toArray() {
+            return valuesToArray(new Object[size]);
+        }
+
+        public <T> T[] toArray(T[] a) {
+            return valuesToArray(prepareArray(a));
+        }
+
+        public final void forEach(Consumer<? super V> action) {
+            YNode<K,V>[] tab;
+            if (action == null)
+                throw new NullPointerException();
+            if (size > 0 && (tab = table) != null) {
+                int mc = modCount;
+                for (YNode<K,V> te : tab) {
+                    if (te.isValue()) {
+                        action.accept(te.value);
+                    }
+                }
+                if (modCount != mc)
+                    throw new ConcurrentModificationException();
+            }
+        }
+    }
+
+    /**
+     * Returns a {@link Set} view of the mappings contained in this map.
+     * The set is backed by the map, so changes to the map are
+     * reflected in the set, and vice-versa.  If the map is modified
+     * while an iteration over the set is in progress (except through
+     * the iterator's own {@code remove} operation, or through the
+     * {@code setValue} operation on a map entry returned by the
+     * iterator) the results of the iteration are undefined.  The set
+     * supports element removal, which removes the corresponding
+     * mapping from the map, via the {@code Iterator.remove},
+     * {@code Set.remove}, {@code removeAll}, {@code retainAll} and
+     * {@code clear} operations.  It does not support the
+     * {@code add} or {@code addAll} operations.
+     *
+     * @return a set view of the mappings contained in this map
+     */
+    public Set<Map.Entry<K,V>> entrySet() {
+        Set<Map.Entry<K,V>> es;
+        return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
+    }
+
+    final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
+        public final int size()                 { return size; }
+        public final void clear()               { HashMap.this.clear(); }
+        public final Iterator<Map.Entry<K,V>> iterator() {
+            return new EntryIterator();
+        }
+        public final boolean contains(Object o) {
+            if (!(o instanceof Map.Entry))
+                return false;
+            Map.Entry<?,?> e = (Map.Entry<?,?>) o;
+            Object key = e.getKey();
+            int index = getNode(hash(key), key);
+            return index >= 0 && table[index].equals(e);
+        }
+        public final boolean remove(Object o) {
+            if (o instanceof Map.Entry) {
+                Map.Entry<?,?> e = (Map.Entry<?,?>) o;
+                Object key = e.getKey();
+                Object value = e.getValue();
+                return removeNode(hash(key), key, value, true, true).isValue();
+            }
+            return false;
+        }
+
+        public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
+            YNode<K,V>[] tab;
+            if (action == null)
+                throw new NullPointerException();
+            if (size > 0 && (tab = table) != null) {
+                int mc = modCount;
+                for (YNode<K,V> te : tab) {
+                    if (te.isValue()) {
+                        action.accept(new YNodeWrapper(te.hash & (tab.length - 1)));
+                    }
+                }
+                if (modCount != mc)
+                    throw new ConcurrentModificationException();
+            }
+        }
+    }
+
+    // Overrides of JDK8 Map extension methods
+
+    @Override
+    public V getOrDefault(Object key, V defaultValue) {
+        final int index;
+        return (index = getNode(hash(key), key)) < 0 ? defaultValue : table[index].value;
+    }
+
+    @Override
+    public V putIfAbsent(K key, V value) {
+        return putVal(hash(key), key, value, true);
+    }
+
+    @Override
+    public boolean remove(Object key, Object value) {
+        return removeNode(hash(key), key, value, true, true).isValue();
+    }
+
+    @Override
+    public boolean replace(K key, V oldValue, V newValue) {
+        int hash, index;
+        V v;
+        if ((index = getNode((hash = hash(key)), key)) >= 0 &&
+                ((v = table[index].value) == oldValue || (v != null && v.equals(oldValue)))) {
+            table[index] = new YNode<>(hash, key, newValue, table[index].probes);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public V replace(K key, V value) {
+        int hash, index;
+        V v;
+        if ((index = getNode((hash = hash(key)), key)) >= 0) {
+            V oldValue = table[index].value;
+            table[index] = new YNode<>(hash, key, value, table[index].probes);
+            return oldValue;
+        }
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This method will, on a best-effort basis, throw a
+     * {@link ConcurrentModificationException} if it is detected that the
+     * mapping function modifies this map during computation.
+     *
+     * @throws ConcurrentModificationException if it is detected that the
+     * mapping function modified this map
+     */
+    @Override
+    public V computeIfAbsent(K key,
+                             Function<? super K, ? extends V> mappingFunction) {
+        if (mappingFunction == null)
+            throw new NullPointerException();
+        int hash = hash(key);
+        YNode<K,V>[] tab = table;
+        YNode<K,V> entry;
+        int index;
+
+        index = getNode(hash, key);
+        if (index >= 0 && (entry = tab[index]).value != null) {
+            return entry.value;
+        }
+        int mc = modCount;
+        V v = mappingFunction.apply(key);
+        if (mc != modCount) { throw new ConcurrentModificationException(); }
+        if (v == null) {
+            return null;
+        }
+        putVal(hash, key, v, false);
+        return v;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This method will, on a best-effort basis, throw a
+     * {@link ConcurrentModificationException} if it is detected that the
+     * remapping function modifies this map during computation.
+     *
+     * @throws ConcurrentModificationException if it is detected that the
+     * remapping function modified this map
+     */
+    @Override
+    public V computeIfPresent(K key,
+                              BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+        if (remappingFunction == null)
+            throw new NullPointerException();
+        YNode<K,V> oldValue;
+        int hash = hash(key);
+        int index = getNode(hash, key);
+        if (index >= 0 && (oldValue = table[index]).value != null) {
+            int mc = modCount;
+            V v = remappingFunction.apply(key, oldValue.value);
+            if (mc != modCount) { throw new ConcurrentModificationException(); }
+            if (v != null) {
+                table[index] = new YNode<>(hash, key, v, oldValue.probes);
+                return v;
+            } else
+                removeNode(hash, key, null, false, true);
+        }
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This method will, on a best-effort basis, throw a
+     * {@link ConcurrentModificationException} if it is detected that the
+     * remapping function modifies this map during computation.
+     *
+     * @throws ConcurrentModificationException if it is detected that the
+     * remapping function modified this map
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public V compute(K key,
+                     BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+        if (remappingFunction == null)
+            throw new NullPointerException();
+
+        int hash = hash(key);
+        int index = getNode(hash, key);
+        YNode<K,V> oldValue = (index >= 0) ? table[index] : new YNode();
+
+        int mc = modCount;
+        V v = remappingFunction.apply(key, oldValue.value);
+        if (mc != modCount) { throw new ConcurrentModificationException(); }
+        if (v != null) {
+            if (index >= 0) {
+                table[index] = new YNode<>(hash, key, v, oldValue.probes);
+//                modCount++;
+            } else
+                putVal(hash, key, v, false);
+        } else
+            // TBD: 2nd lookup to remove even though we have index
+            removeNode(hash, key, null, false, true);
+        return v;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This method will, on a best-effort basis, throw a
+     * {@link ConcurrentModificationException} if it is detected that the
+     * remapping function modifies this map during computation.
+     *
+     * @throws ConcurrentModificationException if it is detected that the
+     * remapping function modified this map
+     */
+    @Override
+    public V merge(K key, V value,
+                   BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+        if (remappingFunction == null)
+            throw new NullPointerException();
+
+        final int hash = hash(key);
+        final int index = getNode(hash, key);
+        if (index >= 0) {
+            int mc = modCount;
+            V v = remappingFunction.apply(table[index].value, value);
+            if (mc != modCount) { throw new ConcurrentModificationException(); }
+            if (v != null) {
+                if (index >= 0) {
+                    table[index] = new YNode<>(hash, key, v, table[index].probes);
+//                    modCount++;
+                } else
+                    putVal(hash, key, v, false);
+            } else {
+                // TBD: 2nd lookup to remove even though we have index
+                removeNode(hash, key, null, false, true);
+            }
+            return v;
+        } else {
+            // put new key/value (even if null)
+            putVal(hash, key, value, false);
+        }
+        return value;
+    }
+
+    @Override
+    public void forEach(BiConsumer<? super K, ? super V> action) {
+        YNode<K,V>[] tab;
+        if (action == null)
+            throw new NullPointerException();
+        if (size > 0 && (tab = table) != null) {
+            int mc = modCount;
+            for (YNode<K,V> te : tab) {
+                if (te.isValue()) {
+                    action.accept(te.key, te.value);
+                }
+            }
+            if (modCount != mc)
+                throw new ConcurrentModificationException();
+        }
+    }
+
+    @Override
+    public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
+        super.replaceAll(function);
+    }
+
+    /* ------------------------------------------------------------ */
+    // Cloning and serialization
+
+    /**
+     * Returns a shallow copy of this {@code HashMap} instance: the keys and
+     * values themselves are not cloned.
+     *
+     * @return a shallow copy of this map
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public Object clone() {
+        HashMap<K,V> result;
+        try {
+            result = (HashMap<K,V>)super.clone();
+        } catch (CloneNotSupportedException e) {
+            // this shouldn't happen, since we are Cloneable
+            throw new InternalError(e);
+        }
+        result.reinitialize();
+        result.putMapEntries(this, false);
+        return result;
+    }
+
+    // These methods are also used when serializing HashSets
+    final float loadFactor() { return loadFactor; }
+    final int capacity() {
+        return (table != null) ? table.length :
+                (threshold > 0) ? threshold :
+                DEFAULT_INITIAL_CAPACITY;
+    }
+
+    /**
+     * Saves this map to a stream (that is, serializes it).
+     *
+     * @param s the stream
+     * @throws IOException if an I/O error occurs
+     * @serialData The <i>capacity</i> of the HashMap (the length of the
+     *             bucket array) is emitted (int), followed by the
+     *             <i>size</i> (an int, the number of key-value
+     *             mappings), followed by the key (Object) and value (Object)
+     *             for each key-value mapping.  The key-value mappings are
+     *             emitted in no particular order.
+     */
+    private void writeObject(java.io.ObjectOutputStream s)
+        throws IOException {
+        int buckets = capacity();
+        // Write out the threshold, loadfactor, and any hidden stuff
+        s.defaultWriteObject();
+        s.writeInt(buckets);
+        s.writeInt(size);
+        internalWriteEntries(s);
+    }
+
+    /**
+     * Reconstitutes this map from a stream (that is, deserializes it).
+     * @param s the stream
+     * @throws ClassNotFoundException if the class of a serialized object
+     *         could not be found
+     * @throws IOException if an I/O error occurs
+     */
+    private void readObject(java.io.ObjectInputStream s)
+        throws IOException, ClassNotFoundException {
+        // Read in the threshold (ignored), loadfactor, and any hidden stuff
+        s.defaultReadObject();
+        reinitialize();
+        if (loadFactor <= 0 || Float.isNaN(loadFactor))
+            throw new InvalidObjectException("Illegal load factor: " +
+                    loadFactor);
+        s.readInt();                // Read and ignore number of buckets
+        int mappings = s.readInt(); // Read number of mappings (size)
+        if (mappings < 0)
+            throw new InvalidObjectException("Illegal mappings count: " +
+                    mappings);
+        else if (mappings > 0) { // (if zero, use defaults)
+            // Size the table using given load factor only if within
+            // range of 0.25...4.0
+            float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
+            float fc = (float)mappings / lf + 1.0f;
+            int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
+                    DEFAULT_INITIAL_CAPACITY :
+                    (fc >= MAXIMUM_CAPACITY) ?
+                            MAXIMUM_CAPACITY :
+                            tableSizeFor((int)fc));
+            float ft = (float)cap * lf;
+            threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
+                    (int)ft : Integer.MAX_VALUE);
+
+            // Check Map.Entry[].class since it's the nearest public type to
+            // what we're actually creating.
+//            SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Map.Entry[].class, cap);
+            @SuppressWarnings({"rawtypes","unchecked"})
+            YNode<K,V>[] tab = (YNode<K,V>[])new YNode[cap];
+            table = tab;
+
+            // Read the keys and values, and put the mappings in the HashMap
+            for (int i = 0; i < mappings; i++) {
+                @SuppressWarnings("unchecked")
+                K key = (K) s.readObject();
+                @SuppressWarnings("unchecked")
+                V value = (V) s.readObject();
+                putVal(hash(key), key, value, false);
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    // iterators
+
+    abstract class HashIterator {
+        int next;        // next entry to return
+        int current;     // current entry
+        int expectedModCount;  // for fast-fail
+        int count;
+
+        HashIterator() {
+            expectedModCount = modCount;
+            current = -1;
+            next = (size > 0 && table != null) ? findNext(0) : -1;
+            count = 0;
+        }
+
+        public final boolean hasNext() {
+            return next >= 0;
+        }
+
+        final Entry<K,V> nextNode() {
+            if (modCount != expectedModCount)
+                throw new ConcurrentModificationException("ex: " + expectedModCount + " != " + modCount);
+            if (!hasNext())
+                throw new NoSuchElementException();
+            current = next;
+            assert current >= 0;
+
+            next = (current + REHASH_HASH) & (table.length - 1);
+            next = (next == 0) ? -1 : findNext(next);
+            return new YNodeWrapper(current);
+        }
+
+        public final void remove() {
+            if (current < 0 || current > table.length)
+                throw new IllegalStateException();
+            if (modCount != expectedModCount)
+                throw new ConcurrentModificationException();
+            YNode<K, V> p = table[current];
+            removeNode(p.hash, p.key, null, false, false);
+            if (table[current].isValue()) {
+                // a node was moved into current
+                next = current;
+            }
+            current = -1;
+            expectedModCount = modCount;
+        }
+
+        /**
+         * Find the next value entry in the rehash sequence.
+         */
+        private final int findNext(int index) {
+            final YNode<K,V>[] t = table;
+            final int lowbitmask = table.length - 1;
+            index = index & lowbitmask;
+            int count = 0;
+            while (!t[index].isValue()) {
+                count ++;
+                index = (index + REHASH_HASH) & lowbitmask;
+		if (index == 0)
+		    return -1;
+            }
+            return index;
+        }
+    }
+
+    final class KeyIterator extends HashIterator
+            implements Iterator<K> {
+        public final K next() { return nextNode().getKey(); }
+    }
+
+    final class ValueIterator extends HashIterator
+            implements Iterator<V> {
+        public final V next() { return nextNode().getValue(); }
+    }
+
+    final class EntryIterator extends HashIterator
+            implements Iterator<Map.Entry<K,V>> {
+        public final Map.Entry<K,V> next() { return nextNode(); }
+    }
+
+    /**
+     * Reset to initial default state.  Called by clone and readObject.
+     */
+    void reinitialize() {
+        table = null;
+        entrySet = null;
+        keySet = null;
+        values = null;
+        modCount = 0;
+        threshold = 0;
+        size = 0;
+    }
+
+    // Called only from writeObject, to ensure compatible ordering.
+    void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
+        YNode<K,V>[] tab;
+        if (size > 0 && (tab = table) != null) {
+            for (YNode<K,V> te : tab) {
+                if (te.isValue()) {
+                    s.writeObject(te.key);
+                    s.writeObject(te.value);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Check each entry in the table.
+     *  - FindNode will find it from the key.
+     *  - the probes value is correct.
+     */
+    boolean isTableOk(final YNode<K,V>[] tab, String msg) {
+        int n;
+        if (!VERIFYTABLEOK || tab == null || (n = tab.length) == 0)
+            return true;
+        boolean ok = true;
+        int valueEntries = 0;
+        for (int index = 0; index < tab.length; index++) {
+            final YNode<K,V> te = tab[index];
+            if (te.isValue()) {
+                valueEntries++;
+                if (te.key == this || te.value == this)
+                    continue;   // skip self referential entries
+                int hash = hash(te.key);
+                int th = hash(te.key);
+                if (th != te.hash) {
+                    ok = false;
+                    debug2("ERROR: computed hash not equal stored hash: th: " + th, index, te);
+                }
+
+                int findex = getNode(hash, te.key);
+                if (findex < index) {
+                    ok = false;
+                    debug2("ERROR: getNode entry not found/found at wrong index: " + findex,
+                            index , te);
+                }
+                if (findex >= 0) {
+                    int h = hash;
+                    for (int probes = 1; probes <= tab.length; probes++, h += REHASH_HASH) {
+                        int i = (n - 1) & h;
+                        if (i == findex) {
+                            if (probes != te.probes) {
+                                ok = false;
+                                debug2("ERROR: computed probes not equal recorded " +
+                                        "probes: " + probes, findex, te);
+                            }
+                            break;
+                        }
+                        if (probes == 500) {
+                            debug2("probes > 500: " + probes, findex, te);
+                        }
+                    }
+                }
+                // Check for duplicate entry
+                for (int j = index + 1; j < tab.length; j++) {
+                    if (te.hash == tab[j].hash &&
+                            te.key.equals(tab[j].key)) {
+                        debug2("ERROR: YNode at index ", index, te);
+                        debug2("ERROR: duplicate YNode", j, tab[j]);
+                    }
+                }
+            }
+        }
+        if (valueEntries != size()) {
+            debug2("ERROR: size wrong: " + valueEntries, size(), new YNode<K,V>());
+            ok = false;
+        }
+        if (!ok) {
+            if (System.out != null) {
+                Thread.dumpStack();
+                dumpTable(table, msg);
+            }
+        }
+        return ok;
+    }
+
+    /**
+     * Print stats of the table to the a stream.
+     * @param out a stream
+     */
+    public void dumpStats(PrintStream out) {
+        out.printf("%s instance: size: %d%n", this.getClass().getName(), this.size());
+        long size = heapSize();
+        long bytesPer = (this.size != 0) ? size / this.size() : 0;
+        out.printf("    heap size: %d(bytes), avg bytes per entry: %d, table len: %d%n",
+                size, bytesPer, (table != null) ? table.length : 0);
+        long[] types = entryTypes();
+        out.printf("    values: %d, empty: %d%n",
+                types[0], types[1]);
+        printStats(out, "hash collisions", entryRehashes());
+        printStats(out, "getProbes      ", minCounts(getProbes));
+        printStats(out, "putProbes      ", minCounts(putProbes));
+        printStats(out, "notFoundProbes ", minCounts(notFoundProbes));
+
+        isTableOk(table, "dumpStats");
+    }
+
+    private void printStats(PrintStream out, String label, int[] hist) {
+        if (hist.length > 1) {
+            out.printf("    %s: max: %d, mean: %3.2f, stddev: %3.2f, %s%n",
+                    label, hist.length - 1,
+                    computeMean(hist), computeStdDev(hist),
+                    Arrays.toString(hist));
+        } else if (hist.length > 0) {
+            out.printf("    %s: max: %d, %s%n",
+                    label, hist.length - 1,
+                    Arrays.toString(hist));
+        } else {
+            out.printf("    %s: n/a%n", label);
+        }
+    }
+
+    private double computeStdDev(int[] hist) {
+        double mean = computeMean(hist);
+        double sum = 0.0f;
+        long count = 0L;
+        for (int i = 1; i < hist.length; i++) {
+            count += hist[i];
+            sum += (i - mean) * (i - mean) * hist[i];
+        }
+        return Math.sqrt(sum / (count - 1));
+    }
+
+    private double computeMean(int[] hist) {
+        long sum = 0L;
+        long count = 0;
+        for (int i = 1; i < hist.length; i++) {
+            count += hist[i];
+            sum += i * hist[i];
+        }
+        return (double)sum / (double)count;
+    }
+
+    private long[] entryTypes() {
+        long[] counts = new long[2];
+        if (table == null)
+            return counts;
+        for (YNode<K,V> te : table) {
+            if (te.isEmpty())
+                counts[1]++;
+            else
+                counts[0]++;
+        }
+        return counts;
+    }
+
+    private int[] incProbeCount(int[] counters, int probes) {
+        if (counters == null)
+            counters = new int[Math.max(probes + 1, 16)];
+        else if (probes >= counters.length)
+            counters = Arrays.copyOf(counters, Math.max(probes + 1, counters.length * 2));
+        counters[probes]++;
+        return counters;
+    }
+
+
+    // Returns a histogram array of the number of rehashs needed to find each key.
+    private int[] entryRehashes() {
+        if (table == null)
+            return new int[0];
+        int[] counts = new int[table.length + 1];
+        YNode<K,V>[] tab = table;
+        int n = tab.length;
+        for (YNode<K,V> te : tab) {
+
+            if (!te.isValue())
+                continue;
+            int h = te.hash;
+            int count;
+            final K key = te.key;
+            K k;
+            for (count = 1; count < tab.length; count++, h += REHASH_HASH) {
+                final YNode<K, V> entry;
+                if ((entry = tab[(n - 1) & h]).isValue() &&
+                        entry.hash == te.hash &&
+                        ((k = entry.key) == key || (k != null && k.equals(key)))) {
+                    break;
+                }
+            }
+
+            counts[count]++;
+        }
+
+        int i;
+        for (i = counts.length - 1; i >= 0 && counts[i] == 0; i--) {
+        }
+        counts = Arrays.copyOf(counts, i + 1);
+        return counts;
+    }
+
+    private int[] minCounts(int[] counts) {
+        int i;
+        for (i = counts.length - 1; i >= 0 && counts[i] == 0; i--) {
+        }
+        counts = Arrays.copyOf(counts, i + 1);
+        return counts;
+    }
+
+    private long heapSize() {
+        long acc = objectSizeMaybe(this);
+        return acc + objectSizeMaybe(table);
+    }
+
+    private long objectSizeMaybe(Object o) {
+        try {
+            return (mObjectSize != null)
+                    ? (long)mObjectSize.invoke(null, o)
+                    : 0L;
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            return 0L;
+        }
+    }
+
+    private static boolean hasObjectSize = false;
+    private static Method mObjectSize = getObjectSizeMethod();
+
+    private static Method getObjectSizeMethod() {
+        try {
+            Method m = Objects.class.getDeclaredMethod("getObjectSize", Object.class);
+            hasObjectSize = true;
+            return m;
+        } catch (NoSuchMethodException nsme) {
+            return null;
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/HashMapBench.java b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/HashMapBench.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/HashMapBench.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2018, Red Hat, 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
+ * 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 org.openjdk.bench.valhalla.sandbox.corelibs.corelibs.mapprotos;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import java.util.function.IntFunction;
+import java.util.stream.IntStream;
+
+import static java.util.stream.Collectors.toMap;
+
+/**
+ * Benchmark                                    (mapType)   (size)  Mode  Cnt    Score    Error  Units
+ * XHashMapBench.put                             HASH_MAP  1000000  avgt    5  214.470 +/- 44.063  ms/op
+ * XHashMapBench.put                            XHASH_MAP  1000000  avgt    5  215.772 +/- 31.595  ms/op
+ * XHashMapBench.putAllWithBigMapToEmptyMap      HASH_MAP  1000000  avgt    5  126.472 +/- 38.452  ms/op
+ * XHashMapBench.putAllWithBigMapToEmptyMap     XHASH_MAP  1000000  avgt    5  117.741 +/- 10.460  ms/op
+ * XHashMapBench.putAllWithBigMapToNonEmptyMap   HASH_MAP  1000000  avgt    5  136.112 +/- 36.712  ms/op
+ * XHashMapBench.putAllWithBigMapToNonEmptyMap  XHASH_MAP  1000000  avgt    5  144.681 +/-  8.755  ms/op
+ * Finished running test 'micro:valhalla.corelibs.XHashMapBench'
+ */
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@Fork(1)
+@State(Scope.Thread)
+public class HashMapBench {
+    private IntFunction<Map<Integer, Integer>> mapSupplier;
+    private Map<Integer, Integer> bigMapToAdd;
+
+    @Param("1000000")
+    private int size;
+
+    @Param(value = {
+            "org.openjdk.bench.valhalla.corelibs.mapprotos.HashMap",
+//            "org.openjdk.bench.valhalla.corelibs.mapprotos.XHashMap",
+            "java.util.HashMap",
+        })
+    private String mapType;
+
+    @Setup
+    public void setup() {
+        try {
+            Class<?> mapClass = Class.forName(mapType);
+            mapSupplier =  (size) -> newInstance(mapClass, size);
+        } catch (Exception ex) {
+            System.out.printf("%s: %s%n", mapType, ex.getMessage());
+            return;
+        }
+
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        this.bigMapToAdd = IntStream.range(0, size).boxed()
+            .collect(toMap(i -> 7 + i * 128, i -> rnd.nextInt()));
+    }
+
+    Map<Integer, Integer> newInstance(Class<?> mapClass, int size) {
+        try {
+            return (Map<Integer, Integer>)mapClass.getConstructor(int.class).newInstance(size);
+        } catch (Exception ex) {
+            throw new RuntimeException("failed", ex);
+        }
+    }
+
+    @Benchmark
+    public int putAllWithBigMapToNonEmptyMap() {
+        Map<Integer, Integer> map = mapSupplier.apply(16);
+        map.put(-1, -1);
+        map.putAll(bigMapToAdd);
+        return map.size();
+    }
+
+    @Benchmark
+    public int putAllWithBigMapToEmptyMap() {
+        Map<Integer, Integer> map = mapSupplier.apply(16);
+        map.putAll(bigMapToAdd);
+        return map.size();
+    }
+
+    @Benchmark
+    public int put() {
+        Map<Integer, Integer> map = mapSupplier.apply(16);
+        for (int k : bigMapToAdd.keySet()) {
+            map.put(k, bigMapToAdd.get(k));
+        }
+        return map.size();
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/HashMapToArray.java b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/HashMapToArray.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/HashMapToArray.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2019, 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 org.openjdk.bench.valhalla.sandbox.corelibs.corelibs.mapprotos;
+
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.IntFunction;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Simple benchmark of toArray.
+ */
+
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@Fork(1)
+@State(Scope.Thread)
+public class HashMapToArray {
+
+    private IntFunction<Map<Integer, Integer>> mapSupplier;
+    Map<Integer, Integer> map;
+
+
+    @Param(value = {
+            "org.openjdk.bench.valhalla.corelibs.mapprotos.HashMap",
+//            "org.openjdk.bench.valhalla.corelibs.mapprotos.XHashMap",
+            "java.util.HashMap",
+        })
+    private String mapType;
+
+    @Param({"1", "10", "1000", "100000"})
+    public int size;
+
+    @Setup
+    public void setup() {
+        try {
+            Class<?> mapClass = Class.forName(mapType);
+            mapSupplier =  (size) -> newInstance(mapClass, size);
+        } catch (Exception ex) {
+            System.out.printf("%s: %s%n", mapType, ex.getMessage());
+            return;
+        }
+
+        map = mapSupplier.apply(0);
+        for (int i = 0; i < size; i++) {
+            map.put(i, i * i);
+        }
+    }
+
+    Map<Integer, Integer> newInstance(Class<?> mapClass, int size) {
+        try {
+            return (Map<Integer, Integer>)mapClass.getConstructor(int.class).newInstance(size);
+        } catch (Exception ex) {
+            throw new RuntimeException("failed", ex);
+        }
+    }
+
+    @Benchmark
+    public Object[] testKeySetToArray() {
+        return map.keySet().toArray();
+    }
+
+    @Benchmark
+    public Object[] testKeySetToArrayTyped() {
+        return map.keySet().toArray(new Integer[0]);
+    }
+
+    @Benchmark
+    public Object[] testValuesToArray() {
+        return map.values().toArray();
+    }
+
+    @Benchmark
+    public Object[] testValuesToArrayTyped() {
+        return map.values().toArray(new Integer[0]);
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/MapBase.java b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/MapBase.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/MapBase.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2016, 2018, 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 org.openjdk.bench.valhalla.sandbox.corelibs.corelibs.mapprotos;
+
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
+
+@Fork(1)
+@OutputTimeUnit(TimeUnit.MICROSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class MapBase {
+
+    @Param({
+            "11",
+            "767",
+            "1572863",
+    })
+    public int size;
+
+    @Param({
+//            "0",
+            "42",
+    })
+    public int seed;
+
+    @Param(value = {
+            "java.util.HashMap",
+            "org.openjdk.bench.valhalla.corelibs.mapprotos.HashMap",
+//            "org.openjdk.bench.valhalla.corelibs.mapprotos.XHashMap",
+//            "java.util.HashMap0",
+    })
+    public String mapType;
+
+    public Random rnd;
+    public Integer[] keys;
+    public Integer[] nonKeys;
+
+    public void init(int size) {
+        Integer[] all;
+        if (seed != 0) {
+            rnd = new Random(seed);
+            all = rnd.ints().distinct().limit(size * 2).boxed().toArray(Integer[]::new);
+            Collections.shuffle(Arrays.asList(all), rnd);
+        } else {
+            rnd = new Random();
+            all = IntStream.range(0, size * 2).boxed().toArray(Integer[]::new);
+            Collections.shuffle(Arrays.asList(all));
+        }
+        keys = Arrays.copyOfRange(all, 0, size);
+        nonKeys = Arrays.copyOfRange(all, size, size * 2);
+    }
+
+    void TearDown(Map<Integer, Integer> map) {
+        try {
+            Method m = map.getClass().getMethod("dumpStats", java.io.PrintStream.class);
+            m.invoke(map, System.out);
+        } catch (Throwable nsme) {
+            System.out.println("Stats not available:");
+        }
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/PutX.java b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/PutX.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/PutX.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2016, 2018, 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 org.openjdk.bench.valhalla.sandbox.corelibs.corelibs.mapprotos;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.IntFunction;
+
+
+public class PutX extends MapBase {
+    IntFunction<Map<Integer, Integer>> mapSupplier;
+    Map<Integer,Integer> lastMap;
+
+    @Setup
+    public void setup() {
+        super.init(size);
+        try {
+            Class<?> mapClass = Class.forName(mapType);
+            mapSupplier =  (size) -> newInstance(mapClass, size);
+        } catch (Exception ex) {
+            System.out.printf("%s: %s%n", mapType, ex.getMessage());
+            return;
+        }
+    }
+
+    Map<Integer, Integer> newInstance(Class<?> mapClass, int size) {
+        try {
+            return (Map<Integer, Integer>)mapClass.getConstructor(int.class).newInstance(size);
+        } catch (Exception ex) {
+            throw new RuntimeException("failed", ex);
+        }
+    }
+
+    @TearDown
+    public void teardown() {
+        super.TearDown(lastMap);
+    }
+
+    @Benchmark
+    public Map<Integer, Integer> put() {
+        Integer[] keys = this.keys;
+        Map<Integer, Integer> map = mapSupplier.apply(0);
+        for (Integer k : keys) {
+            map.put(k, k);
+        }
+        lastMap = map;
+        return map;
+    }
+
+    @Benchmark
+    public Map<Integer, Integer> putSized() {
+        Integer[] keys = this.keys;
+        Map<Integer, Integer> map = mapSupplier.apply(size * 2);
+        for (Integer k : keys) {
+            map.put(k, k);
+        }
+        lastMap = map;
+        return map;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/README.md b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/README.md
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/README.md
@@ -0,0 +1,31 @@
+# Prototype implementations of HashMaps using inline classes for the table entries.
+
+   **NOTE: The implementations have NOT been optimized or tuned.**
+
+## HashMap uses Open Addressing to store all entries in a table of inline classes
+The hash of the key is used as the first index into the table. 
+If there is a collision, double hashing (with a static offset) is used to probe subsequent locations for available storage.
+The Robin Hood hashing variation on insertion is used to reduce worst case lookup times.
+On key removal, the subsequent double-hashed entries are compressed into the entry being removed.
+
+### HashMap Storage requirements
+Typical storage usage for a table near its load factor is 22 bytes per entry.
+
+Inserting entries into the HashMap may resize the table but otherwise does
+not use any additional memory on each get or put.
+
+## XHashMap stores the initial entry in a table of inline classes
+The hash of the key is used as the first index into the table. 
+If there is a collision, subsequent entries add the familiar link list of Nodes.
+On key removal, direct entries in the table are replaced by the first linked node;
+for Nodes in the link list, the Node is unlinked.
+
+### XHashMap Storage requirements:
+Typical storage usage for a table near its load factor is 32 bytes per entry.
+
+## java.util.HashMap (the original)
+HashMap uses a table of references to the initial Node entries, collisions are handled by 
+linked Nodes.
+
+### HashMap Storage requirements:
+Typical storage usage for a table near its load factor is 37 bytes per entry.
diff a/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/ReplX.java b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/ReplX.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/ReplX.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2019, 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 org.openjdk.bench.valhalla.sandbox.corelibs.corelibs.mapprotos;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Setup;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.IntFunction;
+
+public class ReplX extends MapBase {
+    IntFunction<Map<Integer, Integer>> mapSupplier;
+    Integer[] mixed;
+    Map<Integer, Integer> map;
+
+    @Setup
+    public void setup() {
+        super.init(size);
+        try {
+            Class<?> mapClass = Class.forName(mapType);
+            mapSupplier =  (size) -> newInstance(mapClass, size);
+
+            map = mapSupplier.apply(0);
+            for (Integer k : keys) {
+                map.put(k, k);
+            }
+            mixed = new Integer[size];
+            System.arraycopy(keys, 0, mixed, 0, size / 2);
+            System.arraycopy(nonKeys, 0, mixed, size / 2, size / 2);
+            Collections.shuffle(Arrays.asList(mixed), rnd);
+        } catch (Exception ex) {
+            System.out.printf("%s: %s%n", mapType, ex.getMessage());
+            return;
+        }
+    }
+
+    Map<Integer, Integer> newInstance(Class<?> mapClass, int size) {
+        try {
+            return (Map<Integer, Integer>)mapClass.getConstructor(int.class).newInstance(size);
+        } catch (Exception ex) {
+            throw new RuntimeException("failed", ex);
+        }
+    }
+
+    @Benchmark
+    public Map<Integer, Integer> replace() {
+        Integer[] keys = this.keys;
+        for (Integer k : mixed) {
+            if (map.size() < size) {
+                map.put(k, k);
+            } else {
+                map.remove(k);
+            }
+        }
+        return map;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/WalkX.java b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/WalkX.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/WalkX.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2016, 2018, 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 org.openjdk.bench.valhalla.sandbox.corelibs.corelibs.mapprotos;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.IntFunction;
+
+public class WalkX extends MapBase {
+
+    IntFunction<Map<Integer, Integer>> mapSupplier;
+    Map<Integer, Integer> map;
+
+    @Setup
+    public void setup() {
+        super.init(size);
+        try {
+            Class<?> mapClass = Class.forName(mapType);
+            mapSupplier =  (size) -> newInstance(mapClass, size);
+        } catch (Exception ex) {
+            System.out.printf("%s: %s%n", mapType, ex.getMessage());
+            return;
+        }
+
+        map = mapSupplier.apply(0);
+        for (Integer k : keys) {
+            map.put(k, k);
+        }
+    }
+
+    Map<Integer, Integer> newInstance(Class<?> mapClass, int size) {
+        try {
+            return (Map<Integer, Integer>)mapClass.getConstructor(int.class).newInstance(size);
+        } catch (Exception ex) {
+            throw new RuntimeException("failed", ex);
+        }
+    }
+
+    @Benchmark
+    public int sumIterator() {
+        int s = 0;
+        for (Iterator<Integer> iterator = map.keySet().iterator(); iterator.hasNext(); ) {
+            s += iterator.next();
+        }
+        return s;
+    }
+
+    @Benchmark
+    public int sumIteratorHidden() {
+        int s = 0;
+        for (Iterator<Integer> iterator = hide(map.keySet().iterator()); iterator.hasNext(); ) {
+            s += iterator.next();
+        }
+        return s;
+    }
+
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    private static Iterator<Integer> hide(Iterator<Integer> it) {
+        return it;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/XAbstractMap.java b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/XAbstractMap.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/XAbstractMap.java
@@ -0,0 +1,863 @@
+/*
+ * Copyright (c) 1997, 2018, 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.
+ */
+
+package org.openjdk.bench.valhalla.sandbox.corelibs.corelibs.mapprotos;
+
+import java.util.AbstractCollection;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This class provides a skeletal implementation of the {@code Map}
+ * interface, to minimize the effort required to implement this interface.
+ *
+ * <p>To implement an unmodifiable map, the programmer needs only to extend this
+ * class and provide an implementation for the {@code entrySet} method, which
+ * returns a set-view of the map's mappings.  Typically, the returned set
+ * will, in turn, be implemented atop {@code AbstractSet}.  This set should
+ * not support the {@code add} or {@code remove} methods, and its iterator
+ * should not support the {@code remove} method.
+ *
+ * <p>To implement a modifiable map, the programmer must additionally override
+ * this class's {@code put} method (which otherwise throws an
+ * {@code UnsupportedOperationException}), and the iterator returned by
+ * {@code entrySet().iterator()} must additionally implement its
+ * {@code remove} method.
+ *
+ * <p>The programmer should generally provide a void (no argument) and map
+ * constructor, as per the recommendation in the {@code Map} interface
+ * specification.
+ *
+ * <p>The documentation for each non-abstract method in this class describes its
+ * implementation in detail.  Each of these methods may be overridden if the
+ * map being implemented admits a more efficient implementation.
+ *
+ * <p>This class is a member of the
+ * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
+ * Java Collections Framework</a>.
+ *
+ * @param <K> the type of keys maintained by this map
+ * @param <V> the type of mapped values
+ *
+ * @author  Josh Bloch
+ * @author  Neal Gafter
+ * @see Map
+ * @see Collection
+ * @since 1.2
+ */
+
+public abstract class XAbstractMap<K,V> implements Map<K,V> {
+    /**
+     * Sole constructor.  (For invocation by subclass constructors, typically
+     * implicit.)
+     */
+    protected XAbstractMap() {
+    }
+
+    // Query Operations
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * This implementation returns {@code entrySet().size()}.
+     */
+    public int size() {
+        return entrySet().size();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * This implementation returns {@code size() == 0}.
+     */
+    public boolean isEmpty() {
+        return size() == 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * This implementation iterates over {@code entrySet()} searching
+     * for an entry with the specified value.  If such an entry is found,
+     * {@code true} is returned.  If the iteration terminates without
+     * finding such an entry, {@code false} is returned.  Note that this
+     * implementation requires linear time in the size of the map.
+     *
+     * @throws ClassCastException   {@inheritDoc}
+     * @throws NullPointerException {@inheritDoc}
+     */
+    public boolean containsValue(Object value) {
+        Iterator<Entry<K,V>> i = entrySet().iterator();
+        if (value==null) {
+            while (i.hasNext()) {
+                Entry<K,V> e = i.next();
+                if (e.getValue()==null)
+                    return true;
+            }
+        } else {
+            while (i.hasNext()) {
+                Entry<K,V> e = i.next();
+                if (value.equals(e.getValue()))
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * This implementation iterates over {@code entrySet()} searching
+     * for an entry with the specified key.  If such an entry is found,
+     * {@code true} is returned.  If the iteration terminates without
+     * finding such an entry, {@code false} is returned.  Note that this
+     * implementation requires linear time in the size of the map; many
+     * implementations will override this method.
+     *
+     * @throws ClassCastException   {@inheritDoc}
+     * @throws NullPointerException {@inheritDoc}
+     */
+    public boolean containsKey(Object key) {
+        Iterator<Map.Entry<K,V>> i = entrySet().iterator();
+        if (key==null) {
+            while (i.hasNext()) {
+                Entry<K,V> e = i.next();
+                if (e.getKey()==null)
+                    return true;
+            }
+        } else {
+            while (i.hasNext()) {
+                Entry<K,V> e = i.next();
+                if (key.equals(e.getKey()))
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * This implementation iterates over {@code entrySet()} searching
+     * for an entry with the specified key.  If such an entry is found,
+     * the entry's value is returned.  If the iteration terminates without
+     * finding such an entry, {@code null} is returned.  Note that this
+     * implementation requires linear time in the size of the map; many
+     * implementations will override this method.
+     *
+     * @throws ClassCastException            {@inheritDoc}
+     * @throws NullPointerException          {@inheritDoc}
+     */
+    public V get(Object key) {
+        Iterator<Entry<K,V>> i = entrySet().iterator();
+        if (key==null) {
+            while (i.hasNext()) {
+                Entry<K,V> e = i.next();
+                if (e.getKey()==null)
+                    return e.getValue();
+            }
+        } else {
+            while (i.hasNext()) {
+                Entry<K,V> e = i.next();
+                if (key.equals(e.getKey()))
+                    return e.getValue();
+            }
+        }
+        return null;
+    }
+
+
+    // Modification Operations
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * This implementation always throws an
+     * {@code UnsupportedOperationException}.
+     *
+     * @throws UnsupportedOperationException {@inheritDoc}
+     * @throws ClassCastException            {@inheritDoc}
+     * @throws NullPointerException          {@inheritDoc}
+     * @throws IllegalArgumentException      {@inheritDoc}
+     */
+    public V put(K key, V value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * This implementation iterates over {@code entrySet()} searching for an
+     * entry with the specified key.  If such an entry is found, its value is
+     * obtained with its {@code getValue} operation, the entry is removed
+     * from the collection (and the backing map) with the iterator's
+     * {@code remove} operation, and the saved value is returned.  If the
+     * iteration terminates without finding such an entry, {@code null} is
+     * returned.  Note that this implementation requires linear time in the
+     * size of the map; many implementations will override this method.
+     *
+     * <p>Note that this implementation throws an
+     * {@code UnsupportedOperationException} if the {@code entrySet}
+     * iterator does not support the {@code remove} method and this map
+     * contains a mapping for the specified key.
+     *
+     * @throws UnsupportedOperationException {@inheritDoc}
+     * @throws ClassCastException            {@inheritDoc}
+     * @throws NullPointerException          {@inheritDoc}
+     */
+    public V remove(Object key) {
+        Iterator<Entry<K,V>> i = entrySet().iterator();
+        Entry<K,V> correctEntry = null;
+        if (key==null) {
+            while (correctEntry==null && i.hasNext()) {
+                Entry<K,V> e = i.next();
+                if (e.getKey()==null)
+                    correctEntry = e;
+            }
+        } else {
+            while (correctEntry==null && i.hasNext()) {
+                Entry<K,V> e = i.next();
+                if (key.equals(e.getKey()))
+                    correctEntry = e;
+            }
+        }
+
+        V oldValue = null;
+        if (correctEntry !=null) {
+            oldValue = correctEntry.getValue();
+            i.remove();
+        }
+        return oldValue;
+    }
+
+
+    // Bulk Operations
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * This implementation iterates over the specified map's
+     * {@code entrySet()} collection, and calls this map's {@code put}
+     * operation once for each entry returned by the iteration.
+     *
+     * <p>Note that this implementation throws an
+     * {@code UnsupportedOperationException} if this map does not support
+     * the {@code put} operation and the specified map is nonempty.
+     *
+     * @throws UnsupportedOperationException {@inheritDoc}
+     * @throws ClassCastException            {@inheritDoc}
+     * @throws NullPointerException          {@inheritDoc}
+     * @throws IllegalArgumentException      {@inheritDoc}
+     */
+    public void putAll(Map<? extends K, ? extends V> m) {
+        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
+            put(e.getKey(), e.getValue());
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * This implementation calls {@code entrySet().clear()}.
+     *
+     * <p>Note that this implementation throws an
+     * {@code UnsupportedOperationException} if the {@code entrySet}
+     * does not support the {@code clear} operation.
+     *
+     * @throws UnsupportedOperationException {@inheritDoc}
+     */
+    public void clear() {
+        entrySet().clear();
+    }
+
+
+    // Views
+
+    /**
+     * Each of these fields are initialized to contain an instance of the
+     * appropriate view the first time this view is requested.  The views are
+     * stateless, so there's no reason to create more than one of each.
+     *
+     * <p>Since there is no synchronization performed while accessing these fields,
+     * it is expected that java.util.Map view classes using these fields have
+     * no non-final fields (or any fields at all except for outer-this). Adhering
+     * to this rule would make the races on these fields benign.
+     *
+     * <p>It is also imperative that implementations read the field only once,
+     * as in:
+     *
+     * <pre> {@code
+     * public Set<K> keySet() {
+     *   Set<K> ks = keySet;  // single racy read
+     *   if (ks == null) {
+     *     ks = new KeySet();
+     *     keySet = ks;
+     *   }
+     *   return ks;
+     * }
+     *}</pre>
+     */
+    transient Set<K> keySet;
+    transient Collection<V> values;
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * This implementation returns a set that subclasses {@link AbstractSet}.
+     * The subclass's iterator method returns a "wrapper object" over this
+     * map's {@code entrySet()} iterator.  The {@code size} method
+     * delegates to this map's {@code size} method and the
+     * {@code contains} method delegates to this map's
+     * {@code containsKey} method.
+     *
+     * <p>The set is created the first time this method is called,
+     * and returned in response to all subsequent calls.  No synchronization
+     * is performed, so there is a slight chance that multiple calls to this
+     * method will not all return the same set.
+     */
+    public Set<K> keySet() {
+        Set<K> ks = keySet;
+        if (ks == null) {
+            ks = new AbstractSet<K>() {
+                public Iterator<K> iterator() {
+                    return new Iterator<K>() {
+                        private Iterator<Entry<K,V>> i = entrySet().iterator();
+
+                        public boolean hasNext() {
+                            return i.hasNext();
+                        }
+
+                        public K next() {
+                            return i.next().getKey();
+                        }
+
+                        public void remove() {
+                            i.remove();
+                        }
+                    };
+                }
+
+                public int size() {
+                    return XAbstractMap.this.size();
+                }
+
+                public boolean isEmpty() {
+                    return XAbstractMap.this.isEmpty();
+                }
+
+                public void clear() {
+                    XAbstractMap.this.clear();
+                }
+
+                public boolean contains(Object k) {
+                    return XAbstractMap.this.containsKey(k);
+                }
+            };
+            keySet = ks;
+        }
+        return ks;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @implSpec
+     * This implementation returns a collection that subclasses {@link
+     * AbstractCollection}.  The subclass's iterator method returns a
+     * "wrapper object" over this map's {@code entrySet()} iterator.
+     * The {@code size} method delegates to this map's {@code size}
+     * method and the {@code contains} method delegates to this map's
+     * {@code containsValue} method.
+     *
+     * <p>The collection is created the first time this method is called, and
+     * returned in response to all subsequent calls.  No synchronization is
+     * performed, so there is a slight chance that multiple calls to this
+     * method will not all return the same collection.
+     */
+    public Collection<V> values() {
+        Collection<V> vals = values;
+        if (vals == null) {
+            vals = new AbstractCollection<V>() {
+                public Iterator<V> iterator() {
+                    return new Iterator<V>() {
+                        private Iterator<Entry<K,V>> i = entrySet().iterator();
+
+                        public boolean hasNext() {
+                            return i.hasNext();
+                        }
+
+                        public V next() {
+                            return i.next().getValue();
+                        }
+
+                        public void remove() {
+                            i.remove();
+                        }
+                    };
+                }
+
+                public int size() {
+                    return XAbstractMap.this.size();
+                }
+
+                public boolean isEmpty() {
+                    return XAbstractMap.this.isEmpty();
+                }
+
+                public void clear() {
+                    XAbstractMap.this.clear();
+                }
+
+                public boolean contains(Object v) {
+                    return XAbstractMap.this.containsValue(v);
+                }
+            };
+            values = vals;
+        }
+        return vals;
+    }
+
+    public abstract Set<Entry<K,V>> entrySet();
+
+
+    // Comparison and hashing
+
+    /**
+     * Compares the specified object with this map for equality.  Returns
+     * {@code true} if the given object is also a map and the two maps
+     * represent the same mappings.  More formally, two maps {@code m1} and
+     * {@code m2} represent the same mappings if
+     * {@code m1.entrySet().equals(m2.entrySet())}.  This ensures that the
+     * {@code equals} method works properly across different implementations
+     * of the {@code Map} interface.
+     *
+     * @implSpec
+     * This implementation first checks if the specified object is this map;
+     * if so it returns {@code true}.  Then, it checks if the specified
+     * object is a map whose size is identical to the size of this map; if
+     * not, it returns {@code false}.  If so, it iterates over this map's
+     * {@code entrySet} collection, and checks that the specified map
+     * contains each mapping that this map contains.  If the specified map
+     * fails to contain such a mapping, {@code false} is returned.  If the
+     * iteration completes, {@code true} is returned.
+     *
+     * @param o object to be compared for equality with this map
+     * @return {@code true} if the specified object is equal to this map
+     */
+    public boolean equals(Object o) {
+        if (o == this)
+            return true;
+
+        if (!(o instanceof Map))
+            return false;
+        Map<?,?> m = (Map<?,?>) o;
+        if (m.size() != size())
+            return false;
+
+        try {
+            for (Entry<K, V> e : entrySet()) {
+                K key = e.getKey();
+                V value = e.getValue();
+                if (value == null) {
+                    if (!(m.get(key) == null && m.containsKey(key)))
+                        return false;
+                } else {
+                    if (!value.equals(m.get(key)))
+                        return false;
+                }
+            }
+        } catch (ClassCastException unused) {
+            return false;
+        } catch (NullPointerException unused) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns the hash code value for this map.  The hash code of a map is
+     * defined to be the sum of the hash codes of each entry in the map's
+     * {@code entrySet()} view.  This ensures that {@code m1.equals(m2)}
+     * implies that {@code m1.hashCode()==m2.hashCode()} for any two maps
+     * {@code m1} and {@code m2}, as required by the general contract of
+     * {@link Object#hashCode}.
+     *
+     * @implSpec
+     * This implementation iterates over {@code entrySet()}, calling
+     * {@link Map.Entry#hashCode hashCode()} on each element (entry) in the
+     * set, and adding up the results.
+     *
+     * @return the hash code value for this map
+     * @see Map.Entry#hashCode()
+     * @see Object#equals(Object)
+     * @see Set#equals(Object)
+     */
+    public int hashCode() {
+        int h = 0;
+        for (Entry<K, V> entry : entrySet())
+            h += entry.hashCode();
+        return h;
+    }
+
+    /**
+     * Returns a string representation of this map.  The string representation
+     * consists of a list of key-value mappings in the order returned by the
+     * map's {@code entrySet} view's iterator, enclosed in braces
+     * ({@code "{}"}).  Adjacent mappings are separated by the characters
+     * {@code ", "} (comma and space).  Each key-value mapping is rendered as
+     * the key followed by an equals sign ({@code "="}) followed by the
+     * associated value.  Keys and values are converted to strings as by
+     * {@link String#valueOf(Object)}.
+     *
+     * @return a string representation of this map
+     */
+    public String toString() {
+        Iterator<Entry<K,V>> i = entrySet().iterator();
+        if (! i.hasNext())
+            return "{}";
+
+        StringBuilder sb = new StringBuilder();
+        sb.append('{');
+        for (;;) {
+            Entry<K,V> e = i.next();
+            K key = e.getKey();
+            V value = e.getValue();
+            sb.append(key   == this ? "(this Map)" : key);
+            sb.append('=');
+            sb.append(value == this ? "(this Map)" : value);
+            if (! i.hasNext())
+                return sb.append('}').toString();
+            sb.append(',').append(' ');
+        }
+    }
+
+    /**
+     * Returns a shallow copy of this {@code AbstractMap} instance: the keys
+     * and values themselves are not cloned.
+     *
+     * @return a shallow copy of this map
+     */
+    protected Object clone() throws CloneNotSupportedException {
+        XAbstractMap<?,?> result = (XAbstractMap<?,?>)super.clone();
+        result.keySet = null;
+        result.values = null;
+        return result;
+    }
+
+    /**
+     * Utility method for SimpleEntry and SimpleImmutableEntry.
+     * Test for equality, checking for nulls.
+     *
+     * NB: Do not replace with Object.equals until JDK-8015417 is resolved.
+     */
+    private static boolean eq(Object o1, Object o2) {
+        return o1 == null ? o2 == null : o1.equals(o2);
+    }
+
+    // Implementation Note: SimpleEntry and SimpleImmutableEntry
+    // are distinct unrelated classes, even though they share
+    // some code. Since you can't add or subtract final-ness
+    // of a field in a subclass, they can't share representations,
+    // and the amount of duplicated code is too small to warrant
+    // exposing a common abstract class.
+
+
+    /**
+     * An Entry maintaining a key and a value.  The value may be
+     * changed using the {@code setValue} method.  This class
+     * facilitates the process of building custom map
+     * implementations. For example, it may be convenient to return
+     * arrays of {@code SimpleEntry} instances in method
+     * {@code Map.entrySet().toArray}.
+     *
+     * @since 1.6
+     */
+    public static class SimpleEntry<K,V>
+        implements Entry<K,V>, java.io.Serializable
+    {
+        private static final long serialVersionUID = -8499721149061103585L;
+
+        private final K key;
+        private V value;
+
+        /**
+         * Creates an entry representing a mapping from the specified
+         * key to the specified value.
+         *
+         * @param key the key represented by this entry
+         * @param value the value represented by this entry
+         */
+        public SimpleEntry(K key, V value) {
+            this.key   = key;
+            this.value = value;
+        }
+
+        /**
+         * Creates an entry representing the same mapping as the
+         * specified entry.
+         *
+         * @param entry the entry to copy
+         */
+        public SimpleEntry(Entry<? extends K, ? extends V> entry) {
+            this.key   = entry.getKey();
+            this.value = entry.getValue();
+        }
+
+        /**
+         * Returns the key corresponding to this entry.
+         *
+         * @return the key corresponding to this entry
+         */
+        public K getKey() {
+            return key;
+        }
+
+        /**
+         * Returns the value corresponding to this entry.
+         *
+         * @return the value corresponding to this entry
+         */
+        public V getValue() {
+            return value;
+        }
+
+        /**
+         * Replaces the value corresponding to this entry with the specified
+         * value.
+         *
+         * @param value new value to be stored in this entry
+         * @return the old value corresponding to the entry
+         */
+        public V setValue(V value) {
+            V oldValue = this.value;
+            this.value = value;
+            return oldValue;
+        }
+
+        /**
+         * Compares the specified object with this entry for equality.
+         * Returns {@code true} if the given object is also a map entry and
+         * the two entries represent the same mapping.  More formally, two
+         * entries {@code e1} and {@code e2} represent the same mapping
+         * if<pre>
+         *   (e1.getKey()==null ?
+         *    e2.getKey()==null :
+         *    e1.getKey().equals(e2.getKey()))
+         *   &amp;&amp;
+         *   (e1.getValue()==null ?
+         *    e2.getValue()==null :
+         *    e1.getValue().equals(e2.getValue()))</pre>
+         * This ensures that the {@code equals} method works properly across
+         * different implementations of the {@code Map.Entry} interface.
+         *
+         * @param o object to be compared for equality with this map entry
+         * @return {@code true} if the specified object is equal to this map
+         *         entry
+         * @see    #hashCode
+         */
+        public boolean equals(Object o) {
+            if (!(o instanceof Map.Entry))
+                return false;
+            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
+            return eq(key, e.getKey()) && eq(value, e.getValue());
+        }
+
+        /**
+         * Returns the hash code value for this map entry.  The hash code
+         * of a map entry {@code e} is defined to be: <pre>
+         *   (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
+         *   (e.getValue()==null ? 0 : e.getValue().hashCode())</pre>
+         * This ensures that {@code e1.equals(e2)} implies that
+         * {@code e1.hashCode()==e2.hashCode()} for any two Entries
+         * {@code e1} and {@code e2}, as required by the general
+         * contract of {@link Object#hashCode}.
+         *
+         * @return the hash code value for this map entry
+         * @see    #equals
+         */
+        public int hashCode() {
+            return (key   == null ? 0 :   key.hashCode()) ^
+                   (value == null ? 0 : value.hashCode());
+        }
+
+        /**
+         * Returns a String representation of this map entry.  This
+         * implementation returns the string representation of this
+         * entry's key followed by the equals character ("{@code =}")
+         * followed by the string representation of this entry's value.
+         *
+         * @return a String representation of this map entry
+         */
+        public String toString() {
+            return key + "=" + value;
+        }
+
+    }
+
+    /**
+     * An Entry maintaining an immutable key and value.  This class
+     * does not support method {@code setValue}.  This class may be
+     * convenient in methods that return thread-safe snapshots of
+     * key-value mappings.
+     *
+     * @since 1.6
+     */
+    public static class SimpleImmutableEntry<K,V>
+        implements Entry<K,V>, java.io.Serializable
+    {
+        private static final long serialVersionUID = 7138329143949025153L;
+
+        private final K key;
+        private final V value;
+
+        /**
+         * Creates an entry representing a mapping from the specified
+         * key to the specified value.
+         *
+         * @param key the key represented by this entry
+         * @param value the value represented by this entry
+         */
+        public SimpleImmutableEntry(K key, V value) {
+            this.key   = key;
+            this.value = value;
+        }
+
+        /**
+         * Creates an entry representing the same mapping as the
+         * specified entry.
+         *
+         * @param entry the entry to copy
+         */
+        public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
+            this.key   = entry.getKey();
+            this.value = entry.getValue();
+        }
+
+        /**
+         * Returns the key corresponding to this entry.
+         *
+         * @return the key corresponding to this entry
+         */
+        public K getKey() {
+            return key;
+        }
+
+        /**
+         * Returns the value corresponding to this entry.
+         *
+         * @return the value corresponding to this entry
+         */
+        public V getValue() {
+            return value;
+        }
+
+        /**
+         * Replaces the value corresponding to this entry with the specified
+         * value (optional operation).  This implementation simply throws
+         * {@code UnsupportedOperationException}, as this class implements
+         * an <i>immutable</i> map entry.
+         *
+         * @param value new value to be stored in this entry
+         * @return (Does not return)
+         * @throws UnsupportedOperationException always
+         */
+        public V setValue(V value) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * Compares the specified object with this entry for equality.
+         * Returns {@code true} if the given object is also a map entry and
+         * the two entries represent the same mapping.  More formally, two
+         * entries {@code e1} and {@code e2} represent the same mapping
+         * if<pre>
+         *   (e1.getKey()==null ?
+         *    e2.getKey()==null :
+         *    e1.getKey().equals(e2.getKey()))
+         *   &amp;&amp;
+         *   (e1.getValue()==null ?
+         *    e2.getValue()==null :
+         *    e1.getValue().equals(e2.getValue()))</pre>
+         * This ensures that the {@code equals} method works properly across
+         * different implementations of the {@code Map.Entry} interface.
+         *
+         * @param o object to be compared for equality with this map entry
+         * @return {@code true} if the specified object is equal to this map
+         *         entry
+         * @see    #hashCode
+         */
+        public boolean equals(Object o) {
+            if (!(o instanceof Map.Entry))
+                return false;
+            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
+            return eq(key, e.getKey()) && eq(value, e.getValue());
+        }
+
+        /**
+         * Returns the hash code value for this map entry.  The hash code
+         * of a map entry {@code e} is defined to be: <pre>
+         *   (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
+         *   (e.getValue()==null ? 0 : e.getValue().hashCode())</pre>
+         * This ensures that {@code e1.equals(e2)} implies that
+         * {@code e1.hashCode()==e2.hashCode()} for any two Entries
+         * {@code e1} and {@code e2}, as required by the general
+         * contract of {@link Object#hashCode}.
+         *
+         * @return the hash code value for this map entry
+         * @see    #equals
+         */
+        public int hashCode() {
+            return (key   == null ? 0 :   key.hashCode()) ^
+                   (value == null ? 0 : value.hashCode());
+        }
+
+        /**
+         * Returns a String representation of this map entry.  This
+         * implementation returns the string representation of this
+         * entry's key followed by the equals character ("{@code =}")
+         * followed by the string representation of this entry's value.
+         *
+         * @return a String representation of this map entry
+         */
+        public String toString() {
+            return key + "=" + value;
+        }
+
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/XHashMap.java b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/XHashMap.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/sandbox/corelibs/mapprotos/XHashMap.java
@@ -0,0 +1,2375 @@
+/*
+ * 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.  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.
+ */
+
+package org.openjdk.bench.valhalla.sandbox.corelibs.corelibs.mapprotos;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.PrintStream;
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.AbstractCollection;
+//import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.TreeMap;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * Hash map implementation that uses inline class entries in the initial table
+ * and maintains a link list of separate Node entries for key/value pairs
+ * that have the same hash value.  The handling of the link list is the same
+ * as the original java.util.HashMap.
+ * The primary entry array is larger than in HashMap due to the inline storage
+ * of the entries but since it replaces the separate Node instance for the first
+ * Node, the overall memory usage is less for a reasonably full table.
+ * The TreeNode organization is not yet implemented.
+ * <p>
+ * Hash table based implementation of the {@code Map} interface.  This
+ * implementation provides all of the optional map operations, and permits
+ * {@code null} values and the {@code null} key.  (The {@code HashMap}
+ * class is roughly equivalent to {@code Hashtable}, except that it is
+ * unsynchronized and permits nulls.)  This class makes no guarantees as to
+ * the order of the map; in particular, it does not guarantee that the order
+ * will remain constant over time.
+ *
+ * <p>This implementation provides constant-time performance for the basic
+ * operations ({@code get} and {@code put}), assuming the hash function
+ * disperses the elements properly among the buckets.  Iteration over
+ * collection views requires time proportional to the "capacity" of the
+ * {@code HashMap} instance (the number of buckets) plus its size (the number
+ * of key-value mappings).  Thus, it's very important not to set the initial
+ * capacity too high (or the load factor too low) if iteration performance is
+ * important.
+ *
+ * <p>An instance of {@code HashMap} has two parameters that affect its
+ * performance: <i>initial capacity</i> and <i>load factor</i>.  The
+ * <i>capacity</i> is the number of buckets in the hash table, and the initial
+ * capacity is simply the capacity at the time the hash table is created.  The
+ * <i>load factor</i> is a measure of how full the hash table is allowed to
+ * get before its capacity is automatically increased.  When the number of
+ * entries in the hash table exceeds the product of the load factor and the
+ * current capacity, the hash table is <i>rehashed</i> (that is, internal data
+ * structures are rebuilt) so that the hash table has approximately twice the
+ * number of buckets.
+ *
+ * <p>As a general rule, the default load factor (.75) offers a good
+ * tradeoff between time and space costs.  Higher values decrease the
+ * space overhead but increase the lookup cost (reflected in most of
+ * the operations of the {@code HashMap} class, including
+ * {@code get} and {@code put}).  The expected number of entries in
+ * the map and its load factor should be taken into account when
+ * setting its initial capacity, so as to minimize the number of
+ * rehash operations.  If the initial capacity is greater than the
+ * maximum number of entries divided by the load factor, no rehash
+ * operations will ever occur.
+ *
+ * <p>If many mappings are to be stored in a {@code HashMap}
+ * instance, creating it with a sufficiently large capacity will allow
+ * the mappings to be stored more efficiently than letting it perform
+ * automatic rehashing as needed to grow the table.  Note that using
+ * many keys with the same {@code hashCode()} is a sure way to slow
+ * down performance of any hash table. To ameliorate impact, when keys
+ * are {@link Comparable}, this class may use comparison order among
+ * keys to help break ties.
+ *
+ * <p><strong>Note that this implementation is not synchronized.</strong>
+ * If multiple threads access a hash map concurrently, and at least one of
+ * the threads modifies the map structurally, it <i>must</i> be
+ * synchronized externally.  (A structural modification is any operation
+ * that adds or deletes one or more mappings; merely changing the value
+ * associated with a key that an instance already contains is not a
+ * structural modification.)  This is typically accomplished by
+ * synchronizing on some object that naturally encapsulates the map.
+ *
+ * If no such object exists, the map should be "wrapped" using the
+ * {@link Collections#synchronizedMap Collections.synchronizedMap}
+ * method.  This is best done at creation time, to prevent accidental
+ * unsynchronized access to the map:<pre>
+ *   Map m = Collections.synchronizedMap(new HashMap(...));</pre>
+ *
+ * <p>The iterators returned by all of this class's "collection view methods"
+ * are <i>fail-fast</i>: if the map is structurally modified at any time after
+ * the iterator is created, in any way except through the iterator's own
+ * {@code remove} method, the iterator will throw a
+ * {@link ConcurrentModificationException}.  Thus, in the face of concurrent
+ * modification, the iterator fails quickly and cleanly, rather than risking
+ * arbitrary, non-deterministic behavior at an undetermined time in the
+ * future.
+ *
+ * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
+ * as it is, generally speaking, impossible to make any hard guarantees in the
+ * presence of unsynchronized concurrent modification.  Fail-fast iterators
+ * throw {@code ConcurrentModificationException} on a best-effort basis.
+ * Therefore, it would be wrong to write a program that depended on this
+ * exception for its correctness: <i>the fail-fast behavior of iterators
+ * should be used only to detect bugs.</i>
+ *
+ * <p>This class is a member of the
+ * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
+ * Java Collections Framework</a>.
+ *
+ * @param <K> the type of keys maintained by this map
+ * @param <V> the type of mapped values
+ *
+ * @author  Doug Lea
+ * @author  Josh Bloch
+ * @author  Arthur van Hoff
+ * @author  Neal Gafter
+ * @see     Object#hashCode()
+ * @see     Collection
+ * @see     Map
+ * @see     TreeMap
+ * @see     Hashtable
+ * @since   1.2
+ */
+public class XHashMap<K,V> extends XAbstractMap<K,V>
+    implements Map<K,V>, Cloneable, Serializable {
+
+    private static final long serialVersionUID = 362498820763181265L;
+
+    /*
+     * Implementation notes.
+     *
+     * This map usually acts as a binned (bucketed) hash table, but
+     * when bins get too large, they are transformed into bins of
+     * TreeNodes, each structured similarly to those in
+     * java.util.TreeMap. Most methods try to use normal bins, but
+     * relay to TreeNode methods when applicable (simply by checking
+     * instanceof a node).  Bins of TreeNodes may be traversed and
+     * used like any others, but additionally support faster lookup
+     * when overpopulated. However, since the vast majority of bins in
+     * normal use are not overpopulated, checking for existence of
+     * tree bins may be delayed in the course of table methods.
+     *
+     * Tree bins (i.e., bins whose elements are all TreeNodes) are
+     * ordered primarily by hashCode, but in the case of ties, if two
+     * elements are of the same "class C implements Comparable<C>",
+     * type then their compareTo method is used for ordering. (We
+     * conservatively check generic types via reflection to validate
+     * this -- see method comparableClassFor).  The added complexity
+     * of tree bins is worthwhile in providing worst-case O(log n)
+     * operations when keys either have distinct hashes or are
+     * orderable, Thus, performance degrades gracefully under
+     * accidental or malicious usages in which hashCode() methods
+     * return values that are poorly distributed, as well as those in
+     * which many keys share a hashCode, so long as they are also
+     * Comparable. (If neither of these apply, we may waste about a
+     * factor of two in time and space compared to taking no
+     * precautions. But the only known cases stem from poor user
+     * programming practices that are already so slow that this makes
+     * little difference.)
+     *
+     * Because TreeNodes are about twice the size of regular nodes, we
+     * use them only when bins contain enough nodes to warrant use
+     * (see TREEIFY_THRESHOLD). And when they become too small (due to
+     * removal or resizing) they are converted back to plain bins.  In
+     * usages with well-distributed user hashCodes, tree bins are
+     * rarely used.  Ideally, under random hashCodes, the frequency of
+     * nodes in bins follows a Poisson distribution
+     * (http://en.wikipedia.org/wiki/Poisson_distribution) with a
+     * parameter of about 0.5 on average for the default resizing
+     * threshold of 0.75, although with a large variance because of
+     * resizing granularity. Ignoring variance, the expected
+     * occurrences of list size k are (exp(-0.5) * pow(0.5, k) /
+     * factorial(k)). The first values are:
+     *
+     * 0:    0.60653066
+     * 1:    0.30326533
+     * 2:    0.07581633
+     * 3:    0.01263606
+     * 4:    0.00157952
+     * 5:    0.00015795
+     * 6:    0.00001316
+     * 7:    0.00000094
+     * 8:    0.00000006
+     * more: less than 1 in ten million
+     *
+     * The root of a tree bin is normally its first node.  However,
+     * sometimes (currently only upon Iterator.remove), the root might
+     * be elsewhere, but can be recovered following parent links
+     * (method TreeNode.root()).
+     *
+     * All applicable internal methods accept a hash code as an
+     * argument (as normally supplied from a public method), allowing
+     * them to call each other without recomputing user hashCodes.
+     * Most internal methods also accept a "tab" argument, that is
+     * normally the current table, but may be a new or old one when
+     * resizing or converting.
+     *
+     * When bin lists are treeified, split, or untreeified, we keep
+     * them in the same relative access/traversal order (i.e., field
+     * Node.next) to better preserve locality, and to slightly
+     * simplify handling of splits and traversals that invoke
+     * iterator.remove. When using comparators on insertion, to keep a
+     * total ordering (or as close as is required here) across
+     * rebalancings, we compare classes and identityHashCodes as
+     * tie-breakers.
+     *
+     * The use and transitions among plain vs tree modes is
+     * complicated by the existence of subclass LinkedHashMap. See
+     * below for hook methods defined to be invoked upon insertion,
+     * removal and access that allow LinkedHashMap internals to
+     * otherwise remain independent of these mechanics. (This also
+     * requires that a map instance be passed to some utility methods
+     * that may create new nodes.)
+     *
+     * The concurrent-programming-like SSA-based coding style helps
+     * avoid aliasing errors amid all of the twisty pointer operations.
+     */
+
+    /**
+     * The default initial capacity - MUST be a power of two.
+     */
+    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
+
+    /**
+     * The maximum capacity, used if a higher value is implicitly specified
+     * by either of the constructors with arguments.
+     * MUST be a power of two <= 1<<30.
+     */
+    static final int MAXIMUM_CAPACITY = 1 << 30;
+
+    /**
+     * The load factor used when none specified in constructor.
+     */
+    static final float DEFAULT_LOAD_FACTOR = 0.75f;
+
+    /**
+     * The bin count threshold for using a tree rather than list for a
+     * bin.  Bins are converted to trees when adding an element to a
+     * bin with at least this many nodes. The value must be greater
+     * than 2 and should be at least 8 to mesh with assumptions in
+     * tree removal about conversion back to plain bins upon
+     * shrinkage.
+     */
+    static final int TREEIFY_THRESHOLD = 8;
+
+    /**
+     * The bin count threshold for untreeifying a (split) bin during a
+     * resize operation. Should be less than TREEIFY_THRESHOLD, and at
+     * most 6 to mesh with shrinkage detection under removal.
+     */
+    static final int UNTREEIFY_THRESHOLD = 6;
+
+    /**
+     * The smallest table capacity for which bins may be treeified.
+     * (Otherwise the table is resized if too many nodes in a bin.)
+     * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
+     * between resizing and treeification thresholds.
+     */
+    static final int MIN_TREEIFY_CAPACITY = 64;
+
+    private XNode<K,V> emptyXNode() {
+        return new XNode();
+    }
+    /**
+     * Basic hash bin node, used for most entries.  (See below for
+     * TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
+     */
+    static value class XNode<K,V> implements Map.Entry<K,V> {
+        final int hash;
+        final K key;
+        V value;
+        Node<K,V> next;
+
+        XNode() {
+            this.hash = 0;
+            this.key = null;
+            this.value = null;
+            this.next = null;
+        }
+
+        XNode(int hash, K key, V value, Node<K,V> next) {
+            this.hash = hash;
+            this.key = key;
+            this.value = value;
+            this.next = next;
+        }
+
+        boolean isEmpty() {
+            return hash == 0 && key == null && value == null;
+        }
+        public final K getKey()        { return key; }
+        public final V getValue()      { return value; }
+        public final String toString() { return key + "=" + value; }
+
+        public final int hashCode() {
+            return Objects.hashCode(key) ^ Objects.hashCode(value);
+        }
+
+        public final V setValue(V newValue) {
+            throw new IllegalStateException("XNode cannot set a value");
+//            V oldValue = value;
+//            value = newValue;
+//            return oldValue;
+        }
+
+        public final boolean equals(Object o) {
+            if (o instanceof Map.Entry) {
+                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
+                if (Objects.equals(key, e.getKey()) &&
+                    Objects.equals(value, e.getValue()))
+                    return true;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Basic hash bin node, used for overflow entries.  (See below for
+     * TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
+     */
+    static class Node<K,V> implements Map.Entry<K,V> {
+        final int hash;
+        final K key;
+        V value;
+        Node<K,V> next;
+
+        Node(int hash, K key, V value, Node<K,V> next) {
+            this.hash = hash;
+            this.key = key;
+            this.value = value;
+            this.next = next;
+        }
+
+        public final K getKey()        { return key; }
+        public final V getValue()      { return value; }
+        public final String toString() { return key + "=" + value; }
+        public final int hashCode() {
+            return Objects.hashCode(key) ^ Objects.hashCode(value);
+        }
+
+        public final V setValue(V newValue) {
+            V oldValue = value;
+            value = newValue;
+            return oldValue;
+        }
+
+        public final boolean equals(Object o) {
+            if (o == this)
+                return true;
+            if (o instanceof Map.Entry) {
+                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
+                if (Objects.equals(key, e.getKey()) &&
+                    Objects.equals(value, e.getValue()))
+                    return true;
+            }
+            return false;
+        }
+    }
+
+    value class XNodeWrapper implements Map.Entry<K,V> {
+        int index;
+
+        XNodeWrapper(int index) {
+            this.index = index;
+        }
+
+        public K getKey() {
+            XNode<K,V> e = table[index];
+            return e.isEmpty() ? null : e.key;
+        }
+
+        public V getValue() {
+            XNode<K,V> e = table[index];
+            return e.isEmpty() ? null : e.value;
+        }
+
+        /**
+         * Replaces the value corresponding to this entry with the specified
+         * value (optional operation).  (Writes through to the map.)  The
+         * behavior of this call is undefined if the mapping has already been
+         * removed from the map (by the iterator's {@code remove} operation).
+         *
+         * @param value new value to be stored in this entry
+         * @return old value corresponding to the entry
+         * @throws UnsupportedOperationException if the {@code put} operation
+         *         is not supported by the backing map
+         * @throws ClassCastException if the class of the specified value
+         *         prevents it from being stored in the backing map
+         * @throws NullPointerException if the backing map does not permit
+         *         null values, and the specified value is null
+         * @throws IllegalArgumentException if some property of this value
+         *         prevents it from being stored in the backing map
+         * @throws IllegalStateException implementations may, but are not
+         *         required to, throw this exception if the entry has been
+         *         removed from the backing map.
+         */
+        public V setValue(V value) {
+            XNode<K,V> e = table[index];
+            assert !e.isEmpty();
+            table[index] = new XNode(e.hash, e.key, value, e.next);
+            return e.value;
+        }
+    }
+    /* ---------------- Static utilities -------------- */
+
+    /**
+     * Computes key.hashCode() and spreads (XORs) higher bits of hash
+     * to lower.  Because the table uses power-of-two masking, sets of
+     * hashes that vary only in bits above the current mask will
+     * always collide. (Among known examples are sets of Float keys
+     * holding consecutive whole numbers in small tables.)  So we
+     * apply a transform that spreads the impact of higher bits
+     * downward. There is a tradeoff between speed, utility, and
+     * quality of bit-spreading. Because many common sets of hashes
+     * are already reasonably distributed (so don't benefit from
+     * spreading), and because we use trees to handle large sets of
+     * collisions in bins, we just XOR some shifted bits in the
+     * cheapest possible way to reduce systematic lossage, as well as
+     * to incorporate impact of the highest bits that would otherwise
+     * never be used in index calculations because of table bounds.
+     */
+    static final int hash(Object key) {
+        int h;
+        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
+    }
+
+    /**
+     * Returns x's Class if it is of the form "class C implements
+     * Comparable<C>", else null.
+     */
+    static Class<?> comparableClassFor(Object x) {
+        if (x instanceof Comparable) {
+            Class<?> c; Type[] ts, as; ParameterizedType p;
+            if ((c = x.getClass()) == String.class) // bypass checks
+                return c;
+            if ((ts = c.getGenericInterfaces()) != null) {
+                for (Type t : ts) {
+                    if ((t instanceof ParameterizedType) &&
+                        ((p = (ParameterizedType) t).getRawType() ==
+                         Comparable.class) &&
+                        (as = p.getActualTypeArguments()) != null &&
+                        as.length == 1 && as[0] == c) // type arg is c
+                        return c;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns k.compareTo(x) if x matches kc (k's screened comparable
+     * class), else 0.
+     */
+    @SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable
+    static int compareComparables(Class<?> kc, Object k, Object x) {
+        return (x == null || x.getClass() != kc ? 0 :
+                ((Comparable)k).compareTo(x));
+    }
+
+    /**
+     * Returns a power of two size for the given target capacity.
+     */
+    static final int tableSizeFor(int cap) {
+        int n = -1 >>> Integer.numberOfLeadingZeros(cap - 1);
+        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+    }
+
+    /* ---------------- Fields -------------- */
+
+    /**
+     * The table, initialized on first use, and resized as
+     * necessary. When allocated, length is always a power of two.
+     * (We also tolerate length zero in some operations to allow
+     * bootstrapping mechanics that are currently not needed.)
+     */
+    transient XNode<K,V>[] table;
+
+    /**
+     * Holds cached entrySet(). Note that AbstractMap fields are used
+     * for keySet() and values().
+     */
+    transient Set<Map.Entry<K,V>> entrySet;
+
+    /**
+     * The number of key-value mappings contained in this map.
+     */
+    transient int size;
+
+    /**
+     * The number of times this HashMap has been structurally modified
+     * Structural modifications are those that change the number of mappings in
+     * the HashMap or otherwise modify its internal structure (e.g.,
+     * rehash).  This field is used to make iterators on Collection-views of
+     * the HashMap fail-fast.  (See ConcurrentModificationException).
+     */
+    transient int modCount;
+
+    /**
+     * The next size value at which to resize (capacity * load factor).
+     *
+     * @serial
+     */
+    // (The javadoc description is true upon serialization.
+    // Additionally, if the table array has not been allocated, this
+    // field holds the initial array capacity, or zero signifying
+    // DEFAULT_INITIAL_CAPACITY.)
+    int threshold;
+
+    /**
+     * The load factor for the hash table.
+     *
+     * @serial
+     */
+    final float loadFactor;
+
+    /* ---------------- Public operations -------------- */
+
+    /**
+     * Constructs an empty {@code HashMap} with the specified initial
+     * capacity and load factor.
+     *
+     * @param  initialCapacity the initial capacity
+     * @param  loadFactor      the load factor
+     * @throws IllegalArgumentException if the initial capacity is negative
+     *         or the load factor is nonpositive
+     */
+    public XHashMap(int initialCapacity, float loadFactor) {
+        if (initialCapacity < 0)
+            throw new IllegalArgumentException("Illegal initial capacity: " +
+                                               initialCapacity);
+        if (initialCapacity > MAXIMUM_CAPACITY)
+            initialCapacity = MAXIMUM_CAPACITY;
+        if (loadFactor <= 0 || Float.isNaN(loadFactor))
+            throw new IllegalArgumentException("Illegal load factor: " +
+                                               loadFactor);
+        this.loadFactor = loadFactor;
+        this.threshold = tableSizeFor(initialCapacity);
+    }
+
+    /**
+     * Constructs an empty {@code HashMap} with the specified initial
+     * capacity and the default load factor (0.75).
+     *
+     * @param  initialCapacity the initial capacity.
+     * @throws IllegalArgumentException if the initial capacity is negative.
+     */
+    public XHashMap(int initialCapacity) {
+        this(initialCapacity, DEFAULT_LOAD_FACTOR);
+    }
+
+    /**
+     * Constructs an empty {@code HashMap} with the default initial capacity
+     * (16) and the default load factor (0.75).
+     */
+    public XHashMap() {
+        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
+    }
+
+    /**
+     * Constructs a new {@code HashMap} with the same mappings as the
+     * specified {@code Map}.  The {@code HashMap} is created with
+     * default load factor (0.75) and an initial capacity sufficient to
+     * hold the mappings in the specified {@code Map}.
+     *
+     * @param   m the map whose mappings are to be placed in this map
+     * @throws  NullPointerException if the specified map is null
+     */
+    public XHashMap(Map<? extends K, ? extends V> m) {
+        this.loadFactor = DEFAULT_LOAD_FACTOR;
+        putMapEntries(m, false);
+    }
+
+    /**
+     * Implements Map.putAll and Map constructor.
+     *
+     * @param m the map
+     * @param evict false when initially constructing this map, else true.
+     */
+    final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
+        int s = m.size();
+        if (s > 0) {
+            if (table == null) { // pre-size
+                float ft = ((float)s / loadFactor) + 1.0F;
+                int t = ((ft < (float)MAXIMUM_CAPACITY) ?
+                         (int)ft : MAXIMUM_CAPACITY);
+                if (t > threshold)
+                    threshold = tableSizeFor(t);
+            } else {
+                // Because of linked-list bucket constraints, we cannot
+                // expand all at once, but can reduce total resize
+                // effort by repeated doubling now vs later
+                while (s > threshold && table.length < MAXIMUM_CAPACITY)
+                    resize();
+            }
+
+            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
+                K key = e.getKey();
+                V value = e.getValue();
+                putVal(hash(key), key, value, false, evict);
+            }
+        }
+    }
+
+    /**
+     * Returns the number of key-value mappings in this map.
+     *
+     * @return the number of key-value mappings in this map
+     */
+    public int size() {
+        return size;
+    }
+
+    /**
+     * Returns {@code true} if this map contains no key-value mappings.
+     *
+     * @return {@code true} if this map contains no key-value mappings
+     */
+    public boolean isEmpty() {
+        return size == 0;
+    }
+
+    /**
+     * Returns the value to which the specified key is mapped,
+     * or {@code null} if this map contains no mapping for the key.
+     *
+     * <p>More formally, if this map contains a mapping from a key
+     * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
+     * key.equals(k))}, then this method returns {@code v}; otherwise
+     * it returns {@code null}.  (There can be at most one such mapping.)
+     *
+     * <p>A return value of {@code null} does not <i>necessarily</i>
+     * indicate that the map contains no mapping for the key; it's also
+     * possible that the map explicitly maps the key to {@code null}.
+     * The {@link #containsKey containsKey} operation may be used to
+     * distinguish these two cases.
+     *
+     * @see #put(Object, Object)
+     */
+    public V get(Object key) {
+        int hash = hash(key);
+        Node<K,V> e;
+        XNode<K,V> n = getXNode(hash, key);
+        return (!n.isEmpty()) ? n.value
+                : (e = getNode(hash, key)) == null ? null : e.value;
+    }
+
+    /**
+     * Implements Map.get and related methods.
+     *
+     * @param hash hash for key
+     * @param key the key
+     * @return the node, or emptyXNode() if not at top level.
+     */
+    final XNode<K,V> getXNode(int hash, Object key) {
+        XNode<K, V>[] tab;
+        XNode<K, V> first;
+        int n;
+        K k;
+        if ((tab = table) != null && (n = tab.length) > 0 &&
+                !(first = tab[(n - 1) & hash]).isEmpty()) {
+            if (first.hash == hash && // always check first node
+                    ((k = first.key) == key || (key != null && key.equals(k))))
+                return first;
+        }
+        return emptyXNode();
+    }
+
+     /**
+     * Implements Map.get and related methods when the key is not found in the primary entry.
+     *
+     * @param hash hash for key
+     * @param key the key
+     * @return the node, or null if none
+     */
+    final Node<K,V> getNode(int hash, Object key) {
+        XNode<K,V>[] tab; XNode<K,V> first; Node<K,V> e; int n; K k;
+        if ((tab = table) != null && (n = tab.length) > 0 &&
+            !(first = tab[(n - 1) & hash]).isEmpty()) {
+            if ((e = first.next) != null) {
+                if (e instanceof TreeNode)
+                    return ((TreeNode<K,V>)e).getTreeNode(hash, key);
+                do {
+                    if (e.hash == hash &&
+                        ((k = e.key) == key || (key != null && key.equals(k))))
+                        return e;
+                } while ((e = e.next) != null);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns {@code true} if this map contains a mapping for the
+     * specified key.
+     *
+     * @param   key   The key whose presence in this map is to be tested
+     * @return {@code true} if this map contains a mapping for the specified
+     * key.
+     */
+    public boolean containsKey(Object key) {
+        int hash = hash(key);
+        Node<K,V> e;
+        XNode<K,V> n = getXNode(hash, key);
+        return !n.isEmpty() || (e = getNode(hash, key)) != null;
+    }
+
+    /**
+     * Associates the specified value with the specified key in this map.
+     * If the map previously contained a mapping for the key, the old
+     * value is replaced.
+     *
+     * @param key key with which the specified value is to be associated
+     * @param value value to be associated with the specified key
+     * @return the previous value associated with {@code key}, or
+     *         {@code null} if there was no mapping for {@code key}.
+     *         (A {@code null} return can also indicate that the map
+     *         previously associated {@code null} with {@code key}.)
+     */
+    public V put(K key, V value) {
+        return putVal(hash(key), key, value, false, true);
+    }
+
+    /**
+     * Implements Map.put and related methods.
+     *
+     * @param hash hash for key
+     * @param key the key
+     * @param value the value to put
+     * @param onlyIfAbsent if true, don't change existing value
+     * @param evict if false, the table is in creation mode.
+     * @return previous value, or null if none
+     */
+    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
+                   boolean evict) {
+        XNode<K,V>[] tab; XNode<K,V> tp; int n, i;
+        if ((tab = table) == null || (n = tab.length) == 0)
+            n = (tab = resize()).length;
+        if ((tp = tab[i = (n - 1) & hash]).isEmpty()) {
+            tab[i] = new XNode(hash, key, value, null);
+        } else {
+            Node<K,V> e; K k;
+            if (tp.hash == hash &&
+                ((k = tp.key) == key || (key != null && key.equals(k)))) {
+                if (!onlyIfAbsent || tp.value == null) {
+                    tab[i] = new XNode(hash, k, value, tp.next);
+                }
+                return tp.value;
+            } else if ((e = tp.next) == null) {
+                Node<K,V> x = newNode(hash, key, value, null);
+                tab[i] = new XNode(tp.hash, tp.key, tp.value, x);
+            } else if (e instanceof TreeNode) {
+                e = ((TreeNode<K, V>) e).putTreeVal(this, tab, hash, key, value);
+            } else {
+                for (int binCount = 0; ; ++binCount) {
+                    if (e.hash == hash &&
+                            ((k = e.key) == key || (key != null && key.equals(k))))
+                        break;
+                    Node<K,V> p = e;
+                    if ((e = p.next) == null) {
+                        p.next = newNode(hash, key, value, null);
+                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
+                            treeifyBin(tab, hash);
+                        break;
+                    }
+                }
+            }
+            if (e != null) { // existing mapping for key
+                V oldValue = e.value;
+                if (!onlyIfAbsent || oldValue == null)
+                    e.value = value;
+                return oldValue;
+            }
+        }
+
+        ++modCount;
+        if (++size > threshold)
+            resize();
+        return null;
+    }
+
+    /**
+     * Initializes or doubles table size.  If null, allocates in
+     * accord with initial capacity target held in field threshold.
+     * Otherwise, because we are using power-of-two expansion, the
+     * elements from each bin must either stay at same index, or move
+     * with a power of two offset in the new table.
+     *
+     * @return the table
+     */
+    final XNode<K,V>[] resize() {
+        XNode<K,V>[] oldTab = table;
+        int oldCap = (oldTab == null) ? 0 : oldTab.length;
+        int oldThr = threshold;
+        int newCap, newThr = 0;
+        if (oldCap > 0) {
+            if (oldCap >= MAXIMUM_CAPACITY) {
+                threshold = Integer.MAX_VALUE;
+                return oldTab;
+            }
+            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
+                     oldCap >= DEFAULT_INITIAL_CAPACITY)
+                newThr = oldThr << 1; // double threshold
+        }
+        else if (oldThr > 0) // initial capacity was placed in threshold
+            newCap = oldThr;
+        else {               // zero initial threshold signifies using defaults
+            newCap = DEFAULT_INITIAL_CAPACITY;
+            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
+        }
+        if (newThr == 0) {
+            float ft = (float)newCap * loadFactor;
+            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
+                      (int)ft : Integer.MAX_VALUE);
+        }
+        threshold = newThr;
+        @SuppressWarnings({"rawtypes","unchecked"})
+        XNode<K,V>[] newTab = (XNode<K,V>[])new XNode[newCap];
+        table = newTab;
+        if (oldTab != null) {
+            for (int j = 0; j < oldCap; ++j) {
+                XNode<K,V> x;
+                Node<K,V> e;
+                if (!(x = oldTab[j]).isEmpty()) {
+                    oldTab[j] = emptyXNode();
+                    if ((e = x.next) == null)
+                        newTab[x.hash & (newCap - 1)] = new XNode(x.hash, x.key, x.value, null);
+                    else if (e instanceof TreeNode)
+                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
+                    else { // preserve order
+                        Node<K,V> loHead = null, loTail = null;
+                        Node<K,V> hiHead = null, hiTail = null;
+                        Node<K,V> next;
+                        do {
+                            next = e.next;
+                            if ((e.hash & oldCap) == 0) {
+                                if (loTail == null)
+                                    loHead = e;
+                                else
+                                    loTail.next = e;
+                                loTail = e;
+                            }
+                            else {
+                                if (hiTail == null)
+                                    hiHead = e;
+                                else
+                                    hiTail.next = e;
+                                hiTail = e;
+                            }
+                        } while ((e = next) != null);
+                        if (loTail != null)
+                            loTail.next = null;
+                        if (hiTail != null)
+                            hiTail.next = null;
+
+                        newTab[j] = (j == (x.hash & (newCap - 1)))
+                                ? new XNode(x.hash, x.key, x.value, loHead)
+                                : ((loHead != null)
+                                ? new XNode(loHead.hash, loHead.key, loHead.value, loHead.next) :
+                                emptyXNode());
+
+                        newTab[j + oldCap] = ((j + oldCap) == (x.hash & (newCap - 1)))
+                                ? new XNode(x.hash, x.key, x.value, hiHead)
+                                : ((hiHead != null)
+                                ? new XNode(hiHead.hash, hiHead.key, hiHead.value, hiHead.next) :
+                                emptyXNode());
+                    }
+                }
+            }
+        }
+        return newTab;
+    }
+
+    /**
+     * Replaces all linked nodes in bin at index for given hash unless
+     * table is too small, in which case resizes instead.
+     */
+    final void treeifyBin(XNode<K,V>[] tab, int hash) {
+//        int n, index; Node<K,V> e;
+//        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
+//            resize();
+//        else if ((e = tab[index = (n - 1) & hash]) != null) {
+//            TreeNode<K,V> hd = null, tl = null;
+//            do {
+//                TreeNode<K,V> p = replacementTreeNode(e, null);
+//                if (tl == null)
+//                    hd = p;
+//                else {
+//                    p.prev = tl;
+//                    tl.next = p;
+//                }
+//                tl = p;
+//            } while ((e = e.next) != null);
+//            if ((tab[index] = hd) != null)
+//                hd.treeify(tab);
+//        }
+    }
+
+    /**
+     * Copies all of the mappings from the specified map to this map.
+     * These mappings will replace any mappings that this map had for
+     * any of the keys currently in the specified map.
+     *
+     * @param m mappings to be stored in this map
+     * @throws NullPointerException if the specified map is null
+     */
+    public void putAll(Map<? extends K, ? extends V> m) {
+        putMapEntries(m, true);
+    }
+
+    /**
+     * Removes the mapping for the specified key from this map if present.
+     *
+     * @param  key key whose mapping is to be removed from the map
+     * @return the previous value associated with {@code key}, or
+     *         {@code null} if there was no mapping for {@code key}.
+     *         (A {@code null} return can also indicate that the map
+     *         previously associated {@code null} with {@code key}.)
+     */
+    public V remove(Object key) {
+        Optional<V> o = removeNode(hash(key), key, null, false, true);
+        return o.orElse(null);
+    }
+
+    /**
+     * Implements Map.remove and related methods.
+     *
+     * @param hash hash for key
+     * @param key the key
+     * @param value the value to match if matchValue, else ignored
+     * @param matchValue if true only remove if value is equal
+     * @param movable if false do not move other nodes while removing
+     * @return the node, or null if none
+     */
+    final Optional<V> removeNode(int hash, Object key, Object value,
+                               boolean matchValue, boolean movable) {
+        XNode<K,V>[] tab; XNode<K,V> te; int n, index;
+        if ((tab = table) != null && (n = tab.length) > 0 &&
+            !(te = tab[index = (n - 1) & hash]).isEmpty()) {
+            Node<K,V> node = null, e; K k; V v = null;
+            if (te.hash == hash &&
+                ((k = te.key) == key || (key != null && key.equals(k)))) {
+                if ((!matchValue || (v = te.value) == value ||
+                        (value != null && value.equals(v)))) {
+                    tab[index] = ((e = te.next) == null)
+                            ? emptyXNode()
+                            : new XNode(hash, e.key, e.value, e.next);
+                    ++modCount;
+                    --size;
+                    return Optional.ofNullable(v);
+                }
+            } else if ((e = te.next) != null) {
+                Node<K,V> p = null;
+                if (e instanceof TreeNode)
+                    node = ((TreeNode<K,V>)e).getTreeNode(hash, key);
+                else {
+                    do {
+                        if (e.hash == hash &&
+                            ((k = e.key) == key ||
+                             (key != null && key.equals(k)))) {
+                            node = e;
+                            break;
+                        }
+                        p = e;
+                    } while ((e = e.next) != null);
+                }
+
+                if (node != null && (!matchValue || (v = node.value) == value ||
+                                     (value != null && value.equals(v)))) {
+                    if (node instanceof TreeNode)
+                        ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
+                    else if (p == null)
+                        tab[index] = new XNode(hash, node.key, node.value, node.next);
+                    else
+                        p.next = node.next;
+                    ++modCount;
+                    --size;
+                    return Optional.of(node.value);
+                }
+            }
+        }
+        return Optional.empty();
+    }
+
+    /**
+     * Removes all of the mappings from this map.
+     * The map will be empty after this call returns.
+     */
+    public void clear() {
+        modCount++;
+        if (table != null && size > 0) {
+            size = 0;
+            table = null;
+            threshold = 0;
+        }
+    }
+
+    /**
+     * Returns {@code true} if this map maps one or more keys to the
+     * specified value.
+     *
+     * @param value value whose presence in this map is to be tested
+     * @return {@code true} if this map maps one or more keys to the
+     *         specified value
+     */
+    public boolean containsValue(Object value) {
+        XNode<K,V>[] tab; V v;
+        if ((tab = table) != null && size > 0) {
+            for (XNode<K,V> te : tab) {
+                if (!te.isEmpty()) {
+                    if ((v = te.value) == value ||
+                            (value != null && value.equals(v)))
+                        return true;
+                    for (Node<K, V> e = te.next; e != null; e = e.next) {
+                        if ((v = e.value) == value ||
+                                (value != null && value.equals(v)))
+                            return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns a {@link Set} view of the keys contained in this map.
+     * The set is backed by the map, so changes to the map are
+     * reflected in the set, and vice-versa.  If the map is modified
+     * while an iteration over the set is in progress (except through
+     * the iterator's own {@code remove} operation), the results of
+     * the iteration are undefined.  The set supports element removal,
+     * which removes the corresponding mapping from the map, via the
+     * {@code Iterator.remove}, {@code Set.remove},
+     * {@code removeAll}, {@code retainAll}, and {@code clear}
+     * operations.  It does not support the {@code add} or {@code addAll}
+     * operations.
+     *
+     * @return a set view of the keys contained in this map
+     */
+    public Set<K> keySet() {
+        Set<K> ks = keySet;
+        if (ks == null) {
+            ks = new KeySet();
+            keySet = ks;
+        }
+        return ks;
+    }
+
+    /**
+     * Prepares the array for {@link Collection#toArray(Object[])} implementation.
+     * If supplied array is smaller than this map size, a new array is allocated.
+     * If supplied array is bigger than this map size, a null is written at size index.
+     *
+     * @param a an original array passed to {@code toArray()} method
+     * @param <T> type of array elements
+     * @return an array ready to be filled and returned from {@code toArray()} method.
+     */
+    @SuppressWarnings("unchecked")
+    final <T> T[] prepareArray(T[] a) {
+        int size = this.size;
+        if (a.length < size) {
+            return (T[]) java.lang.reflect.Array
+                    .newInstance(a.getClass().getComponentType(), size);
+        }
+        if (a.length > size) {
+            a[size] = null;
+        }
+        return a;
+    }
+
+    /**
+     * Fills an array with this map keys and returns it. This method assumes
+     * that input array is big enough to fit all the keys. Use
+     * {@link #prepareArray(Object[])} to ensure this.
+     *
+     * @param a an array to fill
+     * @param <T> type of array elements
+     * @return supplied array
+     */
+    <T> T[] keysToArray(T[] a) {
+        Object[] r = a;
+        XNode<K,V>[] tab;
+        int idx = 0;
+        int i = 0;
+        if (size > 0 && (tab = table) != null) {
+            for (XNode<K,V> te : tab) {
+                if (!te.isEmpty()) {
+                    r[idx++] = te.key;
+                    for (Node<K, V> e = te.next; e != null; e = e.next) {
+                        r[idx++] = e.key;
+                    }
+                }
+            }
+        }
+        return a;
+    }
+
+    /**
+     * Fills an array with this map values and returns it. This method assumes
+     * that input array is big enough to fit all the values. Use
+     * {@link #prepareArray(Object[])} to ensure this.
+     *
+     * @param a an array to fill
+     * @param <T> type of array elements
+     * @return supplied array
+     */
+    <T> T[] valuesToArray(T[] a) {
+        Object[] r = a;
+        XNode<K,V>[] tab;
+        int idx = 0;
+        if (size > 0 && (tab = table) != null) {
+            for (XNode<K,V> te : tab) {
+                if (!te.isEmpty()) {
+                    r[idx++] = te.value;
+                    for (Node<K, V> e = te.next; e != null; e = e.next) {
+                        r[idx++] = e.value;
+                    }
+                }
+            }
+        }
+        return a;
+    }
+
+    final class KeySet extends AbstractSet<K> {
+        public final int size()                 { return size; }
+        public final void clear()               { XHashMap.this.clear(); }
+        public final Iterator<K> iterator()     { return new KeyIterator(); }
+        public final boolean contains(Object o) { return containsKey(o); }
+        public final boolean remove(Object key) {
+            return removeNode(hash(key), key, null, false, true).isPresent();
+        }
+
+        public Object[] toArray() {
+            return keysToArray(new Object[size]);
+        }
+
+        public <T> T[] toArray(T[] a) {
+            return keysToArray(prepareArray(a));
+        }
+
+        public final void forEach(Consumer<? super K> action) {
+            XNode<K,V>[] tab;
+            if (action == null)
+                throw new NullPointerException();
+            if (size > 0 && (tab = table) != null) {
+                int mc = modCount;
+                for (XNode<K,V> te : tab) {
+                    if (!te.isEmpty()) {
+                        action.accept(te.key);
+                        for (Node<K, V> e = te.next; e != null; e = e.next)
+                            action.accept(e.key);
+                    }
+                }
+                if (modCount != mc)
+                    throw new ConcurrentModificationException();
+            }
+        }
+    }
+
+    /**
+     * Returns a {@link Collection} view of the values contained in this map.
+     * The collection is backed by the map, so changes to the map are
+     * reflected in the collection, and vice-versa.  If the map is
+     * modified while an iteration over the collection is in progress
+     * (except through the iterator's own {@code remove} operation),
+     * the results of the iteration are undefined.  The collection
+     * supports element removal, which removes the corresponding
+     * mapping from the map, via the {@code Iterator.remove},
+     * {@code Collection.remove}, {@code removeAll},
+     * {@code retainAll} and {@code clear} operations.  It does not
+     * support the {@code add} or {@code addAll} operations.
+     *
+     * @return a view of the values contained in this map
+     */
+    public Collection<V> values() {
+        Collection<V> vs = values;
+        if (vs == null) {
+            vs = new Values();
+            values = vs;
+        }
+        return vs;
+    }
+
+    final class Values extends AbstractCollection<V> {
+        public final int size()                 { return size; }
+        public final void clear()               { XHashMap.this.clear(); }
+        public final Iterator<V> iterator()     { return new ValueIterator(); }
+        public final boolean contains(Object o) { return containsValue(o); }
+
+        public Object[] toArray() {
+            return valuesToArray(new Object[size]);
+        }
+
+        public <T> T[] toArray(T[] a) {
+            return valuesToArray(prepareArray(a));
+        }
+
+        public final void forEach(Consumer<? super V> action) {
+            XNode<K,V>[] tab;
+            if (action == null)
+                throw new NullPointerException();
+            if (size > 0 && (tab = table) != null) {
+                int mc = modCount;
+                for (XNode<K,V> te : tab) {
+                    if (!te.isEmpty()) {
+                        action.accept(te.value);
+                        for (Node<K, V> e = te.next; e != null; e = e.next)
+                            action.accept(e.value);
+                    }
+                }
+                if (modCount != mc)
+                    throw new ConcurrentModificationException();
+            }
+        }
+    }
+
+    /**
+     * Returns a {@link Set} view of the mappings contained in this map.
+     * The set is backed by the map, so changes to the map are
+     * reflected in the set, and vice-versa.  If the map is modified
+     * while an iteration over the set is in progress (except through
+     * the iterator's own {@code remove} operation, or through the
+     * {@code setValue} operation on a map entry returned by the
+     * iterator) the results of the iteration are undefined.  The set
+     * supports element removal, which removes the corresponding
+     * mapping from the map, via the {@code Iterator.remove},
+     * {@code Set.remove}, {@code removeAll}, {@code retainAll} and
+     * {@code clear} operations.  It does not support the
+     * {@code add} or {@code addAll} operations.
+     *
+     * @return a set view of the mappings contained in this map
+     */
+    public Set<Map.Entry<K,V>> entrySet() {
+        Set<Map.Entry<K,V>> es;
+        return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
+    }
+
+    final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
+        public final int size()                 { return size; }
+        public final void clear()               { XHashMap.this.clear(); }
+        public final Iterator<Map.Entry<K,V>> iterator() {
+            return new EntryIterator();
+        }
+        public final boolean contains(Object o) {
+            if (!(o instanceof Map.Entry))
+                return false;
+            Map.Entry<?,?> e = (Map.Entry<?,?>) o;
+            Object key = e.getKey();
+            Node<K,V> candidate = getNode(hash(key), key);
+            return candidate != null && candidate.equals(e);
+        }
+        public final boolean remove(Object o) {
+            if (o instanceof Map.Entry) {
+                Map.Entry<?,?> e = (Map.Entry<?,?>) o;
+                Object key = e.getKey();
+                Object value = e.getValue();
+                return removeNode(hash(key), key, value, true, true).isPresent();
+            }
+            return false;
+        }
+        public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
+            XNode<K,V>[] tab;
+            if (action == null)
+                throw new NullPointerException();
+            if (size > 0 && (tab = table) != null) {
+                int mc = modCount;
+                for (XNode<K,V> te : tab) {
+                    if (!te.isEmpty()) {
+                        action.accept(new XNodeWrapper(te.hash & (tab.length - 1)));
+                        for (Node<K, V> e = te.next; e != null; e = e.next)
+                            action.accept(e);
+                    }
+                }
+                if (modCount != mc)
+                    throw new ConcurrentModificationException();
+            }
+        }
+    }
+
+    // Overrides of JDK8 Map extension methods
+
+    @Override
+    public V getOrDefault(Object key, V defaultValue) {
+        Node<K,V> e;
+        return (e = getNode(hash(key), key)) == null ? defaultValue : e.value;
+    }
+
+    @Override
+    public V putIfAbsent(K key, V value) {
+        return putVal(hash(key), key, value, true, true);
+    }
+
+    @Override
+    public boolean remove(Object key, Object value) {
+        return removeNode(hash(key), key, value, true, true).isPresent();
+    }
+
+    @Override
+    public boolean replace(K key, V oldValue, V newValue) {
+        Node<K,V> e; V v;
+        if ((e = getNode(hash(key), key)) != null &&
+            ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
+            e.value = newValue;
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public V replace(K key, V value) {
+        Node<K,V> e;
+        if ((e = getNode(hash(key), key)) != null) {
+            V oldValue = e.value;
+            e.value = value;
+            return oldValue;
+        }
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This method will, on a best-effort basis, throw a
+     * {@link ConcurrentModificationException} if it is detected that the
+     * mapping function modifies this map during computation.
+     *
+     * @throws ConcurrentModificationException if it is detected that the
+     * mapping function modified this map
+     */
+    @Override
+    public V computeIfAbsent(K key,
+                             Function<? super K, ? extends V> mappingFunction) {
+        if (mappingFunction == null)
+            throw new NullPointerException();
+        int hash = hash(key);
+        XNode<K,V>[] tab; XNode<K,V> first; int n, i;
+        int binCount = 0;
+        TreeNode<K,V> t = null;
+        Node<K,V> old = null;
+        if (size > threshold || (tab = table) == null ||
+            (n = tab.length) == 0)
+            n = (tab = resize()).length;
+        if (!(first = tab[i = (n - 1) & hash]).isEmpty()) {
+            K k;
+            if (first.hash == hash &&
+                    ((k = first.key) == key || (key != null && key.equals(k)))) {
+                return first.value;
+            }
+            Node<K,V> e = first.next;
+            if (e instanceof TreeNode)
+                old = (t = (TreeNode<K,V>)e).getTreeNode(hash, key);
+            else {
+                do {
+                    if (e.hash == hash &&
+                            ((k = e.key) == key || (key != null && key.equals(k)))) {
+                        old = e;
+                        break;
+                    }
+                    ++binCount;
+                } while ((e = e.next) != null);
+            }
+
+            V oldValue;
+            if (old != null && (oldValue = old.value) != null) {
+                return oldValue;
+            }
+        }
+        int mc = modCount;
+        V v = mappingFunction.apply(key);
+        if (mc != modCount) { throw new ConcurrentModificationException(); }
+        if (v == null) {
+            return null;
+        } else if (old != null) {
+            old.value = v;
+            return v;
+        }
+        else if (t != null)
+            t.putTreeVal(this, tab, hash, key, v);
+        else {
+            Node<K,V> x = (tab[i].isEmpty()) ? null : newNode(hash, key, v, null);
+            tab[i] = new XNode(hash, key, v, x);
+            if (binCount >= TREEIFY_THRESHOLD - 1)
+                treeifyBin(tab, hash);
+        }
+        modCount = mc + 1;
+        ++size;
+        return v;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This method will, on a best-effort basis, throw a
+     * {@link ConcurrentModificationException} if it is detected that the
+     * remapping function modifies this map during computation.
+     *
+     * @throws ConcurrentModificationException if it is detected that the
+     * remapping function modified this map
+     */
+    @Override
+    public V computeIfPresent(K key,
+                              BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+        if (remappingFunction == null)
+            throw new NullPointerException();
+        Node<K,V> e; V oldValue;
+        int hash = hash(key);
+        if ((e = getNode(hash, key)) != null &&
+            (oldValue = e.value) != null) {
+            int mc = modCount;
+            V v = remappingFunction.apply(key, oldValue);
+            if (mc != modCount) { throw new ConcurrentModificationException(); }
+            if (v != null) {
+                e.value = v;
+                return v;
+            }
+            else
+                removeNode(hash, key, null, false, true);
+        }
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This method will, on a best-effort basis, throw a
+     * {@link ConcurrentModificationException} if it is detected that the
+     * remapping function modifies this map during computation.
+     *
+     * @throws ConcurrentModificationException if it is detected that the
+     * remapping function modified this map
+     */
+    @Override
+    public V compute(K key,
+                     BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+        if (remappingFunction == null)
+            throw new NullPointerException();
+        int hash = hash(key);
+        XNode<K,V>[] tab; XNode<K,V> first; int n, i;
+        int binCount = 0;
+        TreeNode<K,V> t = null;
+        Node<K,V> old = null;
+        if (size > threshold || (tab = table) == null ||
+            (n = tab.length) == 0)
+            n = (tab = resize()).length;
+        if (!(first = tab[i = (n - 1) & hash]).isEmpty()) {
+            Node<K,V> e = first.next;K k;
+            if (first.hash == hash &&
+                    ((k = first.key) == key || (key != null && key.equals(k)))) {
+                V v = remappingFunction.apply(k, first.value);
+                tab[i] = new XNode(hash, k, v, e);
+                return v;
+            }
+            if (e instanceof TreeNode)
+                old = (t = (TreeNode<K,V>)e).getTreeNode(hash, key);
+            else {
+                do {
+                    if (e.hash == hash &&
+                        ((k = e.key) == key || (key != null && key.equals(k)))) {
+                        old = e;
+                        break;
+                    }
+                    ++binCount;
+                } while ((e = e.next) != null);
+            }
+        }
+        V oldValue = (old == null) ? null : old.value;
+        int mc = modCount;
+        V v = remappingFunction.apply(key, oldValue);
+        if (mc != modCount) { throw new ConcurrentModificationException(); }
+        if (old != null) {
+            if (v != null) {
+                old.value = v;
+            }
+            else
+                removeNode(hash, key, null, false, true);
+        }
+        else if (v != null) {
+            if (t != null)
+                t.putTreeVal(this, tab, hash, key, v);
+            else {
+                Node<K,V> x = (tab[i].isEmpty()) ? null : newNode(hash, key, v, null);
+                tab[i] = new XNode(hash, key, v, x);
+                if (binCount >= TREEIFY_THRESHOLD - 1)
+                    treeifyBin(tab, hash);
+            }
+            modCount = mc + 1;
+            ++size;
+        }
+        return v;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This method will, on a best-effort basis, throw a
+     * {@link ConcurrentModificationException} if it is detected that the
+     * remapping function modifies this map during computation.
+     *
+     * @throws ConcurrentModificationException if it is detected that the
+     * remapping function modified this map
+     */
+    @Override
+    public V merge(K key, V value,
+                   BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+        if (value == null || remappingFunction == null)
+            throw new NullPointerException();
+        int hash = hash(key);
+        XNode<K,V>[] tab; XNode<K,V> first; int n, i;
+        int binCount = 0;
+        TreeNode<K,V> t = null;
+        Node<K,V> old = null;
+        if (size > threshold || (tab = table) == null ||
+            (n = tab.length) == 0)
+            n = (tab = resize()).length;
+        if (!(first = tab[i = (n - 1) & hash]).isEmpty()) {
+            Node<K,V> e = first.next;K k;
+            if (first.hash == hash &&
+                    ((k = first.key) == key || (key != null && key.equals(k)))) {
+                V v = remappingFunction.apply(first.value, value);
+                tab[i] = new XNode(hash, k, v, e);
+                return v;
+            }
+            if (e instanceof TreeNode)
+                old = (t = (TreeNode<K,V>)e).getTreeNode(hash, key);
+            else {
+                do {
+                    if (e.hash == hash &&
+                        ((k = e.key) == key || (key != null && key.equals(k)))) {
+                        old = e;
+                        break;
+                    }
+                    ++binCount;
+                } while ((e = e.next) != null);
+            }
+        }
+        if (old != null) {
+            V v;
+            if (old.value != null) {
+                int mc = modCount;
+                v = remappingFunction.apply(old.value, value);
+                if (mc != modCount) {
+                    throw new ConcurrentModificationException();
+                }
+            } else {
+                v = value;
+            }
+            if (v != null) {
+                old.value = v;
+            }
+            else
+                removeNode(hash, key, null, false, true);
+            return v;
+        } else {
+            if (t != null)
+                t.putTreeVal(this, tab, hash, key, value);
+            else {
+                Node<K,V> x = (tab[i].isEmpty()) ? null
+                        : newNode(hash, tab[i].key, tab[i].value, null);
+                tab[i] = new XNode(hash, key, value, x);
+                if (binCount >= TREEIFY_THRESHOLD - 1)
+                    treeifyBin(tab, hash);
+            }
+            ++modCount;
+            ++size;
+            return value;
+        }
+    }
+
+    @Override
+    public void forEach(BiConsumer<? super K, ? super V> action) {
+        XNode<K,V>[] tab;
+        if (action == null)
+            throw new NullPointerException();
+        if (size > 0 && (tab = table) != null) {
+            int mc = modCount;
+            for (XNode<K,V> te : tab) {
+                if (!te.isEmpty()) {
+                    action.accept(te.key, te.value);
+                    for (Node<K, V> e = te.next; e != null; e = e.next)
+                        action.accept(e.key, e.value);
+                }
+            }
+            if (modCount != mc)
+                throw new ConcurrentModificationException();
+        }
+    }
+
+    @Override
+    public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
+        XNode<K,V>[] tab;
+        if (function == null)
+            throw new NullPointerException();
+        if (size > 0 && (tab = table) != null) {
+            int mc = modCount;
+            for (XNode<K,V> te : tab) {
+                if (!te.isEmpty()) {
+                    V v = function.apply(te.key, te.value);
+                    tab[te.hash & (tab.length -1)] = new XNode(te.hash, te.key, v, te.next);
+                    for (Node<K, V> e = te.next; e != null; e = e.next)
+                        e.value = function.apply(e.key, e.value);
+                }
+            }
+            if (modCount != mc)
+                throw new ConcurrentModificationException();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    // Cloning and serialization
+
+    /**
+     * Returns a shallow copy of this {@code HashMap} instance: the keys and
+     * values themselves are not cloned.
+     *
+     * @return a shallow copy of this map
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public Object clone() {
+        XHashMap<K,V> result;
+        try {
+            result = (XHashMap<K,V>)super.clone();
+        } catch (CloneNotSupportedException e) {
+            // this shouldn't happen, since we are Cloneable
+            throw new InternalError(e);
+        }
+        result.reinitialize();
+        result.putMapEntries(this, false);
+        return result;
+    }
+
+    // These methods are also used when serializing HashSets
+    final float loadFactor() { return loadFactor; }
+    final int capacity() {
+        return (table != null) ? table.length :
+            (threshold > 0) ? threshold :
+            DEFAULT_INITIAL_CAPACITY;
+    }
+
+    /**
+     * Saves this map to a stream (that is, serializes it).
+     *
+     * @param s the stream
+     * @throws IOException if an I/O error occurs
+     * @serialData The <i>capacity</i> of the HashMap (the length of the
+     *             bucket array) is emitted (int), followed by the
+     *             <i>size</i> (an int, the number of key-value
+     *             mappings), followed by the key (Object) and value (Object)
+     *             for each key-value mapping.  The key-value mappings are
+     *             emitted in no particular order.
+     */
+    private void writeObject(java.io.ObjectOutputStream s)
+        throws IOException {
+        int buckets = capacity();
+        // Write out the threshold, loadfactor, and any hidden stuff
+        s.defaultWriteObject();
+        s.writeInt(buckets);
+        s.writeInt(size);
+        internalWriteEntries(s);
+    }
+
+    /**
+     * Reconstitutes this map from a stream (that is, deserializes it).
+     * @param s the stream
+     * @throws ClassNotFoundException if the class of a serialized object
+     *         could not be found
+     * @throws IOException if an I/O error occurs
+     */
+    private void readObject(java.io.ObjectInputStream s)
+        throws IOException, ClassNotFoundException {
+        // Read in the threshold (ignored), loadfactor, and any hidden stuff
+        s.defaultReadObject();
+        reinitialize();
+        if (loadFactor <= 0 || Float.isNaN(loadFactor))
+            throw new InvalidObjectException("Illegal load factor: " +
+                                             loadFactor);
+        s.readInt();                // Read and ignore number of buckets
+        int mappings = s.readInt(); // Read number of mappings (size)
+        if (mappings < 0)
+            throw new InvalidObjectException("Illegal mappings count: " +
+                                             mappings);
+        else if (mappings > 0) { // (if zero, use defaults)
+            // Size the table using given load factor only if within
+            // range of 0.25...4.0
+            float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
+            float fc = (float)mappings / lf + 1.0f;
+            int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
+                       DEFAULT_INITIAL_CAPACITY :
+                       (fc >= MAXIMUM_CAPACITY) ?
+                       MAXIMUM_CAPACITY :
+                       tableSizeFor((int)fc));
+            float ft = (float)cap * lf;
+            threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
+                         (int)ft : Integer.MAX_VALUE);
+
+            // Check Map.Entry[].class since it's the nearest public type to
+            // what we're actually creating.
+            @SuppressWarnings({"rawtypes","unchecked"})
+            XNode<K,V>[] tab = (XNode<K,V>[])new XNode[cap];
+            table = tab;
+
+            // Read the keys and values, and put the mappings in the HashMap
+            for (int i = 0; i < mappings; i++) {
+                @SuppressWarnings("unchecked")
+                    K key = (K) s.readObject();
+                @SuppressWarnings("unchecked")
+                    V value = (V) s.readObject();
+                putVal(hash(key), key, value, false, false);
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    // iterators
+
+    static final Node<?,?> START_INDEX = new Node(0, null, null, null);
+
+    abstract class HashIterator {
+        Node<K,V> next;        // next entry to return
+        Node<K,V> current;     // current entry
+        int expectedModCount;  // for fast-fail
+        int index;             // current slot
+
+
+        HashIterator() {
+            expectedModCount = modCount;
+            XNode<K,V>[] t = table;
+            current = next = null;
+            index = 0;
+            if (t != null && size > 0) { // advance to first entry
+                XNode<K,V> n = emptyXNode();
+                for (; index < t.length && (n = t[index]).isEmpty(); index++) {
+                }
+                next = (Node<K,V>)START_INDEX;
+            }
+        }
+
+        public final boolean hasNext() {
+            return next != null;
+        }
+
+        final Entry<K,V> nextNode() {
+            XNode<K,V>[] t;
+            Node<K,V> e = next;
+            if (modCount != expectedModCount)
+                throw new ConcurrentModificationException();
+            if (e == null)
+                throw new NoSuchElementException();
+            if ((next = (current = e).next) == null && (t = table) != null) {
+                var ret = (e == START_INDEX) ? new XNodeWrapper(index++) : e;
+                for (; index < t.length && (t[index]).isEmpty(); index++) { }
+                next = (index < t.length) ? (Node<K, V>) START_INDEX : null;
+                return ret;
+            }
+            return e;
+        }
+
+        public final void remove() {
+            Node<K,V> p = current;
+            if (p == null)
+                throw new IllegalStateException();
+            if (modCount != expectedModCount)
+                throw new ConcurrentModificationException();
+            current = null;
+            removeNode(p.hash, p.key, null, false, false);
+            expectedModCount = modCount;
+        }
+    }
+
+    final class KeyIterator extends HashIterator
+        implements Iterator<K> {
+        public final K next() { return nextNode().getKey(); }
+    }
+
+    final class ValueIterator extends HashIterator
+        implements Iterator<V> {
+        public final V next() { return nextNode().getValue(); }
+    }
+
+    final class EntryIterator extends HashIterator
+        implements Iterator<Map.Entry<K,V>> {
+        public final Map.Entry<K,V> next() { return nextNode(); }
+    }
+
+    /*
+     * The following package-protected methods are designed to be
+     * overridden by LinkedHashMap, but not by any other subclass.
+     * Nearly all other internal methods are also package-protected
+     * but are declared final, so can be used by LinkedHashMap, view
+     * classes, and HashSet.
+     */
+
+    // Create a regular (non-tree) node
+    Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
+        return new Node<>(hash, key, value, next);
+    }
+
+    // For conversion from TreeNodes to plain nodes
+    Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
+        return new Node<>(p.hash, p.key, p.value, next);
+    }
+
+    // Create a tree bin node
+    TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
+        return new TreeNode<>(hash, key, value, next);
+    }
+
+    // For treeifyBin
+    TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
+        return new TreeNode<>(p.hash, p.key, p.value, next);
+    }
+
+    /**
+     * Reset to initial default state.  Called by clone and readObject.
+     */
+    void reinitialize() {
+        table = null;
+        entrySet = null;
+        keySet = null;
+        values = null;
+        modCount = 0;
+        threshold = 0;
+        size = 0;
+    }
+
+    // Called only from writeObject, to ensure compatible ordering.
+    void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
+        XNode<K,V>[] tab;
+        if (size > 0 && (tab = table) != null) {
+            for (XNode<K,V> te : tab) {
+                if (!te.isEmpty()) {
+                    s.writeObject(te.key);
+                    s.writeObject(te.value);
+
+                    for (Node<K, V> e = te.next; e != null; e = e.next) {
+                        s.writeObject(e.key);
+                        s.writeObject(e.value);
+                    }
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    // Tree bins
+
+    /**
+     * Entry for Tree bins. Extends LinkedHashMap.Entry (which in turn
+     * extends Node) so can be used as extension of either regular or
+     * linked node.
+     */
+    static final class TreeNode<K,V> extends Node<K,V> {
+        TreeNode<K,V> parent;  // red-black tree links
+        TreeNode<K,V> left;
+        TreeNode<K,V> right;
+        TreeNode<K,V> prev;    // needed to unlink next upon deletion
+        boolean red;
+        TreeNode(int hash, K key, V val, Node<K,V> next) {
+            super(hash, key, val, next);
+        }
+
+        /**
+         * Returns root of tree containing this node.
+         */
+        final TreeNode<K,V> root() {
+            for (TreeNode<K,V> r = this, p;;) {
+                if ((p = r.parent) == null)
+                    return r;
+                r = p;
+            }
+        }
+
+        /**
+         * Ensures that the given root is the first node of its bin.
+         */
+        static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
+            int n;
+            if (root != null && tab != null && (n = tab.length) > 0) {
+                int index = (n - 1) & root.hash;
+                TreeNode<K,V> first = (TreeNode<K,V>)tab[index];
+                if (root != first) {
+                    Node<K,V> rn;
+                    tab[index] = root;
+                    TreeNode<K,V> rp = root.prev;
+                    if ((rn = root.next) != null)
+                        ((TreeNode<K,V>)rn).prev = rp;
+                    if (rp != null)
+                        rp.next = rn;
+                    if (first != null)
+                        first.prev = root;
+                    root.next = first;
+                    root.prev = null;
+                }
+                assert checkInvariants(root);
+            }
+        }
+
+        /**
+         * Finds the node starting at root p with the given hash and key.
+         * The kc argument caches comparableClassFor(key) upon first use
+         * comparing keys.
+         */
+        final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
+            TreeNode<K,V> p = this;
+            do {
+                int ph, dir; K pk;
+                TreeNode<K,V> pl = p.left, pr = p.right, q;
+                if ((ph = p.hash) > h)
+                    p = pl;
+                else if (ph < h)
+                    p = pr;
+                else if ((pk = p.key) == k || (k != null && k.equals(pk)))
+                    return p;
+                else if (pl == null)
+                    p = pr;
+                else if (pr == null)
+                    p = pl;
+                else if ((kc != null ||
+                          (kc = comparableClassFor(k)) != null) &&
+                         (dir = compareComparables(kc, k, pk)) != 0)
+                    p = (dir < 0) ? pl : pr;
+                else if ((q = pr.find(h, k, kc)) != null)
+                    return q;
+                else
+                    p = pl;
+            } while (p != null);
+            return null;
+        }
+
+        /**
+         * Calls find for root node.
+         */
+        final TreeNode<K,V> getTreeNode(int h, Object k) {
+            return ((parent != null) ? root() : this).find(h, k, null);
+        }
+
+        /**
+         * Tie-breaking utility for ordering insertions when equal
+         * hashCodes and non-comparable. We don't require a total
+         * order, just a consistent insertion rule to maintain
+         * equivalence across rebalancings. Tie-breaking further than
+         * necessary simplifies testing a bit.
+         */
+        static int tieBreakOrder(Object a, Object b) {
+            int d;
+            if (a == null || b == null ||
+                (d = a.getClass().getName().
+                 compareTo(b.getClass().getName())) == 0)
+                d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
+                     -1 : 1);
+            return d;
+        }
+
+        /**
+         * Forms tree of the nodes linked from this node.
+         */
+        final void treeify(XNode<K,V>[] tab) {
+//            TreeNode<K,V> root = null;
+//            for (TreeNode<K,V> x = this, next; x != null; x = next) {
+//                next = (TreeNode<K,V>)x.next;
+//                x.left = x.right = null;
+//                if (root == null) {
+//                    x.parent = null;
+//                    x.red = false;
+//                    root = x;
+//                }
+//                else {
+//                    K k = x.key;
+//                    int h = x.hash;
+//                    Class<?> kc = null;
+//                    for (TreeNode<K,V> p = root;;) {
+//                        int dir, ph;
+//                        K pk = p.key;
+//                        if ((ph = p.hash) > h)
+//                            dir = -1;
+//                        else if (ph < h)
+//                            dir = 1;
+//                        else if ((kc == null &&
+//                                  (kc = comparableClassFor(k)) == null) ||
+//                                 (dir = compareComparables(kc, k, pk)) == 0)
+//                            dir = tieBreakOrder(k, pk);
+//
+//                        TreeNode<K,V> xp = p;
+//                        if ((p = (dir <= 0) ? p.left : p.right) == null) {
+//                            x.parent = xp;
+//                            if (dir <= 0)
+//                                xp.left = x;
+//                            else
+//                                xp.right = x;
+//                            root = balanceInsertion(root, x);
+//                            break;
+//                        }
+//                    }
+//                }
+//            }
+//            moveRootToFront(tab, root);
+        }
+
+        /**
+         * Returns a list of non-TreeNodes replacing those linked from
+         * this node.
+         */
+        final Node<K,V> untreeify(XHashMap<K,V> map) {
+            Node<K,V> hd = null, tl = null;
+            for (Node<K,V> q = this; q != null; q = q.next) {
+                Node<K,V> p = map.replacementNode(q, null);
+                if (tl == null)
+                    hd = p;
+                else
+                    tl.next = p;
+                tl = p;
+            }
+            return hd;
+        }
+
+        /**
+         * Tree version of putVal.
+         */
+        final TreeNode<K,V> putTreeVal(XHashMap<K,V> map, XNode<K,V>[] tab,
+                                       int h, K k, V v) {
+//            Class<?> kc = null;
+//            boolean searched = false;
+//            TreeNode<K,V> root = (parent != null) ? root() : this;
+//            for (TreeNode<K,V> p = root;;) {
+//                int dir, ph; K pk;
+//                if ((ph = p.hash) > h)
+//                    dir = -1;
+//                else if (ph < h)
+//                    dir = 1;
+//                else if ((pk = p.key) == k || (k != null && k.equals(pk)))
+//                    return p;
+//                else if ((kc == null &&
+//                          (kc = comparableClassFor(k)) == null) ||
+//                         (dir = compareComparables(kc, k, pk)) == 0) {
+//                    if (!searched) {
+//                        TreeNode<K,V> q, ch;
+//                        searched = true;
+//                        if (((ch = p.left) != null &&
+//                             (q = ch.find(h, k, kc)) != null) ||
+//                            ((ch = p.right) != null &&
+//                             (q = ch.find(h, k, kc)) != null))
+//                            return q;
+//                    }
+//                    dir = tieBreakOrder(k, pk);
+//                }
+//
+//                TreeNode<K,V> xp = p;
+//                if ((p = (dir <= 0) ? p.left : p.right) == null) {
+//                    Node<K,V> xpn = xp.next;
+//                    TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
+//                    if (dir <= 0)
+//                        xp.left = x;
+//                    else
+//                        xp.right = x;
+//                    xp.next = x;
+//                    x.parent = x.prev = xp;
+//                    if (xpn != null)
+//                        ((TreeNode<K,V>)xpn).prev = x;
+//                    moveRootToFront(tab, balanceInsertion(root, x));
+//                    return null;
+//                }
+//            }
+            return null;
+        }
+
+        /**
+         * Removes the given node, that must be present before this call.
+         * This is messier than typical red-black deletion code because we
+         * cannot swap the contents of an interior node with a leaf
+         * successor that is pinned by "next" pointers that are accessible
+         * independently during traversal. So instead we swap the tree
+         * linkages. If the current tree appears to have too few nodes,
+         * the bin is converted back to a plain bin. (The test triggers
+         * somewhere between 2 and 6 nodes, depending on tree structure).
+         */
+        final void removeTreeNode(XHashMap<K,V> map, XNode<K,V>[] tab,
+                                  boolean movable) {
+//            int n;
+//            if (tab == null || (n = tab.length) == 0)
+//                return;
+//            int index = (n - 1) & hash;
+//            TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl;
+//            TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;
+//            if (pred == null)
+//                tab[index] = first = succ;
+//            else
+//                pred.next = succ;
+//            if (succ != null)
+//                succ.prev = pred;
+//            if (first == null)
+//                return;
+//            if (root.parent != null)
+//                root = root.root();
+//            if (root == null
+//                || (movable
+//                    && (root.right == null
+//                        || (rl = root.left) == null
+//                        || rl.left == null))) {
+//                tab[index] = first.untreeify(map);  // too small
+//                return;
+//            }
+//            TreeNode<K,V> p = this, pl = left, pr = right, replacement;
+//            if (pl != null && pr != null) {
+//                TreeNode<K,V> s = pr, sl;
+//                while ((sl = s.left) != null) // find successor
+//                    s = sl;
+//                boolean c = s.red; s.red = p.red; p.red = c; // swap colors
+//                TreeNode<K,V> sr = s.right;
+//                TreeNode<K,V> pp = p.parent;
+//                if (s == pr) { // p was s's direct parent
+//                    p.parent = s;
+//                    s.right = p;
+//                }
+//                else {
+//                    TreeNode<K,V> sp = s.parent;
+//                    if ((p.parent = sp) != null) {
+//                        if (s == sp.left)
+//                            sp.left = p;
+//                        else
+//                            sp.right = p;
+//                    }
+//                    if ((s.right = pr) != null)
+//                        pr.parent = s;
+//                }
+//                p.left = null;
+//                if ((p.right = sr) != null)
+//                    sr.parent = p;
+//                if ((s.left = pl) != null)
+//                    pl.parent = s;
+//                if ((s.parent = pp) == null)
+//                    root = s;
+//                else if (p == pp.left)
+//                    pp.left = s;
+//                else
+//                    pp.right = s;
+//                if (sr != null)
+//                    replacement = sr;
+//                else
+//                    replacement = p;
+//            }
+//            else if (pl != null)
+//                replacement = pl;
+//            else if (pr != null)
+//                replacement = pr;
+//            else
+//                replacement = p;
+//            if (replacement != p) {
+//                TreeNode<K,V> pp = replacement.parent = p.parent;
+//                if (pp == null)
+//                    (root = replacement).red = false;
+//                else if (p == pp.left)
+//                    pp.left = replacement;
+//                else
+//                    pp.right = replacement;
+//                p.left = p.right = p.parent = null;
+//            }
+//
+//            TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);
+//
+//            if (replacement == p) {  // detach
+//                TreeNode<K,V> pp = p.parent;
+//                p.parent = null;
+//                if (pp != null) {
+//                    if (p == pp.left)
+//                        pp.left = null;
+//                    else if (p == pp.right)
+//                        pp.right = null;
+//                }
+//            }
+//            if (movable)
+//                moveRootToFront(tab, r);
+        }
+
+        /**
+         * Splits nodes in a tree bin into lower and upper tree bins,
+         * or untreeifies if now too small. Called only from resize;
+         * see above discussion about split bits and indices.
+         *
+         * @param map the map
+         * @param tab the table for recording bin heads
+         * @param index the index of the table being split
+         * @param bit the bit of hash to split on
+         */
+        final void split(XHashMap<K,V> map, XNode<K,V>[] tab, int index, int bit) {
+//            TreeNode<K,V> b = this;
+//            // Relink into lo and hi lists, preserving order
+//            TreeNode<K,V> loHead = null, loTail = null;
+//            TreeNode<K,V> hiHead = null, hiTail = null;
+//            int lc = 0, hc = 0;
+//            for (TreeNode<K,V> e = b, next; e != null; e = next) {
+//                next = (TreeNode<K,V>)e.next;
+//                e.next = null;
+//                if ((e.hash & bit) == 0) {
+//                    if ((e.prev = loTail) == null)
+//                        loHead = e;
+//                    else
+//                        loTail.next = e;
+//                    loTail = e;
+//                    ++lc;
+//                }
+//                else {
+//                    if ((e.prev = hiTail) == null)
+//                        hiHead = e;
+//                    else
+//                        hiTail.next = e;
+//                    hiTail = e;
+//                    ++hc;
+//                }
+//            }
+//
+//            if (loHead != null) {
+//                if (lc <= UNTREEIFY_THRESHOLD)
+//                    tab[index] = loHead.untreeify(map);
+//                else {
+//                    tab[index] = loHead;
+//                    if (hiHead != null) // (else is already treeified)
+//                        loHead.treeify(tab);
+//                }
+//            }
+//            if (hiHead != null) {
+//                if (hc <= UNTREEIFY_THRESHOLD)
+//                    tab[index + bit] = hiHead.untreeify(map);
+//                else {
+//                    tab[index + bit] = hiHead;
+//                    if (loHead != null)
+//                        hiHead.treeify(tab);
+//                }
+//            }
+        }
+
+        /**
+         * Recursive invariant check
+         */
+        static <K,V> boolean checkInvariants(TreeNode<K,V> t) {
+            TreeNode<K,V> tp = t.parent, tl = t.left, tr = t.right,
+                tb = t.prev, tn = (TreeNode<K,V>)t.next;
+            if (tb != null && tb.next != t)
+                return false;
+            if (tn != null && tn.prev != t)
+                return false;
+            if (tp != null && t != tp.left && t != tp.right)
+                return false;
+            if (tl != null && (tl.parent != t || tl.hash > t.hash))
+                return false;
+            if (tr != null && (tr.parent != t || tr.hash < t.hash))
+                return false;
+            if (t.red && tl != null && tl.red && tr != null && tr.red)
+                return false;
+            if (tl != null && !checkInvariants(tl))
+                return false;
+            if (tr != null && !checkInvariants(tr))
+                return false;
+            return true;
+        }
+    }
+
+
+    public void dumpStats(PrintStream out) {
+        out.printf("%s instance: size: %d%n", this.getClass().getName(), this.size());
+        long size = heapSize();
+        long bytesPer = size / this.size();
+        out.printf("    heap size: %d(bytes), avg bytes per entry: %d, table len: %d%n",
+                size, bytesPer, table.length);
+        long[] types = entryTypes();
+        out.printf("    values: %d, empty: %d%n", types[0], types[1]);
+        int[] rehashes = entryRehashes();
+        out.printf("    hash collision histogram: max: %d, %s%n",
+                rehashes.length - 1, Arrays.toString(rehashes));
+    }
+
+    private long[] entryTypes() {
+        long[] counts = new long[3];
+        for (XNode<K,V> te : table) {
+            counts[te.isEmpty() ? 1 : 0]++;
+        }
+        return counts;
+    }
+
+    // Returns a histogram array of the number of rehashs needed to find each key.
+    private int[] entryRehashes() {
+        int[] counts = new int[table.length + 1];
+        XNode<K,V>[] tab = table;
+        for (XNode<K,V> te : tab) {
+            if (!te.isEmpty()) {
+                int count = 0;
+                for (Node<K, V> e = te.next; e != null; e = e.next)
+                    count++;
+                counts[count]++;
+            }
+        }
+
+        int i;
+        for (i = counts.length - 1; i >= 0 && counts[i] == 0; i--) {
+        }
+        counts = Arrays.copyOf(counts, i + 1);
+        return counts;
+    }
+
+    private long heapSize() {
+        long acc = objectSizeMaybe(this);
+        acc += objectSizeMaybe(table);
+
+        XNode<K,V>[] tab = table;
+        for (XNode<K,V> te : tab) {
+            if (!te.isEmpty()) {
+                for (Node<K, V> e = te.next; e != null; e = e.next)
+                    acc += objectSizeMaybe(e);
+            }
+        }
+        return acc;
+    }
+
+    private long objectSizeMaybe(Object o) {
+        try {
+            return (mObjectSize != null)
+                    ? (long)mObjectSize.invoke(null, o)
+                    : 0L;
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            return 0L;
+        }
+    }
+
+    private static boolean hasObjectSize = false;
+    private static Method mObjectSize = getObjectSizeMethod();
+
+    private static Method getObjectSizeMethod() {
+        try {
+            Method m = Objects.class.getDeclaredMethod("getObjectSize", Object.class);
+            hasObjectSize = true;
+            return m;
+        } catch (NoSuchMethodException nsme) {
+            return null;
+        }
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/traversal/Identity32.java b/test/micro/org/openjdk/bench/valhalla/traversal/Identity32.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/traversal/Identity32.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.traversal;
+
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.bench.valhalla.types.Int32;
+import org.openjdk.bench.valhalla.types.R32int;
+import org.openjdk.bench.valhalla.util.Utils;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Setup;
+
+public class Identity32 extends SizeBase {
+
+    public static abstract class IntState extends SizeState {
+        public Int32[] arr;
+        void fill() {
+            int[] a = Utils.makeRandomRing(arr.length);
+            for (int i = 0; i < a.length; i++) {
+                arr[i] = new R32int(a[i]);
+            }
+        }
+    }
+
+    public static abstract class RefState extends SizeState {
+        public R32int[] arr;
+        void fill() {
+            int[] a = Utils.makeRandomRing(arr.length);
+            for (int i = 0; i < a.length; i++) {
+                arr[i] = new R32int(a[i]);
+            }
+        }
+    }
+
+    public static class Int_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Int32[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new R32int[size];
+            fill();
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Int_as_Int_walk(Int_as_Int s) {
+        int steps = 0;
+        Int32[] values = s.arr;
+        for (int i = values[0].intValue(); i != 0; i = values[i].intValue()) steps++;
+        return steps;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Ref_as_Ref_walk(Ref_as_Ref s) {
+        int steps = 0;
+        R32int[] values = s.arr;
+        for (int i = values[0].intValue(); i != 0; i = values[i].intValue()) steps++;
+        return steps;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/traversal/Identity64.java b/test/micro/org/openjdk/bench/valhalla/traversal/Identity64.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/traversal/Identity64.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.traversal;
+
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.bench.valhalla.types.A64long;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.R64long;
+import org.openjdk.bench.valhalla.util.Utils;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Setup;
+
+public class Identity64 extends SizeBase {
+
+    public static abstract class IntState extends SizeState {
+        public Int64[] arr;
+        void fill() {
+            int[] a = Utils.makeRandomRing(arr.length);
+            for (int i = 0; i < a.length; i++) {
+                arr[i] = new R64long(a[i]);
+            }
+        }
+    }
+
+    public static abstract class AbsState extends SizeState {
+        public A64long[] arr;
+        void fill() {
+            int[] a = Utils.makeRandomRing(arr.length);
+            for (int i = 0; i < a.length; i++) {
+                arr[i] = new R64long(a[i]);
+            }
+        }
+    }
+
+    public static abstract class RefState extends SizeState {
+        public R64long[] arr;
+        void fill() {
+            int[] a = Utils.makeRandomRing(arr.length);
+            for (int i = 0; i < a.length; i++) {
+                arr[i] = new R64long(a[i]);
+            }
+        }
+    }
+
+
+    public static class Int_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Int64[size];
+            fill();
+        }
+    }
+
+    public static class Abs_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new A64long[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new R64long[size];
+            fill();
+        }
+    }
+
+    public static class Abs_as_Abs extends AbsState {
+        @Setup
+        public void setup() {
+            arr = new A64long[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Abs extends AbsState {
+        @Setup
+        public void setup() {
+            arr = new R64long[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new R64long[size];
+            fill();
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Int_as_Int_walk(Int_as_Int s) {
+        int steps = 0;
+        Int64[] values = s.arr;
+        for (int i = values[0].intValue(); i != 0; i = values[i].intValue()) steps++;
+        return steps;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Abs_as_Int_walk(Abs_as_Int s) {
+        int steps = 0;
+        Int64[] values = s.arr;
+        for (int i = values[0].intValue(); i != 0; i = values[i].intValue()) steps++;
+        return steps;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Ref_as_Int_walk(Ref_as_Int s) {
+        int steps = 0;
+        Int64[] values = s.arr;
+        for (int i = values[0].intValue(); i != 0; i = values[i].intValue()) steps++;
+        return steps;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Abs_as_Abs_walk(Abs_as_Abs s) {
+        int steps = 0;
+        A64long[] values = s.arr;
+        for (int i = values[0].intValue(); i != 0; i = values[i].intValue()) steps++;
+        return steps;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Ref_as_Abs_walk(Ref_as_Abs s) {
+        int steps = 0;
+        A64long[] values = s.arr;
+        for (int i = values[0].intValue(); i != 0; i = values[i].intValue()) steps++;
+        return steps;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Ref_as_Ref_walk(Ref_as_Ref s) {
+        int steps = 0;
+        A64long[] values = s.arr;
+        for (int i = values[0].intValue(); i != 0; i = values[i].intValue()) steps++;
+        return steps;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/traversal/Inline32.java b/test/micro/org/openjdk/bench/valhalla/traversal/Inline32.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/traversal/Inline32.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.traversal;
+
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.bench.valhalla.types.Int32;
+import org.openjdk.bench.valhalla.types.Q32int;
+import org.openjdk.bench.valhalla.util.Utils;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Setup;
+
+public class Inline32 extends SizeBase {
+
+    public static abstract class IntState extends SizeState {
+        public Int32[] arr;
+        void fill() {
+            int[] a = Utils.makeRandomRing(arr.length);
+            for (int i = 0; i < a.length; i++) {
+                arr[i] = new Q32int(a[i]);
+            }
+        }
+    }
+
+    public static abstract class RefState extends SizeState {
+        public Q32int[] arr;
+        void fill() {
+            int[] a = Utils.makeRandomRing(arr.length);
+            for (int i = 0; i < a.length; i++) {
+                arr[i] = new Q32int(a[i]);
+            }
+        }
+    }
+
+    public static abstract class ValState extends SizeState {
+        public Q32int[] arr;
+        void fill() {
+            int[] a = Utils.makeRandomRing(arr.length);
+            for (int i = 0; i < a.length; i++) {
+                arr[i] = new Q32int(a[i]);
+            }
+        }
+    }
+
+
+    public static class Int_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Int32[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new Q32int[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Val extends ValState {
+        @Setup
+        public void setup() {
+            arr = new Q32int[size];
+            fill();
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Int_as_Int_walk(Int_as_Int s) {
+        int steps = 0;
+        Int32[] values = s.arr;
+        for (int i = values[0].intValue(); i != 0; i = values[i].intValue()) steps++;
+        return steps;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Ref_as_Ref_walk(Ref_as_Ref s) {
+        int steps = 0;
+        Q32int[] values = s.arr;
+        for (int i = values[0].intValue(); i != 0; i = values[i].intValue()) steps++;
+        return steps;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Val_walk(Val_as_Val s) {
+        int steps = 0;
+        Q32int[] values = s.arr;
+        for (int i = values[0].intValue(); i != 0; i = values[i].intValue()) steps++;
+        return steps;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/traversal/Inline64.java b/test/micro/org/openjdk/bench/valhalla/traversal/Inline64.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/traversal/Inline64.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.traversal;
+
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.bench.valhalla.types.Int64;
+import org.openjdk.bench.valhalla.types.Q64long;
+import org.openjdk.bench.valhalla.util.Utils;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Setup;
+
+public class Inline64 extends SizeBase {
+
+    public static abstract class IntState extends SizeState {
+        public Int64[] arr;
+        void fill() {
+            int[] a = Utils.makeRandomRing(arr.length);
+            for (int i = 0; i < a.length; i++) {
+                arr[i] = new Q64long(a[i]);
+            }
+        }
+    }
+
+    public static abstract class RefState extends SizeState {
+        public Q64long[] arr;
+        void fill() {
+            int[] a = Utils.makeRandomRing(arr.length);
+            for (int i = 0; i < a.length; i++) {
+                arr[i] = new Q64long(a[i]);
+            }
+        }
+    }
+
+    public static abstract class ValState extends SizeState {
+        public Q64long[] arr;
+        void fill() {
+            int[] a = Utils.makeRandomRing(arr.length);
+            for (int i = 0; i < a.length; i++) {
+                arr[i] = new Q64long(a[i]);
+            }
+        }
+    }
+
+
+    public static class Int_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Int64[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Q64long[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Int extends IntState {
+        @Setup
+        public void setup() {
+            arr = new Q64long[size];
+            fill();
+        }
+    }
+
+    public static class Ref_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new Q64long[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Ref extends RefState {
+        @Setup
+        public void setup() {
+            arr = new Q64long[size];
+            fill();
+        }
+    }
+
+    public static class Val_as_Val extends ValState {
+        @Setup
+        public void setup() {
+            arr = new Q64long[size];
+            fill();
+        }
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Int_as_Int_walk(Int_as_Int s) {
+        int steps = 0;
+        Int64[] values = s.arr;
+        for (int i = values[0].intValue(); i != 0; i = values[i].intValue()) steps++;
+        return steps;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Ref_as_Int_walk(Ref_as_Int s) {
+        int steps = 0;
+        Int64[] values = s.arr;
+        for (int i = values[0].intValue(); i != 0; i = values[i].intValue()) steps++;
+        return steps;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Int_walk(Val_as_Int s) {
+        int steps = 0;
+        Int64[] values = s.arr;
+        for (int i = values[0].intValue(); i != 0; i = values[i].intValue()) steps++;
+        return steps;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Ref_as_Ref_walk(Ref_as_Ref s) {
+        int steps = 0;
+        Q64long[] values = s.arr;
+        for (int i = values[0].intValue(); i != 0; i = values[i].intValue()) steps++;
+        return steps;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Ref_walk(Val_as_Ref s) {
+        int steps = 0;
+        Q64long[] values = s.arr;
+        for (int i = values[0].intValue(); i != 0; i = values[i].intValue()) steps++;
+        return steps;
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int Val_as_Val_walk(Val_as_Val s) {
+        int steps = 0;
+        Q64long[] values = s.arr;
+        for (int i = values[0].intValue(); i != 0; i = values[i].intValue()) steps++;
+        return steps;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/traversal/Primitive32.java b/test/micro/org/openjdk/bench/valhalla/traversal/Primitive32.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/traversal/Primitive32.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2016, 2018, 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 org.openjdk.bench.valhalla.traversal;
+
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.bench.valhalla.util.Utils;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Setup;
+
+public class Primitive32 extends SizeBase {
+
+    public static class IntState extends SizeState {
+        public int[] arr;
+
+        @Setup
+        public void setup() {
+            int[] a = Utils.makeRandomRing(size);
+            arr = new int[a.length];
+            System.arraycopy(a, 0, arr, 0, a.length);
+        }
+
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int walk(IntState s) {
+        int steps = 0;
+        int[] values = s.arr;
+        for (int i = values[0]; i != 0; i = values[i]) steps++;
+        return steps;
+    }
+
+}
+
diff a/test/micro/org/openjdk/bench/valhalla/traversal/Primitive64.java b/test/micro/org/openjdk/bench/valhalla/traversal/Primitive64.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/traversal/Primitive64.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016, 2018, 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 org.openjdk.bench.valhalla.traversal;
+
+import org.openjdk.bench.valhalla.util.SizeBase;
+import org.openjdk.bench.valhalla.util.Utils;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Setup;
+
+public class Primitive64 extends SizeBase {
+
+    public static class LongState extends SizeState {
+        public long[] arr;
+
+        @Setup
+        public void setup() {
+            int[] a = Utils.makeRandomRing(size);
+            arr = new long[a.length];
+            for (int i = 0; i < a.length; i++) {
+                arr[i] = a[i];
+            }
+        }
+
+    }
+
+    @Benchmark
+    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+    public int walk(LongState s) {
+        int steps = 0;
+        long[] values = s.arr;
+        for (int i = (int) values[0]; i != 0; i = (int) values[i]) steps++;
+        return steps;
+    }
+
+}
+
diff a/test/micro/org/openjdk/bench/valhalla/types/A64long.java b/test/micro/org/openjdk/bench/valhalla/types/A64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/A64long.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public abstract class A64long implements Int64, ByLong {
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/ByByte.java b/test/micro/org/openjdk/bench/valhalla/types/ByByte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/ByByte.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public interface ByByte {
+
+    public byte byteSum();
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/ByInt.java b/test/micro/org/openjdk/bench/valhalla/types/ByInt.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/ByInt.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public interface ByInt {
+
+    public int intSum();
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/ByLong.java b/test/micro/org/openjdk/bench/valhalla/types/ByLong.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/ByLong.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public interface ByLong {
+
+    public long longSum();
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/Complex.java b/test/micro/org/openjdk/bench/valhalla/types/Complex.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/Complex.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public interface Complex {
+
+    double re();
+
+    double im();
+
+    Complex add(Complex that);
+
+    Complex mul(Complex that);
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/Int128.java b/test/micro/org/openjdk/bench/valhalla/types/Int128.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/Int128.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public interface Int128 {
+
+    public int intValue();
+
+    public long longValue();
+
+    public long hiValue();
+
+    public long loValue();
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/Int32.java b/test/micro/org/openjdk/bench/valhalla/types/Int32.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/Int32.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public interface Int32 extends Comparable<Int32>  {
+
+    public long longValue();
+
+    public int intValue();
+
+    public Int32 neg();
+
+    public Int32 add(Int32 o);
+
+    public Int32 sub(Int32 o);
+
+    public Int32 mult(Int32 o);
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/Int64.java b/test/micro/org/openjdk/bench/valhalla/types/Int64.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/Int64.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public interface Int64 {
+
+    public long longValue();
+
+    public int intValue();
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/Opt.java b/test/micro/org/openjdk/bench/valhalla/types/Opt.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/Opt.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public interface Opt<T> {
+
+    public T get();
+    public boolean isPresent();
+    public boolean isEmpty();
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/Q128byte.java b/test/micro/org/openjdk/bench/valhalla/types/Q128byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/Q128byte.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public value class Q128byte implements Int128, ByByte {
+
+    public final Q64byte v0;
+    public final Q64byte v1;
+
+    public Q128byte(long v) {
+        this(0, v);
+    }
+
+    public Q128byte(long hi, long lo) {
+        this.v0 = new Q64byte(hi);
+        this.v1 = new Q64byte(lo);
+    }
+
+    @Override
+    public int intValue() {
+        return v1.intValue();
+    }
+
+    @Override
+    public long longValue() {
+        return loValue();
+    }
+
+    @Override
+    public long hiValue() {
+        return v0.longValue();
+    }
+
+    @Override
+    public long loValue() {
+        return v1.longValue();
+    }
+
+    @Override
+    public byte byteSum() {
+        return (byte) (v0.byteSum() + v1.byteSum());
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/Q128int.java b/test/micro/org/openjdk/bench/valhalla/types/Q128int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/Q128int.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public value class Q128int implements Int128, ByInt {
+
+    public final Q64int v0;
+    public final Q64int v1;
+
+    public Q128int() {
+        this(0, 0);
+    }
+
+    public Q128int(long v) {
+        this(0, v);
+    }
+
+    public Q128int(long hi, long lo) {
+        this.v0 = new Q64int(hi);
+        this.v1 = new Q64int(lo);
+    }
+
+    @Override
+    public int intValue() {
+        return v1.intValue();
+    }
+
+    @Override
+    public long longValue() {
+        return loValue();
+    }
+
+    @Override
+    public long hiValue() {
+        return v0.longValue();
+    }
+
+    @Override
+    public long loValue() {
+        return v1.longValue();
+    }
+
+    @Override
+    public int intSum() {
+        return v0.intSum() + v1.intSum();
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/Q128long.java b/test/micro/org/openjdk/bench/valhalla/types/Q128long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/Q128long.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public value class Q128long implements Int128, ByLong {
+
+    public final long v0;
+    public final long v1;
+
+    public Q128long(long v) {
+        this(0, v);
+    }
+
+    public Q128long(long hi, long lo) {
+        this.v0 = hi;
+        this.v1 = lo;
+    }
+
+    @Override
+    public int intValue() {
+        return (int)longValue();
+    }
+
+    @Override
+    public long longValue() {
+        return loValue();
+    }
+
+    @Override
+    public long hiValue() {
+        return v0;
+    }
+
+    @Override
+    public long loValue() {
+        return v1;
+    }
+
+    @Override
+    public long longSum() {
+        return v0 + v1;
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/Q32byte.java b/test/micro/org/openjdk/bench/valhalla/types/Q32byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/Q32byte.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public value class Q32byte implements Int32, ByByte {
+
+    public final byte v0;
+    public final byte v1;
+    public final byte v2;
+    public final byte v3;
+
+    public Q32byte() {
+        this(0);
+    }
+
+    public Q32byte(int v) {
+        this((byte) (v >>> 24), (byte) (v >>> 16), (byte) (v >>> 8), (byte) (v));
+    }
+
+    public Q32byte(byte v0, byte v1, byte v2, byte v3) {
+        this.v0 = v0;
+        this.v1 = v1;
+        this.v2 = v2;
+        this.v3 = v3;
+    }
+
+    private static final int MASK = 0xFF;
+
+    @Override
+    public long longValue() {
+        return (long) intValue();
+    }
+
+    @Override
+    public int intValue() {
+        return (((v0 & MASK) << 24) | ((v1 & MASK) << 16) | ((v2 & MASK) << 8) | (v3 & MASK));
+    }
+
+    @Override
+    public Int32 neg() {
+        return new Q32byte(-intValue());
+    }
+
+    @Override
+    public Int32 add(Int32 o) {
+        return new Q32byte(intValue() + o.intValue());
+    }
+
+    @Override
+    public Int32 sub(Int32 o) {
+        return new Q32byte(intValue() - o.intValue());
+    }
+
+    @Override
+    public Int32 mult(Int32 o) {
+        return new Q32byte(intValue() * o.intValue());
+    }
+
+    @Override
+    public int compareTo(Int32 o) {
+        return Integer.compare(intValue(), o.intValue());
+    }
+
+    @Override
+    public byte byteSum() {
+        return (byte) (v0 + v1 + v2 + v3);
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/Q32int.java b/test/micro/org/openjdk/bench/valhalla/types/Q32int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/Q32int.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public value class Q32int implements Int32, ByInt {
+
+    public final int v0;
+
+    public Q32int() {
+        v0 = 0;
+    }
+
+    public Q32int(int val) {
+        this.v0 = val;
+    }
+
+    @Override
+    public long longValue() {
+        return (long)v0;
+    }
+
+    @Override
+    public int intValue() {
+        return v0;
+    }
+
+    @Override
+    public Int32 neg() {
+        return new Q32int(-v0);
+    }
+
+    @Override
+    public Int32 add(Int32 o) {
+        return new Q32int(v0 + o.intValue());
+    }
+
+    public Int32 add(Q32int o) {
+        return new Q32int(v0 + o.v0);
+    }
+
+    @Override
+    public Int32 sub(Int32 o) {
+        return new Q32int(v0 - o.intValue());
+    }
+
+    public Int32 sub(Q32int o) {
+        return new Q32int(v0 - o.v0);
+    }
+
+    @Override
+    public Int32 mult(Int32 o) {
+        return new Q32int(v0 * o.intValue());
+    }
+
+    public Int32 mult(Q32int o) {
+        return new Q32int(v0 * o.v0);
+    }
+
+    @Override
+    public int compareTo(Int32 o) {
+        return Integer.compare(v0, o.intValue());
+    }
+
+    public int compareTo(Q32int o) {
+        return Integer.compare(v0, o.v0);
+    }
+
+    @Override
+    public int intSum() {
+        return v0;
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/Q64byte.java b/test/micro/org/openjdk/bench/valhalla/types/Q64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/Q64byte.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public value class Q64byte implements Int64, ByByte {
+
+    public final Q32byte v0;
+    public final Q32byte v1;
+
+    public Q64byte() {
+        this.v0 = new Q32byte();
+        this.v1 = new Q32byte();
+    }
+
+    public Q64byte(long v) {
+        this((int) (v >>> 32), (int) v);
+    }
+
+    public Q64byte(int hi, int lo) {
+        this.v0 = new Q32byte(hi);
+        this.v1 = new Q32byte(lo);
+    }
+
+    private static final long MASK = 0xFFFFFFFFL;
+
+    @Override
+    public long longValue() {
+        return (v0.intValue() & MASK) << 32 | (v1.intValue() & MASK);
+    }
+
+    @Override
+    public int intValue() {
+        return v1.intValue();
+    }
+
+    @Override
+    public byte byteSum() {
+        return (byte) (v0.byteSum() + v1.byteSum());
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/Q64int.java b/test/micro/org/openjdk/bench/valhalla/types/Q64int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/Q64int.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public value class Q64int implements Int64, ByInt {
+
+    public final Q32int v0;
+    public final Q32int v1;
+
+    public Q64int() {
+        this(0);
+    }
+
+    public Q64int(long v) {
+        this((int) (v >>> 32), (int) v);
+    }
+
+    public Q64int(int hi, int lo) {
+        this.v0 = new Q32int(hi);
+        this.v1 = new Q32int(lo);
+    }
+
+    private static final long MASK = 0xFFFFFFFFL;
+
+    @Override
+    public long longValue() {
+        return (v0.intValue() & MASK) << 32 | (v1.intValue() & MASK);
+    }
+
+    @Override
+    public int intValue() {
+        return v1.intValue();
+    }
+
+    @Override
+    public int intSum() {
+        return v0.intSum() + v1.intSum();
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/Q64long.java b/test/micro/org/openjdk/bench/valhalla/types/Q64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/Q64long.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public value class Q64long implements Int64, ByLong {
+
+    public final long v0;
+
+    public Q64long() {
+        this(0);
+    }
+
+    public Q64long(long v0) {
+        this.v0 = v0;
+    }
+
+    @Override
+    public long longValue() {
+        return v0;
+    }
+
+    @Override
+    public int intValue() {
+        return (int)v0;
+    }
+
+    @Override
+    public long longSum() {
+        return v0;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/QComplex.java b/test/micro/org/openjdk/bench/valhalla/types/QComplex.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/QComplex.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public value class QComplex implements Complex {
+
+    public final double re;
+    public final double im;
+
+    public QComplex(double re, double im) {
+        this.re =  re;
+        this.im =  im;
+    }
+
+    @Override
+    public double re() { return re; }
+
+    @Override
+    public double im() { return im; }
+
+    @Override
+    public QComplex add(Complex that) {
+        return new QComplex(this.re + that.re(), this.im + that.im());
+    }
+
+    public QComplex add(QComplex that) {
+        return new QComplex(this.re + that.re, this.im + that.im);
+    }
+
+    @Override
+    public QComplex mul(Complex that) {
+        return new QComplex(this.re * that.re() - this.im * that.im(),
+                           this.re * that.im() + this.im * that.re());
+    }
+
+    public QComplex mul(QComplex that) {
+        return new QComplex(this.re * that.re - this.im * that.im,
+                           this.re * that.im + this.im * that.re);
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/QOpt.java b/test/micro/org/openjdk/bench/valhalla/types/QOpt.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/QOpt.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+import java.util.NoSuchElementException;
+
+public value class QOpt<T> implements Opt<T> {
+
+    public final T value;
+
+    private QOpt(T value) {
+        this.value = value;
+    }
+
+    public static <T> QOpt<T> of() {
+        return new QOpt<>(null);
+    }
+
+    public static <T> QOpt<T> of(T value) {
+        return new QOpt<>(value);
+    }
+
+    public T get() {
+        if (value == null) {
+            throw new NoSuchElementException("No value present");
+        }
+        return value;
+    }
+
+    public boolean isPresent() {
+        return value != null;
+    }
+
+    public boolean isEmpty() {
+        return value == null;
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/R128int.java b/test/micro/org/openjdk/bench/valhalla/types/R128int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/R128int.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public class R128int implements Int128, ByInt {
+
+    public final int v0;
+    public final int v1;
+    public final int v2;
+    public final int v3;
+
+    public R128int(long v) {
+        this(0, 0, (int) (v >>> 32), (int) v);
+    }
+
+    public R128int(int v0, int v1, int v2, int v3) {
+        this.v0 = v0;
+        this.v1 = v1;
+        this.v2 = v2;
+        this.v3 = v3;
+    }
+
+    private static final long MASK = 0xFFFFFFFFL;
+
+    @Override
+    public int intValue() {
+        return v3;
+    }
+
+    @Override
+    public long longValue() {
+        return loValue();
+    }
+
+    @Override
+    public long hiValue() {
+        return (v0 & MASK) << 32 | (v1 & MASK);
+    }
+
+    @Override
+    public long loValue() {
+        return (v2 & MASK) << 32 | (v3 & MASK);
+    }
+
+    @Override
+    public int intSum() {
+        return v0 + v1 + v2 + v3;
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/R128long.java b/test/micro/org/openjdk/bench/valhalla/types/R128long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/R128long.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public class R128long implements Int128, ByLong {
+
+    public final long v0;
+    public final long v1;
+
+    public R128long(long v) {
+        this(0, v);
+    }
+
+    public R128long(long hi, long lo) {
+        this.v0 = hi;
+        this.v1 = lo;
+    }
+
+    @Override
+    public int intValue() {
+        return (int)loValue();
+    }
+
+    @Override
+    public long longValue() {
+        return loValue();
+    }
+
+    @Override
+    public long hiValue() {
+        return v0;
+    }
+
+    @Override
+    public long loValue() {
+        return v1;
+    }
+
+    @Override
+    public long longSum() {
+        return v0 + v1;
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/R32byte.java b/test/micro/org/openjdk/bench/valhalla/types/R32byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/R32byte.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public class R32byte implements Int32, ByByte {
+
+    public final byte v0;
+    public final byte v1;
+    public final byte v2;
+    public final byte v3;
+
+    public R32byte(int v) {
+        this((byte) (v >>> 24), (byte) (v >>> 16), (byte) (v >>> 8), (byte) (v));
+    }
+
+    public R32byte(byte v0, byte v1, byte v2, byte v3) {
+        this.v0 = v0;
+        this.v1 = v1;
+        this.v2 = v2;
+        this.v3 = v3;
+    }
+
+    private static final int MASK = 0xFF;
+
+    @Override
+    public long longValue() {
+        return (long) intValue();
+    }
+
+    @Override
+    public int intValue() {
+        return (((v0 & MASK) << 24) | ((v1 & MASK) << 16) | ((v2 & MASK) << 8) | (v3 & MASK));
+    }
+
+    @Override
+    public Int32 neg() {
+        return new R32byte(-intValue());
+    }
+
+    @Override
+    public Int32 add(Int32 o) {
+        return new R32byte(intValue() + o.intValue());
+    }
+
+    @Override
+    public Int32 sub(Int32 o) {
+        return new R32byte(intValue() - o.intValue());
+    }
+
+    @Override
+    public Int32 mult(Int32 o) {
+        return new R32byte(intValue() * o.intValue());
+    }
+
+    @Override
+    public int compareTo(Int32 o) {
+        return Integer.compare(intValue(), o.intValue());
+    }
+
+    @Override
+    public byte byteSum() {
+        return (byte) (v0 + v1 + v2 + v3);
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/R32int.java b/test/micro/org/openjdk/bench/valhalla/types/R32int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/R32int.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public class R32int implements Int32, ByInt {
+
+    public final int v0;
+
+    public R32int() {
+        this.v0 = 0;
+    }
+
+    public R32int(int val) {
+        this.v0 = val;
+    }
+
+    @Override
+    public long longValue() {
+        return v0;
+    }
+
+    @Override
+    public int intValue() {
+        return v0;
+    }
+
+    @Override
+    public Int32 neg() {
+        return new R32int(-v0);
+    }
+
+    @Override
+    public Int32 add(Int32 o) {
+        return new R32int(v0 + o.intValue());
+    }
+
+    public Int32 add(R32int o) {
+        return new R32int(v0 + o.v0);
+    }
+
+    @Override
+    public Int32 sub(Int32 o) {
+        return new R32int(v0 - o.intValue());
+    }
+
+    public Int32 sub(R32int o) {
+        return new R32int(v0 - o.v0);
+    }
+
+    @Override
+    public Int32 mult(Int32 o) {
+        return new R32int(v0 * o.intValue());
+    }
+
+    public Int32 mult(R32int o) {
+        return new R32int(v0 * o.v0);
+    }
+
+    @Override
+    public int compareTo(Int32 o) {
+        return Integer.compare(v0, o.intValue());
+    }
+
+    public int compareTo(R32int o) {
+        return Integer.compare(v0, o.v0);
+    }
+
+    @Override
+    public int intSum() {
+        return v0;
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/R64byte.java b/test/micro/org/openjdk/bench/valhalla/types/R64byte.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/R64byte.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public class R64byte implements Int64, ByByte {
+
+    public final byte v0;
+    public final byte v1;
+    public final byte v2;
+    public final byte v3;
+    public final byte v4;
+    public final byte v5;
+    public final byte v6;
+    public final byte v7;
+
+    public R64byte(long v) {
+        this((byte) (v >>> 56), (byte) (v >>> 48), (byte) (v >>> 40), (byte) (v >>> 32), (byte) (v >>> 24), (byte) (v >>> 16), (byte) (v >>> 8), (byte) (v));
+    }
+
+    public R64byte(byte v0, byte v1, byte v2, byte v3, byte v4, byte v5, byte v6, byte v7) {
+        this.v0 = v0;
+        this.v1 = v1;
+        this.v2 = v2;
+        this.v3 = v3;
+        this.v4 = v4;
+        this.v5 = v5;
+        this.v6 = v6;
+        this.v7 = v7;
+    }
+
+    private static final long MASK = 0xFFL;
+    private static final int IMASK = 0xFF;
+
+    @Override
+    public long longValue() {
+        return ((v0 & MASK) << 56) | ((v1 & MASK) << 48) | ((v2 & MASK) << 40) | ((v3 & MASK) << 32) | ((v4 & MASK) << 24) | ((v5 & MASK) << 16) | ((v6 & MASK) << 8) | (v7 & MASK);
+    }
+
+    @Override
+    public int intValue() {
+        return ((v4 & IMASK) << 24) | ((v5 & IMASK) << 16) | ((v6 & IMASK) << 8) | (v7 & IMASK);
+    }
+
+    @Override
+    public byte byteSum() {
+        return (byte) (v0 + v1 + v2 + v3 + v4 + v5 + v6 + v7);
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/R64int.java b/test/micro/org/openjdk/bench/valhalla/types/R64int.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/R64int.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public class R64int implements Int64, ByInt {
+
+    public final int v0;
+    public final int v1;
+
+    public R64int(long v) {
+        this((int) (v >>> 32), (int) v);
+    }
+
+    public R64int(int hi, int lo) {
+        this.v0 = hi;
+        this.v1 = lo;
+    }
+
+    private static final long MASK = 0xFFFFFFFFL;
+
+    @Override
+    public long longValue() {
+        return (v0 & MASK) << 32 | (v1 & MASK);
+    }
+
+    @Override
+    public int intValue() {
+        return v1;
+    }
+
+    @Override
+    public int intSum() {
+        return v0 + v1 ;
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/R64long.java b/test/micro/org/openjdk/bench/valhalla/types/R64long.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/R64long.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public class R64long extends A64long  {
+
+    public final long v0;
+
+    public R64long(long v0) {
+        this.v0 = v0;
+    }
+
+    @Override
+    public long longValue() {
+        return v0;
+    }
+
+    @Override
+    public int intValue() {
+        return (int) v0;
+    }
+
+    @Override
+    public long longSum() {
+        return v0;
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/RComplex.java b/test/micro/org/openjdk/bench/valhalla/types/RComplex.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/RComplex.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+public class RComplex implements Complex {
+
+    public final double re;
+    public final double im;
+
+    public RComplex(double re, double im) {
+        this.re =  re;
+        this.im =  im;
+    }
+
+    @Override
+    public double re() { return re; }
+
+    @Override
+    public double im() { return im; }
+
+    @Override
+    public RComplex add(Complex that) {
+        return new RComplex(this.re + that.re(), this.im + that.im());
+    }
+
+    public RComplex add(RComplex that) {
+        return new RComplex(this.re + that.re, this.im + that.im);
+    }
+
+    @Override
+    public RComplex mul(Complex that) {
+        return new RComplex(this.re * that.re() - this.im * that.im(),
+                           this.re * that.im() + this.im * that.re());
+    }
+
+    public RComplex mul(RComplex that) {
+        return new RComplex(this.re * that.re - this.im * that.im,
+                           this.re * that.im + this.im * that.re);
+    }
+
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/types/ROpt.java b/test/micro/org/openjdk/bench/valhalla/types/ROpt.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/types/ROpt.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.types;
+
+import java.util.NoSuchElementException;
+
+public class ROpt<T> implements Opt<T> {
+
+    public T value;
+
+    private <T> ROpt() {
+        this.value = null;
+    }
+
+    private ROpt(T value) {
+        this.value = value;
+    }
+
+    public static <T> ROpt<T> of() {
+        return new ROpt<>(null);
+    }
+
+    public static <T> ROpt<T> of(T value) {
+        return new ROpt<>(value);
+    }
+
+    public T get() {
+        if (value == null) {
+            throw new NoSuchElementException("No value present");
+        }
+        return value;
+    }
+
+    public boolean isPresent() {
+        return value != null;
+    }
+
+    public boolean isEmpty() {
+        return value == null;
+    }
+}
diff a/test/micro/org/openjdk/bench/valhalla/util/SizeBase.java b/test/micro/org/openjdk/bench/valhalla/util/SizeBase.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/util/SizeBase.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.util;
+
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(3)
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+public class SizeBase {
+
+    @State(Scope.Thread)
+    public static abstract class SizeState {
+
+        @Param({
+                "100",      // tiny size, to fit into all caches and check codegeneration quality
+                "1000000"   // large size, to be out of all caches and memory effects
+        })
+        public int size;
+    }
+
+}
diff a/test/micro/org/openjdk/bench/valhalla/util/Utils.java b/test/micro/org/openjdk/bench/valhalla/util/Utils.java
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/util/Utils.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020, 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 org.openjdk.bench.valhalla.util;
+
+import java.util.Random;
+
+public class Utils {
+
+    public static void shuffle(int[] a, Random rnd) {
+        for (int i = a.length; i > 1; i--) {
+            int idx = rnd.nextInt(i);
+            int tmp = a[i - 1];
+            a[i - 1] = a[idx];
+            a[idx] = tmp;
+        }
+
+    }
+
+    public static int[] makeRandomRing(int size) {
+        int[] A = new int[size - 1];
+        for (int i = 0; i < A.length; i++) {
+            A[i] = i + 1;
+        }
+        shuffle(A, new Random(42));
+        int[] a = new int[size];
+        int x = 0;
+        for (int i = 0; i < A.length; i++) {
+            x = a[x] = A[i];
+        }
+        a[x] = 0;
+        return a;
+    }
+}
